feat(product): Create (+ workflow), delete, restore (#4459)
* Feat: create product with product module * feat: create product wip * feat: create product wip * feat: update product relation and generate image migration * lint * conitnue implementation * continue implementation and add integration tests for produceService.create * Add integration tests for product creation at the module level for the complete flow * only use persist since write operations are always wrapped in a transaction which will be committed and flushed * simplify the transaction wrapper to make future changes easier * feat: move some utils to the utils package to simplify its usage * tests: fix unit tests * feat: create variants along side the product * Add more integration tests an update migrations * chore: Update actions workflow to include packages integration tests * small types and utils cleanup * chore: Add support for database debug option * chore: Add missing types in package.json from types and util, validate that all the models are sync with medusa * expose retrieve method * fix types issues * fix unit tests and move integration tests workflow with the plugins integration tests * chore: remove migration function export from the definition to prevent them to be ran by the medusa cli just in case * fix package.json script * chore: workflows * feat: start creating the create product workflow * feat: add empty step for prices and sales channel * tests: update scripts and action envs * fix imports * feat: Add proper soft deleted support + add product deletion service public api * chore: update migrations * chore: update migrations * chore: update todo * feat: Add product deletion to the create-product workflow as compensation * chore: cleanup product utils * feat: Add support for cascade soft-remove * feat: refactor repository to take into account withDeleted * fix integration tests * Add support for force delete -> delete, cleanup repositories and improvements * Add support for restoring a product and add integration tests * cleaup + tests * types * fix integration tests * remove unnecessary comments * move specific mikro orm usage to the DAL * Cleanup workflow functions * Make deleted_at optional at the property level and add url index for the images * address feedback + cleanup * fix export * merge migrations into one * feat(product, types): added missing product variant methods (#4475) * chore: added missing product variant methods * chore: address PR feedback * chore: catch undefined case for retrieve + specs for variant service * chore: align TEntity + add changeset * chore: revert changeset, TEntity to ProductVariant * chore: write tests for pagination, unskip the test * Create chilled-mice-deliver.md * update integration fixtuers * update pipeline node version * rename github action * fix pipeline * feat(medusa, types): added missing category tests and service methods (#4499) * chore: added missing category tests and service methods * chore: added type changes to module service * chore: address pr feedback * update repositories manager usage and serialisation from the write public API * move serializisation to the DAL * rename template args * chore: added collection methods for module and collection service (#4505) * chore: added collection methods for module and collection service * Create fresh-islands-teach.md * chore: move retrieve entity to utils package * chore: make products optional in DTO type --------- Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * feat(product): Apply transaction decorators to the services (#4512) --------- Co-authored-by: Riqwan Thamir <rmthamir@gmail.com> Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
co-authored by
Oliver Windall Juhl
Riqwan Thamir
Carlos R. L. Rodrigues
parent
5b91a3503a
commit
befc2f1c80
@@ -3,6 +3,7 @@
|
||||
"version": "1.8.10",
|
||||
"description": "Medusa Types definition",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
|
||||
@@ -41,11 +41,12 @@ export type Writable<T> = {
|
||||
}
|
||||
|
||||
export interface FindConfig<Entity> {
|
||||
select?: (keyof Entity)[]
|
||||
select?: (keyof Entity | string)[]
|
||||
skip?: number
|
||||
take?: number
|
||||
relations?: string[]
|
||||
order?: { [K: string]: "ASC" | "DESC" }
|
||||
withDeleted?: boolean
|
||||
}
|
||||
|
||||
export type ExtendedFindConfig<TEntity> = (
|
||||
|
||||
@@ -21,4 +21,6 @@ export type FindOptions<T = any> = {
|
||||
options?: OptionsQuery<T, any>
|
||||
}
|
||||
|
||||
export const SoftDeletableFilterKey = "softDeletable"
|
||||
|
||||
export * from "./repository-service"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FindOptions } from "./index"
|
||||
import { RepositoryTransformOptions } from "../common"
|
||||
import { Context } from "../shared-context"
|
||||
|
||||
/**
|
||||
* Data access layer (DAL) interface to implements for any repository service.
|
||||
@@ -7,6 +8,54 @@ import { RepositoryTransformOptions } from "../common"
|
||||
* ORM directly and allows to switch to another ORM without changing the business logic.
|
||||
*/
|
||||
export interface RepositoryService<T = any> {
|
||||
find(options?: FindOptions<T>, transformOptions?: RepositoryTransformOptions): Promise<T[]>
|
||||
findAndCount(options?: FindOptions<T>, transformOptions?: RepositoryTransformOptions): Promise<[T[], number]>
|
||||
transaction(
|
||||
task: (transactionManager: unknown) => Promise<any>,
|
||||
context?: {
|
||||
isolationLevel?: string
|
||||
transaction?: unknown
|
||||
enableNestedTransactions?: boolean
|
||||
}
|
||||
): Promise<any>
|
||||
|
||||
serialize<TData extends object, TResult extends object, TOptions = any>(
|
||||
data: TData,
|
||||
options?: TOptions
|
||||
): Promise<TResult>
|
||||
|
||||
serialize<TData extends object[], TResult extends object[], TOptions = any>(
|
||||
data: TData[],
|
||||
options?: TOptions
|
||||
): Promise<TResult>
|
||||
|
||||
find(options?: FindOptions<T>, context?: Context): Promise<T[]>
|
||||
|
||||
findAndCount(
|
||||
options?: FindOptions<T>,
|
||||
context?: Context
|
||||
): Promise<[T[], number]>
|
||||
|
||||
// Only required for some repositories
|
||||
upsert?(data: any, context?: Context): Promise<T[]>
|
||||
|
||||
create(data: unknown[], context?: Context): Promise<T[]>
|
||||
|
||||
delete(ids: string[], context?: Context): Promise<void>
|
||||
|
||||
softDelete(ids: string[], context?: Context): Promise<T[]>
|
||||
|
||||
restore(ids: string[], context?: Context): Promise<T[]>
|
||||
}
|
||||
|
||||
export interface TreeRepositoryService<T = any> extends RepositoryService<T> {
|
||||
find(
|
||||
options?: FindOptions<T>,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
context?: Context
|
||||
): Promise<T[]>
|
||||
|
||||
findAndCount(
|
||||
options?: FindOptions<T>,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
context?: Context
|
||||
): Promise<[T[], number]>
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface IInventoryService {
|
||||
context?: SharedContext
|
||||
): Promise<ReservationItemDTO>
|
||||
|
||||
// TODO make it bulk
|
||||
createInventoryItem(
|
||||
input: CreateInventoryItemInput,
|
||||
context?: SharedContext
|
||||
@@ -95,6 +96,7 @@ export interface IInventoryService {
|
||||
context?: SharedContext
|
||||
): Promise<void>
|
||||
|
||||
// TODO make it bulk
|
||||
deleteInventoryItem(
|
||||
inventoryItemId: string,
|
||||
context?: SharedContext
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MedusaContainer } from "../common"
|
||||
import { Logger } from "../logger"
|
||||
import { RepositoryService } from "../dal"
|
||||
|
||||
export type Constructor<T> = new (...args: any[]) => T
|
||||
export * from "../common/medusa-container"
|
||||
@@ -93,3 +94,17 @@ export type ModuleExports = {
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
): Promise<void>
|
||||
}
|
||||
|
||||
export interface ModuleServiceInitializeOptions {
|
||||
database: {
|
||||
clientUrl: string
|
||||
schema?: string
|
||||
driverOptions?: Record<string, unknown>
|
||||
debug?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type ModuleServiceInitializeCustomDataLayerOptions = {
|
||||
manager?: any
|
||||
repositories?: { [key: string]: Constructor<RepositoryService> }
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface ProductDTO {
|
||||
tags: ProductTagDTO[]
|
||||
variants: ProductVariantDTO[]
|
||||
options: ProductOptionDTO[]
|
||||
images: ProductImageDTO[]
|
||||
discountable?: boolean
|
||||
external_id?: string | null
|
||||
created_at?: string | Date
|
||||
@@ -86,7 +87,7 @@ export interface ProductTagDTO {
|
||||
id: string
|
||||
value: string
|
||||
metadata?: Record<string, unknown> | null
|
||||
products: ProductDTO[]
|
||||
products?: ProductDTO[]
|
||||
}
|
||||
|
||||
export interface ProductCollectionDTO {
|
||||
@@ -95,6 +96,7 @@ export interface ProductCollectionDTO {
|
||||
handle: string
|
||||
metadata?: Record<string, unknown> | null
|
||||
deleted_at?: string | Date
|
||||
products?: ProductDTO[]
|
||||
}
|
||||
|
||||
export interface ProductTypeDTO {
|
||||
@@ -113,6 +115,13 @@ export interface ProductOptionDTO {
|
||||
deleted_at?: string | Date
|
||||
}
|
||||
|
||||
export interface ProductImageDTO {
|
||||
id: string
|
||||
url: string
|
||||
metadata?: Record<string, unknown> | null
|
||||
deleted_at?: string | Date
|
||||
}
|
||||
|
||||
export interface ProductOptionValueDTO {
|
||||
id: string
|
||||
value: string
|
||||
@@ -164,3 +173,126 @@ export interface FilterableProductCategoryProps
|
||||
is_internal?: boolean
|
||||
include_descendants_tree?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Write DTO (module API input)
|
||||
*/
|
||||
|
||||
export interface CreateProductTypeDTO {
|
||||
id?: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface CreateProductTagDTO {
|
||||
id?: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface CreateProductOptionDTO {
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface CreateProductVariantOptionDTO {
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface CreateProductVariantDTO {
|
||||
title: string
|
||||
sku?: string
|
||||
barcode?: string
|
||||
ean?: string
|
||||
upc?: string
|
||||
allow_backorder?: boolean
|
||||
inventory_quantity?: number
|
||||
manage_inventory?: boolean
|
||||
hs_code?: string
|
||||
origin_country?: string
|
||||
mid_code?: string
|
||||
material?: string
|
||||
weight?: number
|
||||
length?: number
|
||||
height?: number
|
||||
width?: number
|
||||
options?: CreateProductVariantOptionDTO[]
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface CreateProductDTO {
|
||||
title: string
|
||||
subtitle?: string
|
||||
description?: string
|
||||
is_giftcard?: boolean
|
||||
discountable?: boolean
|
||||
images?: string[] | { id?: string; url: string }[]
|
||||
thumbnail?: string
|
||||
handle?: string
|
||||
status?: ProductStatus
|
||||
type?: CreateProductTypeDTO
|
||||
type_id?: string
|
||||
collection_id?: string
|
||||
tags?: CreateProductTagDTO[]
|
||||
// sales_channel
|
||||
categories?: { id: string }[]
|
||||
options?: CreateProductOptionDTO[]
|
||||
variants?: CreateProductVariantDTO[]
|
||||
width?: number
|
||||
height?: number
|
||||
length?: number
|
||||
weight?: number
|
||||
origin_country?: string
|
||||
hs_code?: string
|
||||
material?: string
|
||||
mid_code?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface CreateProductOnlyDTO {
|
||||
title: string
|
||||
subtitle?: string
|
||||
description?: string
|
||||
is_giftcard?: boolean
|
||||
discountable?: boolean
|
||||
images?: { id?: string; url: string }[]
|
||||
thumbnail?: string
|
||||
handle?: string
|
||||
status?: ProductStatus
|
||||
collection_id?: string
|
||||
width?: number
|
||||
height?: number
|
||||
length?: number
|
||||
weight?: number
|
||||
origin_country?: string
|
||||
hs_code?: string
|
||||
material?: string
|
||||
mid_code?: string
|
||||
metadata?: Record<string, unknown>
|
||||
tags?: { id: string }[]
|
||||
categories?: { id: string }[]
|
||||
type_id?: string
|
||||
}
|
||||
|
||||
export interface CreateProductVariantOnlyDTO {
|
||||
title: string
|
||||
sku?: string
|
||||
barcode?: string
|
||||
ean?: string
|
||||
upc?: string
|
||||
allow_backorder?: boolean
|
||||
inventory_quantity?: number
|
||||
manage_inventory?: boolean
|
||||
hs_code?: string
|
||||
origin_country?: string
|
||||
mid_code?: string
|
||||
material?: string
|
||||
weight?: number
|
||||
length?: number
|
||||
height?: number
|
||||
width?: number
|
||||
options?: (CreateProductVariantOptionDTO & { option: any })[]
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface CreateProductOptionOnlyDTO {
|
||||
product: { id: string }
|
||||
title: string
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
CreateProductDTO,
|
||||
FilterableProductCategoryProps,
|
||||
FilterableProductCollectionProps,
|
||||
FilterableProductProps,
|
||||
@@ -11,48 +12,94 @@ import {
|
||||
ProductVariantDTO,
|
||||
} from "./common"
|
||||
import { FindConfig } from "../common"
|
||||
import { SharedContext } from "../shared-context"
|
||||
import { Context } from "../shared-context"
|
||||
|
||||
export interface IProductModuleService {
|
||||
retrieve(productId: string, sharedContext?: Context): Promise<ProductDTO>
|
||||
|
||||
export interface IProductModuleService<
|
||||
TProduct = any,
|
||||
TProductVariant = any,
|
||||
TProductTag = any,
|
||||
TProductCollection = any,
|
||||
TProductCategory = any
|
||||
> {
|
||||
list(
|
||||
filters?: FilterableProductProps,
|
||||
config?: FindConfig<ProductDTO>,
|
||||
sharedContext?: SharedContext
|
||||
sharedContext?: Context
|
||||
): Promise<ProductDTO[]>
|
||||
|
||||
listAndCount(
|
||||
filters?: FilterableProductProps,
|
||||
config?: FindConfig<ProductDTO>,
|
||||
sharedContext?: SharedContext
|
||||
sharedContext?: Context
|
||||
): Promise<[ProductDTO[], number]>
|
||||
|
||||
listTags(
|
||||
filters?: FilterableProductTagProps,
|
||||
config?: FindConfig<ProductTagDTO>,
|
||||
sharedContext?: SharedContext
|
||||
sharedContext?: Context
|
||||
): Promise<ProductTagDTO[]>
|
||||
|
||||
retrieveVariant(
|
||||
productVariantId: string,
|
||||
config?: FindConfig<ProductVariantDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<ProductVariantDTO>
|
||||
|
||||
listVariants(
|
||||
filters?: FilterableProductVariantProps,
|
||||
config?: FindConfig<ProductVariantDTO>,
|
||||
sharedContext?: SharedContext
|
||||
sharedContext?: Context
|
||||
): Promise<ProductVariantDTO[]>
|
||||
|
||||
listAndCountVariants(
|
||||
filters?: FilterableProductVariantProps,
|
||||
config?: FindConfig<ProductVariantDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<[ProductVariantDTO[], number]>
|
||||
|
||||
retrieveCollection(
|
||||
productCollectionId: string,
|
||||
config?: FindConfig<ProductCollectionDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<ProductCollectionDTO>
|
||||
|
||||
listCollections(
|
||||
filters?: FilterableProductCollectionProps,
|
||||
config?: FindConfig<ProductCollectionDTO>,
|
||||
sharedContext?: SharedContext
|
||||
sharedContext?: Context
|
||||
): Promise<ProductCollectionDTO[]>
|
||||
|
||||
listAndCountCollections(
|
||||
filters?: FilterableProductCollectionProps,
|
||||
config?: FindConfig<ProductCollectionDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<[ProductCollectionDTO[], number]>
|
||||
|
||||
retrieveCategory(
|
||||
productCategoryId: string,
|
||||
config?: FindConfig<ProductCategoryDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<ProductCategoryDTO>
|
||||
|
||||
listCategories(
|
||||
filters?: FilterableProductCategoryProps,
|
||||
config?: FindConfig<ProductCategoryDTO>,
|
||||
sharedContext?: SharedContext
|
||||
sharedContext?: Context
|
||||
): Promise<ProductCategoryDTO[]>
|
||||
|
||||
listAndCountCategories(
|
||||
filters?: FilterableProductCategoryProps,
|
||||
config?: FindConfig<ProductCategoryDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<[ProductCategoryDTO[], number]>
|
||||
|
||||
create(
|
||||
data: CreateProductDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<ProductDTO[]>
|
||||
|
||||
delete(productIds: string[], sharedContext?: Context): Promise<void>
|
||||
|
||||
softDelete(
|
||||
productIds: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<ProductDTO[]>
|
||||
|
||||
restore(productIds: string[], sharedContext?: Context): Promise<ProductDTO[]>
|
||||
}
|
||||
|
||||
@@ -3,3 +3,9 @@ import { EntityManager } from "typeorm"
|
||||
export type SharedContext = {
|
||||
transactionManager?: EntityManager
|
||||
}
|
||||
|
||||
export type Context<TManager = unknown> = {
|
||||
transactionManager?: TManager
|
||||
isolationLevel?: string
|
||||
enableNestedTransactions?: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user