2018-12-06 18:03:56 +01:00
|
|
|
'use strict';
|
2021-10-18 21:21:06 +02:00
|
|
|
|
2018-12-06 18:03:56 +01:00
|
|
|
const path = require('path');
|
2021-09-02 11:25:24 +02:00
|
|
|
const fs = require('fs-extra');
|
2018-12-06 18:03:56 +01:00
|
|
|
const _ = require('lodash');
|
2021-10-26 11:18:36 +02:00
|
|
|
const { getAbsoluteServerUrl } = require('@strapi/utils');
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2022-03-17 16:54:37 +01:00
|
|
|
const { builApiEndpointPath, buildComponentSchema } = require('./helpers');
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2023-03-14 14:21:45 +01:00
|
|
|
const defaultOpenApiComponents = require('./utils/default-openapi-components');
|
|
|
|
|
2021-10-18 21:21:06 +02:00
|
|
|
module.exports = ({ strapi }) => {
|
|
|
|
const config = strapi.config.get('plugin.documentation');
|
2022-06-01 23:12:16 +02:00
|
|
|
const registeredDocs = [];
|
|
|
|
|
2023-03-15 11:01:09 +01:00
|
|
|
const getPluginsThatNeedDocumentation = () => {
|
|
|
|
// Default plugins that need documentation generated
|
|
|
|
const defaultPlugins = ['upload', 'users-permissions'];
|
|
|
|
// User specified plugins that need documentation generated
|
|
|
|
const userPluginsConfig = config['x-strapi-config'].plugins;
|
|
|
|
|
|
|
|
if (userPluginsConfig === null) {
|
|
|
|
// The user hasn't specified any plugins to document, use the defaults
|
|
|
|
return defaultPlugins;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userPluginsConfig.length) {
|
|
|
|
// The user has specified certain plugins to document, use them
|
|
|
|
return userPluginsConfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The user has specified that no plugins should be documented
|
|
|
|
return [];
|
|
|
|
};
|
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
return {
|
2023-03-14 14:21:45 +01:00
|
|
|
registerDoc(doc, pluginOrigin) {
|
2023-03-15 11:01:09 +01:00
|
|
|
const plugins = getPluginsThatNeedDocumentation();
|
2023-03-14 17:52:14 +01:00
|
|
|
let registeredDoc = doc;
|
|
|
|
|
2023-03-14 14:21:45 +01:00
|
|
|
if (pluginOrigin) {
|
|
|
|
if (!plugins.includes(pluginOrigin)) {
|
|
|
|
return strapi.log.info(
|
|
|
|
`@strapi/documentation will not use the override provided by ${pluginOrigin} since the plugin was not specified in the x-strapi-config.plugins array`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
strapi.log.warn(
|
|
|
|
'@strapi/documentation received an override that did not specify its origin, this could cause unexpected schema generation'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-06-02 10:22:20 +02:00
|
|
|
// parseYaml
|
|
|
|
if (typeof doc === 'string') {
|
2022-09-05 15:18:24 +02:00
|
|
|
registeredDoc = require('yaml').parse(registeredDoc);
|
2022-06-02 10:22:20 +02:00
|
|
|
}
|
|
|
|
// receive an object we can register it directly
|
2022-09-05 15:18:24 +02:00
|
|
|
registeredDocs.push(registeredDoc);
|
2022-06-01 23:12:16 +02:00
|
|
|
},
|
2023-03-15 11:01:09 +01:00
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
getDocumentationVersion() {
|
2021-10-18 21:21:06 +02:00
|
|
|
return _.get(config, 'info.version');
|
2021-09-02 11:25:24 +02:00
|
|
|
},
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
getFullDocumentationPath() {
|
2022-03-14 17:54:35 +01:00
|
|
|
return path.join(strapi.dirs.app.extensions, 'documentation', 'documentation');
|
2021-09-02 11:25:24 +02:00
|
|
|
},
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2023-03-15 11:01:09 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @deprecated
|
|
|
|
* This method will be removed in the next major release
|
|
|
|
*/
|
|
|
|
getCustomDocumentationPath() {
|
|
|
|
return path.join(strapi.dirs.app.extensions, 'documentation', 'config', 'settings.json');
|
|
|
|
},
|
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
getDocumentationVersions() {
|
|
|
|
return fs
|
|
|
|
.readdirSync(this.getFullDocumentationPath())
|
2022-08-08 23:33:39 +02:00
|
|
|
.map((version) => {
|
2018-12-06 18:03:56 +01:00
|
|
|
try {
|
2021-09-02 11:25:24 +02:00
|
|
|
const doc = JSON.parse(
|
|
|
|
fs.readFileSync(
|
|
|
|
path.resolve(this.getFullDocumentationPath(), version, 'full_documentation.json')
|
|
|
|
)
|
2018-12-06 18:03:56 +01:00
|
|
|
);
|
2021-09-02 11:25:24 +02:00
|
|
|
const generatedDate = _.get(doc, ['info', 'x-generation-date'], null);
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
return { version, generatedDate, url: '' };
|
2018-12-06 18:03:56 +01:00
|
|
|
} catch (err) {
|
2021-09-02 11:25:24 +02:00
|
|
|
return null;
|
2018-12-06 18:03:56 +01:00
|
|
|
}
|
2021-09-02 11:25:24 +02:00
|
|
|
})
|
2022-08-08 23:33:39 +02:00
|
|
|
.filter((x) => x);
|
2021-09-02 11:25:24 +02:00
|
|
|
},
|
|
|
|
|
2021-10-18 12:38:21 +02:00
|
|
|
/**
|
|
|
|
* Returns settings stored in core-store
|
|
|
|
*/
|
2021-10-06 15:25:05 -04:00
|
|
|
async getDocumentationAccess() {
|
2021-10-18 12:38:21 +02:00
|
|
|
const { restrictedAccess } = await strapi
|
2021-09-02 11:25:24 +02:00
|
|
|
.store({
|
|
|
|
environment: '',
|
|
|
|
type: 'plugin',
|
|
|
|
name: 'documentation',
|
|
|
|
key: 'config',
|
|
|
|
})
|
|
|
|
.get();
|
|
|
|
|
2021-10-18 12:38:21 +02:00
|
|
|
return { restrictedAccess };
|
2021-09-02 11:25:24 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description - Gets the path for an api or plugin
|
|
|
|
*
|
|
|
|
* @param {object} api
|
|
|
|
* @property {string} api.name - Name of the api
|
|
|
|
* @property {string} api.getter - api | plugin
|
|
|
|
*
|
|
|
|
* @returns path to the api | plugin
|
|
|
|
*/
|
|
|
|
getApiDocumentationPath(api) {
|
|
|
|
if (api.getter === 'plugin') {
|
2022-03-14 17:54:35 +01:00
|
|
|
return path.join(strapi.dirs.app.extensions, api.name, 'documentation');
|
2021-09-02 11:25:24 +02:00
|
|
|
}
|
|
|
|
|
2022-03-14 17:54:35 +01:00
|
|
|
return path.join(strapi.dirs.app.api, api.name, 'documentation');
|
2021-09-02 11:25:24 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
async deleteDocumentation(version) {
|
|
|
|
const apis = this.getPluginAndApiInfo();
|
|
|
|
for (const api of apis) {
|
|
|
|
await fs.remove(path.join(this.getApiDocumentationPath(api), version));
|
|
|
|
}
|
|
|
|
|
|
|
|
await fs.remove(path.join(this.getFullDocumentationPath(), version));
|
|
|
|
},
|
|
|
|
|
2023-03-14 14:21:45 +01:00
|
|
|
getPluginAndApiInfo() {
|
2023-03-15 11:01:09 +01:00
|
|
|
const plugins = getPluginsThatNeedDocumentation();
|
2022-08-08 23:33:39 +02:00
|
|
|
const pluginsToDocument = plugins.map((plugin) => {
|
2021-09-02 11:25:24 +02:00
|
|
|
return {
|
|
|
|
name: plugin,
|
|
|
|
getter: 'plugin',
|
|
|
|
ctNames: Object.keys(strapi.plugin(plugin).contentTypes),
|
|
|
|
};
|
|
|
|
});
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2022-08-08 23:33:39 +02:00
|
|
|
const apisToDocument = Object.keys(strapi.api).map((api) => {
|
2021-09-02 11:25:24 +02:00
|
|
|
return {
|
|
|
|
name: api,
|
|
|
|
getter: 'api',
|
|
|
|
ctNames: Object.keys(strapi.api[api].contentTypes),
|
|
|
|
};
|
|
|
|
});
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
return [...apisToDocument, ...pluginsToDocument];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description - Creates the Swagger json files
|
|
|
|
*/
|
2021-10-18 21:21:06 +02:00
|
|
|
async generateFullDoc(version = this.getDocumentationVersion()) {
|
2021-09-02 11:25:24 +02:00
|
|
|
let paths = {};
|
2022-03-17 16:54:37 +01:00
|
|
|
let schemas = {};
|
2021-09-02 11:25:24 +02:00
|
|
|
const apis = this.getPluginAndApiInfo();
|
|
|
|
for (const api of apis) {
|
|
|
|
const apiName = api.name;
|
2023-03-14 14:21:45 +01:00
|
|
|
// TODO: check if this is necessary
|
2021-10-18 21:21:06 +02:00
|
|
|
const apiDirPath = path.join(this.getApiDocumentationPath(api), version);
|
2023-03-14 14:21:45 +01:00
|
|
|
// TODO: check if this is necessary
|
2021-09-02 11:25:24 +02:00
|
|
|
const apiDocPath = path.join(apiDirPath, `${apiName}.json`);
|
2021-09-13 12:03:12 +02:00
|
|
|
|
2022-03-17 16:54:37 +01:00
|
|
|
const apiPath = builApiEndpointPath(api);
|
|
|
|
|
|
|
|
if (!apiPath) {
|
2021-11-26 11:49:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-03-14 14:21:45 +01:00
|
|
|
// TODO: check if this is necessary
|
2021-11-26 11:49:46 +01:00
|
|
|
await fs.ensureFile(apiDocPath);
|
2022-03-17 16:54:37 +01:00
|
|
|
await fs.writeJson(apiDocPath, apiPath, { spaces: 2 });
|
2021-11-26 11:49:46 +01:00
|
|
|
|
2022-03-17 16:54:37 +01:00
|
|
|
const componentSchema = buildComponentSchema(api);
|
|
|
|
|
|
|
|
schemas = {
|
|
|
|
...schemas,
|
|
|
|
...componentSchema,
|
|
|
|
};
|
|
|
|
|
|
|
|
paths = { ...paths, ...apiPath };
|
2021-09-02 11:25:24 +02:00
|
|
|
}
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
const fullDocJsonPath = path.join(
|
|
|
|
this.getFullDocumentationPath(),
|
2021-10-18 21:21:06 +02:00
|
|
|
version,
|
2021-09-02 11:25:24 +02:00
|
|
|
'full_documentation.json'
|
|
|
|
);
|
2018-12-06 18:03:56 +01:00
|
|
|
|
2023-03-14 14:21:45 +01:00
|
|
|
// Set config defaults
|
2021-10-26 11:18:36 +02:00
|
|
|
const serverUrl = getAbsoluteServerUrl(strapi.config);
|
|
|
|
const apiPath = strapi.config.get('api.rest.prefix');
|
2023-03-14 14:21:45 +01:00
|
|
|
_.set(config, 'servers', [
|
2021-10-26 11:18:36 +02:00
|
|
|
{
|
|
|
|
url: `${serverUrl}${apiPath}`,
|
|
|
|
description: 'Development server',
|
|
|
|
},
|
|
|
|
]);
|
2023-03-14 14:21:45 +01:00
|
|
|
_.set(config, ['info', 'x-generation-date'], new Date().toISOString());
|
|
|
|
_.set(config, ['info', 'version'], version);
|
2023-03-15 11:01:09 +01:00
|
|
|
_.set(config, ['x-strapi-config', 'plugins'], getPluginsThatNeedDocumentation());
|
2023-03-14 14:21:45 +01:00
|
|
|
// Prepare final doc with default config and generated paths
|
2022-06-01 23:12:16 +02:00
|
|
|
const finalDoc = { ...config, paths };
|
2023-03-14 14:21:45 +01:00
|
|
|
// Add the default components to the final doc
|
|
|
|
_.set(finalDoc, 'components', defaultOpenApiComponents);
|
|
|
|
// Merge the generated component schemas with the defaults
|
|
|
|
_.merge(finalDoc.components, { schemas });
|
|
|
|
// Apply the the registered overrides
|
2022-08-08 23:33:39 +02:00
|
|
|
registeredDocs.forEach((doc) => {
|
2023-03-14 17:52:14 +01:00
|
|
|
// Merge ovveride tags with the generated tags
|
2022-06-02 10:22:20 +02:00
|
|
|
finalDoc.tags = finalDoc.tags || [];
|
|
|
|
finalDoc.tags.push(...(doc.tags || []));
|
2023-03-15 11:01:09 +01:00
|
|
|
// Merge override paths with the generated paths
|
|
|
|
// The override will add a new path or replace the value of an existing path
|
2022-06-01 23:12:16 +02:00
|
|
|
_.assign(finalDoc.paths, doc.paths);
|
2022-06-02 10:22:20 +02:00
|
|
|
// Add components
|
|
|
|
_.forEach(doc.components || {}, (val, key) => {
|
|
|
|
finalDoc.components[key] = finalDoc.components[key] || {};
|
2023-03-14 17:52:14 +01:00
|
|
|
// Merge override components with the generated components,
|
2023-03-15 11:01:09 +01:00
|
|
|
// The override will add a new component or replace the value of an existing component
|
2022-06-02 10:22:20 +02:00
|
|
|
_.assign(finalDoc.components[key], val);
|
|
|
|
});
|
2022-06-01 23:12:16 +02:00
|
|
|
});
|
|
|
|
|
2021-09-02 11:25:24 +02:00
|
|
|
await fs.ensureFile(fullDocJsonPath);
|
2022-06-01 23:12:16 +02:00
|
|
|
await fs.writeJson(fullDocJsonPath, finalDoc, { spaces: 2 });
|
2021-09-02 11:25:24 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|