186 lines
5.1 KiB
JavaScript
Raw Normal View History

2021-09-02 11:25:24 +02:00
'use strict';
const _ = require('lodash');
const pathToRegexp = require('path-to-regexp');
2022-03-17 16:54:37 +01:00
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');
2022-05-05 12:32:22 +02:00
const { hasFindMethod, isLocalizedPath } = require('./utils/routes');
2021-09-02 11:25:24 +02:00
/**
* @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
*/
2022-08-08 23:33:39 +02:00
const parsePathWithVariables = (routePath) => {
2021-09-02 11:25:24 +02:00
return pathToRegexp
.parse(routePath)
2022-08-08 23:33:39 +02:00
.map((token) => {
2021-09-02 11:25:24 +02:00
if (_.isObject(token)) {
2022-08-08 15:50:34 +02:00
return `${token.prefix}{${token.name}}`;
2021-09-02 11:25:24 +02:00
}
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
*/
2022-08-08 23:33:39 +02:00
const getPathParams = (routePath) => {
2021-09-02 11:25:24 +02:00
return pathToRegexp
.parse(routePath)
2022-08-08 23:33:39 +02:00
.filter((token) => _.isObject(token))
.map((param) => {
2021-09-02 11:25:24 +02:00
return {
name: param.name,
in: 'path',
description: '',
deprecated: false,
required: true,
schema: { type: 'string' },
};
});
};
/**
*
2022-04-11 11:21:05 +02:00
* @param {string} prefix - The prefix found on the routes object
2022-04-14 10:24:29 +02:00
* @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
2021-09-02 11:25:24 +02:00
*
2021-10-13 12:39:34 -04:00
* @returns {string}
*/
2022-04-14 10:24:29 +02:00
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);
2021-10-13 12:39:34 -04:00
}
2022-04-14 10:24:29 +02:00
// Otherwise just return path
return route.path;
2021-10-13 12:39:34 -04:00
};
/**
2022-04-01 17:43:21 +02:00
* @description Gets all paths based on routes
2021-10-13 12:39:34 -04:00
*
2022-03-17 16:54:37 +01:00
* @param {object} apiInfo
* @property {object} apiInfo.routeInfo - The api routes object
* @property {string} apiInfo.uniqueName - Content type name | Api name + Content type name
2022-04-05 17:24:39 +02:00
* @property {object} apiInfo.contentTypeInfo - The info object found on content type schemas
2021-10-13 12:39:34 -04:00
*
* @returns {object}
2021-09-02 11:25:24 +02:00
*/
2022-04-01 17:42:15 +02:00
const getPaths = ({ routeInfo, uniqueName, contentTypeInfo }) => {
// Get the routes for the current content type
2022-08-08 23:33:39 +02:00
const contentTypeRoutes = routeInfo.routes.filter((route) => {
2022-04-01 17:42:15 +02:00
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
2022-04-29 09:04:24 +02:00
const isListOfEntities = hasFindMethod(route.handler);
2022-05-05 12:32:22 +02:00
const isLocalizationPath = isLocalizedPath(route.path);
const methodVerb = route.method.toLowerCase();
const hasPathParams = route.path.includes('/:');
2022-04-14 10:24:29 +02:00
const pathWithPrefix = getPathWithPrefix(routeInfo.prefix, route);
const routePath = hasPathParams ? parsePathWithVariables(pathWithPrefix) : pathWithPrefix;
2022-05-05 12:32:22 +02:00
const { responses } = getApiResponses({
uniqueName,
route,
isListOfEntities,
isLocalizationPath,
});
2021-09-02 11:25:24 +02:00
const swaggerConfig = {
responses,
2022-03-17 16:54:37 +01:00
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)) {
2022-04-29 10:05:29 +02:00
const refName = isLocalizationPath ? 'LocalizationRequest' : 'Request';
2022-03-17 16:54:37 +01:00
const requestBody = {
required: true,
content: {
'application/json': {
schema: {
2022-04-29 10:05:29 +02:00
$ref: `#/components/schemas/${pascalCase(uniqueName)}${refName}`,
2022-03-17 16:54:37 +01:00
},
},
},
};
swaggerConfig.requestBody = requestBody;
}
_.set(acc, `${routePath}.${methodVerb}`, swaggerConfig);
2021-09-02 11:25:24 +02:00
return acc;
}, {});
2021-09-02 11:25:24 +02:00
2022-03-17 16:54:37 +01:00
return paths;
2021-09-02 11:25:24 +02:00
};
/**
2022-03-17 16:54:37 +01:00
* @decription Gets all open api paths object for a given content type
2021-09-02 11:25:24 +02:00
*
2022-03-17 16:54:37 +01:00
* @param {object} apiInfo
2021-09-02 11:25:24 +02:00
*
2022-03-17 16:54:37 +01:00
* @returns {object} Open API paths
2021-09-02 11:25:24 +02:00
*/
2022-08-08 23:33:39 +02:00
const getAllPathsForContentType = (apiInfo) => {
let paths = {};
2022-03-17 16:54:37 +01:00
const pathsObject = getPaths(apiInfo);
paths = {
...paths,
...pathsObject,
};
return paths;
2021-09-02 11:25:24 +02:00
};
2022-03-17 16:54:37 +01: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
*
* @returns {object}
*/
2022-08-08 23:33:39 +02:00
const buildApiEndpointPath = (api) => {
2022-04-06 17:10:27 +02:00
// 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
2022-03-17 16:54:37 +01:00
return loopContentTypeNames(api, getAllPathsForContentType);
};
module.exports = buildApiEndpointPath;