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

OpenAPI 3 #219

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open

OpenAPI 3 #219

wants to merge 25 commits into from

Conversation

maksbotan
Copy link

Hi,

I've started porting swagger2 to openapi3, I will track my progress here.

I am following the spec at https://swagger.io/specification/, version 3.0.3.

Currently it works for simple cases:

λ> (decs, resp) = runDeclare (declareResponse "application/json" (Proxy @Day)) mempty
λ> resps = mempty & responses.at 200 ?~ Inline resp :: Responses
λ> op = mempty & responses .~ resps :: Operation
λ> swag = mempty & components.schemas <>~ decs & paths.at "/day" ?~ pathItem :: Swagger
λ> BSL.putStrLn $ encode swag

{"openapi":"3.0.3","info":{"version":"","title":""},"paths":{"/day":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day"}}},"description":""}}}}},"components":{"schemas":{"Day":{"example":"2016-07-22","format":"date","type":"string"}}}}

And it renders as expected on Swagger Editor:

image

Of course, servant-swagger will have to be updated as well... See haskell-servant/servant-swagger#99

Fixes #105.

@maksbotan
Copy link
Author

So, test suite passes, doctests not yet, I will fix them later.

OpenAPI 3 pet store example roundtrips without losses, so that's something.

Of course, Generic generators should be updated to use new features like oneOf.

Any preliminary thoughts?

@sir4ur0n
Copy link

I am not qualified to review this PR, but I just wanted to let you know this work on OpenAPI is valuable (I understand this is a first step before adding support for sum types oneOf; a good step nonetheless) and I thank you/appreciate your work on this PR 🙇

@maksbotan
Copy link
Author

Update: I've implemented schema generation for (all) sum types, using SumEncoding configuration from aeson.

Example using Light type from tests:

data Light
  = NoLight
  | LightFreq Double
  | LightColor Color
  | LightWaveLength { waveLength :: Double }
  deriving (Generic)
Light:
  type: object
  oneOf:
    - required:
        - tag
        - contents
      type: object
      properties:
        tag:
          type: string
          enum:
            - NoLight
        contents:
          example: []
          items: {}
          maxItems: 0
          type: array
    - required:
        - tag
        - contents
      type: object
      properties:
        tag:
          type: string
          enum:
            - LightFreq
        contents:
          format: double
          type: number
    - required:
        - tag
        - contents
      type: object
      properties:
        tag:
          type: string
          enum:
            - LightColor
        contents:
          $ref: '#/components/schemas/Color'
    - required:
        - waveLength
        - tag
      type: object
      properties:
        tag:
          type: string
          enum:
            - LightWaveLength
        waveLength:
          format: double
          type: number

Validation tests pass.

There are some TODOs remaining thoigh.

@maksbotan
Copy link
Author

Yay, CI passes!

@sir4ur0n
Copy link

sir4ur0n commented Jul 5, 2020

Hey, just wanted to let you know: as soon as you feel like it works (even if not polished/documented/etc. yet), feel free to ping me, and we will give it a try on my project. We have several sum types, which we currently can't display in Swagger, so we would love to modestly contribute by beta-testing this and provide feedback (or contribute on what we understand, e.g. the documentation).

Cheers and good luck!

@maksbotan
Copy link
Author

Hi @sir4ur0n.

This mostly works now, I'm going to add more tests for new functionality (sum types) and recheck everything by the spec.

However, keep in mind that OpenAPI 3 changed a lot of things, so a migration is certainly needed. And if you are using servant-swagger: it won't work with this PR because of those changes.

But anyway, you are more than welcome to try this code :)

@sir4ur0n
Copy link

sir4ur0n commented Jul 6, 2020

However, keep in mind that OpenAPI 3 changed a lot of things, so a migration is certainly needed. And if you are using servant-swagger: it won't work with this PR because of those changes.

This is exactly the piece I didn't have in mind 😅

I'm not familiar with the boundaries between this library and servant-swagger: will compilation break? Or runtime? Maybe adapting servant-swagger wouldn't be such a big deal?

It would be much smoother if we can test with servant-swagger, but I guess we could give it a manual shot by displaying the JSON in another UI 🤔

@maksbotan
Copy link
Author

Sadly, compilation most likely will break. You can try though :)

