Apply overrides for specified versions

This commit is contained in:
Mark Kaylor 2023-03-22 11:30:48 +01:00
parent 0798cf75f3
commit 19ce046512
5 changed files with 141 additions and 79 deletions

View File

@ -17,7 +17,7 @@ module.exports = () => ({
documentation: { documentation: {
config: { config: {
info: { info: {
version: '2.0.0', version: '1.0.0',
}, },
}, },
}, },

View File

@ -16,12 +16,11 @@ module.exports = {
name: 'Apache 2.0', name: 'Apache 2.0',
url: 'https://www.apache.org/licenses/LICENSE-2.0.html', url: 'https://www.apache.org/licenses/LICENSE-2.0.html',
}, },
'x-strapi-generation-date': new Date().toISOString(),
}, },
'x-strapi-config': { 'x-strapi-config': {
path: '/documentation', path: '/documentation',
plugins: null, plugins: null,
customizer: null, mutateDocumentation: null,
}, },
servers: [], servers: [],
externalDocs: { externalDocs: {

View File

@ -154,13 +154,15 @@ describe('Documentation service', () => {
}, },
], ],
}; };
global.strapi.config.get = () => ({ ...userConfig }); global.strapi.config.get = () => ({ ...userConfig });
const docService = documentation({ strapi: global.strapi }); const docService = documentation({ strapi: global.strapi });
await docService.generateFullDoc(); await docService.generateFullDoc();
const lastMockCall = fse.writeJson.mock.calls[fse.writeJson.mock.calls.length - 1]; const lastMockCall = fse.writeJson.mock.calls[fse.writeJson.mock.calls.length - 1];
const mockFinalDoc = lastMockCall[1]; const mockFinalDoc = lastMockCall[1];
// The generation data is dynamically added, it cannot be modified by the user
expect(mockFinalDoc.info).toEqual(userConfig.info); const { 'x-generation-date': generationConfig, ...mockFinalDocInfo } = mockFinalDoc.info;
expect(mockFinalDocInfo).toEqual(userConfig.info);
expect(mockFinalDoc['x-strapi-config']).toEqual(userConfig['x-strapi-config']); expect(mockFinalDoc['x-strapi-config']).toEqual(userConfig['x-strapi-config']);
expect(mockFinalDoc.externalDocs).toEqual(userConfig.externalDocs); expect(mockFinalDoc.externalDocs).toEqual(userConfig.externalDocs);
expect(mockFinalDoc.security).toEqual(userConfig.security); expect(mockFinalDoc.security).toEqual(userConfig.security);
@ -319,6 +321,66 @@ describe('Documentation service', () => {
'test-new-component' 'test-new-component'
); );
}); });
it('overrides only the specified version', async () => {
const overrideService = override({ strapi: global.strapi });
// Simulate override from upload plugin
overrideService.registerOverride(
{
// Only override for version 1.0.0
info: { version: '1.0.0' },
components: {
schemas: {
// This component schema exists after generating with mock data, replace it
ShouldNotBeAdded: {},
},
},
},
{ pluginOrigin: 'upload' }
);
// Simulate override from upload plugin
overrideService.registerOverride(
{
// Only override for version 2.0.0
info: { version: '2.0.0' },
components: {
schemas: {
// This component schema exists after generating with mock data, replace it
ShouldBeAdded: {},
},
},
},
{ pluginOrigin: 'upload' }
);
// Simulate override from upload plugin
overrideService.registerOverride(
{
components: {
schemas: {
// This component schema exists after generating with mock data, replace it
ShouldAlsoBeAdded: {},
},
},
},
{ pluginOrigin: 'upload' }
);
global.strapi.plugins.documentation = {
service: jest.fn((name) => {
const mockServices = {
override: overrideService,
};
return mockServices[name];
}),
};
const docService = documentation({ strapi: global.strapi });
await docService.generateFullDoc('2.0.0');
const lastMockCall = fse.writeJson.mock.calls[fse.writeJson.mock.calls.length - 1];
const mockFinalDoc = lastMockCall[1];
expect(mockFinalDoc.components.schemas.ShouldNotBeAdded).toBeUndefined();
expect(mockFinalDoc.components.schemas.ShouldBeAdded).toBeDefined();
expect(mockFinalDoc.components.schemas.ShouldAlsoBeAdded).toBeDefined();
});
it('excludes apis and plugins from generation', async () => { it('excludes apis and plugins from generation', async () => {
const overrideService = override({ strapi: global.strapi }); const overrideService = override({ strapi: global.strapi });
@ -346,12 +408,12 @@ describe('Documentation service', () => {
Object.keys(mockFinalDoc.components.schemas).find((compo) => compo.includes('Kitchensink')) Object.keys(mockFinalDoc.components.schemas).find((compo) => compo.includes('Kitchensink'))
).toBeUndefined(); ).toBeUndefined();
}); });
it("applies a user's customizer function", async () => { it("applies a user's mutateDocumentation function", async () => {
global.strapi.config.get = () => ({ global.strapi.config.get = () => ({
...defaultConfig, ...defaultConfig,
'x-strapi-config': { 'x-strapi-config': {
...defaultConfig['x-strapi-config'], ...defaultConfig['x-strapi-config'],
customizer(draft) { mutateDocumentation(draft) {
draft.paths['/kitchensinks'] = { get: { responses: { 200: { description: 'test' } } } }; draft.paths['/kitchensinks'] = { get: { responses: { 200: { description: 'test' } } } };
}, },
}, },

View File

@ -176,7 +176,6 @@ module.exports = ({ strapi }) => {
'full_documentation.json' 'full_documentation.json'
); );
// Set config defaults
const serverUrl = getAbsoluteServerUrl(strapi.config); const serverUrl = getAbsoluteServerUrl(strapi.config);
const apiPath = strapi.config.get('api.rest.prefix'); const apiPath = strapi.config.get('api.rest.prefix');
const generatedDocumentation = produce(config, (draft) => { const generatedDocumentation = produce(config, (draft) => {
@ -189,17 +188,22 @@ module.exports = ({ strapi }) => {
}, },
]; ];
} }
// Set the generated date
draft.info['x-generation-date'] = new Date().toISOString();
// Set the plugins that need documentation
draft['x-strapi-config'].plugins = pluginsThatNeedDocumentation; draft['x-strapi-config'].plugins = pluginsThatNeedDocumentation;
// Delete it from the config so it doesn't end up in the spec // Delete it from the config so it doesn't end up in the spec
delete draft['x-strapi-config'].customizer; delete draft['x-strapi-config'].mutateDocumentation;
// Set the generated paths // Set the generated paths
draft.paths = paths; draft.paths = paths;
// Merge the generated component schemas with the defaults // Merge the generated component schemas with the defaults
draft.components = _.merge(defaultOpenApiComponents, { schemas }); draft.components = _.merge(defaultOpenApiComponents, { schemas });
// Check for overrides and then add them
if (overrideService.registeredOverrides.length > 0) {
overrideService.registeredOverrides.forEach((doc) => { overrideService.registeredOverrides.forEach((doc) => {
// Only run the overrrides when no override version is provided,
// or when the generated documentation version matches the override version
if (!doc?.info?.version || doc.info.version === version) {
if (doc.tags) { if (doc.tags) {
// Merge override tags with the generated tags // Merge override tags with the generated tags
draft.tags = draft.tags || []; draft.tags = draft.tags || [];
@ -220,11 +224,13 @@ module.exports = ({ strapi }) => {
draft.components[key] = { ...draft.components[key], ...val }; draft.components[key] = { ...draft.components[key], ...val };
}); });
} }
}
}); });
}
}); });
// Get the documentation customizer // Get the documentation mutateDocumentation
const documentationCustomizer = config['x-strapi-config'].customizer; const documentationCustomizer = config['x-strapi-config'].mutateDocumentation;
// Escape hatch, allow the user to provide a customizer function that can manipulate // Escape hatch, allow the user to provide a mutateDocumentation function that can alter any part of
// the generated documentation before it is written to the file system // the generated documentation before it is written to the file system
const finalDocumentation = documentationCustomizer const finalDocumentation = documentationCustomizer
? produce(generatedDocumentation, documentationCustomizer) ? produce(generatedDocumentation, documentationCustomizer)

