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

[FEATURE REQUEST] Provide formal type for message examples #329

Closed
lbroudoux opened this issue Mar 11, 2020 · 37 comments
Closed

[FEATURE REQUEST] Provide formal type for message examples #329

lbroudoux opened this issue Mar 11, 2020 · 37 comments
Assignees
Labels
💡 Proposal (RFC 1) RFC Stage 1 (See CONTRIBUTING.md) stale

Comments

@lbroudoux
Copy link
Contributor

Is your feature request related to a problem? Please describe.
I'm working on an API mocking tool that aims to use a specification examples for providing mock endpoints and realize contract testing (see https://microcks.io). For REST APIs specified with OpenAPIs, Microcks tooks advantage of clearly defined exampleObject allowing to specify example values and references for every request or response fragments.

I found it very valuable to have the same mechanism with AsyncAPI to allow specification of real-life examples for providing mock channels and messages helping speed-up development on new consumers / providers. However, examples are just for now a [Map[string, any]] that makes them difficult to agree on a common way to structure samples.

Can't it be tackled using specification extensions?
I do not see any extension mechanism to enable this...

Describe the solution you'd like
I'd like a formal type description on how a message example should be structured including some metadata, an optional set of headers and the associated payload. Something like:

message:
  description: An event describing that a user just signed up.
  traits:
    - $ref: '#/components/messageTraits/commonHeaders'
  headers:
    type: object
    required: true
    properties:
      sentAt:
        type: string
        format: date-time
        description: Date and time when the event was sent
  payload:
    type: object
    additionalProperties: false
    properties:
      fullName:
        type: string
      email:
        type: string
        format: email
      age:
        type: integer
        minimum: 18
  examples:
    - laurent:
        summary: Example for Laurent user
        headers: |-
          {"my-app-header": 23, "sentAt": "2020-03-11T08:03:28Z"}
        payload: |-
          {"fullName": "Laurent Broudoux", "email": "laurent@microcks.io", "age": 41}
    - john:
        summary: Example for John Doe user
        headers:
          my-app-header: 24
          sentAt: "2020-03-11T08:03:38Z"
        payload:
          fullName: John Doe
          email: john@microcks.io
          age: 36

Describe alternatives you've considered
I considered providing example fragments at each level of the specification (header, traits and payload) but I found it confusing because trait does not seem to allow examples and also because examples in the case of headers are the schema level and not the instance level. This proposition looks the more concise and understandable (at least to me ;-) )

Additional context
This feature will allow :

  • to complete specification with real-life examples (and not only generated ones from type schemas) which is very valuable and gives, free and human-readable documentation,
  • to build a set of tools (like Microcks ;-)) that can turn your specification in something executable whether for doing mocking or contract testing.
@github-actions
Copy link

Welcome to AsyncAPI. Thanks a lot for reporting your first issue.

Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

@yada
Copy link

yada commented Mar 11, 2020

+1 this is expected by many async devs, archi... and will be great to be able to rely on specs for this purpose.

@fmvilas fmvilas modified the milestones: AsyncAPI specification 2.0.1, AsyncAPI specification 2.1.0 Mar 13, 2020
@fmvilas fmvilas self-assigned this Mar 13, 2020
@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴
It will be closed in 30 days if no further activity occurs. To unstale this issue, add a comment with detailed explanation.
Thank you for your contributions ❤️

@github-actions github-actions bot added the stale label May 13, 2020
@lbroudoux
Copy link
Contributor Author

Hi @fmvilas and AsyncAPI team,

Just to let you know how we plan to use AsyncAPI examples in Microcks.io, we released a preview video of this feature here: https://www.youtube.com/watch?v=uZaWAekvUz4

You'll see the previous suggested formalism in action. Please tell us what you think about it ;-)

Cheers,

@derberg
Copy link
Member

derberg commented May 26, 2020

@lbroudoux Awesome demo, thanks a lot for sharing. It super nicely shows why would it make sense to have more structured and defined examples

@derberg derberg removed the stale label May 26, 2020
@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴
It will be closed in 30 days if no further activity occurs. To unstale this issue, add a comment with detailed explanation.
Thank you for your contributions ❤️

@github-actions github-actions bot added the stale label Jul 26, 2020
@derberg derberg removed the stale label Jul 27, 2020
@lbroudoux
Copy link
Contributor Author

Hi AsyncAPI team,

Now that we have a working implementation in Microcks and are close to release 1.0.0 version with AsyncAPI support, I'd like to see how to push this ideas further. Would you guide me contributing a draft enhancement proposal?

What could be the best way:

  • directly forking the repo and submitting a PR on spec?
  • establishing some kind of design documentation on GDoc or other?

I've got some time to spend on this in coming weeks so really like to contribute back ;-)

@derberg
Copy link
Member

derberg commented Aug 3, 2020

@lbroudoux there is no formal process like KEP for Kubernetes for example. We simply work and discuss in issues to have all the thinking process in one place. So we first discuss under issue and then once there is a consensus you go on with a PR.

@lbroudoux
Copy link
Contributor Author

lbroudoux commented Aug 3, 2020

