chore: rename doc service

This commit is contained in:
Marc-Roig 2024-01-15 10:30:24 +01:00
parent cb1114d267
commit 83318fb940
No known key found for this signature in database
GPG Key ID: FB4E2C43A0BEE249
10 changed files with 481 additions and 481 deletions

View File

@ -65,7 +65,7 @@ import getNumberOfDynamicZones from './services/utils/dynamic-zones';
import convertCustomFieldType from './utils/convert-custom-field-type';
import { transformContentTypesToModels } from './utils/transform-content-types-to-models';
import { FeaturesService, createFeaturesService } from './services/features';
import { createDocumentEngine } from './services/document-service/document-engine';
import { createDocumentService } from './services/document-service/document-service';
/**
* Resolve the working directories based on the instance options.
@ -152,7 +152,7 @@ class Strapi extends Container implements StrapiI {
entityService?: EntityService.EntityService;
documents?: Documents.Repository;
documents?: Documents.Service;
telemetry: TelemetryService;
@ -512,7 +512,7 @@ class Strapi extends Container implements StrapiI {
entityValidator: this.entityValidator,
});
this.documents = createDocumentEngine(this);
this.documents = createDocumentService(this);
if (this.config.get('server.cron.enabled', true)) {
const cronTasks = this.config.get('server.cron.tasks', {});

View File

@ -1,159 +1,300 @@
import { Strapi, Common, Documents } from '@strapi/types';
import createDocumentRepository from './document-repository';
import createMiddlewareManager from './middlewares';
import { loadDefaultMiddlewares } from './middlewares/defaults';
import type { Database } from '@strapi/database';
import type { Common, Documents, Schema, Shared, Strapi } from '@strapi/types';
import { contentTypes as contentTypesUtils, convertQueryParams, mapAsync } from '@strapi/utils';
import { isArray, omit } from 'lodash/fp';
import uploadFiles from '../utils/upload-files';
import {
cloneComponents,
createComponents,
deleteComponents,
getComponents,
omitComponentData,
updateComponents,
} from '../entity-service/components';
import { createDocumentId } from '../../utils/transform-content-types-to-models';
import { applyTransforms } from '../entity-service/attributes';
import entityValidator from '../entity-validator';
import { pickSelectionParams } from './params';
const { transformParamsToQuery } = convertQueryParams;
/**
* Repository to :
* - Access documents via actions (findMany, findOne, create, update, delete, ...)
* - Execute middlewares on document actions
* - Apply default parameters to document actions
*
* @param strapi
* @param options.defaults - Default parameters to apply to all actions
* @param options.parent - Parent repository, used when creating a new repository with .with()
* @returns DocumentRepository
*
* @example Access documents
* const article = strapi.documents('api::article.article').create(params)
* const allArticles = strapi.documents('api::article.article').findMany(params)
* TODO: Sanitization / validation built-in
* TODO: i18n - Move logic to i18n package
* TODO: Webhooks
* TODO: Audit logs
* TODO: Entity Validation - Uniqueness across same locale and publication status
* TODO: File upload
* TODO: replace 'any'
* TODO: availableLocales
*
*/
export const createDocumentEngine = (
strapi: Strapi,
{ defaults = {} }: { defaults?: any } = {}
): Documents.Engine => {
const documents = createDocumentRepository({ strapi, db: strapi.db! });
const middlewareManager = createMiddlewareManager();
loadDefaultMiddlewares(middlewareManager);
function create<TContentTypeUID extends Common.UID.ContentType>(
uid: TContentTypeUID
): Documents.EngineInstance<TContentTypeUID> {
return {
async findMany(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'findMany', uid, params, options: {} }, ({ params }) =>
documents.findMany(uid, params)
)
);
},
async findFirst(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'findFirst', uid, params, options: {} }, ({ params }) =>
documents.findFirst(uid, params)
)
);
},
async findOne(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'findOne', uid, params, options: { id } }, ({ params }) =>
documents.findOne(uid, id, params)
)
);
},
async delete(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'delete', uid, params, options: { id } }, ({ params }) =>
documents.delete(uid, id, params)
)
);
},
async deleteMany(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'deleteMany', uid, params, options: {} }, ({ params }) =>
documents.deleteMany(uid, params)
)
);
},
async create(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'create', uid, params, options: {} }, ({ params }) =>
documents.create(uid, params)
)
);
},
async clone(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'clone', uid, params, options: { id } }, ({ params }) =>
documents.clone(uid, id, params)
)
);
},
async update(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'update', uid, params, options: { id } }, ({ params }) =>
documents.update(uid, id, params)
)
);
},
async count(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'count', uid, params, options: {} }, ({ params }) =>
documents.count(uid, params)
)
);
},
async publish(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'publish', uid, params, options: { id } }, ({ params }) =>
documents.publish(uid, id, params)
)
);
},
async unpublish(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run(
{ action: 'unpublish', uid, params, options: { id } },
({ params }) => documents.unpublish(uid, id, params)
)
);
},
async discardDraft(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run(
{ action: 'discardDraft', uid, params, options: { id } },
({ params }) => documents.discardDraft(uid, id, params)
)
);
},
// @ts-expect-error - TODO: Fix this
with(params: object) {
return createDocumentEngine(strapi, {
defaults: { ...defaults, ...params },
})(uid);
},
use(action, cb, opts) {
middlewareManager.add(uid, action, cb, opts);
return this;
},
};
}
Object.assign(create, {
use(action: any, cb: any, opts?: any) {
middlewareManager.add('_all', action, cb, opts);
return create;
},
middlewares: middlewareManager,
// NOTE : We should do this in a different way, where lifecycles are executed for the different methods
...documents,
});
// @ts-expect-error - TODO: Fix this
return create;
type Context = {
contentType: Schema.ContentType;
};
const createPipeline = (data: Record<string, unknown>, context: Context) => {
return applyTransforms(data, context);
};
const updatePipeline = (data: Record<string, unknown>, context: Context) => {
return applyTransforms(data, context);
};
const createDocumentEngine = ({
strapi,
db,
}: {
strapi: Strapi;
db: Database;
}): Documents.Engine => ({
uploadFiles,
async findMany(uid, params) {
const { kind } = strapi.getModel(uid);
const query = transformParamsToQuery(uid, params || ({} as any));
query.where = { ...params?.lookup, ...query.where };
if (kind === 'singleType') {
return db.query(uid).findOne(query);
}
return db.query(uid).findMany(query);
},
async findFirst(uid, params) {
const query = transformParamsToQuery(uid, params || ({} as any));
return db.query(uid).findOne({ ...query, where: { ...params?.lookup, ...query.where } });
},
async findOne(uid, documentId, params) {
const query = transformParamsToQuery(uid, params || ({} as any));
return db
.query(uid)
.findOne({ ...query, where: { ...params?.lookup, ...query.where, documentId } });
},
async delete(uid, documentId, params = {} as any) {
const query = transformParamsToQuery(uid, params as any);
if (params.status === 'draft') {
throw new Error('Cannot delete a draft document');
}
const entriesToDelete = await db.query(uid).findMany({
...query,
where: {
...params.lookup,
...query?.where,
documentId,
},
});
// Delete all matched entries and its components
await mapAsync(entriesToDelete, async (entryToDelete: any) => {
const componentsToDelete = await getComponents(uid, entryToDelete);
await db.query(uid).delete({ where: { id: entryToDelete.id } });
await deleteComponents(uid, componentsToDelete as any, { loadComponents: false });
});
// TODO: Change return value to actual count
return { versions: entriesToDelete };
},
// TODO: should we provide two separate methods?
async deleteMany(uid, paramsOrIds) {
let queryParams;
if (isArray(paramsOrIds)) {
queryParams = { filter: { documentID: { $in: paramsOrIds } } };
} else {
queryParams = paramsOrIds;
}
const query = transformParamsToQuery(uid, queryParams || ({} as any));
return db.query(uid).deleteMany(query);
},
async create(uid, params) {
// TODO: Entity validator.
// TODO: File upload - Probably in the lifecycles?
const { data } = params;
if (!data) {
throw new Error('Create requires data attribute');
}
const model = strapi.getModel(uid) as Shared.ContentTypes[Common.UID.ContentType];
const validData = await entityValidator.validateEntityCreation(model, data, { isDraft: true });
const componentData = await createComponents(uid, validData);
const entryData = createPipeline(
Object.assign(omitComponentData(model, validData), componentData),
{
contentType: model,
}
);
// select / populate
const query = transformParamsToQuery(uid, pickSelectionParams(params) as any);
return db.query(uid).create({ ...query, data: entryData });
},
// NOTE: What happens if user doesn't provide specific publications state and locale to update?
async update(uid, documentId, params) {
// TODO: Prevent updating a published document
// TODO: Entity validator.
// TODO: File upload
const { data } = params || {};
const model = strapi.getModel(uid) as Shared.ContentTypes[Common.UID.ContentType];
const query = transformParamsToQuery(uid, pickSelectionParams(params || {}) as any);
// Find all locales of the document
const entryToUpdate = await db
.query(uid)
.findOne({ ...query, where: { ...params?.lookup, ...query?.where, documentId } });
// Document does not exist
if (!entryToUpdate) {
return null;
}
const validData = await entityValidator.validateEntityUpdate(
model,
data,
{ isDraft: true }, // Always update the draft version
entryToUpdate
);
const componentData = await updateComponents(uid, entryToUpdate, validData);
const entryData = updatePipeline(
Object.assign(omitComponentData(model, validData), componentData),
{ contentType: model }
);
return db.query(uid).update({ ...query, where: { id: entryToUpdate.id }, data: entryData });
},
async count(uid, params = undefined) {
const query = transformParamsToQuery(uid, params || ({} as any));
query.where = { ...params?.lookup, ...query.where };
return db.query(uid).count(query);
},
async clone(uid, documentId, params) {
// TODO: File upload
// TODO: Entity validator.
const { data = {} as any } = params!;
const model = strapi.getModel(uid);
const query = transformParamsToQuery(uid, pickSelectionParams(params) as any);
// Find all locales of the document
const entries = await db.query(uid).findMany({
...query,
where: { ...params?.lookup, ...query.where, documentId },
});
// Document does not exist
if (!entries.length) {
return null;
}
const newDocumentId = createDocumentId();
const versions = await mapAsync(entries, async (entryToClone: any) => {
const isDraft = contentTypesUtils.isDraft(data);
// Todo: Merge data with entry to clone
const validData = await entityValidator.validateEntityUpdate(
model,
// Omit id fields, the cloned entity id will be generated by the database
omit(['id'], data),
{ isDraft },
entryToClone
);
const componentData = await cloneComponents(uid, entryToClone, validData);
const entityData = createPipeline(
Object.assign(omitComponentData(model, validData), componentData),
{ contentType: model }
);
// TODO: Transform params to query
return db.query(uid).clone(entryToClone.id, {
...query,
// Allows entityData to override the documentId (e.g. when publishing)
data: { documentId: newDocumentId, ...entityData, locale: entryToClone.locale },
});
});
return { id: newDocumentId, versions };
},
// TODO: Handle relations so they target the published version
async publish(uid, documentId, params) {
// Delete already published versions that match the locales to be published
await this.delete(uid, documentId, {
...params,
lookup: { ...params?.lookup, publishedAt: { $ne: null } },
});
// Clone every draft version to be published
const clonedDocuments = (await this.clone(uid, documentId, {
...(params || {}),
// @ts-expect-error - Generic type does not have publishedAt attribute by default
data: { documentId, publishedAt: new Date() },
})) as any;
// TODO: Return actual count
return { versions: clonedDocuments?.versions || [] };
},
async unpublish(uid, documentId, params) {
// Delete all published versions
return this.delete(uid, documentId, {
...params,
lookup: { ...params?.lookup, publishedAt: { $ne: null } },
}) as any;
},
/**
* Steps:
* - Delete the matching draft versions (publishedAt = null)
* - Clone the matching published versions into draft versions
*/
async discardDraft(uid, documentId, params) {
// Delete draft versions, clone published versions into draft versions
await this.delete(uid, documentId, {
...params,
// Delete all drafts that match query
lookup: { ...params?.lookup, publishedAt: null },
});
// Clone published versions into draft versions
const clonedDocuments = (await this.clone(uid, documentId, {
...(params || {}),
// Clone only published versions
lookup: { ...params?.lookup, publishedAt: { $ne: null } },
// @ts-expect-error - Generic type does not have publishedAt attribute by default
data: { documentId, publishedAt: null },
})) as any;
return { versions: clonedDocuments?.versions || [] };
},
});
export default (ctx: { strapi: Strapi; db: Database }): Documents.Engine => {
const implementation = createDocumentEngine(ctx);
// TODO: Wrap with database error handling
return implementation;
};

