diff --git a/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts b/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts index dab64d59be..cadd6c2b9a 100644 --- a/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts +++ b/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts @@ -309,4 +309,104 @@ describe("Customer Module Service", () => { } }) }) + + describe("update", () => { + it("should update a single customer", async () => { + const [customer] = await service.create([ + { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, + ]) + + const updateData = { first_name: "Jonathan" } + const updatedCustomer = await service.update(customer.id, updateData) + + expect(updatedCustomer).toEqual( + expect.objectContaining({ id: customer.id, first_name: "Jonathan" }) + ) + }) + + it("should update multiple customers by IDs", async () => { + const customers = await service.create([ + { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + const updateData = { last_name: "Updated" } + const customerIds = customers.map((customer) => customer.id) + const updatedCustomers = await service.update(customerIds, updateData) + + updatedCustomers.forEach((updatedCustomer) => { + expect(updatedCustomer).toEqual( + expect.objectContaining({ last_name: "Updated" }) + ) + }) + }) + + it("should update customers using a selector", async () => { + await service.create([ + { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, + { first_name: "Jane", last_name: "Doe", email: "jane.doe@example.com" }, + ]) + + const selector = { last_name: "Doe" } + const updateData = { last_name: "Updated" } + const updatedCustomers = await service.update(selector, updateData) + + updatedCustomers.forEach((updatedCustomer) => { + expect(updatedCustomer).toEqual( + expect.objectContaining({ last_name: "Updated" }) + ) + }) + }) + }) + + describe("delete", () => { + it("should delete a single customer", async () => { + const [customer] = await service.create([ + { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, + ]) + + await service.delete(customer.id) + + await expect(service.retrieve(customer.id)).rejects.toThrow( + `Customer with id: ${customer.id} was not found` + ) + }) + + it("should delete multiple customers by IDs", async () => { + const customers = await service.create([ + { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + const customerIds = customers.map((customer) => customer.id) + await service.delete(customerIds) + + for (const customer of customers) { + await expect(service.retrieve(customer.id)).rejects.toThrow( + `Customer with id: ${customer.id} was not found` + ) + } + }) + + it("should delete customers using a selector", async () => { + await service.create([ + { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, + { first_name: "Jane", last_name: "Doe", email: "jane.doe@example.com" }, + ]) + + const selector = { last_name: "Doe" } + await service.delete(selector) + + const remainingCustomers = await service.list({ last_name: "Doe" }) + expect(remainingCustomers.length).toBe(0) + }) + }) }) diff --git a/packages/customer/src/services/customer-module.ts b/packages/customer/src/services/customer-module.ts index 777b8c17c1..0a1c5dc16c 100644 --- a/packages/customer/src/services/customer-module.ts +++ b/packages/customer/src/services/customer-module.ts @@ -12,6 +12,8 @@ import { InjectManager, InjectTransactionManager, MedusaContext, + isString, + isObject, } from "@medusajs/utils" import { joinerConfig } from "../joiner-config" import * as services from "../services" @@ -91,22 +93,101 @@ export default class CustomerModuleService implements ICustomerModuleService { ) { const data = Array.isArray(dataOrArray) ? dataOrArray : [dataOrArray] const customer = await this.customerService_.create(data, sharedContext) + const serialized = await this.baseRepository_.serialize< + CustomerTypes.CustomerDTO[] + >(customer, { + populate: true, + }) + return Array.isArray(dataOrArray) ? serialized : serialized[0] + } - if (Array.isArray(dataOrArray)) { - return await this.baseRepository_.serialize( - customer, + update( + customerId: string, + data: Partial, + sharedContext?: Context + ): Promise + update( + customerIds: string[], + data: Partial, + sharedContext?: Context + ): Promise + update( + selector: CustomerTypes.FilterableCustomerProps, + data: Partial, + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async update( + idsOrSelector: string | string[] | CustomerTypes.FilterableCustomerProps, + data: Partial, + @MedusaContext() sharedContext: Context = {} + ) { + let updateData: CustomerTypes.UpdateCustomerDTO[] = [] + if (typeof idsOrSelector === "string") { + updateData = [ { - populate: true, - } + id: idsOrSelector, + ...data, + }, + ] + } else if (Array.isArray(idsOrSelector)) { + updateData = idsOrSelector.map((id) => ({ + id, + ...data, + })) + } else { + const ids = await this.customerService_.list( + idsOrSelector, + { select: ["id"] }, + sharedContext ) + updateData = ids.map(({ id }) => ({ + id, + ...data, + })) } - return await this.baseRepository_.serialize( - customer[0], - { - populate: true, - } + const customers = await this.customerService_.update( + updateData, + sharedContext ) + const serialized = await this.baseRepository_.serialize< + CustomerTypes.CustomerDTO[] + >(customers, { + populate: true, + }) + + return isString(idsOrSelector) ? serialized[0] : serialized + } + + delete(customerId: string, sharedContext?: Context): Promise + delete(customerIds: string[], sharedContext?: Context): Promise + delete( + selector: CustomerTypes.FilterableCustomerProps, + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async delete( + idsOrSelector: string | string[] | CustomerTypes.FilterableCustomerProps, + @MedusaContext() sharedContext: Context = {} + ) { + let toDelete = Array.isArray(idsOrSelector) + ? idsOrSelector + : [idsOrSelector as string] + if (isObject(idsOrSelector)) { + const ids = await this.customerService_.list( + idsOrSelector, + { + select: ["id"], + }, + sharedContext + ) + toDelete = ids.map(({ id }) => id) + } + + return await this.customerService_.delete(toDelete, sharedContext) } @InjectManager("baseRepository_") @@ -174,19 +255,13 @@ export default class CustomerModuleService implements ICustomerModuleService { : [dataOrArrayOfData] const groups = await this.customerGroupService_.create(data, sharedContext) + const serialized = await this.baseRepository_.serialize< + CustomerTypes.CustomerGroupDTO[] + >(groups, { + populate: true, + }) - if (Array.isArray(dataOrArrayOfData)) { - return await this.baseRepository_.serialize< - CustomerTypes.CustomerGroupDTO[] - >(groups, { - populate: true, - }) - } - - return await this.baseRepository_.serialize( - groups[0], - { populate: true } - ) + return Array.isArray(dataOrArrayOfData) ? serialized : serialized[0] } async addCustomerToGroup( diff --git a/packages/customer/src/services/customer.ts b/packages/customer/src/services/customer.ts index 03523ee1b8..3aec4769e4 100644 --- a/packages/customer/src/services/customer.ts +++ b/packages/customer/src/services/customer.ts @@ -1,16 +1,19 @@ -import { CreateCustomerDTO, DAL } from "@medusajs/types" +import { CustomerTypes, DAL } from "@medusajs/types" import { ModulesSdkUtils } from "@medusajs/utils" import { Customer } from "@models" type InjectedDependencies = { - cartRepository: DAL.RepositoryService + customerRepository: DAL.RepositoryService } export default class CustomerService< TEntity extends Customer = Customer > extends ModulesSdkUtils.abstractServiceFactory< InjectedDependencies, - { create: CreateCustomerDTO } + { + create: CustomerTypes.CreateCustomerDTO + update: CustomerTypes.UpdateCustomerDTO + } >(Customer) { constructor(container: InjectedDependencies) { // @ts-ignore diff --git a/packages/types/src/customer/mutations.ts b/packages/types/src/customer/mutations.ts index b01a063a9d..929b4f8734 100644 --- a/packages/types/src/customer/mutations.ts +++ b/packages/types/src/customer/mutations.ts @@ -9,6 +9,7 @@ export interface CreateCustomerDTO { } export interface UpdateCustomerDTO { + id: string company_name?: string first_name?: string last_name?: string @@ -28,6 +29,24 @@ export interface CustomerGroupUpdatableFileds { metadata?: Record | null } +export interface UpdateCustomerGroupDTO { + id?: string + name?: string + customer_ids?: string[] + metadata?: Record | null +} + +export interface CreateCustomerGroupDTO { + name: string + metadata?: Record | null + created_by?: string +} + +export interface CustomerGroupUpdatableFileds { + name?: string + metadata?: Record | null +} + export interface UpdateCustomerGroupDTO { id?: string name?: string diff --git a/packages/types/src/customer/service.ts b/packages/types/src/customer/service.ts index f478024e46..c13608f92d 100644 --- a/packages/types/src/customer/service.ts +++ b/packages/types/src/customer/service.ts @@ -24,6 +24,29 @@ export interface ICustomerModuleService extends IModuleService { create(data: CreateCustomerDTO, sharedContext?: Context): Promise + update( + customerId: string, + data: Partial, + sharedContext?: Context + ): Promise + update( + customerIds: string[], + data: Partial, + sharedContext?: Context + ): Promise + update( + selector: FilterableCustomerProps, + data: Partial, + sharedContext?: Context + ): Promise + + delete(customerId: string, sharedContext?: Context): Promise + delete(customerIds: string[], sharedContext?: Context): Promise + delete( + selector: FilterableCustomerProps, + sharedContext?: Context + ): Promise + createCustomerGroup( data: CreateCustomerGroupDTO[], sharedContext?: Context