-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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] Switch to OpenAPI v3 for spec and routing #753
Comments
This does not make sense to me. IMO, the REST component is pretty much tied to OpenAPI. What will remain inside
What are the benefits of this? Are they worth the additional complexity? What if we told people that want to use a different spec format to write their own REST-like component, instead of telling them to configure our empty REST server with a custom router? |
@bajtos The RestServer would be the harness for handling http/s/2 configuration, as well as spinning up whatever's given by the providers for things like Sequence. I was mostly hoping to avoid the need to reinvent the decorators for every single custom implementation of REST (i.e. |
@kjdelisle I am afraid this still does not make much sense to me. For example, it's known that Express's router gets slower with the number of routes registered. So even if we wanted to support Express, then IMO we should still build our own router implementation that can be mounted on an express app. See strongloop/strong-remoting#282 and our internal https://github.com/strongloop-internal/scrum-loopback/issues/712 for more details. I am concerned that in order to support different http servers (express, koa, etc.), we will need to implement (and maintain!) many different flavors of routers, which I don't think we have bandwidth to do. IMO, if we decide to go down the route of supporting 3rd party http server frameworks, then we should pick only one. I personally prefer Koa, because it has been designed for Anyhow, it has been some time since I was working on this part of loopback-next, therefore it's entirely possible that I am missing important constraints that make my arguments moot. I am not against moving some parts of the current REST implementation elsewhere, I just want to avoid situation where we move too much stuff to openapi-related packages. I think the most important concern to keep in mind is versioning and breaking changes. In my experience, breaking changes will occur more frequently in the routing part, while the typescript typedefs for OpenAPI Spec interfaces will remain mostly constant. I think decorators will be somewhere in the middle. From this point of view, the routing logic should live in its own package. It makes sense to me to implement an openapispec router (our I am not sure what's the best place where to put decorators - at one hand, they are quite coupled to the router implementation, but at the other hand, we want components contributing controllers to be able to depend on those decorators only. So maybe another package is needed? IDK. |
@kjdelisle @bajtos in my original proposal I suggested to extract the decorators to For the routing logic...I don't have strong opinion at this moment, how about I finish the decorator extraction and v3 upgrading first, I believe these two are already a big change :) |
As a summary, now we have several plans:
There might be better name for each package, the plans above is about how we organize different functionalities into one or more packages. Considering the "versioning and breaking changes" I would probably choose the first one. Appreciate more opinions on it :) |
I like "Plan 1" most 👍
I am not sure where to put decorators like As for codegen, I'd prefer to put that in a standalone package, because it's almost completely unrelated to specgen code. Anyways, I think these are details that we will figure out once we start splitting |
I would like to extract those path decorators into a specgen package, later on we can add model decorators(model --> openapi3 schema, cc @kjdelisle @shimks ) in that package too
openapi-spec contains some sugar types like
So I think there is still a value to keep the openapi-spec package. |
Status of this issue:
Updated the content...a package name with |
I would like to see more opinions the
And you can specify multiple For different content types, do they usually share the same schema or not?
Never mind, not a concern anymore. People can provide |
See #940 (comment) |
In our MVP release, we should be focusing on API servers using JSON payloads in both request and responses. Is there a way how to defer the design work needed to support non-JSON request bodies and/or multiple accepted content types after MVP? One of your code example shown above looks perfect to me: async function create(
@requestBody(requestBodySpec: RequestBodySpec) order: Order
):void {} Under the hood, In the future, we can modify // assuming the schema is always the same
async function create(
@requestBody(requestBodySpec, {
required: true,
acceptedTypes: ['application/json', 'application/xml']
})
data: Order
);
// assuming different schemas
async function create(
@requestBody({
'application/json': { /* spec */ },
'application/xml': { /* spec */ },
)
data: JsonOrder | XmlOrder
); |
@bajtos I make Raymond suggests to try blog-driven development in the retro meeting, and I am writing a blog draft that contains the UX of new decorator |
The new request format in OpenAPI 3.0.0 raises a big http handler(parser) problem: parameters in For example: class MyController {
function hi(foo: string, bar: string, baz: number) {
// say hi
}
}
// The corresponding swagger spec
swaggerOperationSpec: {
parameters: [
{foo: {in: 'formData', ...fooSpec}},
{bar: {in: 'query', ...barSpec}},
{baz: {in: 'formData', ...bazSpec}},
]
}
// The corresponding openapi spec
openapiOperationSpec: {
requestBody: {
type: 'object',
properties: {
foo: {type: 'string', ...fooSpec},
baz: {type: 'number', ...bazSpec}
}
},
parameters: [
{bar: {in: 'query', ...barSpec}}
]
} TL:DR The openapi 3 operation spec is not consistent with a function's arguments any more. I had a discussion with @virkt25 (thank you for the idea), we take an approach to preserve the argument position's info by binding requestBody(requestBody: RequestBodySpec) {
ParameterDecoratorFactory.createDecorator<RequestBodyObject>(
// used for retrieving requestBody spec,
// separate from parameter spec
OAS3_REQUEST_BODY_KEY,
requestBodySpec as RequestBodyObject,
)(target, member, index);
ParameterDecoratorFactory.createDecorator<RequestBodyArgument>(
// used for retrieving argument spec
// shared with parameter metadata
OPERATION_ARGUMENTS_KEY,
{in: 'body', spec: requestBodySpec as RequestBodyObject},
)(target, member, index);
}
param(requestBody: RequestBodySpec) {
ParameterDecoratorFactory.createDecorator<PamameterObject>(
// used for retrieving parameter spec,
// separate from requestBody spec
OAS3_PARAMETERS_KEY,
parameterSpec,
)(target, member, index);
ParameterDecoratorFactory.createDecorator<PamameterObject>(
// used for retrieving argument spec
// shared with requestBody metadata
OPERATION_ARGUMENTS_KEY,
parameterSpec
)(target, member, index);
} The This solves the problem for bottom-up approach, but not for the top-down ones: server.api()
server.route()
@api()
class MyController {
} They still need to provide two api/route spec:
option 1A solution would be build a helper function that generates option 2Add a new property in the // something like:
['body.foo', 'bar', 'body.baz'] To retrieve the metadata bound to the common key The only additional work for a user is providing this property when define routes in the top-down styles above. Personally I like this one. option 3Another solution could be only allow one
@strongloop/loopback-next Any thought? This could be a high level design. |
Great summary @jannyHou! I've been thinking about this some more and I think we can do without the double decorators ... which would avoid the impact on memory performance. Here's what I'm thinking ...
|
@jannyHou @virkt25 great job! Your proposed approach looks reasonable to me. Regarding the position of body argument in controller method signature in top-down approach: I personally prefer your third option, where the position of the body argument is hardcoded. I'd rather avoid changing the position depending on whether the argument is required or optional, because a small change in spec (required: true -> false) can break the implementation. I expect that the body argument will be more often required than optional, in which case I think we should always put it at the first position. Alternatively, we can introduce an extension field allowing users to specify the position of the body argument. For example: openapiOperationSpec: {
requestBody: {
type: 'object',
properties: {/*...*/},
'x-parameter-index': -1 // 0 means first, 1 means second, -1 means last
},
parameters: [
{bar: {in: 'query', ...barSpec}}
]
} When this extension field is not provided, we can default to our hard-coded convention (first or last, depending how we decide.)
// something like:
['body.foo', 'bar', 'body.baz'] I actually like this proposal too, although I think this configuration should stay inside OpenAPISpec for consistency with the
I think we should follow what OpenAPI Spec encourages, at least for the MVP scope. Let's discuss whether we want to allow formData-like access to individual properties from body payload in a new (non-MVP) GitHub issue. |
Thank you all for replying so quickly and such valuable feedbacks!
For option 1, exactly 👍 and now I kind of understand why Kyu said use a shared key in the first place. While if we take option 2, I think 3 keys are still needed. My concern for option 1, 2 is, if we only allow one whole
If we allow multiple The critical limitation for option 3 is dropping the Now I tend to agree with @bajtos 's suggestion: taking option 3 but using an extension property to specify the position:
Actually we also discussed the extension property yesterday, a concern is we may not want to inject too many loopback specific properties into the openapi file, but BTW, when making choices, there are several things worth taking into account:
|
+1 to use For longer term, I would like to propose that we turn |
Prototype PR: #916 The PRs for this story are split into 4, please see comment #916 (comment) for details. The 1st and 2nd PRs are merged. I am working on the 3rd one. |
This has slipped again. Moving to March 2018 |
Hi everyone. What URL will Loopback use to expose the OpenAPI v3 document? It would be great if it was consistent with the Java MicroProfile OpenAPI model, which exposes the YAML OAS3 document at |
@arash01 Currently, the OpenAPI spec is hosted at the endpoint |
Closing as completed. |
Story
As a LoopBack4 user, I would like to use v3 spec because it's more awesome than v2 spec.
Acceptance Criteria
@loopback/openapi-spec
The text was updated successfully, but these errors were encountered: