Skip to content

Commit

Permalink
Merge pull request #12929 from strapi/fix/documentation-component-sch…
Browse files Browse the repository at this point in the history
…emas

Add component schemas
  • Loading branch information
alexandrebodin committed May 11, 2022
2 parents 488f701 + acd8b1a commit 8eec9c2
Show file tree
Hide file tree
Showing 23 changed files with 896 additions and 556 deletions.
41 changes: 41 additions & 0 deletions packages/plugins/documentation/__mocks__/strapi.js
@@ -0,0 +1,41 @@
'use strict';

const strapi = {
plugins: {
'users-permissions': {
contentTypes: {
role: {
attributes: {
name: {
type: 'string',
},
},
},
},
routes: {
'content-api': {
routes: [],
},
},
},
},
api: {
restaurant: {
contentTypes: {
restaurant: {
attributes: {
name: {
type: 'string',
},
},
},
},
routes: {
restaurant: { routes: [] },
},
},
},
contentType: () => ({ info: {}, attributes: { test: { type: 'string' } } }),
};

module.exports = strapi;
@@ -0,0 +1,266 @@
'use strict';

const _ = require('lodash');
const buildComponentSchema = require('../server/services/helpers/build-component-schema');
const strapi = require('../__mocks__/strapi');

describe('Build Component Schema', () => {
beforeEach(() => {
// Reset the mocked strapi instance
global.strapi = _.cloneDeep(strapi);
});

it('builds the Response schema', () => {
const apiMocks = [
{
name: 'users-permissions',
getter: 'plugin',
ctNames: ['role'],
},
{ name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
];

let schemas = {};
for (const mock of apiMocks) {
schemas = {
...schemas,
...buildComponentSchema(mock),
};
}

const schemaNames = Object.keys(schemas);
const [pluginResponseName, apiResponseName] = Object.keys(schemas);
const [pluginResponseValue, apiResponseValue] = Object.values(schemas);

const expectedShape = {
properties: {
data: {
type: 'object',
properties: {
id: { type: 'string' },
attributes: { type: 'object', properties: { test: { type: 'string' } } },
},
},
meta: { type: 'object' },
},
};

expect(schemaNames.length).toBe(2);
expect(pluginResponseName).toBe('UsersPermissionsRoleResponse');
expect(apiResponseName).toBe('RestaurantResponse');
expect(pluginResponseValue).toStrictEqual(expectedShape);
expect(apiResponseValue).toStrictEqual(expectedShape);
});

it('builds the ResponseList schema', () => {
global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
{ method: 'GET', path: '/test', handler: 'test.find' },
];
global.strapi.api.restaurant.routes.restaurant.routes = [
{ method: 'GET', path: '/test', handler: 'test.find' },
];

const apiMocks = [
{
name: 'users-permissions',
getter: 'plugin',
ctNames: ['role'],
},
{ name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
];

let schemas = {};
for (const mock of apiMocks) {
schemas = {
...schemas,
...buildComponentSchema(mock),
};
}

const schemaNames = Object.keys(schemas);
const pluginListResponseValue = schemas['UsersPermissionsRoleListResponse'];
const apiListResponseValue = schemas['RestaurantListResponse'];

const expectedShape = {
properties: {
data: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
attributes: { type: 'object', properties: { test: { type: 'string' } } },
},
},
},
meta: {
type: 'object',
properties: {
pagination: {
properties: {
page: { type: 'integer' },
pageSize: { type: 'integer', minimum: 25 },
pageCount: { type: 'integer', maximum: 1 },
total: { type: 'integer' },
},
},
},
},
},
};

expect(schemaNames.length).toBe(4);
expect(schemaNames.includes('UsersPermissionsRoleListResponse')).toBe(true);
expect(schemaNames.includes('RestaurantListResponse')).toBe(true);
expect(pluginListResponseValue).toStrictEqual(expectedShape);
expect(apiListResponseValue).toStrictEqual(expectedShape);
});

it('builds the Request schema', () => {
global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
{ method: 'POST', path: '/test', handler: 'test.create' },
];
global.strapi.api.restaurant.routes.restaurant.routes = [
{ method: 'POST', path: '/test', handler: 'test.create' },
];

const apiMocks = [
{
name: 'users-permissions',
getter: 'plugin',
ctNames: ['role'],
},
{ name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
];

let schemas = {};
for (const mock of apiMocks) {
schemas = {
...schemas,
...buildComponentSchema(mock),
};
}

const schemaNames = Object.keys(schemas);
const pluginListResponseValue = schemas['UsersPermissionsRoleRequest'];
const apiListResponseValue = schemas['RestaurantRequest'];

const expectedShape = {
type: 'object',
properties: {
data: {
type: 'object',
properties: { test: { type: 'string' } },
},
},
};

expect(schemaNames.length).toBe(4);
expect(schemaNames.includes('UsersPermissionsRoleRequest')).toBe(true);
expect(schemaNames.includes('RestaurantRequest')).toBe(true);
expect(pluginListResponseValue).toStrictEqual(expectedShape);
expect(apiListResponseValue).toStrictEqual(expectedShape);
});