OK nice!

So I actually implemented some samples using the solution that was here first described:

  • Defining examples as a Map of sample messages with key being the name of the sample,
  • A sample message got a summary that may help refining sample / use-case matching, a headers and payload attributes that are simply Objects,
  • payload should follow message payload schema and provide actual payload that may be expressed directly in YAML or by embedding JSON,
  • headers should follow aggregation of message level header schema and traits schema - expressed in YAML or embedded JSON as well.

For now - after having tested it on a dozen of schemas and use-case - I cannot foresee any caveats, difficulties or challenges following this specification. What do you think?

@derberg
Copy link
Member

derberg commented Aug 14, 2020

I like the suggestion a lot. I only have a question in my head to the example object from OAS that you provided in the description. There is value and externalValue and I'm now curious if externalValue is adopted by the community. My guess is that there are 2 separate properties because example might be super different than json, like xml for example, thus cannot be used with $ref. Anyway thinking about these 2 somehow made my thing about a property that I could use to link to an external resource that contains an example, it could even be a link to documentation with an anchor to a specific section. This would also be an interesting property next to the summary 🤔 any thoughts, have you see a need for something like this?

@lbroudoux
Copy link
Contributor Author

lbroudoux commented Aug 17, 2020

It's a good point ! From what I see until now, externalValue does not seem to be adopted by the community.

As a very personal note and opinion, I also think that referencing stuffs outside of the specification file can lead to imprecise, too generic or totally mismatching examples. For me - and some other folks I discuss with - specifying examples is all about being very specific to the context (think of bounded context) of the operation or the API at a whole. Scaffolding valid examples embedded within the spec is usually an issue of tooling and some tools like Apicurio do this very well and help keeping examples in sync when evolving schema.

Also, from my understanding using value in OpenAPI does not prevent from using some other types that JSON as long as they can be represented as String. I see users specifying XML or Text representations using value. However, right now I'm scratching my head 🤔 thinking on how this could be done using formats like Avro for example ... maybe with JSON to Avro conversion ?? - or is there some subtleties we cannot easily convert ??

So, even if they do not sound essential to me, I tend to think that we could add something like externalPayload and externalHeaders in order to be fully analogous to OpenAPI and to allow future usages we are not aware of right now ;-)

@derberg
Copy link
Member

derberg commented Aug 17, 2020

afaik Avro can be presented as JSON or YAML so you can easily put it inside AsyncAPI file. Have a look at this example for our Avro schema parser https://github.com/asyncapi/avro-schema-parser#usage. Avro is part of the spec, and we specify it is Avro with schemaFormat, so if in some tooling I would have to somehow, in some special way treat the example, I would have to first read schemaFormat to know what kind of format is followed also in the example. Of course schemaFormat is described in the way that it is for the payload, so we would just have to make sure spec is clear that example format must match the payload format?

I see users specifying XML or Text representations using value

how do they do it in JSON, xml is represented as string or?

@lbroudoux
Copy link
Contributor Author

lbroudoux commented Aug 17, 2020

see users specifying XML or Text representations using value
how do they do it in JSON, xml is represented as string or?

Yes, XML represented as string between double-quotes. JSON represented as string or JSON embedded into YAML or in YAML depending on the authoring tool. In the case of Microcks importing YAML, we take care of YAML to JSON conversion based on specified content-type like you suggest for schemaFormat.

@derberg
Copy link
Member

derberg commented Aug 17, 2020

oh 🤦 we have contentType as well of course and this is why we should use to interpret the example, not schemaFormat.
Now it also makes sense to me why the example in the issue description has JSON as String and not directly JSON 👍

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴
It will be closed in 30 days if no further activity occurs. To unstale this issue, add a comment with detailed explanation.
Thank you for your contributions ❤️

@github-actions github-actions bot added the stale label Oct 17, 2020
@derberg derberg removed the stale label Oct 18, 2020
@lbroudoux
Copy link
Contributor Author

Hi @fmvilas and @derberg !

Regarding your last comment on this issue: yes you're right, contentType is the way to go ;-)

Now that we have stabilized things and demonstrate pragmatic usages of examples through Microcks mocking and testing features, I'd like to tackle making this issue and suggestions a real proposal for 2.1.0 release of the specification ! What could be the next steps for it ? Initializing a 2.1.0 folder with https://github.com/asyncapi/asyncapi/tree/master/versions and creating a PR ? Starting a markdown patch in some other way ?

Let me know, I'll be happy to start writing it in a more formal way.

@derberg
Copy link
Member

derberg commented Nov 25, 2020

@lbroudoux I think we need to figure out this first #463. Feel free to jump into discussion

@lbroudoux
Copy link
Contributor Author

Thanks @derberg for the heads-up. I'll jump 😉

@derberg
Copy link
Member

derberg commented Nov 27, 2020

According to AsyncAPI schema, example must match payload and therefore we will support this kind of validation, we just didn't have time to add it yet -> asyncapi/parser-js#99

@lbroudoux
Copy link
Contributor Author

Agree with the role of tooling for this. This is where tooling can bring additional value. FYI, I am a contributor to Apicurio Studio and it has validation rules for OpenAPI including examples.

