fix: cm relation bugs (#19842)

* fix: relations

* fix: hardcode english locale

* fix(cm): don't try and be clever with caching relations

* fix: locale when target is not localized

---------

Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
This commit is contained in:
Marc Roig 2024-03-19 14:57:36 +01:00 committed by GitHub
parent 49e84a90ad
commit 66c1011328
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 82 additions and 67 deletions

View File

@ -61,10 +61,7 @@ const documentApi = contentManagerApi.injectEndpoints({
}), }),
invalidatesTags: (result, _error, { model }) => [ invalidatesTags: (result, _error, { model }) => [
{ type: 'Document', id: `${model}_LIST` }, { type: 'Document', id: `${model}_LIST` },
{ 'Relations',
type: 'Relations',
id: `${model}_${result?.data.documentId}`,
},
], ],
}), }),
deleteDocument: builder.mutation< deleteDocument: builder.mutation<
@ -127,10 +124,7 @@ const documentApi = contentManagerApi.injectEndpoints({
id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model, id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model,
}, },
{ type: 'Document', id: `${model}_LIST` }, { type: 'Document', id: `${model}_LIST` },
{ 'Relations',
type: 'Relations',
id: `${model}_${documentId}`,
},
]; ];
}, },
}), }),
@ -278,10 +272,7 @@ const documentApi = contentManagerApi.injectEndpoints({
id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model, id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model,
}, },
{ type: 'Document', id: `${model}_LIST` }, { type: 'Document', id: `${model}_LIST` },
{ 'Relations',
type: 'Relations',
id: `${model}_${documentId}`,
},
]; ];
}, },
}), }),
@ -321,10 +312,7 @@ const documentApi = contentManagerApi.injectEndpoints({
type: 'Document', type: 'Document',
id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model, id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model,
}, },
{ 'Relations',
type: 'Relations',
id: `${model}_${documentId}`,
},
]; ];
}, },
}), }),

View File

@ -97,9 +97,7 @@ const relationsApi = contentManagerApi.injectEndpoints({
return response; return response;
} }
}, },
providesTags: (result, error, args) => [ providesTags: ['Relations'],
{ type: 'Relations', id: `${args.model}_${args.id}` },
],
}), }),
searchRelations: build.query< searchRelations: build.query<
Contracts.Relations.FindAvailable.Response, Contracts.Relations.FindAvailable.Response,

View File

@ -78,42 +78,66 @@ const addStatusToRelations = async (uid: Common.UID.ContentType, relations: Rela
}); });
}; };
const getPublishedAtClause = ( const getPublishedAtClause = (status: string, uid: Common.UID.Schema) => {
status: Documents.Params.PublicationStatus.Kind, const model = strapi.getModel(uid);
sourceUid: Common.UID.Schema,
targetUid: Common.UID.ContentType
) => {
const sourceModel = strapi.getModel(sourceUid);
const targetModel = strapi.getModel(targetUid);
/** /**
* If target does not have dp, status should be published. * If dp is disabled, ignore the filter
* As it only contains entries with publishedAt set.
*/ */
if (!contentTypes.hasDraftAndPublish(targetModel)) { if (!model || !contentTypes.hasDraftAndPublish(model)) {
return {}; return {};
} }
/**
* If source does not have dp, status should be draft.
* Both draft and publish entries are connectable, but we are doing it like
* this atm.
*/
if (!contentTypes.hasDraftAndPublish(sourceModel)) {
return { $null: true };
}
// Prioritize the draft status in case it's not provided // Prioritize the draft status in case it's not provided
return status === 'published' ? { $notNull: true } : { $null: true }; return status === 'published' ? { $notNull: true } : { $null: true };
}; };
const validateLocale = (
sourceUid: Common.UID.Schema,
targetUid: Common.UID.ContentType,
locale?: string
) => {
const sourceModel = strapi.getModel(sourceUid);
const targetModel = strapi.getModel(targetUid);
const isLocalized = strapi.plugin('i18n').service('content-types').isLocalizedContentType;
const isSourceLocalized = isLocalized(sourceModel);
const isTargetLocalized = isLocalized(targetModel);
let validatedLocale = locale;
if (!targetModel || !isTargetLocalized) validatedLocale = undefined;
return {
locale: validatedLocale,
isSourceLocalized,
isTargetLocalized,
};
};
const validateStatus = (
sourceUid: Common.UID.Schema,
status?: Documents.Params.PublicationStatus.Kind
) => {
const sourceModel = strapi.getModel(sourceUid);
const isDP = contentTypes.hasDraftAndPublish;
const isSourceDP = isDP(sourceModel);
// Default to draft if not set
if (!isSourceDP) return { status: undefined };
switch (status) {
case 'published':
return { status: 'published' };
default:
// Assign to draft if the status is not valid
return { status: 'draft' };
}
};
export default { export default {
async extractAndValidateRequestInfo( async extractAndValidateRequestInfo(ctx: any, id?: Entity.ID) {
ctx: any,
id?: Entity.ID,
locale?: Documents.Params.Locale,
status?: Documents.Params.PublicationStatus.Kind
) {
const { userAbility } = ctx.state; const { userAbility } = ctx.state;
const { model, targetField } = ctx.params; const { model, targetField } = ctx.params;
@ -129,6 +153,16 @@ export default {
); );
} }
const sourceUid = model;
const targetUid = attribute.target;
const { locale, isSourceLocalized, isTargetLocalized } = validateLocale(
sourceUid,
targetUid,
ctx.request?.query?.locale
);
const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
const permissionChecker = getService('permission-checker').create({ const permissionChecker = getService('permission-checker').create({
userAbility, userAbility,
model, model,
@ -150,13 +184,9 @@ export default {
where.documentId = id; where.documentId = id;
if (status) { if (status) {
where.publishedAt = getPublishedAtClause(status, sourceSchema.uid, attribute.target); where.publishedAt = getPublishedAtClause(status, sourceUid);
} }
const isSourceLocalized = strapi
.plugin('i18n')
.service('content-types')
.isLocalizedContentType(sourceSchema);
if (locale && isSourceLocalized) { if (locale && isSourceLocalized) {
where.locale = locale; where.locale = locale;
} }
@ -197,7 +227,7 @@ export default {
? await getService('components').findConfiguration(sourceSchema) ? await getService('components').findConfiguration(sourceSchema)
: await getService('content-types').findConfiguration(sourceSchema); : await getService('content-types').findConfiguration(sourceSchema);
const targetSchema = strapi.getModel(attribute.target); const targetSchema = strapi.getModel(targetUid);
const mainField = flow( const mainField = flow(
prop(`metadatas.${targetField}.edit.mainField`), prop(`metadatas.${targetField}.edit.mainField`),
@ -212,18 +242,14 @@ export default {
'documentId', 'documentId',
]); ]);
const isTargetLocalized = strapi
.plugin('i18n')
.service('content-types')
.isLocalizedContentType(targetSchema);
// TODO: Locale is always present, should we set it regardless?
if (isTargetLocalized) { if (isTargetLocalized) {
fieldsToSelect.push('locale'); fieldsToSelect.push('locale');
} }
return { return {
entryId, entryId,
locale,
status,
attribute, attribute,
fieldsToSelect, fieldsToSelect,
mainField, mainField,
@ -242,12 +268,12 @@ export default {
*/ */
async findAvailable(ctx: any) { async findAvailable(ctx: any) {
const { id } = ctx.request.query; const { id } = ctx.request.query;
const locale = ctx.request?.query?.locale || null;
const status = ctx.request?.query?.status;
await validateFindAvailable(ctx.request.query); await validateFindAvailable(ctx.request.query);
const { const {
locale,
status,
targetField, targetField,
fieldsToSelect, fieldsToSelect,
mainField, mainField,
@ -258,7 +284,7 @@ export default {
schema: { uid: targetUid }, schema: { uid: targetUid },
isLocalized: isTargetLocalized, isLocalized: isTargetLocalized,
}, },
} = await this.extractAndValidateRequestInfo(ctx, id, locale, status); } = await this.extractAndValidateRequestInfo(ctx, id);
const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query; const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
@ -278,7 +304,7 @@ export default {
// If no status is requested, we find all the draft relations and later update them // If no status is requested, we find all the draft relations and later update them
// with the latest available status // with the latest available status
addFiltersClause(queryParams, { addFiltersClause(queryParams, {
publishedAt: getPublishedAtClause(status, sourceUid, targetUid), publishedAt: getPublishedAtClause(status, targetUid),
}); });
// We will only filter by locale if the target content type is localized // We will only filter by locale if the target content type is localized
@ -316,7 +342,7 @@ export default {
// Add the status and locale filters if they are provided // Add the status and locale filters if they are provided
if (status) { if (status) {
where[`${alias}.published_at`] = getPublishedAtClause(status, sourceUid, targetUid); where[`${alias}.published_at`] = getPublishedAtClause(status, targetUid);
} }
if (filterByLocale) { if (filterByLocale) {
where[`${alias}.locale`] = locale; where[`${alias}.locale`] = locale;
@ -374,9 +400,6 @@ export default {
await validateFindExisting(ctx.request.query); await validateFindExisting(ctx.request.query);
const locale = ctx.request?.query?.locale || null;
const status = ctx.request?.query?.status;
const { const {
entryId, entryId,
attribute, attribute,
@ -388,7 +411,7 @@ export default {
target: { target: {
schema: { uid: targetUid }, schema: { uid: targetUid },
}, },
} = await this.extractAndValidateRequestInfo(ctx, id, locale, status); } = await this.extractAndValidateRequestInfo(ctx, id);
const permissionQuery = await getService('permission-checker') const permissionQuery = await getService('permission-checker')
.create({ userAbility, model: targetUid }) .create({ userAbility, model: targetUid })
@ -443,7 +466,12 @@ export default {
const relationsUnion = uniqBy('id', concat(sanitizedRes.results, res.results)); const relationsUnion = uniqBy('id', concat(sanitizedRes.results, res.results));
ctx.body = { ctx.body = {
pagination: res.pagination, pagination: res.pagination || {
page: 1,
pageCount: 1,
pageSize: 10,
total: relationsUnion.length,
},
results: await addStatusToRelations(targetUid, relationsUnion), results: await addStatusToRelations(targetUid, relationsUnion),
}; };
}, },

View File

@ -9,7 +9,8 @@ export const isLocalizedContentType = (uid: Common.UID.Schema) => {
export const getDefaultLocale = () => { export const getDefaultLocale = () => {
// TODO: V5 make this more performant // TODO: V5 make this more performant
return strapi.plugin('i18n').service('locales').getDefaultLocale(); // return strapi.plugin('i18n').service('locales').getDefaultLocale();
return 'en';
}; };
export const getRelationTargetLocale = ( export const getRelationTargetLocale = (