View File

@ -5,11 +5,15 @@ const { getPluginsThatNeedDocumentation } = require('./utils/get-plugins-that-ne
module.exports = ({ strapi }) => { module.exports = ({ strapi }) => {
const registeredOverrides = []; const registeredOverrides = [];
const excludedFromGeneration = []; const excludedFromGeneration = [];
return {
registeredOverrides,
excludedFromGeneration,
/** /**
* *
* @param {string | string[]} api - The name of the api or and array of apis to exclude from generation * @param {string | string[]} api - The name of the api or and array of apis to exclude from generation
*/ */
const excludeFromGeneration = (api) => { excludeFromGeneration(api) {
if (Array.isArray(api)) { if (Array.isArray(api)) {
excludedFromGeneration.push(...api); excludedFromGeneration.push(...api);
@ -17,7 +21,7 @@ module.exports = ({ strapi }) => {
} }
excludedFromGeneration.push(api); excludedFromGeneration.push(api);
}; },
/** /**
* @TODO pluginOrigin should be required in next major release * @TODO pluginOrigin should be required in next major release
* @param {object} doc - The openapi specifcation to override * @param {object} doc - The openapi specifcation to override
@ -25,7 +29,7 @@ module.exports = ({ strapi }) => {
* @param {string} options.pluginOrigin - The name of the plugin that is overriding the documentation * @param {string} options.pluginOrigin - The name of the plugin that is overriding the documentation
* @param {string[]} options.excludeFromGeneration - The name of the plugin that is overriding the documentation * @param {string[]} options.excludeFromGeneration - The name of the plugin that is overriding the documentation
*/ */
const registerOverride = (override, { pluginOrigin, excludeFromGeneration = [] }) => { registerOverride(override, { pluginOrigin, excludeFromGeneration = [] }) {
const pluginsThatNeedDocumentation = getPluginsThatNeedDocumentation( const pluginsThatNeedDocumentation = getPluginsThatNeedDocumentation(
strapi.config.get('plugin.documentation') strapi.config.get('plugin.documentation')
); );
@ -33,10 +37,7 @@ module.exports = ({ strapi }) => {
if (pluginOrigin && !pluginsThatNeedDocumentation.includes(pluginOrigin)) return; if (pluginOrigin && !pluginsThatNeedDocumentation.includes(pluginOrigin)) return;
if (excludeFromGeneration.length) { if (excludeFromGeneration.length) {
strapi this.excludeFromGeneration(excludeFromGeneration);
.plugin('documentation')
.service('override')
.excludeFromGeneration(excludeFromGeneration);
} }
let overrideToRegister = override; let overrideToRegister = override;
@ -46,12 +47,6 @@ module.exports = ({ strapi }) => {
} }
// receive an object we can register it directly // receive an object we can register it directly
registeredOverrides.push(overrideToRegister); registeredOverrides.push(overrideToRegister);
}; },
return {
registeredOverrides,
registerOverride,
excludeFromGeneration,
excludedFromGeneration,
}; };
}; };