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
This commit is contained in:
Riqwan Thamir authored and GitHub committed 2024-09-11 15:08:37 +02:00
1 parent fdd0543011
commit a729fb3fbb
29 files changed
+1037 -464

No files matched your search

+37 -2
View File
@@ -1,5 +1,15 @@
import { IAuthModuleService, IUserModuleService } from "@medusajs/types" import {
import { ModuleRegistrationName } from "@medusajs/utils" ApiKeyDTO,
IApiKeyModuleService,
IAuthModuleService,
IUserModuleService,
MedusaContainer,
} from "@medusajs/types"
import {
ApiKeyType,
ModuleRegistrationName,
PUBLISHABLE_KEY_HEADER,
} from "@medusajs/utils"
import jwt from "jsonwebtoken" import jwt from "jsonwebtoken"
import Scrypt from "scrypt-kdf" import Scrypt from "scrypt-kdf"
import { getContainer } from "../environment-helpers/use-container" import { getContainer } from "../environment-helpers/use-container"
@@ -61,3 +71,28 @@ export const createAdminUser = async (
return { user, authIdentity } return { user, authIdentity }
} }
export const generatePublishableKey = async (container?: MedusaContainer) => {
const appContainer = container ?? getContainer()!
const apiKeyModule = appContainer.resolve<IApiKeyModuleService>(
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,
},
}
}
@@ -1,7 +1,9 @@
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
createAdminUser,
adminHeaders, adminHeaders,
createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -12,9 +14,12 @@ medusaIntegrationTestRunner({
let baseCollection let baseCollection
let baseCollection1 let baseCollection1
let baseCollection2 let baseCollection2
let storeHeaders
beforeEach(async () => { beforeEach(async () => {
const container = getContainer() const container = getContainer()
const publishableKey = await generatePublishableKey(container)
storeHeaders = generateStoreHeaders({ publishableKey })
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
baseCollection = ( baseCollection = (
@@ -46,7 +51,8 @@ medusaIntegrationTestRunner({
describe("/store/collections/:id", () => { describe("/store/collections/:id", () => {
it("gets collection", async () => { it("gets collection", async () => {
const response = await api.get( const response = await api.get(
`/store/collections/${baseCollection.id}` `/store/collections/${baseCollection.id}`,
storeHeaders
) )
expect(response.data.collection).toEqual( expect(response.data.collection).toEqual(
@@ -61,7 +67,7 @@ medusaIntegrationTestRunner({
describe("/store/collections", () => { describe("/store/collections", () => {
it("lists collections", async () => { it("lists collections", async () => {
const response = await api.get("/store/collections") const response = await api.get("/store/collections", storeHeaders)
expect(response.data).toEqual({ expect(response.data).toEqual({
collections: [ collections: [
@@ -5,6 +5,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -17,9 +19,13 @@ medusaIntegrationTestRunner({
let customer4 let customer4
let customer5 let customer5
let container let container
let storeHeaders
beforeEach(async () => { beforeEach(async () => {
container = getContainer() container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
const publishableKey = await generatePublishableKey(container)
storeHeaders = generateStoreHeaders({ publishableKey })
customer1 = ( customer1 = (
await api.post( await api.post(
@@ -415,6 +421,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
Authorization: `Bearer ${registeredCustomerToken}`, Authorization: `Bearer ${registeredCustomerToken}`,
...storeHeaders.headers,
}, },
} }
) )
@@ -3,6 +3,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -10,20 +12,27 @@ jest.setTimeout(30000)
medusaIntegrationTestRunner({ medusaIntegrationTestRunner({
testSuite: ({ dbConnection, api, getContainer }) => { testSuite: ({ dbConnection, api, getContainer }) => {
let appContainer: MedusaContainer let appContainer: MedusaContainer
let storeHeaders
beforeEach(async () => { beforeEach(async () => {
appContainer = getContainer() appContainer = getContainer()
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
}) })
describe("POST /admin/customers", () => { describe("POST /store/customers", () => {
it("should fails to create a customer without an identity", async () => { it("should fails to create a customer without an identity", async () => {
const customer = await api const customer = await api
.post("/store/customers", { .post(
email: "newcustomer@medusa.js", "/store/customers",
first_name: "John", {
last_name: "Doe", email: "newcustomer@medusa.js",
}) first_name: "John",
last_name: "Doe",
},
storeHeaders
)
.catch((e) => e) .catch((e) => e)
expect(customer.response.status).toEqual(401) expect(customer.response.status).toEqual(401)
@@ -48,6 +57,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
authorization: `Bearer ${signup.data.token}`, authorization: `Bearer ${signup.data.token}`,
...storeHeaders.headers,
}, },
} }
) )
@@ -102,6 +112,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
authorization: `Bearer ${signup.data.token}`, authorization: `Bearer ${signup.data.token}`,
...storeHeaders.headers,
}, },
} }
) )
@@ -161,6 +172,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
authorization: `Bearer ${firstSignup.data.token}`, authorization: `Bearer ${firstSignup.data.token}`,
...storeHeaders.headers,
}, },
} }
) )
@@ -181,6 +193,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
authorization: `Bearer ${firstSignin.data.token}`, authorization: `Bearer ${firstSignin.data.token}`,
...storeHeaders.headers,
}, },
} }
) )
@@ -191,6 +204,54 @@ medusaIntegrationTestRunner({
"Request already authenticated as a customer." "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",
})
})
})
}) })
}, },
}) })
@@ -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 = ( const region = (
await api.post( await api.post(
"/admin/regions", "/admin/regions",
@@ -145,36 +152,46 @@ export async function createOrderSeeder({ api }) {
).data.shipping_option ).data.shipping_option
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
region_id: region.id, currency_code: "usd",
shipping_address: { email: "tony@stark-industries.com",
address_1: "test address 1", region_id: region.id,
address_2: "test address 2", shipping_address: {
city: "ny", address_1: "test address 1",
country_code: "us", address_2: "test address 2",
province: "ny", city: "ny",
postal_code: "94016", 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, storeHeaders
items: [{ quantity: 1, variant_id: product.variants[0].id }], )
})
).data.cart ).data.cart
const paymentCollection = ( const paymentCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
await api.post( await api.post(
`/store/payment-collections/${paymentCollection.id}/payment-sessions`, `/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 let order = (
.order await api.post(`/store/carts/${cart.id}/complete`, {}, storeHeaders)
).data.order
order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data.order order = (await api.get(`/admin/orders/${order.id}`, adminHeaders)).data.order
@@ -28,7 +28,7 @@ medusaIntegrationTestRunner({
await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX)) await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX))
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
order = await createOrderSeeder({ api }) order = await createOrderSeeder({ api, container })
shippingProfile = ( shippingProfile = (
await api.post( await api.post(
@@ -2,6 +2,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
import { getProductFixture } from "../../../../helpers/fixtures" import { getProductFixture } from "../../../../helpers/fixtures"
@@ -9,10 +11,12 @@ jest.setTimeout(30000)
medusaIntegrationTestRunner({ medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => { testSuite: ({ dbConnection, getContainer, api }) => {
beforeAll(() => {}) let storeHeaders
beforeEach(async () => { beforeEach(async () => {
const container = getContainer() const container = getContainer()
const publishableKey = await generatePublishableKey(container)
storeHeaders = generateStoreHeaders({ publishableKey })
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
}) })
@@ -55,23 +59,32 @@ medusaIntegrationTestRunner({
).data.product ).data.product
cart = ( cart = (
await api.post("/store/carts", { await api.post(
region_id: region.id, "/store/carts",
items: [{ variant_id: product.variants[0].id, quantity: 1 }], {
}) region_id: region.id,
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
},
storeHeaders
)
).data.cart ).data.cart
}) })
it("should create a payment session", async () => { it("should create a payment session", async () => {
const paymentCollection = ( const paymentCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
await api.post( await api.post(
`/store/payment-collections/${paymentCollection.id}/payment-sessions`, `/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 // Adding a second payment session to ensure only one session gets created
@@ -79,7 +92,8 @@ medusaIntegrationTestRunner({
data: { payment_collection }, data: { payment_collection },
} = await api.post( } = await api.post(
`/store/payment-collections/${paymentCollection.id}/payment-sessions`, `/store/payment-collections/${paymentCollection.id}/payment-sessions`,
{ provider_id: "pp_system_default" } { provider_id: "pp_system_default" },
storeHeaders
) )
expect(payment_collection.payment_sessions).toEqual([ expect(payment_collection.payment_sessions).toEqual([
@@ -37,7 +37,7 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
container = getContainer() container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
order = await createOrderSeeder({ api }) order = await createOrderSeeder({ api, container })
await api.post( await api.post(
`/admin/orders/${order.id}/fulfillments`, `/admin/orders/${order.id}/fulfillments`,
@@ -8,6 +8,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
import { getProductFixture } from "../../../../helpers/fixtures" import { getProductFixture } from "../../../../helpers/fixtures"
@@ -30,6 +32,8 @@ medusaIntegrationTestRunner({
let variant4 let variant4
let inventoryItem1 let inventoryItem1
let inventoryItem2 let inventoryItem2
let storeHeaders
let publishableKey
const createProducts = async (data) => { const createProducts = async (data) => {
const response = await api.post( const response = await api.post(
@@ -82,6 +86,8 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
appContainer = getContainer() appContainer = getContainer()
publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
const storeModule: IStoreModuleService = appContainer.resolve( const storeModule: IStoreModuleService = appContainer.resolve(
@@ -104,7 +110,6 @@ medusaIntegrationTestRunner({
}) })
describe("Get products based on publishable key", () => { describe("Get products based on publishable key", () => {
let pubKey1
let salesChannel1 let salesChannel1
let salesChannel2 let salesChannel2
@@ -133,14 +138,6 @@ medusaIntegrationTestRunner({
) )
).data.product ).data.product
pubKey1 = (
await api.post(
"/admin/api-keys",
{ title: "sample key", type: "publishable" },
adminHeaders
)
).data.api_key
salesChannel1 = ( salesChannel1 = (
await api.post( await api.post(
"/admin/sales-channels", "/admin/sales-channels",
@@ -184,7 +181,7 @@ medusaIntegrationTestRunner({
it("returns products from a specific channel associated with a publishable key", async () => { it("returns products from a specific channel associated with a publishable key", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id], add: [salesChannel1.id],
}, },
@@ -194,7 +191,7 @@ medusaIntegrationTestRunner({
const response = await api.get(`/store/products`, { const response = await api.get(`/store/products`, {
headers: { headers: {
...adminHeaders.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 () => { it("returns products from multiples sales channels associated with a publishable key", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id, salesChannel2.id], add: [salesChannel1.id, salesChannel2.id],
}, },
@@ -220,7 +217,7 @@ medusaIntegrationTestRunner({
const response = await api.get(`/store/products`, { const response = await api.get(`/store/products`, {
headers: { headers: {
...adminHeaders.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 () => { it("SC param overrides PK channels (but SK still needs to be in the PK's scope", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id, salesChannel2.id], add: [salesChannel1.id, salesChannel2.id],
}, },
@@ -251,7 +248,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
...adminHeaders.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 // 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 () => { it.skip("returns all products if passed PK doesn't have associated channels", async () => {
const response = await api.get(`/store/products`, { const response = await api.get(`/store/products`, {
headers: { headers: {
...adminHeaders.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 () => { it("throws because sales channel param is not in the scope of passed PK", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id], add: [salesChannel1.id],
}, },
@@ -333,20 +301,20 @@ medusaIntegrationTestRunner({
.get(`/store/products?sales_channel_id[]=${salesChannel2.id}`, { .get(`/store/products?sales_channel_id[]=${salesChannel2.id}`, {
headers: { headers: {
...adminHeaders.headers, ...adminHeaders.headers,
"x-publishable-api-key": pubKey1.token, "x-publishable-api-key": publishableKey.token,
}, },
}) })
.catch((e) => e) .catch((e) => e)
expect(err.response.status).toEqual(400) expect(err.response.status).toEqual(400)
expect(err.response.data.message).toEqual( 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 () => { it("retrieve a product from a specific channel associated with a publishable key", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id], add: [salesChannel1.id],
}, },
@@ -356,7 +324,7 @@ medusaIntegrationTestRunner({
const response = await api.get(`/store/products/${product1.id}`, { const response = await api.get(`/store/products/${product1.id}`, {
headers: { headers: {
...adminHeaders.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. // 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 () => { it("return 404 because requested product is not in the SC associated with a publishable key", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id], add: [salesChannel1.id],
}, },
@@ -381,7 +349,7 @@ medusaIntegrationTestRunner({
.get(`/store/products/${product2.id}`, { .get(`/store/products/${product2.id}`, {
headers: { headers: {
...adminHeaders.headers, ...adminHeaders.headers,
"x-publishable-api-key": pubKey1.token, "x-publishable-api-key": publishableKey.token,
}, },
}) })
.catch((e) => e) .catch((e) => e)
@@ -392,7 +360,7 @@ medusaIntegrationTestRunner({
// TODO: Add variant endpoints to the store API (if that is what we want) // 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 () => { it.skip("should return 404 when the requested variant doesn't exist", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id], add: [salesChannel1.id],
}, },
@@ -403,7 +371,7 @@ medusaIntegrationTestRunner({
.get(`/store/variants/does-not-exist`, { .get(`/store/variants/does-not-exist`, {
headers: { headers: {
...adminHeaders.headers, ...adminHeaders.headers,
"x-publishable-api-key": pubKey1.token, "x-publishable-api-key": publishableKey.token,
}, },
}) })
.catch((err) => { .catch((err) => {
@@ -418,7 +386,7 @@ medusaIntegrationTestRunner({
it("should return 404 when the requested product doesn't exist", async () => { it("should return 404 when the requested product doesn't exist", async () => {
await api.post( await api.post(
`/admin/api-keys/${pubKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey.id}/sales-channels`,
{ {
add: [salesChannel1.id], add: [salesChannel1.id],
}, },
@@ -429,7 +397,7 @@ medusaIntegrationTestRunner({
.get(`/store/products/does-not-exist`, { .get(`/store/products/does-not-exist`, {
headers: { headers: {
...adminHeaders.headers, ...adminHeaders.headers,
"x-publishable-api-key": pubKey1.token, "x-publishable-api-key": publishableKey.token,
}, },
}) })
.catch((err) => { .catch((err) => {
@@ -448,7 +416,7 @@ medusaIntegrationTestRunner({
.get(`/store/products/${product1.id}`, { .get(`/store/products/${product1.id}`, {
headers: { headers: {
...adminHeaders.headers, ...adminHeaders.headers,
"x-publishable-api-key": pubKey1.token, "x-publishable-api-key": publishableKey.token,
}, },
}) })
.catch((err) => { .catch((err) => {
@@ -461,7 +429,7 @@ medusaIntegrationTestRunner({
.get(`/store/products/${product2.id}`, { .get(`/store/products/${product2.id}`, {
headers: { headers: {
...adminHeaders.headers, ...adminHeaders.headers,
"x-publishable-api-key": pubKey1.token, "x-publishable-api-key": publishableKey.token,
}, },
}) })
.catch((err) => { .catch((err) => {
@@ -566,6 +534,12 @@ medusaIntegrationTestRunner({
[product.id, product2.id, product3.id, product4.id] [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 service = appContainer.resolve(ModuleRegistrationName.STORE)
const [store] = await service.listStores() const [store] = await service.listStores()
@@ -583,7 +557,7 @@ medusaIntegrationTestRunner({
}) })
it("should list all published products", async () => { 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.status).toEqual(200)
expect(response.data.count).toEqual(3) 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.status).toEqual(200)
expect(response.data.count).toEqual(1) expect(response.data.count).toEqual(1)
@@ -618,8 +592,15 @@ medusaIntegrationTestRunner({
[product.id] [product.id]
) )
await api.post(
`/admin/api-keys/${publishableKey.id}/sales-channels`,
{ add: [salesChannel.id] },
adminHeaders
)
let response = await api.get( 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) expect(response.status).toEqual(200)
@@ -643,7 +624,8 @@ medusaIntegrationTestRunner({
) )
const response = await api.get( 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) expect(response.status).toEqual(200)
@@ -656,7 +638,7 @@ medusaIntegrationTestRunner({
}) })
it("returns a list of ordered products by id ASC", async () => { 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.status).toEqual(200)
expect(response.data.products).toEqual( expect(response.data.products).toEqual(
[product.id, product2.id, product3.id] [product.id, product2.id, product3.id]
@@ -666,7 +648,10 @@ medusaIntegrationTestRunner({
}) })
it("returns a list of ordered products by id DESC", async () => { 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.status).toEqual(200)
expect(response.data.products).toEqual( expect(response.data.products).toEqual(
@@ -678,7 +663,10 @@ medusaIntegrationTestRunner({
// TODO: This doesn't work currently, but worked in v1 // TODO: This doesn't work currently, but worked in v1
it.skip("returns a list of ordered products by variants title DESC", async () => { 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.status).toEqual(200)
expect(response.data.products).toEqual([ expect(response.data.products).toEqual([
@@ -690,7 +678,10 @@ medusaIntegrationTestRunner({
// TODO: This doesn't work currently, but worked in v1 // TODO: This doesn't work currently, but worked in v1
it.skip("returns a list of ordered products by variants title ASC", async () => { 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.status).toEqual(200)
expect(response.data.products).toEqual([ expect(response.data.products).toEqual([
@@ -703,7 +694,8 @@ medusaIntegrationTestRunner({
// TODO: This doesn't work currently, but worked in v1 // TODO: This doesn't work currently, but worked in v1
it.skip("returns a list of ordered products by variants prices DESC", async () => { it.skip("returns a list of ordered products by variants prices DESC", async () => {
let response = await api.get( 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.skip("returns a list of ordered products by variants prices ASC", async () => {})
it("products contain only fields defined with `fields` param", 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(response.status).toEqual(200)
expect(Object.keys(response.data.products[0])).toEqual(["handle", "id"]) expect(Object.keys(response.data.products[0])).toEqual(["handle", "id"])
}) })
it("returns a list of products in collection", async () => { it("returns a list of products in collection", async () => {
const response = await api.get( const response = await api.get(
`/store/products?collection_id[]=${collection.id}` `/store/products?collection_id[]=${collection.id}`,
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -730,7 +726,8 @@ medusaIntegrationTestRunner({
it("returns a list of products with a given tag", async () => { it("returns a list of products with a given tag", async () => {
const response = await api.get( 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) expect(response.status).toEqual(200)
@@ -743,7 +740,7 @@ medusaIntegrationTestRunner({
// TODO: Not implemented yet // TODO: Not implemented yet
it.skip("returns gift card product", async () => { it.skip("returns gift card product", async () => {
const response = await api const response = await api
.get("/store/products?is_giftcard=true") .get("/store/products?is_giftcard=true", storeHeaders)
.catch((err) => { .catch((err) => {
console.log(err) console.log(err)
}) })
@@ -752,7 +749,7 @@ medusaIntegrationTestRunner({
// TODO: Not implemented yet // TODO: Not implemented yet
it.skip("returns non gift card products", async () => { it.skip("returns non gift card products", async () => {
const response = await api const response = await api
.get("/store/products?is_giftcard=false") .get("/store/products?is_giftcard=false", storeHeaders)
.catch((err) => { .catch((err) => {
console.log(err) console.log(err)
}) })
@@ -760,7 +757,8 @@ medusaIntegrationTestRunner({
it("returns a list of products in with a given handle", async () => { it("returns a list of products in with a given handle", async () => {
const response = await api.get( const response = await api.get(
`/store/products?handle=${product.handle}` `/store/products?handle=${product.handle}`,
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -773,7 +771,8 @@ medusaIntegrationTestRunner({
it("returns a list of products filtered by variant options", async () => { it("returns a list of products filtered by variant options", async () => {
const option = product.options.find((o) => o.title === "size") const option = product.options.find((o) => o.title === "size")
const response = await api.get( 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) expect(response.status).toEqual(200)
@@ -807,6 +806,12 @@ medusaIntegrationTestRunner({
publishableKey1 = api1Res.data.api_key publishableKey1 = api1Res.data.api_key
await api.post(
`/admin/api-keys/${publishableKey.id}/sales-channels`,
{ add: [salesChannel1.id, salesChannel2.id] },
adminHeaders
)
await api.post( await api.post(
`/admin/api-keys/${publishableKey1.id}/sales-channels`, `/admin/api-keys/${publishableKey1.id}/sales-channels`,
{ add: [salesChannel1.id] }, { add: [salesChannel1.id] },
@@ -830,7 +835,10 @@ medusaIntegrationTestRunner({
}) })
it("should list products by id", async () => { 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.status).toEqual(200)
expect(response.data.count).toEqual(1) expect(response.data.count).toEqual(1)
@@ -850,8 +858,8 @@ medusaIntegrationTestRunner({
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
expect(error.response.data).toEqual({ expect(error.response.data).toEqual({
message: `Publishable API key not found`, message: `A valid publishable key is required to proceed with the request`,
type: "invalid_data", type: "not_allowed",
}) })
}) })
@@ -864,7 +872,7 @@ medusaIntegrationTestRunner({
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
expect(error.response.data).toEqual({ 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", type: "invalid_data",
}) })
}) })
@@ -878,7 +886,7 @@ medusaIntegrationTestRunner({
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
expect(error.response.data).toEqual({ 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", type: "invalid_data",
}) })
}) })
@@ -886,7 +894,10 @@ medusaIntegrationTestRunner({
it("should throw error when calculating prices without context", async () => { it("should throw error when calculating prices without context", async () => {
let error = await api let error = await api
.get(`/store/products?fields=*variants.calculated_price`) .get(
`/store/products?fields=*variants.calculated_price`,
storeHeaders
)
.catch((e) => e) .catch((e) => e)
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
@@ -907,7 +918,8 @@ medusaIntegrationTestRunner({
).data.region ).data.region
let response = await api.get( let response = await api.get(
`/store/products?fields=*variants.calculated_price&region_id=${region.id}` `/store/products?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeaders
) )
const expectation = expect.arrayContaining([ const expectation = expect.arrayContaining([
@@ -957,7 +969,10 @@ medusaIntegrationTestRunner({
expect(response.data.products).toEqual(expectation) expect(response.data.products).toEqual(expectation)
// with only region_id // 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.status).toEqual(200)
expect(response.data.products).toEqual(expectation) expect(response.data.products).toEqual(expectation)
@@ -1137,6 +1152,12 @@ medusaIntegrationTestRunner({
[product.id] [product.id]
) )
await api.post(
`/admin/api-keys/${publishableKey.id}/sales-channels`,
{ add: [defaultSalesChannel.id] },
adminHeaders
)
const service = appContainer.resolve(ModuleRegistrationName.STORE) const service = appContainer.resolve(ModuleRegistrationName.STORE)
const [store] = await service.listStores() const [store] = await service.listStores()
@@ -1154,7 +1175,10 @@ medusaIntegrationTestRunner({
}) })
it("should retrieve product successfully", async () => { 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.status).toEqual(200)
expect(response.data.product).toEqual( expect(response.data.product).toEqual(
@@ -1185,7 +1209,8 @@ medusaIntegrationTestRunner({
) )
const response = await api.get( const response = await api.get(
`/store/products/${product.id}?fields=*categories` `/store/products/${product.id}?fields=*categories`,
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -1200,7 +1225,8 @@ medusaIntegrationTestRunner({
it("should throw error when calculating prices without context", async () => { it("should throw error when calculating prices without context", async () => {
let error = await api let error = await api
.get( .get(
`/store/products/${product.id}?fields=*variants.calculated_price` `/store/products/${product.id}?fields=*variants.calculated_price`,
storeHeaders
) )
.catch((e) => e) .catch((e) => e)
@@ -1222,7 +1248,8 @@ medusaIntegrationTestRunner({
).data.region ).data.region
let response = await api.get( let response = await api.get(
`/store/products/${product.id}?fields=*variants.calculated_price&region_id=${region.id}` `/store/products/${product.id}?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeaders
) )
const expectation = expect.objectContaining({ const expectation = expect.objectContaining({
@@ -1270,7 +1297,8 @@ medusaIntegrationTestRunner({
// with only region_id // with only region_id
response = await api.get( 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) expect(response.status).toEqual(200)
@@ -1285,12 +1313,30 @@ medusaIntegrationTestRunner({
let euCart let euCart
beforeEach(async () => { 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 const store = (await api.get("/admin/stores", adminHeaders)).data
.stores[0] .stores[0]
if (store) { if (store) {
await api.post( await api.post(
`/admin/stores/${store.id}`, `/admin/stores/${store.id}`,
{ {
default_sales_channel_id: salesChannel.id,
supported_currencies: [ supported_currencies: [
{ {
currency_code: "usd", currency_code: "usd",
@@ -1307,6 +1353,7 @@ medusaIntegrationTestRunner({
await api.post( await api.post(
"/admin/stores", "/admin/stores",
{ {
default_sales_channel_id: salesChannel.id,
name: "Test store", name: "Test store",
supported_currencies: [ supported_currencies: [
{ {
@@ -1400,13 +1447,27 @@ medusaIntegrationTestRunner({
product2 = ( product2 = (
await api.post( await api.post(
"/admin/products", "/admin/products",
getProductFixture({ title: "test2", status: "published" }), getProductFixture({
title: "test2",
status: "published",
}),
adminHeaders adminHeaders
) )
).data.product ).data.product
euCart = (await api.post("/store/carts", { region_id: euRegion.id })) await api.post(
.data.cart `/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( await api.post(
`/admin/tax-regions`, `/admin/tax-regions`,
@@ -1451,7 +1512,8 @@ medusaIntegrationTestRunner({
it("should not return tax pricing if the context is not sufficient when listing products", async () => { it("should not return tax pricing if the context is not sufficient when listing products", async () => {
const products = ( const products = (
await api.get( await api.get(
`/store/products?fields=id,*variants.calculated_price&region_id=${usRegion.id}` `/store/products?fields=id,*variants.calculated_price&region_id=${usRegion.id}`,
storeHeaders
) )
).data.products ).data.products
@@ -1467,7 +1529,8 @@ medusaIntegrationTestRunner({
it("should not return tax pricing if automatic taxes are off when listing products", async () => { it("should not return tax pricing if automatic taxes are off when listing products", async () => {
const products = ( const products = (
await api.get( await api.get(
`/store/products?fields=id,*variants.calculated_price&region_id=${usRegion.id}&country_code=us` `/store/products?fields=id,*variants.calculated_price&region_id=${usRegion.id}&country_code=us`,
storeHeaders
) )
).data.products ).data.products
@@ -1483,7 +1546,8 @@ medusaIntegrationTestRunner({
it("should return prices with and without tax for a tax inclusive region when listing products", async () => { it("should return prices with and without tax for a tax inclusive region when listing products", async () => {
const products = ( const products = (
await api.get( await api.get(
`/store/products?fields=id,*variants.calculated_price&region_id=${euRegion.id}&country_code=it` `/store/products?fields=id,*variants.calculated_price&region_id=${euRegion.id}&country_code=it`,
storeHeaders
) )
).data.products ).data.products
@@ -1524,7 +1588,8 @@ medusaIntegrationTestRunner({
it("should return prices with and without tax for a tax exclusive region when listing products", async () => { it("should return prices with and without tax for a tax exclusive region when listing products", async () => {
const products = ( const products = (
await api.get( await api.get(
`/store/products?fields=id,*variants.calculated_price&region_id=${dkRegion.id}&country_code=dk` `/store/products?fields=id,*variants.calculated_price&region_id=${dkRegion.id}&country_code=dk`,
storeHeaders
) )
).data.products ).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 () => { it("should return prices with and without tax when the cart is available and a country is passed when listing products", async () => {
const products = ( const products = (
await api.get( 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 ).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 () => { 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}`, { await api.post(
shipping_address: { `/store/carts/${euCart.id}`,
country_code: "it", {
shipping_address: {
country_code: "it",
},
}, },
}) storeHeaders
)
const products = ( const products = (
await api.get( 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 ).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 () => { it("should not return tax pricing if the context is not sufficient when fetching a single product", async () => {
const product = ( const product = (
await api.get( await api.get(
`/store/products/${product1.id}?fields=id,*variants.calculated_price&region_id=${usRegion.id}` `/store/products/${product1.id}?fields=id,*variants.calculated_price&region_id=${usRegion.id}`,
storeHeaders
) )
).data.product ).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 () => { it("should return prices with and without tax for a tax inclusive region when fetching a single product", async () => {
const product = ( const product = (
await api.get( await api.get(
`/store/products/${product1.id}?fields=id,*variants.calculated_price&region_id=${euRegion.id}&country_code=it` `/store/products/${product1.id}?fields=id,*variants.calculated_price&region_id=${euRegion.id}&country_code=it`,
storeHeaders
) )
).data.product ).data.product
@@ -3,6 +3,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -12,10 +14,13 @@ medusaIntegrationTestRunner({
let region1 let region1
let region2 let region2
let container let container
let storeHeaders
beforeEach(async () => { beforeEach(async () => {
container = getContainer() container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container) await createAdminUser(dbConnection, adminHeaders, container)
const publishableKey = await generatePublishableKey(container)
storeHeaders = generateStoreHeaders({ publishableKey })
region1 = ( region1 = (
await api.post( await api.post(
@@ -111,7 +116,8 @@ medusaIntegrationTestRunner({
) )
let response = await api.get( 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) expect(response.status).toEqual(200)
@@ -125,7 +131,8 @@ medusaIntegrationTestRunner({
]) ])
response = await api.get( response = await api.get(
`/store/regions/${region1.id}?fields=*payment_providers` `/store/regions/${region1.id}?fields=*payment_providers`,
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -10,6 +10,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -24,6 +26,7 @@ medusaIntegrationTestRunner({
let cartModuleService: ICartModuleService let cartModuleService: ICartModuleService
let promotionModuleService: IPromotionModuleService let promotionModuleService: IPromotionModuleService
let remoteLinkService: RemoteLink let remoteLinkService: RemoteLink
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -38,6 +41,8 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
}) })
describe("POST /store/carts/:id/promotions", () => { describe("POST /store/carts/:id/promotions", () => {
@@ -123,9 +128,11 @@ medusaIntegrationTestRunner({
[Modules.PROMOTION]: { promotion_id: appliedPromotion.id }, [Modules.PROMOTION]: { promotion_id: appliedPromotion.id },
}) })
const created = await api.post(`/store/carts/${cart.id}/promotions`, { const created = await api.post(
promo_codes: [createdPromotion.code], `/store/carts/${cart.id}/promotions`,
}) { promo_codes: [createdPromotion.code] },
storeHeaders
)
expect(created.status).toEqual(200) expect(created.status).toEqual(200)
expect(created.data.cart).toEqual( expect(created.data.cart).toEqual(
@@ -264,9 +271,11 @@ medusaIntegrationTestRunner({
}, },
]) ])
const created = await api.post(`/store/carts/${cart.id}/promotions`, { const created = await api.post(
promo_codes: [newPromotion.code], `/store/carts/${cart.id}/promotions`,
}) { promo_codes: [newPromotion.code] },
storeHeaders
)
expect(created.status).toEqual(200) expect(created.status).toEqual(200)
expect(created.data.cart).toEqual( expect(created.data.cart).toEqual(
@@ -25,7 +25,11 @@ import {
RuleOperator, RuleOperator,
} from "@medusajs/utils" } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-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 { seedStorefrontDefaults } from "../../../../helpers/seed-storefront-defaults"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
import { setupTaxStructure } from "../../fixtures" import { setupTaxStructure } from "../../fixtures"
@@ -58,6 +62,7 @@ medusaIntegrationTestRunner({
let region let region
let store let store
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -84,6 +89,8 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
const { store: defaultStore } = await seedStorefrontDefaults( const { store: defaultStore } = await seedStorefrontDefaults(
appContainer, appContainer,
@@ -158,22 +165,26 @@ medusaIntegrationTestRunner({
}, },
]) ])
const created = await api.post(`/store/carts`, { const created = await api.post(
email: "tony@stark.com", `/store/carts`,
currency_code: "usd", {
region_id: region.id, email: "tony@stark.com",
sales_channel_id: salesChannel.id, currency_code: "usd",
items: [ region_id: region.id,
{ sales_channel_id: salesChannel.id,
variant_id: product.variants[0].id, items: [
quantity: 1, {
}, variant_id: product.variants[0].id,
{ quantity: 1,
variant_id: product.variants[1].id, },
quantity: 2, {
}, variant_id: product.variants[1].id,
], quantity: 2,
}) },
],
},
storeHeaders
)
expect(created.status).toEqual(200) expect(created.status).toEqual(200)
expect(created.data.cart).toEqual( expect(created.data.cart).toEqual(
@@ -230,25 +241,29 @@ medusaIntegrationTestRunner({
}, },
]) ])
const created = await api.post(`/store/carts`, { const created = await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
shipping_address: { currency_code: "usd",
address_1: "test address 1", email: "tony@stark-industries.com",
address_2: "test address 2", shipping_address: {
city: "NY", address_1: "test address 1",
country_code: "US", address_2: "test address 2",
province: "NY", city: "NY",
postal_code: "94016", country_code: "US",
}, province: "NY",
sales_channel_id: salesChannel.id, postal_code: "94016",
items: [
{
quantity: 1,
variant_id: product.variants[0].id,
}, },
], sales_channel_id: salesChannel.id,
}) items: [
{
quantity: 1,
variant_id: product.variants[0].id,
},
],
},
storeHeaders
)
expect(created.status).toEqual(200) expect(created.status).toEqual(200)
expect(created.data.cart).toEqual( expect(created.data.cart).toEqual(
@@ -283,10 +298,14 @@ medusaIntegrationTestRunner({
currency_code: "usd", currency_code: "usd",
}) })
const response = await api.post(`/store/carts`, { const response = await api.post(
email: "tony@stark.com", `/store/carts`,
currency_code: "usd", {
}) email: "tony@stark.com",
currency_code: "usd",
},
storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -310,10 +329,14 @@ medusaIntegrationTestRunner({
default_sales_channel_id: sc.id, default_sales_channel_id: sc.id,
}) })
const response = await api.post(`/store/carts`, { const response = await api.post(
email: "tony@stark.com", `/store/carts`,
currency_code: "usd", {
}) email: "tony@stark.com",
currency_code: "usd",
},
storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -332,10 +355,14 @@ medusaIntegrationTestRunner({
currency_code: "usd", currency_code: "usd",
}) })
const response = await api.post(`/store/carts`, { const response = await api.post(
email: "tony@stark.com", `/store/carts`,
region_id: region.id, {
}) email: "tony@stark.com",
region_id: region.id,
},
storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -359,7 +386,10 @@ medusaIntegrationTestRunner({
`/store/carts`, `/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 () => { it("should respond 400 bad request on unknown props", async () => {
await expect( await expect(
api.post(`/store/carts`, { api.post(`/store/carts`, { foo: "bar" }, storeHeaders)
foo: "bar",
})
).rejects.toThrow() ).rejects.toThrow()
}) })
}) })
@@ -624,9 +652,11 @@ medusaIntegrationTestRunner({
]) ])
// Should remove earlier adjustments from other promocodes // Should remove earlier adjustments from other promocodes
let updated = await api.post(`/store/carts/${cart.id}`, { let updated = await api.post(
promo_codes: [createdPromotion.code], `/store/carts/${cart.id}`,
}) { promo_codes: [createdPromotion.code] },
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -654,9 +684,11 @@ medusaIntegrationTestRunner({
}) })
) )
// Should remove all adjustments from other promo codes // Should remove all adjustments from other promo codes
updated = await api.post(`/store/carts/${cart.id}`, { updated = await api.post(
promo_codes: [], `/store/carts/${cart.id}`,
}) { promo_codes: [] },
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -711,9 +743,11 @@ medusaIntegrationTestRunner({
], ],
}) })
let updated = await api.post(`/store/carts/${cart.id}`, { let updated = await api.post(
email: "another@tax.com", `/store/carts/${cart.id}`,
}) { email: "another@tax.com" },
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -733,9 +767,11 @@ medusaIntegrationTestRunner({
region_id: region.id, region_id: region.id,
}) })
updated = await api.post(`/store/carts/${cart.id}`, { updated = await api.post(
email: "another@tax.com", `/store/carts/${cart.id}`,
}) { email: "another@tax.com" },
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -860,11 +896,15 @@ medusaIntegrationTestRunner({
}, },
]) ])
let updated = await api.post(`/store/carts/${cart.id}`, { let updated = await api.post(
region_id: region.id, `/store/carts/${cart.id}`,
email: "tony@stark.com", {
sales_channel_id: salesChannel.id, region_id: region.id,
}) email: "tony@stark.com",
sales_channel_id: salesChannel.id,
},
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -930,10 +970,14 @@ medusaIntegrationTestRunner({
}) })
) )
updated = await api.post(`/store/carts/${cart.id}`, { updated = await api.post(
email: null, `/store/carts/${cart.id}`,
sales_channel_id: null, {
}) email: null,
sales_channel_id: null,
},
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -1063,9 +1107,11 @@ medusaIntegrationTestRunner({
}, },
]) ])
let updated = await api.post(`/store/carts/${cart.id}`, { let updated = await api.post(
email: "jon@stark.com", `/store/carts/${cart.id}`,
}) { email: "jon@stark.com" },
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -1080,10 +1126,14 @@ medusaIntegrationTestRunner({
}) })
) )
updated = await api.post(`/store/carts/${cart.id}`, { updated = await api.post(
email: null, `/store/carts/${cart.id}`,
sales_channel_id: null, {
}) email: null,
sales_channel_id: null,
},
storeHeaders
)
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
expect(updated.data.cart).toEqual( expect(updated.data.cart).toEqual(
@@ -1107,12 +1157,16 @@ medusaIntegrationTestRunner({
name: "Webshop", name: "Webshop",
}) })
const created = await api.post(`/store/carts`, { const created = await api.post(
email: "tony@stark.com", `/store/carts`,
currency_code: "usd", {
region_id: region.id, email: "tony@stark.com",
sales_channel_id: salesChannel.id, currency_code: "usd",
}) region_id: region.id,
sales_channel_id: salesChannel.id,
},
storeHeaders
)
expect(created.status).toEqual(200) expect(created.status).toEqual(200)
expect(created.data.cart).toEqual( expect(created.data.cart).toEqual(
@@ -1132,7 +1186,8 @@ medusaIntegrationTestRunner({
`/store/carts/${created.data.cart.id}`, `/store/carts/${created.data.cart.id}`,
{ {
email: "tony@stark-industries.com", email: "tony@stark-industries.com",
} },
storeHeaders
) )
expect(updated.status).toEqual(200) expect(updated.status).toEqual(200)
@@ -1170,7 +1225,10 @@ medusaIntegrationTestRunner({
sales_channel_id: salesChannel.id, 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.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -1373,10 +1431,14 @@ medusaIntegrationTestRunner({
}, },
]) ])
let response = await api.post(`/store/carts/${cart.id}/line-items`, { let response = await api.post(
variant_id: productWithSpecialTax.variants[0].id, `/store/carts/${cart.id}/line-items`,
quantity: 1, {
}) variant_id: productWithSpecialTax.variants[0].id,
quantity: 1,
},
storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -1422,10 +1484,14 @@ medusaIntegrationTestRunner({
}) })
) )
response = await api.post(`/store/carts/${cart.id}/line-items`, { response = await api.post(
variant_id: productWithDefaultTax.variants[0].id, `/store/carts/${cart.id}/line-items`,
quantity: 1, {
}) variant_id: productWithDefaultTax.variants[0].id,
quantity: 1,
},
storeHeaders
)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
expect.objectContaining({ expect.objectContaining({
@@ -1458,31 +1524,39 @@ medusaIntegrationTestRunner({
).data.product ).data.product
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
email: "tony@stark.com", `/store/carts`,
currency_code: region.currency_code, {
region_id: region.id, email: "tony@stark.com",
items: [ currency_code: region.currency_code,
{ region_id: region.id,
variant_id: product.variants[0].id, items: [
quantity: 1, {
metadata: { variant_id: product.variants[0].id,
Size: "S", quantity: 1,
Color: "Black", metadata: {
Size: "S",
Color: "Black",
},
}, },
}, ],
], },
}) storeHeaders
)
).data.cart ).data.cart
let response = await api.post(`/store/carts/${cart.id}/line-items`, { let response = await api.post(
variant_id: product.variants[0].id, `/store/carts/${cart.id}/line-items`,
quantity: 1, {
metadata: { variant_id: product.variants[0].id,
Size: "S", quantity: 1,
Color: "Black", metadata: {
Size: "S",
Color: "Black",
},
}, },
}) storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -1498,15 +1572,19 @@ medusaIntegrationTestRunner({
}) })
) )
response = await api.post(`/store/carts/${cart.id}/line-items`, { response = await api.post(
variant_id: product.variants[0].id, `/store/carts/${cart.id}/line-items`,
quantity: 1, {
metadata: { variant_id: product.variants[0].id,
Size: "S", quantity: 1,
Color: "White", metadata: {
Special: "attribute", Size: "S",
Color: "White",
Special: "attribute",
},
}, },
}) storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.cart).toEqual( expect(response.data.cart).toEqual(
@@ -1541,9 +1619,11 @@ medusaIntegrationTestRunner({
region_id: region.id, region_id: region.id,
}) })
const response = await api.post(`/store/payment-collections`, { const response = await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) { cart_id: cart.id },
storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.payment_collection).toEqual( expect(response.data.payment_collection).toEqual(
@@ -1566,14 +1646,22 @@ medusaIntegrationTestRunner({
}) })
const firstCollection = ( const firstCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
const response = await api.post(`/store/payment-collections`, { const response = await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.payment_collection.id).toEqual( expect(response.data.payment_collection.id).toEqual(
@@ -1598,15 +1686,23 @@ medusaIntegrationTestRunner({
}) })
const firstCollection = ( const firstCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: firstCart.id, `/store/payment-collections`,
}) {
cart_id: firstCart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
const secondCollection = ( const secondCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: secondCart.id, `/store/payment-collections`,
}) {
cart_id: secondCart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
expect(firstCollection.id).toBeTruthy() 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) expect(updated.status).toEqual(200)
@@ -1696,7 +1796,7 @@ medusaIntegrationTestRunner({
}) })
let error = await api let error = await api
.post(`/store/carts/${cart.id}/taxes`, {}) .post(`/store/carts/${cart.id}/taxes`, {}, storeHeaders)
.catch((e) => e) .catch((e) => e)
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
@@ -1780,7 +1880,8 @@ medusaIntegrationTestRunner({
let response = await api.post( let response = await api.post(
`/store/carts/${cart.id}/shipping-methods`, `/store/carts/${cart.id}/shipping-methods`,
{ option_id: shippingOption.id } { option_id: shippingOption.id },
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -2016,36 +2117,46 @@ medusaIntegrationTestRunner({
it("should create an order and create item reservations", async () => { it("should create an order and create item reservations", async () => {
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
shipping_address: { currency_code: "usd",
address_1: "test address 1", email: "tony@stark-industries.com",
address_2: "test address 2", shipping_address: {
city: "ny", address_1: "test address 1",
country_code: "us", address_2: "test address 2",
province: "ny", city: "ny",
postal_code: "94016", 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, storeHeaders
items: [{ quantity: 1, variant_id: product.variants[0].id }], )
})
).data.cart ).data.cart
const paymentCollection = ( const paymentCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
await api.post( await api.post(
`/store/payment-collections/${paymentCollection.id}/payment-sessions`, `/store/payment-collections/${paymentCollection.id}/payment-sessions`,
{ provider_id: "pp_system_default" } { provider_id: "pp_system_default" },
storeHeaders
) )
const response = await api.post( const response = await api.post(
`/store/carts/${cart.id}/complete`, `/store/carts/${cart.id}/complete`,
{} {},
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -2152,16 +2263,20 @@ medusaIntegrationTestRunner({
it("should throw an error when payment collection isn't created", async () => { it("should throw an error when payment collection isn't created", async () => {
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
sales_channel_id: salesChannel.id, currency_code: "usd",
items: [{ quantity: 1, variant_id: product.variants[0].id }], email: "tony@stark-industries.com",
}) sales_channel_id: salesChannel.id,
items: [{ quantity: 1, variant_id: product.variants[0].id }],
},
storeHeaders
)
).data.cart ).data.cart
const error = await api const error = await api
.post(`/store/carts/${cart.id}/complete`, {}) .post(`/store/carts/${cart.id}/complete`, {}, storeHeaders)
.catch((e) => e) .catch((e) => e)
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
@@ -2173,20 +2288,28 @@ medusaIntegrationTestRunner({
it("should throw an error when payment collection isn't created", async () => { it("should throw an error when payment collection isn't created", async () => {
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
sales_channel_id: salesChannel.id, currency_code: "usd",
items: [{ quantity: 1, variant_id: product.variants[0].id }], email: "tony@stark-industries.com",
}) sales_channel_id: salesChannel.id,
items: [{ quantity: 1, variant_id: product.variants[0].id }],
},
storeHeaders
)
).data.cart ).data.cart
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
const error = await api const error = await api
.post(`/store/carts/${cart.id}/complete`, {}) .post(`/store/carts/${cart.id}/complete`, {}, storeHeaders)
.catch((e) => e) .catch((e) => e)
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
@@ -2198,51 +2321,69 @@ medusaIntegrationTestRunner({
it("should fail to update cart when it is completed", async () => { it("should fail to update cart when it is completed", async () => {
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
shipping_address: { currency_code: "usd",
address_1: "test address 1", email: "tony@stark-industries.com",
address_2: "test address 2", shipping_address: {
city: "ny", address_1: "test address 1",
country_code: "us", address_2: "test address 2",
province: "ny", city: "ny",
postal_code: "94016", 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, storeHeaders
items: [{ quantity: 1, variant_id: product.variants[0].id }], )
})
).data.cart ).data.cart
const paymentCollection = ( const paymentCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
await api.post( await api.post(
`/store/payment-collections/${paymentCollection.id}/payment-sessions`, `/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 const cartRefetch = (
.cart await api.get(`/store/carts/${cart.id}`, storeHeaders)
).data.cart
expect(cartRefetch.completed_at).toBeTruthy() expect(cartRefetch.completed_at).toBeTruthy()
await expect( await expect(
api.post(`/store/carts/${cart.id}/shipping-methods`, { api.post(
option_id: shippingOption.id, `/store/carts/${cart.id}/shipping-methods`,
}) {
option_id: shippingOption.id,
},
storeHeaders
)
).rejects.toThrow() ).rejects.toThrow()
const error = await api const error = await api
.post(`/store/carts/${cart.id}/line-items`, { .post(
variant_id: product.variants[0].id, `/store/carts/${cart.id}/line-items`,
quantity: 1, {
}) variant_id: product.variants[0].id,
quantity: 1,
},
storeHeaders
)
.catch((e) => e) .catch((e) => e)
expect(error.response.status).toEqual(400) expect(error.response.status).toEqual(400)
@@ -2269,36 +2410,46 @@ medusaIntegrationTestRunner({
) )
const cart = ( const cart = (
await api.post(`/store/carts`, { await api.post(
currency_code: "usd", `/store/carts`,
email: "tony@stark-industries.com", {
shipping_address: { currency_code: "usd",
address_1: "test address 1", email: "tony@stark-industries.com",
address_2: "test address 2", shipping_address: {
city: "ny", address_1: "test address 1",
country_code: "us", address_2: "test address 2",
province: "ny", city: "ny",
postal_code: "94016", 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, storeHeaders
items: [{ quantity: 1, variant_id: product.variants[0].id }], )
})
).data.cart ).data.cart
const paymentCollection = ( const paymentCollection = (
await api.post(`/store/payment-collections`, { await api.post(
cart_id: cart.id, `/store/payment-collections`,
}) {
cart_id: cart.id,
},
storeHeaders
)
).data.payment_collection ).data.payment_collection
await api.post( await api.post(
`/store/payment-collections/${paymentCollection.id}/payment-sessions`, `/store/payment-collections/${paymentCollection.id}/payment-sessions`,
{ provider_id: "pp_system_default" } { provider_id: "pp_system_default" },
storeHeaders
) )
const response = await api.post( const response = await api.post(
`/store/carts/${cart.id}/complete`, `/store/carts/${cart.id}/complete`,
{} {},
storeHeaders
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -10,6 +10,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -24,6 +26,7 @@ medusaIntegrationTestRunner({
let cartModuleService: ICartModuleService let cartModuleService: ICartModuleService
let promotionModuleService: IPromotionModuleService let promotionModuleService: IPromotionModuleService
let remoteLinkService: RemoteLink let remoteLinkService: RemoteLink
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -38,6 +41,8 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
}) })
describe("DELETE /store/carts/:id/promotions", () => { describe("DELETE /store/carts/:id/promotions", () => {
@@ -147,6 +152,7 @@ medusaIntegrationTestRunner({
data: { data: {
promo_codes: [appliedPromotionToRemove.code], promo_codes: [appliedPromotionToRemove.code],
}, },
...storeHeaders,
} }
) )
@@ -295,6 +301,7 @@ medusaIntegrationTestRunner({
`/store/carts/${cart.id}/promotions`, `/store/carts/${cart.id}/promotions`,
{ {
data: { promo_codes: [appliedPromotionToRemove.code] }, data: { promo_codes: [appliedPromotionToRemove.code] },
...storeHeaders,
} }
) )
@@ -1,17 +1,21 @@
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
jest.setTimeout(50000) jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true } const env = { MEDUSA_FF_MEDUSA_V2: true }
const storeHeaders = {
headers: {},
}
medusaIntegrationTestRunner({ medusaIntegrationTestRunner({
env, env,
testSuite: ({ api }) => { testSuite: ({ api, getContainer }) => {
describe("Currency - Store", () => { describe("Currency - Store", () => {
it("should correctly retrieve and list currencies", async () => { 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) const listResp = await api.get("/store/currencies", storeHeaders)
expect(listResp.data.currencies).toEqual( expect(listResp.data.currencies).toEqual(
@@ -1,6 +1,10 @@
import { ICustomerModuleService } from "@medusajs/types" import { ICustomerModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils" import { ModuleRegistrationName } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -13,6 +17,7 @@ medusaIntegrationTestRunner({
describe("POST /store/customers/me/addresses", () => { describe("POST /store/customers/me/addresses", () => {
let appContainer let appContainer
let customerModuleService: ICustomerModuleService let customerModuleService: ICustomerModuleService
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -21,6 +26,11 @@ medusaIntegrationTestRunner({
) )
}) })
beforeEach(async () => {
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
})
it("should create a customer address", async () => { it("should create a customer address", async () => {
const { customer, jwt } = await createAuthenticatedCustomer( const { customer, jwt } = await createAuthenticatedCustomer(
appContainer appContainer
@@ -33,7 +43,12 @@ medusaIntegrationTestRunner({
last_name: "Doe", last_name: "Doe",
address_1: "Test street 1", address_1: "Test street 1",
}, },
{ headers: { authorization: `Bearer ${jwt}` } } {
headers: {
authorization: `Bearer ${jwt}`,
...storeHeaders.headers,
},
}
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -9,6 +9,8 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
adminHeaders, adminHeaders,
createAdminUser, createAdminUser,
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -20,6 +22,7 @@ medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => { testSuite: ({ dbConnection, getContainer, api }) => {
describe("POST /store/customers", () => { describe("POST /store/customers", () => {
let appContainer let appContainer
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -27,6 +30,8 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) 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. // TODO: Reenable once the customer authentication is fixed, and use the HTTP endpoints instead.
@@ -55,7 +60,12 @@ medusaIntegrationTestRunner({
last_name: "Doe", last_name: "Doe",
email: "john@me.com", email: "john@me.com",
}, },
{ headers: { authorization: `Bearer ${token}` } } {
headers: {
authorization: `Bearer ${token}`,
...storeHeaders.headers,
},
}
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -1,6 +1,10 @@
import { ICustomerModuleService } from "@medusajs/types" import { ICustomerModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils" import { ModuleRegistrationName } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
const env = { MEDUSA_FF_MEDUSA_V2: true } const env = { MEDUSA_FF_MEDUSA_V2: true }
@@ -13,6 +17,7 @@ medusaIntegrationTestRunner({
describe("DELETE /store/customers/me/addresses/:address_id", () => { describe("DELETE /store/customers/me/addresses/:address_id", () => {
let appContainer let appContainer
let customerModuleService: ICustomerModuleService let customerModuleService: ICustomerModuleService
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() 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 () => { it("should delete a customer address", async () => {
const { customer, jwt } = await createAuthenticatedCustomer( const { customer, jwt } = await createAuthenticatedCustomer(
appContainer appContainer
@@ -35,7 +46,12 @@ medusaIntegrationTestRunner({
const response = await api.delete( const response = await api.delete(
`/store/customers/me/addresses/${address.id}`, `/store/customers/me/addresses/${address.id}`,
{ headers: { authorization: `Bearer ${jwt}` } } {
headers: {
authorization: `Bearer ${jwt}`,
...storeHeaders.headers,
},
}
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -66,7 +82,10 @@ medusaIntegrationTestRunner({
const response = await api const response = await api
.delete(`/store/customers/me/addresses/${address.id}`, { .delete(`/store/customers/me/addresses/${address.id}`, {
headers: { authorization: `Bearer ${jwt}` }, headers: {
authorization: `Bearer ${jwt}`,
...storeHeaders.headers,
},
}) })
.catch((e) => e.response) .catch((e) => e.response)
@@ -1,5 +1,9 @@
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
import { medusaIntegrationTestRunner } from "medusa-test-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) jest.setTimeout(50000)
@@ -10,17 +14,25 @@ medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => { testSuite: ({ dbConnection, getContainer, api }) => {
describe("GET /store/customers", () => { describe("GET /store/customers", () => {
let appContainer let appContainer
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
}) })
beforeEach(async () => {
appContainer = getContainer()
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
})
it("should retrieve auth user's customer", async () => { it("should retrieve auth user's customer", async () => {
const { customer, jwt } = await createAuthenticatedCustomer( const { customer, jwt } = await createAuthenticatedCustomer(
appContainer appContainer
) )
const response = await api.get(`/store/customers/me`, { const response = await api.get(`/store/customers/me`, {
headers: { authorization: `Bearer ${jwt}` }, headers: { authorization: `Bearer ${jwt}`, ...storeHeaders.headers },
}) })
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -1,6 +1,10 @@
import { ICustomerModuleService } from "@medusajs/types" import { ICustomerModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils" import { ModuleRegistrationName } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
const env = { MEDUSA_FF_MEDUSA_V2: true } const env = { MEDUSA_FF_MEDUSA_V2: true }
@@ -13,6 +17,7 @@ medusaIntegrationTestRunner({
describe("GET /store/customers/me/addresses", () => { describe("GET /store/customers/me/addresses", () => {
let appContainer let appContainer
let customerModuleService: ICustomerModuleService let customerModuleService: ICustomerModuleService
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() 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 () => { it("should get all customer addresses and its count", async () => {
const { customer, jwt } = await createAuthenticatedCustomer( const { customer, jwt } = await createAuthenticatedCustomer(
appContainer appContainer
@@ -60,7 +71,7 @@ medusaIntegrationTestRunner({
}) })
const response = await api.get(`/store/customers/me/addresses`, { const response = await api.get(`/store/customers/me/addresses`, {
headers: { authorization: `Bearer ${jwt}` }, headers: { authorization: `Bearer ${jwt}`, ...storeHeaders.headers },
}) })
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -1,6 +1,10 @@
import { ICustomerModuleService } from "@medusajs/types" import { ICustomerModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils" import { ModuleRegistrationName } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
generatePublishableKey,
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer" import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -13,6 +17,7 @@ medusaIntegrationTestRunner({
describe("POST /store/customers/:id/addresses/:address_id", () => { describe("POST /store/customers/:id/addresses/:address_id", () => {
let appContainer let appContainer
let customerModuleService: ICustomerModuleService let customerModuleService: ICustomerModuleService
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() 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( const { customer, jwt } = await createAuthenticatedCustomer(
appContainer appContainer
) )
@@ -38,7 +49,12 @@ medusaIntegrationTestRunner({
{ {
first_name: "Jane", first_name: "Jane",
}, },
{ headers: { authorization: `Bearer ${jwt}` } } {
headers: {
authorization: `Bearer ${jwt}`,
...storeHeaders.headers,
},
}
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
@@ -70,7 +86,12 @@ medusaIntegrationTestRunner({
.post( .post(
`/store/customers/me/addresses/${address.id}`, `/store/customers/me/addresses/${address.id}`,
{ first_name: "Jane" }, { first_name: "Jane" },
{ headers: { authorization: `Bearer ${jwt}` } } {
headers: {
authorization: `Bearer ${jwt}`,
...storeHeaders.headers,
},
}
) )
.catch((e) => e.response) .catch((e) => e.response)
@@ -1,6 +1,10 @@
import { PriceListStatus, PriceListType } from "@medusajs/utils" import { PriceListStatus, PriceListType } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-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" import { getProductFixture } from "../../../../helpers/fixtures"
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -23,6 +27,7 @@ medusaIntegrationTestRunner({
let product let product
let variant let variant
let region let region
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -30,6 +35,9 @@ medusaIntegrationTestRunner({
beforeEach(async () => { beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, appContainer) await createAdminUser(dbConnection, adminHeaders, appContainer)
appContainer = getContainer()
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
region = ( region = (
await api.post( await api.post(
@@ -87,7 +95,8 @@ medusaIntegrationTestRunner({
) )
let response = await api.get( 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) expect(response.status).toEqual(200)
@@ -149,7 +158,8 @@ medusaIntegrationTestRunner({
) )
let response = await api.get( 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) expect(response.status).toEqual(200)
@@ -184,10 +194,14 @@ medusaIntegrationTestRunner({
) )
).data.customer_group ).data.customer_group
const authResponse = await api.post("/store/auth", { const authResponse = await api.post(
email: "test5@email-pl.com", "/store/auth",
password: "test", {
}) email: "test5@email-pl.com",
password: "test",
},
storeHeaders
)
const [authCookie] = authResponse.headers["set-cookie"][0].split(";") const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
@@ -215,6 +229,7 @@ medusaIntegrationTestRunner({
{ {
headers: { headers: {
Cookie: authCookie, Cookie: authCookie,
...storeHeaders.headers,
}, },
} }
) )
@@ -8,7 +8,11 @@ import {
Modules, Modules,
} from "@medusajs/utils" } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-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) jest.setTimeout(50000)
@@ -31,6 +35,7 @@ medusaIntegrationTestRunner({
let fulfillmentSet let fulfillmentSet
let cart let cart
let shippingOption let shippingOption
let storeHeaders
beforeAll(async () => { beforeAll(async () => {
appContainer = getContainer() appContainer = getContainer()
@@ -38,6 +43,8 @@ medusaIntegrationTestRunner({
ModuleRegistrationName.FULFILLMENT ModuleRegistrationName.FULFILLMENT
) )
regionService = appContainer.resolve(ModuleRegistrationName.REGION) regionService = appContainer.resolve(ModuleRegistrationName.REGION)
const publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
}) })
beforeEach(async () => { beforeEach(async () => {
@@ -172,25 +179,30 @@ medusaIntegrationTestRunner({
).data.shipping_option ).data.shipping_option
cart = ( cart = (
await api.post(`/store/carts`, { await api.post(
region_id: region.id, `/store/carts`,
sales_channel_id: salesChannel.id, {
currency_code: "usd", region_id: region.id,
email: "test@admin.com", sales_channel_id: salesChannel.id,
items: [ currency_code: "usd",
{ email: "test@admin.com",
variant_id: product.variants[0].id, items: [
quantity: 1, {
}, variant_id: product.variants[0].id,
], quantity: 1,
}) },
],
},
storeHeaders
)
).data.cart ).data.cart
}) })
describe("GET /admin/shipping-options?cart_id=", () => { describe("GET /admin/shipping-options?cart_id=", () => {
it("should get all shipping options for a cart successfully", async () => { it("should get all shipping options for a cart successfully", async () => {
const resp = await api.get( 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 const shippingOptions = resp.data.shipping_options
@@ -1,4 +1,4 @@
import { BaseFilterable } from "../../dal" import { BaseFilterable, OperatorMap } from "../../dal"
/** /**
* An API key's type. * An API key's type.
@@ -90,4 +90,6 @@ export interface FilterableApiKeyProps
* Filter the API keys by their type. * Filter the API keys by their type.
*/ */
type?: ApiKeyType type?: ApiKeyType
revoked_at?: OperatorMap<Date | null>
} }
@@ -13,3 +13,5 @@ export enum ApiKeyType {
*/ */
SECRET = "secret", SECRET = "secret",
} }
export const PUBLISHABLE_KEY_HEADER = "x-publishable-api-key"
+12 -1
View File
@@ -1,5 +1,5 @@
import { ZodObject } from "zod"
import type { NextFunction, Request, Response } from "express" import type { NextFunction, Request, Response } from "express"
import { ZodObject } from "zod"
import { MedusaPricingContext, RequestQueryFields } from "@medusajs/types" import { MedusaPricingContext, RequestQueryFields } from "@medusajs/types"
import * as core from "express-serve-static-core" import * as core from "express-serve-static-core"
@@ -162,9 +162,20 @@ export interface AuthContext {
app_metadata: Record<string, unknown> app_metadata: Record<string, unknown>
} }
export interface PublishableKeyContext {
key: string
sales_channel_ids: string[]
}
export interface AuthenticatedMedusaRequest<Body = never> export interface AuthenticatedMedusaRequest<Body = never>
extends MedusaRequest<Body> { extends MedusaRequest<Body> {
auth_context: AuthContext auth_context: AuthContext
publishable_key_context?: PublishableKeyContext
}
export interface MedusaStoreRequest<Body = never> extends MedusaRequest<Body> {
auth_context?: AuthContext
publishable_key_context: PublishableKeyContext
} }
export type MedusaResponse<Body = unknown> = Response<Body> export type MedusaResponse<Body = unknown> = Response<Body>
+2
View File
@@ -48,6 +48,7 @@ import { storeCartRoutesMiddlewares } from "./store/carts/middlewares"
import { storeCollectionRoutesMiddlewares } from "./store/collections/middlewares" import { storeCollectionRoutesMiddlewares } from "./store/collections/middlewares"
import { storeCurrencyRoutesMiddlewares } from "./store/currencies/middlewares" import { storeCurrencyRoutesMiddlewares } from "./store/currencies/middlewares"
import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares" import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares"
import { storeRoutesMiddlewares } from "./store/middlewares"
import { storeOrderRoutesMiddlewares } from "./store/orders/middlewares" import { storeOrderRoutesMiddlewares } from "./store/orders/middlewares"
import { storePaymentCollectionsMiddlewares } from "./store/payment-collections/middlewares" import { storePaymentCollectionsMiddlewares } from "./store/payment-collections/middlewares"
import { storePaymentProvidersMiddlewares } from "./store/payment-providers/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" import { storeShippingOptionRoutesMiddlewares } from "./store/shipping-options/middlewares"
export default defineMiddlewares([ export default defineMiddlewares([
...storeRoutesMiddlewares,
...adminCustomerGroupRoutesMiddlewares, ...adminCustomerGroupRoutesMiddlewares,
...adminCustomerRoutesMiddlewares, ...adminCustomerRoutesMiddlewares,
...adminPromotionRoutesMiddlewares, ...adminPromotionRoutesMiddlewares,
@@ -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()],
},
]
@@ -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 { 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: // 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 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 a sales channel id is passed through query params, we use that
// - If not, we use the default sales channel for the store // - If not, we use the default sales channel for the store
export function filterByValidSalesChannels() { export function filterByValidSalesChannels() {
return async (req: AuthenticatedMedusaRequest, _, next: NextFunction) => { return async (req: MedusaStoreRequest, _, next: NextFunction) => {
const publishableApiKey = req.get("x-publishable-api-key") const idsFromRequest = req.filterableFields.sales_channel_id
const salesChannelIds = req.filterableFields.sales_channel_id as const { sales_channel_ids: idsFromPublishableKey = [] } =
| string[] req.publishable_key_context
| undefined
if (publishableApiKey) { // If all sales channel ids are not in the publishable key, we throw an error
const apiKey = await refetchEntity( if (Array.isArray(idsFromRequest) && idsFromRequest.length) {
"api_key", const uniqueInParams = arrayDifference(
{ token: publishableApiKey }, idsFromRequest,
req.scope, idsFromPublishableKey
["id", "sales_channels_link.sales_channel_id"]
) )
if (!apiKey) { if (uniqueInParams.length) {
return next( return next(
new MedusaError( new MedusaError(
MedusaError.Types.INVALID_DATA, 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) { req.filterableFields.sales_channel_id = idsFromRequest
// 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 = result
return next() return next()
} }
if (salesChannelIds?.length) { if (idsFromPublishableKey?.length) {
req.filterableFields.sales_channel_id = salesChannelIds req.filterableFields.sales_channel_id = idsFromPublishableKey
return next() return next()
} }
const store = await refetchEntity("stores", {}, req.scope, [ return next(
"default_sales_channel_id", new MedusaError(
]) MedusaError.Types.INVALID_DATA,
`Publishable key needs to have a sales channel configured`
if (!store) {
return next(
new MedusaError(MedusaError.Types.INVALID_DATA, `Store not found`)
) )
} )
if (store.default_sales_channel_id) {
req.filterableFields.sales_channel_id = [store.default_sales_channel_id]
}
return next()
} }
} }
@@ -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()
}
}