feat: API key sales channel link (#6851)
What - Add link between API key and sales channels - Add API route for batch adding sales channels to a publishable API key - Clean up API key API routes responses - Move API key test suite from `integration-tests/modules` to `integration-tests/api`
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/api-key": patch
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/link-modules": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: API key sales channel link
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
import { ApiKeyType } from "@medusajs/utils"
|
||||||
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
import { createAdminUser } from "../../../helpers/create-admin-user"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||||
|
const adminHeaders = {
|
||||||
|
headers: { "x-medusa-access-token": "test_token" },
|
||||||
|
}
|
||||||
|
|
||||||
|
medusaIntegrationTestRunner({
|
||||||
|
env,
|
||||||
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
|
describe("API Keys - Admin", () => {
|
||||||
|
let container
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
container = getContainer()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await createAdminUser(dbConnection, adminHeaders, container)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should correctly implement the entire lifecycle of an api key", async () => {
|
||||||
|
const created = await api.post(
|
||||||
|
`/admin/api-keys`,
|
||||||
|
{
|
||||||
|
title: "Test Secret Key",
|
||||||
|
type: ApiKeyType.SECRET,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(created.status).toEqual(200)
|
||||||
|
expect(created.data.api_key).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: created.data.api_key.id,
|
||||||
|
title: "Test Secret Key",
|
||||||
|
created_by: "admin_user",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
// On create we get the token in raw form so we can store it.
|
||||||
|
expect(created.data.api_key.token).toContain("sk_")
|
||||||
|
|
||||||
|
const updated = await api.post(
|
||||||
|
`/admin/api-keys/${created.data.api_key.id}`,
|
||||||
|
{
|
||||||
|
title: "Updated Secret Key",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updated.status).toEqual(200)
|
||||||
|
expect(updated.data.api_key).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: created.data.api_key.id,
|
||||||
|
title: "Updated Secret Key",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const revoked = await api.post(
|
||||||
|
`/admin/api-keys/${created.data.api_key.id}/revoke`,
|
||||||
|
{},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(revoked.status).toEqual(200)
|
||||||
|
expect(revoked.data.api_key).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: created.data.api_key.id,
|
||||||
|
revoked_by: "admin_user",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(revoked.data.api_key.revoked_at).toBeTruthy()
|
||||||
|
|
||||||
|
const deleted = await api.delete(
|
||||||
|
`/admin/api-keys/${created.data.api_key.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
const listedApiKeys = await api.get(`/admin/api-keys`, adminHeaders)
|
||||||
|
|
||||||
|
expect(deleted.status).toEqual(200)
|
||||||
|
expect(listedApiKeys.data.api_keys).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("can use a secret api key for authentication", async () => {
|
||||||
|
const created = await api.post(
|
||||||
|
`/admin/api-keys`,
|
||||||
|
{
|
||||||
|
title: "Test Secret Key",
|
||||||
|
type: ApiKeyType.SECRET,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const createdRegion = await api.post(
|
||||||
|
`/admin/regions`,
|
||||||
|
{
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
countries: ["us", "ca"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: created.data.api_key.token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(createdRegion.status).toEqual(200)
|
||||||
|
expect(createdRegion.data.region.name).toEqual("Test Region")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("falls back to other mode of authentication when an api key is not valid", async () => {
|
||||||
|
const created = await api.post(
|
||||||
|
`/admin/api-keys`,
|
||||||
|
{
|
||||||
|
title: "Test Secret Key",
|
||||||
|
type: ApiKeyType.SECRET,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/api-keys/${created.data.api_key.id}/revoke`,
|
||||||
|
{},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const err = await api
|
||||||
|
.post(
|
||||||
|
`/admin/regions`,
|
||||||
|
{
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
countries: ["us", "ca"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: created.data.api_key.token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch((e) => e.message)
|
||||||
|
|
||||||
|
const createdRegion = await api.post(
|
||||||
|
`/admin/regions`,
|
||||||
|
{
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
countries: ["us", "ca"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: created.data.api_key.token,
|
||||||
|
},
|
||||||
|
...adminHeaders,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(err).toEqual("Request failed with status code 401")
|
||||||
|
expect(createdRegion.status).toEqual(200)
|
||||||
|
expect(createdRegion.data.region.name).toEqual("Test Region")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should associate sales channels with a publishable API key", async () => {
|
||||||
|
const salesChannelRes = await api.post(
|
||||||
|
`/admin/sales-channels`,
|
||||||
|
{
|
||||||
|
name: "Test Sales Channel",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const { sales_channel } = salesChannelRes.data
|
||||||
|
|
||||||
|
const apiKeyRes = await api.post(
|
||||||
|
`/admin/api-keys`,
|
||||||
|
{
|
||||||
|
title: "Test publishable KEY",
|
||||||
|
type: ApiKeyType.PUBLISHABLE,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const { api_key } = apiKeyRes.data
|
||||||
|
|
||||||
|
const keyWithChannelsRes = await api.post(
|
||||||
|
`/admin/api-keys/${api_key.id}/sales-channels/batch/add`,
|
||||||
|
{
|
||||||
|
sales_channel_ids: [sales_channel.id],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const { api_key: keyWithChannels } = keyWithChannelsRes.data
|
||||||
|
|
||||||
|
expect(keyWithChannelsRes.status).toEqual(200)
|
||||||
|
expect(keyWithChannels.title).toEqual("Test publishable KEY")
|
||||||
|
expect(keyWithChannels.sales_channels).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: sales_channel.id,
|
||||||
|
name: "Test Sales Channel",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw if API key is not a publishable key", async () => {
|
||||||
|
const salesChannelRes = await api.post(
|
||||||
|
`/admin/sales-channels`,
|
||||||
|
{
|
||||||
|
name: "Test Sales Channel",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const { sales_channel } = salesChannelRes.data
|
||||||
|
|
||||||
|
const apiKeyRes = await api.post(
|
||||||
|
`/admin/api-keys`,
|
||||||
|
{
|
||||||
|
title: "Test secret KEY",
|
||||||
|
type: ApiKeyType.SECRET,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const errorRes = await api
|
||||||
|
.post(
|
||||||
|
`/admin/api-keys/${apiKeyRes.data.api_key.id}/sales-channels/batch/add`,
|
||||||
|
{
|
||||||
|
sales_channel_ids: [sales_channel.id],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((err) => err)
|
||||||
|
|
||||||
|
expect(errorRes.response.status).toEqual(400)
|
||||||
|
expect(errorRes.response.data.message).toEqual(
|
||||||
|
"Sales channels can only be associated with publishable API keys"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw if sales channel does not exist", async () => {
|
||||||
|
const apiKeyRes = await api.post(
|
||||||
|
`/admin/api-keys`,
|
||||||
|
{
|
||||||
|
title: "Test publishable KEY",
|
||||||
|
type: ApiKeyType.PUBLISHABLE,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const errorRes = await api
|
||||||
|
.post(
|
||||||
|
`/admin/api-keys/${apiKeyRes.data.api_key.id}/sales-channels/batch/add`,
|
||||||
|
{
|
||||||
|
sales_channel_ids: ["phony"],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((err) => err)
|
||||||
|
|
||||||
|
expect(errorRes.response.status).toEqual(400)
|
||||||
|
expect(errorRes.response.data.message).toEqual(
|
||||||
|
"Sales channels with IDs phony do not exist"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
import { ApiKeyType } from "@medusajs/utils"
|
|
||||||
import { IRegionModuleService } from "@medusajs/types"
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|
||||||
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
|
||||||
|
|
||||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
|
||||||
const adminHeaders = {
|
|
||||||
headers: { "x-medusa-access-token": "test_token" },
|
|
||||||
}
|
|
||||||
|
|
||||||
medusaIntegrationTestRunner({
|
|
||||||
env,
|
|
||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
|
||||||
describe("API Keys - Admin", () => {
|
|
||||||
let regionService: IRegionModuleService
|
|
||||||
let container
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
container = getContainer()
|
|
||||||
regionService = container.resolve(
|
|
||||||
ModuleRegistrationName.REGION
|
|
||||||
) as IRegionModuleService
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await createAdminUser(dbConnection, adminHeaders, container)
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
// TODO: Once teardown doesn't skip constraint checks and cascades, we can remove this
|
|
||||||
const existingRegions = await regionService.list({})
|
|
||||||
await regionService.delete(existingRegions.map((r) => r.id))
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should correctly implement the entire lifecycle of an api key", async () => {
|
|
||||||
const created = await api.post(
|
|
||||||
`/admin/api-keys`,
|
|
||||||
{
|
|
||||||
title: "Test Secret Key",
|
|
||||||
type: ApiKeyType.SECRET,
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(created.status).toEqual(200)
|
|
||||||
expect(created.data.apiKey).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: created.data.apiKey.id,
|
|
||||||
title: "Test Secret Key",
|
|
||||||
created_by: "admin_user",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
// On create we get the token in raw form so we can store it.
|
|
||||||
expect(created.data.apiKey.token).toContain("sk_")
|
|
||||||
|
|
||||||
const updated = await api.post(
|
|
||||||
`/admin/api-keys/${created.data.apiKey.id}`,
|
|
||||||
{
|
|
||||||
title: "Updated Secret Key",
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(updated.status).toEqual(200)
|
|
||||||
expect(updated.data.apiKey).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: created.data.apiKey.id,
|
|
||||||
title: "Updated Secret Key",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const revoked = await api.post(
|
|
||||||
`/admin/api-keys/${created.data.apiKey.id}/revoke`,
|
|
||||||
{},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(revoked.status).toEqual(200)
|
|
||||||
expect(revoked.data.apiKey).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: created.data.apiKey.id,
|
|
||||||
revoked_by: "admin_user",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
expect(revoked.data.apiKey.revoked_at).toBeTruthy()
|
|
||||||
|
|
||||||
const deleted = await api.delete(
|
|
||||||
`/admin/api-keys/${created.data.apiKey.id}`,
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
const listedApiKeys = await api.get(`/admin/api-keys`, adminHeaders)
|
|
||||||
|
|
||||||
expect(deleted.status).toEqual(200)
|
|
||||||
expect(listedApiKeys.data.apiKeys).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("can use a secret api key for authentication", async () => {
|
|
||||||
const created = await api.post(
|
|
||||||
`/admin/api-keys`,
|
|
||||||
{
|
|
||||||
title: "Test Secret Key",
|
|
||||||
type: ApiKeyType.SECRET,
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
const createdRegion = await api.post(
|
|
||||||
`/admin/regions`,
|
|
||||||
{
|
|
||||||
name: "Test Region",
|
|
||||||
currency_code: "usd",
|
|
||||||
countries: ["us", "ca"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
auth: {
|
|
||||||
username: created.data.apiKey.token,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(createdRegion.status).toEqual(200)
|
|
||||||
expect(createdRegion.data.region.name).toEqual("Test Region")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("falls back to other mode of authentication when an api key is not valid", async () => {
|
|
||||||
const created = await api.post(
|
|
||||||
`/admin/api-keys`,
|
|
||||||
{
|
|
||||||
title: "Test Secret Key",
|
|
||||||
type: ApiKeyType.SECRET,
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
await api.post(
|
|
||||||
`/admin/api-keys/${created.data.apiKey.id}/revoke`,
|
|
||||||
{},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
const err = await api
|
|
||||||
.post(
|
|
||||||
`/admin/regions`,
|
|
||||||
{
|
|
||||||
name: "Test Region",
|
|
||||||
currency_code: "usd",
|
|
||||||
countries: ["us", "ca"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
auth: {
|
|
||||||
username: created.data.apiKey.token,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((e) => e.message)
|
|
||||||
|
|
||||||
const createdRegion = await api.post(
|
|
||||||
`/admin/regions`,
|
|
||||||
{
|
|
||||||
name: "Test Region",
|
|
||||||
currency_code: "usd",
|
|
||||||
countries: ["us", "ca"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
auth: {
|
|
||||||
username: created.data.apiKey.token,
|
|
||||||
},
|
|
||||||
...adminHeaders,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(err).toEqual("Request failed with status code 401")
|
|
||||||
expect(createdRegion.status).toEqual(200)
|
|
||||||
expect(createdRegion.data.region.name).toEqual("Test Region")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -3,7 +3,9 @@ import { ModuleJoinerConfig } from "@medusajs/types"
|
|||||||
import { MapToConfig } from "@medusajs/utils"
|
import { MapToConfig } from "@medusajs/utils"
|
||||||
import ApiKey from "./models/api-key"
|
import ApiKey from "./models/api-key"
|
||||||
|
|
||||||
export const LinkableKeys: Record<string, string> = {}
|
export const LinkableKeys: Record<string, string> = {
|
||||||
|
api_key_id: ApiKey.name,
|
||||||
|
}
|
||||||
|
|
||||||
const entityLinkableKeysMap: MapToConfig = {}
|
const entityLinkableKeysMap: MapToConfig = {}
|
||||||
Object.entries(LinkableKeys).forEach(([key, value]) => {
|
Object.entries(LinkableKeys).forEach(([key, value]) => {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
Entity,
|
Entity,
|
||||||
|
Enum,
|
||||||
OnInit,
|
OnInit,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
Enum,
|
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
const TypeIndex = createPsqlIndexStatementHelper({
|
const TypeIndex = createPsqlIndexStatementHelper({
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
links: {
|
||||||
|
api_key_id: string
|
||||||
|
sales_channel_ids: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const associateApiKeysWithSalesChannelsStepId =
|
||||||
|
"associate-sales-channels-with-api-keys"
|
||||||
|
export const associateApiKeysWithSalesChannelsStep = createStep(
|
||||||
|
associateApiKeysWithSalesChannelsStepId,
|
||||||
|
async (input: StepInput, { container }) => {
|
||||||
|
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
|
||||||
|
const links = input.links
|
||||||
|
.map((link) => {
|
||||||
|
return link.sales_channel_ids.map((id) => {
|
||||||
|
return {
|
||||||
|
[Modules.API_KEY]: {
|
||||||
|
publishable_key_id: link.api_key_id,
|
||||||
|
},
|
||||||
|
[Modules.SALES_CHANNEL]: {
|
||||||
|
sales_channel_id: id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const createdLinks = await remoteLink.create(links)
|
||||||
|
|
||||||
|
return new StepResponse(createdLinks, links)
|
||||||
|
},
|
||||||
|
async (links, { container }) => {
|
||||||
|
if (!links) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
|
||||||
|
await remoteLink.dismiss(links)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
export * from "./associate-sales-channels-with-publishable-keys"
|
||||||
export * from "./create-api-keys"
|
export * from "./create-api-keys"
|
||||||
export * from "./delete-api-keys"
|
export * from "./delete-api-keys"
|
||||||
export * from "./update-api-keys"
|
|
||||||
export * from "./revoke-api-keys"
|
export * from "./revoke-api-keys"
|
||||||
|
export * from "./update-api-keys"
|
||||||
|
export * from "./validate-sales-channel-exists"
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { ISalesChannelModuleService } from "@medusajs/types"
|
||||||
|
import { MedusaError, arrayDifference } from "@medusajs/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
sales_channel_ids: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateSalesChannelsExistStepId = "validate-sales-channels-exist"
|
||||||
|
export const validateSalesChannelsExistStep = createStep(
|
||||||
|
validateSalesChannelsExistStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const salesChannelModuleService =
|
||||||
|
container.resolve<ISalesChannelModuleService>(
|
||||||
|
ModuleRegistrationName.SALES_CHANNEL
|
||||||
|
)
|
||||||
|
|
||||||
|
const salesChannels = await salesChannelModuleService.list(
|
||||||
|
{ id: data.sales_channel_ids },
|
||||||
|
{ select: ["id"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
const salesChannelIds = salesChannels.map((v) => v.id)
|
||||||
|
|
||||||
|
const notFound = arrayDifference(data.sales_channel_ids, salesChannelIds)
|
||||||
|
|
||||||
|
if (notFound.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Sales channels with IDs ${notFound.join(", ")} do not exist`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StepResponse(salesChannelIds)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
WorkflowData,
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
} from "@medusajs/workflows-sdk"
|
||||||
|
import {
|
||||||
|
associateApiKeysWithSalesChannelsStep,
|
||||||
|
validateSalesChannelsExistStep,
|
||||||
|
} from "../steps"
|
||||||
|
|
||||||
|
type WorkflowInput = {
|
||||||
|
data: {
|
||||||
|
api_key_id: string
|
||||||
|
sales_channel_ids: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addSalesChannelsToApiKeyWorkflowId =
|
||||||
|
"add-sales-channels-to-api-key"
|
||||||
|
export const addSalesChannelsToApiKeyWorkflow = createWorkflow(
|
||||||
|
addSalesChannelsToApiKeyWorkflowId,
|
||||||
|
(input: WorkflowData<WorkflowInput>) => {
|
||||||
|
const salesChannelIds = transform(input.data, (data) =>
|
||||||
|
data.map((d) => d.sales_channel_ids).flat()
|
||||||
|
)
|
||||||
|
validateSalesChannelsExistStep({
|
||||||
|
sales_channel_ids: salesChannelIds,
|
||||||
|
})
|
||||||
|
associateApiKeysWithSalesChannelsStep({ links: input.data })
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -2,3 +2,4 @@ export * from "./create-api-keys"
|
|||||||
export * from "./delete-api-keys"
|
export * from "./delete-api-keys"
|
||||||
export * from "./update-api-keys"
|
export * from "./update-api-keys"
|
||||||
export * from "./revoke-api-keys"
|
export * from "./revoke-api-keys"
|
||||||
|
export * from "./add-sales-channels-to-publishable-key"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
import { LINKS } from "../links"
|
import { LINKS } from "../links"
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
|
||||||
|
|
||||||
export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
|
export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
|
||||||
serviceName: LINKS.PublishableApiKeySalesChannel,
|
serviceName: LINKS.PublishableApiKeySalesChannel,
|
||||||
@@ -21,14 +21,12 @@ export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
|
|||||||
relationships: [
|
relationships: [
|
||||||
{
|
{
|
||||||
serviceName: Modules.API_KEY,
|
serviceName: Modules.API_KEY,
|
||||||
isInternalService: true,
|
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
foreignKey: "publishable_key_id",
|
foreignKey: "publishable_key_id",
|
||||||
alias: "api_key",
|
alias: "api_key",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
serviceName: Modules.SALES_CHANNEL,
|
serviceName: Modules.SALES_CHANNEL,
|
||||||
isInternalService: true,
|
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
foreignKey: "sales_channel_id",
|
foreignKey: "sales_channel_id",
|
||||||
alias: "sales_channel",
|
alias: "sales_channel",
|
||||||
@@ -42,7 +40,6 @@ export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
|
|||||||
},
|
},
|
||||||
relationship: {
|
relationship: {
|
||||||
serviceName: LINKS.PublishableApiKeySalesChannel,
|
serviceName: LINKS.PublishableApiKeySalesChannel,
|
||||||
isInternalService: true,
|
|
||||||
primaryKey: "publishable_key_id",
|
primaryKey: "publishable_key_id",
|
||||||
foreignKey: "id",
|
foreignKey: "id",
|
||||||
alias: "sales_channels_link",
|
alias: "sales_channels_link",
|
||||||
@@ -56,7 +53,6 @@ export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
|
|||||||
},
|
},
|
||||||
relationship: {
|
relationship: {
|
||||||
serviceName: LINKS.PublishableApiKeySalesChannel,
|
serviceName: LINKS.PublishableApiKeySalesChannel,
|
||||||
isInternalService: true,
|
|
||||||
primaryKey: "sales_channel_id",
|
primaryKey: "sales_channel_id",
|
||||||
foreignKey: "id",
|
foreignKey: "id",
|
||||||
alias: "api_keys_link",
|
alias: "api_keys_link",
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
|
import { revokeApiKeysWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { RevokeApiKeyDTO } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../../../types/routing"
|
} from "../../../../../types/routing"
|
||||||
|
|
||||||
import { RevokeApiKeyDTO } from "@medusajs/types"
|
|
||||||
import { revokeApiKeysWorkflow } from "@medusajs/core-flows"
|
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
req: AuthenticatedMedusaRequest,
|
req: AuthenticatedMedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
) => {
|
) => {
|
||||||
const id = req.params.id
|
const { errors } = await revokeApiKeysWorkflow(req.scope).run({
|
||||||
|
|
||||||
const { result, errors } = await revokeApiKeysWorkflow(req.scope).run({
|
|
||||||
input: {
|
input: {
|
||||||
selector: { id: req.params.id },
|
selector: { id: req.params.id },
|
||||||
revoke: {
|
revoke: {
|
||||||
@@ -27,5 +28,17 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ apiKey: result[0] })
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "api_key",
|
||||||
|
variables: {
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [apiKey] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ api_key: apiKey })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import {
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "../../../../types/routing"
|
|
||||||
import {
|
import {
|
||||||
deleteApiKeysWorkflow,
|
deleteApiKeysWorkflow,
|
||||||
updateApiKeysWorkflow,
|
updateApiKeysWorkflow,
|
||||||
} from "@medusajs/core-flows"
|
} from "@medusajs/core-flows"
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../../../../types/routing"
|
||||||
|
|
||||||
import { UpdateApiKeyDTO } from "@medusajs/types"
|
import { UpdateApiKeyDTO } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { defaultAdminApiKeyFields } from "../query-config"
|
import { defaultAdminApiKeyFields } from "../query-config"
|
||||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
|
||||||
|
|
||||||
export const GET = async (
|
export const GET = async (
|
||||||
req: AuthenticatedMedusaRequest,
|
req: AuthenticatedMedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
) => {
|
) => {
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
const variables = { id: req.params.id }
|
const variables = { id: req.params.id }
|
||||||
|
|
||||||
@@ -46,7 +49,19 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ apiKey: result[0] })
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const queryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "api_key",
|
||||||
|
variables: {
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [apiKey] = await remoteQuery(queryObject)
|
||||||
|
|
||||||
|
res.status(200).json({ api_key: apiKey })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DELETE = async (
|
export const DELETE = async (
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { addSalesChannelsToApiKeyWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
MedusaError,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../../../../../../../types/routing"
|
||||||
|
import { AdminPostApiKeysApiKeySalesChannelsBatchReq } from "../../../../validators"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => {
|
||||||
|
const body = req.validatedBody as AdminPostApiKeysApiKeySalesChannelsBatchReq
|
||||||
|
|
||||||
|
const apiKeyModule = req.scope.resolve(ModuleRegistrationName.API_KEY)
|
||||||
|
|
||||||
|
const apiKey = await apiKeyModule.retrieve(req.params.id)
|
||||||
|
|
||||||
|
if (apiKey.type !== "publishable") {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
"Sales channels can only be associated with publishable API keys"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowInput = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
api_key_id: req.params.id,
|
||||||
|
sales_channel_ids: body.sales_channel_ids,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const { errors } = await addSalesChannelsToApiKeyWorkflow(req.scope).run({
|
||||||
|
input: workflowInput,
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
throw errors[0].error
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "api_key",
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
|
variables: {
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const [result] = await remoteQuery(query)
|
||||||
|
|
||||||
|
res.status(200).json({ api_key: result })
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
|
|
||||||
|
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||||
import {
|
import {
|
||||||
AdminGetApiKeysApiKeyParams,
|
AdminGetApiKeysApiKeyParams,
|
||||||
AdminGetApiKeysParams,
|
AdminGetApiKeysParams,
|
||||||
AdminPostApiKeysApiKeyReq,
|
AdminPostApiKeysApiKeyReq,
|
||||||
|
AdminPostApiKeysApiKeySalesChannelsBatchReq,
|
||||||
AdminPostApiKeysReq,
|
AdminPostApiKeysReq,
|
||||||
AdminRevokeApiKeysApiKeyReq,
|
AdminRevokeApiKeysApiKeyReq,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
|
||||||
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||||
@@ -40,12 +41,24 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/api-keys",
|
matcher: "/admin/api-keys",
|
||||||
middlewares: [transformBody(AdminPostApiKeysReq)],
|
middlewares: [
|
||||||
|
transformQuery(
|
||||||
|
AdminGetApiKeysApiKeyParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
transformBody(AdminPostApiKeysReq),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/api-keys/:id",
|
matcher: "/admin/api-keys/:id",
|
||||||
middlewares: [transformBody(AdminPostApiKeysApiKeyReq)],
|
middlewares: [
|
||||||
|
transformQuery(
|
||||||
|
AdminGetApiKeysApiKeyParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
transformBody(AdminPostApiKeysApiKeyReq),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["DELETE"],
|
method: ["DELETE"],
|
||||||
@@ -55,6 +68,23 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/api-keys/:id/revoke",
|
matcher: "/admin/api-keys/:id/revoke",
|
||||||
middlewares: [transformBody(AdminRevokeApiKeysApiKeyReq)],
|
middlewares: [
|
||||||
|
transformQuery(
|
||||||
|
AdminGetApiKeysApiKeyParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
transformBody(AdminRevokeApiKeysApiKeyReq),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/api-keys/:id/sales-channels/batch/add",
|
||||||
|
middlewares: [
|
||||||
|
transformQuery(
|
||||||
|
AdminGetApiKeysApiKeyParams,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
transformBody(AdminPostApiKeysApiKeySalesChannelsBatchReq),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
export const defaultAdminApiKeyRelations = []
|
|
||||||
export const allowedAdminApiKeyRelations = []
|
|
||||||
export const defaultAdminApiKeyFields = [
|
export const defaultAdminApiKeyFields = [
|
||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
@@ -11,16 +9,17 @@ export const defaultAdminApiKeyFields = [
|
|||||||
"created_by",
|
"created_by",
|
||||||
"revoked_at",
|
"revoked_at",
|
||||||
"revoked_by",
|
"revoked_by",
|
||||||
|
"sales_channels.id",
|
||||||
|
"sales_channels.name",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const retrieveTransformQueryConfig = {
|
export const retrieveTransformQueryConfig = {
|
||||||
defaultFields: defaultAdminApiKeyFields,
|
defaults: defaultAdminApiKeyFields,
|
||||||
defaultRelations: defaultAdminApiKeyRelations,
|
|
||||||
allowedRelations: allowedAdminApiKeyRelations,
|
|
||||||
isList: false,
|
isList: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const listTransformQueryConfig = {
|
export const listTransformQueryConfig = {
|
||||||
|
...retrieveTransformQueryConfig,
|
||||||
defaultLimit: 20,
|
defaultLimit: 20,
|
||||||
isList: true,
|
isList: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,33 @@
|
|||||||
|
import { createApiKeysWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { CreateApiKeyDTO } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../types/routing"
|
} from "../../../types/routing"
|
||||||
|
|
||||||
import { CreateApiKeyDTO } from "@medusajs/types"
|
|
||||||
import { createApiKeysWorkflow } from "@medusajs/core-flows"
|
|
||||||
import { defaultAdminApiKeyFields } from "./query-config"
|
|
||||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
|
||||||
|
|
||||||
export const GET = async (
|
export const GET = async (
|
||||||
req: AuthenticatedMedusaRequest,
|
req: AuthenticatedMedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
) => {
|
) => {
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
const queryObject = remoteQueryObjectFromString({
|
const queryObject = remoteQueryObjectFromString({
|
||||||
entryPoint: "api_key",
|
entryPoint: "api_key",
|
||||||
variables: {
|
variables: {
|
||||||
filters: req.filterableFields,
|
filters: req.filterableFields,
|
||||||
order: req.listConfig.order,
|
...req.remoteQueryConfig.pagination,
|
||||||
skip: req.listConfig.skip,
|
|
||||||
take: req.listConfig.take,
|
|
||||||
},
|
},
|
||||||
fields: defaultAdminApiKeyFields,
|
fields: req.remoteQueryConfig.fields,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { rows: apiKeys, metadata } = await remoteQuery(queryObject)
|
const { rows: apiKeys, metadata } = await remoteQuery(queryObject)
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
apiKeys,
|
api_keys: apiKeys,
|
||||||
count: metadata.count,
|
count: metadata.count,
|
||||||
offset: metadata.skip,
|
offset: metadata.skip,
|
||||||
limit: metadata.take,
|
limit: metadata.take,
|
||||||
@@ -55,5 +54,7 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ apiKey: result[0] })
|
// We cannot use remoteQuery here, as we need to show the secret key in the response (and never again)
|
||||||
|
// And the only time we get to see the secret, is when we create it
|
||||||
|
res.status(200).json({ api_key: result[0] })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { OperatorMap } from "@medusajs/types"
|
import { ApiKeyType } from "@medusajs/utils"
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import {
|
import {
|
||||||
IsArray,
|
IsArray,
|
||||||
@@ -9,8 +9,6 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||||
import { OperatorMapValidator } from "../../../types/validators/operator-map"
|
|
||||||
import { ApiKeyType } from "@medusajs/utils"
|
|
||||||
|
|
||||||
export class AdminGetApiKeysApiKeyParams extends FindParams {}
|
export class AdminGetApiKeysApiKeyParams extends FindParams {}
|
||||||
/**
|
/**
|
||||||
@@ -80,3 +78,8 @@ export class AdminRevokeApiKeysApiKeyReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AdminDeleteApiKeysApiKeyReq {}
|
export class AdminDeleteApiKeysApiKeyReq {}
|
||||||
|
|
||||||
|
export class AdminPostApiKeysApiKeySalesChannelsBatchReq {
|
||||||
|
@IsArray()
|
||||||
|
sales_channel_ids: string[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import {
|
|||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||||
|
|
||||||
import { AdminPostStockLocationsLocationReq } from "../validators"
|
import { deleteStockLocationsWorkflow, updateStockLocationsWorkflow } from "@medusajs/core-flows"
|
||||||
import { MedusaError } from "@medusajs/utils"
|
import { MedusaError } from "@medusajs/utils"
|
||||||
import { deleteStockLocationsWorkflow } from "@medusajs/core-flows"
|
import { AdminPostStockLocationsLocationReq } from "../validators"
|
||||||
import { updateStockLocationsWorkflow } from "@medusajs/core-flows"
|
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
req: MedusaRequest<AdminPostStockLocationsLocationReq>,
|
req: MedusaRequest<AdminPostStockLocationsLocationReq>,
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
export * as publishableApiKey from "./publishable-api-key-service"
|
|
||||||
export * as shippingProfile from "./shipping-profile-service"
|
export * as shippingProfile from "./shipping-profile-service"
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
serviceName: "publishableApiKeyService",
|
|
||||||
primaryKeys: ["id"],
|
|
||||||
linkableKeys: { publishable_key_id: "PublishableApiKey" },
|
|
||||||
schema: `
|
|
||||||
scalar Date
|
|
||||||
scalar JSON
|
|
||||||
|
|
||||||
type PublishableApiKey {
|
|
||||||
id: ID!
|
|
||||||
sales_channel_id: String!
|
|
||||||
publishable_key_id: String!
|
|
||||||
created_at: Date!
|
|
||||||
updated_at: Date!
|
|
||||||
deleted_at: Date
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
alias: [
|
|
||||||
{
|
|
||||||
name: ["publishable_api_key", "publishable_api_keys"],
|
|
||||||
args: {
|
|
||||||
entity: "PublishableApiKey",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as ModuleJoinerConfig
|
|
||||||
Reference in New Issue
Block a user