Skip to content

Conversation

@jannyHou
Copy link
Contributor

@jannyHou jannyHou commented Feb 23, 2018

connect to: #753

Description

This is the 3rd PR of prototype PR: #916 (comment)

connect to issue: #753

Review

  • the first commit just copy paste openapi-v2 package for comparison, please review code change from the 2nd commit
  • commit test: add tests contains all the test files(30) update
  • other files update are separated into each commit, usually one or a few file's change

Checklist

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • Related API Documentation was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in packages/example-* were updated

@jannyHou jannyHou force-pushed the oas3/openapi-v3 branch 3 times, most recently from 44e1d86 to 4b4dc5c Compare February 23, 2018 23:12
};

/**
* Shortcut parameter decorators
Copy link
Contributor Author

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 :)

@jannyHou jannyHou force-pushed the oas3/openapi-v3 branch 10 times, most recently from fb494d9 to 5de8b9b Compare February 24, 2018 21:08
}

ParameterDecoratorFactory.createDecorator<ParameterObject>(
OAI3.PARAMETERS_KEY,
Copy link
Contributor Author

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

* Define a parameter of "integer" type that's read from the query string.
* Usage: @param.query.string('paramName')
*
*
Copy link
Contributor Author

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', () => {
Copy link
Contributor Author

@jannyHou jannyHou Feb 24, 2018

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

@jannyHou jannyHou force-pushed the oas3/openapi-v3 branch 2 times, most recently from d55758d to b111a2c Compare February 24, 2018 21:55
]);
});
// tslint:disable-next-line:max-line-length
it('throws an error if @param is used at both method and parameter level', () => {
Copy link
Contributor Author

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', () => {
Copy link
Contributor Author

@jannyHou jannyHou Feb 24, 2018

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', () => {
Copy link
Contributor Author

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', () => {
Copy link
Contributor Author

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

});
});

it('does not produce nested definitions', () => {
Copy link
Contributor Author

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

Copy link
Contributor

@b-admike b-admike left a 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 './';
Copy link
Contributor

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);
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 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?

Copy link
Contributor Author

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).

Copy link
Contributor

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be optional?

Copy link
Contributor Author

@jannyHou jannyHou Feb 27, 2018

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 || {});

Copy link
Contributor

@shimks shimks Feb 27, 2018

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

Copy link
Contributor Author

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 './';
Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Contributor Author

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 './';
Copy link
Contributor

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 './';
Copy link
Contributor

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

Copy link
Contributor Author

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);
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see comment #1046 (comment)

class MyController {
@post('/users/{location}')
async createUser(
@param.query.string('type') type: string,
Copy link
Contributor

@shimks shimks Feb 27, 2018

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?

Copy link
Contributor Author

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:

Copy link
Contributor

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

Copy link
Contributor

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?

@jannyHou
Copy link
Contributor Author

@shimks thanks for looking into the long PR! feedbacks applied in 3cf42d1, and answered other comments

@jannyHou
Copy link
Contributor Author

And @bajtos 's feedbacks applied in 7b05087

@jannyHou jannyHou changed the title feat: adding openapi-v3 [WIP]feat: adding openapi-v3 Feb 27, 2018
@jannyHou jannyHou changed the title [WIP]feat: adding openapi-v3 feat: adding openapi-v3 Feb 27, 2018
Copy link
Contributor

@shimks shimks left a 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?

@jannyHou
Copy link
Contributor Author

@shimks

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})

@shimks
Copy link
Contributor

shimks commented Feb 28, 2018

I took a quick look at content-type headers and it seems like a request can set only one content-type in the header. For reference: https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.14.17. I remember you asking @raymondfeng about this, what was his response? Setting multiple content-types is fine in OpenAPI so nevermind.

I'm going to list out the cases that requestBody may need:

  • single content-type
    • schema automatically generated with application/json
      • with or without optional properties
    • schema automatically generated with explicitly set content-type
      • with or without optional properties
    • schema set by user

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.

Copy link
Contributor

@shimks shimks left a 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', () => {
Copy link
Contributor

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?

Copy link
Contributor

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

Copy link
Contributor Author

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!

Copy link
Contributor Author

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'},
Copy link
Contributor

@shimks shimks Feb 28, 2018

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@shimks I didn't implement shortcut functions for @requestBody.array and also @param.array is because I have different plans for them, and I think it takes discussion time, created an issue for it:

#1064

export const array = function(
name: string,
source: ParameterLocation,
itemSpec: SchemaObject | ReferenceObject,
Copy link
Contributor

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.

Copy link
Contributor Author

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 {
Copy link
Contributor

@shimks shimks Feb 28, 2018

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

Copy link
Contributor Author

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

@jannyHou
Copy link
Contributor Author

@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.

@jannyHou jannyHou merged commit 45a4bdf into master Mar 1, 2018
@jannyHou jannyHou deleted the oas3/openapi-v3 branch March 1, 2018 15:20
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.

6 participants