mirror of
https://github.com/ComfortablyCoding/strapi-plugin-slugify.git
synced 2025-06-23 00:00:35 -04:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f7843ee318 | ||
|
65c3a4e5f0 | ||
|
66b9d2789c | ||
|
68ea256e5b | ||
|
5752ccfc93 | ||
|
f7ba4cce37 | ||
|
430f90b992 | ||
|
e45d80d826 | ||
|
eead698ec7 | ||
|
78229365a5 | ||
|
b668ceed26 | ||
|
ddf130a110 | ||
|
7a41a004e1 | ||
|
691e39feb0 | ||
|
0d446655ec | ||
|
8f8f375fb0 | ||
|
77a1c81978 | ||
|
38292fedd3 | ||
|
8943c3fcbe | ||
|
2d53829e94 | ||
|
9c336ebb1d |
4
.github/workflows/npm-publish.yml
vendored
4
.github/workflows/npm-publish.yml
vendored
@ -13,10 +13,10 @@ jobs:
|
|||||||
- name: Checkout branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node v14
|
- name: Install Node v18
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '18.x'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- name: Install Yarn
|
- name: Install Yarn
|
||||||
|
@ -54,7 +54,9 @@ module.exports = ({ env }) => ({
|
|||||||
|
|
||||||
This will listen for any record created or updated in the article content type and set a slugified value for the slug field automatically based on the title field.
|
This will listen for any record created or updated in the article content type and set a slugified value for the slug field automatically based on the title field.
|
||||||
|
|
||||||
> Note that if you want to rewrite the same field (so `title` is both a reference and a slug) then you just put `title` for both the `field` and `references` properties.
|
> Note: To rewrite the same field (e.g. `title` is both a reference and a slug) use `title` as the `field` and `references` value.
|
||||||
|
|
||||||
|
> Note: Compound slugs (basing the slug on multiple fields) can be achieved by passing an array of fields to the `references` property (e.g. `references: ['date','title']`).
|
||||||
|
|
||||||
**IMPORTANT NOTE**: Make sure any sensitive data is stored in env files.
|
**IMPORTANT NOTE**: Make sure any sensitive data is stored in env files.
|
||||||
|
|
||||||
|
24
package.json
24
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package",
|
"$schema": "https://json.schemastore.org/package",
|
||||||
"name": "strapi-plugin-slugify",
|
"name": "strapi-plugin-slugify",
|
||||||
"version": "2.3.1",
|
"version": "2.3.8",
|
||||||
"description": "A plugin for Strapi Headless CMS that provides the ability to auto slugify a field for any content type.",
|
"description": "A plugin for Strapi Headless CMS that provides the ability to auto slugify a field for any content type.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --fix",
|
"lint": "eslint . --fix",
|
||||||
@ -26,20 +26,18 @@
|
|||||||
"url": "https://github.com/ComfortablyCoding/strapi-plugin-slugify/issues"
|
"url": "https://github.com/ComfortablyCoding/strapi-plugin-slugify/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sindresorhus/slugify": "1.1.0"
|
"@sindresorhus/slugify": "1.1.0",
|
||||||
},
|
"@strapi/strapi": "^4.14.0",
|
||||||
"devDependencies": {
|
"@strapi/utils": "^4.14.0",
|
||||||
"eslint": "^8.8.0",
|
|
||||||
"eslint-config-prettier": "^8.3.0",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
|
||||||
"prettier": "^2.5.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@strapi/strapi": "^4.0.7",
|
|
||||||
"@strapi/utils": "^4.0.7",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
},
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^8.53.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"prettier": "^3.1.0"
|
||||||
|
},
|
||||||
"strapi": {
|
"strapi": {
|
||||||
"displayName": "Slugify",
|
"displayName": "Slugify",
|
||||||
"name": "slugify",
|
"name": "slugify",
|
||||||
@ -47,7 +45,7 @@
|
|||||||
"kind": "plugin"
|
"kind": "plugin"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.x.x <=16.x.x",
|
"node": ">=18.0.0 <=20.x.x",
|
||||||
"npm": ">=6.0.0"
|
"npm": ">=6.0.0"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@ -2,8 +2,15 @@
|
|||||||
|
|
||||||
const { buildSettings } = require('./buildSettings');
|
const { buildSettings } = require('./buildSettings');
|
||||||
const { setupLifecycles } = require('./setupLifecycles');
|
const { setupLifecycles } = require('./setupLifecycles');
|
||||||
|
const { syncSlugCount } = require('./syncSlugCount');
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
const settings = await buildSettings();
|
||||||
|
|
||||||
|
if (settings.slugifyWithCount) {
|
||||||
|
// Ensure correct count used for old plugin versions and projects with existing slugs.
|
||||||
|
await syncSlugCount(settings);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = async ({ strapi }) => {
|
|
||||||
const settings = await buildSettings(strapi);
|
|
||||||
setupLifecycles(settings);
|
setupLifecycles(settings);
|
||||||
};
|
};
|
||||||
|
75
server/bootstrap/syncSlugCount.js
Normal file
75
server/bootstrap/syncSlugCount.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const syncSlugCount = async (settings) => {
|
||||||
|
const entries = await strapi.entityService.findMany('plugin::slugify.slug', {
|
||||||
|
filters: { createdAt: { $gt: 1 } },
|
||||||
|
});
|
||||||
|
|
||||||
|
// if entries aready present we can skip sync
|
||||||
|
if (entries && entries.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strapi.log.info('[slugify] syncing slug count for registered content types');
|
||||||
|
|
||||||
|
const slugs = new Map();
|
||||||
|
|
||||||
|
// chec slugs in each reigistered model
|
||||||
|
for (const uid in settings.modelsByUID) {
|
||||||
|
if (!Object.hasOwnProperty.call(settings.modelsByUID, uid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = settings.modelsByUID[uid];
|
||||||
|
|
||||||
|
// using db query to avoid the need to check if CT has draftAndPublish enabled
|
||||||
|
const modelEntries = await strapi.db.query(model.uid).findMany({
|
||||||
|
filters: { createdAt: { $gt: 1 } },
|
||||||
|
});
|
||||||
|
|
||||||
|
strapi.log.info(`[slugify] syncing slug count for ${model.uid}`);
|
||||||
|
for (const entry of modelEntries) {
|
||||||
|
const slug = entry[model.field];
|
||||||
|
if (!slug) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = slugs.get(getNonAppendedSlug(slug));
|
||||||
|
if (!record) {
|
||||||
|
slugs.set(slug, { slug, count: 1 });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
slugs.set(record.slug, { slug: record.slug, count: record.count + 1 });
|
||||||
|
}
|
||||||
|
strapi.log.info(`[slugify] sync for ${model.uid} completed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slugs.size) {
|
||||||
|
// create all required records
|
||||||
|
const createResponse = await strapi.db.query('plugin::slugify.slug').createMany({
|
||||||
|
data: [...slugs.values()],
|
||||||
|
});
|
||||||
|
|
||||||
|
strapi.log.info(
|
||||||
|
`[slugify] ${createResponse.count} out of ${slugs.size} slugs synced successfully`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
strapi.log.info('[slugify] No syncable slugs found');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// removes any appended number from a slug/string if found
|
||||||
|
const getNonAppendedSlug = (slug) => {
|
||||||
|
const match = slug.match('[\\-]{1}[\\d]+$');
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
return slug.replace(match[0], '');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
syncSlugCount,
|
||||||
|
};
|
9
server/content-types/index.js
Normal file
9
server/content-types/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const slugSchema = require('./slug/schema.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
slug: {
|
||||||
|
schema: slugSchema,
|
||||||
|
},
|
||||||
|
};
|
29
server/content-types/slug/schema.json
Normal file
29
server/content-types/slug/schema.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"kind": "collectionType",
|
||||||
|
"collectionName": "slugs",
|
||||||
|
"info": {
|
||||||
|
"singularName": "slug",
|
||||||
|
"pluralName": "slugs",
|
||||||
|
"displayName": "slug"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"draftAndPublish": false,
|
||||||
|
"comment": ""
|
||||||
|
},
|
||||||
|
"pluginOptions": {
|
||||||
|
"content-manager": {
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
"content-type-builder": {
|
||||||
|
"visible": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attributes": {
|
||||||
|
"slug": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { NotFoundError } = require('@strapi/utils/lib/errors');
|
|
||||||
const { getPluginService } = require('../utils/getPluginService');
|
const { getPluginService } = require('../utils/getPluginService');
|
||||||
const { transformResponse } = require('@strapi/strapi/lib/core-api/controller/transform');
|
|
||||||
const { isValidFindSlugParams } = require('../utils/isValidFindSlugParams');
|
const { isValidFindSlugParams } = require('../utils/isValidFindSlugParams');
|
||||||
const { sanitizeOutput } = require('../utils/sanitizeOutput');
|
const { sanitizeOutput } = require('../utils/sanitizeOutput');
|
||||||
const { hasRequiredModelScopes } = require('../utils/hasRequiredModelScopes');
|
const { hasRequiredModelScopes } = require('../utils/hasRequiredModelScopes');
|
||||||
|
const transform = require('../utils/transform');
|
||||||
|
|
||||||
module.exports = ({ strapi }) => ({
|
module.exports = ({ strapi }) => ({
|
||||||
async findSlug(ctx) {
|
async findSlug(ctx) {
|
||||||
@ -14,15 +13,23 @@ module.exports = ({ strapi }) => ({
|
|||||||
const { modelName, slug } = ctx.request.params;
|
const { modelName, slug } = ctx.request.params;
|
||||||
const { auth } = ctx.state;
|
const { auth } = ctx.state;
|
||||||
|
|
||||||
isValidFindSlugParams({
|
try {
|
||||||
modelName,
|
isValidFindSlugParams({
|
||||||
slug,
|
modelName,
|
||||||
modelsByName,
|
slug,
|
||||||
});
|
modelsByName,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return ctx.badRequest(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
const { uid, field, contentType } = modelsByName[modelName];
|
const { uid, field, contentType } = modelsByName[modelName];
|
||||||
|
|
||||||
await hasRequiredModelScopes(strapi, uid, auth);
|
try {
|
||||||
|
await hasRequiredModelScopes(strapi, uid, auth);
|
||||||
|
} catch (error) {
|
||||||
|
return ctx.forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
// add slug filter to any already existing query restrictions
|
// add slug filter to any already existing query restrictions
|
||||||
let query = ctx.query || {};
|
let query = ctx.query || {};
|
||||||
@ -40,9 +47,9 @@ module.exports = ({ strapi }) => ({
|
|||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
const sanitizedEntity = await sanitizeOutput(data, contentType, auth);
|
const sanitizedEntity = await sanitizeOutput(data, contentType, auth);
|
||||||
ctx.body = transformResponse(sanitizedEntity, {}, { contentType });
|
ctx.body = transform.response({ data: sanitizedEntity, schema: contentType });
|
||||||
} else {
|
} else {
|
||||||
throw new NotFoundError();
|
ctx.notFound();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@ const { getPluginService } = require('../utils/getPluginService');
|
|||||||
const { isValidFindSlugParams } = require('../utils/isValidFindSlugParams');
|
const { isValidFindSlugParams } = require('../utils/isValidFindSlugParams');
|
||||||
const { hasRequiredModelScopes } = require('../utils/hasRequiredModelScopes');
|
const { hasRequiredModelScopes } = require('../utils/hasRequiredModelScopes');
|
||||||
const { sanitizeOutput } = require('../utils/sanitizeOutput');
|
const { sanitizeOutput } = require('../utils/sanitizeOutput');
|
||||||
|
const { ForbiddenError, ValidationError } = require('@strapi/utils').errors;
|
||||||
|
|
||||||
const getCustomTypes = (strapi, nexus) => {
|
const getCustomTypes = (strapi, nexus) => {
|
||||||
const { naming } = getPluginService('utils', 'graphql');
|
const { naming } = getPluginService('utils', 'graphql');
|
||||||
@ -54,16 +55,23 @@ const getCustomTypes = (strapi, nexus) => {
|
|||||||
const { modelName, slug, publicationState } = args;
|
const { modelName, slug, publicationState } = args;
|
||||||
const { auth } = ctx.state;
|
const { auth } = ctx.state;
|
||||||
|
|
||||||
isValidFindSlugParams({
|
try {
|
||||||
modelName,
|
isValidFindSlugParams({
|
||||||
slug,
|
modelName,
|
||||||
modelsByName,
|
slug,
|
||||||
publicationState,
|
modelsByName,
|
||||||
});
|
publicationState,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new ValidationError(error.message);
|
||||||
|
}
|
||||||
const { uid, field, contentType } = modelsByName[modelName];
|
const { uid, field, contentType } = modelsByName[modelName];
|
||||||
|
|
||||||
await hasRequiredModelScopes(strapi, uid, auth);
|
try {
|
||||||
|
await hasRequiredModelScopes(strapi, uid, auth);
|
||||||
|
} catch (error) {
|
||||||
|
throw new ForbiddenError();
|
||||||
|
}
|
||||||
|
|
||||||
// build query
|
// build query
|
||||||
let query = {
|
let query = {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const bootstrap = require('./bootstrap');
|
const bootstrap = require('./bootstrap');
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
|
const contentTypes = require('./content-types');
|
||||||
const controllers = require('./controllers');
|
const controllers = require('./controllers');
|
||||||
const register = require('./register');
|
const register = require('./register');
|
||||||
const routes = require('./routes');
|
const routes = require('./routes');
|
||||||
@ -10,6 +11,7 @@ const services = require('./services');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
bootstrap,
|
bootstrap,
|
||||||
config,
|
config,
|
||||||
|
contentTypes,
|
||||||
controllers,
|
controllers,
|
||||||
register,
|
register,
|
||||||
routes,
|
routes,
|
||||||
|
41
server/services/slug-service/buildSlug.js
Normal file
41
server/services/slug-service/buildSlug.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const slugify = require('@sindresorhus/slugify');
|
||||||
|
|
||||||
|
const buildSlug = async (string, settings) => {
|
||||||
|
let slug = slugify(string, settings.slugifyOptions);
|
||||||
|
|
||||||
|
// slugify with count
|
||||||
|
if (!settings.slugifyWithCount) {
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slugEntry = await strapi.db.query('plugin::slugify.slug').findOne({
|
||||||
|
select: ['id', 'count'],
|
||||||
|
where: { slug },
|
||||||
|
});
|
||||||
|
|
||||||
|
// if no result then count is 1 and base slug is returned
|
||||||
|
if (!slugEntry) {
|
||||||
|
await strapi.entityService.create('plugin::slugify.slug', {
|
||||||
|
data: {
|
||||||
|
slug,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
const count = slugEntry.count + 1;
|
||||||
|
await strapi.entityService.update('plugin::slugify.slug', slugEntry.id, {
|
||||||
|
data: {
|
||||||
|
count,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const separator = settings.slugifyOptions.separator || '-';
|
||||||
|
return `${slug}${separator}${count}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
buildSlug,
|
||||||
|
};
|
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { toSlug } = require('../../utils/slugification');
|
|
||||||
const { getPluginService } = require('../../utils/getPluginService');
|
const { getPluginService } = require('../../utils/getPluginService');
|
||||||
const { shouldUpdateSlug } = require('./shoudUpdateSlug');
|
const { shouldUpdateSlug } = require('./shoudUpdateSlug');
|
||||||
const { getReferenceFieldValues } = require('./getReferenceFieldValues');
|
const { getReferenceFieldValues } = require('./getReferenceFieldValues');
|
||||||
|
const { buildSlug } = require('./buildSlug');
|
||||||
|
|
||||||
module.exports = ({ strapi }) => ({
|
module.exports = ({ strapi }) => ({
|
||||||
async slugify(ctx) {
|
async slugify(ctx) {
|
||||||
@ -40,13 +40,10 @@ module.exports = ({ strapi }) => ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
referenceFieldValues = referenceFieldValues.join(' ');
|
referenceFieldValues = referenceFieldValues.join(' ');
|
||||||
const toSlugOptions = settings.slugifyOptions;
|
|
||||||
if (settings.slugifyWithCount) {
|
|
||||||
toSlugOptions.slugifyWithCount = settings.slugifyWithCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update slug field based on action type
|
// update slug field based on action type
|
||||||
const slug = toSlug(referenceFieldValues, toSlugOptions);
|
const slug = await buildSlug(referenceFieldValues, settings);
|
||||||
|
|
||||||
if (ctx.action === 'beforeCreate' || ctx.action === 'beforeUpdate') {
|
if (ctx.action === 'beforeCreate' || ctx.action === 'beforeUpdate') {
|
||||||
data[field] = slug;
|
data[field] = slug;
|
||||||
} else if (ctx.action === 'afterCreate') {
|
} else if (ctx.action === 'afterCreate') {
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
const { ForbiddenError } = require('@strapi/utils/lib/errors');
|
const hasRequiredModelScopes = (strapi, uid, auth) =>
|
||||||
|
strapi.auth.verify(auth, { scope: `${uid}.find` });
|
||||||
const hasRequiredModelScopes = async (strapi, uid, auth) => {
|
|
||||||
try {
|
|
||||||
await strapi.auth.verify(auth, { scope: `${uid}.find` });
|
|
||||||
} catch (e) {
|
|
||||||
throw new ForbiddenError();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
hasRequiredModelScopes,
|
hasRequiredModelScopes,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const { ValidationError } = require('@strapi/utils/lib/errors');
|
const { ValidationError } = require('@strapi/utils').errors;
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const isValidFindSlugParams = (params) => {
|
const isValidFindSlugParams = (params) => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const { contentAPI } = require('@strapi/utils/lib/sanitize');
|
const { sanitize } = require('@strapi/utils');
|
||||||
|
|
||||||
const sanitizeOutput = (data, contentType, auth) => contentAPI.output(data, contentType, { auth });
|
const sanitizeOutput = (data, contentType, auth) =>
|
||||||
|
sanitize.contentAPI.output(data, contentType, { auth });
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sanitizeOutput,
|
sanitizeOutput,
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const _ = require('lodash');
|
|
||||||
const slugify = require('@sindresorhus/slugify');
|
|
||||||
const slugifyWithCount = slugify.counter();
|
|
||||||
|
|
||||||
const toSlug = (string, options) => {
|
|
||||||
if (options.slugifyWithCount) {
|
|
||||||
_.omit(options, 'slugifyWithCount');
|
|
||||||
return slugifyWithCount(string, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
return slugify(string, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
toSlug,
|
|
||||||
};
|
|
97
server/utils/transform.js
Normal file
97
server/utils/transform.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
'use strict';
|
||||||
|
const { isNil, isPlainObject } = require('lodash/fp');
|
||||||
|
|
||||||
|
function response({ data, schema }) {
|
||||||
|
return transformResponse(data, {}, { contentType: schema });
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted from https://github.com/strapi/strapi/blob/main/packages/core/strapi/src/core-api/controller/transform.ts
|
||||||
|
function isEntry(property) {
|
||||||
|
return property === null || isPlainObject(property) || Array.isArray(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDZEntries(property) {
|
||||||
|
return Array.isArray(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformResponse(resource, meta = {}, opts = {}) {
|
||||||
|
if (isNil(resource)) {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: transformEntry(resource, opts?.contentType),
|
||||||
|
meta,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformComponent(data, component) {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return data.map((datum) => transformComponent(datum, component));
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = transformEntry(data, component);
|
||||||
|
|
||||||
|
if (isNil(res)) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, attributes } = res;
|
||||||
|
return { id, ...attributes };
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformEntry(entry, type) {
|
||||||
|
if (isNil(entry)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(entry)) {
|
||||||
|
return entry.map((singleEntry) => transformEntry(singleEntry, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPlainObject(entry)) {
|
||||||
|
throw new Error('Entry must be an object');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, ...properties } = entry;
|
||||||
|
|
||||||
|
const attributeValues = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(properties)) {
|
||||||
|
const property = properties[key];
|
||||||
|
const attribute = type && type.attributes[key];
|
||||||
|
|
||||||
|
if (attribute && attribute.type === 'relation' && isEntry(property) && 'target' in attribute) {
|
||||||
|
const data = transformEntry(property, strapi.contentType(attribute.target));
|
||||||
|
|
||||||
|
attributeValues[key] = { data };
|
||||||
|
} else if (attribute && attribute.type === 'component' && isEntry(property)) {
|
||||||
|
attributeValues[key] = transformComponent(property, strapi.components[attribute.component]);
|
||||||
|
} else if (attribute && attribute.type === 'dynamiczone' && isDZEntries(property)) {
|
||||||
|
if (isNil(property)) {
|
||||||
|
attributeValues[key] = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeValues[key] = property.map((subProperty) => {
|
||||||
|
return transformComponent(subProperty, strapi.components[subProperty.__component]);
|
||||||
|
});
|
||||||
|
} else if (attribute && attribute.type === 'media' && isEntry(property)) {
|
||||||
|
const data = transformEntry(property, strapi.contentType('plugin::upload.file'));
|
||||||
|
|
||||||
|
attributeValues[key] = { data };
|
||||||
|
} else {
|
||||||
|
attributeValues[key] = property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
attributes: attributeValues,
|
||||||
|
// NOTE: not necessary for now
|
||||||
|
// meta: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
response,
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user