CTB handles singular and plural names

This commit is contained in:
Pierre Noël 2021-09-13 16:57:04 +02:00
parent de3c41d716
commit 64940edbae
70 changed files with 331 additions and 181 deletions

View File

@ -43,7 +43,9 @@ const productWithCompo = {
repeatable: true,
},
},
name: 'product with compo',
displayName: 'product with compo',
singularName: 'product-with-compo',
pluralName: 'product-with-compos',
description: '',
collectionName: '',
};

View File

@ -42,7 +42,9 @@ const productWithCompo = {
required: true,
},
},
name: 'product with compo',
displayName: 'product with compo',
singularName: 'product-with-compo',
pluralName: 'product-with-compos',
description: '',
collectionName: '',
};

View File

@ -46,7 +46,9 @@ const productWithCompoAndDP = {
},
},
draftAndPublish: true,
name: 'product with compo and DP',
displayName: 'product with compo and DP',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
description: '',
collectionName: '',
};

View File

@ -45,7 +45,9 @@ const productWithCompoAndDP = {
},
},
draftAndPublish: true,
name: 'product with compo and DP',
displayName: 'product with compo and DP',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
description: '',
collectionName: '',
};

View File

@ -45,7 +45,9 @@ const productWithCompoAndDP = {
},
},
draftAndPublish: true,
name: 'product with dz and DP',
displayName: 'product with dz and DP',
singularName: 'product-with-dz-and-dp',
pluralName: 'product-with-dz-and-dps',
description: '',
collectionName: '',
};

View File

@ -26,7 +26,9 @@ const productWithDP = {
},
},
draftAndPublish: true,
name: 'product with DP',
displayName: 'product with DP',
singularName: 'product-with-dp',
pluralName: 'product-with-dps',
description: '',
collectionName: '',
};

View File

@ -42,7 +42,9 @@ const productWithDz = {
required: true,
},
},
name: 'product with dz',
displayName: 'Product with dz',
singularName: 'product-with-dz',
pluralName: 'product-with-dzs',
description: '',
collectionName: '',
};

View File

