Skip to content
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

feat!: handle const and discriminator #1169

Merged
merged 28 commits into from
Apr 19, 2023
Merged

Conversation

kennethaasan
Copy link
Collaborator

@kennethaasan kennethaasan commented Mar 21, 2023

I started on this feature at #958, but had to abandon it because of other priorities.

Description

Properties that use const should be initialized with that const value and not be included in the constructor of a model. This functionality is added for TypeScript and Java.

If discriminator is set in a schema that other schemas are extending, it will create an enum instead of a string const.

Interpreters
The interpreters are now reading the discriminator property from schemas and saves it to the interpreterOptions so that it can be used recursively inside properties. In https://github.com/kennethaasan/modelina/blob/const/src/interpreter/InterpretConst.ts#L29-L31, we now only make the const an enum if discriminator is set. By doing that, we allow const to be a string const if the discriminator is not set. In https://github.com/kennethaasan/modelina/blob/const/src/interpreter/InterpretAllOf.ts#L35-L42, we make sure to pass the discriminator to schemas that are extending the schema with discriminator set. The same is done in https://github.com/kennethaasan/modelina/blob/const/src/interpreter/InterpretOneOfWithProperties.ts#L33-L38 and https://github.com/kennethaasan/modelina/blob/const/src/interpreter/InterpretOneOfWithAllOf.ts#L34-L41. In https://github.com/kennethaasan/modelina/blob/const/src/interpreter/InterpretProperties.ts#L34-L40, it will now pass the discriminator to properties if the value of the discriminator is the same as the property name so that we can handle it in interpretConst.

MetaModel
I've added options to the MetaModel in https://github.com/kennethaasan/modelina/blob/const/src/models/MetaModel.ts#L9. This caused a major diff in this PR because the MetaModel was used in a lot of places. By adding an options object, we can easier add things like default later. At first, I tried to add it to the ObjectPropertyModel, but that didn't work because I couldn't read the constValue in the UnionModel.

ConstrainedMetaModel
When I added the options object to the MetaModel, I also had to add it to the ConstrainedMetaModel because it was extending the MetaModel, and we need access to the constValue in renderers.

ConstrainedObjectPropertyModel
In ConstrainedObjectPropertyModel, I added methods related to the constValue which we can use in renderers.

src/generators/java/renderers/ClassRenderer.ts
In the ClassRenderer, I've added a getConstValue method which is used to render const values for Java.

src/generators/typescript/TypeScriptObjectRenderer.ts
In the TypeScriptObjectRenderer, I've added a getConstValue method which is used to render const values for TypeScript.

Related issue(s)
Resolves #946

@netlify
Copy link

netlify bot commented Mar 21, 2023

Deploy Preview for modelina failed.

Name Link
🔨 Latest commit a5c2b63
🔍 Latest deploy log https://app.netlify.com/sites/modelina/deploys/64394b46f85b9b0008831095

@kennethaasan kennethaasan changed the base branch from master to next March 21, 2023 16:49
@github-advanced-security
Copy link

You have successfully added a new SonarCloud configuration ``. As part of the setup process, we have scanned this repository and found no existing alerts. In the future, you will see all code scanning alerts on the repository Security tab.

@kennethaasan kennethaasan marked this pull request as draft March 21, 2023 20:53
@kennethaasan
Copy link
Collaborator Author

kennethaasan commented Mar 21, 2023

TODO:

  • add tests for Java
  • add migrating guidelines

@jonaslagoni
Copy link
Member

@kennethaasan you could split it up and introduce it in the meta model, and interpreter etc first, and then do the generators afterwards.

Just to keep the pr as small as possible 🙂

@kennethaasan kennethaasan marked this pull request as ready for review March 22, 2023 09:42
@kennethaasan
Copy link
Collaborator Author

@jonaslagoni, 🤔 . I can if you prefer, but it's now ready for review.

@jonaslagoni
Copy link
Member

jonaslagoni commented Mar 22, 2023

It's entirely up to you @kennethaasan 😄 It's just gonna take longer to review 🙂

@kennethaasan
Copy link
Collaborator Author

