Skip to content

Commit

Permalink
feat!: add pagination support
Browse files Browse the repository at this point in the history
  • Loading branch information
Mairu committed Aug 27, 2022
1 parent c40b3a6 commit 79e58ce
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 9 deletions.
3 changes: 2 additions & 1 deletion example/openapi-v3/definitions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Example for openapi v3
* - using definitions option of service.docs to define all needed definitions
* - service with pagination
* - using swagger ui with a custom indexFile
* - add parameter to find (globally)
* - set specific values and sub values for a operation
Expand All @@ -12,7 +13,7 @@ const memory = require('feathers-memory');
const swagger = require('../../lib');

module.exports = (app) => {
const messageService = memory();
const messageService = memory({ paginate: { default: 10, max: 50 } });
const uiIndexFile = path.join(__dirname, 'docs.html');

messageService.docs = {
Expand Down
1 change: 1 addition & 0 deletions example/swagger-v2/customTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = (app) => {
};

app.configure(swagger({
openApiVersion: 2,
prefix: 'v2/custom-tags/',
docsJsonPath: '/v2/custom-tags.json',
ui: swagger.swaggerUI({ docsPath: '/v2/custom-tags' }),
Expand Down
1 change: 1 addition & 0 deletions example/swagger-v2/definitionWithCustomizedSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = (app) => {
};

app.configure(swagger({
openApiVersion: 2,
prefix: 'v2/definition-with-customized-update/',
docsJsonPath: '/v2/definition-with-customized-update.json',
ui: swagger.swaggerUI({ docsPath: '/v2/definition-with-customized-update' }),
Expand Down
4 changes: 3 additions & 1 deletion example/swagger-v2/definitions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Example for swagger v2
* - using definitions option of service.docs to define all needed definitions
* - service with pagination
* - using swagger ui with a custom indexFile
* - add parameter to find (globally)
* - set specific values and sub values for a operation
Expand All @@ -11,7 +12,7 @@ const memory = require('feathers-memory');
const swagger = require('../../lib');

module.exports = (app) => {
const messageService = memory();
const messageService = memory({ paginate: { default: 10 } });
const uiIndexFile = path.join(__dirname, 'docs.html');

messageService.docs = {
Expand Down Expand Up @@ -54,6 +55,7 @@ module.exports = (app) => {
};

app.configure(swagger({
openApiVersion: 2,
prefix: 'v2/definitions/',
docsJsonPath: '/v2/definitions.json',
ui: swagger.swaggerUI({ docsPath: '/v2/definitions', indexFile: uiIndexFile }),
Expand Down
1 change: 1 addition & 0 deletions example/swagger-v2/security.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = (app) => {
};

app.configure(swagger({
openApiVersion: 3,
prefix: 'v2/security/',
docsJsonPath: '/v2/security.json',
ui: swagger.swaggerUI({ docsPath: '/v2/security' }),
Expand Down
4 changes: 4 additions & 0 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ exports.assignWithSet = (object, ...sources) => {
}
return object;
};

exports.isPaginationEnabled = function (service) {
return service.options && service.options.paginate && service.options.paginate.default;
};
8 changes: 5 additions & 3 deletions lib/openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class OpenApiGenerator {
/* istanbul ignore next: abstract method */
getOperationDefaults () {} // has to be implemented in sub class
/* istanbul ignore next: abstract method */
applyDefinitionsToSpecs (service, model) {} // has to implemented in sub class
applyDefinitionsToSpecs (service, model, modelName, refs) {} // has to implemented in sub class
/* istanbul ignore next: abstract method */
getPathParameterSpec (name) {} // has to implemented in sub class
/* istanbul ignore next: abstract method */
Expand Down Expand Up @@ -171,14 +171,16 @@ class OpenApiGenerator {
const multiOperations = doc.multi || this.config.defaults.multi || determineMultiOperations(service);
const idSeparator = (service.options && service.options.idSeparator) || ',';

this.applyDefinitionsToSpecs(service, model, modelName);
const refs = this.getOperationsRefs(service, model);

this.applyDefinitionsToSpecs(service, model, modelName, refs);

const defaultArgumentObject = {
idName,
idType,
...operationArgs,
tags,
refs: this.getOperationsRefs(service, model),
refs,
specs: this.specs,
service,
config: this.config,
Expand Down
21 changes: 19 additions & 2 deletions lib/v2/generator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const AbstractApiGenerator = require('../openapi');
const { isPaginationEnabled } = require('../helpers');
const utils = require('../utils');

function idPathParameters (idName, idType, description) {
Expand Down Expand Up @@ -49,7 +50,7 @@ class OpenApiV2Generator extends AbstractApiGenerator {
getOperationsRefs (service, model) {
const modelList = `${model}_list`;
const refs = {
findResponse: modelList,
findResponse: isPaginationEnabled(service) ? `${model}_pagination` : modelList,
getResponse: model,
createRequest: model,
createResponse: model,
Expand Down Expand Up @@ -377,7 +378,7 @@ class OpenApiV2Generator extends AbstractApiGenerator {
};
}

applyDefinitionsToSpecs (service, model, modelName) {
applyDefinitionsToSpecs (service, model, modelName, refs) {
if (typeof service.docs.definition !== 'undefined') {
this.specs.definitions[model] = service.docs.definition;
this.specs.definitions[`${model}_list`] = {
Expand All @@ -395,6 +396,22 @@ class OpenApiV2Generator extends AbstractApiGenerator {
this.config.defaults.schemasGenerator(service, model, modelName, this.specs.definitions)
);
}
if (isPaginationEnabled(service) &&
refs.findResponse === `${model}_pagination` &&
typeof this.specs.definitions[`${model}_list`] !== 'undefined' &&
typeof this.specs.definitions[`${model}_pagination`] === 'undefined'
) {
this.specs.definitions[`${model}_pagination`] = {
title: `${modelName} pagination result`,
type: 'object',
properties: {
total: { type: 'integer' },
limit: { type: 'integer' },
skip: { type: 'integer' },
data: { $ref: `#/definitions/${model}_list` }
}
};
}
}

getPathParameterSpec (name) {
Expand Down
21 changes: 19 additions & 2 deletions lib/v3/generator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const AbstractApiGenerator = require('../openapi');
const { isPaginationEnabled } = require('../helpers');
const utils = require('../utils');

function addDefinitionToSchemas (schemas, definition, model, modelName) {
Expand Down Expand Up @@ -93,7 +94,7 @@ class OpenApiV3Generator extends AbstractApiGenerator {
getOperationsRefs (service, model) {
const modelList = `${model}_list`;
const refs = {
findResponse: modelList,
findResponse: isPaginationEnabled(service) ? `${model}_pagination` : modelList,
getResponse: model,
createRequest: model,
createResponse: model,
Expand Down Expand Up @@ -414,7 +415,7 @@ class OpenApiV3Generator extends AbstractApiGenerator {
};
}

applyDefinitionsToSpecs (service, model, modelName) {
applyDefinitionsToSpecs (service, model, modelName, refs) {
if (typeof service.docs.definition !== 'undefined') {
addDefinitionToSchemas(this.specs.components.schemas, service.docs.definition, model, modelName);
}
Expand All @@ -433,6 +434,22 @@ class OpenApiV3Generator extends AbstractApiGenerator {
this.config.defaults.schemasGenerator(service, model, modelName, this.specs.components.schemas)
);
}
if (isPaginationEnabled(service) &&
refs.findResponse === `${model}_pagination` &&
typeof this.specs.components.schemas[`${model}_list`] !== 'undefined' &&
typeof this.specs.components.schemas[`${model}_pagination`] === 'undefined'
) {
this.specs.components.schemas[`${model}_pagination`] = {
title: `${modelName} pagination result`,
type: 'object',
properties: {
total: { type: 'integer' },
limit: { type: 'integer' },
skip: { type: 'integer' },
data: { $ref: `#/components/schemas/${model}_list` }
}
};
}
}

getPathParameterSpec (name) {
Expand Down
110 changes: 110 additions & 0 deletions test/v2/expected-memory-spec-pagination-find.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"info": {
"title": "swagger generator v2 tests",
"version": "1.0.0"
},
"paths": {
"/message": {
"get": {
"parameters": [
{
"description": "Number of results to return",
"in": "query",
"name": "$limit",
"type": "integer"
},
{
"description": "Number of results to skip",
"in": "query",
"name": "$skip",
"type": "integer"
},
{
"description": "Property to sort results",
"in": "query",
"name": "$sort",
"type": "string"
}
],
"responses": {
"200": {
"description": "success",
"schema": {
"$ref": "#/definitions/message_pagination"
}
},
"401": {
"description": "not authenticated"
},
"500": {
"description": "general error"
}
},
"description": "Retrieves a list of all resources from the service.",
"summary": "",
"tags": [
"message"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"security": []
}
}
},
"definitions": {
"message": {
"type": "object",
"properties": {
"content": {
"type": "string"
}
}
},
"message_list": {
"title": "message list",
"type": "array",
"items": {
"$ref": "#/definitions/message"
}
},
"message_pagination": {
"title": "message pagination result",
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"skip": {
"type": "integer"
},
"data": {
"$ref": "#/definitions/message_list"
}
}
}
},
"swagger": "2.0",
"schemes": [
"http"
],
"tags": [
{
"name": "message",
"description": "A message service"
}
],
"basePath": "/",
"consumes": [
"application/json"
],
"produces": [
"application/json"
]
}
27 changes: 27 additions & 0 deletions test/v2/generator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,33 @@ describe('swagger v2 generator', function () {
expect(specs).to.deep.equal(require('./expected-memory-spec-multi-only.json'));
});

it('should generate expected specification for service with enabled paginationof memory service', () => {
const specs = {};
const gen = new OpenApi2Generator(specs, swaggerOptions);
const service = memory({ paginate: { default: 10 } });
service.docs = {
definition: {
type: 'object',
properties: {
content: {
type: 'string'
}
}
},
operations: {
get: false,
create: false,
update: false,
patch: false,
remove: false
}
};

gen.addService(service, 'message');

expect(specs).to.deep.equal(require('./expected-memory-spec-pagination-find.json'));
});

// contains only tests that are v2 specific, tests that test "abstract" generator are part of v3 tests
describe('swaggerOptions', function () {
let service;
Expand Down
Loading

0 comments on commit 79e58ce

Please sign in to comment.