From db419953075e0907b8c4d27ab5188e9bd3e3d72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Fri, 26 May 2023 13:20:12 +0200 Subject: [PATCH] chore(utils): clean util package deps (#4146) --- .changeset/twelve-otters-jam.md | 8 + .../inventory/src/models/inventory-item.ts | 27 +- .../inventory/src/models/inventory-level.ts | 27 +- .../inventory/src/models/reservation-item.ts | 27 +- .../inventory/src/services/inventory-item.ts | 2 +- .../inventory/src/services/inventory-level.ts | 2 +- .../src/services/reservation-item.ts | 2 +- packages/inventory/src/utils/build-query.ts | 154 +++++++++ packages/inventory/src/utils/query.ts | 5 +- .../interfaces/cart-completion-strategy.ts | 2 +- .../medusa/src/repositories/customer-group.ts | 4 +- packages/medusa/src/repositories/product.ts | 4 +- packages/medusa/src/repositories/tax-rate.ts | 2 +- packages/medusa/src/services/order.ts | 16 +- .../src/services/product-variant-inventory.ts | 3 +- .../medusa/src/services/product-variant.ts | 2 +- packages/medusa/src/services/product.ts | 17 +- .../src/services/sales-channel-inventory.ts | 2 +- packages/medusa/src/utils/build-query.ts | 190 ++--------- .../src/models/stock-location-address.ts | 27 +- .../src/models/stock-location.ts | 20 +- .../src/services/stock-location.ts | 3 +- .../stock-location/src/utils/build-query.ts | 154 +++++++++ packages/utils/package.json | 3 - .../src/common/__tests__/build-query.spec.ts | 318 ------------------ .../src/common/__tests__/is-email.spec.ts | 21 ++ packages/utils/src/common/build-query.ts | 278 ++------------- packages/utils/src/common/db-aware-column.ts | 26 -- packages/utils/src/common/index.ts | 6 +- packages/utils/src/common/is-email.ts | 12 +- .../utils/src/common/models/base-entity.ts | 16 - packages/utils/src/common/models/index.ts | 2 - .../common/models/soft-deletable-entity.ts | 8 - .../utils/src/common/object-to-string-path.ts | 44 +++ .../src/common/transaction-base-service.ts | 152 --------- yarn.lock | 3 - 36 files changed, 593 insertions(+), 996 deletions(-) create mode 100644 .changeset/twelve-otters-jam.md create mode 100644 packages/inventory/src/utils/build-query.ts create mode 100644 packages/stock-location/src/utils/build-query.ts delete mode 100644 packages/utils/src/common/__tests__/build-query.spec.ts create mode 100644 packages/utils/src/common/__tests__/is-email.spec.ts delete mode 100644 packages/utils/src/common/db-aware-column.ts delete mode 100644 packages/utils/src/common/models/base-entity.ts delete mode 100644 packages/utils/src/common/models/index.ts delete mode 100644 packages/utils/src/common/models/soft-deletable-entity.ts create mode 100644 packages/utils/src/common/object-to-string-path.ts delete mode 100644 packages/utils/src/common/transaction-base-service.ts diff --git a/.changeset/twelve-otters-jam.md b/.changeset/twelve-otters-jam.md new file mode 100644 index 0000000000..e18a22f550 --- /dev/null +++ b/.changeset/twelve-otters-jam.md @@ -0,0 +1,8 @@ +--- +"@medusajs/stock-location": minor +"@medusajs/inventory": minor +"@medusajs/utils": minor +"@medusajs/medusa": patch +--- + +chore(medusa, utils, inventory, stock-location): clear deps in the utils package diff --git a/packages/inventory/src/models/inventory-item.ts b/packages/inventory/src/models/inventory-item.ts index c802cf9861..61bcee9e89 100644 --- a/packages/inventory/src/models/inventory-item.ts +++ b/packages/inventory/src/models/inventory-item.ts @@ -1,8 +1,29 @@ -import { BeforeInsert, Column, Entity, Index } from "typeorm" -import { SoftDeletableEntity, generateEntityId } from "@medusajs/utils" +import { generateEntityId } from "@medusajs/utils" +import { + BeforeInsert, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + PrimaryColumn, + UpdateDateColumn, +} from "typeorm" @Entity() -export class InventoryItem extends SoftDeletableEntity { +export class InventoryItem { + @PrimaryColumn() + id: string + + @CreateDateColumn({ type: "timestamptz" }) + created_at: Date + + @UpdateDateColumn({ type: "timestamptz" }) + updated_at: Date + + @DeleteDateColumn({ type: "timestamptz" }) + deleted_at: Date | null + @Index({ unique: true }) @Column({ type: "text", nullable: true }) sku: string | null diff --git a/packages/inventory/src/models/inventory-level.ts b/packages/inventory/src/models/inventory-level.ts index 3f0c7cdd45..c7814b5437 100644 --- a/packages/inventory/src/models/inventory-level.ts +++ b/packages/inventory/src/models/inventory-level.ts @@ -1,9 +1,30 @@ -import { generateEntityId, SoftDeletableEntity } from "@medusajs/utils" -import { BeforeInsert, Column, Entity, Index } from "typeorm" +import { generateEntityId } from "@medusajs/utils" +import { + BeforeInsert, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + PrimaryColumn, + UpdateDateColumn, +} from "typeorm" @Entity() @Index(["inventory_item_id", "location_id"], { unique: true }) -export class InventoryLevel extends SoftDeletableEntity { +export class InventoryLevel { + @PrimaryColumn() + id: string + + @CreateDateColumn({ type: "timestamptz" }) + created_at: Date + + @UpdateDateColumn({ type: "timestamptz" }) + updated_at: Date + + @DeleteDateColumn({ type: "timestamptz" }) + deleted_at: Date | null + @Index() @Column({ type: "text" }) inventory_item_id: string diff --git a/packages/inventory/src/models/reservation-item.ts b/packages/inventory/src/models/reservation-item.ts index eece34accd..569806286d 100644 --- a/packages/inventory/src/models/reservation-item.ts +++ b/packages/inventory/src/models/reservation-item.ts @@ -1,8 +1,29 @@ -import { BeforeInsert, Column, Entity, Index } from "typeorm" -import { SoftDeletableEntity, generateEntityId } from "@medusajs/utils" +import { generateEntityId } from "@medusajs/utils" +import { + BeforeInsert, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + PrimaryColumn, + UpdateDateColumn, +} from "typeorm" @Entity() -export class ReservationItem extends SoftDeletableEntity { +export class ReservationItem { + @PrimaryColumn() + id: string + + @CreateDateColumn({ type: "timestamptz" }) + created_at: Date + + @UpdateDateColumn({ type: "timestamptz" }) + updated_at: Date + + @DeleteDateColumn({ type: "timestamptz" }) + deleted_at: Date | null + @Index() @Column({ type: "text", nullable: true }) line_item_id: string | null diff --git a/packages/inventory/src/services/inventory-item.ts b/packages/inventory/src/services/inventory-item.ts index 46a76dcd0c..041f725940 100644 --- a/packages/inventory/src/services/inventory-item.ts +++ b/packages/inventory/src/services/inventory-item.ts @@ -7,7 +7,6 @@ import { SharedContext, } from "@medusajs/types" import { - buildQuery, InjectEntityManager, isDefined, MedusaContext, @@ -16,6 +15,7 @@ import { import { DeepPartial, EntityManager, FindManyOptions } from "typeorm" import { InventoryItem } from "../models" import { getListQuery } from "../utils/query" +import { buildQuery } from "../utils/build-query" type InjectedDependencies = { eventBusService: IEventBusService diff --git a/packages/inventory/src/services/inventory-level.ts b/packages/inventory/src/services/inventory-level.ts index ee27a6a663..9f3fa820e1 100644 --- a/packages/inventory/src/services/inventory-level.ts +++ b/packages/inventory/src/services/inventory-level.ts @@ -6,7 +6,6 @@ import { SharedContext, } from "@medusajs/types" import { - buildQuery, InjectEntityManager, isDefined, MedusaContext, @@ -14,6 +13,7 @@ import { } from "@medusajs/utils" import { DeepPartial, EntityManager, FindManyOptions, In } from "typeorm" import { InventoryLevel } from "../models" +import { buildQuery } from "../utils/build-query" type InjectedDependencies = { eventBusService: IEventBusService diff --git a/packages/inventory/src/services/reservation-item.ts b/packages/inventory/src/services/reservation-item.ts index 11e6dc6286..0dec2de6f6 100644 --- a/packages/inventory/src/services/reservation-item.ts +++ b/packages/inventory/src/services/reservation-item.ts @@ -10,12 +10,12 @@ import { InjectEntityManager, MedusaContext, MedusaError, - buildQuery, isDefined, } from "@medusajs/utils" import { EntityManager, FindManyOptions } from "typeorm" import { InventoryLevelService } from "." import { ReservationItem } from "../models" +import { buildQuery } from "../utils/build-query" type InjectedDependencies = { eventBusService: IEventBusService diff --git a/packages/inventory/src/utils/build-query.ts b/packages/inventory/src/utils/build-query.ts new file mode 100644 index 0000000000..f2d32c7ae2 --- /dev/null +++ b/packages/inventory/src/utils/build-query.ts @@ -0,0 +1,154 @@ +import { ExtendedFindConfig, FindConfig } from "@medusajs/types" +import { + And, + FindManyOptions, + FindOperator, + FindOptionsOrder, + FindOptionsRelations, + FindOptionsSelect, + FindOptionsWhere, + ILike, + In, + IsNull, + LessThan, + LessThanOrEqual, + MoreThan, + MoreThanOrEqual, +} from "typeorm" +import { buildOrder, buildRelations, buildSelects } from "@medusajs/utils" + +const operatorsMap = { + lt: (value) => LessThan(value), + gt: (value) => MoreThan(value), + lte: (value) => LessThanOrEqual(value), + gte: (value) => MoreThanOrEqual(value), + contains: (value) => ILike(`%${value}%`), + starts_with: (value) => ILike(`${value}%`), + ends_with: (value) => ILike(`%${value}`), +} + +/** + * Used to build TypeORM queries. + * @param selector The selector + * @param config The config + * @return The QueryBuilderConfig + */ +export function buildQuery( + selector: TWhereKeys, + config: FindConfig = {} +) { + const query: ExtendedFindConfig = { + where: buildWhere(selector), + } + + if ("deleted_at" in selector) { + query.withDeleted = true + } + + if ("skip" in config) { + ;(query as FindManyOptions).skip = config.skip + } + + if ("take" in config) { + ;(query as FindManyOptions).take = config.take + } + + if (config.relations) { + query.relations = buildRelations( + config.relations + ) as FindOptionsRelations + } + + if (config.select) { + query.select = buildSelects( + config.select as string[] + ) as FindOptionsSelect + } + + if (config.order) { + query.order = buildOrder(config.order) as FindOptionsOrder + } + + return query +} + +/** + * @param constraints + * + * @example + * const q = buildWhere( + * { + * id: "1234", + * test1: ["123", "12", "1"], + * test2: Not("this"), + * date: { gt: date }, + * amount: { gt: 10 }, + * }, + *) + * + * // Output + * { + * id: "1234", + * test1: In(["123", "12", "1"]), + * test2: Not("this"), + * date: MoreThan(date), + * amount: MoreThan(10) + * } + */ +function buildWhere( + constraints: TWhereKeys +): FindOptionsWhere { + const where: FindOptionsWhere = {} + for (const [key, value] of Object.entries(constraints)) { + if (value === undefined) { + continue + } + + if (value === null) { + where[key] = IsNull() + continue + } + + if (value instanceof FindOperator) { + where[key] = value + continue + } + + if (Array.isArray(value)) { + where[key] = In(value) + continue + } + + if (typeof value === "object") { + Object.entries(value).forEach(([objectKey, objectValue]) => { + where[key] = where[key] || [] + if (operatorsMap[objectKey]) { + where[key].push(operatorsMap[objectKey](objectValue)) + } else { + if (objectValue != undefined && typeof objectValue === "object") { + where[key] = buildWhere(objectValue) + return + } + where[key] = value + } + return + }) + + if (!Array.isArray(where[key])) { + continue + } + + if (where[key].length === 1) { + where[key] = where[key][0] + } else { + where[key] = And(...where[key]) + } + + continue + } + + where[key] = value + } + + return where +} diff --git a/packages/inventory/src/utils/query.ts b/packages/inventory/src/utils/query.ts index be26d5b1e8..d750e5c7e9 100644 --- a/packages/inventory/src/utils/query.ts +++ b/packages/inventory/src/utils/query.ts @@ -1,12 +1,13 @@ -import { Brackets, EntityManager, FindOptionsWhere } from "typeorm" import { ExtendedFindConfig, FilterableInventoryItemProps, FindConfig, } from "@medusajs/types" -import { buildQuery, objectToStringPath } from "@medusajs/utils" +import { objectToStringPath } from "@medusajs/utils" +import { EntityManager, FindOptionsWhere, Brackets } from "typeorm" import { InventoryItem } from "../models" +import { buildQuery } from "./build-query" export function getListQuery( manager: EntityManager, diff --git a/packages/medusa/src/interfaces/cart-completion-strategy.ts b/packages/medusa/src/interfaces/cart-completion-strategy.ts index c0ed22d8e3..eb7ade83d5 100644 --- a/packages/medusa/src/interfaces/cart-completion-strategy.ts +++ b/packages/medusa/src/interfaces/cart-completion-strategy.ts @@ -1,4 +1,4 @@ -import { TransactionBaseService } from "@medusajs/utils" +import { TransactionBaseService } from "./transaction-base-service" import { IdempotencyKey } from "../models" import { RequestContext } from "../types/request" diff --git a/packages/medusa/src/repositories/customer-group.ts b/packages/medusa/src/repositories/customer-group.ts index 7052f3b7f3..c9a6635a82 100644 --- a/packages/medusa/src/repositories/customer-group.ts +++ b/packages/medusa/src/repositories/customer-group.ts @@ -82,7 +82,9 @@ export const CustomerGroupRepository = dataSource : { ...idsOrOptionsWithoutRelations.order } const originalSelect = isOptionsArray ? undefined - : objectToStringPath(idsOrOptionsWithoutRelations.select) + : (objectToStringPath( + idsOrOptionsWithoutRelations.select + ) as (keyof CustomerGroup)[]) const clonedOptions = isOptionsArray ? idsOrOptionsWithoutRelations : cloneDeep(idsOrOptionsWithoutRelations) diff --git a/packages/medusa/src/repositories/product.ts b/packages/medusa/src/repositories/product.ts index b4b15a0a92..47c791a37a 100644 --- a/packages/medusa/src/repositories/product.ts +++ b/packages/medusa/src/repositories/product.ts @@ -479,7 +479,9 @@ export const ProductRepository = dataSource.getRepository(Product).extend({ : { ...idsOrOptionsWithoutRelations.order } const originalSelect = isOptionsArray ? undefined - : objectToStringPath(idsOrOptionsWithoutRelations.select) + : (objectToStringPath( + idsOrOptionsWithoutRelations.select + ) as (keyof Product)[]) const clonedOptions = isOptionsArray ? idsOrOptionsWithoutRelations : cloneDeep(idsOrOptionsWithoutRelations) diff --git a/packages/medusa/src/repositories/tax-rate.ts b/packages/medusa/src/repositories/tax-rate.ts index 12e8029c36..bbf21e228b 100644 --- a/packages/medusa/src/repositories/tax-rate.ts +++ b/packages/medusa/src/repositories/tax-rate.ts @@ -35,7 +35,7 @@ export const TaxRateRepository = dataSource.getRepository(TaxRate).extend({ const selectableCols: (keyof TaxRate)[] = [] const legacySelect = objectToStringPath( findOptions.select as FindOptionsSelect - ) + ) as (keyof TaxRate)[] for (const k of legacySelect) { if (!resolveableFields.includes(k)) { selectableCols.push(k) diff --git a/packages/medusa/src/services/order.ts b/packages/medusa/src/services/order.ts index 31f128c661..a2414480f0 100644 --- a/packages/medusa/src/services/order.ts +++ b/packages/medusa/src/services/order.ts @@ -1,5 +1,10 @@ import { IInventoryService } from "@medusajs/types" -import { isDefined, MedusaError, TransactionBaseService } from "@medusajs/utils" +import { + buildRelations, + buildSelects, + isDefined, + MedusaError, +} from "@medusajs/utils" import { EntityManager, FindManyOptions, @@ -45,6 +50,7 @@ import { Swap, TrackingLink, } from "../models" +import { TransactionBaseService } from "../interfaces" import { AddressRepository } from "../repositories/address" import { OrderRepository } from "../repositories/order" import { FindConfig, QuerySelector, Selector } from "../types/common" @@ -54,13 +60,7 @@ import { } from "../types/fulfillment" import { TotalsContext, UpdateOrderInput } from "../types/orders" import { CreateShippingMethodDto } from "../types/shipping-options" -import { - buildQuery, - buildRelations, - buildSelects, - isString, - setMetadata, -} from "../utils" +import { buildQuery, isString, setMetadata } from "../utils" import { FlagRouter } from "../utils/flag-router" import EventBusService from "./event-bus" diff --git a/packages/medusa/src/services/product-variant-inventory.ts b/packages/medusa/src/services/product-variant-inventory.ts index e545086fcf..c12fbf33e6 100644 --- a/packages/medusa/src/services/product-variant-inventory.ts +++ b/packages/medusa/src/services/product-variant-inventory.ts @@ -10,8 +10,9 @@ import { ReserveQuantityContext, } from "@medusajs/types" import { LineItem, Product, ProductVariant } from "../models" -import { MedusaError, TransactionBaseService, isDefined } from "@medusajs/utils" +import { MedusaError, isDefined } from "@medusajs/utils" import { PricedProduct, PricedVariant } from "../types/pricing" +import { TransactionBaseService } from "../interfaces" import { ProductVariantInventoryItem } from "../models/product-variant-inventory-item" import ProductVariantService from "./product-variant" diff --git a/packages/medusa/src/services/product-variant.ts b/packages/medusa/src/services/product-variant.ts index fa364c3e2e..e4dec40b1f 100644 --- a/packages/medusa/src/services/product-variant.ts +++ b/packages/medusa/src/services/product-variant.ts @@ -40,7 +40,6 @@ import { } from "../types/product-variant" import { buildQuery, - buildRelations, hasChanges, isObject, isString, @@ -54,6 +53,7 @@ import { ProductRepository } from "../repositories/product" import { ProductOptionValueRepository } from "../repositories/product-option-value" import EventBusService from "./event-bus" import RegionService from "./region" +import { buildRelations } from "@medusajs/utils" class ProductVariantService extends TransactionBaseService { static Events = { diff --git a/packages/medusa/src/services/product.ts b/packages/medusa/src/services/product.ts index 7fdb49fa92..3bb264968b 100644 --- a/packages/medusa/src/services/product.ts +++ b/packages/medusa/src/services/product.ts @@ -31,15 +31,14 @@ import { ProductSelector, UpdateProductInput, } from "../types/product" -import { - buildQuery, - buildRelationsOrSelect, - isString, - setMetadata, -} from "../utils" +import { buildQuery, isString, setMetadata } from "../utils" import { FlagRouter } from "../utils/flag-router" import EventBusService from "./event-bus" -import { objectToStringPath } from "@medusajs/utils" +import { + buildRelations, + buildSelects, + objectToStringPath, +} from "@medusajs/utils" type InjectedDependencies = { manager: EntityManager @@ -967,11 +966,11 @@ class ProductService extends TransactionBaseService { query.order = config.order if (config.relations && config.relations.length > 0) { - query.relations = buildRelationsOrSelect(config.relations) + query.relations = buildRelations(config.relations) } if (config.select && config.select.length > 0) { - query.select = buildRelationsOrSelect(config.select) + query.select = buildSelects(config.select) } const rels = objectToStringPath(query.relations) diff --git a/packages/medusa/src/services/sales-channel-inventory.ts b/packages/medusa/src/services/sales-channel-inventory.ts index e49481aa35..b4479ee1f4 100644 --- a/packages/medusa/src/services/sales-channel-inventory.ts +++ b/packages/medusa/src/services/sales-channel-inventory.ts @@ -1,5 +1,5 @@ import { EventBusTypes, IInventoryService } from "@medusajs/types" -import { TransactionBaseService } from "@medusajs/utils" +import { TransactionBaseService } from "../interfaces" import { EntityManager } from "typeorm" import SalesChannelLocationService from "./sales-channel-location" diff --git a/packages/medusa/src/utils/build-query.ts b/packages/medusa/src/utils/build-query.ts index 892465e9ed..42e9aebad3 100644 --- a/packages/medusa/src/utils/build-query.ts +++ b/packages/medusa/src/utils/build-query.ts @@ -17,6 +17,17 @@ import { ExtendedFindConfig, FindConfig } from "../types/common" import { FindOptionsOrder } from "typeorm/find-options/FindOptionsOrder" import { isObject } from "./is-object" +import { buildOrder, buildRelations, buildSelects } from "@medusajs/utils" + +const operatorsMap = { + lt: (value) => LessThan(value), + gt: (value) => MoreThan(value), + lte: (value) => LessThanOrEqual(value), + gte: (value) => MoreThanOrEqual(value), + contains: (value) => ILike(`%${value}%`), + starts_with: (value) => ILike(`${value}%`), + ends_with: (value) => ILike(`%${value}`), +} /** * Used to build TypeORM queries. @@ -45,15 +56,19 @@ export function buildQuery( } if (config.relations) { - query.relations = buildRelations(config.relations) + query.relations = buildRelations( + config.relations + ) as FindOptionsRelations } if (config.select) { - query.select = buildSelects(config.select as string[]) + query.select = buildSelects( + config.select as string[] + ) as FindOptionsSelect } if (config.order) { - query.order = buildOrder(config.order) + query.order = buildOrder(config.order) as FindOptionsOrder } return query @@ -109,34 +124,14 @@ function buildWhere( if (typeof value === "object") { Object.entries(value).forEach(([objectKey, objectValue]) => { where[key] = where[key] || [] - switch (objectKey) { - case "lt": - where[key].push(LessThan(objectValue)) - break - case "gt": - where[key].push(MoreThan(objectValue)) - break - case "lte": - where[key].push(LessThanOrEqual(objectValue)) - break - case "gte": - where[key].push(MoreThanOrEqual(objectValue)) - break - case "contains": - where[key].push(ILike(`%${objectValue}%`)) - break - case "starts_with": - where[key].push(ILike(`${objectValue}%`)) - break - case "ends_with": - where[key].push(ILike(`%${objectValue}`)) - break - default: - if (objectValue != undefined && typeof objectValue === "object") { - where[key] = buildWhere(objectValue) - return - } - where[key] = value + if (operatorsMap[objectKey]) { + where[key].push(operatorsMap[objectKey](objectValue)) + } else { + if (objectValue != undefined && typeof objectValue === "object") { + where[key] = buildWhere(objectValue) + return + } + where[key] = value } return }) @@ -209,20 +204,6 @@ export function buildLegacyFieldsListFrom( return Array.from(output) as (keyof TEntity)[] } -export function buildSelects( - selectCollection: string[] -): FindOptionsSelect { - return buildRelationsOrSelect(selectCollection) as FindOptionsSelect -} - -export function buildRelations( - relationCollection: string[] -): FindOptionsRelations { - return buildRelationsOrSelect( - relationCollection - ) as FindOptionsRelations -} - export function addOrderToSelect( order: FindOptionsOrder, select: FindOptionsSelect @@ -241,125 +222,6 @@ export function addOrderToSelect( } } -/** - * Convert an collection of dot string into a nested object - * @example - * input: [ - * order, - * order.items, - * order.swaps, - * order.swaps.additional_items, - * order.discounts, - * order.discounts.rule, - * order.claims, - * order.claims.additional_items, - * additional_items, - * additional_items.variant, - * return_order, - * return_order.items, - * return_order.shipping_method, - * return_order.shipping_method.tax_lines - * ] - * output: { - * "order": { - * "items": true, - * "swaps": { - * "additional_items": true - * }, - * "discounts": { - * "rule": true - * }, - * "claims": { - * "additional_items": true - * } - * }, - * "additional_items": { - * "variant": true - * }, - * "return_order": { - * "items": true, - * "shipping_method": { - * "tax_lines": true - * } - * } - * } - * @param collection - */ -export function buildRelationsOrSelect( - collection: string[] -): FindOptionsRelations | FindOptionsSelect { - const output: FindOptionsRelations | FindOptionsSelect = {} - - for (const relation of collection) { - if (relation.indexOf(".") > -1) { - const nestedRelations = relation.split(".") - - let parent = output - - while (nestedRelations.length > 1) { - const nestedRelation = nestedRelations.shift() as string - parent = parent[nestedRelation] = - parent[nestedRelation] !== true && - typeof parent[nestedRelation] === "object" - ? parent[nestedRelation] - : {} - } - - parent[nestedRelations[0]] = true - - continue - } - - output[relation] = output[relation] ?? true - } - - return output -} - -/** - * Convert an order of dot string into a nested object - * @example - * input: { id: "ASC", "items.title": "ASC", "items.variant.title": "ASC" } - * output: { - * "id": "ASC", - * "items": { - * "id": "ASC", - * "variant": { - * "title": "ASC" - * } - * }, - * } - * @param orderBy - */ -function buildOrder(orderBy: { - [k: string]: "ASC" | "DESC" -}): FindOptionsOrder { - const output: FindOptionsOrder = {} - - const orderKeys = Object.keys(orderBy) - - for (const order of orderKeys) { - if (order.indexOf(".") > -1) { - const nestedOrder = order.split(".") - - let parent = output - - while (nestedOrder.length > 1) { - const nestedRelation = nestedOrder.shift() as string - parent = parent[nestedRelation] = parent[nestedRelation] ?? {} - } - - parent[nestedOrder[0]] = orderBy[order] - - continue - } - - output[order] = orderBy[order] - } - - return output -} - export function nullableValue(value: any): FindOperator { if (value === null) { return IsNull() diff --git a/packages/stock-location/src/models/stock-location-address.ts b/packages/stock-location/src/models/stock-location-address.ts index 391f76ca3b..5f3d8d3faa 100644 --- a/packages/stock-location/src/models/stock-location-address.ts +++ b/packages/stock-location/src/models/stock-location-address.ts @@ -1,8 +1,29 @@ -import { generateEntityId, SoftDeletableEntity } from "@medusajs/utils" -import { BeforeInsert, Column, Entity, Index } from "typeorm" +import { generateEntityId } from "@medusajs/utils" +import { + BeforeInsert, + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + PrimaryColumn, + UpdateDateColumn, +} from "typeorm" @Entity() -export class StockLocationAddress extends SoftDeletableEntity { +export class StockLocationAddress { + @PrimaryColumn() + id: string + + @CreateDateColumn({ type: "timestamptz" }) + created_at: Date + + @UpdateDateColumn({ type: "timestamptz" }) + updated_at: Date + + @DeleteDateColumn({ type: "timestamptz" }) + deleted_at: Date | null + @Column({ type: "text" }) address_1: string diff --git a/packages/stock-location/src/models/stock-location.ts b/packages/stock-location/src/models/stock-location.ts index 20b7b40d6a..ebf857c7da 100644 --- a/packages/stock-location/src/models/stock-location.ts +++ b/packages/stock-location/src/models/stock-location.ts @@ -1,16 +1,32 @@ -import { generateEntityId, SoftDeletableEntity } from "@medusajs/utils" +import { generateEntityId } from "@medusajs/utils" import { BeforeInsert, Column, + CreateDateColumn, + DeleteDateColumn, Entity, Index, JoinColumn, ManyToOne, + PrimaryColumn, + UpdateDateColumn, } from "typeorm" import { StockLocationAddress } from "./stock-location-address" @Entity() -export class StockLocation extends SoftDeletableEntity { +export class StockLocation { + @PrimaryColumn() + id: string + + @CreateDateColumn({ type: "timestamptz" }) + created_at: Date + + @UpdateDateColumn({ type: "timestamptz" }) + updated_at: Date + + @DeleteDateColumn({ type: "timestamptz" }) + deleted_at: Date | null + @Column({ type: "text" }) name: string diff --git a/packages/stock-location/src/services/stock-location.ts b/packages/stock-location/src/services/stock-location.ts index 7f690726db..74772d7685 100644 --- a/packages/stock-location/src/services/stock-location.ts +++ b/packages/stock-location/src/services/stock-location.ts @@ -10,7 +10,6 @@ import { UpdateStockLocationInput, } from "@medusajs/types" import { - buildQuery, InjectEntityManager, isDefined, MedusaContext, @@ -18,7 +17,9 @@ import { setMetadata, } from "@medusajs/utils" import { EntityManager } from "typeorm" + import { StockLocation, StockLocationAddress } from "../models" +import { buildQuery } from "../utils/build-query" type InjectedDependencies = { manager: EntityManager diff --git a/packages/stock-location/src/utils/build-query.ts b/packages/stock-location/src/utils/build-query.ts new file mode 100644 index 0000000000..f2d32c7ae2 --- /dev/null +++ b/packages/stock-location/src/utils/build-query.ts @@ -0,0 +1,154 @@ +import { ExtendedFindConfig, FindConfig } from "@medusajs/types" +import { + And, + FindManyOptions, + FindOperator, + FindOptionsOrder, + FindOptionsRelations, + FindOptionsSelect, + FindOptionsWhere, + ILike, + In, + IsNull, + LessThan, + LessThanOrEqual, + MoreThan, + MoreThanOrEqual, +} from "typeorm" +import { buildOrder, buildRelations, buildSelects } from "@medusajs/utils" + +const operatorsMap = { + lt: (value) => LessThan(value), + gt: (value) => MoreThan(value), + lte: (value) => LessThanOrEqual(value), + gte: (value) => MoreThanOrEqual(value), + contains: (value) => ILike(`%${value}%`), + starts_with: (value) => ILike(`${value}%`), + ends_with: (value) => ILike(`%${value}`), +} + +/** + * Used to build TypeORM queries. + * @param selector The selector + * @param config The config + * @return The QueryBuilderConfig + */ +export function buildQuery( + selector: TWhereKeys, + config: FindConfig = {} +) { + const query: ExtendedFindConfig = { + where: buildWhere(selector), + } + + if ("deleted_at" in selector) { + query.withDeleted = true + } + + if ("skip" in config) { + ;(query as FindManyOptions).skip = config.skip + } + + if ("take" in config) { + ;(query as FindManyOptions).take = config.take + } + + if (config.relations) { + query.relations = buildRelations( + config.relations + ) as FindOptionsRelations + } + + if (config.select) { + query.select = buildSelects( + config.select as string[] + ) as FindOptionsSelect + } + + if (config.order) { + query.order = buildOrder(config.order) as FindOptionsOrder + } + + return query +} + +/** + * @param constraints + * + * @example + * const q = buildWhere( + * { + * id: "1234", + * test1: ["123", "12", "1"], + * test2: Not("this"), + * date: { gt: date }, + * amount: { gt: 10 }, + * }, + *) + * + * // Output + * { + * id: "1234", + * test1: In(["123", "12", "1"]), + * test2: Not("this"), + * date: MoreThan(date), + * amount: MoreThan(10) + * } + */ +function buildWhere( + constraints: TWhereKeys +): FindOptionsWhere { + const where: FindOptionsWhere = {} + for (const [key, value] of Object.entries(constraints)) { + if (value === undefined) { + continue + } + + if (value === null) { + where[key] = IsNull() + continue + } + + if (value instanceof FindOperator) { + where[key] = value + continue + } + + if (Array.isArray(value)) { + where[key] = In(value) + continue + } + + if (typeof value === "object") { + Object.entries(value).forEach(([objectKey, objectValue]) => { + where[key] = where[key] || [] + if (operatorsMap[objectKey]) { + where[key].push(operatorsMap[objectKey](objectValue)) + } else { + if (objectValue != undefined && typeof objectValue === "object") { + where[key] = buildWhere(objectValue) + return + } + where[key] = value + } + return + }) + + if (!Array.isArray(where[key])) { + continue + } + + if (where[key].length === 1) { + where[key] = where[key][0] + } else { + where[key] = And(...where[key]) + } + + continue + } + + where[key] = value + } + + return where +} diff --git a/packages/utils/package.json b/packages/utils/package.json index 76547431e6..dff64c2cd0 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -26,9 +26,6 @@ }, "dependencies": { "awilix": "^8.0.0", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", - "typeorm": "^0.3.16", "ulid": "^2.3.0" }, "scripts": { diff --git a/packages/utils/src/common/__tests__/build-query.spec.ts b/packages/utils/src/common/__tests__/build-query.spec.ts deleted file mode 100644 index 2e9384e1ff..0000000000 --- a/packages/utils/src/common/__tests__/build-query.spec.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { - And, - FindOptionsOrder, - FindOptionsSelect, - In, - LessThanOrEqual, - MoreThan, - MoreThanOrEqual, - Not, -} from "typeorm" -import { - addOrderToSelect, - buildQuery, - objectToStringPath, -} from "../build-query" - -describe("buildQuery", () => { - it("successfully creates query", () => { - const date = new Date() - - const q = buildQuery( - { - id: "1234", - test1: ["123", "12", "1"], - test2: Not("this"), - date: { gt: date }, - amount: { gt: 10 }, - rule: { - type: "fixed", - }, - updated_at: { - gte: "value", - lte: "value", - }, - }, - { - select: [ - "order", - "order.items", - "order.swaps", - "order.swaps.additional_items", - "order.discounts", - "order.discounts.rule", - "order.claims", - "order.claims.additional_items", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - "return_order.shipping_method.tax_lines", - ], - relations: [ - "order", - "order.items", - "order.swaps", - "order.swaps.additional_items", - "order.discounts", - "order.discounts.rule", - "order.claims", - "order.claims.additional_items", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - "return_order.shipping_method.tax_lines", - "items.variants", - "items.variants.product", - "items", - "items.tax_lines", - "items.adjustments", - ], - order: { - id: "ASC", - "items.id": "ASC", - "items.variant.id": "ASC", - }, - } - ) - - expect(q).toEqual({ - where: { - id: "1234", - test1: In(["123", "12", "1"]), - test2: Not("this"), - date: MoreThan(date), - amount: MoreThan(10), - rule: { - type: "fixed", - }, - updated_at: And(MoreThanOrEqual("value"), LessThanOrEqual("value")), - }, - select: { - order: { - items: true, - swaps: { - additional_items: true, - }, - discounts: { - rule: true, - }, - claims: { - additional_items: true, - }, - }, - additional_items: { - variant: true, - }, - return_order: { - items: true, - shipping_method: { - tax_lines: true, - }, - }, - }, - relations: { - order: { - items: true, - swaps: { - additional_items: true, - }, - discounts: { - rule: true, - }, - claims: { - additional_items: true, - }, - }, - additional_items: { - variant: true, - }, - return_order: { - items: true, - shipping_method: { - tax_lines: true, - }, - }, - items: { - variants: { - product: true, - }, - tax_lines: true, - adjustments: true, - }, - }, - order: { - id: "ASC", - items: { - id: "ASC", - variant: { - id: "ASC", - }, - }, - }, - }) - }) -}) - -describe("objectToStringPath", () => { - it("successfully build back select object shape to list", () => { - const q = objectToStringPath({ - order: { - items: true, - swaps: { - additional_items: true, - }, - discounts: { - rule: true, - }, - claims: { - additional_items: true, - }, - }, - additional_items: { - variant: true, - }, - return_order: { - items: true, - shipping_method: { - tax_lines: true, - }, - }, - }) - - expect(q.length).toBe(14) - expect(q).toEqual( - expect.arrayContaining([ - "order", - "order.items", - "order.swaps", - "order.swaps.additional_items", - "order.discounts", - "order.discounts.rule", - "order.claims", - "order.claims.additional_items", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - "return_order.shipping_method.tax_lines", - ]) - ) - }) - - it("successfully build back relation object shape to list", () => { - const q = objectToStringPath({ - order: { - items: true, - swaps: { - additional_items: true, - }, - discounts: { - rule: true, - }, - claims: { - additional_items: true, - }, - }, - additional_items: { - variant: true, - }, - return_order: { - items: true, - shipping_method: { - tax_lines: true, - }, - }, - items: { - variants: { - product: true, - }, - tax_lines: true, - adjustments: true, - }, - }) - - expect(q.length).toBe(19) - expect(q).toEqual( - expect.arrayContaining([ - "order", - "order.items", - "order.swaps", - "order.swaps.additional_items", - "order.discounts", - "order.discounts.rule", - "order.claims", - "order.claims.additional_items", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - "return_order.shipping_method.tax_lines", - "items.variants", - "items.variants.product", - "items", - "items.tax_lines", - "items.adjustments", - ]) - ) - }) - - it("successfully build back order object shape to list", () => { - const q = objectToStringPath({ - id: "ASC", - items: { - id: "ASC", - variant: { - id: "ASC", - }, - }, - }) - - expect(q.length).toBe(5) - expect(q).toEqual( - expect.arrayContaining([ - "id", - "items", - "items.id", - "items.variant", - "items.variant.id", - ]) - ) - }) - - describe("addOrderToSelect", function () { - it("successfully add the order fields to the select object", () => { - const select: FindOptionsSelect = { - item: { - variant: { - id: true, - }, - }, - } - - const order: FindOptionsOrder = { - item: { - variant: { - rank: "ASC", - }, - }, - } - - addOrderToSelect(order, select) - - expect(select).toEqual({ - item: { - variant: { - id: true, - rank: true, - }, - }, - }) - }) - }) -}) diff --git a/packages/utils/src/common/__tests__/is-email.spec.ts b/packages/utils/src/common/__tests__/is-email.spec.ts new file mode 100644 index 0000000000..ba82f7fdbf --- /dev/null +++ b/packages/utils/src/common/__tests__/is-email.spec.ts @@ -0,0 +1,21 @@ +import { validateEmail } from "../is-email" + +describe("validateEmail", () => { + it("successfully validates an email", () => { + expect(validateEmail("test@email.com")).toBe("test@email.com") + expect(validateEmail("test.test@email.com")).toBe("test.test@email.com") + expect(validateEmail("test.test123@email.com")).toBe( + "test.test123@email.com" + ) + }) + + it("throws on an invalidates email", () => { + expect.assertions(1) + + try { + validateEmail("not-an-email") + } catch (e) { + expect(e.message).toBe("The email is not valid") + } + }) +}) diff --git a/packages/utils/src/common/build-query.ts b/packages/utils/src/common/build-query.ts index cdb3c8ab19..ae835a94af 100644 --- a/packages/utils/src/common/build-query.ts +++ b/packages/utils/src/common/build-query.ts @@ -1,248 +1,27 @@ -import { - And, - FindManyOptions, - FindOperator, - FindOptionsRelations, - FindOptionsSelect, - FindOptionsWhere, - ILike, - In, - IsNull, - LessThan, - LessThanOrEqual, - MoreThan, - MoreThanOrEqual, -} from "typeorm" -import { ExtendedFindConfig, FindConfig } from "@medusajs/types" +// Those utils are used in a typeorm context and we can't be sure that they can be used elsewhere -import { FindOptionsOrder } from "typeorm/find-options/FindOptionsOrder" -import { isObject } from "./is-object" +type Order = { + [key: string]: "ASC" | "DESC" | Order +} -/** - * Used to build TypeORM queries. - * @param selector The selector - * @param config The config - * @return The QueryBuilderConfig - */ -export function buildQuery( - selector: TWhereKeys, - config: FindConfig = {} -) { - const query: ExtendedFindConfig = { - where: buildWhere(selector), - } +type Selects = { + [key: string]: boolean | Selects +} - if ("deleted_at" in selector) { - query.withDeleted = true - } +type Relations = { + [key: string]: boolean | Relations +} - if ("skip" in config) { - ;(query as FindManyOptions).skip = config.skip - } +export function buildSelects(selectCollection: string[]): Selects { + return buildRelationsOrSelect(selectCollection) +} - if ("take" in config) { - ;(query as FindManyOptions).take = config.take - } - - if (config.relations) { - query.relations = buildRelations(config.relations) - } - - if (config.select) { - query.select = buildSelects(config.select as string[]) - } - - if (config.order) { - query.order = buildOrder(config.order) - } - - return query +export function buildRelations(relationCollection: string[]): Relations { + return buildRelationsOrSelect(relationCollection) } /** - * @param constraints - * - * @example - * const q = buildWhere( - * { - * id: "1234", - * test1: ["123", "12", "1"], - * test2: Not("this"), - * date: { gt: date }, - * amount: { gt: 10 }, - * }, - *) - * - * // Output - * { - * id: "1234", - * test1: In(["123", "12", "1"]), - * test2: Not("this"), - * date: MoreThan(date), - * amount: MoreThan(10) - * } - */ -function buildWhere( - constraints: TWhereKeys -): FindOptionsWhere { - const where: FindOptionsWhere = {} - for (const [key, value] of Object.entries(constraints)) { - if (value === undefined) { - continue - } - - if (value === null) { - where[key] = IsNull() - continue - } - - if (value instanceof FindOperator) { - where[key] = value - continue - } - - if (Array.isArray(value)) { - where[key] = In(value) - continue - } - - if (typeof value === "object") { - Object.entries(value).forEach(([objectKey, objectValue]) => { - where[key] = where[key] || [] - switch (objectKey) { - case "lt": - where[key].push(LessThan(objectValue)) - break - case "gt": - where[key].push(MoreThan(objectValue)) - break - case "lte": - where[key].push(LessThanOrEqual(objectValue)) - break - case "gte": - where[key].push(MoreThanOrEqual(objectValue)) - break - case "contains": - where[key].push(ILike(`%${objectValue}%`)) - break - case "starts_with": - where[key].push(ILike(`${objectValue}%`)) - break - case "ends_with": - where[key].push(ILike(`%${objectValue}`)) - break - default: - if (objectValue != undefined && typeof objectValue === "object") { - where[key] = buildWhere(objectValue) - return - } - where[key] = value - } - return - }) - - if (!Array.isArray(where[key])) { - continue - } - - if (where[key].length === 1) { - where[key] = where[key][0] - } else { - where[key] = And(...where[key]) - } - - continue - } - - where[key] = value - } - - return where -} - -/** - * Converts a typeorms structure of find options to an - * array of string paths - * @example - * input: { - * test: { - * test1: true, - * test2: true, - * test3: { - * test4: true - * }, - * }, - * test2: true - * } - * output: ['test.test1', 'test.test2', 'test.test3.test4', 'test2'] - * @param input - */ -export function objectToStringPath( - input: - | FindOptionsWhere - | FindOptionsSelect - | FindOptionsOrder - | FindOptionsRelations = {} -): (keyof TEntity)[] { - if (!Object.keys(input).length) { - return [] - } - - const output: Set = new Set(Object.keys(input)) - - for (const key of Object.keys(input)) { - if (input[key] != undefined && typeof input[key] === "object") { - const deepRes = objectToStringPath(input[key]) - - const items = deepRes.reduce((acc, val) => { - acc.push(`${key}.${val}`) - return acc - }, [] as string[]) - - items.forEach((item) => output.add(item)) - continue - } - - output.add(key) - } - - return Array.from(output) as (keyof TEntity)[] -} - -export function buildSelects( - selectCollection: string[] -): FindOptionsSelect { - return buildRelationsOrSelect(selectCollection) as FindOptionsSelect -} - -export function buildRelations( - relationCollection: string[] -): FindOptionsRelations { - return buildRelationsOrSelect( - relationCollection - ) as FindOptionsRelations -} - -export function addOrderToSelect( - order: FindOptionsOrder, - select: FindOptionsSelect -): void { - for (const orderBy of Object.keys(order)) { - if (isObject(order[orderBy])) { - select[orderBy] = - select[orderBy] && isObject(select[orderBy]) ? select[orderBy] : {} - addOrderToSelect(order[orderBy], select[orderBy]) - continue - } - - select[orderBy] = isObject(select[orderBy]) - ? { ...select[orderBy], id: true, [orderBy]: true } - : true - } -} - -/** - * Convert an collection of dot string into a nested object + * Convert a collection of dot string into a nested object * @example * input: [ * order, @@ -285,10 +64,8 @@ export function addOrderToSelect( * } * @param collection */ -function buildRelationsOrSelect( - collection: string[] -): FindOptionsRelations | FindOptionsSelect { - const output: FindOptionsRelations | FindOptionsSelect = {} +function buildRelationsOrSelect(collection: string[]): Selects | Relations { + const output: Selects | Relations = {} for (const relation of collection) { if (relation.indexOf(".") > -1) { @@ -298,11 +75,12 @@ function buildRelationsOrSelect( while (nestedRelations.length > 1) { const nestedRelation = nestedRelations.shift() as string - parent = parent[nestedRelation] = + parent = parent[nestedRelation] = ( parent[nestedRelation] !== true && typeof parent[nestedRelation] === "object" ? parent[nestedRelation] : {} + ) as Selects | Relations } parent[nestedRelations[0]] = true @@ -331,10 +109,8 @@ function buildRelationsOrSelect( * } * @param orderBy */ -function buildOrder(orderBy: { - [k: string]: "ASC" | "DESC" -}): FindOptionsOrder { - const output: FindOptionsOrder = {} +export function buildOrder(orderBy: { [k: string]: "ASC" | "DESC" }): Order { + const output: Order = {} const orderKeys = Object.keys(orderBy) @@ -346,7 +122,7 @@ function buildOrder(orderBy: { while (nestedOrder.length > 1) { const nestedRelation = nestedOrder.shift() as string - parent = parent[nestedRelation] = parent[nestedRelation] ?? {} + parent = (parent[nestedRelation] as Order) ??= {} } parent[nestedOrder[0]] = orderBy[order] @@ -359,11 +135,3 @@ function buildOrder(orderBy: { return output } - -export function nullableValue(value: any): FindOperator { - if (value === null) { - return IsNull() - } else { - return value - } -} diff --git a/packages/utils/src/common/db-aware-column.ts b/packages/utils/src/common/db-aware-column.ts deleted file mode 100644 index 9dc7e740a1..0000000000 --- a/packages/utils/src/common/db-aware-column.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Column, ColumnOptions, ColumnType } from "typeorm" - -export function resolveDbType(pgSqlType: ColumnType): ColumnType { - return pgSqlType -} - -export function resolveDbGenerationStrategy( - pgSqlType: "increment" | "uuid" | "rowid" -): "increment" | "uuid" | "rowid" { - return pgSqlType -} - -export function DbAwareColumn(columnOptions: ColumnOptions): PropertyDecorator { - const pre = columnOptions.type - if (columnOptions.type) { - columnOptions.type = resolveDbType(columnOptions.type) - } - - if (pre === "jsonb" && pre !== columnOptions.type) { - if ("default" in columnOptions) { - columnOptions.default = JSON.stringify(columnOptions.default) - } - } - - return Column(columnOptions) -} diff --git a/packages/utils/src/common/index.ts b/packages/utils/src/common/index.ts index d65ad70421..8d444ebe48 100644 --- a/packages/utils/src/common/index.ts +++ b/packages/utils/src/common/index.ts @@ -1,5 +1,3 @@ -export * from "./build-query" -export * from "./db-aware-column" export * from "./errors" export * from "./generate-entity-id" export * from "./get-config-file" @@ -8,8 +6,8 @@ export * from "./is-defined" export * from "./is-email" export * from "./is-object" export * from "./is-string" +export * from "./object-to-string-path" export * from "./medusa-container" -export * from "./models" export * from "./set-metadata" -export * from "./transaction-base-service" export * from "./wrap-handler" +export * from "./build-query" diff --git a/packages/utils/src/common/is-email.ts b/packages/utils/src/common/is-email.ts index 3d1126792b..d53a4a2633 100644 --- a/packages/utils/src/common/is-email.ts +++ b/packages/utils/src/common/is-email.ts @@ -1,6 +1,16 @@ -import { isEmail } from "class-validator" import { MedusaError } from "./errors" +const EMAIL_REGEX = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + +/** + * Check whether provided string is an email. + * @param email - string to check + */ +function isEmail(email: string) { + return email.toLowerCase().match(EMAIL_REGEX) +} + /** * Used to validate user email. * @param {string} email - email to validate diff --git a/packages/utils/src/common/models/base-entity.ts b/packages/utils/src/common/models/base-entity.ts deleted file mode 100644 index db3b5079c9..0000000000 --- a/packages/utils/src/common/models/base-entity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CreateDateColumn, PrimaryColumn, UpdateDateColumn } from "typeorm" -import { resolveDbType } from "../db-aware-column" - -/** - * Base abstract entity for all entities - */ -export abstract class BaseEntity { - @PrimaryColumn() - id: string - - @CreateDateColumn({ type: resolveDbType("timestamptz") }) - created_at: Date - - @UpdateDateColumn({ type: resolveDbType("timestamptz") }) - updated_at: Date -} diff --git a/packages/utils/src/common/models/index.ts b/packages/utils/src/common/models/index.ts deleted file mode 100644 index 0ebedceeb2..0000000000 --- a/packages/utils/src/common/models/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./base-entity" -export * from "./soft-deletable-entity" diff --git a/packages/utils/src/common/models/soft-deletable-entity.ts b/packages/utils/src/common/models/soft-deletable-entity.ts deleted file mode 100644 index 833f6aa989..0000000000 --- a/packages/utils/src/common/models/soft-deletable-entity.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { DeleteDateColumn } from "typeorm" -import { resolveDbType } from "../db-aware-column" -import { BaseEntity } from "./base-entity" - -export abstract class SoftDeletableEntity extends BaseEntity { - @DeleteDateColumn({ type: resolveDbType("timestamptz") }) - deleted_at: Date | null -} diff --git a/packages/utils/src/common/object-to-string-path.ts b/packages/utils/src/common/object-to-string-path.ts new file mode 100644 index 0000000000..b1010df1ab --- /dev/null +++ b/packages/utils/src/common/object-to-string-path.ts @@ -0,0 +1,44 @@ +import { isObject } from "./is-object" + +/** + * Converts a structure of find options to an + * array of string paths + * @example + * input: { + * test: { + * test1: true, + * test2: true, + * test3: { + * test4: true + * }, + * }, + * test2: true + * } + * output: ['test.test1', 'test.test2', 'test.test3.test4', 'test2'] + * @param input + */ +export function objectToStringPath(input: object = {}): string[] { + if (!isObject(input) || !Object.keys(input).length) { + return [] + } + + const output: Set = new Set(Object.keys(input)) + + for (const key of Object.keys(input)) { + if (input[key] != undefined && typeof input[key] === "object") { + const deepRes = objectToStringPath(input[key]) + + const items = deepRes.reduce((acc, val) => { + acc.push(`${key}.${val}`) + return acc + }, [] as string[]) + + items.forEach((item) => output.add(item)) + continue + } + + output.add(key) + } + + return Array.from(output) +} diff --git a/packages/utils/src/common/transaction-base-service.ts b/packages/utils/src/common/transaction-base-service.ts deleted file mode 100644 index 87158f0dc0..0000000000 --- a/packages/utils/src/common/transaction-base-service.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { EntityManager } from "typeorm" -import { IsolationLevel } from "typeorm/driver/types/IsolationLevel" - -export abstract class TransactionBaseService { - protected manager_: EntityManager - protected transactionManager_: EntityManager | undefined - - protected get activeManager_(): EntityManager { - return this.transactionManager_ ?? this.manager_ - } - - protected constructor( - protected readonly __container__: any, - protected readonly __configModule__?: Record, - protected readonly __moduleDeclaration__?: Record - ) { - this.manager_ = __container__.manager - } - - withTransaction(transactionManager?: EntityManager): this { - if (!transactionManager) { - return this - } - - const cloned = new (this.constructor as any)( - this.__container__, - this.__configModule__, - this.__moduleDeclaration__ - ) - - cloned.manager_ = transactionManager - cloned.transactionManager_ = transactionManager - - return cloned - } - - protected shouldRetryTransaction_( - err: { code: string } | Record - ): boolean { - if (!(err as { code: string })?.code) { - return false - } - const code = (err as { code: string })?.code - return code === "40001" || code === "40P01" - } - - /** - * Wraps some work within a transactional block. If the service already has - * a transaction manager attached this will be reused, otherwise a new - * transaction manager is created. - * @param work - the transactional work to be done - * @param isolationOrErrorHandler - the isolation level to be used for the work. - * @param maybeErrorHandlerOrDontFail Potential error handler - * @return the result of the transactional work - */ - protected async atomicPhase_( - work: (transactionManager: EntityManager) => Promise, - isolationOrErrorHandler?: - | IsolationLevel - | ((error: TError) => Promise), - maybeErrorHandlerOrDontFail?: ( - error: TError - ) => Promise - ): Promise { - let errorHandler = maybeErrorHandlerOrDontFail - let isolation: - | IsolationLevel - | ((error: TError) => Promise) - | undefined - | null = isolationOrErrorHandler - let dontFail = false - if (typeof isolationOrErrorHandler === "function") { - isolation = null - errorHandler = isolationOrErrorHandler - dontFail = !!maybeErrorHandlerOrDontFail - } - - if (this.transactionManager_) { - const doWork = async (m: EntityManager): Promise => { - this.manager_ = m - this.transactionManager_ = m - try { - return await work(m) - } catch (error) { - if (errorHandler) { - const queryRunner = this.transactionManager_.queryRunner - if (queryRunner && queryRunner.isTransactionActive) { - await queryRunner.rollbackTransaction() - } - - await errorHandler(error) - } - throw error - } - } - - return await doWork(this.transactionManager_) - } else { - const temp = this.manager_ - const doWork = async (m: EntityManager): Promise => { - this.manager_ = m - this.transactionManager_ = m - try { - const result = await work(m) - this.manager_ = temp - this.transactionManager_ = undefined - return result - } catch (error) { - this.manager_ = temp - this.transactionManager_ = undefined - throw error - } - } - - if (isolation && this.manager_) { - let result - try { - result = await this.manager_.transaction( - isolation as IsolationLevel, - async (m) => doWork(m) - ) - return result - } catch (error) { - if (this.shouldRetryTransaction_(error)) { - return this.manager_.transaction( - isolation as IsolationLevel, - async (m): Promise => doWork(m) - ) - } else { - if (errorHandler) { - await errorHandler(error) - } - throw error - } - } - } - - try { - return await this.manager_.transaction(async (m) => doWork(m)) - } catch (error) { - if (errorHandler) { - const result = await errorHandler(error) - if (dontFail) { - return result as TResult - } - } - - throw error - } - } - } -} diff --git a/yarn.lock b/yarn.lock index c48c501845..0e059049e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6525,12 +6525,9 @@ __metadata: "@medusajs/types": 1.8.6 "@types/express": ^4.17.17 awilix: ^8.0.0 - class-transformer: ^0.5.1 - class-validator: ^0.14.0 cross-env: ^5.2.1 jest: ^25.5.4 ts-jest: ^25.5.1 - typeorm: ^0.3.16 typescript: ^4.4.4 ulid: ^2.3.0 languageName: unknown