From a729fb3fbb1164191037356bceeabcabbebb72ff Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 11 Sep 2024 15:08:37 +0200 Subject: [PATCH] feat(utils,types,framework,medusa): store endpoints should require publishable key (#9068) * feat(utils,types,framework,medusa): store endpoints should require publishable key * chore: fix specs * chore: fix more specs * chore: update js-sdk * chore: fix specs wrt to default SC * chore: revert custom headers + change error message * chore: fix specs * chore: fix new store specs --- .../helpers/create-admin-user.ts | 39 +- .../collection/store/collection.spec.ts | 12 +- .../__tests__/customer/admin/customer.spec.ts | 7 + .../__tests__/customer/store/customer.spec.ts | 73 ++- .../http/__tests__/fixtures/order.ts | 61 +- .../__tests__/order/admin/rma-flows.spec.ts | 2 +- .../admin/payment-sessions.spec.ts | 34 +- .../__tests__/payment/admin/payment.spec.ts | 2 +- .../__tests__/product/store/product.spec.ts | 277 +++++--- .../__tests__/region/admin/region.spec.ts | 11 +- .../cart/store/add-promotions-to-cart.spec.ts | 21 +- .../__tests__/cart/store/carts.spec.ts | 595 +++++++++++------- .../store/remove-promotions-from-cart.spec.ts | 7 + .../__tests__/currency/store/currency.spec.ts | 12 +- .../store/create-customer-addresses.ts | 17 +- .../customer/store/create-customer.spec.ts | 12 +- .../store/delete-customer-address.spec.ts | 23 +- .../__tests__/customer/store/get-me.spec.ts | 16 +- .../customer/store/list-customer-addresses.ts | 13 +- .../store/update-customer-address.spec.ts | 27 +- .../price-lists/store/get-product.ts | 29 +- .../store/shipping-options.spec.ts | 40 +- .../core/types/src/api-key/common/api-key.ts | 4 +- .../core/utils/src/api-key/api-key-type.ts | 2 + .../framework/framework/src/http/types.ts | 13 +- packages/medusa/src/api/middlewares.ts | 2 + packages/medusa/src/api/store/middlewares.ts | 10 + .../filter-by-valid-sales-channels.ts | 72 +-- .../middlewares/ensure-publishable-api-key.ts | 68 ++ 29 files changed, 1037 insertions(+), 464 deletions(-) create mode 100644 packages/medusa/src/api/store/middlewares.ts create mode 100644 packages/medusa/src/utils/middlewares/ensure-publishable-api-key.ts diff --git a/integration-tests/helpers/create-admin-user.ts b/integration-tests/helpers/create-admin-user.ts index 36810c3b9b..fedd7a9238 100644 --- a/integration-tests/helpers/create-admin-user.ts +++ b/integration-tests/helpers/create-admin-user.ts @@ -1,5 +1,15 @@ -import { IAuthModuleService, IUserModuleService } from "@medusajs/types" -import { ModuleRegistrationName } from "@medusajs/utils" +import { + ApiKeyDTO, + IApiKeyModuleService, + IAuthModuleService, + IUserModuleService, + MedusaContainer, +} from "@medusajs/types" +import { + ApiKeyType, + ModuleRegistrationName, + PUBLISHABLE_KEY_HEADER, +} from "@medusajs/utils" import jwt from "jsonwebtoken" import Scrypt from "scrypt-kdf" import { getContainer } from "../environment-helpers/use-container" @@ -61,3 +71,28 @@ export const createAdminUser = async ( return { user, authIdentity } } + +export const generatePublishableKey = async (container?: MedusaContainer) => { + const appContainer = container ?? getContainer()! + const apiKeyModule = appContainer.resolve( + ModuleRegistrationName.API_KEY + ) + + return await apiKeyModule.createApiKeys({ + title: "test publishable key", + type: ApiKeyType.PUBLISHABLE, + created_by: "test", + }) +} + +export const generateStoreHeaders = ({ + publishableKey, +}: { + publishableKey: ApiKeyDTO +}) => { + return { + headers: { + [PUBLISHABLE_KEY_HEADER]: publishableKey.token, + }, + } +} diff --git a/integration-tests/http/__tests__/collection/store/collection.spec.ts b/integration-tests/http/__tests__/collection/store/collection.spec.ts index 8431d4b551..eb77f5d6ef 100644 --- a/integration-tests/http/__tests__/collection/store/collection.spec.ts +++ b/integration-tests/http/__tests__/collection/store/collection.spec.ts @@ -1,7 +1,9 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { - createAdminUser, adminHeaders, + createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(30000) @@ -12,9 +14,12 @@ medusaIntegrationTestRunner({ let baseCollection let baseCollection1 let baseCollection2 + let storeHeaders beforeEach(async () => { const container = getContainer() + const publishableKey = await generatePublishableKey(container) + storeHeaders = generateStoreHeaders({ publishableKey }) await createAdminUser(dbConnection, adminHeaders, container) baseCollection = ( @@ -46,7 +51,8 @@ medusaIntegrationTestRunner({ describe("/store/collections/:id", () => { it("gets collection", async () => { const response = await api.get( - `/store/collections/${baseCollection.id}` + `/store/collections/${baseCollection.id}`, + storeHeaders ) expect(response.data.collection).toEqual( @@ -61,7 +67,7 @@ medusaIntegrationTestRunner({ describe("/store/collections", () => { it("lists collections", async () => { - const response = await api.get("/store/collections") + const response = await api.get("/store/collections", storeHeaders) expect(response.data).toEqual({ collections: [ diff --git a/integration-tests/http/__tests__/customer/admin/customer.spec.ts b/integration-tests/http/__tests__/customer/admin/customer.spec.ts index 8fb02867f4..9b25428422 100644 --- a/integration-tests/http/__tests__/customer/admin/customer.spec.ts +++ b/integration-tests/http/__tests__/customer/admin/customer.spec.ts @@ -5,6 +5,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(30000) @@ -17,9 +19,13 @@ medusaIntegrationTestRunner({ let customer4 let customer5 let container + let storeHeaders + beforeEach(async () => { container = getContainer() await createAdminUser(dbConnection, adminHeaders, container) + const publishableKey = await generatePublishableKey(container) + storeHeaders = generateStoreHeaders({ publishableKey }) customer1 = ( await api.post( @@ -415,6 +421,7 @@ medusaIntegrationTestRunner({ { headers: { Authorization: `Bearer ${registeredCustomerToken}`, + ...storeHeaders.headers, }, } ) diff --git a/integration-tests/http/__tests__/customer/store/customer.spec.ts b/integration-tests/http/__tests__/customer/store/customer.spec.ts index cbc864ed70..b464f47cf2 100644 --- a/integration-tests/http/__tests__/customer/store/customer.spec.ts +++ b/integration-tests/http/__tests__/customer/store/customer.spec.ts @@ -3,6 +3,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(30000) @@ -10,20 +12,27 @@ jest.setTimeout(30000) medusaIntegrationTestRunner({ testSuite: ({ dbConnection, api, getContainer }) => { let appContainer: MedusaContainer + let storeHeaders beforeEach(async () => { appContainer = getContainer() + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) await createAdminUser(dbConnection, adminHeaders, appContainer) }) - describe("POST /admin/customers", () => { + describe("POST /store/customers", () => { it("should fails to create a customer without an identity", async () => { const customer = await api - .post("/store/customers", { - email: "newcustomer@medusa.js", - first_name: "John", - last_name: "Doe", - }) + .post( + "/store/customers", + { + email: "newcustomer@medusa.js", + first_name: "John", + last_name: "Doe", + }, + storeHeaders + ) .catch((e) => e) expect(customer.response.status).toEqual(401) @@ -48,6 +57,7 @@ medusaIntegrationTestRunner({ { headers: { authorization: `Bearer ${signup.data.token}`, + ...storeHeaders.headers, }, } ) @@ -102,6 +112,7 @@ medusaIntegrationTestRunner({ { headers: { authorization: `Bearer ${signup.data.token}`, + ...storeHeaders.headers, }, } ) @@ -161,6 +172,7 @@ medusaIntegrationTestRunner({ { headers: { authorization: `Bearer ${firstSignup.data.token}`, + ...storeHeaders.headers, }, } ) @@ -181,6 +193,7 @@ medusaIntegrationTestRunner({ { headers: { authorization: `Bearer ${firstSignin.data.token}`, + ...storeHeaders.headers, }, } ) @@ -191,6 +204,54 @@ medusaIntegrationTestRunner({ "Request already authenticated as a customer." ) }) + + describe("With ensurePublishableApiKey middleware", () => { + it("should fail when no publishable key is passed in the header", async () => { + const { response } = await api + .post( + "/store/customers", + { + email: "newcustomer@medusa.js", + first_name: "John", + last_name: "Doe", + }, + { + headers: {}, + } + ) + .catch((e) => e) + + expect(response.data).toEqual({ + message: + "Publishable API key required in the request header: x-publishable-api-key. You can manage your keys in settings in the dashboard.", + type: "not_allowed", + }) + }) + + it("should fail when publishable keys are invalid", async () => { + const { response } = await api + .post( + "/store/customers", + { + email: "newcustomer@medusa.js", + first_name: "John", + last_name: "Doe", + }, + { + headers: { + "x-publishable-api-key": ["test1", "test2"], + }, + } + ) + .catch((e) => e) + + expect(response.data).toEqual({ + message: + "A valid publishable key is required to proceed with the request", + type: "not_allowed", + }) + }) + }) }) }, }) diff --git a/integration-tests/http/__tests__/fixtures/order.ts b/integration-tests/http/__tests__/fixtures/order.ts index cc67541fba..a57e454030 100644 --- a/integration-tests/http/__tests__/fixtures/order.ts +++ b/integration-tests/http/__tests__/fixtures/order.ts @@ -1,6 +1,13 @@ -import { adminHeaders } from "../../../helpers/create-admin-user" +import { + adminHeaders, + generatePublishableKey, + generateStoreHeaders, +} from "../../../helpers/create-admin-user" + +export async function createOrderSeeder({ api, container }) { + const publishableKey = await generatePublishableKey(container) + const storeHeaders = generateStoreHeaders({ publishableKey }) -export async function createOrderSeeder({ api }) { const region = ( await api.post( "/admin/regions", @@ -145,36 +152,46 @@ export async function createOrderSeeder({ api }) { ).data.shipping_option const cart = ( - await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - region_id: region.id, - shipping_address: { - address_1: "test address 1", - address_2: "test address 2", - city: "ny", - country_code: "us", - province: "ny", - postal_code: "94016", + await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + region_id: region.id, + shipping_address: { + address_1: "test address 1", + address_2: "test address 2", + city: "ny", + country_code: "us", + province: "ny", + postal_code: "94016", + }, + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], }, - sales_channel_id: salesChannel.id, - items: [{ quantity: 1, variant_id: product.variants[0].id }], - }) + storeHeaders + ) ).data.cart const paymentCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) ).data.payment_collection await api.post( `/store/payment-collections/${paymentCollection.id}/payment-sessions`, - { provider_id: "pp_system_default" } + { provider_id: "pp_system_default" }, + storeHeaders ) - let order = (await api.post(`/store/carts/${cart.id}/complete`, {})).data - .order + let order = ( + await api.post(`/store/carts/${cart.id}/complete`, {}, storeHeaders) + ).data.order order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data.order diff --git a/integration-tests/http/__tests__/order/admin/rma-flows.spec.ts b/integration-tests/http/__tests__/order/admin/rma-flows.spec.ts index c79c5fe1fb..5739da112f 100644 --- a/integration-tests/http/__tests__/order/admin/rma-flows.spec.ts +++ b/integration-tests/http/__tests__/order/admin/rma-flows.spec.ts @@ -28,7 +28,7 @@ medusaIntegrationTestRunner({ await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX)) await createAdminUser(dbConnection, adminHeaders, container) - order = await createOrderSeeder({ api }) + order = await createOrderSeeder({ api, container }) shippingProfile = ( await api.post( diff --git a/integration-tests/http/__tests__/payment-collection/admin/payment-sessions.spec.ts b/integration-tests/http/__tests__/payment-collection/admin/payment-sessions.spec.ts index 8d154a97d1..c243dd777b 100644 --- a/integration-tests/http/__tests__/payment-collection/admin/payment-sessions.spec.ts +++ b/integration-tests/http/__tests__/payment-collection/admin/payment-sessions.spec.ts @@ -2,6 +2,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" import { getProductFixture } from "../../../../helpers/fixtures" @@ -9,10 +11,12 @@ jest.setTimeout(30000) medusaIntegrationTestRunner({ testSuite: ({ dbConnection, getContainer, api }) => { - beforeAll(() => {}) + let storeHeaders beforeEach(async () => { const container = getContainer() + const publishableKey = await generatePublishableKey(container) + storeHeaders = generateStoreHeaders({ publishableKey }) await createAdminUser(dbConnection, adminHeaders, container) }) @@ -55,23 +59,32 @@ medusaIntegrationTestRunner({ ).data.product cart = ( - await api.post("/store/carts", { - region_id: region.id, - items: [{ variant_id: product.variants[0].id, quantity: 1 }], - }) + await api.post( + "/store/carts", + { + region_id: region.id, + items: [{ variant_id: product.variants[0].id, quantity: 1 }], + }, + storeHeaders + ) ).data.cart }) it("should create a payment session", async () => { const paymentCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) ).data.payment_collection await api.post( `/store/payment-collections/${paymentCollection.id}/payment-sessions`, - { provider_id: "pp_system_default" } + { provider_id: "pp_system_default" }, + storeHeaders ) // Adding a second payment session to ensure only one session gets created @@ -79,7 +92,8 @@ medusaIntegrationTestRunner({ data: { payment_collection }, } = await api.post( `/store/payment-collections/${paymentCollection.id}/payment-sessions`, - { provider_id: "pp_system_default" } + { provider_id: "pp_system_default" }, + storeHeaders ) expect(payment_collection.payment_sessions).toEqual([ diff --git a/integration-tests/http/__tests__/payment/admin/payment.spec.ts b/integration-tests/http/__tests__/payment/admin/payment.spec.ts index a40904c4c8..1b3c93702b 100644 --- a/integration-tests/http/__tests__/payment/admin/payment.spec.ts +++ b/integration-tests/http/__tests__/payment/admin/payment.spec.ts @@ -37,7 +37,7 @@ medusaIntegrationTestRunner({ beforeEach(async () => { container = getContainer() await createAdminUser(dbConnection, adminHeaders, container) - order = await createOrderSeeder({ api }) + order = await createOrderSeeder({ api, container }) await api.post( `/admin/orders/${order.id}/fulfillments`, diff --git a/integration-tests/http/__tests__/product/store/product.spec.ts b/integration-tests/http/__tests__/product/store/product.spec.ts index 42ee397dd3..9364062c8d 100644 --- a/integration-tests/http/__tests__/product/store/product.spec.ts +++ b/integration-tests/http/__tests__/product/store/product.spec.ts @@ -8,6 +8,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" import { getProductFixture } from "../../../../helpers/fixtures" @@ -30,6 +32,8 @@ medusaIntegrationTestRunner({ let variant4 let inventoryItem1 let inventoryItem2 + let storeHeaders + let publishableKey const createProducts = async (data) => { const response = await api.post( @@ -82,6 +86,8 @@ medusaIntegrationTestRunner({ beforeEach(async () => { appContainer = getContainer() + publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) await createAdminUser(dbConnection, adminHeaders, appContainer) const storeModule: IStoreModuleService = appContainer.resolve( @@ -104,7 +110,6 @@ medusaIntegrationTestRunner({ }) describe("Get products based on publishable key", () => { - let pubKey1 let salesChannel1 let salesChannel2 @@ -133,14 +138,6 @@ medusaIntegrationTestRunner({ ) ).data.product - pubKey1 = ( - await api.post( - "/admin/api-keys", - { title: "sample key", type: "publishable" }, - adminHeaders - ) - ).data.api_key - salesChannel1 = ( await api.post( "/admin/sales-channels", @@ -184,7 +181,7 @@ medusaIntegrationTestRunner({ it("returns products from a specific channel associated with a publishable key", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id], }, @@ -194,7 +191,7 @@ medusaIntegrationTestRunner({ const response = await api.get(`/store/products`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) @@ -210,7 +207,7 @@ medusaIntegrationTestRunner({ it("returns products from multiples sales channels associated with a publishable key", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id, salesChannel2.id], }, @@ -220,7 +217,7 @@ medusaIntegrationTestRunner({ const response = await api.get(`/store/products`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) @@ -239,7 +236,7 @@ medusaIntegrationTestRunner({ it("SC param overrides PK channels (but SK still needs to be in the PK's scope", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id, salesChannel2.id], }, @@ -251,7 +248,7 @@ medusaIntegrationTestRunner({ { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, } ) @@ -266,41 +263,12 @@ medusaIntegrationTestRunner({ ) }) - it("returns default product from default sales channel if PK is not passed", async () => { - await api.post( - `/admin/stores/${store.id}`, - { default_sales_channel_id: salesChannel2.id }, - adminHeaders - ) - - await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, - { - add: [salesChannel1.id, salesChannel2.id], - }, - adminHeaders - ) - - const response = await api.get(`/store/products`, { - adminHeaders, - }) - - expect(response.data.products.length).toBe(1) - expect(response.data.products).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: product2.id, - }), - ]) - ) - }) - // TODO: Decide if this is the behavior we want to keep in v2, as it seems a bit strange it.skip("returns all products if passed PK doesn't have associated channels", async () => { const response = await api.get(`/store/products`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) @@ -322,7 +290,7 @@ medusaIntegrationTestRunner({ it("throws because sales channel param is not in the scope of passed PK", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id], }, @@ -333,20 +301,20 @@ medusaIntegrationTestRunner({ .get(`/store/products?sales_channel_id[]=${salesChannel2.id}`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) .catch((e) => e) expect(err.response.status).toEqual(400) expect(err.response.data.message).toEqual( - `Requested sales channel is not part of the publishable key mappings` + `Requested sales channel is not part of the publishable key` ) }) it("retrieve a product from a specific channel associated with a publishable key", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id], }, @@ -356,7 +324,7 @@ medusaIntegrationTestRunner({ const response = await api.get(`/store/products/${product1.id}`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) @@ -370,7 +338,7 @@ medusaIntegrationTestRunner({ // BREAKING: If product not in sales channel we used to return 400, we return 404 instead. it("return 404 because requested product is not in the SC associated with a publishable key", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id], }, @@ -381,7 +349,7 @@ medusaIntegrationTestRunner({ .get(`/store/products/${product2.id}`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) .catch((e) => e) @@ -392,7 +360,7 @@ medusaIntegrationTestRunner({ // TODO: Add variant endpoints to the store API (if that is what we want) it.skip("should return 404 when the requested variant doesn't exist", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id], }, @@ -403,7 +371,7 @@ medusaIntegrationTestRunner({ .get(`/store/variants/does-not-exist`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) .catch((err) => { @@ -418,7 +386,7 @@ medusaIntegrationTestRunner({ it("should return 404 when the requested product doesn't exist", async () => { await api.post( - `/admin/api-keys/${pubKey1.id}/sales-channels`, + `/admin/api-keys/${publishableKey.id}/sales-channels`, { add: [salesChannel1.id], }, @@ -429,7 +397,7 @@ medusaIntegrationTestRunner({ .get(`/store/products/does-not-exist`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) .catch((err) => { @@ -448,7 +416,7 @@ medusaIntegrationTestRunner({ .get(`/store/products/${product1.id}`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) .catch((err) => { @@ -461,7 +429,7 @@ medusaIntegrationTestRunner({ .get(`/store/products/${product2.id}`, { headers: { ...adminHeaders.headers, - "x-publishable-api-key": pubKey1.token, + "x-publishable-api-key": publishableKey.token, }, }) .catch((err) => { @@ -566,6 +534,12 @@ medusaIntegrationTestRunner({ [product.id, product2.id, product3.id, product4.id] ) + await api.post( + `/admin/api-keys/${publishableKey.id}/sales-channels`, + { add: [defaultSalesChannel.id] }, + adminHeaders + ) + const service = appContainer.resolve(ModuleRegistrationName.STORE) const [store] = await service.listStores() @@ -583,7 +557,7 @@ medusaIntegrationTestRunner({ }) it("should list all published products", async () => { - let response = await api.get(`/store/products`) + let response = await api.get(`/store/products`, storeHeaders) expect(response.status).toEqual(200) expect(response.data.count).toEqual(3) @@ -601,7 +575,7 @@ medusaIntegrationTestRunner({ ]) ) - response = await api.get(`/store/products?q=uniquely`) + response = await api.get(`/store/products?q=uniquely`, storeHeaders) expect(response.status).toEqual(200) expect(response.data.count).toEqual(1) @@ -618,8 +592,15 @@ medusaIntegrationTestRunner({ [product.id] ) + await api.post( + `/admin/api-keys/${publishableKey.id}/sales-channels`, + { add: [salesChannel.id] }, + adminHeaders + ) + let response = await api.get( - `/store/products?sales_channel_id[]=${salesChannel.id}` + `/store/products?sales_channel_id[]=${salesChannel.id}`, + storeHeaders ) expect(response.status).toEqual(200) @@ -643,7 +624,8 @@ medusaIntegrationTestRunner({ ) const response = await api.get( - `/store/products?category_id[]=${category.id}&category_id[]=${category2.id}` + `/store/products?category_id[]=${category.id}&category_id[]=${category2.id}`, + storeHeaders ) expect(response.status).toEqual(200) @@ -656,7 +638,7 @@ medusaIntegrationTestRunner({ }) it("returns a list of ordered products by id ASC", async () => { - const response = await api.get("/store/products?order=id") + const response = await api.get("/store/products?order=id", storeHeaders) expect(response.status).toEqual(200) expect(response.data.products).toEqual( [product.id, product2.id, product3.id] @@ -666,7 +648,10 @@ medusaIntegrationTestRunner({ }) it("returns a list of ordered products by id DESC", async () => { - const response = await api.get("/store/products?order=-id") + const response = await api.get( + "/store/products?order=-id", + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.products).toEqual( @@ -678,7 +663,10 @@ medusaIntegrationTestRunner({ // TODO: This doesn't work currently, but worked in v1 it.skip("returns a list of ordered products by variants title DESC", async () => { - const response = await api.get("/store/products?order=-variants.title") + const response = await api.get( + "/store/products?order=-variants.title", + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.products).toEqual([ @@ -690,7 +678,10 @@ medusaIntegrationTestRunner({ // TODO: This doesn't work currently, but worked in v1 it.skip("returns a list of ordered products by variants title ASC", async () => { - const response = await api.get("/store/products?order=variants.title") + const response = await api.get( + "/store/products?order=variants.title", + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.products).toEqual([ @@ -703,7 +694,8 @@ medusaIntegrationTestRunner({ // TODO: This doesn't work currently, but worked in v1 it.skip("returns a list of ordered products by variants prices DESC", async () => { let response = await api.get( - "/store/products?order=-variants.prices.amount" + "/store/products?order=-variants.prices.amount", + storeHeaders ) }) @@ -711,14 +703,18 @@ medusaIntegrationTestRunner({ it.skip("returns a list of ordered products by variants prices ASC", async () => {}) it("products contain only fields defined with `fields` param", async () => { - const response = await api.get("/store/products?fields=handle") + const response = await api.get( + "/store/products?fields=handle", + storeHeaders + ) expect(response.status).toEqual(200) expect(Object.keys(response.data.products[0])).toEqual(["handle", "id"]) }) it("returns a list of products in collection", async () => { const response = await api.get( - `/store/products?collection_id[]=${collection.id}` + `/store/products?collection_id[]=${collection.id}`, + storeHeaders ) expect(response.status).toEqual(200) @@ -730,7 +726,8 @@ medusaIntegrationTestRunner({ it("returns a list of products with a given tag", async () => { const response = await api.get( - `/store/products?tag_id[]=${product.tags[0].id}` + `/store/products?tag_id[]=${product.tags[0].id}`, + storeHeaders ) expect(response.status).toEqual(200) @@ -743,7 +740,7 @@ medusaIntegrationTestRunner({ // TODO: Not implemented yet it.skip("returns gift card product", async () => { const response = await api - .get("/store/products?is_giftcard=true") + .get("/store/products?is_giftcard=true", storeHeaders) .catch((err) => { console.log(err) }) @@ -752,7 +749,7 @@ medusaIntegrationTestRunner({ // TODO: Not implemented yet it.skip("returns non gift card products", async () => { const response = await api - .get("/store/products?is_giftcard=false") + .get("/store/products?is_giftcard=false", storeHeaders) .catch((err) => { console.log(err) }) @@ -760,7 +757,8 @@ medusaIntegrationTestRunner({ it("returns a list of products in with a given handle", async () => { const response = await api.get( - `/store/products?handle=${product.handle}` + `/store/products?handle=${product.handle}`, + storeHeaders ) expect(response.status).toEqual(200) @@ -773,7 +771,8 @@ medusaIntegrationTestRunner({ it("returns a list of products filtered by variant options", async () => { const option = product.options.find((o) => o.title === "size") const response = await api.get( - `/store/products?variants.options[option_id]=${option?.id}&variants.options[value]=large` + `/store/products?variants.options[option_id]=${option?.id}&variants.options[value]=large`, + storeHeaders ) expect(response.status).toEqual(200) @@ -807,6 +806,12 @@ medusaIntegrationTestRunner({ publishableKey1 = api1Res.data.api_key + await api.post( + `/admin/api-keys/${publishableKey.id}/sales-channels`, + { add: [salesChannel1.id, salesChannel2.id] }, + adminHeaders + ) + await api.post( `/admin/api-keys/${publishableKey1.id}/sales-channels`, { add: [salesChannel1.id] }, @@ -830,7 +835,10 @@ medusaIntegrationTestRunner({ }) it("should list products by id", async () => { - let response = await api.get(`/store/products?id[]=${product.id}`) + let response = await api.get( + `/store/products?id[]=${product.id}`, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.count).toEqual(1) @@ -850,8 +858,8 @@ medusaIntegrationTestRunner({ expect(error.response.status).toEqual(400) expect(error.response.data).toEqual({ - message: `Publishable API key not found`, - type: "invalid_data", + message: `A valid publishable key is required to proceed with the request`, + type: "not_allowed", }) }) @@ -864,7 +872,7 @@ medusaIntegrationTestRunner({ expect(error.response.status).toEqual(400) expect(error.response.data).toEqual({ - message: `Requested sales channel is not part of the publishable key mappings`, + message: `Requested sales channel is not part of the publishable key`, type: "invalid_data", }) }) @@ -878,7 +886,7 @@ medusaIntegrationTestRunner({ expect(error.response.status).toEqual(400) expect(error.response.data).toEqual({ - message: `Requested sales channel is not part of the publishable key mappings`, + message: `Requested sales channel is not part of the publishable key`, type: "invalid_data", }) }) @@ -886,7 +894,10 @@ medusaIntegrationTestRunner({ it("should throw error when calculating prices without context", async () => { let error = await api - .get(`/store/products?fields=*variants.calculated_price`) + .get( + `/store/products?fields=*variants.calculated_price`, + storeHeaders + ) .catch((e) => e) expect(error.response.status).toEqual(400) @@ -907,7 +918,8 @@ medusaIntegrationTestRunner({ ).data.region let response = await api.get( - `/store/products?fields=*variants.calculated_price®ion_id=${region.id}` + `/store/products?fields=*variants.calculated_price®ion_id=${region.id}`, + storeHeaders ) const expectation = expect.arrayContaining([ @@ -957,7 +969,10 @@ medusaIntegrationTestRunner({ expect(response.data.products).toEqual(expectation) // with only region_id - response = await api.get(`/store/products?region_id=${region.id}`) + response = await api.get( + `/store/products?region_id=${region.id}`, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.products).toEqual(expectation) @@ -1137,6 +1152,12 @@ medusaIntegrationTestRunner({ [product.id] ) + await api.post( + `/admin/api-keys/${publishableKey.id}/sales-channels`, + { add: [defaultSalesChannel.id] }, + adminHeaders + ) + const service = appContainer.resolve(ModuleRegistrationName.STORE) const [store] = await service.listStores() @@ -1154,7 +1175,10 @@ medusaIntegrationTestRunner({ }) it("should retrieve product successfully", async () => { - let response = await api.get(`/store/products/${product.id}`) + let response = await api.get( + `/store/products/${product.id}`, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.product).toEqual( @@ -1185,7 +1209,8 @@ medusaIntegrationTestRunner({ ) const response = await api.get( - `/store/products/${product.id}?fields=*categories` + `/store/products/${product.id}?fields=*categories`, + storeHeaders ) expect(response.status).toEqual(200) @@ -1200,7 +1225,8 @@ medusaIntegrationTestRunner({ it("should throw error when calculating prices without context", async () => { let error = await api .get( - `/store/products/${product.id}?fields=*variants.calculated_price` + `/store/products/${product.id}?fields=*variants.calculated_price`, + storeHeaders ) .catch((e) => e) @@ -1222,7 +1248,8 @@ medusaIntegrationTestRunner({ ).data.region let response = await api.get( - `/store/products/${product.id}?fields=*variants.calculated_price®ion_id=${region.id}` + `/store/products/${product.id}?fields=*variants.calculated_price®ion_id=${region.id}`, + storeHeaders ) const expectation = expect.objectContaining({ @@ -1270,7 +1297,8 @@ medusaIntegrationTestRunner({ // with only region_id response = await api.get( - `/store/products/${product.id}?region_id=${region.id}` + `/store/products/${product.id}?region_id=${region.id}`, + storeHeaders ) expect(response.status).toEqual(200) @@ -1285,12 +1313,30 @@ medusaIntegrationTestRunner({ let euCart beforeEach(async () => { + const salesChannel = ( + await api.post( + "/admin/sales-channels", + { + name: "test name", + description: "test description", + }, + adminHeaders + ) + ).data.sales_channel + + await api.post( + `/admin/api-keys/${publishableKey.id}/sales-channels`, + { add: [salesChannel.id] }, + adminHeaders + ) + const store = (await api.get("/admin/stores", adminHeaders)).data .stores[0] if (store) { await api.post( `/admin/stores/${store.id}`, { + default_sales_channel_id: salesChannel.id, supported_currencies: [ { currency_code: "usd", @@ -1307,6 +1353,7 @@ medusaIntegrationTestRunner({ await api.post( "/admin/stores", { + default_sales_channel_id: salesChannel.id, name: "Test store", supported_currencies: [ { @@ -1400,13 +1447,27 @@ medusaIntegrationTestRunner({ product2 = ( await api.post( "/admin/products", - getProductFixture({ title: "test2", status: "published" }), + getProductFixture({ + title: "test2", + status: "published", + }), adminHeaders ) ).data.product - euCart = (await api.post("/store/carts", { region_id: euRegion.id })) - .data.cart + await api.post( + `/admin/sales-channels/${salesChannel.id}/products`, + { add: [product1.id, product2.id] }, + adminHeaders + ) + + euCart = ( + await api.post( + "/store/carts", + { region_id: euRegion.id }, + storeHeaders + ) + ).data.cart await api.post( `/admin/tax-regions`, @@ -1451,7 +1512,8 @@ medusaIntegrationTestRunner({ it("should not return tax pricing if the context is not sufficient when listing products", async () => { const products = ( await api.get( - `/store/products?fields=id,*variants.calculated_price®ion_id=${usRegion.id}` + `/store/products?fields=id,*variants.calculated_price®ion_id=${usRegion.id}`, + storeHeaders ) ).data.products @@ -1467,7 +1529,8 @@ medusaIntegrationTestRunner({ it("should not return tax pricing if automatic taxes are off when listing products", async () => { const products = ( await api.get( - `/store/products?fields=id,*variants.calculated_price®ion_id=${usRegion.id}&country_code=us` + `/store/products?fields=id,*variants.calculated_price®ion_id=${usRegion.id}&country_code=us`, + storeHeaders ) ).data.products @@ -1483,7 +1546,8 @@ medusaIntegrationTestRunner({ it("should return prices with and without tax for a tax inclusive region when listing products", async () => { const products = ( await api.get( - `/store/products?fields=id,*variants.calculated_price®ion_id=${euRegion.id}&country_code=it` + `/store/products?fields=id,*variants.calculated_price®ion_id=${euRegion.id}&country_code=it`, + storeHeaders ) ).data.products @@ -1524,7 +1588,8 @@ medusaIntegrationTestRunner({ it("should return prices with and without tax for a tax exclusive region when listing products", async () => { const products = ( await api.get( - `/store/products?fields=id,*variants.calculated_price®ion_id=${dkRegion.id}&country_code=dk` + `/store/products?fields=id,*variants.calculated_price®ion_id=${dkRegion.id}&country_code=dk`, + storeHeaders ) ).data.products @@ -1558,7 +1623,8 @@ medusaIntegrationTestRunner({ it("should return prices with and without tax when the cart is available and a country is passed when listing products", async () => { const products = ( await api.get( - `/store/products?fields=id,*variants.calculated_price&cart_id=${euCart.id}&country_code=it` + `/store/products?fields=id,*variants.calculated_price&cart_id=${euCart.id}&country_code=it`, + storeHeaders ) ).data.products @@ -1584,15 +1650,20 @@ medusaIntegrationTestRunner({ }) it("should return prices with and without tax when the cart context is available when listing products", async () => { - await api.post(`/store/carts/${euCart.id}`, { - shipping_address: { - country_code: "it", + await api.post( + `/store/carts/${euCart.id}`, + { + shipping_address: { + country_code: "it", + }, }, - }) + storeHeaders + ) const products = ( await api.get( - `/store/products?fields=id,*variants.calculated_price&cart_id=${euCart.id}` + `/store/products?fields=id,*variants.calculated_price&cart_id=${euCart.id}`, + storeHeaders ) ).data.products @@ -1620,7 +1691,8 @@ medusaIntegrationTestRunner({ it("should not return tax pricing if the context is not sufficient when fetching a single product", async () => { const product = ( await api.get( - `/store/products/${product1.id}?fields=id,*variants.calculated_price®ion_id=${usRegion.id}` + `/store/products/${product1.id}?fields=id,*variants.calculated_price®ion_id=${usRegion.id}`, + storeHeaders ) ).data.product @@ -1635,7 +1707,8 @@ medusaIntegrationTestRunner({ it("should return prices with and without tax for a tax inclusive region when fetching a single product", async () => { const product = ( await api.get( - `/store/products/${product1.id}?fields=id,*variants.calculated_price®ion_id=${euRegion.id}&country_code=it` + `/store/products/${product1.id}?fields=id,*variants.calculated_price®ion_id=${euRegion.id}&country_code=it`, + storeHeaders ) ).data.product diff --git a/integration-tests/http/__tests__/region/admin/region.spec.ts b/integration-tests/http/__tests__/region/admin/region.spec.ts index 16fc258df9..e15168930d 100644 --- a/integration-tests/http/__tests__/region/admin/region.spec.ts +++ b/integration-tests/http/__tests__/region/admin/region.spec.ts @@ -3,6 +3,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(30000) @@ -12,10 +14,13 @@ medusaIntegrationTestRunner({ let region1 let region2 let container + let storeHeaders beforeEach(async () => { container = getContainer() await createAdminUser(dbConnection, adminHeaders, container) + const publishableKey = await generatePublishableKey(container) + storeHeaders = generateStoreHeaders({ publishableKey }) region1 = ( await api.post( @@ -111,7 +116,8 @@ medusaIntegrationTestRunner({ ) let response = await api.get( - `/store/regions/${region1.id}?fields=*payment_providers` + `/store/regions/${region1.id}?fields=*payment_providers`, + storeHeaders ) expect(response.status).toEqual(200) @@ -125,7 +131,8 @@ medusaIntegrationTestRunner({ ]) response = await api.get( - `/store/regions/${region1.id}?fields=*payment_providers` + `/store/regions/${region1.id}?fields=*payment_providers`, + storeHeaders ) expect(response.status).toEqual(200) diff --git a/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts b/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts index 450e88fc50..aac7851a46 100644 --- a/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts @@ -10,6 +10,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(50000) @@ -24,6 +26,7 @@ medusaIntegrationTestRunner({ let cartModuleService: ICartModuleService let promotionModuleService: IPromotionModuleService let remoteLinkService: RemoteLink + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -38,6 +41,8 @@ medusaIntegrationTestRunner({ beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders, appContainer) + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) }) describe("POST /store/carts/:id/promotions", () => { @@ -123,9 +128,11 @@ medusaIntegrationTestRunner({ [Modules.PROMOTION]: { promotion_id: appliedPromotion.id }, }) - const created = await api.post(`/store/carts/${cart.id}/promotions`, { - promo_codes: [createdPromotion.code], - }) + const created = await api.post( + `/store/carts/${cart.id}/promotions`, + { promo_codes: [createdPromotion.code] }, + storeHeaders + ) expect(created.status).toEqual(200) expect(created.data.cart).toEqual( @@ -264,9 +271,11 @@ medusaIntegrationTestRunner({ }, ]) - const created = await api.post(`/store/carts/${cart.id}/promotions`, { - promo_codes: [newPromotion.code], - }) + const created = await api.post( + `/store/carts/${cart.id}/promotions`, + { promo_codes: [newPromotion.code] }, + storeHeaders + ) expect(created.status).toEqual(200) expect(created.data.cart).toEqual( diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index 6a959b4e43..01893c6bbb 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -25,7 +25,11 @@ import { RuleOperator, } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" -import { createAdminUser } from "../../../../helpers/create-admin-user" +import { + createAdminUser, + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" import { seedStorefrontDefaults } from "../../../../helpers/seed-storefront-defaults" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { setupTaxStructure } from "../../fixtures" @@ -58,6 +62,7 @@ medusaIntegrationTestRunner({ let region let store + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -84,6 +89,8 @@ medusaIntegrationTestRunner({ beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders, appContainer) + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) const { store: defaultStore } = await seedStorefrontDefaults( appContainer, @@ -158,22 +165,26 @@ medusaIntegrationTestRunner({ }, ]) - const created = await api.post(`/store/carts`, { - email: "tony@stark.com", - currency_code: "usd", - region_id: region.id, - sales_channel_id: salesChannel.id, - items: [ - { - variant_id: product.variants[0].id, - quantity: 1, - }, - { - variant_id: product.variants[1].id, - quantity: 2, - }, - ], - }) + const created = await api.post( + `/store/carts`, + { + email: "tony@stark.com", + currency_code: "usd", + region_id: region.id, + sales_channel_id: salesChannel.id, + items: [ + { + variant_id: product.variants[0].id, + quantity: 1, + }, + { + variant_id: product.variants[1].id, + quantity: 2, + }, + ], + }, + storeHeaders + ) expect(created.status).toEqual(200) expect(created.data.cart).toEqual( @@ -230,25 +241,29 @@ medusaIntegrationTestRunner({ }, ]) - const created = await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - shipping_address: { - address_1: "test address 1", - address_2: "test address 2", - city: "NY", - country_code: "US", - province: "NY", - postal_code: "94016", - }, - sales_channel_id: salesChannel.id, - items: [ - { - quantity: 1, - variant_id: product.variants[0].id, + const created = await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + shipping_address: { + address_1: "test address 1", + address_2: "test address 2", + city: "NY", + country_code: "US", + province: "NY", + postal_code: "94016", }, - ], - }) + sales_channel_id: salesChannel.id, + items: [ + { + quantity: 1, + variant_id: product.variants[0].id, + }, + ], + }, + storeHeaders + ) expect(created.status).toEqual(200) expect(created.data.cart).toEqual( @@ -283,10 +298,14 @@ medusaIntegrationTestRunner({ currency_code: "usd", }) - const response = await api.post(`/store/carts`, { - email: "tony@stark.com", - currency_code: "usd", - }) + const response = await api.post( + `/store/carts`, + { + email: "tony@stark.com", + currency_code: "usd", + }, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -310,10 +329,14 @@ medusaIntegrationTestRunner({ default_sales_channel_id: sc.id, }) - const response = await api.post(`/store/carts`, { - email: "tony@stark.com", - currency_code: "usd", - }) + const response = await api.post( + `/store/carts`, + { + email: "tony@stark.com", + currency_code: "usd", + }, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -332,10 +355,14 @@ medusaIntegrationTestRunner({ currency_code: "usd", }) - const response = await api.post(`/store/carts`, { - email: "tony@stark.com", - region_id: region.id, - }) + const response = await api.post( + `/store/carts`, + { + email: "tony@stark.com", + region_id: region.id, + }, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -359,7 +386,10 @@ medusaIntegrationTestRunner({ `/store/carts`, {}, { - headers: { authorization: `Bearer ${jwt}` }, + headers: { + authorization: `Bearer ${jwt}`, + ...storeHeaders.headers, + }, } ) @@ -531,9 +561,7 @@ medusaIntegrationTestRunner({ it("should respond 400 bad request on unknown props", async () => { await expect( - api.post(`/store/carts`, { - foo: "bar", - }) + api.post(`/store/carts`, { foo: "bar" }, storeHeaders) ).rejects.toThrow() }) }) @@ -624,9 +652,11 @@ medusaIntegrationTestRunner({ ]) // Should remove earlier adjustments from other promocodes - let updated = await api.post(`/store/carts/${cart.id}`, { - promo_codes: [createdPromotion.code], - }) + let updated = await api.post( + `/store/carts/${cart.id}`, + { promo_codes: [createdPromotion.code] }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -654,9 +684,11 @@ medusaIntegrationTestRunner({ }) ) // Should remove all adjustments from other promo codes - updated = await api.post(`/store/carts/${cart.id}`, { - promo_codes: [], - }) + updated = await api.post( + `/store/carts/${cart.id}`, + { promo_codes: [] }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -711,9 +743,11 @@ medusaIntegrationTestRunner({ ], }) - let updated = await api.post(`/store/carts/${cart.id}`, { - email: "another@tax.com", - }) + let updated = await api.post( + `/store/carts/${cart.id}`, + { email: "another@tax.com" }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -733,9 +767,11 @@ medusaIntegrationTestRunner({ region_id: region.id, }) - updated = await api.post(`/store/carts/${cart.id}`, { - email: "another@tax.com", - }) + updated = await api.post( + `/store/carts/${cart.id}`, + { email: "another@tax.com" }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -860,11 +896,15 @@ medusaIntegrationTestRunner({ }, ]) - let updated = await api.post(`/store/carts/${cart.id}`, { - region_id: region.id, - email: "tony@stark.com", - sales_channel_id: salesChannel.id, - }) + let updated = await api.post( + `/store/carts/${cart.id}`, + { + region_id: region.id, + email: "tony@stark.com", + sales_channel_id: salesChannel.id, + }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -930,10 +970,14 @@ medusaIntegrationTestRunner({ }) ) - updated = await api.post(`/store/carts/${cart.id}`, { - email: null, - sales_channel_id: null, - }) + updated = await api.post( + `/store/carts/${cart.id}`, + { + email: null, + sales_channel_id: null, + }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -1063,9 +1107,11 @@ medusaIntegrationTestRunner({ }, ]) - let updated = await api.post(`/store/carts/${cart.id}`, { - email: "jon@stark.com", - }) + let updated = await api.post( + `/store/carts/${cart.id}`, + { email: "jon@stark.com" }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -1080,10 +1126,14 @@ medusaIntegrationTestRunner({ }) ) - updated = await api.post(`/store/carts/${cart.id}`, { - email: null, - sales_channel_id: null, - }) + updated = await api.post( + `/store/carts/${cart.id}`, + { + email: null, + sales_channel_id: null, + }, + storeHeaders + ) expect(updated.status).toEqual(200) expect(updated.data.cart).toEqual( @@ -1107,12 +1157,16 @@ medusaIntegrationTestRunner({ name: "Webshop", }) - const created = await api.post(`/store/carts`, { - email: "tony@stark.com", - currency_code: "usd", - region_id: region.id, - sales_channel_id: salesChannel.id, - }) + const created = await api.post( + `/store/carts`, + { + email: "tony@stark.com", + currency_code: "usd", + region_id: region.id, + sales_channel_id: salesChannel.id, + }, + storeHeaders + ) expect(created.status).toEqual(200) expect(created.data.cart).toEqual( @@ -1132,7 +1186,8 @@ medusaIntegrationTestRunner({ `/store/carts/${created.data.cart.id}`, { email: "tony@stark-industries.com", - } + }, + storeHeaders ) expect(updated.status).toEqual(200) @@ -1170,7 +1225,10 @@ medusaIntegrationTestRunner({ sales_channel_id: salesChannel.id, }) - const response = await api.get(`/store/carts/${cart.id}`) + const response = await api.get( + `/store/carts/${cart.id}`, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -1373,10 +1431,14 @@ medusaIntegrationTestRunner({ }, ]) - let response = await api.post(`/store/carts/${cart.id}/line-items`, { - variant_id: productWithSpecialTax.variants[0].id, - quantity: 1, - }) + let response = await api.post( + `/store/carts/${cart.id}/line-items`, + { + variant_id: productWithSpecialTax.variants[0].id, + quantity: 1, + }, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -1422,10 +1484,14 @@ medusaIntegrationTestRunner({ }) ) - response = await api.post(`/store/carts/${cart.id}/line-items`, { - variant_id: productWithDefaultTax.variants[0].id, - quantity: 1, - }) + response = await api.post( + `/store/carts/${cart.id}/line-items`, + { + variant_id: productWithDefaultTax.variants[0].id, + quantity: 1, + }, + storeHeaders + ) expect(response.data.cart).toEqual( expect.objectContaining({ @@ -1458,31 +1524,39 @@ medusaIntegrationTestRunner({ ).data.product const cart = ( - await api.post(`/store/carts`, { - email: "tony@stark.com", - currency_code: region.currency_code, - region_id: region.id, - items: [ - { - variant_id: product.variants[0].id, - quantity: 1, - metadata: { - Size: "S", - Color: "Black", + await api.post( + `/store/carts`, + { + email: "tony@stark.com", + currency_code: region.currency_code, + region_id: region.id, + items: [ + { + variant_id: product.variants[0].id, + quantity: 1, + metadata: { + Size: "S", + Color: "Black", + }, }, - }, - ], - }) + ], + }, + storeHeaders + ) ).data.cart - let response = await api.post(`/store/carts/${cart.id}/line-items`, { - variant_id: product.variants[0].id, - quantity: 1, - metadata: { - Size: "S", - Color: "Black", + let response = await api.post( + `/store/carts/${cart.id}/line-items`, + { + variant_id: product.variants[0].id, + quantity: 1, + metadata: { + Size: "S", + Color: "Black", + }, }, - }) + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -1498,15 +1572,19 @@ medusaIntegrationTestRunner({ }) ) - response = await api.post(`/store/carts/${cart.id}/line-items`, { - variant_id: product.variants[0].id, - quantity: 1, - metadata: { - Size: "S", - Color: "White", - Special: "attribute", + response = await api.post( + `/store/carts/${cart.id}/line-items`, + { + variant_id: product.variants[0].id, + quantity: 1, + metadata: { + Size: "S", + Color: "White", + Special: "attribute", + }, }, - }) + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.cart).toEqual( @@ -1541,9 +1619,11 @@ medusaIntegrationTestRunner({ region_id: region.id, }) - const response = await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + const response = await api.post( + `/store/payment-collections`, + { cart_id: cart.id }, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.payment_collection).toEqual( @@ -1566,14 +1646,22 @@ medusaIntegrationTestRunner({ }) const firstCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) ).data.payment_collection - const response = await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + const response = await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) expect(response.status).toEqual(200) expect(response.data.payment_collection.id).toEqual( @@ -1598,15 +1686,23 @@ medusaIntegrationTestRunner({ }) const firstCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: firstCart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: firstCart.id, + }, + storeHeaders + ) ).data.payment_collection const secondCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: secondCart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: secondCart.id, + }, + storeHeaders + ) ).data.payment_collection expect(firstCollection.id).toBeTruthy() @@ -1646,7 +1742,11 @@ medusaIntegrationTestRunner({ ], }) - let updated = await api.post(`/store/carts/${cart.id}/taxes`, {}) + let updated = await api.post( + `/store/carts/${cart.id}/taxes`, + {}, + storeHeaders + ) expect(updated.status).toEqual(200) @@ -1696,7 +1796,7 @@ medusaIntegrationTestRunner({ }) let error = await api - .post(`/store/carts/${cart.id}/taxes`, {}) + .post(`/store/carts/${cart.id}/taxes`, {}, storeHeaders) .catch((e) => e) expect(error.response.status).toEqual(400) @@ -1780,7 +1880,8 @@ medusaIntegrationTestRunner({ let response = await api.post( `/store/carts/${cart.id}/shipping-methods`, - { option_id: shippingOption.id } + { option_id: shippingOption.id }, + storeHeaders ) expect(response.status).toEqual(200) @@ -2016,36 +2117,46 @@ medusaIntegrationTestRunner({ it("should create an order and create item reservations", async () => { const cart = ( - await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - shipping_address: { - address_1: "test address 1", - address_2: "test address 2", - city: "ny", - country_code: "us", - province: "ny", - postal_code: "94016", + await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + shipping_address: { + address_1: "test address 1", + address_2: "test address 2", + city: "ny", + country_code: "us", + province: "ny", + postal_code: "94016", + }, + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], }, - sales_channel_id: salesChannel.id, - items: [{ quantity: 1, variant_id: product.variants[0].id }], - }) + storeHeaders + ) ).data.cart const paymentCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) ).data.payment_collection await api.post( `/store/payment-collections/${paymentCollection.id}/payment-sessions`, - { provider_id: "pp_system_default" } + { provider_id: "pp_system_default" }, + storeHeaders ) const response = await api.post( `/store/carts/${cart.id}/complete`, - {} + {}, + storeHeaders ) expect(response.status).toEqual(200) @@ -2152,16 +2263,20 @@ medusaIntegrationTestRunner({ it("should throw an error when payment collection isn't created", async () => { const cart = ( - await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - sales_channel_id: salesChannel.id, - items: [{ quantity: 1, variant_id: product.variants[0].id }], - }) + await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], + }, + storeHeaders + ) ).data.cart const error = await api - .post(`/store/carts/${cart.id}/complete`, {}) + .post(`/store/carts/${cart.id}/complete`, {}, storeHeaders) .catch((e) => e) expect(error.response.status).toEqual(400) @@ -2173,20 +2288,28 @@ medusaIntegrationTestRunner({ it("should throw an error when payment collection isn't created", async () => { const cart = ( - await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - sales_channel_id: salesChannel.id, - items: [{ quantity: 1, variant_id: product.variants[0].id }], - }) + await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], + }, + storeHeaders + ) ).data.cart - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) const error = await api - .post(`/store/carts/${cart.id}/complete`, {}) + .post(`/store/carts/${cart.id}/complete`, {}, storeHeaders) .catch((e) => e) expect(error.response.status).toEqual(400) @@ -2198,51 +2321,69 @@ medusaIntegrationTestRunner({ it("should fail to update cart when it is completed", async () => { const cart = ( - await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - shipping_address: { - address_1: "test address 1", - address_2: "test address 2", - city: "ny", - country_code: "us", - province: "ny", - postal_code: "94016", + await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + shipping_address: { + address_1: "test address 1", + address_2: "test address 2", + city: "ny", + country_code: "us", + province: "ny", + postal_code: "94016", + }, + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], }, - sales_channel_id: salesChannel.id, - items: [{ quantity: 1, variant_id: product.variants[0].id }], - }) + storeHeaders + ) ).data.cart const paymentCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) ).data.payment_collection await api.post( `/store/payment-collections/${paymentCollection.id}/payment-sessions`, - { provider_id: "pp_system_default" } + { provider_id: "pp_system_default" }, + storeHeaders ) - await api.post(`/store/carts/${cart.id}/complete`, {}) + await api.post(`/store/carts/${cart.id}/complete`, {}, storeHeaders) - const cartRefetch = (await api.get(`/store/carts/${cart.id}`)).data - .cart + const cartRefetch = ( + await api.get(`/store/carts/${cart.id}`, storeHeaders) + ).data.cart expect(cartRefetch.completed_at).toBeTruthy() await expect( - api.post(`/store/carts/${cart.id}/shipping-methods`, { - option_id: shippingOption.id, - }) + api.post( + `/store/carts/${cart.id}/shipping-methods`, + { + option_id: shippingOption.id, + }, + storeHeaders + ) ).rejects.toThrow() const error = await api - .post(`/store/carts/${cart.id}/line-items`, { - variant_id: product.variants[0].id, - quantity: 1, - }) + .post( + `/store/carts/${cart.id}/line-items`, + { + variant_id: product.variants[0].id, + quantity: 1, + }, + storeHeaders + ) .catch((e) => e) expect(error.response.status).toEqual(400) @@ -2269,36 +2410,46 @@ medusaIntegrationTestRunner({ ) const cart = ( - await api.post(`/store/carts`, { - currency_code: "usd", - email: "tony@stark-industries.com", - shipping_address: { - address_1: "test address 1", - address_2: "test address 2", - city: "ny", - country_code: "us", - province: "ny", - postal_code: "94016", + await api.post( + `/store/carts`, + { + currency_code: "usd", + email: "tony@stark-industries.com", + shipping_address: { + address_1: "test address 1", + address_2: "test address 2", + city: "ny", + country_code: "us", + province: "ny", + postal_code: "94016", + }, + sales_channel_id: salesChannel.id, + items: [{ quantity: 1, variant_id: product.variants[0].id }], }, - sales_channel_id: salesChannel.id, - items: [{ quantity: 1, variant_id: product.variants[0].id }], - }) + storeHeaders + ) ).data.cart const paymentCollection = ( - await api.post(`/store/payment-collections`, { - cart_id: cart.id, - }) + await api.post( + `/store/payment-collections`, + { + cart_id: cart.id, + }, + storeHeaders + ) ).data.payment_collection await api.post( `/store/payment-collections/${paymentCollection.id}/payment-sessions`, - { provider_id: "pp_system_default" } + { provider_id: "pp_system_default" }, + storeHeaders ) const response = await api.post( `/store/carts/${cart.id}/complete`, - {} + {}, + storeHeaders ) expect(response.status).toEqual(200) diff --git a/integration-tests/modules/__tests__/cart/store/remove-promotions-from-cart.spec.ts b/integration-tests/modules/__tests__/cart/store/remove-promotions-from-cart.spec.ts index 3c80da9b50..9be3d22ab4 100644 --- a/integration-tests/modules/__tests__/cart/store/remove-promotions-from-cart.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/remove-promotions-from-cart.spec.ts @@ -10,6 +10,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(50000) @@ -24,6 +26,7 @@ medusaIntegrationTestRunner({ let cartModuleService: ICartModuleService let promotionModuleService: IPromotionModuleService let remoteLinkService: RemoteLink + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -38,6 +41,8 @@ medusaIntegrationTestRunner({ beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders, appContainer) + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) }) describe("DELETE /store/carts/:id/promotions", () => { @@ -147,6 +152,7 @@ medusaIntegrationTestRunner({ data: { promo_codes: [appliedPromotionToRemove.code], }, + ...storeHeaders, } ) @@ -295,6 +301,7 @@ medusaIntegrationTestRunner({ `/store/carts/${cart.id}/promotions`, { data: { promo_codes: [appliedPromotionToRemove.code] }, + ...storeHeaders, } ) diff --git a/integration-tests/modules/__tests__/currency/store/currency.spec.ts b/integration-tests/modules/__tests__/currency/store/currency.spec.ts index b9fb905f37..fe500e8d36 100644 --- a/integration-tests/modules/__tests__/currency/store/currency.spec.ts +++ b/integration-tests/modules/__tests__/currency/store/currency.spec.ts @@ -1,17 +1,21 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" jest.setTimeout(50000) const env = { MEDUSA_FF_MEDUSA_V2: true } -const storeHeaders = { - headers: {}, -} medusaIntegrationTestRunner({ env, - testSuite: ({ api }) => { + testSuite: ({ api, getContainer }) => { describe("Currency - Store", () => { it("should correctly retrieve and list currencies", async () => { + const publishableKey = await generatePublishableKey(getContainer()) + const storeHeaders = generateStoreHeaders({ publishableKey }) + const listResp = await api.get("/store/currencies", storeHeaders) expect(listResp.data.currencies).toEqual( diff --git a/integration-tests/modules/__tests__/customer/store/create-customer-addresses.ts b/integration-tests/modules/__tests__/customer/store/create-customer-addresses.ts index 5e2ee72365..a30f518aef 100644 --- a/integration-tests/modules/__tests__/customer/store/create-customer-addresses.ts +++ b/integration-tests/modules/__tests__/customer/store/create-customer-addresses.ts @@ -1,6 +1,10 @@ import { ICustomerModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" jest.setTimeout(50000) @@ -13,6 +17,7 @@ medusaIntegrationTestRunner({ describe("POST /store/customers/me/addresses", () => { let appContainer let customerModuleService: ICustomerModuleService + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -21,6 +26,11 @@ medusaIntegrationTestRunner({ ) }) + beforeEach(async () => { + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) + }) + it("should create a customer address", async () => { const { customer, jwt } = await createAuthenticatedCustomer( appContainer @@ -33,7 +43,12 @@ medusaIntegrationTestRunner({ last_name: "Doe", address_1: "Test street 1", }, - { headers: { authorization: `Bearer ${jwt}` } } + { + headers: { + authorization: `Bearer ${jwt}`, + ...storeHeaders.headers, + }, + } ) expect(response.status).toEqual(200) diff --git a/integration-tests/modules/__tests__/customer/store/create-customer.spec.ts b/integration-tests/modules/__tests__/customer/store/create-customer.spec.ts index 28bc574e26..9f95afc17f 100644 --- a/integration-tests/modules/__tests__/customer/store/create-customer.spec.ts +++ b/integration-tests/modules/__tests__/customer/store/create-customer.spec.ts @@ -9,6 +9,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, createAdminUser, + generatePublishableKey, + generateStoreHeaders, } from "../../../../helpers/create-admin-user" jest.setTimeout(50000) @@ -20,6 +22,7 @@ medusaIntegrationTestRunner({ testSuite: ({ dbConnection, getContainer, api }) => { describe("POST /store/customers", () => { let appContainer + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -27,6 +30,8 @@ medusaIntegrationTestRunner({ beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders, appContainer) + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) }) // TODO: Reenable once the customer authentication is fixed, and use the HTTP endpoints instead. @@ -55,7 +60,12 @@ medusaIntegrationTestRunner({ last_name: "Doe", email: "john@me.com", }, - { headers: { authorization: `Bearer ${token}` } } + { + headers: { + authorization: `Bearer ${token}`, + ...storeHeaders.headers, + }, + } ) expect(response.status).toEqual(200) diff --git a/integration-tests/modules/__tests__/customer/store/delete-customer-address.spec.ts b/integration-tests/modules/__tests__/customer/store/delete-customer-address.spec.ts index fb7734c2a5..89a50b8ade 100644 --- a/integration-tests/modules/__tests__/customer/store/delete-customer-address.spec.ts +++ b/integration-tests/modules/__tests__/customer/store/delete-customer-address.spec.ts @@ -1,6 +1,10 @@ import { ICustomerModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" const env = { MEDUSA_FF_MEDUSA_V2: true } @@ -13,6 +17,7 @@ medusaIntegrationTestRunner({ describe("DELETE /store/customers/me/addresses/:address_id", () => { let appContainer let customerModuleService: ICustomerModuleService + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -21,6 +26,12 @@ medusaIntegrationTestRunner({ ) }) + beforeEach(async () => { + appContainer = getContainer() + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) + }) + it("should delete a customer address", async () => { const { customer, jwt } = await createAuthenticatedCustomer( appContainer @@ -35,7 +46,12 @@ medusaIntegrationTestRunner({ const response = await api.delete( `/store/customers/me/addresses/${address.id}`, - { headers: { authorization: `Bearer ${jwt}` } } + { + headers: { + authorization: `Bearer ${jwt}`, + ...storeHeaders.headers, + }, + } ) expect(response.status).toEqual(200) @@ -66,7 +82,10 @@ medusaIntegrationTestRunner({ const response = await api .delete(`/store/customers/me/addresses/${address.id}`, { - headers: { authorization: `Bearer ${jwt}` }, + headers: { + authorization: `Bearer ${jwt}`, + ...storeHeaders.headers, + }, }) .catch((e) => e.response) diff --git a/integration-tests/modules/__tests__/customer/store/get-me.spec.ts b/integration-tests/modules/__tests__/customer/store/get-me.spec.ts index d654165c98..f1970db5af 100644 --- a/integration-tests/modules/__tests__/customer/store/get-me.spec.ts +++ b/integration-tests/modules/__tests__/customer/store/get-me.spec.ts @@ -1,5 +1,9 @@ -import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" +import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" jest.setTimeout(50000) @@ -10,17 +14,25 @@ medusaIntegrationTestRunner({ testSuite: ({ dbConnection, getContainer, api }) => { describe("GET /store/customers", () => { let appContainer + let storeHeaders + beforeAll(async () => { appContainer = getContainer() }) + beforeEach(async () => { + appContainer = getContainer() + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) + }) + it("should retrieve auth user's customer", async () => { const { customer, jwt } = await createAuthenticatedCustomer( appContainer ) const response = await api.get(`/store/customers/me`, { - headers: { authorization: `Bearer ${jwt}` }, + headers: { authorization: `Bearer ${jwt}`, ...storeHeaders.headers }, }) expect(response.status).toEqual(200) diff --git a/integration-tests/modules/__tests__/customer/store/list-customer-addresses.ts b/integration-tests/modules/__tests__/customer/store/list-customer-addresses.ts index 6c110f7338..af14f51017 100644 --- a/integration-tests/modules/__tests__/customer/store/list-customer-addresses.ts +++ b/integration-tests/modules/__tests__/customer/store/list-customer-addresses.ts @@ -1,6 +1,10 @@ import { ICustomerModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" const env = { MEDUSA_FF_MEDUSA_V2: true } @@ -13,6 +17,7 @@ medusaIntegrationTestRunner({ describe("GET /store/customers/me/addresses", () => { let appContainer let customerModuleService: ICustomerModuleService + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -21,6 +26,12 @@ medusaIntegrationTestRunner({ ) }) + beforeEach(async () => { + appContainer = getContainer() + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) + }) + it("should get all customer addresses and its count", async () => { const { customer, jwt } = await createAuthenticatedCustomer( appContainer @@ -60,7 +71,7 @@ medusaIntegrationTestRunner({ }) const response = await api.get(`/store/customers/me/addresses`, { - headers: { authorization: `Bearer ${jwt}` }, + headers: { authorization: `Bearer ${jwt}`, ...storeHeaders.headers }, }) expect(response.status).toEqual(200) diff --git a/integration-tests/modules/__tests__/customer/store/update-customer-address.spec.ts b/integration-tests/modules/__tests__/customer/store/update-customer-address.spec.ts index b72970a511..e081089376 100644 --- a/integration-tests/modules/__tests__/customer/store/update-customer-address.spec.ts +++ b/integration-tests/modules/__tests__/customer/store/update-customer-address.spec.ts @@ -1,6 +1,10 @@ import { ICustomerModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" jest.setTimeout(50000) @@ -13,6 +17,7 @@ medusaIntegrationTestRunner({ describe("POST /store/customers/:id/addresses/:address_id", () => { let appContainer let customerModuleService: ICustomerModuleService + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -21,7 +26,13 @@ medusaIntegrationTestRunner({ ) }) - it("should update a customer address", async () => { + beforeEach(async () => { + appContainer = getContainer() + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) + }) + + it.only("should update a customer address", async () => { const { customer, jwt } = await createAuthenticatedCustomer( appContainer ) @@ -38,7 +49,12 @@ medusaIntegrationTestRunner({ { first_name: "Jane", }, - { headers: { authorization: `Bearer ${jwt}` } } + { + headers: { + authorization: `Bearer ${jwt}`, + ...storeHeaders.headers, + }, + } ) expect(response.status).toEqual(200) @@ -70,7 +86,12 @@ medusaIntegrationTestRunner({ .post( `/store/customers/me/addresses/${address.id}`, { first_name: "Jane" }, - { headers: { authorization: `Bearer ${jwt}` } } + { + headers: { + authorization: `Bearer ${jwt}`, + ...storeHeaders.headers, + }, + } ) .catch((e) => e.response) diff --git a/integration-tests/modules/__tests__/price-lists/store/get-product.ts b/integration-tests/modules/__tests__/price-lists/store/get-product.ts index 9b6c2500fd..6afdd2e244 100644 --- a/integration-tests/modules/__tests__/price-lists/store/get-product.ts +++ b/integration-tests/modules/__tests__/price-lists/store/get-product.ts @@ -1,6 +1,10 @@ import { PriceListStatus, PriceListType } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" -import { createAdminUser } from "../../../../helpers/create-admin-user" +import { + createAdminUser, + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" import { getProductFixture } from "../../../../helpers/fixtures" jest.setTimeout(50000) @@ -23,6 +27,7 @@ medusaIntegrationTestRunner({ let product let variant let region + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -30,6 +35,9 @@ medusaIntegrationTestRunner({ beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders, appContainer) + appContainer = getContainer() + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) region = ( await api.post( @@ -87,7 +95,8 @@ medusaIntegrationTestRunner({ ) let response = await api.get( - `/store/products/${product.id}?currency_code=usd` + `/store/products/${product.id}?currency_code=usd`, + storeHeaders ) expect(response.status).toEqual(200) @@ -149,7 +158,8 @@ medusaIntegrationTestRunner({ ) let response = await api.get( - `/store/products/${product.id}?currency_code=usd` + `/store/products/${product.id}?currency_code=usd`, + storeHeaders ) expect(response.status).toEqual(200) @@ -184,10 +194,14 @@ medusaIntegrationTestRunner({ ) ).data.customer_group - const authResponse = await api.post("/store/auth", { - email: "test5@email-pl.com", - password: "test", - }) + const authResponse = await api.post( + "/store/auth", + { + email: "test5@email-pl.com", + password: "test", + }, + storeHeaders + ) const [authCookie] = authResponse.headers["set-cookie"][0].split(";") @@ -215,6 +229,7 @@ medusaIntegrationTestRunner({ { headers: { Cookie: authCookie, + ...storeHeaders.headers, }, } ) diff --git a/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts b/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts index 09d9f804c0..aac1704d3c 100644 --- a/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts +++ b/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts @@ -8,7 +8,11 @@ import { Modules, } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" -import { createAdminUser } from "../../../../helpers/create-admin-user" +import { + createAdminUser, + generatePublishableKey, + generateStoreHeaders, +} from "../../../../helpers/create-admin-user" jest.setTimeout(50000) @@ -31,6 +35,7 @@ medusaIntegrationTestRunner({ let fulfillmentSet let cart let shippingOption + let storeHeaders beforeAll(async () => { appContainer = getContainer() @@ -38,6 +43,8 @@ medusaIntegrationTestRunner({ ModuleRegistrationName.FULFILLMENT ) regionService = appContainer.resolve(ModuleRegistrationName.REGION) + const publishableKey = await generatePublishableKey(appContainer) + storeHeaders = generateStoreHeaders({ publishableKey }) }) beforeEach(async () => { @@ -172,25 +179,30 @@ medusaIntegrationTestRunner({ ).data.shipping_option cart = ( - await api.post(`/store/carts`, { - region_id: region.id, - sales_channel_id: salesChannel.id, - currency_code: "usd", - email: "test@admin.com", - items: [ - { - variant_id: product.variants[0].id, - quantity: 1, - }, - ], - }) + await api.post( + `/store/carts`, + { + region_id: region.id, + sales_channel_id: salesChannel.id, + currency_code: "usd", + email: "test@admin.com", + items: [ + { + variant_id: product.variants[0].id, + quantity: 1, + }, + ], + }, + storeHeaders + ) ).data.cart }) describe("GET /admin/shipping-options?cart_id=", () => { it("should get all shipping options for a cart successfully", async () => { const resp = await api.get( - `/store/shipping-options?cart_id=${cart.id}` + `/store/shipping-options?cart_id=${cart.id}`, + storeHeaders ) const shippingOptions = resp.data.shipping_options diff --git a/packages/core/types/src/api-key/common/api-key.ts b/packages/core/types/src/api-key/common/api-key.ts index 45f7c74140..53878ebc66 100644 --- a/packages/core/types/src/api-key/common/api-key.ts +++ b/packages/core/types/src/api-key/common/api-key.ts @@ -1,4 +1,4 @@ -import { BaseFilterable } from "../../dal" +import { BaseFilterable, OperatorMap } from "../../dal" /** * An API key's type. @@ -90,4 +90,6 @@ export interface FilterableApiKeyProps * Filter the API keys by their type. */ type?: ApiKeyType + + revoked_at?: OperatorMap } diff --git a/packages/core/utils/src/api-key/api-key-type.ts b/packages/core/utils/src/api-key/api-key-type.ts index 9927c8ac47..7064ce6948 100644 --- a/packages/core/utils/src/api-key/api-key-type.ts +++ b/packages/core/utils/src/api-key/api-key-type.ts @@ -13,3 +13,5 @@ export enum ApiKeyType { */ SECRET = "secret", } + +export const PUBLISHABLE_KEY_HEADER = "x-publishable-api-key" diff --git a/packages/framework/framework/src/http/types.ts b/packages/framework/framework/src/http/types.ts index 50c4a35b94..62ef7a0037 100644 --- a/packages/framework/framework/src/http/types.ts +++ b/packages/framework/framework/src/http/types.ts @@ -1,5 +1,5 @@ -import { ZodObject } from "zod" import type { NextFunction, Request, Response } from "express" +import { ZodObject } from "zod" import { MedusaPricingContext, RequestQueryFields } from "@medusajs/types" import * as core from "express-serve-static-core" @@ -162,9 +162,20 @@ export interface AuthContext { app_metadata: Record } +export interface PublishableKeyContext { + key: string + sales_channel_ids: string[] +} + export interface AuthenticatedMedusaRequest extends MedusaRequest { auth_context: AuthContext + publishable_key_context?: PublishableKeyContext +} + +export interface MedusaStoreRequest extends MedusaRequest { + auth_context?: AuthContext + publishable_key_context: PublishableKeyContext } export type MedusaResponse = Response diff --git a/packages/medusa/src/api/middlewares.ts b/packages/medusa/src/api/middlewares.ts index 579cf184d2..d61f396d9e 100644 --- a/packages/medusa/src/api/middlewares.ts +++ b/packages/medusa/src/api/middlewares.ts @@ -48,6 +48,7 @@ import { storeCartRoutesMiddlewares } from "./store/carts/middlewares" import { storeCollectionRoutesMiddlewares } from "./store/collections/middlewares" import { storeCurrencyRoutesMiddlewares } from "./store/currencies/middlewares" import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares" +import { storeRoutesMiddlewares } from "./store/middlewares" import { storeOrderRoutesMiddlewares } from "./store/orders/middlewares" import { storePaymentCollectionsMiddlewares } from "./store/payment-collections/middlewares" import { storePaymentProvidersMiddlewares } from "./store/payment-providers/middlewares" @@ -58,6 +59,7 @@ import { storeReturnReasonRoutesMiddlewares } from "./store/return-reasons/middl import { storeShippingOptionRoutesMiddlewares } from "./store/shipping-options/middlewares" export default defineMiddlewares([ + ...storeRoutesMiddlewares, ...adminCustomerGroupRoutesMiddlewares, ...adminCustomerRoutesMiddlewares, ...adminPromotionRoutesMiddlewares, diff --git a/packages/medusa/src/api/store/middlewares.ts b/packages/medusa/src/api/store/middlewares.ts new file mode 100644 index 0000000000..81a46e4d13 --- /dev/null +++ b/packages/medusa/src/api/store/middlewares.ts @@ -0,0 +1,10 @@ +import { MiddlewareRoute } from "@medusajs/framework" +import { ensurePublishableApiKey } from "../../utils/middlewares/ensure-publishable-api-key" + +export const storeRoutesMiddlewares: MiddlewareRoute[] = [ + { + method: "ALL", + matcher: "/store*", + middlewares: [ensurePublishableApiKey()], + }, +] diff --git a/packages/medusa/src/api/utils/middlewares/products/filter-by-valid-sales-channels.ts b/packages/medusa/src/api/utils/middlewares/products/filter-by-valid-sales-channels.ts index 4227855d2b..18763f8075 100644 --- a/packages/medusa/src/api/utils/middlewares/products/filter-by-valid-sales-channels.ts +++ b/packages/medusa/src/api/utils/middlewares/products/filter-by-valid-sales-channels.ts @@ -1,77 +1,49 @@ -import { MedusaError } from "@medusajs/utils" +import { MedusaStoreRequest } from "@medusajs/framework" +import { arrayDifference, MedusaError } from "@medusajs/utils" import { NextFunction } from "express" -import { AuthenticatedMedusaRequest } from "../../../../types/routing" -import { refetchEntity } from "../../refetch-entity" -import { arrayDifference } from "@medusajs/utils" // Selection of sales channels happens in the following priority: // - If a publishable API key is passed, we take the sales channels attached to it and filter them down based on the query params // - If a sales channel id is passed through query params, we use that // - If not, we use the default sales channel for the store export function filterByValidSalesChannels() { - return async (req: AuthenticatedMedusaRequest, _, next: NextFunction) => { - const publishableApiKey = req.get("x-publishable-api-key") - const salesChannelIds = req.filterableFields.sales_channel_id as - | string[] - | undefined + return async (req: MedusaStoreRequest, _, next: NextFunction) => { + const idsFromRequest = req.filterableFields.sales_channel_id + const { sales_channel_ids: idsFromPublishableKey = [] } = + req.publishable_key_context - if (publishableApiKey) { - const apiKey = await refetchEntity( - "api_key", - { token: publishableApiKey }, - req.scope, - ["id", "sales_channels_link.sales_channel_id"] + // If all sales channel ids are not in the publishable key, we throw an error + if (Array.isArray(idsFromRequest) && idsFromRequest.length) { + const uniqueInParams = arrayDifference( + idsFromRequest, + idsFromPublishableKey ) - if (!apiKey) { + if (uniqueInParams.length) { return next( new MedusaError( MedusaError.Types.INVALID_DATA, - `Publishable API key not found` + `Requested sales channel is not part of the publishable key` ) ) } - let result = apiKey.sales_channels_link.map( - (link) => link.sales_channel_id - ) - if (salesChannelIds?.length) { - // If all sales channel ids are not in the publishable key, we throw an error - const uniqueInParams = arrayDifference(salesChannelIds, result) - if (uniqueInParams.length) { - return next( - new MedusaError( - MedusaError.Types.INVALID_DATA, - `Requested sales channel is not part of the publishable key mappings` - ) - ) - } - result = salesChannelIds - } + req.filterableFields.sales_channel_id = idsFromRequest - req.filterableFields.sales_channel_id = result return next() } - if (salesChannelIds?.length) { - req.filterableFields.sales_channel_id = salesChannelIds + if (idsFromPublishableKey?.length) { + req.filterableFields.sales_channel_id = idsFromPublishableKey + return next() } - const store = await refetchEntity("stores", {}, req.scope, [ - "default_sales_channel_id", - ]) - - if (!store) { - return next( - new MedusaError(MedusaError.Types.INVALID_DATA, `Store not found`) + return next( + new MedusaError( + MedusaError.Types.INVALID_DATA, + `Publishable key needs to have a sales channel configured` ) - } - - if (store.default_sales_channel_id) { - req.filterableFields.sales_channel_id = [store.default_sales_channel_id] - } - - return next() + ) } } diff --git a/packages/medusa/src/utils/middlewares/ensure-publishable-api-key.ts b/packages/medusa/src/utils/middlewares/ensure-publishable-api-key.ts new file mode 100644 index 0000000000..dd491ceae6 --- /dev/null +++ b/packages/medusa/src/utils/middlewares/ensure-publishable-api-key.ts @@ -0,0 +1,68 @@ +import { + MedusaNextFunction, + MedusaResponse, + MedusaStoreRequest, +} from "@medusajs/framework" +import { + ApiKeyType, + isPresent, + MedusaError, + PUBLISHABLE_KEY_HEADER, +} from "@medusajs/utils" +import { refetchEntity } from "../../api/utils/refetch-entity" + +export function ensurePublishableApiKey() { + return async ( + req: MedusaStoreRequest, + _res: MedusaResponse, + next: MedusaNextFunction + ) => { + const publishableApiKey = req.get("x-publishable-api-key") + + if (!isPresent(publishableApiKey)) { + try { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `Publishable API key required in the request header: ${PUBLISHABLE_KEY_HEADER}. You can manage your keys in settings in the dashboard.` + ) + } catch (e) { + return next(e) + } + } + + // TODO: Replace this with the fancy new gql fetch + const apiKey = await refetchEntity( + "api_key", + { + token: publishableApiKey, + type: ApiKeyType.PUBLISHABLE, + $or: [ + { revoked_at: { $eq: null } }, + { revoked_at: { $gt: new Date() } }, + ], + }, + req.scope, + ["id", "token", "sales_channels_link.sales_channel_id"] + ) + + if (!apiKey) { + try { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `A valid publishable key is required to proceed with the request` + ) + } catch (e) { + return next(e) + } + } + + req.publishable_key_context = { + key: apiKey.token, + sales_channel_ids: apiKey.sales_channels_link.map( + (link) => link.sales_channel_id + ), + } + + return next() + } +}