chore: Internal medusa service proper typings with DML (#7792)

This commit is contained in:
Adrien de Peretti
2024-06-21 12:36:54 +02:00
committed by GitHub
parent 944051a951
commit 90e6ca0e9e
29 changed files with 198 additions and 142 deletions

View File

@@ -1,6 +1,6 @@
import { expectTypeOf } from "expect-type"
import { BaseProperty } from "../properties/base"
import { PropertyMetadata } from "../types"
import { PropertyMetadata } from "@medusajs/types"
describe("Base property", () => {
test("create a property type from base property", () => {

View File

@@ -13,7 +13,7 @@ import type {
PropertyType,
RelationshipOptions,
RelationshipType,
} from "./types"
} from "@medusajs/types"
import { NullableModifier } from "./properties/nullable"
import { IdProperty } from "./properties/id"

View File

@@ -2,11 +2,11 @@ import { BelongsTo } from "./relations/belongs-to"
import {
EntityCascades,
ExtractEntityRelations,
IDmlEntity,
IsDmlEntity,
PropertyType,
RelationshipType,
} from "./types"
const IsDmlEntity = Symbol("isDmlEntity")
} from "@medusajs/types"
/**
* Dml entity is a representation of a DML model with a unique
@@ -14,8 +14,9 @@ const IsDmlEntity = Symbol("isDmlEntity")
*/
export class DmlEntity<
Schema extends Record<string, PropertyType<any> | RelationshipType<any>>
> {
[IsDmlEntity] = true
> implements IDmlEntity<Schema>
{
[IsDmlEntity]: true = true
#cascades: EntityCascades<string[]> = {}
constructor(public name: string, public schema: Schema) {}

View File

@@ -34,7 +34,7 @@ import type {
PropertyType,
RelationshipMetadata,
RelationshipType,
} from "../types"
} from "@medusajs/types"
/**
* DML entity data types to PostgreSQL data types via

View File

@@ -1,4 +1,4 @@
import { PropertyMetadata, PropertyType } from "../types"
import { PropertyMetadata, PropertyType } from "@medusajs/types"
import { NullableModifier } from "./nullable"
/**

View File

@@ -1,4 +1,4 @@
import { PropertyType } from "../types"
import { PropertyType } from "@medusajs/types"
/**
* Nullable modifier marks a schema node as nullable

View File

@@ -3,7 +3,7 @@ import {
RelationshipOptions,
RelationshipType,
RelationshipTypes,
} from "../types"
} from "@medusajs/types"
/**
* The BaseRelationship encapsulates the repetitive parts of defining

View File

@@ -1,5 +1,4 @@
import { BaseRelationship } from "./base"
import { RelationshipTypes } from "../types"
import { NullableModifier } from "./nullable"
export class BelongsTo<T> extends BaseRelationship<T> {

View File

@@ -1,5 +1,4 @@
import { BaseRelationship } from "./base"
import { RelationshipTypes } from "../types"
/**
* HasMany relationship defines a relationship between two entities

View File

@@ -1,6 +1,5 @@
import { BaseRelationship } from "./base"
import { NullableModifier } from "./nullable"
import { RelationshipTypes } from "../types"
/**
* HasOne relationship defines a relationship between two entities

View File

@@ -1,5 +1,4 @@
import { BaseRelationship } from "./base"
import { RelationshipTypes } from "../types"
/**
* ManyToMany relationship defines a relationship between two entities

View File

@@ -1,4 +1,4 @@
import { RelationshipType, PropertyType } from "../types"
import { RelationshipType } from "@medusajs/types"
/**
* Nullable modifier marks a schema node as nullable

View File

@@ -1,125 +0,0 @@
import { DmlEntity } from "./entity"
/**
* The supported data types
*/
export type KnownDataTypes =
| "text"
| "boolean"
| "enum"
| "number"
| "dateTime"
| "json"
| "id"
/**
* List of available relationships at DML level
*/
export type RelationshipTypes =
| "hasOne"
| "hasMany"
| "belongsTo"
| "manyToMany"
/**
* The meta-data returned by the property parse method
*/
export type PropertyMetadata = {
fieldName: string
defaultValue?: any
nullable: boolean
dataType: {
name: KnownDataTypes
options?: Record<string, any>
}
indexes: {
name?: string
type: "index" | "unique"
}[]
relationships: RelationshipMetadata[]
}
/**
* Definition of a property type. It should have a parse
* method to get the metadata and a type-only property
* to get its static type
*/
export type PropertyType<T> = {
$dataType: T
parse(fieldName: string): PropertyMetadata
}
/**
* Options accepted by all the relationships
*/
export type RelationshipOptions = {
mappedBy?: string
} & Record<string, any>
/**
* The meta-data returned by the relationship parse
* method
*/
export type RelationshipMetadata = {
name: string
type: RelationshipTypes
entity: unknown
nullable: boolean
mappedBy?: string
options: Record<string, any>
}
/**
* Definition of a relationship type. It should have a parse
* method to get the metadata and a type-only property
* to get its static type
*/
export type RelationshipType<T> = {
$dataType: T
type: RelationshipTypes
parse(relationshipName: string): RelationshipMetadata
}
/**
* A type-only representation of a MikroORM entity. Since we generate
* entities on the fly, we need a way to represent a type-safe
* constructor and its instance properties.
*/
export interface EntityConstructor<Props> extends Function {
new (): Props
}
/**
* Helper to infer the schema type of a DmlEntity
*/
export type Infer<T> = T extends DmlEntity<infer Schema>
? EntityConstructor<{
[K in keyof Schema]: Schema[K]["$dataType"] extends () => infer R
? Infer<R>
: Schema[K]["$dataType"] extends (() => infer R) | null
? Infer<R> | null
: Schema[K]["$dataType"]
}>
: never
/**
* Extracts names of relationships from a schema
*/
export type ExtractEntityRelations<
Schema extends Record<string, any>,
OfType extends RelationshipTypes
> = {
[K in keyof Schema & string]: Schema[K] extends RelationshipType<any>
? Schema[K] extends { type: OfType }
? K
: never
: never
}[keyof Schema & string][]
/**
* The actions to cascade from a given entity to its
* relationship.
*/
export type EntityCascades<Relationships> = {
delete?: Relationships
}

View File

@@ -1,6 +1,7 @@
import {
BaseFilterable,
Context,
ExtractEntityType,
FilterQuery,
FilterQuery as InternalFilterQuery,
FindConfig,
@@ -100,9 +101,9 @@ export function MedusaInternalService<TContainer extends object = object>(
@InjectManager(propertyRepositoryName)
async retrieve(
idOrObject: string | object,
config: FindConfig<TEntity> = {},
config: FindConfig<ExtractEntityType<TEntity>> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
): Promise<ExtractEntityType<TEntity>> {
const primaryKeys = AbstractService_.retrievePrimaryKeys(model)
if (
@@ -162,7 +163,7 @@ export function MedusaInternalService<TContainer extends object = object>(
filters: FilterQuery<any> | BaseFilterable<FilterQuery<any>> = {},
config: FindConfig<any> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
): Promise<ExtractEntityType<TEntity>[]> {
AbstractService_.applyDefaultOrdering(config)
AbstractService_.applyFreeTextSearchFilter(filters, config)
@@ -179,7 +180,7 @@ export function MedusaInternalService<TContainer extends object = object>(
filters: FilterQuery<any> | BaseFilterable<FilterQuery<any>> = {},
config: FindConfig<any> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
): Promise<[ExtractEntityType<TEntity>[], number]> {
AbstractService_.applyDefaultOrdering(config)
AbstractService_.applyFreeTextSearchFilter(filters, config)
@@ -191,16 +192,24 @@ export function MedusaInternalService<TContainer extends object = object>(
)
}
create(data: any, sharedContext?: Context): Promise<TEntity>
create(data: any[], sharedContext?: Context): Promise<TEntity[]>
create(
data: any,
sharedContext?: Context
): Promise<ExtractEntityType<TEntity>>
create(
data: any[],
sharedContext?: Context
): Promise<ExtractEntityType<TEntity>[]>
@InjectTransactionManager(shouldForceTransaction, propertyRepositoryName)
async create(
data: any | any[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity | TEntity[]> {
): Promise<ExtractEntityType<TEntity> | ExtractEntityType<TEntity>[]> {
if (!isDefined(data) || (Array.isArray(data) && data.length === 0)) {
return (Array.isArray(data) ? [] : void 0) as TEntity | TEntity[]
return (Array.isArray(data) ? [] : void 0) as
| ExtractEntityType<TEntity>
| ExtractEntityType<TEntity>[]
}
const data_ = Array.isArray(data) ? data : [data]
@@ -212,24 +221,32 @@ export function MedusaInternalService<TContainer extends object = object>(
return Array.isArray(data) ? entities : entities[0]
}
update(data: any[], sharedContext?: Context): Promise<TEntity[]>
update(data: any, sharedContext?: Context): Promise<TEntity>
update(
data: any[],
sharedContext?: Context
): Promise<ExtractEntityType<TEntity>[]>
update(
data: any,
sharedContext?: Context
): Promise<ExtractEntityType<TEntity>>
update(
selectorAndData: SelectorAndData,
sharedContext?: Context
): Promise<TEntity[]>
): Promise<ExtractEntityType<TEntity>[]>
update(
selectorAndData: SelectorAndData[],
sharedContext?: Context
): Promise<TEntity[]>
): Promise<ExtractEntityType<TEntity>[]>
@InjectTransactionManager(shouldForceTransaction, propertyRepositoryName)
async update(
input: any | any[] | SelectorAndData | SelectorAndData[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity | TEntity[]> {
): Promise<ExtractEntityType<TEntity> | ExtractEntityType<TEntity>[]> {
if (!isDefined(input) || (Array.isArray(input) && input.length === 0)) {
return (Array.isArray(input) ? [] : void 0) as TEntity | TEntity[]
return (Array.isArray(input) ? [] : void 0) as
| ExtractEntityType<TEntity>
| ExtractEntityType<TEntity>[]
}
const primaryKeys = AbstractService_.retrievePrimaryKeys(model)
@@ -294,7 +311,8 @@ export function MedusaInternalService<TContainer extends object = object>(
// Only throw for missing entities when we dont have selectors involved as selector by design can return 0 entities
if (entitiesToUpdate.length !== keySelectorDataMap.size) {
const entityName = (model as EntityClass<TEntity>).name ?? model
const entityName =
(model as EntityClass<ExtractEntityType<TEntity>>).name ?? model
const compositeKeysValuesForFoundEntities = new Set(
entitiesToUpdate.map((entity) => {
@@ -447,7 +465,7 @@ export function MedusaInternalService<TContainer extends object = object>(
| InternalFilterQuery
| InternalFilterQuery[],
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], Record<string, unknown[]>]> {
): Promise<[ExtractEntityType<TEntity>[], Record<string, unknown[]>]> {
if (
(Array.isArray(idsOrFilter) && !idsOrFilter.length) ||
(!Array.isArray(idsOrFilter) && !idsOrFilter)
@@ -465,21 +483,27 @@ export function MedusaInternalService<TContainer extends object = object>(
async restore(
idsOrFilter: string[] | InternalFilterQuery,
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], Record<string, unknown[]>]> {
): Promise<[ExtractEntityType<TEntity>[], Record<string, unknown[]>]> {
return await this[propertyRepositoryName].restore(
idsOrFilter,
sharedContext
)
}
upsert(data: any[], sharedContext?: Context): Promise<TEntity[]>
upsert(data: any, sharedContext?: Context): Promise<TEntity>
upsert(
data: any[],
sharedContext?: Context
): Promise<ExtractEntityType<TEntity>[]>
upsert(
data: any,
sharedContext?: Context
): Promise<ExtractEntityType<TEntity>>
@InjectTransactionManager(propertyRepositoryName)
async upsert(
data: any | any[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity | TEntity[]> {
): Promise<ExtractEntityType<TEntity> | ExtractEntityType<TEntity>[]> {
const data_ = Array.isArray(data) ? data : [data]
const entities = await this[propertyRepositoryName].upsert(
data_,
@@ -490,24 +514,30 @@ export function MedusaInternalService<TContainer extends object = object>(
upsertWithReplace(
data: any[],
config?: UpsertWithReplaceConfig<TEntity>,
config?: UpsertWithReplaceConfig<ExtractEntityType<TEntity>>,
sharedContext?: Context
): Promise<{ entities: TEntity[]; performedActions: PerformedActions }>
): Promise<{
entities: ExtractEntityType<TEntity>[]
performedActions: PerformedActions
}>
upsertWithReplace(
data: any,
config?: UpsertWithReplaceConfig<TEntity>,
config?: UpsertWithReplaceConfig<ExtractEntityType<TEntity>>,
sharedContext?: Context
): Promise<{ entities: TEntity; performedActions: PerformedActions }>
): Promise<{
entities: ExtractEntityType<TEntity>
performedActions: PerformedActions
}>
@InjectTransactionManager(propertyRepositoryName)
async upsertWithReplace(
data: any | any[],
config: UpsertWithReplaceConfig<TEntity> = {
config: UpsertWithReplaceConfig<ExtractEntityType<TEntity>> = {
relations: [],
},
@MedusaContext() sharedContext: Context = {}
): Promise<{
entities: TEntity | TEntity[]
entities: ExtractEntityType<TEntity> | TEntity[]
performedActions: PerformedActions
}> {
const data_ = Array.isArray(data) ? data : [data]