diff --git a/integration-tests/api/__tests__/admin/customer-groups.js b/integration-tests/api/__tests__/admin/customer-groups.js index b2eb41a400..95dd8d9e7e 100644 --- a/integration-tests/api/__tests__/admin/customer-groups.js +++ b/integration-tests/api/__tests__/admin/customer-groups.js @@ -55,7 +55,7 @@ describe("/admin/customer-groups", () => { }) expect(response.status).toEqual(200) - expect(response.data.customerGroup).toEqual( + expect(response.data.customer_group).toEqual( expect.objectContaining({ name: "test group", }) @@ -193,6 +193,88 @@ describe("/admin/customer-groups", () => { }) }) + describe("POST /admin/customer-groups/:id", () => { + beforeEach(async () => { + try { + await adminSeeder(dbConnection) + await customerSeeder(dbConnection) + } catch (err) { + console.log(err) + throw err + } + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("updates group name & metadata", async () => { + const api = useApi() + + const id = "customer-group-2" + + const body = { + name: "vip-customers-v2", + metadata: { + metaKey1: `metaValue1`, + }, + } + + const response = await api.post(`/admin/customer-groups/${id}`, body, { + headers: { + Authorization: "Bearer test_token", + }, + }) + + expect(response.status).toEqual(200) + expect(response.data.customer_group).toEqual( + expect.objectContaining({ + id: "customer-group-2", + name: "vip-customers-v2", + metadata: { + data1: "value1", + metaKey1: `metaValue1`, + }, + }) + ) + expect(response.data.customer_group).not.toHaveProperty("customers") + }) + + it("deletes `metadata` nested key", async () => { + const api = useApi() + + const id = "customer-group-2" + // already has some metadata initially + + const body = { + name: "vip-customers-v2", + metadata: { + data1: null, // delete + data2: "val2", // insert + }, + } + + const response = await api + .post(`/admin/customer-groups/${id}?expand=customers`, body, { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch(console.log) + + expect(response.status).toEqual(200) + expect(response.data.customer_group).toEqual( + expect.objectContaining({ + id: "customer-group-2", + name: "vip-customers-v2", + metadata: { data1: null, data2: "val2" }, + customers: [], + }) + ) + }) + }) + describe("GET /admin/customer-groups", () => { beforeEach(async () => { try { @@ -221,13 +303,13 @@ describe("/admin/customer-groups", () => { }) expect(response.status).toEqual(200) - expect(response.data.customerGroup).toEqual( + expect(response.data.customer_group).toEqual( expect.objectContaining({ id: "customer-group-1", name: "vip-customers", }) ) - expect(response.data.customerGroup).not.toHaveProperty("customers") + expect(response.data.customer_group).not.toHaveProperty("customers") }) it("gets customer group with `customers` prop", async () => { @@ -245,13 +327,13 @@ describe("/admin/customer-groups", () => { ) expect(response.status).toEqual(200) - expect(response.data.customerGroup).toEqual( + expect(response.data.customer_group).toEqual( expect.objectContaining({ id: "customer-group-1", name: "vip-customers", }) ) - expect(response.data.customerGroup.customers).toEqual([]) + expect(response.data.customer_group.customers).toEqual([]) }) it("throws error when a customer group doesn't exist", async () => { diff --git a/integration-tests/api/helpers/customer-seeder.js b/integration-tests/api/helpers/customer-seeder.js index 8a2d4d8826..92224e2cc9 100644 --- a/integration-tests/api/helpers/customer-seeder.js +++ b/integration-tests/api/helpers/customer-seeder.js @@ -52,6 +52,17 @@ module.exports = async (connection, data = {}) => { name: "vip-customers", }) + await manager.insert(CustomerGroup, { + id: "customer-group-2", + name: "test-group-2", + metadata: { data1: "value1" }, + }) + + await manager.insert(CustomerGroup, { + id: "customer-group-3", + name: "vest-group-3", + }) + await manager.insert(CustomerGroup, { id: "test-group-4", name: "test-group-4", diff --git a/packages/medusa/src/api/routes/admin/customer-groups/create-customer-group.ts b/packages/medusa/src/api/routes/admin/customer-groups/create-customer-group.ts index d02ced32c5..8be4b65270 100644 --- a/packages/medusa/src/api/routes/admin/customer-groups/create-customer-group.ts +++ b/packages/medusa/src/api/routes/admin/customer-groups/create-customer-group.ts @@ -20,8 +20,8 @@ import { validator } from "../../../../utils/validator" * application/json: * schema: * properties: - * customerGroup: - * $ref: "#/components/schemas/customergroup" + * customer_group: + * $ref: "#/components/schemas/customer_group" */ export default async (req, res) => { @@ -32,7 +32,7 @@ export default async (req, res) => { ) const customerGroup = await customerGroupService.create(validated) - res.status(200).json({ customerGroup }) + res.status(200).json({ customer_group: customerGroup }) } export class AdminPostCustomerGroupsReq { diff --git a/packages/medusa/src/api/routes/admin/customer-groups/get-customer-group.ts b/packages/medusa/src/api/routes/admin/customer-groups/get-customer-group.ts index ecbad1af32..2318f62d62 100644 --- a/packages/medusa/src/api/routes/admin/customer-groups/get-customer-group.ts +++ b/packages/medusa/src/api/routes/admin/customer-groups/get-customer-group.ts @@ -20,13 +20,16 @@ import { defaultAdminCustomerGroupsRelations } from "." * application/json: * schema: * properties: - * customerGroup: - * $ref: "#/components/schemas/customer-group" + * customer_group: + * $ref: "#/components/schemas/customer_group" */ export default async (req, res) => { const { id } = req.params - const validated = await validator(AdminGetCustomerGroupsGroupParams, req.query) + const validated = await validator( + AdminGetCustomerGroupsGroupParams, + req.query + ) const customerGroupService: CustomerGroupService = req.scope.resolve( "customerGroupService" @@ -45,7 +48,7 @@ export default async (req, res) => { const customerGroup = await customerGroupService.retrieve(id, findConfig) - res.json({ customerGroup }) + res.json({ customer_group: customerGroup }) } export class AdminGetCustomerGroupsGroupParams extends FindParams {} diff --git a/packages/medusa/src/api/routes/admin/customer-groups/index.ts b/packages/medusa/src/api/routes/admin/customer-groups/index.ts index 3e3b87571f..7bbed35516 100644 --- a/packages/medusa/src/api/routes/admin/customer-groups/index.ts +++ b/packages/medusa/src/api/routes/admin/customer-groups/index.ts @@ -14,6 +14,11 @@ export default (app) => { "/:id", middlewares.wrap(require("./delete-customer-group").default) ) + route.post( + "/:id", + middlewares.wrap(require("./update-customer-group").default) + ) + return app } diff --git a/packages/medusa/src/api/routes/admin/customer-groups/update-customer-group.ts b/packages/medusa/src/api/routes/admin/customer-groups/update-customer-group.ts new file mode 100644 index 0000000000..c89497f630 --- /dev/null +++ b/packages/medusa/src/api/routes/admin/customer-groups/update-customer-group.ts @@ -0,0 +1,70 @@ +import { IsObject, IsOptional, IsString } from "class-validator" +import { defaultAdminCustomerGroupsRelations } from "." + +import { CustomerGroupService } from "../../../../services" +import { FindParams } from "../../../../types/common" +import { validator } from "../../../../utils/validator" + +/** + * @oas [post] /customer-groups/{id} + * operationId: "PostCustomerGroupsGroup" + * summary: "Update a CustomerGroup" + * description: "Update a CustomerGroup." + * x-authenticated: true + * parameters: + * - (path) id=* {string} The id of the customer group. + * - (body) name=* {string} Name of the customer group + * - (body) metadata {object} Metadata for the customer. + * tags: + * - CustomerGroup + * responses: + * 200: + * description: OK + * content: + * application/json: + * schema: + * properties: + * customer_group: + * $ref: "#/components/schemas/customer_group" + */ + +export default async (req, res) => { + const { id } = req.params + + const validatedBody = await validator( + AdminPostCustomerGroupsGroupReq, + req.body + ) + const validatedQuery = await validator(FindParams, req.query) + + const customerGroupService: CustomerGroupService = req.scope.resolve( + "customerGroupService" + ) + + await customerGroupService.update(id, validatedBody) + + let expandFields: string[] = [] + if (validatedQuery.expand) { + expandFields = validatedQuery.expand.split(",") + } + + const findConfig = { + relations: expandFields.length + ? expandFields + : defaultAdminCustomerGroupsRelations, + } + + const customerGroup = await customerGroupService.retrieve(id, findConfig) + + res.json({ customer_group: customerGroup }) +} + +export class AdminPostCustomerGroupsGroupReq { + @IsString() + @IsOptional() + name?: string + + @IsObject() + @IsOptional() + metadata?: object +} diff --git a/packages/medusa/src/services/customer-group.ts b/packages/medusa/src/services/customer-group.ts index 16d7ce45f6..671b9e48e8 100644 --- a/packages/medusa/src/services/customer-group.ts +++ b/packages/medusa/src/services/customer-group.ts @@ -4,7 +4,10 @@ import { DeepPartial, EntityManager } from "typeorm" import { CustomerGroup } from ".." import { CustomerGroupRepository } from "../repositories/customer-group" import { FindConfig } from "../types/common" -import { FilterableCustomerGroupProps } from "../types/customer-groups" +import { + CustomerGroupUpdate, + FilterableCustomerGroupProps, +} from "../types/customer-groups" type CustomerGroupConstructorProps = { manager: EntityManager @@ -42,14 +45,14 @@ class CustomerGroupService extends BaseService { } async retrieve(id: string, config = {}): Promise { - const customerRepo = this.manager_.getCustomRepository( + const cgRepo = this.manager_.getCustomRepository( this.customerGroupRepository_ ) const validatedId = this.validateId_(id) const query = this.buildQuery_({ id: validatedId }, config) - const customerGroup = await customerRepo.findOne(query) + const customerGroup = await cgRepo.findOne(query) if (!customerGroup) { throw new MedusaError( MedusaError.Types.NOT_FOUND, @@ -86,6 +89,38 @@ class CustomerGroupService extends BaseService { }) } + /** + * Update a customer group. + * + * @param {string} customerGroupId - id of the customer group + * @param {CustomerGroupUpdate} update - customer group partial data + */ + async update( + customerGroupId: string, + update: CustomerGroupUpdate + ): Promise { + return this.atomicPhase_(async (manager) => { + const { metadata, ...properties } = update + + const cgRepo: CustomerGroupRepository = manager.getCustomRepository( + this.customerGroupRepository_ + ) + + const customerGroup = await this.retrieve(customerGroupId) + + for (const key in properties) { + if (typeof properties[key] !== "undefined") { + customerGroup[key] = properties[key] + } + } + + if (typeof metadata !== "undefined") { + customerGroup.metadata = this.setMetadata_(customerGroup, metadata) + } + return await cgRepo.save(customerGroup) + }) + } + /** * Remove customer group * diff --git a/packages/medusa/src/types/customer-groups.ts b/packages/medusa/src/types/customer-groups.ts index f2829205c9..8812821c16 100644 --- a/packages/medusa/src/types/customer-groups.ts +++ b/packages/medusa/src/types/customer-groups.ts @@ -8,3 +8,8 @@ export class FilterableCustomerGroupProps { @IsType([String, [String], StringComparisonOperator]) id?: string | string[] | StringComparisonOperator } + +export class CustomerGroupUpdate { + name?: string + metadata?: object +}