@jonaslagoni, it does. I can try to split up next time. I added some extra tests. Please have a look whenever you have time :)

@jonaslagoni
Copy link
Member

@kennethaasan do you mind updating with the latest next changes, I want to see if I can use the Playground to test your changes, without having to clone -> test 😄

@kennethaasan
Copy link
Collaborator Author

@jonaslagoni done 😄

Copy link
Member

@jonaslagoni jonaslagoni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got a couple of things without even going into the code.

Should const always use enums or values?

I.e. should the following:

{
    "type": "object",
    "$id": "LightMeasured",
    "additionalProperties": false,
    "properties": {
        "type": {
            "type": "string",
            "const": "test"
        }
    }
}

Generate

private _reservedType?: ReservedType = ReservedType.TEST
// Or
private _reservedType?: string = "test"

Or should the user choose? 🤔

Weird results

Multiple enum values only render 1

{
    "type": "object",
    "$id": "LightMeasured",
    "additionalProperties": false,
    "properties": {
        "type": {
            "const": "MyMessage",
            "allOf": [
                {
                    "$ref": "#/definitions/MyCommonEnums"
                }
            ]
        },
        "type2": {
            "allOf": [
                {
                    "$ref": "#/definitions/MyCommonEnums"
                },
                {
                    "const": "MyMessage2"
                }
            ]
        }
    },
    "definitions": {
        "MyCommonEnums": {
            "enum": ["MyMessage", "MyMessage2"]
        }
    }
}

I would expect the above to give me a single enum with two values, where type uses the first one, and type2 uses the second one 🤔 There are two bugs there

  1. They both should be referring to the same enum instead of two different ones. This is something that is unrelated to your PR, so ignore that.
  2. There are a different interpretation between the type and type2 properties, where type2 actually does what I would expect, and type does not. They are mutually exclusive the two representations.

Problem number 2 is related to this PR as far as I can tell 🤔 Especially when it comes to your cloudevent example, where the messages uses DogType and CatType instead of referring to Pet 🤔

oneOf/anyOf splits them out

{
    "type": "object",
    "$id": "LightMeasured",
    "additionalProperties": false,
    "properties": {
        "type": {
            "oneOf": [
                {
                    "$ref": "#/definitions/MyCommonEnums"
                },
                {
                    "const": "MyMessage2"
                }
            ]
        }
    },
    "definitions": {
        "MyCommonEnums": {
            "title": "MyCommonEnums",
            "enum": ["MyMessage", "MyMessage2"]
        }
    }
}

I would expect that only MyCommonEnums would be used and technically the const ignored because they overlap 🤔

@kennethaasan kennethaasan changed the title feat!: handle const feat!: handle const and discriminator Mar 29, 2023
@jonaslagoni
Copy link
Member

@kennethaasan I have no idea where to even begin here 😄

Do you mind listing the changes you are proposing, so its easy to follow along in the code?

@kennethaasan
Copy link
Collaborator Author

@jonaslagoni, understandable 😄 I tried to make it more clear in the PR description. The change in the MetaModel caused a big diff, because it's used in a lot of tests. I can also walk you through it over zoom if you want. Feel free to dm me on Slack.

Copy link
Member

@jonaslagoni jonaslagoni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, it took so long to review... Been a bit swamped lately 🙃

This caused a major diff in this PR because the MetaModel was used in a lot of places

Yea, we might want to use an object argument sometime in the future instead of regular arguments. But yea, down the road.

Yea, after reviewing the code I don't really like you have to remember where information is within the MetaModel, i.e. whether it's under .options or .. But on the other hand it is future proof for adding extra information as you said.

Now we are just mixing object arguments (options) and regular arguments, which should not be the case IMO. But if we get an issue for it I think its fine for now 👍

At first, I tried to add it to the ObjectPropertyModel, but that didn't work because I couldn't read the constValue in the UnionModel.

Ahh okay!

In ConstrainedObjectPropertyModel, I added methods related to the constValue which we can use in renderers.

Is there a reason it's now functions, instead of raw values? 🤔 Because functions were one of the reasons the last big refactor took place, it was a horrible user experience when writing presets. I.e. you had to call a function to get a rendered property name instead of just saying model.propertyName.