View File

@ -1,300 +0,0 @@
import type { Database } from '@strapi/database';
import type { Common, Documents, Schema, Shared, Strapi } from '@strapi/types';
import { contentTypes as contentTypesUtils, convertQueryParams, mapAsync } from '@strapi/utils';
import { isArray, omit } from 'lodash/fp';
import uploadFiles from '../utils/upload-files';
import {
cloneComponents,
createComponents,
deleteComponents,
getComponents,
omitComponentData,
updateComponents,
} from '../entity-service/components';
import { createDocumentId } from '../../utils/transform-content-types-to-models';
import { applyTransforms } from '../entity-service/attributes';
import entityValidator from '../entity-validator';
import { pickSelectionParams } from './params';
const { transformParamsToQuery } = convertQueryParams;
/**
* TODO: Sanitization / validation built-in
* TODO: i18n - Move logic to i18n package
* TODO: Webhooks
* TODO: Audit logs
* TODO: Entity Validation - Uniqueness across same locale and publication status
* TODO: File upload
* TODO: replace 'any'
* TODO: availableLocales
*
*/
type Context = {
contentType: Schema.ContentType;
};
const createPipeline = (data: Record<string, unknown>, context: Context) => {
return applyTransforms(data, context);
};
const updatePipeline = (data: Record<string, unknown>, context: Context) => {
return applyTransforms(data, context);
};
const createDocumentRepository = ({
strapi,
db,
}: {
strapi: Strapi;
db: Database;
}): Documents.Repository => ({
uploadFiles,
async findMany(uid, params) {
const { kind } = strapi.getModel(uid);
const query = transformParamsToQuery(uid, params || ({} as any));
query.where = { ...params?.lookup, ...query.where };
if (kind === 'singleType') {
return db.query(uid).findOne(query);
}
return db.query(uid).findMany(query);
},
async findFirst(uid, params) {
const query = transformParamsToQuery(uid, params || ({} as any));
return db.query(uid).findOne({ ...query, where: { ...params?.lookup, ...query.where } });
},
async findOne(uid, documentId, params) {
const query = transformParamsToQuery(uid, params || ({} as any));
return db
.query(uid)
.findOne({ ...query, where: { ...params?.lookup, ...query.where, documentId } });
},
async delete(uid, documentId, params = {} as any) {
const query = transformParamsToQuery(uid, params as any);
if (params.status === 'draft') {
throw new Error('Cannot delete a draft document');
}
const entriesToDelete = await db.query(uid).findMany({
...query,
where: {
...params.lookup,
...query?.where,
documentId,
},
});
// Delete all matched entries and its components
await mapAsync(entriesToDelete, async (entryToDelete: any) => {
const componentsToDelete = await getComponents(uid, entryToDelete);
await db.query(uid).delete({ where: { id: entryToDelete.id } });
await deleteComponents(uid, componentsToDelete as any, { loadComponents: false });
});
// TODO: Change return value to actual count
return { versions: entriesToDelete };
},
// TODO: should we provide two separate methods?
async deleteMany(uid, paramsOrIds) {
let queryParams;
if (isArray(paramsOrIds)) {
queryParams = { filter: { documentID: { $in: paramsOrIds } } };
} else {
queryParams = paramsOrIds;
}
const query = transformParamsToQuery(uid, queryParams || ({} as any));
return db.query(uid).deleteMany(query);
},
async create(uid, params) {
// TODO: Entity validator.
// TODO: File upload - Probably in the lifecycles?
const { data } = params;
if (!data) {
throw new Error('Create requires data attribute');
}
const model = strapi.getModel(uid) as Shared.ContentTypes[Common.UID.ContentType];
const validData = await entityValidator.validateEntityCreation(model, data, { isDraft: true });
const componentData = await createComponents(uid, validData);
const entryData = createPipeline(
Object.assign(omitComponentData(model, validData), componentData),
{
contentType: model,
}
);
// select / populate
const query = transformParamsToQuery(uid, pickSelectionParams(params));
return db.query(uid).create({ ...query, data: entryData });
},
// NOTE: What happens if user doesn't provide specific publications state and locale to update?
async update(uid, documentId, params) {
// TODO: Prevent updating a published document
// TODO: Entity validator.
// TODO: File upload
const { data } = params || {};
const model = strapi.getModel(uid) as Shared.ContentTypes[Common.UID.ContentType];
const query = transformParamsToQuery(uid, pickSelectionParams(params || {}));
// Find all locales of the document
const entryToUpdate = await db
.query(uid)
.findOne({ ...query, where: { ...params?.lookup, ...query?.where, documentId } });
// Document does not exist
if (!entryToUpdate) {
return null;
}
const validData = await entityValidator.validateEntityUpdate(
model,
data,
{ isDraft: true }, // Always update the draft version
entryToUpdate
);
const componentData = await updateComponents(uid, entryToUpdate, validData);
const entryData = updatePipeline(
Object.assign(omitComponentData(model, validData), componentData),
{ contentType: model }
);
return db.query(uid).update({ ...query, where: { id: entryToUpdate.id }, data: entryData });
},
async count(uid, params = undefined) {
const query = transformParamsToQuery(uid, params || ({} as any));
query.where = { ...params?.lookup, ...query.where };
return db.query(uid).count(query);
},
async clone(uid, documentId, params) {
// TODO: File upload
// TODO: Entity validator.
const { data = {} as any } = params!;
const model = strapi.getModel(uid);
const query = transformParamsToQuery(uid, pickSelectionParams(params));
// Find all locales of the document
const entries = await db.query(uid).findMany({
...query,
where: { ...params?.lookup, ...query.where, documentId },
});
// Document does not exist
if (!entries.length) {
return null;
}
const newDocumentId = createDocumentId();
const versions = await mapAsync(entries, async (entryToClone: any) => {
const isDraft = contentTypesUtils.isDraft(data);
// Todo: Merge data with entry to clone
const validData = await entityValidator.validateEntityUpdate(
model,
// Omit id fields, the cloned entity id will be generated by the database
omit(['id'], data),
{ isDraft },
entryToClone
);
const componentData = await cloneComponents(uid, entryToClone, validData);
const entityData = createPipeline(
Object.assign(omitComponentData(model, validData), componentData),
{ contentType: model }
);
// TODO: Transform params to query
return db.query(uid).clone(entryToClone.id, {
...query,
// Allows entityData to override the documentId (e.g. when publishing)
data: { documentId: newDocumentId, ...entityData, locale: entryToClone.locale },
});
});
return { id: newDocumentId, versions };
},
// TODO: Handle relations so they target the published version
async publish(uid, documentId, params) {
// Delete already published versions that match the locales to be published
await this.delete(uid, documentId, {
...params,
lookup: { ...params?.lookup, publishedAt: { $ne: null } },
});
// Clone every draft version to be published
const clonedDocuments = (await this.clone(uid, documentId, {
...(params || {}),
// @ts-expect-error - Generic type does not have publishedAt attribute by default
data: { documentId, publishedAt: new Date() },
})) as any;
// TODO: Return actual count
return { versions: clonedDocuments?.versions || [] };
},
async unpublish(uid, documentId, params) {
// Delete all published versions
return this.delete(uid, documentId, {
...params,
lookup: { ...params?.lookup, publishedAt: { $ne: null } },
}) as any;
},
/**
* Steps:
* - Delete the matching draft versions (publishedAt = null)
* - Clone the matching published versions into draft versions
*/
async discardDraft(uid, documentId, params) {
// Delete draft versions, clone published versions into draft versions
await this.delete(uid, documentId, {
...params,
// Delete all drafts that match query
lookup: { ...params?.lookup, publishedAt: null },
});
// Clone published versions into draft versions
const clonedDocuments = (await this.clone(uid, documentId, {
...(params || {}),
// Clone only published versions
lookup: { ...params?.lookup, publishedAt: { $ne: null } },
// @ts-expect-error - Generic type does not have publishedAt attribute by default
data: { documentId, publishedAt: null },
})) as any;
return { versions: clonedDocuments?.versions || [] };
},
});
export default (ctx: { strapi: Strapi; db: Database }): Documents.Repository => {
const implementation = createDocumentRepository(ctx);
// TODO: Wrap with database error handling
return implementation;
};

