chore: Internal medusa service proper typings with DML (#7792)
This commit is contained in:
committed by
GitHub
parent
944051a951
commit
90e6ca0e9e
@@ -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", () => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import type {
|
||||
PropertyType,
|
||||
RelationshipOptions,
|
||||
RelationshipType,
|
||||
} from "./types"
|
||||
} from "@medusajs/types"
|
||||
import { NullableModifier } from "./properties/nullable"
|
||||
import { IdProperty } from "./properties/id"
|
||||
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -34,7 +34,7 @@ import type {
|
||||
PropertyType,
|
||||
RelationshipMetadata,
|
||||
RelationshipType,
|
||||
} from "../types"
|
||||
} from "@medusajs/types"
|
||||
|
||||
/**
|
||||
* DML entity data types to PostgreSQL data types via
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PropertyMetadata, PropertyType } from "../types"
|
||||
import { PropertyMetadata, PropertyType } from "@medusajs/types"
|
||||
import { NullableModifier } from "./nullable"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PropertyType } from "../types"
|
||||
import { PropertyType } from "@medusajs/types"
|
||||
|
||||
/**
|
||||
* Nullable modifier marks a schema node as nullable
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
RelationshipOptions,
|
||||
RelationshipType,
|
||||
RelationshipTypes,
|
||||
} from "../types"
|
||||
} from "@medusajs/types"
|
||||
|
||||
/**
|
||||
* The BaseRelationship encapsulates the repetitive parts of defining
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { RelationshipTypes } from "../types"
|
||||
import { NullableModifier } from "./nullable"
|
||||
|
||||
export class BelongsTo<T> extends BaseRelationship<T> {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { RelationshipTypes } from "../types"
|
||||
|
||||
/**
|
||||
* HasMany relationship defines a relationship between two entities
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { NullableModifier } from "./nullable"
|
||||
import { RelationshipTypes } from "../types"
|
||||
|
||||
/**
|
||||
* HasOne relationship defines a relationship between two entities
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { RelationshipTypes } from "../types"
|
||||
|
||||
/**
|
||||
* ManyToMany relationship defines a relationship between two entities
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RelationshipType, PropertyType } from "../types"
|
||||
import { RelationshipType } from "@medusajs/types"
|
||||
|
||||
/**
|
||||
* Nullable modifier marks a schema node as nullable
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user