mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 16:29:34 +00:00
Merge branch 'main' into enhancement/axios-refactoring
This commit is contained in:
commit
20cbf0d630
2
.github/workflows/contributor-doc.yml
vendored
2
.github/workflows/contributor-doc.yml
vendored
@ -21,7 +21,7 @@ permissions: {}
|
|||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # to push pages branch (peaceiris/actions-gh-pages)
|
contents: write # to push pages branch (peaceiris/actions-gh-pages)
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: github-pages
|
||||||
|
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@ -6,7 +6,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # to fetch code (actions/checkout)
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
|
43
.github/workflows/tests.yml
vendored
43
.github/workflows/tests.yml
vendored
@ -14,7 +14,7 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # to fetch code (actions/checkout)
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
@ -142,18 +142,18 @@ jobs:
|
|||||||
node: [14, 16, 18]
|
node: [14, 16, 18]
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql
|
image: bitnami/mysql:latest
|
||||||
|
env:
|
||||||
|
MYSQL_ROOT_PASSWORD: strapi
|
||||||
|
MYSQL_USER: strapi
|
||||||
|
MYSQL_PASSWORD: strapi
|
||||||
|
MYSQL_DATABASE: strapi_test
|
||||||
|
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd="mysqladmin ping"
|
--health-cmd="mysqladmin ping"
|
||||||
--health-interval=10s
|
--health-interval=10s
|
||||||
--health-timeout=5s
|
--health-timeout=5s
|
||||||
--health-retries=3
|
--health-retries=3
|
||||||
-e MYSQL_ROOT_PASSWORD=strapi
|
|
||||||
-e MYSQL_ROOT_HOST="%"
|
|
||||||
-e MYSQL_USER=strapi
|
|
||||||
-e MYSQL_PASSWORD=strapi
|
|
||||||
-e MYSQL_DATABASE=strapi_test
|
|
||||||
--entrypoint sh mysql -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
|
|
||||||
ports:
|
ports:
|
||||||
# Maps tcp port 5432 on service container to the host
|
# Maps tcp port 5432 on service container to the host
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
@ -180,18 +180,17 @@ jobs:
|
|||||||
node: [14, 16, 18]
|
node: [14, 16, 18]
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:5
|
image: bitnami/mysql:5.7
|
||||||
|
env:
|
||||||
|
MYSQL_ROOT_PASSWORD: strapi
|
||||||
|
MYSQL_USER: strapi
|
||||||
|
MYSQL_PASSWORD: strapi
|
||||||
|
MYSQL_DATABASE: strapi_test
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd="mysqladmin ping"
|
--health-cmd="mysqladmin ping"
|
||||||
--health-interval=10s
|
--health-interval=10s
|
||||||
--health-timeout=5s
|
--health-timeout=5s
|
||||||
--health-retries=3
|
--health-retries=3
|
||||||
-e MYSQL_ROOT_PASSWORD=strapi
|
|
||||||
-e MYSQL_ROOT_HOST="%"
|
|
||||||
-e MYSQL_USER=strapi
|
|
||||||
-e MYSQL_PASSWORD=strapi
|
|
||||||
-e MYSQL_DATABASE=strapi_test
|
|
||||||
--entrypoint sh mysql -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
|
|
||||||
ports:
|
ports:
|
||||||
# Maps tcp port 5432 on service container to the host
|
# Maps tcp port 5432 on service container to the host
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
@ -288,18 +287,18 @@ jobs:
|
|||||||
node: [14, 16, 18]
|
node: [14, 16, 18]
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql
|
image: bitnami/mysql:latest
|
||||||
|
env:
|
||||||
|
MYSQL_ROOT_PASSWORD: strapi
|
||||||
|
MYSQL_USER: strapi
|
||||||
|
MYSQL_PASSWORD: strapi
|
||||||
|
MYSQL_DATABASE: strapi_test
|
||||||
|
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd="mysqladmin ping"
|
--health-cmd="mysqladmin ping"
|
||||||
--health-interval=10s
|
--health-interval=10s
|
||||||
--health-timeout=5s
|
--health-timeout=5s
|
||||||
--health-retries=3
|
--health-retries=3
|
||||||
-e MYSQL_ROOT_PASSWORD=strapi
|
|
||||||
-e MYSQL_ROOT_HOST="%"
|
|
||||||
-e MYSQL_USER=strapi
|
|
||||||
-e MYSQL_PASSWORD=strapi
|
|
||||||
-e MYSQL_DATABASE=strapi_test
|
|
||||||
--entrypoint sh mysql -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
|
|
||||||
ports:
|
ports:
|
||||||
# Maps tcp port 5432 on service container to the host
|
# Maps tcp port 5432 on service container to the host
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
|
@ -436,7 +436,7 @@ describe('Admin API Token v2 CRUD (api)', () => {
|
|||||||
expect(res.body.data).toMatchObject({
|
expect(res.body.data).toMatchObject({
|
||||||
accessKey: expect.any(String),
|
accessKey: expect.any(String),
|
||||||
name: body.name,
|
name: body.name,
|
||||||
permissions: body.permissions,
|
permissions: expect.arrayContaining(body.permissions),
|
||||||
description: body.description,
|
description: body.description,
|
||||||
type: body.type,
|
type: body.type,
|
||||||
id: expect.any(Number),
|
id: expect.any(Number),
|
||||||
|
@ -446,6 +446,11 @@ const morphToMany = async (input, ctx) => {
|
|||||||
.where({
|
.where({
|
||||||
[joinColumn.name]: referencedValues,
|
[joinColumn.name]: referencedValues,
|
||||||
...(joinTable.on || {}),
|
...(joinTable.on || {}),
|
||||||
|
// If the populateValue contains an "on" property,
|
||||||
|
// only populate the types defined in it
|
||||||
|
...('on' in populateValue
|
||||||
|
? { [morphColumn.typeColumn.name]: Object.keys(populateValue.on) }
|
||||||
|
: {}),
|
||||||
})
|
})
|
||||||
.orderBy([joinColumn.name, 'order'])
|
.orderBy([joinColumn.name, 'order'])
|
||||||
.execute({ mapResults: false });
|
.execute({ mapResults: false });
|
||||||
@ -470,6 +475,8 @@ const morphToMany = async (input, ctx) => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const map = {};
|
const map = {};
|
||||||
|
const { on, ...typePopulate } = populateValue;
|
||||||
|
|
||||||
for (const type of Object.keys(idsByType)) {
|
for (const type of Object.keys(idsByType)) {
|
||||||
const ids = idsByType[type];
|
const ids = idsByType[type];
|
||||||
|
|
||||||
@ -483,7 +490,7 @@ const morphToMany = async (input, ctx) => {
|
|||||||
const qb = db.entityManager.createQueryBuilder(type);
|
const qb = db.entityManager.createQueryBuilder(type);
|
||||||
|
|
||||||
const rows = await qb
|
const rows = await qb
|
||||||
.init(populateValue)
|
.init(on?.[type] ?? typePopulate)
|
||||||
.addSelect(`${qb.alias}.${idColumn.referencedColumn}`)
|
.addSelect(`${qb.alias}.${idColumn.referencedColumn}`)
|
||||||
.where({ [idColumn.referencedColumn]: ids })
|
.where({ [idColumn.referencedColumn]: ids })
|
||||||
.execute({ mapResults: false });
|
.execute({ mapResults: false });
|
||||||
@ -540,6 +547,8 @@ const morphToOne = async (input, ctx) => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const map = {};
|
const map = {};
|
||||||
|
const { on, ...typePopulate } = populateValue;
|
||||||
|
|
||||||
for (const type of Object.keys(idsByType)) {
|
for (const type of Object.keys(idsByType)) {
|
||||||
const ids = idsByType[type];
|
const ids = idsByType[type];
|
||||||
|
|
||||||
@ -552,7 +561,7 @@ const morphToOne = async (input, ctx) => {
|
|||||||
const qb = db.entityManager.createQueryBuilder(type);
|
const qb = db.entityManager.createQueryBuilder(type);
|
||||||
|
|
||||||
const rows = await qb
|
const rows = await qb
|
||||||
.init(populateValue)
|
.init(on?.[type] ?? typePopulate)
|
||||||
.addSelect(`${qb.alias}.${idColumn.referencedColumn}`)
|
.addSelect(`${qb.alias}.${idColumn.referencedColumn}`)
|
||||||
.where({ [idColumn.referencedColumn]: ids })
|
.where({ [idColumn.referencedColumn]: ids })
|
||||||
.execute({ mapResults: false });
|
.execute({ mapResults: false });
|
||||||
@ -579,7 +588,16 @@ const morphToOne = async (input, ctx) => {
|
|||||||
|
|
||||||
// TODO: Omit limit & offset to avoid needing a query per result to avoid making too many queries
|
// TODO: Omit limit & offset to avoid needing a query per result to avoid making too many queries
|
||||||
const pickPopulateParams = (populate) => {
|
const pickPopulateParams = (populate) => {
|
||||||
const fieldsToPick = ['select', 'count', 'where', 'populate', 'orderBy', 'filters', 'ordering'];
|
const fieldsToPick = [
|
||||||
|
'select',
|
||||||
|
'count',
|
||||||
|
'where',
|
||||||
|
'populate',
|
||||||
|
'orderBy',
|
||||||
|
'filters',
|
||||||
|
'ordering',
|
||||||
|
'on',
|
||||||
|
];
|
||||||
|
|
||||||
if (populate.count !== true) {
|
if (populate.count !== true) {
|
||||||
fieldsToPick.push('limit', 'offset');
|
fieldsToPick.push('limit', 'offset');
|
||||||
|
@ -108,6 +108,11 @@ const fixtures = {
|
|||||||
number: 2,
|
number: 2,
|
||||||
field: 'short string',
|
field: 'short string',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
__component: 'default.foo',
|
||||||
|
number: 3,
|
||||||
|
field: 'long string',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
__component: 'default.bar',
|
__component: 'default.bar',
|
||||||
title: 'this is a title',
|
title: 'this is a title',
|
||||||
@ -315,4 +320,100 @@ describe('Populate filters', () => {
|
|||||||
expect(body.data[0].attributes.third).toBeUndefined();
|
expect(body.data[0].attributes.third).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Populate a dynamic zone', () => {
|
||||||
|
test('Populate every component in the dynamic zone', async () => {
|
||||||
|
const qs = {
|
||||||
|
populate: {
|
||||||
|
dz: '*',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, body } = await rq.get(`/${schemas.contentTypes.b.pluralName}`, { qs });
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.data).toHaveLength(2);
|
||||||
|
|
||||||
|
fixtures.b.forEach((fixture, i) => {
|
||||||
|
const res = body.data[i];
|
||||||
|
const { dz } = res.attributes;
|
||||||
|
|
||||||
|
expect(dz).toHaveLength(fixture.dz.length);
|
||||||
|
expect(dz).toMatchObject(
|
||||||
|
fixture.dz.map((component) => ({
|
||||||
|
...omit('field', component),
|
||||||
|
id: expect.any(Number),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Populate only one component type using fragment', async () => {
|
||||||
|
const qs = {
|
||||||
|
populate: {
|
||||||
|
dz: {
|
||||||
|
on: {
|
||||||
|
'default.foo': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, body } = await rq.get(`/${schemas.contentTypes.b.pluralName}`, { qs });
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.data).toHaveLength(2);
|
||||||
|
|
||||||
|
expect(body.data[0].attributes.dz).toHaveLength(3);
|
||||||
|
expect(body.data[1].attributes.dz).toHaveLength(0);
|
||||||
|
|
||||||
|
const expected = fixtures.b[0].dz
|
||||||
|
.filter(({ __component }) => __component === 'default.foo')
|
||||||
|
.map((component) => ({
|
||||||
|
...component,
|
||||||
|
id: expect.any(Number),
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(body.data[0].attributes.dz).toMatchObject(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Populate the dynamic zone with filters in fragments', async () => {
|
||||||
|
const qs = {
|
||||||
|
populate: {
|
||||||
|
dz: {
|
||||||
|
on: {
|
||||||
|
'default.foo': {
|
||||||
|
filters: { number: { $lt: 3 } },
|
||||||
|
},
|
||||||
|
'default.bar': {
|
||||||
|
filters: { title: { $contains: 'another' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, body } = await rq.get(`/${schemas.contentTypes.b.pluralName}`, { qs });
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.data).toHaveLength(2);
|
||||||
|
expect(body.data[0].attributes.dz).toHaveLength(2);
|
||||||
|
expect(body.data[1].attributes.dz).toHaveLength(1);
|
||||||
|
|
||||||
|
const filter = (data = []) =>
|
||||||
|
data
|
||||||
|
.filter(({ __component, number, title }) => {
|
||||||
|
if (__component === 'default.foo') return number < 3;
|
||||||
|
if (__component === 'default.bar') return title.includes('another');
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map((component) => ({
|
||||||
|
...(component.__component === 'default.foo' ? component : omit('field', component)),
|
||||||
|
id: expect.any(Number),
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(body.data[0].attributes.dz).toMatchObject(filter(fixtures.b[0].dz));
|
||||||
|
expect(body.data[1].attributes.dz).toMatchObject(filter(fixtures.b[1].dz));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -115,7 +115,6 @@ describe('Uploads folder (GraphQL)', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -170,7 +169,6 @@ describe('Uploads folder (GraphQL)', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -255,7 +253,6 @@ describe('Uploads folder (GraphQL)', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -310,7 +307,6 @@ describe('Uploads folder (GraphQL)', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
|
@ -118,7 +118,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -163,7 +162,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -230,7 +228,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -276,7 +273,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -360,7 +356,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -408,7 +403,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
|
|
||||||
uploadFolder = file.folder;
|
uploadFolder = file.folder;
|
||||||
});
|
});
|
||||||
@ -563,7 +557,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(files.every((file) => file.folder.id === files[0].folder.id)).toBe(true);
|
expect(files.every((file) => file.folder.id === files[0].folder.id)).toBe(true);
|
||||||
@ -625,7 +618,6 @@ describe('Uploads folder', () => {
|
|||||||
},
|
},
|
||||||
folderPath: `/${file.folder.pathId}`,
|
folderPath: `/${file.folder.pathId}`,
|
||||||
});
|
});
|
||||||
expect(file.folder.id).not.toBe(uploadFolder.id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(files.every((file) => file.folder.id === files[0].folder.id)).toBe(true);
|
expect(files.every((file) => file.folder.id === files[0].folder.id)).toBe(true);
|
||||||
|
@ -104,12 +104,16 @@ const isPrivateAttribute = (model = {}, attributeName) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isScalarAttribute = (attribute) => {
|
const isScalarAttribute = (attribute) => {
|
||||||
return !['media', 'component', 'relation', 'dynamiczone'].includes(attribute.type);
|
return !['media', 'component', 'relation', 'dynamiczone'].includes(attribute?.type);
|
||||||
|
};
|
||||||
|
const isMediaAttribute = (attribute) => attribute?.type === 'media';
|
||||||
|
const isRelationalAttribute = (attribute) => attribute?.type === 'relation';
|
||||||
|
const isComponentAttribute = (attribute) => ['component', 'dynamiczone'].includes(attribute?.type);
|
||||||
|
|
||||||
|
const isDynamicZoneAttribute = (attribute) => attribute?.type === 'dynamiczone';
|
||||||
|
const isMorphToRelationalAttribute = (attribute) => {
|
||||||
|
return isRelationalAttribute(attribute) && attribute?.relation?.startsWith?.('morphTo');
|
||||||
};
|
};
|
||||||
const isMediaAttribute = (attribute) => attribute && attribute.type === 'media';
|
|
||||||
const isRelationalAttribute = (attribute) => attribute && attribute.type === 'relation';
|
|
||||||
const isComponentAttribute = (attribute) =>
|
|
||||||
attribute && ['component', 'dynamiczone'].includes(attribute.type);
|
|
||||||
|
|
||||||
const getComponentAttributes = (schema) => {
|
const getComponentAttributes = (schema) => {
|
||||||
return _.reduce(
|
return _.reduce(
|
||||||
@ -158,6 +162,8 @@ module.exports = {
|
|||||||
isMediaAttribute,
|
isMediaAttribute,
|
||||||
isRelationalAttribute,
|
isRelationalAttribute,
|
||||||
isComponentAttribute,
|
isComponentAttribute,
|
||||||
|
isDynamicZoneAttribute,
|
||||||
|
isMorphToRelationalAttribute,
|
||||||
isTypedAttribute,
|
isTypedAttribute,
|
||||||
getPrivateAttributes,
|
getPrivateAttributes,
|
||||||
isPrivateAttribute,
|
isPrivateAttribute,
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
* Converts the standard Strapi REST query params to a more usable format for querying
|
* Converts the standard Strapi REST query params to a more usable format for querying
|
||||||
* You can read more here: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#filters
|
* You can read more here: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#filters
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
isNil,
|
||||||
|
toNumber,
|
||||||
|
isInteger,
|
||||||
has,
|
has,
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isObject,
|
isObject,
|
||||||
@ -14,14 +18,16 @@ const {
|
|||||||
cloneDeep,
|
cloneDeep,
|
||||||
get,
|
get,
|
||||||
mergeAll,
|
mergeAll,
|
||||||
isNil,
|
|
||||||
toNumber,
|
|
||||||
isInteger,
|
|
||||||
} = require('lodash/fp');
|
} = require('lodash/fp');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const parseType = require('./parse-type');
|
const parseType = require('./parse-type');
|
||||||
const contentTypesUtils = require('./content-types');
|
const contentTypesUtils = require('./content-types');
|
||||||
const { PaginationError } = require('./errors');
|
const { PaginationError } = require('./errors');
|
||||||
|
const {
|
||||||
|
isMediaAttribute,
|
||||||
|
isDynamicZoneAttribute,
|
||||||
|
isMorphToRelationalAttribute,
|
||||||
|
} = require('./content-types');
|
||||||
|
|
||||||
const { PUBLISHED_AT_ATTRIBUTE } = contentTypesUtils.constants;
|
const { PUBLISHED_AT_ATTRIBUTE } = contentTypesUtils.constants;
|
||||||
|
|
||||||
@ -185,8 +191,31 @@ const convertPopulateObject = (populate, schema) => {
|
|||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This is a temporary solution for dynamic zones that should be
|
// Allow adding an 'on' strategy to populate queries for polymorphic relations, media and dynamic zones
|
||||||
// fixed when we'll implement a more accurate way to query them
|
const isAllowedAttributeForFragmentPopulate =
|
||||||
|
isDynamicZoneAttribute(attribute) ||
|
||||||
|
isMediaAttribute(attribute) ||
|
||||||
|
isMorphToRelationalAttribute(attribute);
|
||||||
|
|
||||||
|
const hasFragmentPopulateDefined = typeof subPopulate === 'object' && 'on' in subPopulate;
|
||||||
|
|
||||||
|
if (isAllowedAttributeForFragmentPopulate && hasFragmentPopulateDefined) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[key]: {
|
||||||
|
on: Object.entries(subPopulate.on).reduce(
|
||||||
|
(acc, [type, typeSubPopulate]) => ({
|
||||||
|
...acc,
|
||||||
|
[type]: convertNestedPopulate(typeSubPopulate, strapi.getModel(type)),
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is a query's populate fallback for DynamicZone and is kept for legacy purpose.
|
||||||
|
// Removing it could break existing user queries but it should be removed in V5.
|
||||||
if (attribute.type === 'dynamiczone') {
|
if (attribute.type === 'dynamiczone') {
|
||||||
const populates = attribute.components
|
const populates = attribute.components
|
||||||
.map((uid) => strapi.getModel(uid))
|
.map((uid) => strapi.getModel(uid))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user