View File

@ -0,0 +1,159 @@
import { Strapi, Common, Documents } from '@strapi/types';
import createDocumentRepository from './document-engine';
import createMiddlewareManager from './middlewares';
import { loadDefaultMiddlewares } from './middlewares/defaults';
/**
* Repository to :
* - Access documents via actions (findMany, findOne, create, update, delete, ...)
* - Execute middlewares on document actions
* - Apply default parameters to document actions
*
* @param strapi
* @param options.defaults - Default parameters to apply to all actions
* @param options.parent - Parent repository, used when creating a new repository with .with()
* @returns DocumentService
*
* @example Access documents
* const article = strapi.documents('api::article.article').create(params)
* const allArticles = strapi.documents('api::article.article').findMany(params)
*
*/
export const createDocumentService = (
strapi: Strapi,
{ defaults = {} }: { defaults?: any } = {}
): Documents.Service => {
const documents = createDocumentRepository({ strapi, db: strapi.db! });
const middlewareManager = createMiddlewareManager();
loadDefaultMiddlewares(middlewareManager);
function create<TContentTypeUID extends Common.UID.ContentType>(
uid: TContentTypeUID
): Documents.ServiceInstance<TContentTypeUID> {
return {
async findMany(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'findMany', uid, params, options: {} }, ({ params }) =>
documents.findMany(uid, params)
)
);
},
async findFirst(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'findFirst', uid, params, options: {} }, ({ params }) =>
documents.findFirst(uid, params)
)
);
},
async findOne(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'findOne', uid, params, options: { id } }, ({ params }) =>
documents.findOne(uid, id, params)
)
);
},
async delete(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'delete', uid, params, options: { id } }, ({ params }) =>
documents.delete(uid, id, params)
)
);
},
async deleteMany(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'deleteMany', uid, params, options: {} }, ({ params }) =>
documents.deleteMany(uid, params)
)
);
},
async create(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'create', uid, params, options: {} }, ({ params }) =>
documents.create(uid, params)
)
);
},
async clone(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'clone', uid, params, options: { id } }, ({ params }) =>
documents.clone(uid, id, params)
)
);
},
async update(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'update', uid, params, options: { id } }, ({ params }) =>
documents.update(uid, id, params)
)
);
},
async count(params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'count', uid, params, options: {} }, ({ params }) =>
documents.count(uid, params)
)
);
},
async publish(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run({ action: 'publish', uid, params, options: { id } }, ({ params }) =>
documents.publish(uid, id, params)
)
);
},
async unpublish(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run(
{ action: 'unpublish', uid, params, options: { id } },
({ params }) => documents.unpublish(uid, id, params)
)
);
},
async discardDraft(id: string, params = {} as any) {
return strapi.db?.transaction?.(async () =>
middlewareManager.run(
{ action: 'discardDraft', uid, params, options: { id } },
({ params }) => documents.discardDraft(uid, id, params)
)
);
},
// @ts-expect-error - TODO: Fix this
with(params: object) {
return createDocumentService(strapi, {
defaults: { ...defaults, ...params },
})(uid);
},
use(action, cb, opts) {
middlewareManager.add(uid, action, cb, opts);
return this;
},
};
}
Object.assign(create, {
use(action: any, cb: any, opts?: any) {
middlewareManager.add('_all', action, cb, opts);
return create;
},
middlewares: middlewareManager,
// NOTE : We should do this in a different way, where lifecycles are executed for the different methods
...documents,
});
// @ts-expect-error - TODO: Fix this
return create;
};

