Merge pull request #21428 from strapi/fix/cm-relations

fix: cm relations
This commit is contained in:
Alexandre BODIN 2024-09-27 10:22:45 +02:00 committed by GitHub
commit 9a4af3a3cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 225 additions and 64 deletions

View File

@ -120,6 +120,7 @@ const EditViewPage = () => {
return transformDocument(schema, components)(form); return transformDocument(schema, components)(form);
}, [document, isCreatingDocument, isSingleType, schema, components]); }, [document, isCreatingDocument, isSingleType, schema, components]);
if (hasError) { if (hasError) {
return <Page.Error />; return <Page.Error />;
} }

View File

@ -19,9 +19,10 @@ import {
ButtonProps, ButtonProps,
} from '@strapi/design-system'; } from '@strapi/design-system';
import { Cross, More, WarningCircle } from '@strapi/icons'; import { Cross, More, WarningCircle } from '@strapi/icons';
import mapValues from 'lodash/fp/mapValues';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useMatch, useNavigate } from 'react-router-dom'; import { useMatch, useNavigate } from 'react-router-dom';
import { styled, DefaultTheme } from 'styled-components'; import { DefaultTheme } from 'styled-components';
import { PUBLISHED_AT_ATTRIBUTE_NAME } from '../../../constants/attributes'; import { PUBLISHED_AT_ATTRIBUTE_NAME } from '../../../constants/attributes';
import { SINGLE_TYPES } from '../../../constants/collections'; import { SINGLE_TYPES } from '../../../constants/collections';
@ -35,6 +36,7 @@ import { getTranslation } from '../../../utils/translations';
import type { RelationsFormValue } from './FormInputs/Relations'; import type { RelationsFormValue } from './FormInputs/Relations';
import type { DocumentActionComponent } from '../../../content-manager'; import type { DocumentActionComponent } from '../../../content-manager';
/* ------------------------------------------------------------------------------------------------- /* -------------------------------------------------------------------------------------------------
* Types * Types
* -----------------------------------------------------------------------------------------------*/ * -----------------------------------------------------------------------------------------------*/
@ -493,6 +495,22 @@ const DocumentActionModal = ({
); );
}; };
const transformData = (data: Record<string, any>): any => {
if (Array.isArray(data)) {
return data.map(transformData);
}
if (typeof data === 'object' && data !== null) {
if ('apiData' in data) {
return data.apiData;
}
return mapValues(transformData)(data);
}
return data;
};
/* ------------------------------------------------------------------------------------------------- /* -------------------------------------------------------------------------------------------------
* DocumentActionComponents * DocumentActionComponents
* -----------------------------------------------------------------------------------------------*/ * -----------------------------------------------------------------------------------------------*/
@ -643,7 +661,7 @@ const PublishAction: DocumentActionComponent = ({
documentId, documentId,
params, params,
}, },
formValues transformData(formValues)
); );
if ('data' in res && collectionType !== SINGLE_TYPES) { if ('data' in res && collectionType !== SINGLE_TYPES) {
@ -797,7 +815,7 @@ const UpdateAction: DocumentActionComponent = ({
documentId: cloneMatch.params.origin!, documentId: cloneMatch.params.origin!,
params, params,
}, },
document transformData(document)
); );
if ('data' in res) { if ('data' in res) {
@ -823,7 +841,7 @@ const UpdateAction: DocumentActionComponent = ({
documentId, documentId,
params, params,
}, },
document transformData(document)
); );
if ( if (
@ -841,7 +859,7 @@ const UpdateAction: DocumentActionComponent = ({
model, model,
params, params,
}, },
document transformData(document)
); );
if ('data' in res && collectionType !== SINGLE_TYPES) { if ('data' in res && collectionType !== SINGLE_TYPES) {

View File

@ -84,7 +84,13 @@ function useHandleDisconnect(fieldName: string, consumerName: string) {
} }
} }
addFieldRow(`${fieldName}.disconnect`, { id: relation.id }); addFieldRow(`${fieldName}.disconnect`, {
id: relation.id,
apiData: {
documentId: relation.documentId,
locale: relation.locale,
},
});
}; };
return handleDisconnect; return handleDisconnect;
@ -145,14 +151,23 @@ const RelationsField = React.forwardRef<HTMLDivElement, RelationsFieldProps>(
const isMorph = props.attribute.relation.toLowerCase().includes('morph'); const isMorph = props.attribute.relation.toLowerCase().includes('morph');
const isDisabled = isMorph || disabled; const isDisabled = isMorph || disabled;
const { id: componentId, uid } = useComponent('RelationsField', ({ uid, id }) => ({ id, uid })); const { componentId, componentUID } = useComponent('RelationsField', ({ uid, id }) => ({
componentId: id,
componentUID: uid,
}));
const isSubmitting = useForm('RelationsList', (state) => state.isSubmitting);
React.useEffect(() => {
setCurrentPage(1);
}, [isSubmitting]);
/** /**
* We'll always have a documentId in a created entry, so we look for a componentId first. * We'll always have a documentId in a created entry, so we look for a componentId first.
* Same with `uid` and `documentModel`. * Same with `uid` and `documentModel`.
*/ */
const id = componentId ? componentId.toString() : documentId; const id = componentId ? componentId.toString() : documentId;
const model = uid ?? documentModel; const model = componentUID ?? documentModel;
/** /**
* The `name` prop is a complete path to the field, e.g. `field1.field2.field3`. * The `name` prop is a complete path to the field, e.g. `field1.field2.field3`.
@ -199,6 +214,7 @@ const RelationsField = React.forwardRef<HTMLDivElement, RelationsFieldProps>(
const realServerRelationsCount = const realServerRelationsCount =
'pagination' in data && data.pagination ? data.pagination.total : 0; 'pagination' in data && data.pagination ? data.pagination.total : 0;
/** /**
* Items that are already connected, but reordered would be in * Items that are already connected, but reordered would be in
* this list, so to get an accurate figure, we remove them. * this list, so to get an accurate figure, we remove them.
@ -259,6 +275,10 @@ const RelationsField = React.forwardRef<HTMLDivElement, RelationsFieldProps>(
const item = { const item = {
id: relation.id, id: relation.id,
apiData: {
documentId: relation.documentId,
locale: relation.locale,
},
status: relation.status, status: relation.status,
/** /**
* If there's a last item, that's the first key we use to generate out next one. * If there's a last item, that's the first key we use to generate out next one.
@ -268,7 +288,7 @@ const RelationsField = React.forwardRef<HTMLDivElement, RelationsFieldProps>(
[props.mainField?.name ?? 'documentId']: relation[props.mainField?.name ?? 'documentId'], [props.mainField?.name ?? 'documentId']: relation[props.mainField?.name ?? 'documentId'],
label: getRelationLabel(relation, props.mainField), label: getRelationLabel(relation, props.mainField),
// @ts-expect-error targetModel does exist on the attribute, but it's not typed. // @ts-expect-error targetModel does exist on the attribute, but it's not typed.
href: `../${COLLECTION_TYPES}/${props.attribute.targetModel}/${relation.documentId}`, href: `../${COLLECTION_TYPES}/${props.attribute.targetModel}/${relation.documentId}?${relation.locale ? `plugins[i18n][locale]=${relation.locale}` : ''}`,
}; };
if (ONE_WAY_RELATIONS.includes(props.attribute.relation)) { if (ONE_WAY_RELATIONS.includes(props.attribute.relation)) {
@ -294,7 +314,8 @@ const RelationsField = React.forwardRef<HTMLDivElement, RelationsFieldProps>(
<StyledFlex direction="column" alignItems="start" gap={2} width="100%"> <StyledFlex direction="column" alignItems="start" gap={2} width="100%">
<RelationsInput <RelationsInput
disabled={isDisabled} disabled={isDisabled}
id={id} // NOTE: we should not default to using the documentId if the component is being created (componentUID is undefined)
id={componentUID ? (componentId ? `${componentId}` : '') : documentId}
label={`${label} ${relationsCount > 0 ? `(${relationsCount})` : ''}`} label={`${label} ${relationsCount > 0 ? `(${relationsCount})` : ''}`}
model={model} model={model}
onChange={handleConnect} onChange={handleConnect}
@ -389,7 +410,7 @@ const addLabelAndHref =
// Fallback to `id` if there is no `mainField` value, which will overwrite the above `documentId` property with the exact same data. // Fallback to `id` if there is no `mainField` value, which will overwrite the above `documentId` property with the exact same data.
[mainField?.name ?? 'documentId']: relation[mainField?.name ?? 'documentId'], [mainField?.name ?? 'documentId']: relation[mainField?.name ?? 'documentId'],
label: getRelationLabel(relation, mainField), label: getRelationLabel(relation, mainField),
href: `${href}/${relation.documentId}`, href: `${href}/${relation.documentId}?${relation.locale ? `plugins[i18n][locale]=${relation.locale}` : ''}`,
}; };
}); });
@ -454,6 +475,7 @@ const RelationsInput = ({
* individual components. Therefore we split the string and take the last item. * individual components. Therefore we split the string and take the last item.
*/ */
const [targetField] = name.split('.').slice(-1); const [targetField] = name.split('.').slice(-1);
searchForTrigger({ searchForTrigger({
model, model,
targetField, targetField,
@ -701,9 +723,7 @@ const RelationsList = ({
*/ */
const connectedRelations = newData const connectedRelations = newData
.reduce<Relation[]>((acc, relation, currentIndex, array) => { .reduce<Relation[]>((acc, relation, currentIndex, array) => {
const relationOnServer = serverData.find( const relationOnServer = serverData.find((oldRelation) => oldRelation.id === relation.id);
(oldRelation) => oldRelation.documentId === relation.documentId
);
const relationInFront = array[currentIndex + 1]; const relationInFront = array[currentIndex + 1];
@ -712,11 +732,23 @@ const RelationsList = ({
? { ? {
before: relationInFront.documentId, before: relationInFront.documentId,
locale: relationInFront.locale, locale: relationInFront.locale,
status: relationInFront.status, status:
'publishedAt' in relationInFront && relationInFront.publishedAt
? 'published'
: 'draft',
} }
: { end: true }; : { end: true };
const relationWithPosition: Relation = { ...relation, position }; const relationWithPosition: Relation = {
...relation,
...{
apiData: {
documentId: relation.documentId,
locale: relation.locale,
position,
},
},
};
return [...acc, relationWithPosition]; return [...acc, relationWithPosition];
} }
@ -899,7 +931,7 @@ const ListItem = ({ data, index, style }: ListItemProps) => {
} = data; } = data;
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const { href, documentId, label, status } = relations[index]; const { href, id, label, status } = relations[index];
const [{ handlerId, isDragging, handleKeyDown }, relationRef, dropRef, dragRef, dragPreviewRef] = const [{ handlerId, isDragging, handleKeyDown }, relationRef, dropRef, dragRef, dragPreviewRef] =
useDragAndDrop<number, Omit<RelationDragPreviewProps, 'width'>, HTMLDivElement>( useDragAndDrop<number, Omit<RelationDragPreviewProps, 'width'>, HTMLDivElement>(
@ -910,7 +942,7 @@ const ListItem = ({ data, index, style }: ListItemProps) => {
item: { item: {
displayedValue: label, displayedValue: label,
status, status,
id: documentId, id: id,
index, index,
}, },
onMoveItem: handleMoveItem, onMoveItem: handleMoveItem,

View File

@ -67,11 +67,8 @@ const relationsApi = contentManagerApi.injectEndpoints({
* Relations will always have unique IDs, so we can therefore assume * Relations will always have unique IDs, so we can therefore assume
* that we only need to push the new items to the cache. * that we only need to push the new items to the cache.
*/ */
const existingIds = currentCache.results.map((item) => item.documentId);
const uniqueNewItems = newItems.results.filter( currentCache.results.push(...prepareTempKeys(newItems.results, currentCache.results));
(item) => !existingIds.includes(item.documentId)
);
currentCache.results.push(...prepareTempKeys(uniqueNewItems, currentCache.results));
currentCache.pagination = newItems.pagination; currentCache.pagination = newItems.pagination;
} else if (newItems.pagination.page === 1) { } else if (newItems.pagination.page === 1) {
/** /**

View File

@ -49,17 +49,39 @@ const sanitizeMainField = (model: any, mainField: any, userAbility: any) => {
return mainField; return mainField;
}; };
const addStatusToRelations = async (uid: UID.ContentType, relations: RelationEntity[]) => { /**
if (!contentTypes.hasDraftAndPublish(strapi.contentTypes[uid])) { *
* All relations sent to this function should have the same status or no status
*/
const addStatusToRelations = async (targetUid: UID.Schema, relations: RelationEntity[]) => {
if (!contentTypes.hasDraftAndPublish(strapi.getModel(targetUid))) {
return relations; return relations;
} }
const documentMetadata = getService('document-metadata'); const documentMetadata = getService('document-metadata');
const documentsAvailableStatus = await documentMetadata.getManyAvailableStatus(uid, relations);
if (!relations.length) {
return relations;
}
const firstRelation = relations[0];
const filters: any = {
documentId: { $in: relations.map((r) => r.documentId) },
// NOTE: find the "opposite" status
publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true },
};
const availableStatus = await strapi.query(targetUid).findMany({
select: ['id', 'documentId', 'locale', 'updatedAt', 'createdAt', 'publishedAt'],
filters,
});
return relations.map((relation: RelationEntity) => { return relations.map((relation: RelationEntity) => {
const availableStatuses = documentsAvailableStatus.filter( const availableStatuses = availableStatus.filter(
(availableDocument: RelationEntity) => availableDocument.documentId === relation.documentId (availableDocument: RelationEntity) =>
availableDocument.documentId === relation.documentId &&
(relation.locale ? availableDocument.locale === relation.locale : true)
); );
return { return {
@ -396,14 +418,14 @@ export default {
attribute, attribute,
targetField, targetField,
fieldsToSelect, fieldsToSelect,
source: { status,
schema: { uid: sourceUid }, source: { schema: sourceSchema },
}, target: { schema: targetSchema },
target: {
schema: { uid: targetUid },
},
} = await this.extractAndValidateRequestInfo(ctx, id); } = await this.extractAndValidateRequestInfo(ctx, id);
const { uid: sourceUid } = sourceSchema;
const { uid: targetUid } = targetSchema;
const permissionQuery = await getService('permission-checker') const permissionQuery = await getService('permission-checker')
.create({ userAbility, model: targetUid }) .create({ userAbility, model: targetUid })
.sanitizedQuery.read({ fields: fieldsToSelect }); .sanitizedQuery.read({ fields: fieldsToSelect });
@ -424,6 +446,23 @@ export default {
// Ensure response is an array // Ensure response is an array
.then((res) => ({ results: res ? [res] : [] })); .then((res) => ({ results: res ? [res] : [] }));
const filters: {
publishedAt?: Record<string, any>;
} = {};
if (sourceSchema?.options?.draftAndPublish) {
if (targetSchema?.options?.draftAndPublish) {
if (status === 'published') {
filters.publishedAt = { $notNull: true };
} else {
filters.publishedAt = { $null: true };
}
}
} else if (targetSchema?.options?.draftAndPublish) {
// NOTE: we must return the drafts as some targets might not have a published version yet
filters.publishedAt = { $null: true };
}
/** /**
* If user does not have access to specific relations (custom conditions), * If user does not have access to specific relations (custom conditions),
* only the ids of the relations are returned. * only the ids of the relations are returned.
@ -434,10 +473,11 @@ export default {
* The response contains the union of the two queries. * The response contains the union of the two queries.
*/ */
const res = await loadRelations({ id: entryId }, targetField, { const res = await loadRelations({ id: entryId }, targetField, {
select: ['id', 'documentId', 'locale', 'publishedAt'], select: ['id', 'documentId', 'locale', 'publishedAt', 'updatedAt'],
ordering: 'desc', ordering: 'desc',
page: ctx.request.query.page, page: ctx.request.query.page,
pageSize: ctx.request.query.pageSize, pageSize: ctx.request.query.pageSize,
filters,
}); });
/** /**
@ -458,6 +498,7 @@ export default {
ordering: 'desc', ordering: 'desc',
}); });
// NOTE: the order is very import to make sure sanitized relations are kept in priority
const relationsUnion = uniqBy('id', concat(sanitizedRes.results, res.results)); const relationsUnion = uniqBy('id', concat(sanitizedRes.results, res.results));
ctx.body = { ctx.body = {

View File

@ -7,11 +7,11 @@ import type { DocumentMetadata } from '../../../shared/contracts/collection-type
import { getValidatableFieldsPopulate } from './utils/populate'; import { getValidatableFieldsPopulate } from './utils/populate';
export interface DocumentVersion { export interface DocumentVersion {
id: number; id: string | number;
documentId: Modules.Documents.ID; documentId: Modules.Documents.ID;
locale: string; locale?: string;
updatedAt: string | null | Date; updatedAt?: string | null | Date;
publishedAt: string | null | Date; publishedAt?: string | null | Date;
} }
const AVAILABLE_STATUS_FIELDS = [ const AVAILABLE_STATUS_FIELDS = [
@ -86,7 +86,9 @@ export default ({ strapi }: { strapi: Core.Strapi }) => ({
const versionsByLocale = groupBy('locale', allVersions); const versionsByLocale = groupBy('locale', allVersions);
// Delete the current locale // Delete the current locale
delete versionsByLocale[version.locale]; if (version.locale) {
delete versionsByLocale[version.locale];
}
// For each locale, get the ones with the same status // For each locale, get the ones with the same status
// There will not be a draft and a version counterpart if the content // There will not be a draft and a version counterpart if the content

View File

@ -1,8 +1,11 @@
import '@strapi/types'; import '@strapi/types';
import { DocumentManagerService } from 'src/services/document-manager'; import { DocumentManagerService } from 'src/services/document-manager';
import DocumentMetadata from 'src/services/document-metadata';
type Services = { type Services = {
'document-manager': DocumentManagerService; 'document-manager': DocumentManagerService;
'document-metadata': typeof DocumentMetadata;
[key: string]: any; [key: string]: any;
}; };

View File

@ -291,7 +291,10 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
]); ]);
// Load any unidirectional relation targetting the old published entries // Load any unidirectional relation targetting the old published entries
const relationsToSync = await unidirectionalRelations.load(uid, oldPublishedVersions); const relationsToSync = await unidirectionalRelations.load(uid, {
newVersions: draftsToPublish,
oldVersions: oldPublishedVersions,
});
// Delete old published versions // Delete old published versions
await async.map(oldPublishedVersions, (entry: any) => entries.delete(entry.id)); await async.map(oldPublishedVersions, (entry: any) => entries.delete(entry.id));
@ -302,7 +305,11 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
); );
// Sync unidirectional relations with the new published entries // Sync unidirectional relations with the new published entries
await unidirectionalRelations.sync(oldPublishedVersions, publishedEntries, relationsToSync); await unidirectionalRelations.sync(
[...oldPublishedVersions, ...draftsToPublish],
publishedEntries,
relationsToSync
);
publishedEntries.forEach(emitEvent('entry.publish')); publishedEntries.forEach(emitEvent('entry.publish'));
@ -358,7 +365,10 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
]); ]);
// Load any unidirectional relation targeting the old drafts // Load any unidirectional relation targeting the old drafts
const relationsToSync = await unidirectionalRelations.load(uid, oldDrafts); const relationsToSync = await unidirectionalRelations.load(uid, {
newVersions: versionsToDraft,
oldVersions: oldDrafts,
});
// Delete old drafts // Delete old drafts
await async.map(oldDrafts, (entry: any) => entries.delete(entry.id)); await async.map(oldDrafts, (entry: any) => entries.delete(entry.id));
@ -369,7 +379,11 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
); );
// Sync unidirectional relations with the new draft entries // Sync unidirectional relations with the new draft entries
await unidirectionalRelations.sync(oldDrafts, draftEntries, relationsToSync); await unidirectionalRelations.sync(
[...oldDrafts, ...versionsToDraft],
draftEntries,
relationsToSync
);
draftEntries.forEach(emitEvent('entry.draft-discard')); draftEntries.forEach(emitEvent('entry.draft-discard'));
return { documentId, entries: draftEntries }; return { documentId, entries: draftEntries };

View File

@ -1,18 +1,19 @@
/* eslint-disable no-continue */ /* eslint-disable no-continue */
import { keyBy } from 'lodash/fp'; import { keyBy, omit } from 'lodash/fp';
import { UID, Schema } from '@strapi/types'; import { UID, Schema } from '@strapi/types';
interface LoadContext {
oldVersions: { id: string; locale: string }[];
newVersions: { id: string; locale: string }[];
}
/** /**
* Loads lingering relations that need to be updated when overriding a published or draft entry. * 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 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. * 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 load = async (uid: UID.ContentType, { oldVersions, newVersions }: LoadContext) => {
const updates = [] as any; const updates = [] as any;
// Iterate all components and content types to find relations that need to be updated // Iterate all components and content types to find relations that need to be updated
@ -27,29 +28,80 @@ const load = async (uid: UID.ContentType, oldEntries: { id: string; locale: stri
/** /**
* Only consider unidirectional relations * Only consider unidirectional relations
*/ */
if (attribute.type !== 'relation') continue; if (
if (attribute.target !== uid) continue; attribute.type !== 'relation' ||
if (attribute.inversedBy || attribute.mappedBy) continue; attribute.target !== uid ||
const joinTable = attribute.joinTable; attribute.inversedBy ||
// TODO: joinColumn relations attribute.mappedBy
if (!joinTable) continue; ) {
continue;
}
const { name } = joinTable.inverseJoinColumn; // TODO: joinColumn relations
const joinTable = attribute.joinTable;
if (!joinTable) {
continue;
}
const { name: sourceColumnName } = joinTable.joinColumn;
const { name: targetColumnName } = joinTable.inverseJoinColumn;
/** /**
* Load all relations that need to be updated * Load all relations that need to be updated
*/ */
const oldEntriesIds = oldEntries.map((entry) => entry.id); // NOTE: when the model has draft and publish, we can assume relation are only draft to draft & published to published
const relations = await strapi.db const ids = oldVersions.map((entry) => entry.id);
const oldVersionsRelations = await strapi.db
.getConnection() .getConnection()
.select('*') .select('*')
.from(joinTable.name) .from(joinTable.name)
.whereIn(name, oldEntriesIds) .whereIn(targetColumnName, ids)
.transacting(trx); .transacting(trx);
if (relations.length === 0) continue; if (oldVersionsRelations.length > 0) {
updates.push({ joinTable, relations: oldVersionsRelations });
}
updates.push({ joinTable, relations }); /**
* if publishing
* if published version exists
* updated published versions links
* else
* create link to newly published version
*
* if discarding
* if published version link exists & not draft version link
* create link to new draft version
*/
if (!model.options?.draftAndPublish) {
const ids = newVersions.map((entry) => entry.id);
const newVersionsRelations = await strapi.db
.getConnection()
.select('*')
.from(joinTable.name)
.whereIn(targetColumnName, ids)
.transacting(trx);
if (newVersionsRelations.length > 0) {
// when publishing a draft that doesn't have a published version yet,
// copy the links to the draft over to the published version
// when discarding a published version, if no drafts exists
const discardToAdd = newVersionsRelations
.filter((relation) => {
const matchingOldVerion = oldVersionsRelations.find((oldRelation) => {
return oldRelation[sourceColumnName] === relation[sourceColumnName];
});
return !matchingOldVerion;
})
.map(omit('id'));
updates.push({ joinTable, relations: discardToAdd });
}
}
} }
} }
}); });
@ -89,8 +141,9 @@ const sync = async (
// Iterate old relations that are deleted and insert the new ones // Iterate old relations that are deleted and insert the new ones
for (const { joinTable, relations } of oldRelations) { for (const { joinTable, relations } of oldRelations) {
// Update old ids with the new ones // Update old ids with the new ones
const column = joinTable.inverseJoinColumn.name;
const newRelations = relations.map((relation) => { const newRelations = relations.map((relation) => {
const column = joinTable.inverseJoinColumn.name;
const newId = oldEntriesMap[relation[column]]; const newId = oldEntriesMap[relation[column]];
return { ...relation, [column]: newId }; return { ...relation, [column]: newId };
}); });

View File

@ -138,7 +138,7 @@ describe('Relation permissions', () => {
const shopEntry = await createEntry( const shopEntry = await createEntry(
'api::shop.shop', 'api::shop.shop',
{ name: 'Shop', products: [product.id] }, { name: 'Shop', products: [product.documentId] },
populateShop populateShop
); );
shop = shopEntry.data; shop = shopEntry.data;