From a87e1cdf6558fd56bd91540853ca0bb715eda46e Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Tue, 17 May 2022 11:17:17 +0200 Subject: [PATCH] feat(medusa): Add endpoint for retrieving a DiscountCondition (#1525) --- .../admin/__snapshots__/discount.js.snap | 62 +++++++ .../api/__tests__/admin/discount.js | 155 +++++++++++++++++- integration-tests/api/package.json | 6 +- integration-tests/api/yarn.lock | 68 ++++---- .../src/resources/admin/discounts.ts | 21 +++ packages/medusa-react/mocks/handlers/admin.ts | 15 +- .../src/hooks/admin/discounts/queries.ts | 32 +++- .../hooks/admin/discounts/queries.test.ts | 28 +++- .../routes/admin/discounts/get-condition.ts | 90 ++++++++++ .../src/api/routes/admin/discounts/index.ts | 15 ++ 10 files changed, 450 insertions(+), 42 deletions(-) create mode 100644 packages/medusa/src/api/routes/admin/discounts/get-condition.ts diff --git a/integration-tests/api/__tests__/admin/__snapshots__/discount.js.snap b/integration-tests/api/__tests__/admin/__snapshots__/discount.js.snap index 3299580946..68602778e1 100644 --- a/integration-tests/api/__tests__/admin/__snapshots__/discount.js.snap +++ b/integration-tests/api/__tests__/admin/__snapshots__/discount.js.snap @@ -1,5 +1,67 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`/admin/discounts GET /admin/discounts/:id/conditions/:condition_id should get condition 1`] = ` +Object { + "created_at": Any, + "discount_rule": Object { + "allocation": "total", + "created_at": Any, + "deleted_at": null, + "description": null, + "id": Any, + "metadata": null, + "type": "percentage", + "updated_at": Any, + "value": 10, + }, + "discount_rule_id": Any, + "id": "test-condition", + "operator": "in", + "type": "products", + "updated_at": Any, +} +`; + +exports[`/admin/discounts GET /admin/discounts/:id/conditions/:condition_id should get condition with expand + fields 1`] = ` +Object { + "id": "test-condition", + "products": Array [ + Object { + "collection_id": null, + "created_at": Any, + "deleted_at": null, + "description": null, + "discountable": true, + "external_id": null, + "handle": null, + "height": null, + "hs_code": null, + "id": "test-product", + "is_giftcard": false, + "length": null, + "material": null, + "metadata": null, + "mid_code": null, + "origin_country": null, + "profile_id": Any, + "status": "draft", + "subtitle": null, + "thumbnail": null, + "title": "Practical Frozen Fish", + "type_id": Any, + "updated_at": Any, + "weight": null, + "width": null, + }, + ], + "type": "products", +} +`; + +exports[`/admin/discounts GET /admin/discounts/:id/conditions/:condition_id throws if condition does not belong to discount: DiscountCondition with id test-condition was not found for Discount test-discount-2 1`] = `"Request failed with status code 404"`; + +exports[`/admin/discounts GET /admin/discounts/:id/conditions/:condition_id throws if condition does not exist: DiscountCondition with id test-condition was not found 1`] = `"Request failed with status code 404"`; + exports[`/admin/discounts POST /admin/discounts fails if multiple types of resources are provided on update 1`] = ` Object { "message": "Only one of products, product_types is allowed, Only one of product_types, products is allowed", diff --git a/integration-tests/api/__tests__/admin/discount.js b/integration-tests/api/__tests__/admin/discount.js index 3957b122db..21e43264ab 100644 --- a/integration-tests/api/__tests__/admin/discount.js +++ b/integration-tests/api/__tests__/admin/discount.js @@ -431,6 +431,7 @@ describe("/admin/discounts", () => { await adminSeeder(dbConnection) await discountSeeder(dbConnection) } catch (err) { + console.log(err) throw err } }) @@ -1326,7 +1327,7 @@ describe("/admin/discounts", () => { expect.assertions(2) const api = useApi() - const response = await api + await api .post( "/admin/discounts", { @@ -2032,4 +2033,156 @@ describe("/admin/discounts", () => { } }) }) + + describe("GET /admin/discounts/:id/conditions/:condition_id", () => { + beforeEach(async () => { + try { + await adminSeeder(dbConnection) + } catch (err) { + console.log(err) + } + + const prod = await simpleProductFactory(dbConnection, { + type: "pants", + id: "test-product", + }) + + await simpleDiscountFactory(dbConnection, { + id: "test-discount", + code: "TEST", + rule: { + type: "percentage", + value: "10", + allocation: "total", + conditions: [ + { + id: "test-condition", + type: "products", + operator: "in", + products: [prod.id], + }, + ], + }, + }) + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("should get condition", async () => { + const api = useApi() + + const discountCondition = await api + .get("/admin/discounts/test-discount/conditions/test-condition", { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + const cond = discountCondition.data.discount_condition + + expect(discountCondition.status).toEqual(200) + expect(cond).toMatchSnapshot({ + id: "test-condition", + type: "products", + operator: "in", + created_at: expect.any(String), + updated_at: expect.any(String), + discount_rule_id: expect.any(String), + discount_rule: { + id: expect.any(String), + updated_at: expect.any(String), + created_at: expect.any(String), + }, + }) + }) + + it("should get condition with expand + fields", async () => { + const api = useApi() + + const discountCondition = await api + .get( + "/admin/discounts/test-discount/conditions/test-condition?expand=products&fields=id,type", + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err) + }) + + const cond = discountCondition.data.discount_condition + console.log(cond.products) + + expect(discountCondition.status).toEqual(200) + expect(cond).toMatchSnapshot({ + id: "test-condition", + type: "products", + products: [ + { + id: "test-product", + profile_id: expect.any(String), + type_id: expect.any(String), + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ], + }) + }) + + it("throws if condition does not exist", async () => { + const api = useApi() + + const prod2 = await simpleProductFactory(dbConnection, { type: "pants" }) + + try { + await api.post( + "/admin/discounts/test-discount/conditions/does-not-exist", + { + products: [prod2.id], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + } catch (error) { + expect(error.message).toMatchSnapshot( + "DiscountCondition with id test-condition was not found" + ) + } + }) + + it("throws if condition does not belong to discount", async () => { + const api = useApi() + + const prod2 = await simpleProductFactory(dbConnection, { type: "pants" }) + + try { + await api.post( + "/admin/discounts/test-discount-2/conditions/test-condition", + { + products: [prod2.id], + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + } catch (error) { + expect(error.message).toMatchSnapshot( + "DiscountCondition with id test-condition was not found for Discount test-discount-2" + ) + } + }) + }) }) diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index e0545c48ed..b33b3077b5 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -8,16 +8,16 @@ "build": "babel src -d dist --extensions \".ts,.js\"" }, "dependencies": { - "@medusajs/medusa": "1.3.0-dev-1652692202580", + "@medusajs/medusa": "1.3.0-dev-1652704115624", "faker": "^5.5.3", - "medusa-interfaces": "1.3.0-dev-1652692202580", + "medusa-interfaces": "1.3.0-dev-1652704115624", "typeorm": "^0.2.31" }, "devDependencies": { "@babel/cli": "^7.12.10", "@babel/core": "^7.12.10", "@babel/node": "^7.12.10", - "babel-preset-medusa-package": "1.1.19-dev-1652692202580", + "babel-preset-medusa-package": "1.1.19-dev-1652704115624", "jest": "^26.6.3" } } diff --git a/integration-tests/api/yarn.lock b/integration-tests/api/yarn.lock index 8d00653ffd..9222f91511 100644 --- a/integration-tests/api/yarn.lock +++ b/integration-tests/api/yarn.lock @@ -1327,10 +1327,10 @@ semver "^7.3.5" tar "^6.1.11" -"@medusajs/medusa-cli@1.3.0-dev-1652692202580": - version "1.3.0-dev-1652692202580" - resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.3.0-dev-1652692202580.tgz#a09041aec6e641cecf106ef7d30781a19c8b48b4" - integrity sha512-MtpUElK/kvnHy0nlEsk2Vh62s/FMnOVV2KJI96fe1kQsBnBgbjXJDbjWM8oq/P9ZDlftJNfDChYyD3XXmhQiIg== +"@medusajs/medusa-cli@1.3.0-dev-1652704115624": + version "1.3.0-dev-1652704115624" + resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.3.0-dev-1652704115624.tgz#9841fcc6123cd9c72d544d48316d66cf49ad8dc0" + integrity sha512-Lhk6pdvgv4UrLLUY/aYuty3TKfgDlvSUfmG4cTgZXbLehnRSbLelPcX+YKCtiM4d0NYfgF364H7p0NAMuCkBIg== dependencies: "@babel/polyfill" "^7.8.7" "@babel/runtime" "^7.9.6" @@ -1348,8 +1348,8 @@ is-valid-path "^0.1.1" joi-objectid "^3.0.1" meant "^1.0.1" - medusa-core-utils "1.1.31-dev-1652692202580" - medusa-telemetry "0.0.11-dev-1652692202580" + medusa-core-utils "1.1.31-dev-1652704115624" + medusa-telemetry "0.0.11-dev-1652704115624" netrc-parser "^3.1.6" open "^8.0.6" ora "^5.4.1" @@ -1363,13 +1363,13 @@ winston "^3.3.3" yargs "^15.3.1" -"@medusajs/medusa@1.3.0-dev-1652692202580": - version "1.3.0-dev-1652692202580" - resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.3.0-dev-1652692202580.tgz#6574e8b8afd63be81d4eb12e1e2a78af80808597" - integrity sha512-uAmGXmYCGBuesAPcBk6ev54PdGnUz9rqBK+5xM8K5Y5YiK4CDV0aZRN9/EHjNGMMYf24XPBSYS394wXULMZ0jw== +"@medusajs/medusa@1.3.0-dev-1652704115624": + version "1.3.0-dev-1652704115624" + resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.3.0-dev-1652704115624.tgz#040eede718d0eec6b01b4471e9af8495314c3ff0" + integrity sha512-x2Wg7lP5A25NMENqcZoC00O46Y3ojLaSnxa9L6E3u375nYWa99uxyT8ckXhklVqzhM3iBtReb/20H4r2+niwmw== dependencies: "@hapi/joi" "^16.1.8" - "@medusajs/medusa-cli" "1.3.0-dev-1652692202580" + "@medusajs/medusa-cli" "1.3.0-dev-1652704115624" "@types/lodash" "^4.14.168" awilix "^4.2.3" body-parser "^1.19.0" @@ -1392,8 +1392,8 @@ joi "^17.3.0" joi-objectid "^3.0.1" jsonwebtoken "^8.5.1" - medusa-core-utils "1.1.31-dev-1652692202580" - medusa-test-utils "1.1.37-dev-1652692202580" + medusa-core-utils "1.1.31-dev-1652704115624" + medusa-test-utils "1.1.37-dev-1652704115624" morgan "^1.9.1" multer "^1.4.2" passport "^0.4.0" @@ -2039,10 +2039,10 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-preset-medusa-package@1.1.19-dev-1652692202580: - version "1.1.19-dev-1652692202580" - resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1652692202580.tgz#318aa92054eb2856045f33f6870f7cb4b2dccdec" - integrity sha512-77FA4Gq4/5n9mHvaKK3flp7EcNm/J9DmeSY2zEbK6NYf7SNuUYHG+O5P07yb7RF/KMXaTxIgY/VyZywsvFJSNg== +babel-preset-medusa-package@1.1.19-dev-1652704115624: + version "1.1.19-dev-1652704115624" + resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1652704115624.tgz#fa584e39e7c0a1b808af25953f81653252225be7" + integrity sha512-Nv8Si592nO+UZmMQuOGCs1pFs5ShIsCKclbqBybahjGZIPPD6bVNaz9dujSaZnlxvA9a3gn4dvuh3H8IRitUug== dependencies: "@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-decorators" "^7.12.1" @@ -5168,23 +5168,23 @@ media-typer@0.3.0: resolved "http://localhost:4873/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -medusa-core-utils@1.1.31-dev-1652692202580: - version "1.1.31-dev-1652692202580" - resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1652692202580.tgz#b1fe3a2b8a696ddbccffd108b167db448b234360" - integrity sha512-cS3qcdpbwNFq4ci0/Pv7l2p80Q/ZMKJEiMg3CmtsvPwfvCPdt/bjdG8Nd93w3f/VNZ1oCROWzMRfx2l7yIDnGA== +medusa-core-utils@1.1.31-dev-1652704115624: + version "1.1.31-dev-1652704115624" + resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1652704115624.tgz#cda24aa1a292a1bd0a770774aae9a970fc7ab963" + integrity sha512-evkWva10x6JaHBex5S8Zi9Ae/ZStG858nb18w5ITxbCHWvGYq6/VyLpo4vB7I6wuU3z6kDTMd/yyvsVXv33O2g== dependencies: joi "^17.3.0" joi-objectid "^3.0.1" -medusa-interfaces@1.3.0-dev-1652692202580: - version "1.3.0-dev-1652692202580" - resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.3.0-dev-1652692202580.tgz#b217598f7e72abe149fea26d10fadbafeb07d07f" - integrity sha512-KEq5UPvhimGrKwn6H9W6FpvGYprh8evPLOCzDBOnjMqNpQExbidpP++sprBmxpLdjE61KAqMjP4fJZsXSYgY0A== +medusa-interfaces@1.3.0-dev-1652704115624: + version "1.3.0-dev-1652704115624" + resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.3.0-dev-1652704115624.tgz#02dbbda562f99e7de96e7e6657a6af2a20855e36" + integrity sha512-aSm6gYWF0gPHoIswHllB/YrGwkrYRr4ZrBmWh3QSulDYnjmtyBFHKkatrC1pscAEFG/5h4d1LEENUHYrBQ4tQg== -medusa-telemetry@0.0.11-dev-1652692202580: - version "0.0.11-dev-1652692202580" - resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1652692202580.tgz#a4598130cfb8878e4bad35d839169cdc6dab3d5a" - integrity sha512-D1SiNTd4gLX87cHnOQUt/C/+vX4hPd7nwYAll/pCelyt5/oPX9JoD4kjhEXIP052J17zXWGX0averCR1KkE6/A== +medusa-telemetry@0.0.11-dev-1652704115624: + version "0.0.11-dev-1652704115624" + resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1652704115624.tgz#4de0f4a66a9f8bb4f61d8b7c24aa9cbd719039a7" + integrity sha512-sfCzUM4mlXpBJVaqAycKAmeEWalzyg18+gUo+As/mxoEUIUR5XtpZ5Bdx484/2GUvRP8OdOYC1uPpbuTZbLXcw== dependencies: axios "^0.21.1" axios-retry "^3.1.9" @@ -5196,13 +5196,13 @@ medusa-telemetry@0.0.11-dev-1652692202580: remove-trailing-slash "^0.1.1" uuid "^8.3.2" -medusa-test-utils@1.1.37-dev-1652692202580: - version "1.1.37-dev-1652692202580" - resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.37-dev-1652692202580.tgz#7b0284eae047678013236f58eef78b1cae945b41" - integrity sha512-bdbt/itF78BCzPW73mZJigQeImbwMcCHx0Ix02gGmNc66LAKUsBWRKWLRpWJo+dvGyrN5RYqFiysH8PmuOHuqA== +medusa-test-utils@1.1.37-dev-1652704115624: + version "1.1.37-dev-1652704115624" + resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.37-dev-1652704115624.tgz#7118753a4afd1c6ed6f1d1ceb75fdf9bfb65afa3" + integrity sha512-OnQEA/1jj4jnQGukMtbcqg2HWlKve0BqzeS8T5O2VDL5zldFG9SXFF3LupC51nh96u97dcKZk5A6JL8oHwonog== dependencies: "@babel/plugin-transform-classes" "^7.9.5" - medusa-core-utils "1.1.31-dev-1652692202580" + medusa-core-utils "1.1.31-dev-1652704115624" randomatic "^3.1.1" merge-descriptors@1.0.1: diff --git a/packages/medusa-js/src/resources/admin/discounts.ts b/packages/medusa-js/src/resources/admin/discounts.ts index 95cba9fa89..da601f8004 100644 --- a/packages/medusa-js/src/resources/admin/discounts.ts +++ b/packages/medusa-js/src/resources/admin/discounts.ts @@ -1,7 +1,9 @@ import { + AdminDiscountConditionsRes, AdminDiscountsDeleteRes, AdminDiscountsListRes, AdminDiscountsRes, + AdminGetDiscountsDiscountConditionsConditionParams, AdminGetDiscountsParams, AdminPostDiscountsDiscountConditions, AdminPostDiscountsDiscountConditionsCondition, @@ -187,6 +189,25 @@ class AdminDiscountsResource extends BaseResource { const path = `/admin/discounts/${discountId}/conditions/${conditionId}` return this.client.request("DELETE", path, {}, {}, customHeaders) } + + /** + * @description Gets a condition from a discount + */ + getCondition( + discountId: string, + conditionId: string, + query?: AdminGetDiscountsDiscountConditionsConditionParams, + customHeaders: Record = {} + ): ResponsePromise { + let path = `/admin/discounts/${discountId}/conditions/${conditionId}` + + if (query) { + const queryString = qs.stringify(query) + path = `/admin/discounts/${discountId}/conditions/${conditionId}?${queryString}` + } + + return this.client.request("GET", path, {}, {}, customHeaders) + } } export default AdminDiscountsResource diff --git a/packages/medusa-react/mocks/handlers/admin.ts b/packages/medusa-react/mocks/handlers/admin.ts index 3a3238eeff..3e87c2dd69 100644 --- a/packages/medusa-react/mocks/handlers/admin.ts +++ b/packages/medusa-react/mocks/handlers/admin.ts @@ -1,5 +1,5 @@ -import { fixtures } from "../data" import { rest } from "msw" +import { fixtures } from "../data" export const adminHandlers = [ rest.post("/admin/collections/", (req, res, ctx) => { @@ -784,6 +784,19 @@ export const adminHandlers = [ ) }), + rest.get("/admin/discounts/:id/conditions/:conditionId", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + discount_condition: { + ...fixtures + .get("discount") + .rule.conditions.find(c => c.id === req.params.conditionId), + }, + }) + ) + }), + rest.delete( "/admin/discounts/:id/conditions/:conditionId", (req, res, ctx) => { diff --git a/packages/medusa-react/src/hooks/admin/discounts/queries.ts b/packages/medusa-react/src/hooks/admin/discounts/queries.ts index 975d39442f..9b989493a5 100644 --- a/packages/medusa-react/src/hooks/admin/discounts/queries.ts +++ b/packages/medusa-react/src/hooks/admin/discounts/queries.ts @@ -1,7 +1,9 @@ import { + AdminDiscountConditionsRes, AdminDiscountsListRes, AdminDiscountsRes, AdminGetDiscountParams, + AdminGetDiscountsDiscountConditionsConditionParams, AdminGetDiscountsParams, } from "@medusajs/medusa" import { Response } from "@medusajs/medusa-js" @@ -12,7 +14,16 @@ import { queryKeysFactory } from "../../utils/index" const ADMIN_DISCOUNTS_QUERY_KEY = `admin_discounts` as const -export const adminDiscountKeys = queryKeysFactory(ADMIN_DISCOUNTS_QUERY_KEY) +export const adminDiscountKeys = { + ...queryKeysFactory(ADMIN_DISCOUNTS_QUERY_KEY), + detailCondition(id: string, query?: any) { + return [ + ...this.detail(id), + "condition" as const, + { ...(query || {}) }, + ] as const + }, +} type DiscountQueryKeys = typeof adminDiscountKeys @@ -67,3 +78,22 @@ export const useAdminGetDiscountByCode = ( ) return { ...data, ...rest } as const } + +export const useAdminGetDiscountCondition = ( + id: string, + conditionId: string, + query?: AdminGetDiscountsDiscountConditionsConditionParams, + options?: UseQueryOptionsWrapper< + Response, + Error, + ReturnType + > +) => { + const { client } = useMedusa() + const { data, ...rest } = useQuery( + adminDiscountKeys.detailCondition(conditionId), + () => client.admin.discounts.getCondition(id, conditionId, query), + options + ) + return { ...data, ...rest } as const +} diff --git a/packages/medusa-react/test/hooks/admin/discounts/queries.test.ts b/packages/medusa-react/test/hooks/admin/discounts/queries.test.ts index 3ecfc6cc8a..a59b89605e 100644 --- a/packages/medusa-react/test/hooks/admin/discounts/queries.test.ts +++ b/packages/medusa-react/test/hooks/admin/discounts/queries.test.ts @@ -1,10 +1,11 @@ +import { renderHook } from "@testing-library/react-hooks" +import { fixtures } from "../../../../mocks/data" import { useAdminDiscount, useAdminDiscounts, useAdminGetDiscountByCode, + useAdminGetDiscountCondition, } from "../../../../src" -import { renderHook } from "@testing-library/react-hooks" -import { fixtures } from "../../../../mocks/data" import { createWrapper } from "../../../utils" describe("useAdminDiscounts hook", () => { @@ -54,3 +55,26 @@ describe("useAdminDiscount hook", () => { expect(result.current.discount).toEqual(discount) }) }) + +describe("useAdminGetDiscountCondition hook", () => { + test("returns a discount condition", async () => { + const discount = fixtures.get("discount") + const { result, waitFor } = renderHook( + () => + useAdminGetDiscountCondition( + discount.id, + discount.rule.conditions[0].id + ), + { + wrapper: createWrapper(), + } + ) + + await waitFor(() => result.current.isSuccess) + + expect(result.current.response.status).toEqual(200) + expect(result.current.discount_condition).toEqual( + discount.rule.conditions[0] + ) + }) +}) diff --git a/packages/medusa/src/api/routes/admin/discounts/get-condition.ts b/packages/medusa/src/api/routes/admin/discounts/get-condition.ts new file mode 100644 index 0000000000..3946da6abb --- /dev/null +++ b/packages/medusa/src/api/routes/admin/discounts/get-condition.ts @@ -0,0 +1,90 @@ +import { IsOptional, IsString } from "class-validator" +import { MedusaError } from "medusa-core-utils" +import { + defaultAdminDiscountConditionFields, + defaultAdminDiscountConditionRelations, +} from "." +import { DiscountCondition } from "../../../../models" +import { DiscountService } from "../../../../services" +import DiscountConditionService from "../../../../services/discount-condition" +import { getRetrieveConfig } from "../../../../utils/get-query-config" +import { validator } from "../../../../utils/validator" +/** + * @oas [get] /discounts/{discount_id}/conditions/{condition_id} + * operationId: "GetDiscountsDiscountConditionsCondition" + * summary: "Gets a DiscountCondition" + * x-authenticated: true + * parameters: + * - (path) discount_id=* {string} The id of the Discount. + * - (path) condition_id=* {string} The id of the DiscountCondition. + * query: + * - (query) expand {string} Comma separated list of relations to include in the results. + * - (query) fields {string} Comma separated list of fields to include in the results. + * description: "Gets a DiscountCondition" + + * tags: + * - DiscountCondition + * responses: + * 200: + * description: OK + * content: + * application/json: + * schema: + * properties: + * discount_condition: + * $ref: "#/components/schemas/discount_condition" + */ + +export default async (req, res) => { + const { discount_id, condition_id } = req.params + + const validatedParams = await validator( + AdminGetDiscountsDiscountConditionsConditionParams, + req.query + ) + + const discountService: DiscountService = req.scope.resolve("discountService") + + const discount = await discountService.retrieve(discount_id, { + relations: ["rule", "rule.conditions"], + }) + + const existsOnDiscount = discount.rule.conditions.some( + (c) => c.id === condition_id + ) + + if (!existsOnDiscount) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Condition with id ${condition_id} does not belong to Discount with id ${discount_id}` + ) + } + + const config = getRetrieveConfig( + defaultAdminDiscountConditionFields, + defaultAdminDiscountConditionRelations, + validatedParams?.fields?.split(",") as (keyof DiscountCondition)[], + validatedParams?.expand?.split(",") + ) + + const conditionService: DiscountConditionService = req.scope.resolve( + "discountConditionService" + ) + + const discountCondition = await conditionService.retrieve( + condition_id, + config + ) + + res.status(200).json({ discount_condition: discountCondition }) +} + +export class AdminGetDiscountsDiscountConditionsConditionParams { + @IsString() + @IsOptional() + expand?: string + + @IsString() + @IsOptional() + fields?: string +} diff --git a/packages/medusa/src/api/routes/admin/discounts/index.ts b/packages/medusa/src/api/routes/admin/discounts/index.ts index a6f3ea5a7e..8f93dd555d 100644 --- a/packages/medusa/src/api/routes/admin/discounts/index.ts +++ b/packages/medusa/src/api/routes/admin/discounts/index.ts @@ -1,6 +1,7 @@ import { Router } from "express" import "reflect-metadata" import { Discount } from "../../../.." +import { DiscountCondition } from "../../../../models" import { DeleteResponse, PaginatedResponse } from "../../../../types/common" import middlewares from "../../../middlewares" @@ -50,6 +51,10 @@ export default (app) => { ) // Discount condition management + route.get( + "/:discount_id/conditions/:condition_id", + middlewares.wrap(require("./get-condition").default) + ) route.post( "/:discount_id/conditions/:condition_id", middlewares.wrap(require("./update-condition").default) @@ -91,10 +96,19 @@ export const defaultAdminDiscountsRelations = [ "rule.conditions", ] +export const defaultAdminDiscountConditionFields: (keyof DiscountCondition)[] = + ["id", "type", "operator", "discount_rule_id", "created_at", "updated_at"] + +export const defaultAdminDiscountConditionRelations = ["discount_rule"] + export type AdminDiscountsRes = { discount: Discount } +export type AdminDiscountConditionsRes = { + discount_condition: DiscountCondition +} + export type AdminDiscountsDeleteRes = DeleteResponse export type AdminDiscountsListRes = PaginatedResponse & { @@ -108,6 +122,7 @@ export * from "./create-dynamic-code" export * from "./delete-condition" export * from "./delete-discount" export * from "./delete-dynamic-code" +export * from "./get-condition" export * from "./get-discount" export * from "./get-discount-by-code" export * from "./list-discounts"