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: enable AsyncAPI v3 validation #825

Merged

Conversation

smoya
Copy link
Member

@smoya smoya commented Aug 1, 2023

Tests are failing. Read comments below.

@smoya
Copy link
Member Author

smoya commented Aug 1, 2023

Validation fails with the following errors:

[
   {
      "code":"asyncapi-document-resolved",
      "message":"'someOperation1' property must match exactly one schema in oneOf",
      "path":[
         "operations",
         "someOperation1"
      ],
      "range":{
         "end":{
            "character":46,
            "line":20
         },
         "start":{
            "character":21,
            "line":10
         }
      },
      "severity":0
   },
   {
      "code":"asyncapi-document-resolved",
      "message":"'someOperation2' property must match exactly one schema in oneOf",
      "path":[
         "operations",
         "someOperation2"
      ],
      "range":{
         "end":{
            "character":46,
            "line":35
         },
         "start":{
            "character":21,
            "line":24
         }
      },
      "severity":0
   }
]

The validation rule that fails is the asyncapi-document-resolved, which checks if the AsyncAPI document is valid (using JSON Schema) after resolving references.
Tests are failing due to the fact channel property under Operation one should be just a reference to a channel and not a channel object directly. This is the very first time we don't allow a field to, besides being a reference, to also be defined inline. The asyncapi-document-resolved rule applies after resolving the reference for the channel object, which becomes an invalid document since the expected channel value should be always a reference.

Thanks @jonaslagoni for helping (and actually finding) that out.

The error messages shown above are also wrong. We do some error filtering as stated in https://github.com/asyncapi/parser-js/blob/master/src/ruleset/functions/documentStructure.ts#L20, and it seems we are doing too much and, in this case, we skip the real error, which can be found in the list of errors prior to the filtering:

[
  {
    "instancePath": "/operations/someOperation1",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation1/channel",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation1",
    "schemaPath": "#/additionalProperties/oneOf",
    "keyword": "oneOf",
    "params": {
      "passingSchemas": null
    },
    "message": "must match exactly one schema in oneOf"
  },
  {
    "instancePath": "/operations/someOperation2",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation2/channel",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": {
      "missingProperty": "$ref"
    },
    "message": "must have required property '$ref'"
  },
  {
    "instancePath": "/operations/someOperation2",
    "schemaPath": "#/additionalProperties/oneOf",
    "keyword": "oneOf",
    "params": {
      "passingSchemas": null
    },
    "message": "must match exactly one schema in oneOf"
  }
]

The expected error should be the ones with an instancePath pointing to the operation channel, where the message says must have required property '$ref'.

@smoya
Copy link
Member Author

smoya commented Aug 1, 2023

Based on my prior comment, this is what we need to do next in this PR:

  1. To fix the bug in the error messages filter, so the error that stays is the one that points to each operation.channel field.
  2. To skip validation of operation.channel for v3 in the asyncapi-document-resolved (still will happen in asyncapi-document-unresolved).

Alternatives for the second point will be to either allow by JSON Schema to define operation.channel as object aswell, which will then incompatible with the current spec, or rather to keep different JSON Schema files, one for pre and another for post reference resolution. I think this is also discarded as it is a lot of work and effort.

src/ruleset/formats.ts Outdated Show resolved Hide resolved
@smoya
Copy link
Member Author

smoya commented Aug 7, 2023

I’m thinking that we should not go in the direction of manipulating the schema for the operation.channel in the mid term. Having to manipulate the schema from now on is not either good nor maintainable IMHO.

The alternative is to remove the asyncapi-document-resolved rule forever and just relay on the asyncapi-document-unresolved. The point is that we are filtering errors here, so we would need to return them and not filter. What is the real implication of it?

WDYT @magicmatatjahu @jonaslagoni

@magicmatatjahu
Copy link
Member

@smoya Issues are fixed :)

@smoya
Copy link
Member Author

smoya commented Aug 8, 2023

@smoya Issues are fixed :)

Thanks for the help @magicmatatjahu 🙌 🙌 !!

I still have this concern and I would love to know your opinion as you are the one with most knowledge on this code.

I would like to have this PR merged but to keep iterating after so we go for a better and maintainable alternative.

I created a follow up issue so we don't block this anymore.

