Skip to content

Commit

Permalink
feat: Remove sales channels from pub keys (#6876)
Browse files Browse the repository at this point in the history
**What**
- Add workflow + step for detaching sales channels from pub API keys
- Tweak linking error message to be more helpful
- Add `removeRemoteLink` step to delete API key workflow
  • Loading branch information
olivermrbl authored Mar 29, 2024
1 parent 8fd1488 commit 1bcb13f
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 26 deletions.
8 changes: 8 additions & 0 deletions .changeset/hot-bobcats-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@medusajs/medusa": patch
"@medusajs/core-flows": patch
"@medusajs/link-modules": patch
"@medusajs/modules-sdk": patch
---

feat: Remove sales channels from pub keys
123 changes: 122 additions & 1 deletion integration-tests/api/__tests__/admin/api-key.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiKeyType } from "@medusajs/utils"
import { ApiKeyType, ContainerRegistrationKeys } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createAdminUser } from "../../../helpers/create-admin-user"

Expand Down Expand Up @@ -268,6 +268,127 @@ medusaIntegrationTestRunner({
"Sales channels with IDs phony do not exist"
)
})

it("should detach sales channels from 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",
}),
])

const keyWithoutChannelsRes = await api.post(
`/admin/api-keys/${api_key.id}/sales-channels/batch/remove`,
{
sales_channel_ids: [sales_channel.id],
},
adminHeaders
)

const { api_key: keyWithoutChannels } = keyWithoutChannelsRes.data

expect(keyWithoutChannelsRes.status).toEqual(200)
expect(keyWithoutChannels.title).toEqual("Test publishable KEY")
expect(keyWithoutChannels.sales_channels).toEqual([])
})

it("should detach sales channels from a publishable API key on delete", 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",
}),
])

await api.delete(`/admin/api-keys/${api_key.id}`, adminHeaders)

const deletedApiKeys = await api.get(
`/admin/api-keys?id=${api_key.id}`,
adminHeaders
)

expect(deletedApiKeys.data.api_keys).toHaveLength(0)

const remoteQuery = container.resolve(
ContainerRegistrationKeys.REMOTE_QUERY
)

// Not the prettiest, but an easy way to check if the link was removed
const channels = await remoteQuery({
publishable_api_key_sales_channels: {
__args: { sales_channel_id: [sales_channel.id] },
fields: ["id", "sales_channel_id", "publishable_key_id"],
},
})

expect(channels).toHaveLength(0)
})
})
},
})
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export const associateApiKeysWithSalesChannelsStep = createStep(
async (input: StepInput, { container }) => {
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)

if (!input.links) {
return
}

const links = input.links
.map((link) => {
return link.sales_channel_ids.map((id) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 detachApiKeysWithSalesChannelsStepId =
"detach-sales-channels-with-api-keys"
export const detachApiKeysWithSalesChannelsStep = createStep(
detachApiKeysWithSalesChannelsStepId,
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()

await remoteLink.dismiss(links)

return new StepResponse(void 0, links)
},
async (links, { container }) => {
if (!links?.length) {
return
}

const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)

await remoteLink.create(links)
}
)
1 change: 1 addition & 0 deletions packages/core-flows/src/api-key/steps/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./associate-sales-channels-with-publishable-keys"
export * from "./create-api-keys"
export * from "./delete-api-keys"
export * from "./detach-sales-channels-from-publishable-keys"
export * from "./revoke-api-keys"
export * from "./update-api-keys"
export * from "./validate-sales-channel-exists"
9 changes: 8 additions & 1 deletion packages/core-flows/src/api-key/workflows/delete-api-keys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Modules } from "@medusajs/modules-sdk"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { removeRemoteLinkStep } from "../../common/steps/remove-remote-links"
import { deleteApiKeysStep } from "../steps"

type WorkflowInput = { ids: string[] }
Expand All @@ -7,6 +9,11 @@ export const deleteApiKeysWorkflowId = "delete-api-keys"
export const deleteApiKeysWorkflow = createWorkflow(
deleteApiKeysWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
return deleteApiKeysStep(input.ids)
deleteApiKeysStep(input.ids)

// Please note, the ids here should be publishable key IDs
removeRemoteLinkStep({
[Modules.API_KEY]: { publishable_key_id: input.ids },
})
}
)
1 change: 1 addition & 0 deletions packages/core-flows/src/api-key/workflows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./delete-api-keys"
export * from "./update-api-keys"
export * from "./revoke-api-keys"
export * from "./add-sales-channels-to-publishable-key"
export * from "./remove-sales-channels-from-publishable-key"
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { detachApiKeysWithSalesChannelsStep } from "../steps/detach-sales-channels-from-publishable-keys"

type WorkflowInput = {
data: {
api_key_id: string
sales_channel_ids: string[]
}[]
}

export const removeSalesChannelsFromApiKeyWorkflowId =
"remove-sales-channels-from-api-key"
export const removeSalesChannelsFromApiKeyWorkflow = createWorkflow(
removeSalesChannelsFromApiKeyWorkflowId,
(input: WorkflowData<WorkflowInput>) => {
detachApiKeysWithSalesChannelsStep({ links: input.data })
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
},
alias: [
{
name: "publishable_api_key_sales_channel",
},
{
name: "publishable_api_key_sales_channels",
name: [
"publishable_api_key_sales_channel",
"publishable_api_key_sales_channels",
],
},
],
primaryKeys: ["id", "publishable_key_id", "sales_channel_id"],
Expand Down Expand Up @@ -49,7 +49,7 @@ export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
{
serviceName: Modules.SALES_CHANNEL,
fieldAlias: {
api_keys: "api_keys_link.api_key",
publishable_api_keys: "api_keys_link.api_key",
},
relationship: {
serviceName: LINKS.PublishableApiKeySalesChannel,
Expand Down
24 changes: 12 additions & 12 deletions packages/link-modules/src/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ export const LINKS = {
Modules.STOCK_LOCATION,
"location_id"
),
PublishableApiKeySalesChannel: composeLinkName(
Modules.API_KEY,
"api_key_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
ProductSalesChannel: composeLinkName(
Modules.PRODUCT,
"product_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),

// Internal services
ProductShippingProfile: composeLinkName(
Expand All @@ -58,22 +70,10 @@ export const LINKS = {
"shippingProfileService",
"profile_id"
),
ProductSalesChannel: composeLinkName(
Modules.PRODUCT,
"product_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
OrderSalesChannel: composeLinkName(
"orderService",
"order_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
PublishableApiKeySalesChannel: composeLinkName(
Modules.API_KEY,
"api_key_id",
Modules.SALES_CHANNEL,
"sales_channel_id"
),
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { AdminPostApiKeysApiKeySalesChannelsBatchReq } from "../../../../validators"
import { AdminPostApiKeysApiKeySalesChannelsBatchAddReq } from "../../../../validators"

export const POST = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const body = req.validatedBody as AdminPostApiKeysApiKeySalesChannelsBatchReq
const body =
req.validatedBody as AdminPostApiKeysApiKeySalesChannelsBatchAddReq

const apiKeyModule = req.scope.resolve(ModuleRegistrationName.API_KEY)

Expand Down
Loading

0 comments on commit 1bcb13f

Please sign in to comment.