View File

@ -108,7 +108,7 @@ export interface Strapi extends Container {
store?: CoreStore;
entityValidator?: EntityValidator;
entityService?: EntityService.EntityService;
documents?: Documents.Engine;
documents?: Documents.Service;
telemetry: TelemetryService;
requestContext: RequestContext;
customFields: CustomFields.CustomFields;

View File

@ -1,6 +1,6 @@
import type { Common } from '../../types';
import type * as Params from './params/document-repository';
import type * as Result from './result/document-repository';
import type * as Params from './params/document-engine';
import type * as Result from './result/document-enigne';
export type ID = string;
@ -10,7 +10,7 @@ export type UploadFile = (
files: Record<string, unknown>
) => Promise<void>;
export interface DocumentRepository {
export interface DocumentEngine {
uploadFiles: UploadFile;
findMany<

View File

@ -1,16 +1,16 @@
import { Common } from '../..';
import { ID, type DocumentRepository } from './document-repository';
import { ID, type DocumentEngine } from './document-engine';
import type * as Middleware from './middleware';
import type * as Params from './params/document-repository';
import type * as Result from './result/document-repository';
import type * as Params from './params/document-engine';
import type * as Result from './result/document-enigne';
export { ID, DocumentRepository as Repository } from './document-repository';
export { ID, DocumentEngine as Engine } from './document-engine';
export type * as Middleware from './middleware';
export * as Params from './params';
export * from './plugin';
export * from './result';
export type EngineInstance<
export type ServiceInstance<
TContentTypeUID extends Common.UID.ContentType = Common.UID.ContentType
> = {
findMany: <TParams extends Params.FindMany<TContentTypeUID>>(
@ -74,7 +74,7 @@ export type EngineInstance<
* return result;
* })
*/
use: <TAction extends keyof DocumentRepository>(
use: <TAction extends keyof DocumentEngine>(
action: TAction,
// QUESTION: How do we type the result type of next?
// Should we send params + document id attribute?
@ -82,7 +82,7 @@ export type EngineInstance<
| Middleware.Middleware<Common.UID.ContentType, TAction>
| Middleware.Middleware<Common.UID.ContentType, TAction>[],
opts?: Middleware.Options
) => ThisType<EngineInstance<TContentTypeUID>>;
) => ThisType<ServiceInstance<TContentTypeUID>>;
/**
* `.with()` instantiates a new document repository with default parameters
@ -99,13 +99,13 @@ export type EngineInstance<
*/
with: <TParams extends Params.With<TContentTypeUID>>(
params?: TParams
) => EngineInstance<TContentTypeUID>;
) => ServiceInstance<TContentTypeUID>;
};
export type Engine = {
export type Service = {
<TContentTypeUID extends Common.UID.ContentType>(
uid: TContentTypeUID
): EngineInstance<TContentTypeUID>;
): ServiceInstance<TContentTypeUID>;
/** Add a middleware for all uid's and a specific action
* @example - Add a default locale
@ -114,13 +114,13 @@ export type Engine = {
* return next(ctx)
* })
*/
use: <TAction extends keyof DocumentRepository>(
use: <TAction extends keyof DocumentEngine>(
action: TAction,
cb:
| Middleware.Middleware<Common.UID.ContentType, TAction>
| Middleware.Middleware<Common.UID.ContentType, TAction>[],
opts?: Middleware.Options
) => Engine;
) => Service;
middlewares: Middleware.Manager;
} & DocumentRepository;
} & DocumentEngine;