cc @jonaslagoni @Amzani

@magicmatatjahu
Copy link
Member

magicmatatjahu commented Aug 8, 2023

@smoya

I’m thinking that we should not go in the direction of manipulating the schema for the operation.channel in the mid term. Having to manipulate the schema from now on is not either good nor maintainable IMHO.

I agree that it's not good but parser should throws all errors, so I can accept situation when the source code is "ugly" but DX is better. We have also option to change the JSON Schemas to allow also inline objects in given places, but it won't follow the spec. There is also a idea to allow inline objects in spec itself - but then 3.1.0 is needed.

The alternative is to remove the asyncapi-document-resolved rule forever and just relay on the asyncapi-document-unresolved. The point is that we are filtering errors here, so we would need to return them and not filter. What is the real implication of it?

asyncapi-document-resolved is very needed because we should also check the correctness of imported objects from external files/https (external refs). Without this rule how to check if something imported is ok?

@smoya
Copy link
Member Author

smoya commented Aug 8, 2023

There is also a idea to allow inline objects in spec itself - but then 3.1.0 is needed.

Yeah, this is something I want to bring up and discuss with the community.

asyncapi-document-resolved is very needed because we should also check the correctness of imported objects from external files/https (external refs). Without this rule how to check if something imported is ok?

Yeah, I agree there is no better alternative and, for sure, this validation is an important addition on top of the basic schema validation.

Maybe we could work on an automation, for every time we find a property with only a reference to a Reference Object , to modify that schema to allow the object as well (oneOf or just right the definition object as you did).

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.

As I see it there is one additional approach that is unmentioned, and that is we could make the reference resolver ignore the specific path i.e.

  • channelObject.properties.servers.items.$ref
  • operationSchema.properties.channel.$ref
  • operationSchema.properties.messages.items.$ref
  • operationReplySchema.properties.channel.$ref
  • operationReplySchema.properties.messages.items.$ref
    Just for that specific rule, but it might be hard to pull of. There are also some more unknowns such as can the library we use do this? (I know https://apitools.dev/json-schema-ref-parser/docs/options.html can) Can we still ensure the refs are correct and point to a valid location?

So yea, I think your current solution makes sense 👍

@smoya have you added enough test cases to test for all those paths and that they are caught?

The alternative is to remove the asyncapi-document-resolved rule forever and just relay on the asyncapi-document-unresolved. The point is that we are filtering errors here, so we would need to return them and not filter. What is the real implication of it?

What @magicmatatjahu said 👍

There is also a idea to allow inline objects in spec itself - but then 3.1.0 is needed.

I don't see this as making much sense from a spec point of view, so it might be really hard to do just because reference resolvement becomes hard to do 😄

test/ruleset/formats.spec.ts Outdated Show resolved Hide resolved
},

{
name: 'valid case for 3.X.X (case when other $ref errors should also occur but we filter them out)',
Copy link
Member

Choose a reason for hiding this comment

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

Are these names on cases still valid?

Copy link
Member Author

Choose a reason for hiding this comment

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

@magicmatatjahu pinging you because you added this test. What are the errors you expect here to be filtered?

Copy link
Member

@magicmatatjahu magicmatatjahu Aug 8, 2023

Choose a reason for hiding this comment

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

Spectral works in two modes - with schema resolved and unresolved (with untouched refs). Even if we wrote a ref in a place then Spectral would not throw an error (because it operates on resolved schema). This is more of a test for checking if rule isn't "breaking" resolved/unresolved spectral behaviour, just in case for us :)

Copy link
Member

Choose a reason for hiding this comment

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

If you want you can change the name of the test because it should have a different name, I can't remember why I called it that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed. Thanks!

@smoya smoya requested a review from jonaslagoni August 8, 2023 11:41
@sonarcloud
Copy link

sonarcloud bot commented Aug 8, 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 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

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.

👍

@jonaslagoni
Copy link
Member

/rtm

@asyncapi-bot asyncapi-bot merged commit 3fa3290 into asyncapi:next-major-spec Aug 9, 2023
11 checks passed
@asyncapi-bot
Copy link
Contributor

🎉 This PR is included in version 2.2.0-next-major-spec.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@smoya smoya deleted the feat/enableV3Validation branch December 11, 2023 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants