Skip to content

Commit

Permalink
feat!: add multi support for create
Browse files Browse the repository at this point in the history
  • Loading branch information
Mairu committed Aug 26, 2022
1 parent 3a6f111 commit c40b3a6
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 131 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ __Options:__
- `docsPath` (*optional*, default: `/docs`) - Path where Swagger UI is served
- `indexFile` - (*optional*) - Path to a file which is served instead of default Swagger UI index file
- `getSwaggerInitializerScript({ docsPath, docsJsonPath, specs })` (*optional*) - Function to create the script that will be served as swagger-initializer.js instead of the default one from Swagger UI
- The function takes one options and should return a string that contains valid JS
- The function takes one options object and should return a string that contains valid JS

### `service.id`

Expand Down
2 changes: 1 addition & 1 deletion example/openapi-v3/multi.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module.exports = (app) => {

messageService.docs = {
description: 'A service to send and receive messages',
multi: ['patch', 'remove'],
multi: ['patch', 'remove', 'create'],
refs: {
sortParameter: 'messages_sort_filter'
}
Expand Down
36 changes: 5 additions & 31 deletions lib/openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function ignoreService (service, path, includeConfig, ignoreConfig) {

function determineMultiOperations (service) {
if (service.options && service.options.multi) {
return ['remove', 'update', 'patch'].filter((operation) => _.isFunction(service[operation]));
return ['remove', 'update', 'patch', 'create'].filter((operation) => _.isFunction(service[operation]));
}

return [];
Expand Down Expand Up @@ -96,6 +96,8 @@ class OpenApiGenerator {
applyDefinitionsToSpecs (service, model) {} // has to implemented in sub class
/* istanbul ignore next: abstract method */
getPathParameterSpec (name) {} // has to implemented in sub class
/* istanbul ignore next: abstract method */
getOperationsRefs (service, model) {} // has to implemented in sub class
getOperationSpecDefaults () { return undefined; }

getOperationArgs ({ service, apiPath, version }) {
Expand Down Expand Up @@ -126,35 +128,6 @@ class OpenApiGenerator {
};
}

getOperationsRefs (service, model) {
const modelList = `${model}_list`;
const refs = {
findResponse: modelList,
getResponse: model,
createRequest: model,
createResponse: model,
updateRequest: model,
updateResponse: model,
updateMultiRequest: modelList,
updateMultiResponse: modelList,
patchRequest: model,
patchResponse: model,
patchMultiRequest: model,
patchMultiResponse: modelList,
removeResponse: model,
removeMultiResponse: modelList,
filterParameter: model,
sortParameter: ''
};
if (typeof this.config.defaults.getOperationsRefs === 'function') {
Object.assign(refs, this.config.defaults.getOperationsRefs(model, service));
}
if (typeof service.docs.refs === 'object') {
Object.assign(refs, service.docs.refs);
}
return refs;
}

addService (service, path) {
if (ignoreService(service, path, this.config.include, this.config.ignore)) {
return;
Expand Down Expand Up @@ -208,7 +181,8 @@ class OpenApiGenerator {
refs: this.getOperationsRefs(service, model),
specs: this.specs,
service,
config: this.config
config: this.config,
multiOperations
};

const addMethodToSpecs = (pathObj, path, methodIdName, method, httpMethod, customMethod = false) => {
Expand Down
105 changes: 57 additions & 48 deletions lib/v2/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ function idPathParameters (idName, idType, description) {
return params;
}

function jsonSchemaRef (ref) {
if (typeof ref === 'object' && ref.refs) {
throw new Error('Multiple refs defined as object are only supported with openApiVersion 3');
}

return {
$ref: `#/definitions/${ref}`
};
}

class OpenApiV2Generator extends AbstractApiGenerator {
getDefaultSpecs () {
return {
Expand All @@ -36,6 +46,37 @@ class OpenApiV2Generator extends AbstractApiGenerator {
};
}

getOperationsRefs (service, model) {
const modelList = `${model}_list`;
const refs = {
findResponse: modelList,
getResponse: model,
createRequest: model,
createResponse: model,
createMultiRequest: model,
createMultiResponse: model,
updateRequest: model,
updateResponse: model,
updateMultiRequest: modelList,
updateMultiResponse: modelList,
patchRequest: model,
patchResponse: model,
patchMultiRequest: model,
patchMultiResponse: modelList,
removeResponse: model,
removeMultiResponse: modelList,
filterParameter: model,
sortParameter: ''
};
if (typeof this.config.defaults.getOperationsRefs === 'function') {
Object.assign(refs, this.config.defaults.getOperationsRefs(model, service));
}
if (typeof service.docs.refs === 'object') {
Object.assign(refs, service.docs.refs);
}
return refs;
}

getOperationDefaults () {
return {
find ({ tags, security, securities, specs, refs }) {
Expand Down Expand Up @@ -63,9 +104,7 @@ class OpenApiV2Generator extends AbstractApiGenerator {
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.findResponse}`
}
schema: jsonSchemaRef(refs.findResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -87,9 +126,7 @@ class OpenApiV2Generator extends AbstractApiGenerator {
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.getResponse}`
}
schema: jsonSchemaRef(refs.getResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -114,16 +151,12 @@ class OpenApiV2Generator extends AbstractApiGenerator {
in: 'body',
name: 'body',
required: true,
schema: {
$ref: `#/definitions/${refs.createRequest}`
}
schema: jsonSchemaRef(refs.createRequest)
}],
responses: {
201: {
description: 'created',
schema: {
$ref: `#/definitions/${refs.createResponse}`
}
schema: jsonSchemaRef(refs.createResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -145,16 +178,12 @@ class OpenApiV2Generator extends AbstractApiGenerator {
in: 'body',
name: 'body',
required: true,
schema: {
$ref: `#/definitions/${refs.updateRequest}`
}
schema: jsonSchemaRef(refs.updateRequest)
}]),
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.updateResponse}`
}
schema: jsonSchemaRef(refs.updateResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -179,16 +208,12 @@ class OpenApiV2Generator extends AbstractApiGenerator {
in: 'body',
name: 'body',
required: true,
schema: {
$ref: `#/definitions/${refs.updateMultiRequest}`
}
schema: jsonSchemaRef(refs.updateMultiRequest)
}],
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.updateMultiResponse}`
}
schema: jsonSchemaRef(refs.updateMultiResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -210,16 +235,12 @@ class OpenApiV2Generator extends AbstractApiGenerator {
in: 'body',
name: 'body',
required: true,
schema: {
$ref: `#/definitions/${refs.patchRequest}`
}
schema: jsonSchemaRef(refs.patchRequest)
}]),
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.patchResponse}`
}
schema: jsonSchemaRef(refs.patchResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -245,17 +266,13 @@ class OpenApiV2Generator extends AbstractApiGenerator {
in: 'body',
name: 'body',
required: true,
schema: {
$ref: `#/definitions/${refs.patchMultiRequest}`
}
schema: jsonSchemaRef(refs.patchMultiRequest)
}
],
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.patchMultiResponse}`
}
schema: jsonSchemaRef(refs.patchMultiResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -277,9 +294,7 @@ class OpenApiV2Generator extends AbstractApiGenerator {
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.removeResponse}`
}
schema: jsonSchemaRef(refs.removeResponse)
},
401: {
description: 'not authenticated'
Expand All @@ -304,9 +319,7 @@ class OpenApiV2Generator extends AbstractApiGenerator {
responses: {
200: {
description: 'success',
schema: {
$ref: `#/definitions/${refs.removeMultiResponse}`
}
schema: jsonSchemaRef(refs.removeMultiResponse)
},
401: {
description: 'not authenticated'
Expand Down Expand Up @@ -349,18 +362,14 @@ class OpenApiV2Generator extends AbstractApiGenerator {
in: 'body',
name: 'body',
required: true,
schema: {
$ref: `#/definitions/${refs[refRequestName]}`
}
schema: jsonSchemaRef(refs[refRequestName])
});
}
}

const refResponseName = `${method}Response`;
if (refs[refResponseName]) {
customDoc.responses['200'].schema = {
$ref: `#/definitions/${refs[refResponseName]}`
};
customDoc.responses['200'].schema = jsonSchemaRef(refs[refResponseName]);
}

return customDoc;
Expand Down
53 changes: 49 additions & 4 deletions lib/v3/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ function filterParameter (refs) {
}

function jsonSchemaRef (ref) {
if (typeof ref === 'object' && ref.refs) {
const { refs, type, ...rest } = ref;

return {
'application/json': {
schema: {
[type]: refs.map(innerRef => ({ $ref: `#/components/schemas/${innerRef}` })),
...rest
}
}
};
}

return {
'application/json': {
schema: {
Expand Down Expand Up @@ -60,7 +73,7 @@ class OpenApiV3Generator extends AbstractApiGenerator {
components: {
schemas: {}
},
openapi: '3.0.2',
openapi: '3.0.3',
tags: [],
info: {}
};
Expand All @@ -77,6 +90,37 @@ class OpenApiV3Generator extends AbstractApiGenerator {
};
}

getOperationsRefs (service, model) {
const modelList = `${model}_list`;
const refs = {
findResponse: modelList,
getResponse: model,
createRequest: model,
createResponse: model,
createMultiRequest: { refs: [model, modelList], type: 'oneOf' },
createMultiResponse: { refs: [model, modelList], type: 'oneOf' },
updateRequest: model,
updateResponse: model,
updateMultiRequest: modelList,
updateMultiResponse: modelList,
patchRequest: model,
patchResponse: model,
patchMultiRequest: model,
patchMultiResponse: modelList,
removeResponse: model,
removeMultiResponse: modelList,
filterParameter: model,
sortParameter: ''
};
if (typeof this.config.defaults.getOperationsRefs === 'function') {
Object.assign(refs, this.config.defaults.getOperationsRefs(model, service));
}
if (typeof service.docs.refs === 'object') {
Object.assign(refs, service.docs.refs);
}
return refs;
}

getOperationDefaults () {
return {
find ({ tags, security, securities, refs }) {
Expand Down Expand Up @@ -147,18 +191,19 @@ class OpenApiV3Generator extends AbstractApiGenerator {
security: utils.security('get', securities, security)
};
},
create ({ tags, security, securities, refs }) {
create ({ tags, security, securities, refs, multiOperations }) {
const multi = multiOperations.includes('create');
return {
tags,
description: 'Creates a new resource with data.',
requestBody: {
required: true,
content: jsonSchemaRef(refs.createRequest)
content: multi ? jsonSchemaRef(refs.createMultiRequest) : jsonSchemaRef(refs.createRequest)
},
responses: {
201: {
description: 'created',
content: jsonSchemaRef(refs.createResponse)
content: multi ? jsonSchemaRef(refs.createMultiResponse) : jsonSchemaRef(refs.createResponse)
},
401: {
description: 'not authenticated'
Expand Down
2 changes: 1 addition & 1 deletion test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe('feathers-swagger', () => {

it('serves json specification at docJsonPath', () => {
return axios.get('http://localhost:6776/docs.json').then(({ data: docs }) => {
expect(docs.openapi).to.equal('3.0.2');
expect(docs.openapi).to.equal('3.0.3');
expect(docs.info.title).to.equal('A test');
expect(docs.info.description).to.equal('A description');
expect(docs.paths['/messages']).to.exist;
Expand Down
Loading

0 comments on commit c40b3a6

Please sign in to comment.