Adapting servant-swagger is on my list, I'll go to it after this PR is in ready state.

@maksbotan maksbotan force-pushed the maksbotan/openapi3 branch 2 times, most recently from f205201 to ff87915 Compare July 10, 2020 13:06
@maksbotan maksbotan force-pushed the maksbotan/openapi3 branch from ff87915 to 23caa64 Compare July 26, 2020 10:19
@maksbotan maksbotan marked this pull request as ready for review July 26, 2020 10:24
@maksbotan
Copy link
Author

Hi @fizruk, @phadej, @fisx and everyone interested!

I've marked this PR as ready for review. I tried to clean up the history with a rebase.

Here are some thoughts.

  1. This is a massive change, just as the change in the spec itself. I suppose that it would make sense to split it into a separate openapi3 package.

  2. servant-swagger is migrated in OpenAPI 3 haskell-servant/servant-swagger#121, perhaps it should be called servant-openapi3 as well.

  3. OpenAPI 3 got simplified with respect to parameters, so SwaggerKind and stuff are no longer required. Maybe there are some relics of it left though.

  4. I'm ready to hear your suggestions on the code and work together to make this change happen.

Copy link

@roosemberth roosemberth left a comment

Choose a reason for hiding this comment

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

Thank you for the amazing work!

I reviewed your types agains the OpenAPI v3.0.3 specification, in particular Data.Swagger.Internal and some of the spec tests (thank you). Unfortunatelly I don't know optics, so I can't poke there.

Cheers!

, _componentsParameters :: Definitions Param
, _componentsExamples :: Definitions Example
, _componentsRequestBodies :: Definitions RequestBody
, _componentsHeader :: Definitions Header

Choose a reason for hiding this comment

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

Suggested change
, _componentsHeader :: Definitions Header
, _componentsHeaders :: Definitions Header

@@ -248,41 +257,41 @@ data Operation = Operation
, _operationSummary :: Maybe Text

Choose a reason for hiding this comment

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

-- For maximum readability in the swagger-ui, this field SHOULD be less than 120 characters.

I couldn't find this note in the OpenAPI specification v3.0.3.

@@ -330,159 +457,130 @@ data Param = Param
-- Otherwise, the property MAY be included and its default value is @False@.

Choose a reason for hiding this comment

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

Unfortunatelly Github won't allow me to put a comment on an unmodified line, but comment on line 452 should be updated from GFM to Commonmark.

-- Furthermore, if referencing a schema that contains an example,
-- the examples value SHALL override the example provided by the schema.
, _paramExamples :: InsOrdHashMap Text (Referenced Example)
} deriving (Eq, Show, Generic, Data, Typeable)

Choose a reason for hiding this comment

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

Missing _paramAllowReserved. It would also be useful to change the order of the fields in this class to match that in the OpenAPI specification v3.0.3, but it's not necessary.


, _schemaMaxProperties :: Maybe Integer
, _schemaMinProperties :: Maybe Integer

, _schemaParamSchema :: ParamSchema 'SwaggerKindSchema
, _schemaParamSchema :: ParamSchema

Choose a reason for hiding this comment

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

I could not find this property in the OpenAPI v3.0.3 specification. Can you please add a comment where it is coming from or what its purpose is?

Copy link
Member

Choose a reason for hiding this comment

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

This is a subset of Schema that is used for both parameters and request/response body.


, _headerParamSchema :: ParamSchema ('SwaggerKindNormal Header)
, _headerSchema :: Maybe (Referenced Schema)
} deriving (Eq, Show, Generic, Data, Typeable)

Choose a reason for hiding this comment

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

From the OpenAPI v3.0.3 specification.

The Header Object follows the structure of the Parameter Object with the following changes:

