From 7e3ed913a6008a25974634d489f9339c40066125 Mon Sep 17 00:00:00 2001 From: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Fri, 2 Jan 2026 11:27:03 +0100 Subject: [PATCH] chore: Create publishable API key as part of the defaults (#14356) * wip * chore: fix tests * Create tiny-carrots-bathe.md --- .changeset/tiny-carrots-bathe.md | 5 + .../__tests__/api-key/admin/api-key.spec.ts | 17 +++- .../api-key/admin/publishable-key.spec.ts | 2 +- .../__tests__/defaults/defaults.spec.ts | 91 +++++++++++++++++++ .../src/defaults/workflows/create-defaults.ts | 55 ++++++++++- 5 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 .changeset/tiny-carrots-bathe.md create mode 100644 integration-tests/modules/__tests__/defaults/defaults.spec.ts diff --git a/.changeset/tiny-carrots-bathe.md b/.changeset/tiny-carrots-bathe.md new file mode 100644 index 0000000000..c282ac3372 --- /dev/null +++ b/.changeset/tiny-carrots-bathe.md @@ -0,0 +1,5 @@ +--- +"@medusajs/core-flows": patch +--- + +chore: Create publishable API key in defaults diff --git a/integration-tests/http/__tests__/api-key/admin/api-key.spec.ts b/integration-tests/http/__tests__/api-key/admin/api-key.spec.ts index 7fe592de22..38cf1186c7 100644 --- a/integration-tests/http/__tests__/api-key/admin/api-key.spec.ts +++ b/integration-tests/http/__tests__/api-key/admin/api-key.spec.ts @@ -1,5 +1,5 @@ -import { ApiKeyType } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "@medusajs/test-utils" +import { ApiKeyType } from "@medusajs/utils" import { adminHeaders, createAdminUser, @@ -74,7 +74,7 @@ medusaIntegrationTestRunner({ const listedApiKeys = await api.get(`/admin/api-keys`, adminHeaders) expect(deleted.status).toEqual(200) - expect(listedApiKeys.data.api_keys).toHaveLength(0) + expect(listedApiKeys.data.api_keys).toHaveLength(1) // we still have the default publishable api key }) it("should allow searching for api keys", async () => { @@ -108,9 +108,16 @@ medusaIntegrationTestRunner({ expect(listedSecretKeys.data.api_keys[0].title).toEqual( "Test Secret Key" ) - expect(listedPublishableKeys.data.api_keys).toHaveLength(1) - expect(listedPublishableKeys.data.api_keys[0].title).toEqual( - "Test Publishable Key" + expect(listedPublishableKeys.data.api_keys).toHaveLength(2) + expect(listedPublishableKeys.data.api_keys).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: "Test Publishable Key", + }), + expect.objectContaining({ + title: "Default Publishable API Key", + }), + ]) ) }) diff --git a/integration-tests/http/__tests__/api-key/admin/publishable-key.spec.ts b/integration-tests/http/__tests__/api-key/admin/publishable-key.spec.ts index 1e98956c89..bed887be59 100644 --- a/integration-tests/http/__tests__/api-key/admin/publishable-key.spec.ts +++ b/integration-tests/http/__tests__/api-key/admin/publishable-key.spec.ts @@ -61,7 +61,7 @@ medusaIntegrationTestRunner({ adminHeaders ) - expect(response.data.count).toBe(2) + expect(response.data.count).toBe(3) // two created keys and the default publishable api key expect(response.data.limit).toBe(2) expect(response.data.offset).toBe(0) expect(response.data.api_keys).toHaveLength(2) diff --git a/integration-tests/modules/__tests__/defaults/defaults.spec.ts b/integration-tests/modules/__tests__/defaults/defaults.spec.ts new file mode 100644 index 0000000000..4eab9add97 --- /dev/null +++ b/integration-tests/modules/__tests__/defaults/defaults.spec.ts @@ -0,0 +1,91 @@ +import { createDefaultsWorkflow } from "@medusajs/core-flows" +import { Query } from "@medusajs/modules-sdk" +import { medusaIntegrationTestRunner } from "@medusajs/test-utils" + +jest.setTimeout(50000) + +const env = {} + +medusaIntegrationTestRunner({ + env, + testSuite: ({ getContainer }) => { + describe("Defaults", () => { + let appContainer + let query: Query + + beforeAll(async () => { + appContainer = getContainer() + query = appContainer.resolve("query") + }) + + it("should successfully create default data on first run", async () => { + const { + data: [store], + } = await query.graph({ + entity: "store", + fields: ["id", "name", "default_sales_channel_id"], + }) + const { + data: [salesChannel], + } = await query.graph({ + entity: "sales_channel", + fields: ["id", "name"], + }) + const { + data: [publishableApiKey], + } = await query.graph({ + entity: "api_key", + fields: ["id", "type", "title"], + filters: { + type: "publishable", + }, + }) + + expect(store).toEqual( + expect.objectContaining({ + id: expect.any(String), + name: "Medusa Store", + default_sales_channel_id: salesChannel.id, + }) + ) + expect(salesChannel).toEqual( + expect.objectContaining({ + id: expect.any(String), + name: "Default Sales Channel", + }) + ) + expect(publishableApiKey).toEqual( + expect.objectContaining({ + id: expect.any(String), + title: "Default Publishable API Key", + type: "publishable", + }) + ) + }) + + it("should skip creating default data on n+1 runs", async () => { + await createDefaultsWorkflow(appContainer).run() + + const { data: stores } = await query.graph({ + entity: "store", + fields: ["id"], + }) + const { data: salesChannels } = await query.graph({ + entity: "sales_channel", + fields: ["id"], + }) + const { data: publishableApiKeys } = await query.graph({ + entity: "api_key", + fields: ["id", "type"], + filters: { + type: "publishable", + }, + }) + + expect(stores.length).toEqual(1) + expect(salesChannels.length).toEqual(1) + expect(publishableApiKeys.length).toEqual(1) + }) + }) + }, +}) diff --git a/packages/core/core-flows/src/defaults/workflows/create-defaults.ts b/packages/core/core-flows/src/defaults/workflows/create-defaults.ts index 8103ac84a6..92afd9a0f6 100644 --- a/packages/core/core-flows/src/defaults/workflows/create-defaults.ts +++ b/packages/core/core-flows/src/defaults/workflows/create-defaults.ts @@ -1,7 +1,14 @@ import { createWorkflow, + transform, + when, WorkflowResponse, } from "@medusajs/framework/workflows-sdk" +import { + createApiKeysStep, + linkSalesChannelsToApiKeyWorkflow, +} from "../../api-key" +import { useQueryGraphStep } from "../../common" import { createDefaultSalesChannelStep } from "../../sales-channel" import { createDefaultStoreStep } from "../steps/create-default-store" @@ -10,16 +17,16 @@ export const createDefaultsWorkflowID = "create-defaults" * This workflow creates default data for a Medusa application, including * a default sales channel and store. The Medusa application uses this workflow * to create the default data, if not existing, when the application is first started. - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to * create default data within your custom flows, such as seed scripts. - * + * * @example * const { result } = await createDefaultsWorkflow(container) * .run() - * + * * @summary - * + * * Create default data for a Medusa application. */ export const createDefaultsWorkflow = createWorkflow( @@ -37,6 +44,46 @@ export const createDefaultsWorkflow = createWorkflow( }, }) + const publishableApiKey = useQueryGraphStep({ + entity: "api_key", + fields: ["id", "type"], + filters: { + type: "publishable", + }, + options: { + isList: false, + }, + }) + + when( + { publishableApiKey }, + ({ publishableApiKey }) => !publishableApiKey?.data + ).then(() => { + const createResult = createApiKeysStep({ + api_keys: [ + { + title: "Default Publishable API Key", + type: "publishable", + created_by: "", + }, + ], + }) + + const publishableApiKey = transform( + { createResult }, + ({ createResult }) => { + return createResult[0] + } + ) + + linkSalesChannelsToApiKeyWorkflow.runAsStep({ + input: { + id: publishableApiKey.id, + add: [salesChannel.id], + }, + }) + }) + return new WorkflowResponse(store) } )