it('builds the LocalizationResponse schema', () => {
global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
{ method: 'GET', path: '/localizations', handler: 'test' },
];
global.strapi.api.restaurant.routes.restaurant.routes = [
{ method: 'GET', path: '/localizations', handler: 'test' },
];

const apiMocks = [
{
name: 'users-permissions',
getter: 'plugin',
ctNames: ['role'],
},
{ name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
];

let schemas = {};
for (const mock of apiMocks) {
schemas = {
...schemas,
...buildComponentSchema(mock),
};
}

const schemaNames = Object.keys(schemas);
const pluginListResponseValue = schemas['UsersPermissionsRoleLocalizationResponse'];
const apiListResponseValue = schemas['RestaurantLocalizationResponse'];

const expectedShape = {
type: 'object',
properties: {
id: { type: 'string' },
test: { type: 'string' },
},
};

expect(schemaNames.length).toBe(4);
expect(schemaNames.includes('UsersPermissionsRoleLocalizationResponse')).toBe(true);
expect(schemaNames.includes('RestaurantLocalizationResponse')).toBe(true);
expect(pluginListResponseValue).toStrictEqual(expectedShape);
expect(apiListResponseValue).toStrictEqual(expectedShape);
});

it('builds the LocalizationRequest schema', () => {
global.strapi.plugins['users-permissions'].routes['content-api'].routes = [
{ method: 'POST', path: '/localizations', handler: 'test' },
];
global.strapi.api.restaurant.routes.restaurant.routes = [
{ method: 'POST', path: '/localizations', handler: 'test' },
];

const apiMocks = [
{
name: 'users-permissions',
getter: 'plugin',
ctNames: ['role'],
},
{ name: 'restaurant', getter: 'api', ctNames: ['restaurant'] },
];

let schemas = {};
for (const mock of apiMocks) {
schemas = {
...schemas,
...buildComponentSchema(mock),
};
}

const schemaNames = Object.keys(schemas);
const pluginListResponseValue = schemas['UsersPermissionsRoleLocalizationRequest'];
const apiListResponseValue = schemas['RestaurantLocalizationRequest'];

const expectedShape = {
type: 'object',
properties: { test: { type: 'string' } },
};

expect(schemaNames.length).toBe(8);
expect(schemaNames.includes('UsersPermissionsRoleLocalizationRequest')).toBe(true);
expect(schemaNames.includes('RestaurantLocalizationRequest')).toBe(true);
expect(pluginListResponseValue).toStrictEqual(expectedShape);
expect(apiListResponseValue).toStrictEqual(expectedShape);
});

it('creates the correct name given multiple content types', () => {
const apiMock = {
name: 'users-permissions',
getter: 'plugin',
ctNames: ['permission', 'role', 'user'],
};

const schemas = buildComponentSchema(apiMock);
const schemaNames = Object.keys(schemas);
const [permission, role, user] = schemaNames;

expect(schemaNames.length).toBe(3);
expect(permission).toBe('UsersPermissionsPermissionResponse');
expect(role).toBe('UsersPermissionsRoleResponse');
expect(user).toBe('UsersPermissionsUserResponse');
});
});
Expand Up @@ -21,7 +21,7 @@ module.exports = {
path: '/documentation',
showGeneratedFiles: true,
generateDefaultResponse: true,
plugins: ['email', 'upload'],
plugins: ['email', 'upload', 'users-permissions'],
},
servers: [],
externalDocs: {
Expand All @@ -41,5 +41,34 @@ module.exports = {
bearerFormat: 'JWT',
},
},
schemas: {
Error: {
type: 'object',
required: ['error'],
properties: {
data: {
nullable: true,
oneOf: [{ type: 'object' }, { type: 'array' }],
},
error: {
type: 'object',
properties: {
status: {
type: 'integer',
},
name: {
type: 'string',
},
message: {
type: 'string',
},
details: {
type: 'object',
},
},
},
},
},
},
},
};
4 changes: 2 additions & 2 deletions packages/plugins/documentation/server/config/index.js
@@ -1,7 +1,7 @@
'use strict';

const defaultDocumentationConfig = require('./default-config');
const defaultPluginConfig = require('./default-plugin-config');

module.exports = {
default: defaultDocumentationConfig,
default: defaultPluginConfig,
};

0 comments on commit 8eec9c2

Please sign in to comment.