2021-09-02 11:25:24 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const _ = require('lodash');
|
|
|
|
const pathToRegexp = require('path-to-regexp');
|
|
|
|
|
|
|
|
const queryParams = require('../query-params');
|
|
|
|
const buildApiRequests = require('./build-api-requests');
|
|
|
|
const buildApiResponses = require('./build-api-responses');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description Parses a route with ':variable'
|
|
|
|
*
|
|
|
|
* @param {string} routePath - The route's path property
|
2021-10-13 12:39:34 -04:00
|
|
|
* @returns {string}
|
2021-09-02 11:25:24 +02:00
|
|
|
*/
|
|
|
|
const parsePathWithVariables = routePath => {
|
|
|
|
return pathToRegexp
|
|
|
|
.parse(routePath)
|
|
|
|
.map(token => {
|
|
|
|
if (_.isObject(token)) {
|
|
|
|
return token.prefix + '{' + token.name + '}';
|
|
|
|
}
|
|
|
|
|
|
|
|
return token;
|
|
|
|
})
|
|
|
|
.join('');
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description Builds the required object for a path parameter
|
|
|
|
*
|
|
|
|
* @param {string} routePath - The route's path property
|
|
|
|
*
|
2021-10-13 12:39:34 -04:00
|
|
|
* @returns {object } Swagger path params object
|
2021-09-02 11:25:24 +02:00
|
|
|
*/
|
|
|
|
const getPathParams = routePath => {
|
|
|
|
return pathToRegexp
|
|
|
|
.parse(routePath)
|
|
|
|
.filter(token => _.isObject(token))
|
|
|
|
.map(param => {
|
|
|
|
return {
|
|
|
|
name: param.name,
|
|
|
|
in: 'path',
|
|
|
|
description: '',
|
|
|
|
deprecated: false,
|
|
|
|
required: true,
|
|
|
|
schema: { type: 'string' },
|
|
|
|
};
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2021-10-13 12:39:34 -04:00
|
|
|
* @param {string} prefix - The route prefix
|
|
|
|
* @param {string} path - The route path
|
2021-09-02 11:25:24 +02:00
|
|
|
*
|
2021-10-13 12:39:34 -04:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
const getPathWithPrefix = (prefix, path) => {
|
|
|
|
if (path.includes('localizations')) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.endsWith('/')) {
|
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
return prefix.concat(path);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {object} api - Information about the api
|
|
|
|
* @param {object} api.routeInfo - The routes for a given api or plugin
|
|
|
|
* @param {string} api.routeInfo.prefix - The prefix for all routes
|
|
|
|
* @param {array} api.routeInfo.routes - The routes for the current api
|
|
|
|
* @param {object} api.attributes - The attributes for a given api or plugin
|
|
|
|
* @param {string} api.tag - A descriptor for OpenAPI
|
|
|
|
*
|
|
|
|
* @returns {object}
|
2021-09-02 11:25:24 +02:00
|
|
|
*/
|
2021-10-13 09:58:48 -04:00
|
|
|
const getPaths = ({ routeInfo, attributes, tag }) => {
|
2021-10-19 09:39:10 +02:00
|
|
|
const paths = routeInfo.routes.reduce((acc, route) => {
|
|
|
|
// TODO: Find a more reliable way to determine list of entities vs a single entity
|
|
|
|
const isListOfEntities = route.handler.split('.').pop() === 'find';
|
|
|
|
const methodVerb = route.method.toLowerCase();
|
2021-09-02 11:25:24 +02:00
|
|
|
|
2021-10-19 09:39:10 +02:00
|
|
|
const hasPathParams = route.path.includes('/:');
|
|
|
|
const pathWithPrefix = routeInfo.prefix
|
|
|
|
? getPathWithPrefix(routeInfo.prefix, route.path)
|
|
|
|
: route.path;
|
|
|
|
const routePath = hasPathParams ? parsePathWithVariables(pathWithPrefix) : pathWithPrefix;
|
2021-09-02 11:25:24 +02:00
|
|
|
|
2021-10-19 09:39:10 +02:00
|
|
|
const { responses } = buildApiResponses(attributes, route, isListOfEntities);
|
2021-09-02 11:25:24 +02:00
|
|
|
|
2021-10-19 09:39:10 +02:00
|
|
|
const swaggerConfig = {
|
|
|
|
responses,
|
|
|
|
tags: [_.upperFirst(tag)],
|
|
|
|
parameters: [],
|
|
|
|
requestBody: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
if (isListOfEntities) {
|
|
|
|
swaggerConfig.parameters.push(...queryParams);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasPathParams) {
|
|
|
|
const pathParams = getPathParams(route.path);
|
|
|
|
swaggerConfig.parameters.push(...pathParams);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (['post', 'put'].includes(methodVerb)) {
|
|
|
|
const { requestBody } = buildApiRequests(attributes, route);
|
|
|
|
|
|
|
|
swaggerConfig.requestBody = requestBody;
|
|
|
|
}
|
|
|
|
|
|
|
|
_.set(acc, `${routePath}.${methodVerb}`, swaggerConfig);
|
2021-09-02 11:25:24 +02:00
|
|
|
|
2021-10-19 09:39:10 +02:00
|
|
|
return acc;
|
|
|
|
}, {});
|
2021-09-02 11:25:24 +02:00
|
|
|
|
2021-10-19 09:39:10 +02:00
|
|
|
return { paths };
|
2021-09-02 11:25:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description - Builds the Swagger paths object for each api
|
|
|
|
*
|
|
|
|
* @param {object} api - Information about the current api
|
|
|
|
* @property {string} api.name - The name of the api
|
|
|
|
* @property {string} api.getter - The getter for the api (api | plugin)
|
|
|
|
* @property {array} api.ctNames - The name of all contentTypes found on the api
|
|
|
|
*
|
2021-10-13 12:39:34 -04:00
|
|
|
* @returns {object}
|
2021-09-02 11:25:24 +02:00
|
|
|
*/
|
|
|
|
module.exports = api => {
|
|
|
|
if (!api.ctNames.length && api.getter === 'plugin') {
|
|
|
|
// Set arbitrary attributes
|
|
|
|
const attributes = { foo: { type: 'string' } };
|
2021-10-13 09:58:48 -04:00
|
|
|
const routeInfo = strapi.plugin(api.name).routes['admin'];
|
|
|
|
|
|
|
|
const apiInfo = {
|
|
|
|
routeInfo,
|
|
|
|
attributes,
|
|
|
|
tag: api.name,
|
|
|
|
};
|
2021-10-13 12:39:34 -04:00
|
|
|
|
2021-10-13 09:58:48 -04:00
|
|
|
return getPaths(apiInfo);
|
2021-09-02 11:25:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// An api could have multiple contentTypes
|
|
|
|
for (const contentTypeName of api.ctNames) {
|
|
|
|
// Get the attributes found on the api's contentType
|
2021-10-13 09:58:48 -04:00
|
|
|
const uid = `${api.getter}::${api.name}.${contentTypeName}`;
|
|
|
|
const ct = strapi.contentType(uid);
|
|
|
|
const attributes = ct.attributes;
|
2021-09-02 11:25:24 +02:00
|
|
|
|
|
|
|
// Get the routes for the current api
|
2021-10-13 09:58:48 -04:00
|
|
|
const routeInfo =
|
2021-09-02 11:25:24 +02:00
|
|
|
api.getter === 'plugin'
|
2021-10-13 09:58:48 -04:00
|
|
|
? strapi.plugin(api.name).routes['content-api']
|
|
|
|
: strapi.api[api.name].routes[contentTypeName];
|
2021-09-02 11:25:24 +02:00
|
|
|
|
|
|
|
// Parse an identifier for OpenAPI tag if the api name and contentType name don't match
|
|
|
|
const tag = api.name === contentTypeName ? api.name : `${api.name} - ${contentTypeName}`;
|
2021-10-13 09:58:48 -04:00
|
|
|
const apiInfo = {
|
|
|
|
routeInfo,
|
|
|
|
attributes,
|
|
|
|
tag,
|
|
|
|
};
|
2021-10-13 12:39:34 -04:00
|
|
|
|
2021-10-13 09:58:48 -04:00
|
|
|
return getPaths(apiInfo);
|
2021-09-02 11:25:24 +02:00
|
|
|
}
|
|
|
|
};
|