mirror of
https://github.com/strapi/strapi.git
synced 2025-07-04 15:42:03 +00:00
187 lines
5.2 KiB
JavaScript
187 lines
5.2 KiB
JavaScript
'use strict';
|
|
|
|
const _ = require('lodash');
|
|
const pathToRegexp = require('path-to-regexp');
|
|
|
|
const pascalCase = require('./utils/pascal-case');
|
|
const queryParams = require('./utils/query-params');
|
|
const loopContentTypeNames = require('./utils/loop-content-type-names');
|
|
const getApiResponses = require('./utils/get-api-responses');
|
|
const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
|
|
|
|
/**
|
|
* @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: 'number' },
|
|
};
|
|
});
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @param {string} prefix - The prefix found on the routes object
|
|
* @param {string} route - The current route
|
|
* @property {string} route.path - The current route's path
|
|
* @property {object} route.config - The current route's config object
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
const getPathWithPrefix = (prefix, route) => {
|
|
// When the prefix is set on the routes and
|
|
// the current route is not trying to remove it
|
|
if (prefix && !_.has(route.config, 'prefix')) {
|
|
// Add the prefix to the path
|
|
return prefix.concat(route.path);
|
|
}
|
|
|
|
// Otherwise just return path
|
|
return route.path;
|
|
};
|
|
/**
|
|
* @description Gets all paths based on routes
|
|
*
|
|
* @param {object} apiInfo
|
|
* @property {object} apiInfo.routeInfo - The api routes object
|
|
* @property {string} apiInfo.uniqueName - Content type name | Api name + Content type name
|
|
* @property {object} apiInfo.contentTypeInfo - The info object found on content type schemas
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
const getPaths = ({ routeInfo, uniqueName, contentTypeInfo, kind }) => {
|
|
// Get the routes for the current content type
|
|
const contentTypeRoutes = routeInfo.routes.filter((route) => {
|
|
return (
|
|
route.path.includes(contentTypeInfo.pluralName) ||
|
|
route.path.includes(contentTypeInfo.singularName)
|
|
);
|
|
});
|
|
|
|
const paths = contentTypeRoutes.reduce((acc, route) => {
|
|
// TODO: Find a more reliable way to determine list of entities vs a single entity
|
|
const isListOfEntities = hasFindMethod(route.handler);
|
|
const isLocalizationPath = isLocalizedPath(route.path);
|
|
const methodVerb = route.method.toLowerCase();
|
|
const hasPathParams = route.path.includes('/:');
|
|
const pathWithPrefix = getPathWithPrefix(routeInfo.prefix, route);
|
|
const routePath = hasPathParams ? parsePathWithVariables(pathWithPrefix) : pathWithPrefix;
|
|
|
|
const { responses } = getApiResponses({
|
|
uniqueName,
|
|
route,
|
|
isListOfEntities: kind !== 'singleType' && isListOfEntities,
|
|
isLocalizationPath,
|
|
});
|
|
|
|
const swaggerConfig = {
|
|
responses,
|
|
tags: [_.upperFirst(uniqueName)],
|
|
parameters: [],
|
|
operationId: `${methodVerb}${routePath}`,
|
|
};
|
|
|
|
if (isListOfEntities) {
|
|
swaggerConfig.parameters.push(...queryParams);
|
|
}
|
|
|
|
if (hasPathParams) {
|
|
const pathParams = getPathParams(route.path);
|
|
swaggerConfig.parameters.push(...pathParams);
|
|
}
|
|
|
|
if (['post', 'put'].includes(methodVerb)) {
|
|
const refName = isLocalizationPath ? 'LocalizationRequest' : 'Request';
|
|
const requestBody = {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
$ref: `#/components/schemas/${pascalCase(uniqueName)}${refName}`,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
swaggerConfig.requestBody = requestBody;
|
|
}
|
|
|
|
_.set(acc, `${routePath}.${methodVerb}`, swaggerConfig);
|
|
|
|
return acc;
|
|
}, {});
|
|
|
|
return paths;
|
|
};
|
|
|
|
/**
|
|
* @decription Gets all open api paths object for a given content type
|
|
*
|
|
* @param {object} apiInfo
|
|
*
|
|
* @returns {object} Open API paths
|
|
*/
|
|
const getAllPathsForContentType = (apiInfo) => {
|
|
let paths = {};
|
|
|
|
const pathsObject = getPaths(apiInfo);
|
|
|
|
paths = {
|
|
...paths,
|
|
...pathsObject,
|
|
};
|
|
|
|
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}
|
|
*/
|
|
const buildApiEndpointPath = (api) => {
|
|
// A reusable loop for building paths and component schemas
|
|
// Uses the api param to build a new set of params for each content type
|
|
// Passes these new params to the function provided
|
|
return loopContentTypeNames(api, getAllPathsForContentType);
|
|
};
|
|
|
|
module.exports = buildApiEndpointPath;
|