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

$ref support - OAS 2.0 compliant #239

Merged
merged 16 commits into from
May 24, 2020
Merged

$ref support - OAS 2.0 compliant #239

merged 16 commits into from
May 24, 2020

Conversation

Eomm
Copy link
Member

@Eomm Eomm commented May 6, 2020

Opening this PR as POC:
when the user uses $ref, swagger-ui will do an HTTP GET to retrieve the external JSON.
So, to let this works, we need to:

  • expose many routes as many fastify.addSchema() the user does
  • the user should use $id: hello because if he uses $id: http://example.com.. the GET will go to http://example.com and it won't work!!
  • write a good tutorial 😄

I will rebase it to #238
This plugin could benefit from the changes too: SkeLLLa/fastify-oas#30

Writing this feature I will check the OAS 2.0 feature supported to add those who are missing like #161

My personal achievements would be to fix all the "invalid schema error" that doesn't let me generate a Postman collection form the generated swagger

@mcollina
Copy link
Member

mcollina commented May 7, 2020

I think we should really try to resolve all the internal references if possible. I think it provides a better user experience, mainly because somebody can download all the files and store them in version control if they need to auto-generate a client.

Would it be opposed to that @Eomm? I think it's just a matter to resolve all the $ref

@Eomm
Copy link
Member Author

Eomm commented May 7, 2020

I would resolve URI like $id: 'myawesome:schema' and not URL $id: 'http://my-server/schema'

One single file output seems mandatory to me also to let users generate docs or other useful stuff

@mcollina
Copy link
Member

mcollina commented May 8, 2020

I would resolve URI like $id: 'myawesome:schema' and not URL $id: 'http://my-server/schema'

Agreed.

One single file output seems mandatory to me also to let users generate docs or other useful stuff

Agreed.

routes.js Outdated
// throw err
// }
// })
const allSchemas = fastify.getSchemas()
Copy link
Contributor

Choose a reason for hiding this comment

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

@Eomm, do you know if in v3 this method was reworked, so it will return all schemes, even those what were registered in child scopes with fastify.register?
Cause in fastify v2 I had to go through all child scopes in order to get all of them like https://github.com/SkeLLLa/fastify-oas/blob/master/lib/openapi/index.js#L5

Copy link
Member Author

Choose a reason for hiding this comment

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

nope, it is encapsulated and tested here:
https://github.com/fastify/fastify/blob/master/test/schema-feature.test.js#L505

I agree that we could think a nicer solution 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I think looping through children is quiet fine, unless you have schemes with same names. But in fastify-oas module there were no such issues, so it can be used.

Copy link
Member Author

Choose a reason for hiding this comment

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

In think the onRegister hook could let us to avoid to use the symbol, I will give it a try

Copy link
Contributor

@SkeLLLa SkeLLLa May 10, 2020

Choose a reason for hiding this comment

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

One more idea - is to add addSchema hook to Fastify. That will allow plugins to get schemes when they are registered. And, for example, instead of resolving them we could put schema to swagger definitions when addSchema is called and use $ref inside swagger route to that definition.

@Eomm Eomm mentioned this pull request May 16, 2020
4 tasks
dynamic.js Outdated Show resolved Hide resolved
@Eomm
Copy link
Member Author

Eomm commented May 18, 2020

This pr is harder than I thought... now I understand why json-schema would like to standardize with OAS

@mcollina
Copy link
Member

This pr is harder than I thought... now I understand why json-schema would like to standardize with OAS

Why?

@Eomm
Copy link
Member Author

Eomm commented May 19, 2020

I wanted to replace $ref to point to local definitions, as suggested by the RFC.

But the swagger config support $ref to definitions only for the body;

headers, params and query parameter do not support $ref out of the box so I must adopt another approach

@Eomm
Copy link
Member Author

Eomm commented May 20, 2020

Update: missing more tests, than it should be ready to review

@Eomm
Copy link
Member Author

Eomm commented May 21, 2020

I think this PR is ready for feedback

I would like a check by @SkeLLLa too if you could be so kind to give me your though

@Eomm Eomm marked this pull request as ready for review May 21, 2020 21:44
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

good work!!

dynamic.js Show resolved Hide resolved
instance.addHook('onReady', (done) => {
const allSchemas = instance.getSchemas()
for (const schemaId of Object.keys(allSchemas)) {
if (!sharedSchemasMap.has(schemaId)) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we detect if the id is being overridden by something else?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe here we can add instance prefix to schema name if it exists? In some cases I guess it could solve some schema conflicts.

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem here is that onRegister is receiving for all the register so in this case:

  • register root - addSchema 1
    • child 1 registered - addSchema 2
      • subchild 2 registered - addSchema 3

For subchild 2 i will read schema 1, 2, 3
For child 1 schema 1 and 2
For root only schema 1

So it will be quite normal to have duplicate and I think it is ok since the addSchema already has a check to verify duplicate $id

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean slightly different case.

  • root - schema-1
    • child1 (prefix '/news') - item
    • child2 (prefix '/comments') - item

So if item schemas are registered in isolated scopes they will know now nothing about each other. However when you gather all them in shared schema map they will have the same id.

Copy link
Member

Choose a reason for hiding this comment

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

Could you leave a TODO comment in here? Or open an issue? I think we might want/be able to have a way to get all schemas defined at the current level so you do not get them all here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree and I will describe the case 👍

routes.js Outdated Show resolved Hide resolved
test/json-schema.test.js Outdated Show resolved Hide resolved
Copy link
Contributor

@SkeLLLa SkeLLLa left a comment

Choose a reason for hiding this comment

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

@Eomm
Besides that thing with potential schema conflicts and Matteo's comments, LGTM.

instance.addHook('onReady', (done) => {
const allSchemas = instance.getSchemas()
for (const schemaId of Object.keys(allSchemas)) {
if (!sharedSchemasMap.has(schemaId)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe here we can add instance prefix to schema name if it exists? In some cases I guess it could solve some schema conflicts.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Contributor

@SkeLLLa SkeLLLa left a comment

Choose a reason for hiding this comment

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

LGTM. I guess it will support most of common cases, for others it's possible to modify behaviour without any breaking changes needed.

@Eomm
Copy link
Member Author

Eomm commented May 24, 2020

I must add some other commit.. I forgot the response 😱
I'm working on it now

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

@Eomm Eomm merged commit 1bd5447 into fastify:master May 24, 2020
@Eomm Eomm deleted the fastify-v3 branch May 24, 2020 18:50
@ahgentil
Copy link

I have my schemas in multiple files.
One of them has common stuff like:

  $id: 'http://example.com/',
  definitions: {
    uuid: {
      type: 'string',
      pattern: '^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$'
    }
...

Then inside other plugins' schemas I reference that:

id: { $ref: 'http://example.com/#/definitions/uuid' },

So, I just noticed that it blows up here:

if (jsonSchema.type && jsonSchema.properties) {

because, as the function name states localRefResolve(), this ends up returning undefined:

fastify-swagger/dynamic.js

Lines 392 to 396 in 32d4f42

if (jsonSchema.$ref) {
// $ref is in the format: #/definitions/<resolved definition>/<optional fragment>
const localReference = jsonSchema.$ref.split('/')[2]
return localRefResolve(externalSchemas[localReference], externalSchemas)
}

Should this $ref resolver be able to resolve any $ref?
The uuid definition is in the externalSchemas param.

@Eomm
Copy link
Member Author

Eomm commented May 28, 2020

Could you open an issue with a complete example?
I get the point but it is time spending recreate it

@ahgentil
Copy link

voilá #252

@Eomm Eomm mentioned this pull request Jun 1, 2020
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants