feat(medusa): Improve base-service

This commit is contained in:
adrien2p committed 2022-04-21 13:48:16 +02:00
1 parent 313cb0658b
commit bfb81b8b32
2 files changed
+27 -32

No files matched your search

@@ -1,4 +1,4 @@
import BaseService from "../base-service" import { BaseService } from "../base-service"
import { In, Not } from "typeorm" import { In, Not } from "typeorm"
import { MockManager } from "medusa-test-utils" import { MockManager } from "medusa-test-utils"
@@ -6,7 +6,7 @@ describe("BaseService", () => {
it("should cloned the child class withTransaction", () => { it("should cloned the child class withTransaction", () => {
class Child extends BaseService<Child> { class Child extends BaseService<Child> {
constructor(protected readonly container) { constructor(protected readonly container) {
super(container, {}); super(container);
this.container = container this.container = container
} }
+25 -30
View File
@@ -1,7 +1,7 @@
import { MedusaError } from "medusa-core-utils" import { MedusaError } from "medusa-core-utils"
import { EntityManager, FindOperator, In, Raw } from "typeorm" import { EntityManager, FindOperator, In, Raw } from "typeorm"
import { IsolationLevel } from "typeorm/driver/types/IsolationLevel" import { IsolationLevel } from "typeorm/driver/types/IsolationLevel"
import { FindConfig } from "../types/common" import { FindConfig, Writable } from "../types/common"
type Selector<TEntity> = { [key in keyof TEntity]?: unknown } type Selector<TEntity> = { [key in keyof TEntity]?: unknown }
@@ -9,7 +9,7 @@ type Selector<TEntity> = { [key in keyof TEntity]?: unknown }
* Common functionality for Services * Common functionality for Services
* @interface * @interface
*/ */
class BaseService< export class BaseService<
TChild extends BaseService<TChild, TContainer>, TChild extends BaseService<TChild, TContainer>,
TContainer = unknown TContainer = unknown
> { > {
@@ -19,22 +19,17 @@ class BaseService<
constructor( constructor(
container: TContainer, container: TContainer,
protected readonly configModule: Record<string, unknown> protected readonly configModule?: Record<string, unknown>
) { ) {
this.container_ = container this.container_ = container
} }
withTransaction(): this
withTransaction(transactionManager: EntityManager): TChild
withTransaction(transactionManager?: EntityManager): this | TChild { withTransaction(transactionManager?: EntityManager): this | TChild {
if (!transactionManager) { if (!transactionManager) {
return this return this
} }
const cloned = new (<typeof BaseService>this.constructor)< const cloned = new (<any>this.constructor)(
TChild,
TContainer
>(
{ {
...this.container_, ...this.container_,
manager: transactionManager, manager: transactionManager,
@@ -57,12 +52,12 @@ class BaseService<
selector: Selector<TEntity>, selector: Selector<TEntity>,
config: FindConfig<TEntity> = {} config: FindConfig<TEntity> = {}
): FindConfig<TEntity> & { ): FindConfig<TEntity> & {
where: { [key in keyof TEntity]?: unknown } where: Partial<Writable<TEntity>>
withDeleted?: boolean withDeleted?: boolean
} { } {
const build = ( const build = (
obj: Record<string, unknown> obj: Record<string, unknown>
): { [key in keyof TEntity]?: unknown } => { ): Partial<Writable<TEntity>> => {
return Object.entries(obj).reduce((acc, [key, value]: any) => { return Object.entries(obj).reduce((acc, [key, value]: any) => {
// Undefined values indicate that they have no significance to the query. // Undefined values indicate that they have no significance to the query.
// If the query is looking for rows where a column is not set it should use null instead of undefined // If the query is looking for rows where a column is not set it should use null instead of undefined
@@ -83,8 +78,7 @@ class BaseService<
acc[key] = In([...(value as unknown[])]) acc[key] = In([...(value as unknown[])])
break break
case value !== null && typeof value === "object": case value !== null && typeof value === "object":
Object.entries(value as Record<string, unknown>).map( Object.entries(value).map(([modifier, val]) => {
([modifier, val]) => {
switch (modifier) { switch (modifier) {
case "lt": case "lt":
subquery.push({ operator: "<", value: val }) subquery.push({ operator: "<", value: val })
@@ -102,8 +96,7 @@ class BaseService<
acc[key] = value acc[key] = value
break break
} }
} })
)
if (subquery.length) { if (subquery.length) {
acc[key] = Raw( acc[key] = Raw(
@@ -121,11 +114,11 @@ class BaseService<
} }
return acc return acc
}, {} as { [key in keyof TEntity]?: unknown }) }, {} as Partial<Writable<TEntity>>)
} }
const query: FindConfig<TEntity> & { const query: FindConfig<TEntity> & {
where: { [key in keyof TEntity]?: unknown } where: Partial<Writable<TEntity>>
withDeleted?: boolean withDeleted?: boolean
} = { } = {
where: build(selector), where: build(selector),
@@ -217,17 +210,19 @@ class BaseService<
* @param maybeErrorHandlerOrDontFail Potential error handler * @param maybeErrorHandlerOrDontFail Potential error handler
* @return the result of the transactional work * @return the result of the transactional work
*/ */
async atomicPhase_( async atomicPhase_<TResult, TError>(
work: (transactionManager: EntityManager) => Promise<unknown>, work: (transactionManager: EntityManager) => Promise<TResult | never>,
isolationOrErrorHandler?: isolationOrErrorHandler?:
| IsolationLevel | IsolationLevel
| ((error: unknown) => Promise<unknown>), | ((error: TError) => Promise<never | TResult | void>),
maybeErrorHandlerOrDontFail?: (error: unknown) => Promise<unknown> maybeErrorHandlerOrDontFail?: (
): Promise<unknown | never> { error: TError
) => Promise<never | TResult | void>
): Promise<never | TResult> {
let errorHandler = maybeErrorHandlerOrDontFail let errorHandler = maybeErrorHandlerOrDontFail
let isolation: let isolation:
| IsolationLevel | IsolationLevel
| ((error: unknown) => Promise<unknown>) | ((error: TError) => Promise<never | TResult | void>)
| undefined | undefined
| null = isolationOrErrorHandler | null = isolationOrErrorHandler
let dontFail = false let dontFail = false
@@ -238,7 +233,7 @@ class BaseService<
} }
if (this.transactionManager_) { if (this.transactionManager_) {
const doWork = async (m: EntityManager): Promise<unknown | never> => { const doWork = async (m: EntityManager): Promise<never | TResult> => {
this.manager_ = m this.manager_ = m
this.transactionManager_ = m this.transactionManager_ = m
try { try {
@@ -256,10 +251,10 @@ class BaseService<
} }
} }
return doWork(this.transactionManager_) return await doWork(this.transactionManager_)
} else { } else {
const temp = this.manager_ const temp = this.manager_
const doWork = async (m: EntityManager): Promise<unknown | never> => { const doWork = async (m: EntityManager): Promise<never | TResult> => {
this.manager_ = m this.manager_ = m
this.transactionManager_ = m this.transactionManager_ = m
try { try {
@@ -284,8 +279,9 @@ class BaseService<
return result return result
} catch (error) { } catch (error) {
if (this.shouldRetryTransaction(error)) { if (this.shouldRetryTransaction(error)) {
return this.manager_.transaction(isolation as IsolationLevel, (m) => return this.manager_.transaction(
doWork(m) isolation as IsolationLevel,
(m): Promise<never | TResult> => doWork(m)
) )
} else { } else {
if (errorHandler) { if (errorHandler) {
@@ -302,7 +298,7 @@ class BaseService<
if (errorHandler) { if (errorHandler) {
const result = await errorHandler(error) const result = await errorHandler(error)
if (dontFail) { if (dontFail) {
return result return result as TResult
} }
} }
@@ -339,4 +335,3 @@ class BaseService<
} }
} }
} }
export default BaseService