@ -14,7 +14,9 @@ let data = {
};
const stamp = {
name: 'stamp',
displayName: 'stamp',
singularName: 'stamp',
pluralName: 'stamps',
kind: 'collectionType',
attributes: {
name: {
@ -24,7 +26,9 @@ const stamp = {
};
const collector = {
name: 'collector',
displayName: 'collector',
singularName: 'collector',
pluralName: 'collectors',
kind: 'collectionType',
attributes: {
name: {
@ -113,15 +117,15 @@ describe('CM API', () => {
beforeAll(async () => {
await builder
.addContentTypes([stamp, collector])
.addFixtures(stamp.name, stampFixtures)
.addFixtures(collector.name, collectorFixtures)
.addFixtures(stamp.singularName, stampFixtures)
.addFixtures(collector.singularName, collectorFixtures)
.build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
data.collectors = builder.sanitizedFixturesFor(collector.name, strapi);
data.stamps = builder.sanitizedFixturesFor(stamp.name, strapi);
data.collectors = builder.sanitizedFixturesFor(collector.singularName, strapi);
data.stamps = builder.sanitizedFixturesFor(stamp.singularName, strapi);
});
afterAll(async () => {

View File

@ -24,7 +24,9 @@ const product = {
maxLength: 30,
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};

View File

@ -35,7 +35,9 @@ const productModel = {
target: 'api::shop.shop',
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
};
const categoryModel = {
@ -45,7 +47,9 @@ const categoryModel = {
unique: true,
},
},
name: 'category',
displayName: 'Category',
singularName: 'category',
pluralName: 'categories',
};
const shopModel = {
@ -58,7 +62,9 @@ const shopModel = {
type: 'string',
},
},
name: 'shop',
displayName: 'Shop',
singularName: 'shop',
pluralName: 'shops',
};
const PRODUCT_SHOP_COUNT = 12;
@ -105,14 +111,14 @@ const getUID = modelName => `api::${modelName}.${modelName}`;
const getCMPrefixUrl = modelName => `/content-manager/collection-types/${getUID(modelName)}`;
describe('x-to-many RF Preview', () => {
const cmProductUrl = getCMPrefixUrl(productModel.name);
const cmProductUrl = getCMPrefixUrl(productModel.singularName);
beforeAll(async () => {
await builder
.addContentTypes([shopModel, categoryModel, productModel])
.addFixtures(shopModel.name, fixtures.shop)
.addFixtures(categoryModel.name, fixtures.category)
.addFixtures(productModel.name, fixtures.product)
.addFixtures(shopModel.singularName, fixtures.shop)
.addFixtures(categoryModel.singularName, fixtures.category)
.addFixtures(productModel.singularName, fixtures.product)
.build();
strapi = await createStrapiInstance();
@ -146,7 +152,7 @@ describe('x-to-many RF Preview', () => {
describe('Relation Nature', () => {
test(`Throws if the relation's nature is not a x-to-many`, async () => {
const url = getCMPrefixUrl(categoryModel.name);
const url = getCMPrefixUrl(categoryModel.singularName);
const id = data.category[0].id;
const { body, statusCode } = await rq.get(`${url}/${id}/product`);

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -18,6 +18,9 @@ const component = {
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -12,7 +12,9 @@ let uid = 'api::uid-model.uid-model';
const model = {
kind: 'collectionType',
name: 'uid-model',
displayName: 'uid-model',
singularName: 'uid-model',
pluralName: 'uid-models',
attributes: {
title: {
type: 'string',

View File

@ -24,7 +24,9 @@ const defaultBody = {
const models = {
ct: {
name: 'withdynamiczone',
displayName: 'withdynamiczone',
singularName: 'withdynamiczone',
pluralName: 'withdynamiczones',
attributes: {
field: {
type: 'dynamiczone',

View File

@ -55,7 +55,9 @@ const components = {
};
const ct = {
name: 'withdynamiczonemedia',
displayName: 'withdynamiczonemedia',
singularName: 'withdynamiczonemedia',
pluralName: 'withdynamiczonemedias',
attributes: {
field: {
type: 'dynamiczone',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withbiginteger',
displayName: 'withbiginteger',
singularName: 'withbiginteger',
pluralName: 'withbigintegers',
attributes: {
field: {
type: 'biginteger',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withboolean',
displayName: 'withboolean',
singularName: 'withboolean',
pluralName: 'withbooleans',
attributes: {
field: {
type: 'boolean',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withdate',
displayName: 'withdate',
singularName: 'withdate',
pluralName: 'withdates',
attributes: {
field: {
type: 'date',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withdatetime',
displayName: 'withdatetime',
singularName: 'withdatetime',
pluralName: 'withdatetimes',
attributes: {
field: {
type: 'datetime',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withdecimal',
displayName: 'withdecimal',
singularName: 'withdecimal',
pluralName: 'withdecimals',
attributes: {
field: {
type: 'decimal',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withemail',
displayName: 'withemail',
singularName: 'withemail',
pluralName: 'withemails',
attributes: {
field: {
type: 'email',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withenumeration',
displayName: 'withenumeration',
singularName: 'withenumeration',
pluralName: 'withenumerations',
attributes: {
field: {
type: 'enumeration',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withfloat',
displayName: 'withfloat',
singularName: 'withfloat',
pluralName: 'withfloats',
attributes: {
field: {
type: 'float',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withinteger',
displayName: 'withinteger',
singularName: 'withinteger',
pluralName: 'withintegers',
attributes: {
field: {
type: 'integer',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withjson',
displayName: 'withjson',
singularName: 'withjson',
pluralName: 'withjsons',
attributes: {
field: {
type: 'json',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withpassword',
displayName: 'withpassword',
singularName: 'withpassword',
pluralName: 'withpasswords',
attributes: {
field: {
type: 'password',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withrichtext',
displayName: 'withrichtext',
singularName: 'withrichtext',
pluralName: 'withrichtexts',
attributes: {
field: {
type: 'richtext',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withstring',
displayName: 'withstring',
singularName: 'withstring',
pluralName: 'withstrings',
attributes: {
field: {
type: 'string',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withtext',
displayName: 'withtext',
singularName: 'withtext',
pluralName: 'withtexts',
attributes: {
field: {
type: 'text',

View File

@ -9,7 +9,9 @@ let strapi;
let rq;
const ct = {
name: 'withtime',
displayName: 'withtime',
singularName: 'withtime',
pluralName: 'withtimes',
attributes: {
field: {
type: 'time',

View File

@ -10,7 +10,9 @@ let rq;
describe('Test type UID', () => {
describe('No targetField, required=false, not length limits', () => {
const model = {
name: 'withuid',
displayName: 'With uid',
singularName: 'withuid',
pluralName: 'withuids',
attributes: {
slug: {
type: 'uid',
@ -82,7 +84,9 @@ describe('Test type UID', () => {
describe('No targetField, required, no length limits', () => {
const model = {
name: 'withrequireduid',
displayName: 'withrequireduid',
singularName: 'withrequireduid',
pluralName: 'withrequireduids',
attributes: {
slug: {
type: 'uid',

View File

@ -21,7 +21,9 @@ const productModel = {
type: 'string',
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};
@ -32,7 +34,9 @@ const productWithDPModel = {
type: 'string',
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
draftAndPublish: true,
description: '',
collectionName: '',
@ -50,7 +54,9 @@ const shopModel = {
targetAttribute: 'shops',
},
},
name: 'shop',
displayName: 'Shop',
singularName: 'shop',
pluralName: 'shops',
};
const shops = [
@ -89,15 +95,15 @@ describe('Relation-list route', () => {
beforeAll(async () => {
await builder
.addContentTypes([productModel, shopModel])
.addFixtures(shopModel.name, shops)
.addFixtures(productModel.name, products({ withPublished: false }))
.addFixtures(shopModel.singularName, shops)
.addFixtures(productModel.singularName, products({ withPublished: false }))
.build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
data.shops = builder.sanitizedFixturesFor(shopModel.name, strapi);
data.products = builder.sanitizedFixturesFor(productModel.name, strapi);
data.shops = builder.sanitizedFixturesFor(shopModel.singularName, strapi);
data.products = builder.sanitizedFixturesFor(productModel.singularName, strapi);
});
afterAll(async () => {
@ -137,15 +143,15 @@ describe('Relation-list route', () => {
beforeAll(async () => {
await builder
.addContentTypes([productWithDPModel, shopModel])
.addFixtures(shopModel.name, shops)
.addFixtures(productWithDPModel.name, products({ withPublished: true }))
.addFixtures(shopModel.singularName, shops)
.addFixtures(productWithDPModel.singularName, products({ withPublished: true }))
.build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
data.shops = builder.sanitizedFixturesFor(shopModel.name, strapi);
data.products = builder.sanitizedFixturesFor(productWithDPModel.name, strapi);
data.shops = builder.sanitizedFixturesFor(shopModel.singularName, strapi);
data.products = builder.sanitizedFixturesFor(productWithDPModel.singularName, strapi);
});
afterAll(async () => {

View File

@ -16,7 +16,9 @@ let data = {
};
const bedModel = {
name: 'bed',
displayName: 'Bed',
singularName: 'bed',
pluralName: 'beds',
kind: 'collectionType',
attributes: {
name: {
@ -120,13 +122,13 @@ describe('Search query', () => {
beforeAll(async () => {
await builder
.addContentType(bedModel)
.addFixtures(bedModel.name, bedFixtures)
.addFixtures(bedModel.singularName, bedFixtures)
.build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
data.beds = builder.sanitizedFixturesFor(bedModel.name, strapi);
data.beds = builder.sanitizedFixturesFor(bedModel.singularName, strapi);
});
afterAll(async () => {

View File

@ -12,7 +12,9 @@ let uid = 'api::single-type-model.single-type-model';
const ct = {
kind: 'singleType',
name: 'single-type-model',
displayName: 'single-type-model',
singularName: 'single-type-model',
pluralName: 'single-type-models',
attributes: {
title: {
type: 'string',

View File

@ -16,6 +16,10 @@ const componentSchema = createSchema(VALID_TYPES, VALID_RELATIONS, {
modelType: modelTypes.COMPONENT,
})
.shape({
name: yup
.string()
.min(1)
.required('name.required'),
icon: yup
.string()
.nullable()

View File

@ -2,9 +2,7 @@
const _ = require('lodash');
const yup = require('yup');
const { formatYupErrors, nameToSlug } = require('@strapi/utils');
const pluralize = require('pluralize');
const { formatYupErrors } = require('@strapi/utils');
const { getService } = require('../../utils');
const { modelTypes, DEFAULT_TYPES, typeKinds } = require('../../services/constants');
const createSchema = require('./model-schema');
@ -46,16 +44,26 @@ const VALID_TYPES = [...DEFAULT_TYPES, 'uid', 'component', 'dynamiczone'];
*/
const createContentTypeSchema = (data, { isEdition = false } = {}) => {
const kind = _.get(data, 'contentType.kind', typeKinds.COLLECTION_TYPE);
const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
modelType: modelTypes.CONTENT_TYPE,
}).shape({
name: yup
displayName: yup
.string()
.test(hasPluralName)
.min(1)
.required(),
singularName: yup
.string()
.min(1)
.test(alreadyUsedContentTypeName(isEdition))
.test(forbiddenContentTypeNameValidator())
.isKebabCase()
.required(),
pluralName: yup
.string()
.min(1)
.test(alreadyUsedContentTypeName(isEdition))
.test(forbiddenContentTypeNameValidator())
.isKebabCase()
.required(),
});
@ -112,7 +120,7 @@ const forbiddenContentTypeNameValidator = () => {
name: 'forbiddenContentTypeName',
message: `Content Type name cannot be one of ${reservedNames.join(', ')}`,
test: value => {
if (reservedNames.includes(nameToSlug(value))) {
if (value && reservedNames.includes(value)) {
return false;
}
@ -121,21 +129,8 @@ const forbiddenContentTypeNameValidator = () => {
};
};
const hasPluralName = {
name: 'hasPluralName',
message:
'Content Type name `${value}` cannot be pluralized. \nSuggestion: add Item after the name (e.g News -> NewsItem).',
test: value => {
if (pluralize.singular(value) === pluralize(value)) {
return false;
}
return true;
},
};
const alreadyUsedContentTypeName = isEdition => {
const usedNames = Object.values(strapi.contentTypes).map(ct => ct.modelName);
const usedNames = _.flatMap(strapi.contentTypes, ct => [ct.singularName, ct.pluralName]);
return {
name: 'nameAlreadyUsed',
@ -144,7 +139,7 @@ const alreadyUsedContentTypeName = isEdition => {
// don't check on edition
if (isEdition) return true;
if (usedNames.includes(nameToSlug(value))) {
if (usedNames.includes(value)) {
return false;
}
return true;

View File

@ -11,10 +11,6 @@ const getRelationValidator = require('./relations');
const createSchema = (types, relations, { modelType } = {}) => {
const shape = {
name: yup
.string()
.min(1)
.required('name.required'),
description: yup.string(),
draftAndPublish: yup.boolean(),
pluginOptions: yup.object(),

View File

@ -3,7 +3,7 @@
const _ = require('lodash');
const { getOr } = require('lodash/fp');
const { nameToSlug, contentTypes: contentTypesUtils } = require('@strapi/utils');
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
const createBuilder = require('./schema-builder');
const { coreUids, pluginsUids } = require('./constants');
@ -25,13 +25,6 @@ const getRestrictRelationsTo = (contentType = {}) => {
return null;
};
const getformattedName = (contentType = {}) => {
const { info } = contentType;
const name = _.get(info, 'displayName');
return name;
};
/**
* Format a contentType info to be used by the front-end
* @param {Object} contentType
@ -44,7 +37,9 @@ const formatContentType = contentType => {
plugin,
apiID: modelName,
schema: {
name: getformattedName(contentType),
displayName: info.displayName,
singularName: info.singularName,
pluralName: info.pluralName,
description: _.get(info, 'description', ''),
draftAndPublish: contentTypesUtils.hasDraftAndPublish({ options }),
pluginOptions: contentType.pluginOptions,
@ -110,7 +105,7 @@ const createContentType = async ({ contentType, components = [] }, options = {})
// generate api skeleton
await generateAPI({
name: contentType.name,
singularName: contentType.singularName,
kind: contentType.kind,
});
@ -125,9 +120,9 @@ const createContentType = async ({ contentType, components = [] }, options = {})
* Generate an API squeleton
* @param {string} name
*/
const generateAPI = ({ name, kind = 'collectionType' }) => {
const generateAPI = ({ singularName, kind = 'collectionType' }) => {
const strapiGenerators = require('@strapi/generators');
return strapiGenerators.generate('api', { id: nameToSlug(name), kind }, { dir: strapi.dir });
return strapiGenerators.generate('api', { id: singularName, kind }, { dir: strapi.dir });
};
/**
@ -177,7 +172,7 @@ const editContentType = async (uid, { contentType, components = [] }) => {
// generate new api skeleton
await generateAPI({
name: updatedContentType.schema.info.name,
singularName: updatedContentType.schema.info.singularName,
kind: updatedContentType.schema.kind,
});

View File

@ -2,9 +2,8 @@
const path = require('path');
const _ = require('lodash');
const pluralize = require('pluralize');
const { nameToSlug, nameToCollectionName } = require('@strapi/utils');
const { nameToCollectionName } = require('@strapi/utils');
const { isRelation, isConfigurable } = require('../../utils/attributes');
const { typeKinds } = require('../constants');
const createSchemaHandler = require('./schema-handler');
@ -77,16 +76,15 @@ module.exports = function createComponentBuilder() {
throw new Error('contentType.alreadyExists');
}
const modelName = nameToSlug(infos.name);
const contentType = createSchemaHandler({
modelName: modelName,
dir: path.join(strapi.dir, 'api', modelName, 'content-types', modelName),
modelName: infos.singularName,
dir: path.join(strapi.dir, 'api', infos.singularName, 'content-types', infos.singularName),
filename: `schema.json`,
});
this.contentTypes.set(uid, contentType);
const defaultCollectionName = `${nameToCollectionName(pluralize(infos.name))}`;
const defaultCollectionName = nameToCollectionName(infos.pluralName);
// support self referencing content type relation
Object.keys(infos.attributes).forEach(key => {
@ -101,11 +99,9 @@ module.exports = function createComponentBuilder() {
.set('kind', infos.kind || typeKinds.COLLECTION_TYPE)
.set('collectionName', infos.collectionName || defaultCollectionName)
.set('info', {
singularName: modelName,
pluralName: pluralize(modelName),
displayName: infos.name,
// TODO: remove name eventually
name: infos.name,
singularName: infos.singularName,
pluralName: infos.pluralName,
displayName: infos.displayName,
description: infos.description,
})
.set('options', {
@ -219,7 +215,7 @@ module.exports = function createComponentBuilder() {
contentType
.set('collectionName', infos.collectionName)
.set('kind', infos.kind || contentType.schema.kind)
.set(['info', 'name'], infos.name)
.set(['info', 'displayName'], infos.displayName)
.set(['info', 'description'], infos.description)
.set(['options', 'draftAndPublish'], infos.draftAndPublish || false)
.set('pluginOptions', infos.pluginOptions)
@ -250,10 +246,10 @@ module.exports = function createComponentBuilder() {
* Returns a uid from a content type infos
*
* @param {object} options options
* @param {string} options.name component name
* @param {string} options.singularName content-type singularName
* @returns {string} uid
*/
const createContentTypeUID = ({ name }) => `api::${nameToSlug(name)}.${nameToSlug(name)}`;
const createContentTypeUID = ({ singularName }) => `api::${singularName}.${singularName}`;
const generateRelation = ({ key, attribute, uid, targetAttribute = {} }) => {
const opts = {

View File

@ -17,15 +17,17 @@ Object {
},
"collectionName": "test_collection_types",
"description": "",
"displayName": "Test Collection Type",
"draftAndPublish": false,
"kind": "collectionType",
"name": "Test Collection Type",
"pluginOptions": Object {
"i18n": Object {
"localized": true,
},
},
"pluralName": "test-collection-types",
"restrictRelationsTo": null,
"singularName": "test-collection-type",
"visible": true,
},
"uid": "api::test-collection-type.test-collection-type",
@ -45,10 +47,12 @@ Object {
},
"collectionName": "ct_with_dps",
"description": "",
"displayName": "CT with DP",
"draftAndPublish": true,
"kind": "collectionType",
"name": "CT with DP",
"pluralName": "ct-with-dps",
"restrictRelationsTo": null,
"singularName": "ct-with-dp",
"visible": true,
},
"uid": "api::ct-with-dp.ct-with-dp",
@ -73,15 +77,17 @@ Object {
},
"collectionName": "test_single_types",
"description": "",
"displayName": "Test Single Type",
"draftAndPublish": false,
"kind": "singleType",
"name": "Test Single Type",
"pluginOptions": Object {
"i18n": Object {
"localized": true,
},
},
"pluralName": "test-single-types",
"restrictRelationsTo": null,
"singularName": "test-single-type",
"visible": true,
},
"uid": "api::test-single-type.test-single-type",

View File

@ -50,7 +50,9 @@ describe('Content Type Builder - Content types', () => {
url: '/content-type-builder/content-types',
body: {
contentType: {
name: 'Test Collection Type',
displayName: 'Test Collection Type',
singularName: 'test-collection-type',
pluralName: 'test-collection-types',
pluginOptions: {
i18n: {
localized: true,
@ -94,7 +96,9 @@ describe('Content Type Builder - Content types', () => {
url: '/content-type-builder/content-types',
body: {
contentType: {
name: 'CT with DP',
displayName: 'CT with DP',
singularName: 'ct-with-dp',
pluralName: 'ct-with-dps',
draftAndPublish: true,
attributes: {
title: {
@ -134,7 +138,9 @@ describe('Content Type Builder - Content types', () => {
body: {
contentType: {
kind: 'singleType',
name: 'Test Single Type',
displayName: 'Test Single Type',
singularName: 'test-single-type',
pluralName: 'test-single-types',
pluginOptions: {
i18n: {
localized: true,
@ -209,7 +215,9 @@ describe('Content Type Builder - Content types', () => {
body: {
contentType: {
kind: 'collectionType',
name: 'test-collection',
displayName: 'test-collection',
singularName: 'test-collection',
pluralName: 'test-collections',
attributes: {
title: {
type: 'string',
@ -236,7 +244,9 @@ describe('Content Type Builder - Content types', () => {
body: {
contentType: {
kind: 'singleType',
name: 'test-collection',
displayName: 'test-collection',
singularName: 'test-collection',
pluralName: 'test-collections',
attributes: {
title: {
type: 'string',
@ -261,7 +271,9 @@ describe('Content Type Builder - Content types', () => {
body: {
contentType: {
kind: 'singleType',
name: 'test-collection',
displayName: 'test-collection',
singularName: 'test-collection',
pluralName: 'test-collections',
attributes: {
relation: {
private: true,

View File

@ -1,7 +1,7 @@
'use strict';
const _ = require('lodash');
const { pickBy, has } = require('lodash/fp');
const { pickBy, has, isFunction } = require('lodash/fp');
const { addNamespace, hasNamespace } = require('../utils');
const servicesRegistry = strapi => {
@ -16,7 +16,7 @@ const servicesRegistry = strapi => {
const service = services[uid];
if (service) {
instanciatedServices[uid] = service({ strapi });
instanciatedServices[uid] = isFunction(service) ? service({ strapi }) : service;
return instanciatedServices[uid];
}

View File

@ -43,7 +43,9 @@ const productWithCompo = {
repeatable: true,
},
},
name: 'product-with-compo',
displayName: 'product-with-compo',
singularName: 'product-with-compo',
pluralName: 'product-with-compos',
description: '',
collectionName: '',
};

View File

@ -42,7 +42,9 @@ const productWithCompo = {
required: true,
},
},
name: 'product-with-compo',
displayName: 'product-with-compo',
singularName: 'product-with-compo',
pluralName: 'product-with-compos',
description: '',
collectionName: '',
};

View File

@ -44,7 +44,9 @@ const productWithCompoAndDP = {
},
},
draftAndPublish: true,
name: 'product-with-compo-and-dp',
displayName: 'product-with-compo-and-dp',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
description: '',
collectionName: '',
};

View File

@ -43,7 +43,9 @@ const productWithCompoAndDP = {
},
},
draftAndPublish: true,
name: 'product-with-compo-and-dp',
displayName: 'product-with-compo-and-dp',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
description: '',
collectionName: '',
};

View File

@ -41,7 +41,9 @@ const productWithDP = {
},
},
draftAndPublish: true,
name: 'product-with-dp',
displayName: 'product-with-dp',
singularName: 'product-with-dp',
pluralName: 'product-with-dps',
description: '',
collectionName: '',
};

View File

@ -42,7 +42,9 @@ const productWithDz = {
required: true,
},
},
name: 'product-with-dz',
displayName: 'product-with-dz',
singularName: 'product-with-dz',
pluralName: 'product-with-dzs',
description: '',
collectionName: '',
};

View File

@ -20,7 +20,9 @@ const product = {
type: 'text',
},
},
name: 'product',
displayName: 'product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
displayName: 'With Component',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -17,7 +17,9 @@ const component = {
};
const ct = {
name: 'withcomponent',
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',

View File

@ -18,7 +18,9 @@ let rq;
let strapi;
const card = {
name: 'card',
displayName: 'Card',
singularName: 'card',
pluralName: 'cards',
kind: 'collectionType',
attributes: {
name: {
@ -28,7 +30,9 @@ const card = {
};
const collector = {
name: 'collector',
displayName: 'Collector',
singularName: 'collector',
pluralName: 'collectors',
kind: 'collectionType',
attributes: {
name: {
@ -87,8 +91,8 @@ describe('Deep Filtering API', () => {
beforeAll(async () => {
await builder
.addContentTypes([card, collector])
.addFixtures(card.name, fixtures.card)
.addFixtures(collector.name, fixtures.collector)
.addFixtures(card.singularName, fixtures.card)
.addFixtures(collector.singularName, fixtures.collector)
.build();
strapi = await createStrapiInstance();

View File

@ -24,7 +24,9 @@ const defaultBody = {
const models = {
ct: {
name: 'withdynamiczone',
displayName: 'withdynamiczone',
singularName: 'withdynamiczone',
pluralName: 'withdynamiczones',
attributes: {
field: {
type: 'dynamiczone',

View File

@ -55,7 +55,9 @@ const components = {
};
const ct = {
name: 'withdynamiczonemedia',
displayName: 'withdynamiczonemedia',
singularName: 'withdynamiczonemedia',
pluralName: 'withdynamiczonemedias',
attributes: {
field: {
type: 'dynamiczone',

View File

@ -36,7 +36,9 @@ const product = {
type: 'biginteger',
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};
@ -88,7 +90,7 @@ describe('Filtering API', () => {
beforeAll(async () => {
await builder
.addContentType(product)
.addFixtures(product.name, productFixtures)
.addFixtures(product.singularName, productFixtures)
.build();
strapi = await createStrapiInstance();

View File

@ -43,12 +43,6 @@ const fixtures = {
const data = { product: [], category: [], country: [] };
const pluralizedModels = {
product: 'products',
country: 'countries',
category: 'categories',
};
const contentTypes = {
product: {
attributes: {
@ -67,7 +61,9 @@ const contentTypes = {
},
},
draftAndPublish: true,
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
},
@ -78,7 +74,9 @@ const contentTypes = {
},
},
draftAndPublish: true,
name: 'country',
displayName: 'Country',
singularName: 'country',
pluralName: 'countries',
description: '',
collectionName: '',
},
@ -89,7 +87,9 @@ const contentTypes = {
},
},
draftAndPublish: true,
name: 'category',
displayName: 'Category',
singularName: 'category',
pluralName: 'categories',
description: '',
collectionName: '',
},
@ -135,9 +135,9 @@ describe('Publication State', () => {
.addContentType(contentTypes.country)
.addComponent(components.comp)
.addContentTypes([contentTypes.category, contentTypes.product])
.addFixtures(contentTypes.country.name, fixtures.country)
.addFixtures(contentTypes.category.name, fixtures.category)
.addFixtures(contentTypes.product.name, f =>
.addFixtures(contentTypes.country.singularName, fixtures.country)
.addFixtures(contentTypes.category.singularName, fixtures.category)
.addFixtures(contentTypes.product.singularName, f =>
fixtures.product.map(product => ({
name: product.name,
categories: product.categories.map(name => f.category.find(cat => cat.name === name).id),
@ -164,7 +164,7 @@ describe('Publication State', () => {
describe.each(['default', 'live', 'preview'])('Mode: "%s"', mode => {
describe.each(['country', 'category', 'product'])('For %s', modelName => {
const baseUrl = `/${pluralizedModels[modelName]}`;
const baseUrl = `/${contentTypes[modelName].pluralName}`;
const query = getQueryFromMode(mode);
test('Can get entries', async () => {
@ -179,12 +179,11 @@ describe('Publication State', () => {
describe('Advanced checks', () => {
describe('Nested level of relations (live mode)', () => {
let products;
const pluralizedModelName = pluralizedModels[contentTypes.product.name];
beforeEach(async () => {
const res = await rq({
method: 'GET',
url: `/${pluralizedModelName}?publicationState=live`,
url: `/${contentTypes.product.pluralName}?publicationState=live`,
qs: {
populate: ['categories', 'comp.countries'],
},
@ -196,7 +195,7 @@ describe('Publication State', () => {
const getApiRef = id => data.product.find(product => product.id === id);
test('Payload integrity', () => {
expect(products).toHaveLength(lengthFor(contentTypes.product.name));
expect(products).toHaveLength(lengthFor(contentTypes.product.singularName));
});
test('Root level', () => {

View File

@ -16,7 +16,9 @@ let data = {
};
const bedModel = {
name: 'bed',
displayName: 'Bed',
singularName: 'bed',
pluralName: 'beds',
kind: 'collectionType',
attributes: {
name: {
@ -126,13 +128,13 @@ describe('Search query', () => {
beforeAll(async () => {
await builder
.addContentType(bedModel)
.addFixtures(bedModel.name, bedFixtures)
.addFixtures(bedModel.singularName, bedFixtures)
.build();
strapi = await createStrapiInstance();
rq = await createContentAPIRequest({ strapi });
data.bed = builder.sanitizedFixturesFor(bedModel.name, strapi);
data.bed = builder.sanitizedFixturesFor(bedModel.singularName, strapi);
});
afterAll(async () => {

View File

@ -13,7 +13,9 @@ let data = {};
const model = {
kind: 'singleType',
name: 'single-type',
displayName: 'single-type',
singularName: 'single-type',
pluralName: 'single-types',
attributes: {
title: {
type: 'string',

View File

@ -24,7 +24,9 @@ const productModel = {
type: 'string',
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};
@ -46,7 +48,9 @@ const shopModel = {
targetAttribute: 'shops',
},
},
name: 'shop',
displayName: 'Shop',
singularName: 'shop',
pluralName: 'shops',
};
const shops = [
@ -87,15 +91,15 @@ describe('i18n - Relation-list route', () => {
code: 'it',
},
])
.addFixtures(shopModel.name, shops)
.addFixtures(productModel.name, products)
.addFixtures(shopModel.singularName, shops)
.addFixtures(productModel.singularName, products)
.build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
data.shops = builder.sanitizedFixturesFor(shopModel.name, strapi);
data.products = builder.sanitizedFixturesFor(productModel.name, strapi);
data.shops = builder.sanitizedFixturesFor(shopModel.singularName, strapi);
data.products = builder.sanitizedFixturesFor(productModel.singularName, strapi);
});
afterAll(async () => {

View File

@ -25,7 +25,9 @@ const productModel = {
type: 'string',
},
},
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};

View File

@ -23,7 +23,9 @@ module.exports = {
},
},
uid: 'api::article.article',
name: 'article',
displayName: 'Article',
singularName: 'article',
pluralName: 'articles',
description: '',
collectionName: '',
},
@ -40,7 +42,9 @@ module.exports = {
},
},
uid: 'api::tag.tag',
name: 'tag',
displayName: 'Tag',
singularName: 'tag',
pluralName: 'tags',
description: '',
collectionName: '',
},
@ -57,7 +61,9 @@ module.exports = {
},
},
uid: 'api::category.category',
name: 'category',
displayName: 'Category',
singularName: 'category',
pluralName: 'categories',
description: '',
collectionName: '',
},
@ -79,7 +85,9 @@ module.exports = {
},
},
uid: 'api::reference.reference',
name: 'reference',
displayName: 'Reference',
singularName: 'reference',
pluralName: 'references',
description: '',
collectionName: 'refs',
},
@ -96,7 +104,9 @@ module.exports = {
},
},
uid: 'api::product.product',
name: 'product',
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
},
@ -112,7 +122,9 @@ module.exports = {
},
},
uid: 'api::articlewit.articlewit',
name: 'articlewithtag',
displayName: 'Article with tag',
singularName: 'articlewithtag',
pluralName: 'articlewithtags',
description: '',
collectionName: '',
},

View File

@ -185,7 +185,7 @@ async function modifyContentType(data, { strapi } = {}) {
delete sanitizedData.editable;
delete sanitizedData.restrictRelationsTo;
const uid = toUID(sanitizedData.name);
const uid = toUID(sanitizedData.singularName);
const ct = await contentTypeService.editContentType(uid, {
contentType: {