From 3f7317028808cd3c1b44cb7b66694501a7c706c4 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:01:36 +0200 Subject: [PATCH] fix(medusa): Normalize discount code before querying DB (#2224) **What** Normalize discount code before querying DB Fixes CORE-567 --- .changeset/polite-flowers-fly.md | 5 + .../api/__tests__/admin/discount.js | 118 +++++++++++++++++- .../api/__tests__/admin/draft-order.js | 2 +- .../api/factories/simple-discount-factory.ts | 4 +- .../api/helpers/draft-order-seeder.js | 2 +- packages/medusa/src/models/discount.ts | 8 +- packages/medusa/src/services/discount.ts | 24 ++-- 7 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 .changeset/polite-flowers-fly.md diff --git a/.changeset/polite-flowers-fly.md b/.changeset/polite-flowers-fly.md new file mode 100644 index 0000000000..f4a0536ee9 --- /dev/null +++ b/.changeset/polite-flowers-fly.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +Normalize discount code before querying DB in `DiscountService.retrieveByCode` diff --git a/integration-tests/api/__tests__/admin/discount.js b/integration-tests/api/__tests__/admin/discount.js index 903df5ce96..1b7832ffe4 100644 --- a/integration-tests/api/__tests__/admin/discount.js +++ b/integration-tests/api/__tests__/admin/discount.js @@ -403,7 +403,7 @@ describe("/admin/discounts", () => { expect.objectContaining({ id: "dynamic-discount", code: "Dyn100", - }) + }), ]) ) }) @@ -2365,4 +2365,120 @@ describe("/admin/discounts", () => { } }) }) + + describe("GET /admin/discounts/code/:code", () => { + beforeEach(async () => { + const manager = dbConnection.manager + await adminSeeder(dbConnection) + + await manager.insert(DiscountRule, { + id: "test-discount-rule-fixed", + description: "Test discount rule", + type: "fixed", + value: 10, + allocation: "total", + }) + + await simpleDiscountFactory(dbConnection, { + id: "test-discount", + code: "TEST", + rule: { + type: "percentage", + value: "10", + allocation: "total", + }, + }) + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("should retrieve discount using uppercase code", async () => { + const api = useApi() + + const response = await api + .get("/admin/discounts/code/TEST", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + const disc = response.data.discount + expect(response.status).toEqual(200) + expect(disc).toEqual( + expect.objectContaining({ + id: "test-discount", + code: "TEST", + }) + ) + }) + + it("should retrieve discount using lowercase code", async () => { + const api = useApi() + + const response = await api + .get("/admin/discounts/code/test", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + const disc = response.data.discount + expect(response.status).toEqual(200) + expect(disc).toEqual( + expect.objectContaining({ + id: "test-discount", + code: "TEST", + }) + ) + }) + + it("should retrieve discount using mixed casing code", async () => { + const api = useApi() + + const response = await api + .get("/admin/discounts/code/TesT", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + const disc = response.data.discount + expect(response.status).toEqual(200) + expect(disc).toEqual( + expect.objectContaining({ + id: "test-discount", + code: "TEST", + }) + ) + }) + + it("should respond with 404 on non-existing code", async () => { + const api = useApi() + + try { + await api.get("/admin/discounts/code/non-existing", { + headers: { + Authorization: "Bearer test_token", + }, + }) + } catch (error) { + expect(error.response.status).toEqual(404) + expect(error.response.data.message).toBe( + "Discount with code non-existing was not found" + ) + } + }) + }) }) diff --git a/integration-tests/api/__tests__/admin/draft-order.js b/integration-tests/api/__tests__/admin/draft-order.js index be34f01200..a6a8089a2e 100644 --- a/integration-tests/api/__tests__/admin/draft-order.js +++ b/integration-tests/api/__tests__/admin/draft-order.js @@ -420,7 +420,7 @@ describe("/admin/draft-orders", () => { code: "TEST", }), expect.objectContaining({ - code: "free-shipping", + code: "FREE-SHIPPING", }), ]) ) diff --git a/integration-tests/api/factories/simple-discount-factory.ts b/integration-tests/api/factories/simple-discount-factory.ts index 24cf20ff33..f8e88eea43 100644 --- a/integration-tests/api/factories/simple-discount-factory.ts +++ b/integration-tests/api/factories/simple-discount-factory.ts @@ -2,13 +2,13 @@ import { AllocationType, Discount, DiscountRule, - DiscountRuleType, + DiscountRuleType } from "@medusajs/medusa" import faker from "faker" import { Connection } from "typeorm" import { DiscountConditionFactoryData, - simpleDiscountConditionFactory, + simpleDiscountConditionFactory } from "./simple-discount-condition-factory" export type DiscountRuleFactoryData = { diff --git a/integration-tests/api/helpers/draft-order-seeder.js b/integration-tests/api/helpers/draft-order-seeder.js index e865f51eb4..4f4c9dcc1e 100644 --- a/integration-tests/api/helpers/draft-order-seeder.js +++ b/integration-tests/api/helpers/draft-order-seeder.js @@ -137,7 +137,7 @@ module.exports = async (connection, data = {}) => { const freeShippingDiscount = manager.create(Discount, { id: "free-shipping-discount", - code: "free-shipping", + code: "FREE-SHIPPING", is_dynamic: false, is_disabled: false, rule_id: "free-shipping-rule", diff --git a/packages/medusa/src/models/discount.ts b/packages/medusa/src/models/discount.ts index 4cbeaec97c..ae0c7d8fab 100644 --- a/packages/medusa/src/models/discount.ts +++ b/packages/medusa/src/models/discount.ts @@ -6,14 +6,14 @@ import { JoinColumn, JoinTable, ManyToMany, - ManyToOne, + ManyToOne } from "typeorm" import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column" -import { DiscountRule } from "./discount-rule" -import { Region } from "./region" import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity" import { generateEntityId } from "../utils/generate-entity-id" +import { DiscountRule } from "./discount-rule" +import { Region } from "./region" @Entity() export class Discount extends SoftDeletableEntity { @@ -79,10 +79,10 @@ export class Discount extends SoftDeletableEntity { @BeforeInsert() private upperCaseCode(): void { + this.code = this.code.toUpperCase() if (this.id) return this.id = generateEntityId(this.id, "disc") - this.code = this.code.toUpperCase() } } diff --git a/packages/medusa/src/services/discount.ts b/packages/medusa/src/services/discount.ts index 3253572bbc..8535a36654 100644 --- a/packages/medusa/src/services/discount.ts +++ b/packages/medusa/src/services/discount.ts @@ -6,19 +6,21 @@ import { DeepPartial, EntityManager, ILike, - SelectQueryBuilder, + SelectQueryBuilder } from "typeorm" import { EventBusService, ProductService, RegionService, - TotalsService, + TotalsService } from "." +import { TransactionBaseService } from "../interfaces" +import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing" import { Cart, Discount, LineItem, Region } from "../models" import { AllocationType as DiscountAllocation, DiscountRule, - DiscountRuleType, + DiscountRuleType } from "../models/discount-rule" import { DiscountRepository } from "../repositories/discount" import { DiscountConditionRepository } from "../repositories/discount-condition" @@ -31,16 +33,14 @@ import { CreateDynamicDiscountInput, FilterableDiscountProps, UpdateDiscountInput, - UpdateDiscountRuleInput, + UpdateDiscountRuleInput } from "../types/discount" +import { buildQuery, setMetadata } from "../utils" import { isFuture, isPast } from "../utils/date-helpers" import { formatException } from "../utils/exception-formatter" -import DiscountConditionService from "./discount-condition" -import CustomerService from "./customer" -import { TransactionBaseService } from "../interfaces" -import { buildQuery, setMetadata } from "../utils" import { FlagRouter } from "../utils/flag-router" -import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing" +import CustomerService from "./customer" +import DiscountConditionService from "./discount-condition" /** * Provides layer to manipulate discounts. @@ -276,11 +276,13 @@ class DiscountService extends TransactionBaseService { const manager = this.manager_ const discountRepo = manager.getCustomRepository(this.discountRepository_) - let query = buildQuery({ code: discountCode, is_dynamic: false }, config) + const normalizedCode = discountCode.toUpperCase() + + let query = buildQuery({ code: normalizedCode, is_dynamic: false }, config) let discount = await discountRepo.findOne(query) if (!discount) { - query = buildQuery({ code: discountCode, is_dynamic: true }, config) + query = buildQuery({ code: normalizedCode, is_dynamic: true }, config) discount = await discountRepo.findOne(query) if (!discount) {