I get the renderer's helper functions, but for the models I am a bit unsure of the benefit 🤔

src/generators/java/renderers/ClassRenderer.ts Outdated Show resolved Hide resolved
@kennethaasan
Copy link
Collaborator Author

kennethaasan commented Apr 13, 2023

But if we get an issue for it I think its fine for now

Yes, def agree. I'll create an issue for that once this PR is merged.

@kennethaasan
Copy link
Collaborator Author

@sonarcloud
Copy link

sonarcloud bot commented Apr 14, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

Copy link
Member

@jonaslagoni jonaslagoni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think I can find anything specific about the changes, other than there is too many things going on in this PR 😄 For the future try to keep one PR = one feature or change 😅 It really helps on the reviewing part and the speed it can happen 👍

The only things I see we need to handle after this PR are the following:

  • Add related docs for each language for the new constraints (I see we are missing for enumValue): https://github.com/asyncapi/modelina/blob/master/docs/constraints/Java.md
  • Add issues for the other generators that they should implement the new constrain logic, and discriminator and adapt serialization presets where applicable, this can be done either now or once we release v2.

How does that sound? I'll help you create the issues if you want 🙂

@kennethaasan kennethaasan merged commit 32913ad into asyncapi:next Apr 19, 2023
@kennethaasan kennethaasan deleted the const branch April 19, 2023 11:13
@kennethaasan
Copy link
Collaborator Author

@jonaslagoni, thanks! Will do. When working on this PR I didn't realize that this PR would end up this big. An experience to learn from.

@asyncapi-bot
Copy link
Contributor

🎉 This PR is included in version 2.0.0-next.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

