-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: adding openapi-v3 #1046
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: adding openapi-v3 #1046
Conversation
44e1d86 to
4b4dc5c
Compare
| }; | ||
|
|
||
| /** | ||
| * Shortcut parameter decorators |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The git compare is messy for the shorcuts. Suggest look at the file directly :)
fb494d9 to
5de8b9b
Compare
| } | ||
|
|
||
| ParameterDecoratorFactory.createDecorator<ParameterObject>( | ||
| OAI3.PARAMETERS_KEY, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ fixed in commit "feat: add tests":
OAI3 ---> OAI3Keys
5de8b9b to
b9e5c23
Compare
| * Define a parameter of "integer" type that's read from the query string. | ||
| * Usage: @param.query.string('paramName') | ||
| * | ||
| * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit for lint is squashed into "test: add tests" :(
| ]); | ||
| }); | ||
|
|
||
| it('can define multiple parameters by arguments', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moves(and modified) to integration test:
integration/operation-spec.test.ts
d55758d to
b111a2c
Compare
| ]); | ||
| }); | ||
| // tslint:disable-next-line:max-line-length | ||
| it('throws an error if @param is used at both method and parameter level', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed due to deprecating method level decorator.
| expect(actualSpec.paths['/greet']['get']).to.eql(expectedSpec); | ||
| }); | ||
|
|
||
| it('infers simple body parameter type', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replaced by new tests in request-body.test.ts
| expect(actualSpec.paths['/greet']['get']).to.eql(expectedSpec); | ||
| }); | ||
|
|
||
| it('infers complex body parameter type', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replaced by new tests in request-body.test.ts
| expect(actualSpec.paths['/greet']['get']).to.eql(expectedSpec); | ||
| }); | ||
|
|
||
| it('infers complex body parameter schema into the controller spec', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replaced by new tests in request-body.test.ts
b111a2c to
bfa6537
Compare
| }); | ||
| }); | ||
|
|
||
| it('does not produce nested definitions', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to test/integration/controller-spec.test.ts
b-admike
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on Miroslav's comments, but LGTM otherwise :)
| SchemaObject, | ||
| } from '@loopback/openapi-v3-types'; | ||
| import {MetadataInspector, ParameterDecoratorFactory} from '@loopback/context'; | ||
| import {getSchemaForParam} from './'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import should come from the file itself (generate-schema)
| ) { | ||
| paramSpec = paramSpec || {}; | ||
| // Get the design time method parameter metadata | ||
| const methodSig = MetadataInspector.getDesignTypeForMethod(target, member); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a remnant of having method level @param decorator. Can we rework the code here so that we don't need leverage getDesignTypeForMethod here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shimks interestingly, I had the same thought, but I am afraid that's the only inspector method we can use, and it makes perfect sense.
If you look into all inspector methods in metadata/src/inspector.ts, this is the only function that returns design type information of a method, including its parameter types, return types(we can use it for generating response object in the future).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, but is ResponseObject something we want to generate from @param or @requestBody in the future? I guess that's up for discussion way down the road, but I guess it's ok to leave it in for now since it's not a huge issue anyway.
| return function(target: Object, member: string | symbol, index: number) { | ||
| paramSpec = paramSpec || {}; | ||
| // Get the design time method parameter metadata | ||
| const methodSig = MetadataInspector.getDesignTypeForMethod(target, member); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copying because github ignored my comment:
I think this is a remnant of having method level @param decorator. Can we rework the code here so that we don't need leverage getDesignTypeForMethod here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment #1046 (comment)
| */ | ||
| export function getSchemaForParam( | ||
| type: Function, | ||
| schema: SchemaObject, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, see the method that calls it in parameter-decorator.ts:
paramSpec.schema = getSchemaForParam(paramType, paramSpec.schema || {});There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that could be handled within the function itself can't it? with something like this:
export function getSchemaForparam(
type: Function,
schema?: SchemaObject,
) {
schema = schema || {};
// .. rest of the code here
}or
export function getSchemaForParam(
type: Function,
schema: SchemaObject = {}
) {}we wouldn't need to hand in an empty object every time we call the function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
| // This file is licensed under the MIT License. | ||
| // License text available at https://opensource.org/licenses/MIT | ||
|
|
||
| import {ControllerSpec} from './'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import from the file itself please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason or is it a documented convention to export it from the file itself?
For the ones exported in src/index.ts, I am used to import from root level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Importing from index.ts can cause a circular dependency for some reason
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh! that's important. changing all of them
| } from '@loopback/openapi-v3-types'; | ||
| import {getJsonSchema} from '@loopback/repository-json-schema'; | ||
| import {OAI3Keys} from './keys'; | ||
| import {jsonToSchemaObject} from './'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import from the file itself please
| ReferenceObject, | ||
| } from '@loopback/openapi-v3-types'; | ||
| import {MetadataInspector, ParameterDecoratorFactory} from '@loopback/context'; | ||
| import {getSchemaForRequestBody} from './'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import from the file itself please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👍
I delete exporting generate-schema.ts in index.ts, the functions in it are only for internal use
| requestBodySpec.content = {'application/json': {}}; | ||
|
|
||
| // Get the design time method parameter metadata | ||
| const methodSig = MetadataInspector.getDesignTypeForMethod(target, member); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as in parameter decorator, can we replace this with getParameterMetadata or something that works like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment #1046 (comment)
5858055 to
4e5607d
Compare
| class MyController { | ||
| @post('/users/{location}') | ||
| async createUser( | ||
| @param.query.string('type') type: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a side note, do we ever use the string given as an argument (type in this case) in any of our codebase anymore? Is it possible for us to get rid of it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shimks sorry could you elaborate more about this? not sure I understand...:speak_no_evil:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh, I think I'm overloading the scope of this task, ignore my last comment :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shimks Did you mean to say @param.query('type') is good enough as we can infer string from the TS type?
4e5607d to
3cf42d1
Compare
3cf42d1 to
290a8b6
Compare
shimks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. It might be out of scope, but what do you think about allowing @requestBody to take in a string instead of an object to specify the content-type?
Good point, this goes back to my old question: what if people specify different schemas(even slightly different) for multiple content-types? While I agree with you that the object style looks redundant when shares the same schema among multiple content-types: @requestBody({'application/json': {}, 'application/text': {}})So I think a better way is creating a shortcut, e.g. // not a valid code, just a concept shows the type for each parameter
@requestBody.multipleContents(
contents: string[],
schemaSpec:? SchemaObject ,
otherProperties: {description?: string, required: boolean}) |
7b32ad3 to
fb79382
Compare
|
I'm going to list out the cases that
Now as to the cases with multiple possible content-types, I think we just leave that out of the task and create another issue for it since we're already heavily overloaded on this one anyways. If users really want to provide multiple possibly content-types for the body, they should handle it themselves anyway (imo, unless this is commonly done; it's a discussion in the other potential issue). As for the second case I've listed out (custom content-type), I think we should just allow users to provide a string of the content-type if you agree to forget about automatic schema generations for multiple content-types in this story. |
shimks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple more comments 🔨
| assertRequestBodySpec({type: 'object'}, MyController); | ||
| }); | ||
|
|
||
| it('infers array', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Show we force the users to use @requestBody.array for the cases with array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also want to ask the same question with @param.array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a valid use case ^
Show we force the users to use @requestBody.array for the cases with array?
Yes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shimks I am summarizing some features/improve we want for @requestBody, sorry I don't want to keep adding features in this PR since it blocks the 4th PR(upgrade the whole project), after land this one people can work on the improvement and upgrade in parallel.
| @post('/greeting') | ||
| greet( | ||
| @requestBody.array( | ||
| {type: 'string'}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should allow the parameters to take in primitive wrapper functions. This is so that users can write @requestBody.array(String, optSpec) instead of @requestBody.array({type: 'string'}, optSpec). We can leverage getSchemaForParam to do this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| export const array = function( | ||
| name: string, | ||
| source: ParameterLocation, | ||
| itemSpec: SchemaObject | ReferenceObject, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should accept a string ('number') or a function (Number). Make sure to fix the jsdoc so that it reflects the change if you want to go through with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ the code is correct, jsdoc is out-of-date
| * ```ts | ||
| * export class MyController { | ||
| * @get('/greet') | ||
| * greet(@param.array('names', 'query', 'string') names: string[]): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguments are wrong here at the moment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is the old document ^ I am changing it to reflect the current code
011f84c to
e87c670
Compare
|
@shimks Thank you for the thoughtful feedbacks! Really appreciate them. And I totally agree with you that we should simplify some use cases, summarize them in issue #1064. Sorry I could not address them in this PR since 1. IMO how to simplify the use cases still needs discussion 2. this PR blocks the whole upgrade, and those improvements can be made in parallel with the upgrade PR. Let's keep discussion in PR#1064 and create a new PR to improve it. |
f53e919 to
af60c07
Compare
af60c07 to
0739ffc
Compare
connect to: #753
Description
This is the 3rd PR of prototype PR: #916 (comment)
connect to issue: #753
Review
openapi-v2package for comparison, please review code change from the 2nd committest: add testscontains all the test files(30) updateChecklist
npm testpasses on your machineAffected artifact templates inpackages/cliwere updatedAffected example projects inpackages/example-*were updated