name MUST NOT be specified, it is given in the corresponding headers map.
in MUST NOT be specified, it is implicitly in header.
All traits that are affected by the location MUST be applicable to a location of header (for example, style).

The following properties seem to be missing:

  • required
  • deprecated
  • allowEmptyValue
  • style
  • explode
  • example
  • examples

A comment shall be added if these properties are not supported.

@maksbotan
Copy link
Author

Thanks for your review, @roosemberth! I will look at this :)

@fizruk
Copy link
Member

fizruk commented Aug 3, 2020

Hi @fizruk, @phadej, @fisx and everyone interested!

I've marked this PR as ready for review. I tried to clean up the history with a rebase.

@maksbotan Thank you for this amazing work!

Here are some thoughts.

  1. This is a massive change, just as the change in the spec itself. I suppose that it would make sense to split it into a separate openapi3 package.

Yes, I think this is the most sensible thing to do, since it should be way easier to maintain that way! Feel free to just create a copy of this repo named openapi3.

  1. servant-swagger is migrated in haskell-servant/servant-swagger#121, perhaps it should be called servant-openapi3 as well.

Yes, same goes for servant-openapi3.

  1. OpenAPI 3 got simplified with respect to parameters, so SwaggerKind and stuff are no longer required. Maybe there are some relics of it left though.

That sounds great! Less code is better 👍

  1. I'm ready to hear your suggestions on the code and work together to make this change happen.

One suggestion is to also adapt servant-swagger-ui, since it is the trio of packages I find extremely useful (and I think others do as well) 😊

Another suggestion might be to update doctests to use YAML, as I see that it is what people use often, and also it is easier to read in documentation, I think.

At lastly, perhaps a list of key changes since Swagger 2.0 (or a link to such a list) somewhere in the obvious place might be great for users to switch over :) Especially, I believe we should emphasize that sum types are now encoded (more) faithfully in OpenAPI 3.0 than in Swagger 2.0, which should be a great motivator to move to the new package. We could also add references openapi3, servant-openapi3 and servant-openapi3-ui to the front pages of swagger2 packages, for people to find new packages faster :)

@maksbotan
Copy link
Author

Thanks for the response!

I will make new packages in the biocad/ org.

Are you going to review this PR first or I can just proceed with copying?

I will prepare a list of changes, as you suggested, once the repo is a new place.

@roosemberth
Copy link

IMHO: I think it would be a good idea to finish reviewing the PR here (maybe change the target branch?) and then fork-off the merged branch.

@fizruk
Copy link
Member

fizruk commented Aug 3, 2020

Thanks for the response!

I will make new packages in the biocad/ org.

Perfect!

Are you going to review this PR first or I can just proceed with copying?

I glanced over it, it looks good to me, so I think you can proceed. You can take the suggestion of @roosemberth to complete merge here (in a separate branch), or proceed in any other way you want :)

I will prepare a list of changes, as you suggested, once the repo is a new place.

Great, thank you! 😊

@maksbotan
Copy link
Author

Thanks @roosemberth for your review!

@maksbotan
Copy link
Author

So, the new home is at https://github.com/biocad/openapi3/ 🎉

I hope I did not mess with copyrights and acknowledgments. I've also added @fizruk as a maintainer to that repo.

By all means, check it out and file issues :)

Copy link

@roosemberth roosemberth left a comment

Choose a reason for hiding this comment

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

The types seems to match the OpenAPI v3.0.3 spec. Thank you!

@sir4ur0n
Copy link

Hi, I'm asking here pretty much the same questions I asked there:

Hi, so what's the status of this PR? Will this development land in swagger2 or remain in a dedicated openapi3 package?

If new package:

  • Shouldn't swagger2 readme/documentation state that it's a deprecated package, and users should migrate to openapi3? I don't see the point of maintaining both packages (except for a few months for retro-compatibility reasons)

If it lands in this package:

  • Are there remaining steps to merge it?

Thank you!

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.

OpenAPI3
4 participants