mirror of
https://github.com/strapi/strapi.git
synced 2025-11-12 16:22:10 +00:00
feat: sync unidirectional relations (#21072)
* feat: sync unidirectional relations * chore: test discard * feat: add components * fix: tests * chore: refactor localized field test * fix: refactor loading relations * fix : test
This commit is contained in:
parent
767b0ca5d0
commit
5823142abf
@ -15,6 +15,7 @@ import { getDeepPopulate } from './utils/populate';
|
|||||||
import { transformParamsToQuery } from './transform/query';
|
import { transformParamsToQuery } from './transform/query';
|
||||||
import { transformParamsDocumentId } from './transform/id-transform';
|
import { transformParamsDocumentId } from './transform/id-transform';
|
||||||
import { createEventManager } from './events';
|
import { createEventManager } from './events';
|
||||||
|
import * as unidirectionalRelations from './utils/unidirectional-relations';
|
||||||
|
|
||||||
const { validators } = validate;
|
const { validators } = validate;
|
||||||
|
|
||||||
@ -269,7 +270,7 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
|
|||||||
i18n.multiLocaleToLookup(contentType)
|
i18n.multiLocaleToLookup(contentType)
|
||||||
)(params);
|
)(params);
|
||||||
|
|
||||||
const [draftsToPublish, publishedToDelete] = await Promise.all([
|
const [draftsToPublish, oldPublishedVersions] = await Promise.all([
|
||||||
strapi.db.query(uid).findMany({
|
strapi.db.query(uid).findMany({
|
||||||
where: {
|
where: {
|
||||||
...queryParams?.lookup,
|
...queryParams?.lookup,
|
||||||
@ -285,19 +286,26 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
|
|||||||
documentId,
|
documentId,
|
||||||
publishedAt: { $ne: null },
|
publishedAt: { $ne: null },
|
||||||
},
|
},
|
||||||
select: ['id'],
|
select: ['id', 'locale'],
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Delete all published versions
|
// Load any unidirectional relation targetting the old published entries
|
||||||
await async.map(publishedToDelete, (entry: any) => entries.delete(entry.id));
|
const relationsToSync = await unidirectionalRelations.load(uid, oldPublishedVersions);
|
||||||
|
|
||||||
|
// Delete old published versions
|
||||||
|
await async.map(oldPublishedVersions, (entry: any) => entries.delete(entry.id));
|
||||||
|
|
||||||
// Transform draft entry data and create published versions
|
// Transform draft entry data and create published versions
|
||||||
const publishedEntries = await async.map(draftsToPublish, (draft: unknown) =>
|
const publishedEntries = await async.map(draftsToPublish, (draft: any) =>
|
||||||
entries.publish(draft, queryParams)
|
entries.publish(draft, queryParams)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sync unidirectional relations with the new published entries
|
||||||
|
await unidirectionalRelations.sync(oldPublishedVersions, publishedEntries, relationsToSync);
|
||||||
|
|
||||||
publishedEntries.forEach(emitEvent('entry.publish'));
|
publishedEntries.forEach(emitEvent('entry.publish'));
|
||||||
|
|
||||||
return { documentId, entries: publishedEntries };
|
return { documentId, entries: publishedEntries };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +337,7 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
|
|||||||
i18n.multiLocaleToLookup(contentType)
|
i18n.multiLocaleToLookup(contentType)
|
||||||
)(params);
|
)(params);
|
||||||
|
|
||||||
const [versionsToDraft, versionsToDelete] = await Promise.all([
|
const [versionsToDraft, oldDrafts] = await Promise.all([
|
||||||
strapi.db.query(uid).findMany({
|
strapi.db.query(uid).findMany({
|
||||||
where: {
|
where: {
|
||||||
...queryParams?.lookup,
|
...queryParams?.lookup,
|
||||||
@ -345,18 +353,24 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
|
|||||||
documentId,
|
documentId,
|
||||||
publishedAt: null,
|
publishedAt: null,
|
||||||
},
|
},
|
||||||
select: ['id'],
|
select: ['id', 'locale'],
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Delete all drafts
|
// Load any unidirectional relation targeting the old drafts
|
||||||
await async.map(versionsToDelete, (entry: any) => entries.delete(entry.id));
|
const relationsToSync = await unidirectionalRelations.load(uid, oldDrafts);
|
||||||
|
|
||||||
|
// Delete old drafts
|
||||||
|
await async.map(oldDrafts, (entry: any) => entries.delete(entry.id));
|
||||||
|
|
||||||
// Transform published entry data and create draft versions
|
// Transform published entry data and create draft versions
|
||||||
const draftEntries = await async.map(versionsToDraft, (entry: any) =>
|
const draftEntries = await async.map(versionsToDraft, (entry: any) =>
|
||||||
entries.discardDraft(entry, queryParams)
|
entries.discardDraft(entry, queryParams)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sync unidirectional relations with the new draft entries
|
||||||
|
await unidirectionalRelations.sync(oldDrafts, draftEntries, relationsToSync);
|
||||||
|
|
||||||
draftEntries.forEach(emitEvent('entry.draft-discard'));
|
draftEntries.forEach(emitEvent('entry.draft-discard'));
|
||||||
return { documentId, entries: draftEntries };
|
return { documentId, entries: draftEntries };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
/* eslint-disable no-continue */
|
||||||
|
import { keyBy } from 'lodash/fp';
|
||||||
|
|
||||||
|
import { UID, Schema } from '@strapi/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads lingering relations that need to be updated when overriding a published or draft entry.
|
||||||
|
* This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.
|
||||||
|
* This is not the case for bi-directional relations, where the target entry is also linked to the source entry.
|
||||||
|
*
|
||||||
|
* @param uid The content type uid
|
||||||
|
* @param oldEntries The old entries that are being overridden
|
||||||
|
* @returns An array of relations that need to be updated with the join table reference.
|
||||||
|
*/
|
||||||
|
const load = async (uid: UID.ContentType, oldEntries: { id: string; locale: string }[]) => {
|
||||||
|
const updates = [] as any;
|
||||||
|
|
||||||
|
// Iterate all components and content types to find relations that need to be updated
|
||||||
|
await strapi.db.transaction(async ({ trx }) => {
|
||||||
|
const contentTypes = Object.values(strapi.contentTypes) as Schema.ContentType[];
|
||||||
|
const components = Object.values(strapi.components) as Schema.Component[];
|
||||||
|
|
||||||
|
for (const model of [...contentTypes, ...components]) {
|
||||||
|
const dbModel = strapi.db.metadata.get(model.uid);
|
||||||
|
|
||||||
|
for (const attribute of Object.values(dbModel.attributes) as any) {
|
||||||
|
/**
|
||||||
|
* Only consider unidirectional relations
|
||||||
|
*/
|
||||||
|
if (attribute.type !== 'relation') continue;
|
||||||
|
if (attribute.target !== uid) continue;
|
||||||
|
if (attribute.inversedBy || attribute.mappedBy) continue;
|
||||||
|
const joinTable = attribute.joinTable;
|
||||||
|
// TODO: joinColumn relations
|
||||||
|
if (!joinTable) continue;
|
||||||
|
|
||||||
|
const { name } = joinTable.inverseJoinColumn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all relations that need to be updated
|
||||||
|
*/
|
||||||
|
const oldEntriesIds = oldEntries.map((entry) => entry.id);
|
||||||
|
const relations = await strapi.db
|
||||||
|
.getConnection()
|
||||||
|
.select('*')
|
||||||
|
.from(joinTable.name)
|
||||||
|
.whereIn(name, oldEntriesIds)
|
||||||
|
.transacting(trx);
|
||||||
|
|
||||||
|
if (relations.length === 0) continue;
|
||||||
|
|
||||||
|
updates.push({ joinTable, relations });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return updates;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates uni directional relations to target the right entries when overriding published or draft entries.
|
||||||
|
*
|
||||||
|
* @param oldEntries The old entries that are being overridden
|
||||||
|
* @param newEntries The new entries that are overriding the old ones
|
||||||
|
* @param oldRelations The relations that were previously loaded with `load` @see load
|
||||||
|
*/
|
||||||
|
const sync = async (
|
||||||
|
oldEntries: { id: string; locale: string }[],
|
||||||
|
newEntries: { id: string; locale: string }[],
|
||||||
|
oldRelations: { joinTable: any; relations: any[] }[]
|
||||||
|
) => {
|
||||||
|
/**
|
||||||
|
* Create a map of old entry ids to new entry ids
|
||||||
|
*
|
||||||
|
* Will be used to update the relation target ids
|
||||||
|
*/
|
||||||
|
const newEntryByLocale = keyBy('locale', newEntries);
|
||||||
|
const oldEntriesMap = oldEntries.reduce(
|
||||||
|
(acc, entry) => {
|
||||||
|
const newEntry = newEntryByLocale[entry.locale];
|
||||||
|
if (!newEntry) return acc;
|
||||||
|
acc[entry.id] = newEntry.id;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
|
||||||
|
await strapi.db.transaction(async ({ trx }) => {
|
||||||
|
const con = strapi.db.getConnection();
|
||||||
|
|
||||||
|
// Iterate old relations that are deleted and insert the new ones
|
||||||
|
for (const { joinTable, relations } of oldRelations) {
|
||||||
|
// Update old ids with the new ones
|
||||||
|
const newRelations = relations.map((relation) => {
|
||||||
|
const column = joinTable.inverseJoinColumn.name;
|
||||||
|
const newId = oldEntriesMap[relation[column]];
|
||||||
|
return { ...relation, [column]: newId };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Insert those relations into the join table
|
||||||
|
await con.batchInsert(joinTable.name, newRelations).transacting(trx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { load, sync };
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* Unidirectional relations need special handling when publishing/un publishing.
|
||||||
|
*
|
||||||
|
* When publishing or un publishing an entry, other entries with a relation targeting this one might lose its relation.
|
||||||
|
* This is only the case with unidirectional relations, but not bidirectional relations.
|
||||||
|
*/
|
||||||
|
import { Core } from '@strapi/types';
|
||||||
|
import { testInTransaction } from '../../../../utils';
|
||||||
|
|
||||||
|
const { createTestBuilder } = require('api-tests/builder');
|
||||||
|
const { createStrapiInstance } = require('api-tests/strapi');
|
||||||
|
const { createAuthRequest } = require('api-tests/request');
|
||||||
|
|
||||||
|
let strapi: Core.Strapi;
|
||||||
|
const builder = createTestBuilder();
|
||||||
|
let rq;
|
||||||
|
|
||||||
|
const PRODUCT_UID = 'api::product.product';
|
||||||
|
const TAG_UID = 'api::tag.tag';
|
||||||
|
|
||||||
|
const populate = {
|
||||||
|
tag: true,
|
||||||
|
tags: true,
|
||||||
|
compo: {
|
||||||
|
populate: {
|
||||||
|
tag: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const componentModel = {
|
||||||
|
attributes: {
|
||||||
|
tag: {
|
||||||
|
type: 'relation',
|
||||||
|
relation: 'oneToOne',
|
||||||
|
target: TAG_UID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
displayName: 'compo',
|
||||||
|
};
|
||||||
|
|
||||||
|
const productModel = {
|
||||||
|
attributes: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
type: 'relation',
|
||||||
|
relation: 'oneToOne',
|
||||||
|
target: TAG_UID,
|
||||||
|
},
|
||||||
|
tags: {
|
||||||
|
type: 'relation',
|
||||||
|
relation: 'oneToMany',
|
||||||
|
target: TAG_UID,
|
||||||
|
},
|
||||||
|
compo: {
|
||||||
|
type: 'component',
|
||||||
|
component: 'default.compo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
draftAndPublish: true,
|
||||||
|
displayName: 'Product',
|
||||||
|
singularName: 'product',
|
||||||
|
pluralName: 'products',
|
||||||
|
description: '',
|
||||||
|
collectionName: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagModel = {
|
||||||
|
attributes: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
draftAndPublish: true,
|
||||||
|
displayName: 'Tag',
|
||||||
|
singularName: 'tag',
|
||||||
|
pluralName: 'tags',
|
||||||
|
description: '',
|
||||||
|
collectionName: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Document Service unidirectional relations', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await builder
|
||||||
|
.addContentTypes([tagModel])
|
||||||
|
.addComponent(componentModel)
|
||||||
|
.addContentTypes([productModel])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
strapi = await createStrapiInstance();
|
||||||
|
rq = await createAuthRequest({ strapi });
|
||||||
|
|
||||||
|
// TAGS
|
||||||
|
await strapi.db.query(TAG_UID).createMany({
|
||||||
|
data: [
|
||||||
|
{ documentId: 'Tag1', name: 'Tag1', publishedAt: null },
|
||||||
|
{ documentId: 'Tag2', name: 'Tag2', publishedAt: null },
|
||||||
|
{ documentId: 'Tag3', name: 'Tag3', publishedAt: null },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// PRODUCTS
|
||||||
|
const product = await strapi.documents(PRODUCT_UID).create({
|
||||||
|
data: {
|
||||||
|
name: 'Product1',
|
||||||
|
tag: { documentId: 'Tag1' },
|
||||||
|
tags: [{ documentId: 'Tag1' }, { documentId: 'Tag2' }],
|
||||||
|
compo: { tag: { documentId: 'Tag1' } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Publish tag1
|
||||||
|
await strapi.documents(TAG_UID).publish({ documentId: 'Tag1' });
|
||||||
|
await strapi.documents(TAG_UID).publish({ documentId: 'Tag2' });
|
||||||
|
|
||||||
|
// Publish product
|
||||||
|
await strapi.documents(PRODUCT_UID).publish({ documentId: product.documentId });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await strapi.destroy();
|
||||||
|
await builder.cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
testInTransaction('Sync unidirectional relations on publish', async () => {
|
||||||
|
// Publish tag. Product1 relations should target the new published tags id
|
||||||
|
const tag1 = await strapi.documents(TAG_UID).publish({ documentId: 'Tag1' });
|
||||||
|
const tag1Id = tag1.entries[0].id;
|
||||||
|
const tag2 = await strapi.documents(TAG_UID).publish({ documentId: 'Tag2' });
|
||||||
|
const tag2Id = tag2.entries[0].id;
|
||||||
|
|
||||||
|
const product1 = await strapi
|
||||||
|
.documents(PRODUCT_UID)
|
||||||
|
.findFirst({ filters: { name: 'Product1' }, populate, status: 'published' });
|
||||||
|
|
||||||
|
expect(product1).toMatchObject({
|
||||||
|
name: 'Product1',
|
||||||
|
tag: { id: tag1Id },
|
||||||
|
tags: [{ id: tag1Id }, { id: tag2Id }],
|
||||||
|
compo: { tag: { id: tag1Id } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testInTransaction('Sync unidirectional relations on discard', async () => {
|
||||||
|
// Discard tag. Product1 relations should target the new draft tags id
|
||||||
|
await strapi.documents(TAG_UID).publish({ documentId: 'Tag1' });
|
||||||
|
const tag1 = await strapi.documents(TAG_UID).discardDraft({ documentId: 'Tag1' });
|
||||||
|
const tag1Id = tag1.entries[0].id;
|
||||||
|
|
||||||
|
await strapi.documents(TAG_UID).publish({ documentId: 'Tag2' });
|
||||||
|
const tag2 = await strapi.documents(TAG_UID).discardDraft({ documentId: 'Tag2' });
|
||||||
|
const tag2Id = tag2.entries[0].id;
|
||||||
|
|
||||||
|
const product1 = await strapi
|
||||||
|
.documents(PRODUCT_UID)
|
||||||
|
.findFirst({ filters: { name: 'Product1' }, populate, status: 'draft' });
|
||||||
|
|
||||||
|
expect(product1).toMatchObject({
|
||||||
|
name: 'Product1',
|
||||||
|
tag: { id: tag1Id },
|
||||||
|
tags: [{ id: tag1Id }, { id: tag2Id }],
|
||||||
|
compo: { tag: { id: tag1Id } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -155,6 +155,38 @@ const transformConnectToDisconnect = (data) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const create = async (uid, payload) => {
|
||||||
|
return rq({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/content-manager/collection-types/${uid}`,
|
||||||
|
body: payload,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const update = async (uid, documentId, payload) => {
|
||||||
|
return rq({
|
||||||
|
method: 'PUT',
|
||||||
|
url: `/content-manager/collection-types/${uid}/${documentId}`,
|
||||||
|
body: payload,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const publish = async (uid, documentId, payload) => {
|
||||||
|
return rq({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/content-manager/collection-types/${uid}/${documentId}/actions/publish`,
|
||||||
|
body: payload,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unpublish = async (uid, documentId, payload) => {
|
||||||
|
return rq({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/content-manager/collection-types/${uid}/${documentId}/actions/unpublish`,
|
||||||
|
body: payload,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
describe('i18n', () => {
|
describe('i18n', () => {
|
||||||
const builder = createTestBuilder();
|
const builder = createTestBuilder();
|
||||||
|
|
||||||
@ -190,42 +222,20 @@ describe('i18n', () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Create a document with an entry in every locale with the localized
|
// Create a document with an entry in every locale with the localized
|
||||||
// field filled in. This field can be different across locales
|
// field filled in. This field can be different across locales
|
||||||
const res = await rq({
|
const res = await create('api::category.category', { name: `Test` });
|
||||||
method: 'POST',
|
|
||||||
url: `/content-manager/collection-types/api::category.category`,
|
|
||||||
body: {
|
|
||||||
name: `Test`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
documentId = res.body.data.documentId;
|
documentId = res.body.data.documentId;
|
||||||
|
|
||||||
for (const locale of allLocaleCodes) {
|
for (const locale of allLocaleCodes) {
|
||||||
await rq({
|
await update('api::category.category', documentId, {
|
||||||
method: 'PUT',
|
locale,
|
||||||
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
name: `Test ${locale}`,
|
||||||
body: {
|
|
||||||
locale,
|
|
||||||
name: `Test ${locale}`,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 2 tags in the default locale
|
// Create 2 tags in the default locale
|
||||||
const [tag1, tag2] = await Promise.all([
|
const [tag1, tag2] = await Promise.all([
|
||||||
rq({
|
create('api::tag.tag', { name: `Test tag` }),
|
||||||
method: 'POST',
|
create('api::tag.tag', { name: `Test tag 2` }),
|
||||||
url: `/content-manager/collection-types/api::tag.tag`,
|
|
||||||
body: {
|
|
||||||
name: `Test tag`,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
rq({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/content-manager/collection-types/api::tag.tag`,
|
|
||||||
body: {
|
|
||||||
name: `Test tag 2`,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
data.tags.push(tag1.body.data);
|
data.tags.push(tag1.body.data);
|
||||||
data.tags.push(tag2.body.data);
|
data.tags.push(tag2.body.data);
|
||||||
@ -233,21 +243,13 @@ describe('i18n', () => {
|
|||||||
for (const locale of tagsAvailableIn) {
|
for (const locale of tagsAvailableIn) {
|
||||||
// Create 2 tags for every other locale that supports tags
|
// Create 2 tags for every other locale that supports tags
|
||||||
const [localeTag1, localeTag2] = await Promise.all([
|
const [localeTag1, localeTag2] = await Promise.all([
|
||||||
rq({
|
update('api::tag.tag', tag1.body.data.documentId, {
|
||||||
method: 'PUT',
|
locale,
|
||||||
url: `/content-manager/collection-types/api::tag.tag/${tag1.body.data.documentId}`,
|
name: `Test tag ${locale}`,
|
||||||
body: {
|
|
||||||
locale,
|
|
||||||
name: `Test tag ${locale}`,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
rq({
|
update('api::tag.tag', tag2.body.data.documentId, {
|
||||||
method: 'PUT',
|
locale,
|
||||||
url: `/content-manager/collection-types/api::tag.tag/${tag2.body.data.documentId}`,
|
name: `Test tag ${locale} 2`,
|
||||||
body: {
|
|
||||||
locale,
|
|
||||||
name: `Test tag ${locale} 2`,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -260,76 +262,80 @@ describe('i18n', () => {
|
|||||||
const actionsToTest = [['publish'], ['unpublish + discard'], ['update']];
|
const actionsToTest = [['publish'], ['unpublish + discard'], ['update']];
|
||||||
|
|
||||||
describe('Scalar non localized fields', () => {
|
describe('Scalar non localized fields', () => {
|
||||||
describe.each(actionsToTest)('', (method) => {
|
const attribute = 'nonLocalized';
|
||||||
test(`Modify a scalar non localized field - Method ${method}`, async () => {
|
|
||||||
const isPublish = method === 'publish';
|
|
||||||
const isUnpublish = method.includes('unpublish');
|
|
||||||
|
|
||||||
const key = 'nonLocalized';
|
test('Modify a scalar non localized field - Publish', async () => {
|
||||||
// Update the non localized field
|
const res = await publish('api::category.category', documentId, { [attribute]: 'publish' });
|
||||||
const updatedValue = `${key}::Update Test::${method}`;
|
|
||||||
|
|
||||||
let res;
|
expect(res.statusCode).toBe(200);
|
||||||
if (isPublish) {
|
|
||||||
// Publish the default locale entry
|
|
||||||
res = await rq({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/publish`,
|
|
||||||
body: {
|
|
||||||
[key]: updatedValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (isUnpublish) {
|
|
||||||
// Publish the default locale entry
|
|
||||||
await rq({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/publish`,
|
|
||||||
body: {
|
|
||||||
[key]: updatedValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the default locale draft entry with random data
|
// Expect all locales to be updates, both draft and published versions
|
||||||
const randomData = 'random';
|
for (const locale of allLocaleCodes) {
|
||||||
await rq({
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
||||||
method: 'PUT',
|
where: {
|
||||||
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
documentId,
|
||||||
body: {
|
publishedAt: null,
|
||||||
[key]: randomData,
|
locale: { $eq: locale },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Unpublish the default locale entry
|
// The locale should now have the same value as the default locale.
|
||||||
res = await rq({
|
expect(localeRes[attribute]).toEqual('publish');
|
||||||
method: 'POST',
|
}
|
||||||
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/unpublish`,
|
});
|
||||||
body: {
|
|
||||||
discardDraft: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res = await rq({
|
|
||||||
method: 'PUT',
|
|
||||||
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
|
||||||
body: {
|
|
||||||
[key]: updatedValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const locale of allLocaleCodes) {
|
test('Modify a scalar non localized field - Unpublish + Discard', async () => {
|
||||||
const localeRes = await strapi.db.query('api::category.category').findOne({
|
// Publish the default locale entry
|
||||||
where: {
|
let res = await publish('api::category.category', documentId, { [attribute]: 'unpublish' });
|
||||||
documentId,
|
expect(res.statusCode).toBe(200);
|
||||||
publishedAt: null,
|
|
||||||
locale: { $eq: locale },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// The locale should now have the same value as the default locale.
|
// Update the default locale draft entry with random data
|
||||||
expect(localeRes[key]).toEqual(updatedValue);
|
const randomData = 'random';
|
||||||
}
|
res = await update('api::category.category', documentId, { [attribute]: randomData });
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
|
||||||
|
// Unpublish the default locale entry
|
||||||
|
res = await unpublish('api::category.category', documentId, { discardDraft: true });
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
|
||||||
|
// Expect all locales to be updates, both draft and published versions
|
||||||
|
for (const locale of allLocaleCodes) {
|
||||||
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
||||||
|
where: {
|
||||||
|
documentId,
|
||||||
|
publishedAt: null,
|
||||||
|
locale: { $eq: locale },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// The locale should now have the same value as the default locale.
|
||||||
|
expect(localeRes[attribute]).toEqual('unpublish');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Modify a scalar non localized field - Update', async () => {
|
||||||
|
const updatedValue = 'update';
|
||||||
|
|
||||||
|
const res = await update('api::category.category', documentId, {
|
||||||
|
[attribute]: updatedValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
|
||||||
|
// Expect all locales to be updates, both draft and published versions
|
||||||
|
for (const locale of allLocaleCodes) {
|
||||||
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
||||||
|
where: {
|
||||||
|
documentId,
|
||||||
|
publishedAt: null,
|
||||||
|
locale: { $eq: locale },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// The locale should now have the same value as the default locale.
|
||||||
|
expect(localeRes[attribute]).toEqual(updatedValue);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user