From 4fdf1a0272c8c3d2ffb4dffd326bba06788fa9b6 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Wed, 30 Aug 2023 19:00:54 +0200 Subject: [PATCH] Ts migration of the entity service --- .../entity-service/attributes/index.ts | 7 ++-- .../entity-service/attributes/transforms.ts | 10 +++--- .../src/services/entity-service/index.ts | 35 ++++++++++++------- .../src/services/entity-service/params.ts | 7 +++- .../services/entity-service/types/index.ts | 25 +++++++------ .../entity-service/types/params/attributes.ts | 2 +- .../types/params/publication-state.ts | 5 +-- .../services/entity-service/types/result.ts | 20 +++++++---- .../strapi/src/services/utils/upload-files.ts | 16 +++++---- packages/core/strapi/src/types/index.ts | 2 -- .../strapi/src/types/shared/registries.ts | 13 +------ .../core/utils/src/convert-query-params.ts | 4 +-- 12 files changed, 82 insertions(+), 64 deletions(-) diff --git a/packages/core/strapi/src/services/entity-service/attributes/index.ts b/packages/core/strapi/src/services/entity-service/attributes/index.ts index d14cd5c8c3..7cf2c32e54 100644 --- a/packages/core/strapi/src/services/entity-service/attributes/index.ts +++ b/packages/core/strapi/src/services/entity-service/attributes/index.ts @@ -1,5 +1,6 @@ import transforms from './transforms'; -import type { Schema } from '../../../types'; +import type { Common, Schema } from '../../../types'; +import { Data } from '../types/params'; const applyTransforms = ( data: Record, @@ -9,9 +10,9 @@ const applyTransforms = ( ) => { const { contentType } = context; - const entries = Object.entries(data); + for (const attributeName of Object.keys(data)) { + const value = data[attributeName]; - for (const [attributeName, value] of entries) { const attribute = contentType.attributes[attributeName]; if (!attribute) { diff --git a/packages/core/strapi/src/services/entity-service/attributes/transforms.ts b/packages/core/strapi/src/services/entity-service/attributes/transforms.ts index 8b1ee39271..f7fbbf540a 100644 --- a/packages/core/strapi/src/services/entity-service/attributes/transforms.ts +++ b/packages/core/strapi/src/services/entity-service/attributes/transforms.ts @@ -4,10 +4,10 @@ import bcrypt from 'bcryptjs'; import type { Attribute } from '../../../types'; type Transforms = { - [key in Attribute.Kind]?: ( - value: Attribute.GetValue>, - context: { attribute: Attribute.Attribute; attributeName: string } - ) => unknown; + [TKind in Attribute.Kind]?: ( + value: Attribute.GetValue>, + context: { attribute: Attribute.Attribute; attributeName: string } + ) => Attribute.GetValue>; }; const transforms: Transforms = { @@ -22,6 +22,6 @@ const transforms: Transforms = { return bcrypt.hashSync(value, rounds); }, -} as const; +}; export default transforms; diff --git a/packages/core/strapi/src/services/entity-service/index.ts b/packages/core/strapi/src/services/entity-service/index.ts index 4a9c92a4dc..a7fb86eb84 100644 --- a/packages/core/strapi/src/services/entity-service/index.ts +++ b/packages/core/strapi/src/services/entity-service/index.ts @@ -38,8 +38,6 @@ type Context = { contentType: Schema.ContentType; }; -type Entity = {}; - const transformLoadParamsToQuery = ( uid: string, field: string, @@ -89,19 +87,22 @@ const createDefaultImplementation = ({ eventHub: EventHub; entityValidator: EntityValidator; }): types.EntityService => ({ + /** + * Upload files utility + */ uploadFiles, - async wrapParams(options) { + async wrapParams(options: any) { return options; }, - async wrapResult(result) { + async wrapResult(result: any) { return result; }, - async emitEvent(uid: Common.UID.Schema, event: string, entity: Entity) { + async emitEvent(uid, event: string, entity) { // Ignore audit log events to prevent infinite loops - if (uid === 'admin::audit-log') { + if (uid === ('admin::audit-log' as Common.UID.ContentType)) { return; } @@ -115,7 +116,7 @@ const createDefaultImplementation = ({ }); }, - async findMany(uid: Common.UID.Schema, opts) { + async findMany(uid, opts) { const { kind } = strapi.getModel(uid); const wrappedParams = await this.wrapParams(opts, { uid, action: 'findMany' }); @@ -131,7 +132,7 @@ const createDefaultImplementation = ({ return this.wrapResult(entities, { uid, action: 'findMany' }); }, - async findPage(uid: Common.UID.Schema, opts) { + async findPage(uid, opts) { const wrappedParams = await this.wrapParams(opts, { uid, action: 'findPage' }); const query = transformParamsToQuery(uid, wrappedParams); @@ -183,16 +184,24 @@ const createDefaultImplementation = ({ }, async create(uid, opts) { - const wrappedParams = await this.wrapParams(opts, { uid, action: 'create' }); + const wrappedParams = await this.wrapParams< + types.Params.Pick + >(opts, { uid, action: 'create' }); const { data, files } = wrappedParams; + if (!data) { + throw new Error('cannot create'); + } + const model = strapi.getModel(uid); const isDraft = contentTypesUtils.isDraft(data, model); const validData = await entityValidator.validateEntityCreation(model, data, { isDraft }); // select / populate - const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams)); + const x = pickSelectionParams(wrappedParams); + + const query = transformParamsToQuery(uid, x); // TODO: wrap into transaction const componentData = await createComponents(uid, validData); @@ -353,7 +362,7 @@ const createDefaultImplementation = ({ return entity; }, // FIXME: used only for the CM to be removed - async deleteMany(uid: Common.UID.Schema, opts) { + async deleteMany(uid, opts) { const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' }); // select / populate @@ -383,7 +392,7 @@ const createDefaultImplementation = ({ return deletedEntities; }, - async load(uid: Common.UID.Schema, entity, field, params = {}) { + async load(uid, entity, field, params = {}) { if (!_.isString(field)) { throw new Error(`Invalid load. Expected "${field}" to be a string`); } @@ -395,7 +404,7 @@ const createDefaultImplementation = ({ return this.wrapResult(loadedEntity, { uid, field, action: 'load' }); }, - async loadPages(uid: Common.UID.Schema, entity: Entity, field, params = {}, pagination = {}) { + async loadPages(uid, entity: Entity, field, params = {}, pagination = {}) { if (!_.isString(field)) { throw new Error(`Invalid load. Expected "${field}" to be a string`); } diff --git a/packages/core/strapi/src/services/entity-service/params.ts b/packages/core/strapi/src/services/entity-service/params.ts index e1dbf9c1a0..46d64c2fb4 100644 --- a/packages/core/strapi/src/services/entity-service/params.ts +++ b/packages/core/strapi/src/services/entity-service/params.ts @@ -1,5 +1,10 @@ import { pick } from 'lodash/fp'; -const pickSelectionParams = pick(['fields', 'populate']); +import type { Common } from '../../types'; +import type { Params } from './types'; + +const pickSelectionParams = >( + data: T +): Pick => pick(['fields', 'populate'])(data); export { pickSelectionParams }; diff --git a/packages/core/strapi/src/services/entity-service/types/index.ts b/packages/core/strapi/src/services/entity-service/types/index.ts index ea55a04ea9..360af65abc 100644 --- a/packages/core/strapi/src/services/entity-service/types/index.ts +++ b/packages/core/strapi/src/services/entity-service/types/index.ts @@ -1,5 +1,6 @@ import type { Attribute, Common, Utils } from '../../../types'; import type { PartialEntity, Entity, Result, PaginatedResult } from './result'; +import type { UploadFile } from '../../utils/upload-files'; import type * as Params from './params'; @@ -12,8 +13,10 @@ export * from './plugin'; type WrapAction = Omit; export interface EntityService { + uploadFiles: UploadFile; + wrapParams< - TResult = unknown, + TResult extends object = object, TContentTypeUID extends Common.UID.ContentType = Common.UID.ContentType, TParams extends object = object >( @@ -22,7 +25,7 @@ export interface EntityService { ): Promise | TResult; wrapResult< - TResult = unknown, + TResult = any, TContentTypeUID extends Common.UID.ContentType = Common.UID.ContentType >( result: unknown, @@ -51,12 +54,14 @@ export interface EntityService { >( uid: TContentTypeUID, params?: TParams - ): Utils.Expression.MatchFirst< - [ - [Common.UID.IsCollectionType, Promise[]>], - [Common.UID.IsSingleType, Promise | null>] - ], - Promise<(Result | null) | Result[]> + ): Promise< + Utils.Expression.MatchFirst< + [ + [Common.UID.IsCollectionType, Result[]], + [Common.UID.IsSingleType, Result | null] + ], + (Result | null) | Result[] + > >; findOne< @@ -134,7 +139,7 @@ export interface EntityService { * @deprecated */ findWithRelationCounts< - TContentTypeUID extends Common.UID.Schema, + TContentTypeUID extends Common.UID.ContentType, TParams extends Params.Pick< TContentTypeUID, | 'fields' @@ -155,7 +160,7 @@ export interface EntityService { * @deprecated */ findWithRelationCountsPage< - TContentTypeUID extends Common.UID.Schema, + TContentTypeUID extends Common.UID.ContentType, TParams extends Params.Pick< TContentTypeUID, | 'fields' diff --git a/packages/core/strapi/src/services/entity-service/types/params/attributes.ts b/packages/core/strapi/src/services/entity-service/types/params/attributes.ts index df20fa834a..2ef61ddf01 100644 --- a/packages/core/strapi/src/services/entity-service/types/params/attributes.ts +++ b/packages/core/strapi/src/services/entity-service/types/params/attributes.ts @@ -55,7 +55,7 @@ export type ScalarValues = GetValue< | Attribute.Text | Attribute.Time | Attribute.Timestamp - | Attribute.UID + | Attribute.UID >; /** diff --git a/packages/core/strapi/src/services/entity-service/types/params/publication-state.ts b/packages/core/strapi/src/services/entity-service/types/params/publication-state.ts index bd2ec71f86..cea7cd301d 100644 --- a/packages/core/strapi/src/services/entity-service/types/params/publication-state.ts +++ b/packages/core/strapi/src/services/entity-service/types/params/publication-state.ts @@ -6,7 +6,7 @@ export type IsEnabled = Utils.Expression.M [ [ Common.UID.IsContentType, - Utils.Expression.IsTrue + Utils.Expression.IsTrue['draftAndPublish']> ], [ // Here, we're manually excluding potential overlap between Component and ContentTypes' UIDs and thus preventing false positives @@ -33,6 +33,7 @@ export type For = IsEnabled ex // Then add the publicationState param { publicationState?: Kind }, // Else, don't do anything - {} + unknown > : never; + diff --git a/packages/core/strapi/src/services/entity-service/types/result.ts b/packages/core/strapi/src/services/entity-service/types/result.ts index 18a85c40af..0ad737105b 100644 --- a/packages/core/strapi/src/services/entity-service/types/result.ts +++ b/packages/core/strapi/src/services/entity-service/types/result.ts @@ -60,11 +60,14 @@ export type PaginatedResult< */ export type GetValues< TSchemaUID extends Common.UID.Schema, - TFields extends Attribute.GetKeys, - TPopulate extends Attribute.GetKeys + TFields extends Attribute.GetKeys = Attribute.GetNonPopulatableKeys, + TPopulate extends Attribute.GetKeys = Attribute.GetPopulatableKeys > = Utils.Expression.If< Common.AreSchemaRegistriesExtended, - Utils.Guard.Never> extends infer TKeys + Utils.Guard.Never< + TFields | TPopulate, + Attribute.GetKeys + > extends infer TKeys extends Attribute.GetKeys ? Attribute.GetValues : never, AnyEntity @@ -72,14 +75,17 @@ export type GetValues< type ExtractFields< TSchemaUID extends Common.UID.Schema, - TFields extends Params.Fields.Any + TFields extends Params.Fields.Any | undefined > = Utils.Expression.MatchFirst< [ // No fields provided [ Utils.Expression.Or< Utils.Expression.StrictEqual>, - Utils.Expression.IsNever + Utils.Expression.Or< + Utils.Expression.IsNever, + Utils.Expression.StrictEqual + > >, never ], @@ -121,7 +127,7 @@ type ParseStringFields< type ExtractPopulate< TSchemaUID extends Common.UID.Schema, - TPopulate extends Params.Populate.Any + TPopulate extends Params.Populate.Any | undefined > = Utils.Expression.MatchFirst< [ // No populate provided @@ -166,7 +172,7 @@ type ExtractPopulate< type ParsePopulateDotNotation< TSchemaUID extends Common.UID.Schema, TPopulate extends Params.Populate.StringNotation -> = Utils.String.Split, '.'>[0]; +> = Utils.Cast, '.'>[0], Attribute.GetPopulatableKeys>; type ParseStringPopulate< TSchemaUID extends Common.UID.Schema, diff --git a/packages/core/strapi/src/services/utils/upload-files.ts b/packages/core/strapi/src/services/utils/upload-files.ts index c5c965997d..eba9721042 100644 --- a/packages/core/strapi/src/services/utils/upload-files.ts +++ b/packages/core/strapi/src/services/utils/upload-files.ts @@ -1,14 +1,16 @@ import _ from 'lodash'; -import { Attribute, Common, Schema } from '../../types'; +import type { Attribute, Common, Schema } from '../../types'; + +export type UploadFile = ( + uid: Common.UID.Schema, + entity: Record, + files: Record +) => Promise; /** * Upload files and link them to an entity */ -export default async ( - uid: Common.UID.ContentType | Common.UID.Component, - entity: Record, - files: { [key: string]: unknown } -) => { +const uploadFile: UploadFile = async (uid, entity, files) => { const modelDef = strapi.getModel(uid); if (!_.has(strapi.plugins, 'upload')) { @@ -85,3 +87,5 @@ export default async ( await Promise.all(Object.keys(files).map((key) => doUpload(key, files[key]))); }; + +export default uploadFile; diff --git a/packages/core/strapi/src/types/index.ts b/packages/core/strapi/src/types/index.ts index 5609b98b5e..780786a88e 100644 --- a/packages/core/strapi/src/types/index.ts +++ b/packages/core/strapi/src/types/index.ts @@ -1,8 +1,6 @@ // Exports from core should already be modules export * from './core'; -export * as EntityService from '../services/entity-service'; - export * as CoreApi from './core-api'; export * as Utils from './utils'; export * as Shared from './shared'; diff --git a/packages/core/strapi/src/types/shared/registries.ts b/packages/core/strapi/src/types/shared/registries.ts index 40174f6e79..5390aa0942 100644 --- a/packages/core/strapi/src/types/shared/registries.ts +++ b/packages/core/strapi/src/types/shared/registries.ts @@ -1,4 +1,4 @@ -import { Common, Schema, UID } from '..'; +import type { Common, Schema, UID } from '..'; /** * Shared service registry @@ -35,17 +35,6 @@ export interface Middlewares { */ export interface ContentTypes { [key: UID.ContentType]: Schema.ContentType; - // 'admin::coucou': { - // modelType: 'contentType'; - // kind: 'collectionType'; - // uid: 'admin::coucou'; - // attributes: Schema.CollectionType['attributes']; - // info: { - // displayName: 'coucou'; - // singularName: 'coucou'; - // pluralName: 'coucous'; - // }; - // }; } /** diff --git a/packages/core/utils/src/convert-query-params.ts b/packages/core/utils/src/convert-query-params.ts index 54e75a0701..ae9f4be6ad 100644 --- a/packages/core/utils/src/convert-query-params.ts +++ b/packages/core/utils/src/convert-query-params.ts @@ -62,8 +62,8 @@ export interface Params { fields?: FieldsParams; filters?: FiltersParams; populate?: PopulateParams; - count: boolean; - ordering: unknown; + count?: boolean; + ordering?: unknown; _q?: string; limit?: number | string; start?: number | string;