View File

@ -1,7 +1,7 @@
// Utility type to reuse Param definition in MiddlewareContext
import { Common } from '../..';
import { DocumentRepository } from './document-repository';
import type * as Params from './params/document-repository';
import { DocumentEngine } from './document-engine';
import type * as Params from './params/document-engine';
export type ParamsMap<TContentTypeUID extends Common.UID.ContentType = Common.UID.ContentType> = {
findOne: Params.FindOne<TContentTypeUID>;
@ -20,7 +20,7 @@ export type ParamsMap<TContentTypeUID extends Common.UID.ContentType = Common.UI
export interface Context<
TContentTypeUID extends Common.UID.ContentType = Common.UID.ContentType,
TAction extends keyof DocumentRepository = keyof DocumentRepository
TAction extends keyof DocumentEngine = keyof DocumentEngine
> {
uid: TContentTypeUID;
action: TAction;
@ -40,11 +40,11 @@ export interface Options {
export type Middleware<
TContentTypeUID extends Common.UID.ContentType,
TAction extends keyof DocumentRepository
TAction extends keyof DocumentEngine
> = (
ctx: Context<TContentTypeUID, TAction>,
next: (ctx: Context<TContentTypeUID, TAction>) => ReturnType<DocumentRepository[TAction]>
) => ReturnType<DocumentRepository[TAction]>;
next: (ctx: Context<TContentTypeUID, TAction>) => ReturnType<DocumentEngine[TAction]>
) => ReturnType<DocumentEngine[TAction]>;
/**
* Handles middlewares for document service

View File

@ -1,6 +1,6 @@
import { Common, Utils } from '../../..';
import { Result } from '.';
import * as Params from '../params/document-repository';
import * as Params from '../params/document-engine';
export type CountResult = { count: number };