@WaleedAshraf
Copy link
Contributor

Ok. If we plan to provide example validation in the future, then this (formal type for message examples) is a good feature to support. 👍

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴
It will be closed in 60 days if no further activity occurs. To unstale this issue, add a comment with detailed explanation.
Thank you for your contributions ❤️

@lbroudoux
Copy link
Contributor Author

Still there ;-) with hope to tackle this one soon !

@derberg
Copy link
Member

derberg commented Apr 7, 2021

@lbroudoux let's do this. Please have a look at the latest contribution guide and update here information about the stage your proposal reached. I bet you want to be the champion for it and drive it forward until the end

@derberg
Copy link
Member

derberg commented Apr 21, 2021

@lbroudoux I had a thought recently about this proposal and wanted to suggest an alternative that would not introduce a breaking change to the spec.

Initial proposal is

  examples:
    - laurent:
        summary: Example for Laurent user
        headers: |-
          {"my-app-header": 23, "sentAt": "2020-03-11T08:03:28Z"}
        payload: |-
          {"fullName": "Laurent Broudoux", "email": "laurent@microcks.io", "age": 41}
    - john:
        summary: Example for John Doe user
        headers:
          my-app-header: 24
          sentAt: "2020-03-11T08:03:38Z"
        payload:
          fullName: John Doe
          email: john@microcks.io
          age: 36

Current structure:

  examples:
    - headers: |-
          {"my-app-header": 23, "sentAt": "2020-03-11T08:03:28Z"}
      payload: |-
          {"fullName": "Laurent Broudoux", "email": "laurent@microcks.io", "age": 41}
    - headers:
          my-app-header: 24
          sentAt: "2020-03-11T08:03:38Z"
      payload:
          fullName: John Doe
          email: john@microcks.io
          age: 36

Why not doing:

  examples:
    - name: laurent #machine readable as it is now with message.name (for consistency)
      summary: Example for Laurent user
      headers: |-
          {"my-app-header": 23, "sentAt": "2020-03-11T08:03:28Z"}
      payload: |-
          {"fullName": "Laurent Broudoux", "email": "laurent@microcks.io", "age": 41}
    - name: john #machine readable as it is now with message.name (for consistency)
      summary: Example for John Doe user
      headers:
          my-app-header: 24
          sentAt: "2020-03-11T08:03:38Z"
      payload:
          fullName: John Doe
          email: john@microcks.io
          age: 36

Not everyone uses spec for testing, so not everyone should be "forced" to provide a key for the example object, I think. Also as result all example related properties will be in one place.

@lbroudoux
Copy link
Contributor Author

Hi @derberg,

thanks for the suggestion, this is a great one and make the trick possible! The initial proposal of having a key was intended to align the structure on the one from OpenAPI Spec but it's better to introduce this without a breaking change.

Luckily enough, I had some time in the coming days and will review the new contribution guide to see how ti push this onward.

@lbroudoux
Copy link
Contributor Author

Hi @derberg!

Pushed this 2 PRs few days ago. However, I did not have access to labels in order to propose a Strawman, Proposal or Draft status on them ...

@fmvilas fmvilas removed this from the Next specification version milestone May 12, 2021
@derberg
Copy link
Member

derberg commented May 14, 2021

@fmvilas I think we reached Stage 1 here, right?

@lbroudoux we also need a PR against the parser as this is release requirement, to add support for new fields, not only bump node-asyncapi but also extend https://github.com/asyncapi/parser-js/blob/master/lib/models/message-traitable.js#L89 as for now we just have a simple helper that returns all examples.

Thoughts?

@lbroudoux
Copy link
Contributor Author

Great! As I saw that examples are still of Any type (despite payload and headers already in the spec) I thought there was a design decision to keep them generic ;-) Anyway, I will push the PR!

@derberg
Copy link
Member

derberg commented May 14, 2021

keep in mind parser is still a bit behind the spec, some features are missing like examples validation against the schemas, so don't pay that much attention to the type that you see there

@fmvilas fmvilas added the 💡 Proposal (RFC 1) RFC Stage 1 (See CONTRIBUTING.md) label May 14, 2021
@fmvilas
Copy link
Member

fmvilas commented May 14, 2021

@derberg Yup, label added.

@lbroudoux
Copy link
Contributor Author

lbroudoux commented May 27, 2021

Suggestions have been resolved into existing PRs.
Parser is now implemented and pushed here: asyncapi/parser-js#307

I think the check list is now complete for entering Stage 2: DRAFT 😄

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity 😴
It will be closed in 60 days if no further activity occurs. To unstale this issue, add a comment with detailed explanation.
Thank you for your contributions ❤️

@github-actions github-actions bot added the stale label Jul 27, 2021
@magicmatatjahu
Copy link
Member

I think that this issue is resolved @lbroudoux If you think that something should be added, please reopen it. Thanks again for the contribution! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💡 Proposal (RFC 1) RFC Stage 1 (See CONTRIBUTING.md) stale
Projects
None yet
Development

No branches or pull requests

6 participants