2021-10-14 10:54:57 -04:00

171 lines
4.7 KiB
JavaScript

'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
* @returns {string}
*/
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
*
* @returns {object } Swagger path params object
*/
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' },
};
});
};
/**
*
* @param {string} prefix - The route prefix
* @param {string} path - The route path
*
* @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}
*/
const getPaths = ({ routeInfo, attributes, tag }) => {
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();
const hasPathParams = route.path.includes('/:');
const pathWithPrefix = routeInfo.prefix
? getPathWithPrefix(routeInfo.prefix, route.path)
: route.path;
const routePath = hasPathParams ? parsePathWithVariables(pathWithPrefix) : pathWithPrefix;
const { responses } = buildApiResponses(attributes, route, isListOfEntities);
_.set(acc.paths, `${routePath}.${methodVerb}.responses`, responses);
_.set(acc.paths, `${routePath}.${methodVerb}.tags`, [_.upperFirst(tag)]);
if (isListOfEntities) {
_.set(acc.paths, `${routePath}.${methodVerb}.parameters`, queryParams);
}
if (hasPathParams) {
const pathParams = getPathParams(route.path);
_.set(acc.paths, `${routePath}.${methodVerb}.parameters`, pathParams);
}
if (methodVerb === 'post' || methodVerb === 'put') {
const { requestBody } = buildApiRequests(attributes, route);
_.set(acc.paths, `${routePath}.${methodVerb}.requestBody`, requestBody);
}
return acc;
},
{ paths: {} }
);
return paths;
};
/**
* @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
*
* @returns {object}
*/
module.exports = api => {
if (!api.ctNames.length && api.getter === 'plugin') {
// Set arbitrary attributes
const attributes = { foo: { type: 'string' } };
const routeInfo = strapi.plugin(api.name).routes['admin'];
const apiInfo = {
routeInfo,
attributes,
tag: api.name,
};
return getPaths(apiInfo);
}
// An api could have multiple contentTypes
for (const contentTypeName of api.ctNames) {
// Get the attributes found on the api's contentType
const uid = `${api.getter}::${api.name}.${contentTypeName}`;
const ct = strapi.contentType(uid);
const attributes = ct.attributes;
// Get the routes for the current api
const routeInfo =
api.getter === 'plugin'
? strapi.plugin(api.name).routes['content-api']
: strapi.api[api.name].routes[contentTypeName];
// 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}`;
const apiInfo = {
routeInfo,
attributes,
tag,
};
return getPaths(apiInfo);
}
};