diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index d65b796fd5..66cab04b8f 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -658,8 +658,7 @@ medusaIntegrationTestRunner({ } }) - // TODO: Enforce tag uniqueness in product module - it.skip("returns a list of products with tags", async () => { + it("returns a list of products with tags", async () => { const response = await api.get( `/admin/products?tags[]=${baseProduct.tags[0].id}`, adminHeaders @@ -668,26 +667,25 @@ medusaIntegrationTestRunner({ expect(response.status).toEqual(200) expect(response.data.products).toHaveLength(2) expect(response.data.products).toEqual( - expect.arrayContaining( - [ - expect.objectContaining({ - id: baseProduct.id, - tags: [ - expect.objectContaining({ id: baseProduct.tags[0].id }), - ], - }), - ], + expect.arrayContaining([ + expect.objectContaining({ + id: baseProduct.id, + tags: expect.arrayContaining([ + expect.objectContaining({ id: baseProduct.tags[0].id }), + ]), + }), expect.objectContaining({ id: publishedProduct.id, // It should be the same tag instance in both products - tags: [expect.objectContaining({ id: baseProduct.tags[0].id })], - }) - ) + tags: expect.arrayContaining([ + expect.objectContaining({ id: baseProduct.tags[0].id }), + ]), + }), + ]) ) }) - // TODO: Enforce tag uniqueness in product module - it.skip("returns a list of products with tags in a collection", async () => { + it("returns a list of products with tags in a collection", async () => { const response = await api.get( `/admin/products?collection_id[]=${baseCollection.id}&tags[]=${baseProduct.tags[0].id}`, adminHeaders @@ -700,7 +698,9 @@ medusaIntegrationTestRunner({ expect.objectContaining({ id: baseProduct.id, collection_id: baseCollection.id, - tags: [expect.objectContaining({ id: baseProduct.tags[0].id })], + tags: expect.arrayContaining([ + expect.objectContaining({ id: baseProduct.tags[0].id }), + ]), }), ]) ) @@ -2655,8 +2655,7 @@ medusaIntegrationTestRunner({ expect(response2.data.id).toEqual(res.data.product.id) }) - // TODO: We just need to return the correct error message - it.skip("should fail when creating a product with a handle that already exists", async () => { + it("should fail when creating a product with a handle that already exists", async () => { // Lets try to create a product with same handle as deleted one const payload = { title: baseProduct.title, @@ -2675,7 +2674,10 @@ medusaIntegrationTestRunner({ await api.post("/admin/products", payload, adminHeaders) } catch (error) { expect(error.response.data.message).toMatch( - "Product with handle test-product already exists." + breaking( + () => "Product with handle base-product already exists.", + () => "Product with handle: base-product already exists." + ) ) } }) diff --git a/packages/medusa/src/api-v2/admin/products/validators.ts b/packages/medusa/src/api-v2/admin/products/validators.ts index d939211a57..b440dda1da 100644 --- a/packages/medusa/src/api-v2/admin/products/validators.ts +++ b/packages/medusa/src/api-v2/admin/products/validators.ts @@ -84,6 +84,7 @@ export const AdminGetProductOptionsParams = createFindParams({ limit: 50, }).merge( z.object({ + q: z.string().optional(), id: z.union([z.string(), z.array(z.string())]).optional(), title: z.string().optional(), $and: z.lazy(() => AdminGetProductsParams.array()).optional(), diff --git a/packages/product/integration-tests/__fixtures__/product/data/products.ts b/packages/product/integration-tests/__fixtures__/product/data/products.ts index a0de3f11b0..ebd58385cb 100644 --- a/packages/product/integration-tests/__fixtures__/product/data/products.ts +++ b/packages/product/integration-tests/__fixtures__/product/data/products.ts @@ -30,7 +30,7 @@ export const productsData = [ tags: [ { id: "tag-3", - value: "Germany", + value: "Netherlands", }, ], }, diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts index ac9131f401..0d35fe2f0f 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts @@ -825,6 +825,7 @@ moduleIntegrationTestRunner({ const productTwoData = buildProductAndRelationsData({ collection_id: productCollectionTwo.id, + tags: [], }) await service.create([productOneData, productTwoData]) diff --git a/packages/product/integration-tests/__tests__/services/product-option/index.ts b/packages/product/integration-tests/__tests__/services/product-option/index.ts index 4884380c96..079937a53a 100644 --- a/packages/product/integration-tests/__tests__/services/product-option/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-option/index.ts @@ -341,7 +341,7 @@ moduleIntegrationTestRunner({ const productOptions = await service.list( { - title: "US%", + q: "US%", }, { relations: ["product"], diff --git a/packages/product/integration-tests/__tests__/services/product-tag/index.ts b/packages/product/integration-tests/__tests__/services/product-tag/index.ts index ef055797fa..d0491e41ff 100644 --- a/packages/product/integration-tests/__tests__/services/product-tag/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-tag/index.ts @@ -99,7 +99,7 @@ moduleIntegrationTestRunner({ }) it("list product tags by value matching string", async () => { - const tagsResults = await service.list({ value: "united kingdom" }) + const tagsResults = await service.list({ q: "united kingdom" }) expect(tagsResults).toEqual([ expect.objectContaining({ diff --git a/packages/product/integration-tests/__tests__/services/product/index.ts b/packages/product/integration-tests/__tests__/services/product/index.ts index a29321fe2a..a203fa9119 100644 --- a/packages/product/integration-tests/__tests__/services/product/index.ts +++ b/packages/product/integration-tests/__tests__/services/product/index.ts @@ -24,6 +24,7 @@ import { SqlEntityManager } from "@mikro-orm/postgresql" import { createProductCategories } from "../../../__fixtures__/product-category" import { Modules } from "@medusajs/modules-sdk" import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { ProductTag } from "../../../../src/models" jest.setTimeout(30000) diff --git a/packages/product/src/migrations/InitialSetup20240401153642.ts b/packages/product/src/migrations/InitialSetup20240401153642.ts index ccbfb65b1a..2afb361dee 100644 --- a/packages/product/src/migrations/InitialSetup20240401153642.ts +++ b/packages/product/src/migrations/InitialSetup20240401153642.ts @@ -48,8 +48,7 @@ export class InitialSetup20240315083440 extends Migration { this.addSql('create table if not exists "product_tag" ("id" text not null, "value" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "product_tag_pkey" primary key ("id"));'); - // TODO: We need to modify upsertWithReplace to handle unique constraints - // this.addSql('create unique index if not exists "IDX_tag_value_unique" on "product_tag" (value) where deleted_at is null;') + this.addSql('create unique index if not exists "IDX_tag_value_unique" on "product_tag" (value) where deleted_at is null;') this.addSql('create index if not exists "IDX_product_tag_deleted_at" on "product_tag" ("deleted_at");'); this.addSql('create table if not exists "product_type" ("id" text not null, "value" text not null, "metadata" json null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "product_type_pkey" primary key ("id"));'); diff --git a/packages/product/src/models/product-option.ts b/packages/product/src/models/product-option.ts index 5cf5fc4779..cb537f0f35 100644 --- a/packages/product/src/models/product-option.ts +++ b/packages/product/src/models/product-option.ts @@ -1,5 +1,6 @@ import { DALUtils, + Searchable, createPsqlIndexStatementHelper, generateEntityId, } from "@medusajs/utils" @@ -35,6 +36,7 @@ class ProductOption { @PrimaryKey({ columnType: "text" }) id!: string + @Searchable() @Property({ columnType: "text" }) title: string diff --git a/packages/product/src/models/product-tag.ts b/packages/product/src/models/product-tag.ts index ccaadf3cbf..e03c82dc14 100644 --- a/packages/product/src/models/product-tag.ts +++ b/packages/product/src/models/product-tag.ts @@ -12,6 +12,7 @@ import { import { DALUtils, + Searchable, createPsqlIndexStatementHelper, generateEntityId, } from "@medusajs/utils" @@ -33,6 +34,7 @@ class ProductTag { @PrimaryKey({ columnType: "text" }) id!: string + @Searchable() @Property({ columnType: "text" }) value: string diff --git a/packages/product/src/models/product-type.ts b/packages/product/src/models/product-type.ts index f9f941abbc..bf67de8ac7 100644 --- a/packages/product/src/models/product-type.ts +++ b/packages/product/src/models/product-type.ts @@ -10,6 +10,7 @@ import { import { DALUtils, + Searchable, createPsqlIndexStatementHelper, generateEntityId, } from "@medusajs/utils" @@ -30,6 +31,7 @@ class ProductType { @PrimaryKey({ columnType: "text" }) id!: string + @Searchable() @Property({ columnType: "text" }) value: string diff --git a/packages/product/src/models/product-variant.ts b/packages/product/src/models/product-variant.ts index d3bdf65604..8128b1adf9 100644 --- a/packages/product/src/models/product-variant.ts +++ b/packages/product/src/models/product-variant.ts @@ -85,12 +85,15 @@ class ProductVariant { @Property({ columnType: "text", nullable: true }) sku?: string | null + @Searchable() @Property({ columnType: "text", nullable: true }) barcode?: string | null + @Searchable() @Property({ columnType: "text", nullable: true }) ean?: string | null + @Searchable() @Property({ columnType: "text", nullable: true }) upc?: string | null diff --git a/packages/product/src/models/product.ts b/packages/product/src/models/product.ts index 917ee2de92..46e72ecf28 100644 --- a/packages/product/src/models/product.ts +++ b/packages/product/src/models/product.ts @@ -72,6 +72,7 @@ class Product { @Property({ columnType: "text" }) handle?: string + @Searchable() @Property({ columnType: "text", nullable: true }) subtitle?: string | null diff --git a/packages/product/src/services/index.ts b/packages/product/src/services/index.ts index 3003e5d8c2..b880627471 100644 --- a/packages/product/src/services/index.ts +++ b/packages/product/src/services/index.ts @@ -1,7 +1,3 @@ export { default as ProductService } from "./product" export { default as ProductCategoryService } from "./product-category" -export { default as ProductCollectionService } from "./product-collection" export { default as ProductModuleService } from "./product-module-service" -export { default as ProductTagService } from "./product-tag" -export { default as ProductTypeService } from "./product-type" -export { default as ProductOptionService } from "./product-option" diff --git a/packages/product/src/services/product-collection.ts b/packages/product/src/services/product-collection.ts deleted file mode 100644 index d53ee6d139..0000000000 --- a/packages/product/src/services/product-collection.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types" -import { InjectManager, MedusaContext, ModulesSdkUtils } from "@medusajs/utils" - -import { ProductCollection } from "@models" - -type InjectedDependencies = { - productCollectionRepository: DAL.RepositoryService -} - -export default class ProductCollectionService< - TEntity extends ProductCollection = ProductCollection -> extends ModulesSdkUtils.internalModuleServiceFactory( - ProductCollection -) { - // eslint-disable-next-line max-len - protected readonly productCollectionRepository_: DAL.RepositoryService - - constructor(container: InjectedDependencies) { - super(container) - this.productCollectionRepository_ = container.productCollectionRepository - } - - @InjectManager("productCollectionRepository_") - async list( - filters: ProductTypes.FilterableProductCollectionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - return await this.productCollectionRepository_.find( - this.buildListQueryOptions(filters, config), - sharedContext - ) - } - - @InjectManager("productCollectionRepository_") - async listAndCount( - filters: ProductTypes.FilterableProductCollectionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise<[TEntity[], number]> { - return await this.productCollectionRepository_.findAndCount( - this.buildListQueryOptions(filters, config), - sharedContext - ) - } - - protected buildListQueryOptions( - filters: ProductTypes.FilterableProductCollectionProps = {}, - config: FindConfig = {} - ): DAL.FindOptions { - const queryOptions = ModulesSdkUtils.buildQuery(filters, config) - - queryOptions.where ??= {} - - if (filters.title) { - queryOptions.where.title = { - $like: `%${filters.title}%`, - } as DAL.FindOptions["where"]["title"] - } - - return queryOptions - } -} diff --git a/packages/product/src/services/product-module-service.ts b/packages/product/src/services/product-module-service.ts index 43a089ea1c..efe4b6672c 100644 --- a/packages/product/src/services/product-module-service.ts +++ b/packages/product/src/services/product-module-service.ts @@ -18,14 +18,7 @@ import { ProductType, ProductVariant, } from "@models" -import { - ProductCategoryService, - ProductCollectionService, - ProductOptionService, - ProductService, - ProductTagService, - ProductTypeService, -} from "@services" +import { ProductCategoryService, ProductService } from "@services" import { arrayDifference, @@ -58,12 +51,12 @@ type InjectedDependencies = { baseRepository: DAL.RepositoryService productService: ProductService productVariantService: ModulesSdkTypes.InternalModuleService - productTagService: ProductTagService + productTagService: ModulesSdkTypes.InternalModuleService productCategoryService: ProductCategoryService - productCollectionService: ProductCollectionService + productCollectionService: ModulesSdkTypes.InternalModuleService productImageService: ModulesSdkTypes.InternalModuleService - productTypeService: ProductTypeService - productOptionService: ProductOptionService + productTypeService: ModulesSdkTypes.InternalModuleService + productOptionService: ModulesSdkTypes.InternalModuleService productOptionValueService: ModulesSdkTypes.InternalModuleService eventBusModuleService?: IEventBusModuleService } @@ -133,13 +126,13 @@ export default class ProductModuleService< // eslint-disable-next-line max-len protected readonly productCategoryService_: ProductCategoryService - protected readonly productTagService_: ProductTagService + protected readonly productTagService_: ModulesSdkTypes.InternalModuleService // eslint-disable-next-line max-len - protected readonly productCollectionService_: ProductCollectionService + protected readonly productCollectionService_: ModulesSdkTypes.InternalModuleService // eslint-disable-next-line max-len protected readonly productImageService_: ModulesSdkTypes.InternalModuleService - protected readonly productTypeService_: ProductTypeService - protected readonly productOptionService_: ProductOptionService + protected readonly productTypeService_: ModulesSdkTypes.InternalModuleService + protected readonly productOptionService_: ModulesSdkTypes.InternalModuleService // eslint-disable-next-line max-len protected readonly productOptionValueService_: ModulesSdkTypes.InternalModuleService protected readonly eventBusModuleService_?: IEventBusModuleService @@ -1122,8 +1115,8 @@ export default class ProductModuleService< data: ProductTypes.CreateProductDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { - const normalizedInput = data.map( - ProductModuleService.normalizeCreateProductInput + const normalizedInput = await Promise.all( + data.map((d) => this.normalizeCreateProductInput(d, sharedContext)) ) const productData = await this.productService_.upsertWithReplace( @@ -1178,8 +1171,8 @@ export default class ProductModuleService< data: UpdateProductInput[], @MedusaContext() sharedContext: Context = {} ): Promise { - const normalizedInput = data.map( - ProductModuleService.normalizeUpdateProductInput + const normalizedInput = await Promise.all( + data.map((d) => this.normalizeUpdateProductInput(d, sharedContext)) ) const productData = await this.productService_.upsertWithReplace( @@ -1260,12 +1253,13 @@ export default class ProductModuleService< return productData } - protected static normalizeCreateProductInput( - product: ProductTypes.CreateProductDTO - ): ProductTypes.CreateProductDTO { - const productData = ProductModuleService.normalizeUpdateProductInput( + protected async normalizeCreateProductInput( + product: ProductTypes.CreateProductDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const productData = (await this.normalizeUpdateProductInput( product as UpdateProductInput - ) as ProductTypes.CreateProductDTO + )) as ProductTypes.CreateProductDTO if (!productData.handle && productData.title) { productData.handle = kebabCase(productData.title) @@ -1282,14 +1276,35 @@ export default class ProductModuleService< return productData } - protected static normalizeUpdateProductInput( - product: UpdateProductInput - ): UpdateProductInput { + protected async normalizeUpdateProductInput( + product: UpdateProductInput, + @MedusaContext() sharedContext: Context = {} + ): Promise { const productData = { ...product } if (productData.is_giftcard) { productData.discountable = false } + if (productData.tags?.length && productData.tags.some((t) => !t.id)) { + const dbTags = await this.productTagService_.list( + { + value: productData.tags + .map((t) => t.value) + .filter((v) => !!v) as string[], + }, + { take: null }, + sharedContext + ) + + productData.tags = productData.tags.map((tag) => { + const dbTag = dbTags.find((t) => t.value === tag.value) + return { + ...tag, + ...(dbTag ? { id: dbTag.id } : {}), + } + }) + } + if (productData.options?.length) { ;(productData as any).options = productData.options?.map((option) => { return { diff --git a/packages/product/src/services/product-option.ts b/packages/product/src/services/product-option.ts deleted file mode 100644 index eca5140fde..0000000000 --- a/packages/product/src/services/product-option.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ProductOption } from "@models" -import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types" -import { InjectManager, MedusaContext, ModulesSdkUtils } from "@medusajs/utils" - -type InjectedDependencies = { - productOptionRepository: DAL.RepositoryService -} - -export default class ProductOptionService< - TEntity extends ProductOption = ProductOption -> extends ModulesSdkUtils.internalModuleServiceFactory( - ProductOption -) { - protected readonly productOptionRepository_: DAL.RepositoryService - - constructor(container: InjectedDependencies) { - super(container) - this.productOptionRepository_ = container.productOptionRepository - } - - @InjectManager("productOptionRepository_") - async list( - filters: ProductTypes.FilterableProductOptionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext?: Context - ): Promise { - return await this.productOptionRepository_.find( - this.buildQueryForList(filters, config), - sharedContext - ) - } - - @InjectManager("productOptionRepository_") - async listAndCount( - filters: ProductTypes.FilterableProductOptionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext?: Context - ): Promise<[TEntity[], number]> { - return await this.productOptionRepository_.findAndCount( - this.buildQueryForList(filters, config), - sharedContext - ) - } - - private buildQueryForList( - filters: ProductTypes.FilterableProductOptionProps = {}, - config: FindConfig = {} - ): DAL.FindOptions { - const queryOptions = ModulesSdkUtils.buildQuery(filters, config) - - if (filters.title) { - queryOptions.where.title = { - $ilike: filters.title, - } as DAL.FindOptions["where"]["title"] - } - - return queryOptions - } -} diff --git a/packages/product/src/services/product-tag.ts b/packages/product/src/services/product-tag.ts deleted file mode 100644 index 2111a12862..0000000000 --- a/packages/product/src/services/product-tag.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { ProductTag } from "@models" -import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types" -import { InjectManager, MedusaContext, ModulesSdkUtils } from "@medusajs/utils" - -type InjectedDependencies = { - productTagRepository: DAL.RepositoryService -} - -export default class ProductTagService< - TEntity extends ProductTag = ProductTag -> extends ModulesSdkUtils.internalModuleServiceFactory( - ProductTag -) { - protected readonly productTagRepository_: DAL.RepositoryService - - constructor(container: InjectedDependencies) { - super(container) - this.productTagRepository_ = container.productTagRepository - } - @InjectManager("productTagRepository_") - async list( - filters: ProductTypes.FilterableProductTagProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - return await this.productTagRepository_.find( - this.buildQueryForList(filters, config), - sharedContext - ) - } - - @InjectManager("productTagRepository_") - async listAndCount( - filters: ProductTypes.FilterableProductTagProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise<[TEntity[], number]> { - return await this.productTagRepository_.findAndCount( - this.buildQueryForList(filters, config), - sharedContext - ) - } - - private buildQueryForList( - filters: ProductTypes.FilterableProductTagProps = {}, - config: FindConfig = {} - ): DAL.FindOptions { - const queryOptions = ModulesSdkUtils.buildQuery(filters, config) - - if (filters.value) { - queryOptions.where.value = { - $ilike: filters.value, - } as DAL.FindOptions["where"]["value"] - } - - return queryOptions - } -} diff --git a/packages/product/src/services/product-type.ts b/packages/product/src/services/product-type.ts deleted file mode 100644 index 0c54344a91..0000000000 --- a/packages/product/src/services/product-type.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ProductType } from "@models" -import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types" -import { InjectManager, MedusaContext, ModulesSdkUtils } from "@medusajs/utils" - -type InjectedDependencies = { - productTypeRepository: DAL.RepositoryService -} - -export default class ProductTypeService< - TEntity extends ProductType = ProductType -> extends ModulesSdkUtils.internalModuleServiceFactory( - ProductType -) { - protected readonly productTypeRepository_: DAL.RepositoryService - - constructor(container: InjectedDependencies) { - super(container) - this.productTypeRepository_ = container.productTypeRepository - } - - @InjectManager("productTypeRepository_") - async list( - filters: ProductTypes.FilterableProductTypeProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - return await this.productTypeRepository_.find( - this.buildQueryForList(filters, config), - sharedContext - ) - } - - @InjectManager("productTypeRepository_") - async listAndCount( - filters: ProductTypes.FilterableProductTypeProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise<[TEntity[], number]> { - return await this.productTypeRepository_.findAndCount( - this.buildQueryForList(filters, config), - sharedContext - ) - } - - private buildQueryForList( - filters: ProductTypes.FilterableProductTypeProps = {}, - config: FindConfig = {} - ): DAL.FindOptions { - const queryOptions = ModulesSdkUtils.buildQuery(filters, config) - - if (filters.value) { - queryOptions.where.value = { - $ilike: filters.value, - } as DAL.FindOptions["where"]["value"] - } - - return queryOptions - } -} diff --git a/packages/types/src/product/common.ts b/packages/types/src/product/common.ts index 0708a753c2..a40cc133bd 100644 --- a/packages/types/src/product/common.ts +++ b/packages/types/src/product/common.ts @@ -724,6 +724,10 @@ export interface FilterableProductProps */ export interface FilterableProductTagProps extends BaseFilterable { + /** + * Search through the tags' values. + */ + q?: string /** * The IDs to filter product tags by. */ @@ -731,7 +735,7 @@ export interface FilterableProductTagProps /** * The value to filter product tags by. */ - value?: string + value?: string | string[] } /** @@ -744,6 +748,10 @@ export interface FilterableProductTagProps */ export interface FilterableProductTypeProps extends BaseFilterable { + /** + * Search through the types' values. + */ + q?: string /** * The IDs to filter product types by. */ @@ -765,6 +773,10 @@ export interface FilterableProductTypeProps */ export interface FilterableProductOptionProps extends BaseFilterable { + /** + * Search through the options' titles. + */ + q?: string /** * The IDs to filter product options by. */ @@ -789,6 +801,10 @@ export interface FilterableProductOptionProps */ export interface FilterableProductCollectionProps extends BaseFilterable { + /** + * Search through the collections' titles. + */ + q?: string /** * The IDs to filter product collections by. */ @@ -815,6 +831,10 @@ export interface FilterableProductCollectionProps */ export interface FilterableProductVariantProps extends BaseFilterable { + /** + * Search through the title and different code attributes on the variant + */ + q?: string /** * The IDs to filter product variants by. */ diff --git a/packages/utils/src/dal/mikro-orm/db-error-mapper.ts b/packages/utils/src/dal/mikro-orm/db-error-mapper.ts index 7893d778dc..2109d05f15 100644 --- a/packages/utils/src/dal/mikro-orm/db-error-mapper.ts +++ b/packages/utils/src/dal/mikro-orm/db-error-mapper.ts @@ -24,7 +24,7 @@ export const dbErrorMapper = (err: Error) => { throw new MedusaError( MedusaError.Types.INVALID_DATA, - `${upperCaseFirst(info.table)} with ${info.keys + `${upperCaseFirst(info.table.split("_").join(" "))} with ${info.keys .map((key, i) => `${key}: ${info.values[i]}`) .join(", ")} already exists.` ) @@ -37,7 +37,7 @@ export const dbErrorMapper = (err: Error) => { throw new MedusaError( MedusaError.Types.INVALID_DATA, `Cannot set field '${(err as any).column}' of ${upperCaseFirst( - (err as any).table + (err as any).table.split("_").join(" ") )} to null` ) }