jonaslagoni added a commit that referenced this pull request Oct 17, 2023
* fix!: use preferred ids over anonymous ids (#1099)

* chore(release): v2.0.0-next.1 (#1113)

* fix!: adds union type when operation.message.oneOf is set (#1136)

* chore(release): v2.0.0-next.2 (#1144)

* chore(release): v2.0.0-next.3 (#1146)

* fix!: additionalItems being applied for regular arrays (#1140)

* chore(release): v2.0.0-next.4 (#1151)

* test: upgrades eslint and adds eslint-plugin-jest (#1166)

* fix!: fixes required properties when if/then/else is used (#1149)

* chore(release): v2.0.0-next.5 (#1184)

* chore: update snapshot and packagelock

* chore: update snapshot of example

* feat: force release (#1221)

* chore(release): v2.0.0-next.6 (#1222)

* feat!: handle const and discriminator (#1169)

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* fixes bug when using oneOf in channel by not adding models to input model if they have already been scanned

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: handle const

* feat: set correct java quotes

* remove discriminator from CommonModel because we don't need it

* remove discriminator from CommonModel because we don't need it

* fixes review comments

* adds constant constrainers

* adds constant constrainers

* adds constant constrainers

* adds constant constrainers

* remove unused var

* chore(release): v2.0.0-next.7 (#1242)

* fix!: should not carry over options from metaModel to constrainedModel (#1243)

* chore(release): v2.0.0-next.8 (#1247)

* docs: fixes constant constraints documentation (#1241)

* refactor: adds discriminator in CommonModel and interpreters (#1269)

* refactor: adds discriminator in CommonModel and interpreters

* refactor: adds discriminator in CommonModel and interpreters

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel (#1270)

* refactor: adds discriminator in CommonModel and interpreters

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel

* fix test

* review fix

* fix!: update default Kotlin renderer to make non-required properties nullable (#1277)

* chore(release): v2.0.0-next.9 (#1278)

* feat!: adds interface for oneOf objects for Java (#1271)

* refactor: adds discriminator in CommonModel and interpreters

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel

* feat: adds interface for oneOf objects for Java

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel

* update snapshot

* refactor: adds discriminator in MetaModel and ConstrainedMetaModel

* fix test

* feat: adds interface for oneOf objects for Java

* Revert "feat: adds interface for oneOf objects for Java"

This reverts commit e7bd8fb.

* review fix

* merge fix

* adds tests

* adds documentation

* fixes docs

* Update docs/migrations/version-1-to-2.md

Co-authored-by: Daniel KJ <github@dlkj.co.uk>

* adds test for union without jackson preset

* fixes docs

* fixes docs

* fixes docs

---------

Co-authored-by: Daniel KJ <github@dlkj.co.uk>

* chore(release): v2.0.0-next.10 (#1279)

* chore: update Rust union render to use ConstrainedMetaModelOptionsDiscriminator (#1282)

Update Rust union render to use `ConstrainedMetaModelOptionsDiscriminator`

Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* fix wrong import

* Revert "fix wrong import"

This reverts commit b6c5be6.

* fix: wrong import for Java preset (#1283)

fix: wrong import

* chore(release): v2.0.0-next.11 (#1286)

* feat!: add discriminator support for OpenAPI v3 and Swagger v2 (#1281)


Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* chore(release): v2.0.0-next.12 (#1287)

* feat!: use EXISTING_PROPERTY as JsonTypeInfo.As annotation to avoid duplicates when serializing (#1290)

* chore(release): v2.0.0-next.13 (#1291)

* chore: blackbox tests (#1303)


Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* feat!: enable nullable models (#1141)

* chore(release): v2.0.0-next.14 (#1313)

* chore: update new generators with next syntax

* chore: fix linting problem

* feat!: add java oneOf union support for properties of models  (#1296)


Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* chore: update dependency

* feat: update dependencies

* feat: update dependencies

* feat: update dependencies

* feat: trigger release and update dependencies (#1428)

* ci: temporarily enable release for next (#1430)

* fix: ts/js unmarshalling function bug (#1429)

* fix!: support java nullable double type (#1439)

Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* feat: update release configuration and trigger release (#1467)

* chore(release): v2.0.0-next.15 (#1468)

* feat: make discriminator property visible to deserializer from Java Jackson preset (#1469)


Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* chore(release): v2.0.0-next.16 (#1470)

* fix: typescript interface variable assignment (#1472)


Co-authored-by: Kristupas Narkeliunas <kristupas.narkeliun@ihsmarkit.com>

* chore(release): v2.0.0-next.17 (#1473)

* feat!: upgrade to node 18 (#1422)

* upgrade to v18

* adapt rest of workflows

* update readme

* recommit packagelock

* feat!: supports optional properties in java (#1485)

* chore(release): v2.0.0-next.18 (#1487)

* chore: add next integration example (#1488)

* fix: adds format in options instead of using original input (#1486)

* chore(release): v2.0.0-next.19 (#1492)

* feat!: generate models for OpenAPI parameters (#1498)


Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>

* chore(release): v2.0.0-next.20 (#1500)

* fix: fixes java jackson discriminator when property is not camel case (#1502)

* chore(release): v2.0.0-next.21 (#1504)

* fix: fixes performance issues in java when calling parent unions (#1501)

* chore(release): v2.0.0-next.22 (#1506)

* feat!: refactors generators to use the same arg types (#1505)

* chore(release): v2.0.0-next.23 (#1507)

* fix: escape java string literals used for regex patterns and string constants (#1509)


Co-authored-by: Daniel Kenyon-Jones <kenyonjd@featurespace.co.uk>

* chore(release): v2.0.0-next.24 (#1558)

* chore: update linting and dev dependency versions

* chore: update migration guide (#1560)

* chore(release): v2.0.0-next.25 (#1571)

---------

Co-authored-by: Kenneth Aasan <k.aasan@sportradar.com>
Co-authored-by: asyncapi-bot <bot+chan@asyncapi.io>
Co-authored-by: Cyprian Gracz <cyprian.gracz@micro-jumbo.eu>
Co-authored-by: Daniel KJ <github@dlkj.co.uk>
Co-authored-by: Daniel Kenyon-Jones <daniel.kenyon-jones@featurespace.co.uk>
Co-authored-by: Kristupas <53404771+Ksisa@users.noreply.github.com>
Co-authored-by: Kristupas Narkeliunas <kristupas.narkeliun@ihsmarkit.com>
Co-authored-by: Daniel Kenyon-Jones <kenyonjd@featurespace.co.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants