chore: create updateEntry

This commit is contained in:
Alexandre Bodin 2024-04-05 11:33:35 +02:00
parent 5e5bcf8c8f
commit 2b548ef558
6 changed files with 77 additions and 79 deletions

View File

@ -137,11 +137,9 @@ describe('history-version service', () => {
contentType: {
uid: 'api::article.article',
},
args: [
{
locale: 'fr',
},
],
params: {
locale: 'fr',
},
};
const next = jest.fn((context) => ({ ...context, documentId: 'document-id' }));
@ -154,7 +152,8 @@ describe('history-version service', () => {
expect(next).toHaveBeenCalled();
// Ensure we're only storing the data we need in the database
expect(mockFindOne).toHaveBeenLastCalledWith('document-id', {
expect(mockFindOne).toHaveBeenLastCalledWith({
documentId: 'document-id',
locale: 'fr',
populate: {
component: {

View File

@ -308,7 +308,7 @@ const createHistoryService = ({ strapi }: { strapi: Core.Strapi }) => {
if ('documentId' in entry) {
relatedEntry = await strapi
.documents(attributeSchema.target)
.findOne(entry.documentId, { locale: entry.locale || undefined });
.findOne({ documentId: entry.documentId, locale: entry.locale || undefined });
}
// For media assets, only the id is available, double check that we have it
} else if ('id' in entry) {

View File

@ -105,7 +105,8 @@ describe('Default Service', () => {
expect(dbInstance.findOne).toHaveBeenCalledWith();
expect(documentService.update).toHaveBeenCalledWith(1, {
expect(documentService.update).toHaveBeenCalledWith({
documentId: 1,
data: input,
status: 'published',
});
@ -137,7 +138,7 @@ describe('Default Service', () => {
expect(dbInstance.findOne).toHaveBeenCalledWith();
expect(documentService.delete).toHaveBeenCalledWith(1, { status: 'published' });
expect(documentService.delete).toHaveBeenCalledWith({ documentId: 1, status: 'published' });
});
});
});

View File

@ -19,8 +19,6 @@ type ComponentBody = {
[key: string]: ComponentValue | DynamicZoneValue;
};
const isDialectMySQL = () => strapi.db?.dialect.client === 'mysql';
function omitComponentData(
contentType: Struct.ContentTypeSchema,
data: Modules.EntityService.Params.Data.Input<Struct.ContentTypeSchema['uid']>
@ -83,11 +81,9 @@ const createComponents = async <
}
// MySQL/MariaDB can cause deadlocks here if concurrency higher than 1
const components = (await async.map(
componentValue,
(value: any) => createComponent(componentUID, value),
{ concurrency: isDialectMySQL() && !strapi.db?.inTransaction() ? 1 : Infinity }
)) as RepeatableComponentValue;
const components: RepeatableComponentValue = await async.map(componentValue, (value: any) =>
createComponent(componentUID, value)
);
componentBody[attributeName] = components.map(({ id }) => {
return {
@ -103,6 +99,7 @@ const createComponents = async <
componentUID,
componentValue as Modules.EntityService.Params.Data.Input<UID.Component>
);
componentBody[attributeName] = {
id: component.id,
__pivot: {
@ -140,8 +137,7 @@ const createComponents = async <
// MySQL/MariaDB can cause deadlocks here if concurrency higher than 1
componentBody[attributeName] = await async.map(
dynamiczoneValues,
createDynamicZoneComponents,
{ concurrency: isDialectMySQL() && !strapi.db?.inTransaction() ? 1 : Infinity }
createDynamicZoneComponents
);
continue;
@ -200,11 +196,9 @@ const updateComponents = async <
}
// MySQL/MariaDB can cause deadlocks here if concurrency higher than 1
const components = (await async.map(
componentValue,
(value: any) => updateOrCreateComponent(componentUID, value),
{ concurrency: isDialectMySQL() && !strapi.db?.inTransaction() ? 1 : Infinity }
)) as RepeatableComponentValue;
const components: RepeatableComponentValue = await async.map(componentValue, (value: any) =>
updateOrCreateComponent(componentUID, value)
);
componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }) => {
return {
@ -235,21 +229,17 @@ const updateComponents = async <
}
// MySQL/MariaDB can cause deadlocks here if concurrency higher than 1
componentBody[attributeName] = await async.map(
dynamiczoneValues,
async (value: any) => {
const { id } = await updateOrCreateComponent(value.__component, value);
componentBody[attributeName] = await async.map(dynamiczoneValues, async (value: any) => {
const { id } = await updateOrCreateComponent(value.__component, value);
return {
id,
__component: value.__component,
__pivot: {
field: attributeName,
},
};
},
{ concurrency: isDialectMySQL() && !strapi.db?.inTransaction() ? 1 : Infinity }
);
return {
id,
__component: value.__component,
__pivot: {
field: attributeName,
},
};
});
}
}
@ -379,20 +369,14 @@ const deleteComponents = async <TUID extends UID.Schema, TEntity extends Data.En
if (attribute.type === 'component') {
const { component: componentUID } = attribute;
// MySQL/MariaDB can cause deadlocks here if concurrency higher than 1
await async.map(
_.castArray(value),
(subValue: any) => deleteComponent(componentUID, subValue),
{
concurrency: isDialectMySQL() && !strapi.db?.inTransaction() ? 1 : Infinity,
}
await async.map(_.castArray(value), (subValue: any) =>
deleteComponent(componentUID, subValue)
);
} else {
// delete dynamic zone components
// MySQL/MariaDB can cause deadlocks here if concurrency higher than 1
await async.map(
_.castArray(value),
(subValue: any) => deleteComponent(subValue.__component, subValue),
{ concurrency: isDialectMySQL() && !strapi.db?.inTransaction() ? 1 : Infinity }
await async.map(_.castArray(value), (subValue: any) =>
deleteComponent(subValue.__component, subValue)
);
}
@ -469,8 +453,21 @@ const deleteComponent = async <TUID extends UID.Component>(
await strapi.db.query(uid).delete({ where: { id: componentToDelete.id } });
};
const assignComponentData = <TUID extends UID.ContentType>(
data: Modules.EntityService.Params.Data.Input<TUID>,
componentData: ComponentBody,
{
contentType,
}: {
contentType: Schema.ContentType<TUID>;
}
) => {
return Object.assign(omitComponentData(contentType, data), componentData);
};
export {
omitComponentData,
assignComponentData,
getComponents,
createComponents,
updateComponents,

View File

@ -29,11 +29,10 @@ const createEntriesService = (uid: UID.ContentType) => {
// Component handling
const componentData = await components.createComponents(uid, validData);
const contentTypeWithoutComponentData = components.omitComponentData(contentType, validData);
const entryData = applyTransforms(
Object.assign(contentTypeWithoutComponentData, componentData) as any,
{ contentType }
);
const dataWithComponents = components.assignComponentData(validData, componentData, {
contentType,
});
const entryData = applyTransforms(dataWithComponents, { contentType });
const doc = await strapi.db.query(uid).create({ ...query, data: entryData });
@ -48,9 +47,35 @@ const createEntriesService = (uid: UID.ContentType) => {
await components.deleteComponents(uid, componentsToDelete as any, { loadComponents: false });
}
async function updateEntry(entryToUpdate: any, params = {} as any) {
const { data, ...restParams } = await transformParamsDocumentId(uid, params);
const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate
const validData = await entityValidator.validateEntityUpdate(
contentType,
data,
{
isDraft: !params?.data?.publishedAt, // Always update the draft version
locale: params?.locale,
},
entryToUpdate
);
// Component handling
const componentData = await components.updateComponents(uid, entryToUpdate, validData as any);
const dataWithComponents = components.assignComponentData(validData, componentData, {
contentType,
});
const entryData = applyTransforms(dataWithComponents, { contentType });
return strapi.db
.query(uid)
.update({ ...query, where: { id: entryToUpdate.id }, data: entryData });
}
return {
create: createEntry,
delete: deleteEntry,
update: updateEntry,
};
};

View File

@ -5,18 +5,15 @@ import { async, contentTypes as contentTypesUtils } from '@strapi/utils';
import { wrapInTransaction, type RepositoryFactoryMethod } from './common';
import * as DP from './draft-and-publish';
import * as i18n from './internationalization';
import { transformParamsDocumentId } from './transform/id-transform';
import * as components from './components';
import { createEntriesService } from './entries';
import { createEntriesService } from './entries';
import { pickSelectionParams } from './params';
import { applyTransforms } from './attributes';
import entityValidator from '../entity-validator';
import { createDocumentId } from '../../utils/transform-content-types-to-models';
import { getDeepPopulate } from './utils/populate';
import { transformData } from './transform/data';
import { transformParamsToQuery } from './transform/query';
import { transformParamsDocumentId } from './transform/id-transform';
export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
const contentType = strapi.contentType(uid);
@ -167,7 +164,6 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
const query = transformParamsToQuery(uid, pickSelectionParams(restParams || {}) as any);
// Validation
const model = strapi.contentType(uid);
// Find if document exists
const entryToUpdate = await strapi.db
.query(uid)
@ -175,27 +171,7 @@ export const createContentTypeRepository: RepositoryFactoryMethod = (uid) => {
let updatedDraft = null;
if (entryToUpdate) {
const validData = await entityValidator.validateEntityUpdate(
model,
// @ts-expect-error we need type guard to assert that data has the valid type
data,
{
isDraft: !queryParams?.data?.publishedAt, // Always update the draft version
locale: queryParams?.locale,
},
entryToUpdate
);
// Component handling
const componentData = await updateComponents(entryToUpdate, validData as any);
const entryData = applyTransforms(
Object.assign(omitComponentData(validData), componentData as any),
{ contentType: model }
);
updatedDraft = await strapi.db
.query(uid)
.update({ ...query, where: { id: entryToUpdate.id }, data: entryData });
updatedDraft = await entries.update(entryToUpdate, queryParams);
}
if (!updatedDraft) {