Skip to content

Create reusable feature metadata for all generators #840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jimschubert opened this issue Aug 18, 2018 · 1 comment
Closed

Create reusable feature metadata for all generators #840

jimschubert opened this issue Aug 18, 2018 · 1 comment

Comments

@jimschubert
Copy link
Member

Description

As discussed in #503, this would be a starting point for creating a compatibility matrix. We need some way for generators to define all feature sets supported by the generator.

We need to take into consideration that a base (abstract) type used across multiple generators may implement more features than a subtype intends to support. This means we need to be able to mutate the feature metadata from derived types.

Related issues/PRs
Suggest a fix/enhancement

I have a prototype in which I created a bitmask type in Kotlin, and apply feature sets as properties.

The feature sets I created are:

Click to expand Bitmask/Feature snippet
class BitMask(val value: Long)
interface Flags  {
    val bit: Long

    fun toBitMask(): BitMask = BitMask(bit)
}

// Flags extensions
infix fun Flags.and(other: Long): BitMask = BitMask(bit and other)
infix fun <T: Flags> Flags.or(other: T): BitMask = BitMask(bit or other.bit)
infix operator fun Flags.plus(other: Flags): BitMask = BitMask(bit or other.bit)
inline fun <reified T> enabledValues(mask: BitMask) : List<T> where T : Enum<T>, T : Flags {
    return enumValues<T>().filter {
        mask hasFlag it
    }
}

infix fun BitMask.or(other: Flags): BitMask = BitMask(value or other.bit)
infix operator fun BitMask.plus(other: BitMask): BitMask = BitMask(value or other.value)
infix operator fun BitMask.plus(other: Flags): BitMask = BitMask(value or other.bit)
infix fun <T: Flags> BitMask.hasFlag(which: T): Boolean {
    // an Undefined flag is a special case.
    if(value == 0L || (value > 0L && which.bit == 0L)) return false

    return value and which.bit == which.bit
}
infix fun <T: Flags> BitMask.unset(which: T): BitMask = BitMask(value xor which.bit)

// Features
enum class ClientModificationFeature(override val bit: Long) : Flags {
    Undefined(0),
    BasePath(1 shl 0),
    Authorizations(1 shl 1),
    UserAgent(1 shl 2);
}
enum class DataTypeFeature(override val bit: Long) : Flags {
    Undefined(0),
    Int32(1 shl 0),
    Int64(1 shl 1),
    Float(1 shl 2),
    Double(1 shl 3),
    Decimal(1 shl 4),
    String(1 shl 5),
    Byte(1 shl 6),
    Binary(1 shl 7),
    Boolean(1 shl 8),
    Date(1 shl 9),
    DateTime(1 shl 10),
    Password(1 shl 11),
    File(1 shl 12),
    Array(1 shl 13),
    Maps(1 shl 14),
    CollectionFormat(1 shl 15),
    CollectionFormatMulti(1 shl 16),
    Enum(1 shl 17),
    ArrayOfEnum(1 shl 18),
    ArrayOfModel(1 shl 19),
    ArrayOfCollectionOfPrimitives(1 shl 20),
    ArrayOfCollectionOfModel(1 shl 21),
    ArrayOfCollectionOfEnum(1 shl 22),
    MapOfEnum(1 shl 23),
    MapOfModel(1 shl 24),
    MapOfCollectionOfPrimitives(1 shl 25),
    MapOfCollectionOfModel(1 shl 26),
    MapOfCollectionOfEnum(1 shl 27);
}
enum class DocumentationFeature(override val bit: Long) : Flags {
    Undefined(0),
    Readme(1 shl 0),
    Model(1 shl 1),
    Api(1 shl 2);
}
enum class ModelReuseFeature(override val bit: Long) : Flags {
    Undefined(0),
    Composition(1 shl 0),
    Inheritance(1 shl 1);
}
enum class ParameterFeature(override val bit: Long) : Flags {
    Undefined(0),
    Path(1 shl 0),
    Query(1 shl 1),
    Header(1 shl 2),
    Body(1 shl 3),
    FormUnencoded(1 shl 4),
    FormMultipart(1 shl 5);
}
enum class SecurityFeature(override val bit: Long) : Flags {
    Undefined(0),
    BasicAuth(1 shl 0),
    ApiKey(1 shl 1),
    OAuth2(1 shl 2);
}

Types representing generator options then include these as properties:

Click to expand interface example
/**
 * A generator specific to a language and framework
 */
interface FrameworkGenerator {

    //

    /**
     * DataTypes support available in this generator
     */
    @FeatureFlag(DataTypeFeature::class)
    val supportedDataTypes: BitMask

    /**
     * Model reuse support available in this generator
     */
    @FeatureFlag(ModelReuseFeature::class)
    val supportedModelReuse: BitMask

    /**
     * Parameter support available in this generator
     */
    @FeatureFlag(ParameterFeature::class)
    val supportedParameters: BitMask

    /**
     * Client configuration/modification support available in this generator
     */
    @FeatureFlag(ClientModificationFeature::class)
    val supportedClientModification: BitMask

    /**
     * Security support available in this generator
     */
    @FeatureFlag(SecurityFeature::class)
    val supportedSecurity: BitMask

    /**
     * Documentation support available in this generator
     */
    @FeatureFlag(DocumentationFeature::class)
    val supportedDocumentation: BitMask

    //
}

Following a similar approach here would allow us to extract information about all generators and their supported feature set using reflection.

We should also eventually be able to automate templating tests related to features as well.

jimschubert added a commit that referenced this issue May 4, 2019
GeneratorMetadata offers an immutable object created via Builder pattern
which allows generators to explicitly define their stability (stable,
beta, experimental, deprecated) as well as a message to be shown during
generation.

This is a step toward:

* Fleshing out the "Core" artifact (#845)
* Providing a place to encapsulate feature-oriented metadata (#840)
* Providing a means to communicate end of life scheduling (#116)

This new structure, specifically the Stability property, allows us to
offer future enhancements such as allowing users to filter down to only
"Stable" generators via CLI, and eventually any compat table (see #503).
jimschubert added a commit that referenced this issue May 5, 2019
* [feat] Intro GeneratorMetadata (stability index)

GeneratorMetadata offers an immutable object created via Builder pattern
which allows generators to explicitly define their stability (stable,
beta, experimental, deprecated) as well as a message to be shown during
generation.

This is a step toward:

* Fleshing out the "Core" artifact (#845)
* Providing a place to encapsulate feature-oriented metadata (#840)
* Providing a means to communicate end of life scheduling (#116)

This new structure, specifically the Stability property, allows us to
offer future enhancements such as allowing users to filter down to only
"Stable" generators via CLI, and eventually any compat table (see #503).

* Mark deprecated generators as deprecated in-code

* Re-export docs/generators.md
@jimschubert jimschubert added this to the 5.0.0 milestone Sep 30, 2019
@jimschubert jimschubert modified the milestones: 5.0.0, 4.2.3 Jan 11, 2020
@jimschubert
Copy link
Member Author

Closed via #3614

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant