From 59085681c723f3837cce22ff325462be36c109da Mon Sep 17 00:00:00 2001 From: Ariel Caplan Date: Mon, 14 Sep 2026 16:04:14 +0300 Subject: [PATCH] Record dev store shop id in command analytics Dev store creations can't be attributed to the CLI today. Both the CLI and the dev dashboard call the same Business Platform mutation, and the `store_creations` row it writes keeps no caller, so the only way to identify CLI-created dev stores is a regex on the shop signup request's user agent. Request `shopifyShopId` from the create mutation and record it, along with the store domain, as command metadata. Preview stores already do this, so move `recordStoreFqdnMetadata` into `organizations` and share it rather than duplicating the metadata shape in two packages. `store` already depends on `organizations`, so its own attribution module becomes a one-line re-export, matching the shims it already uses for `createDevStore` and `businessPlatformTokenRefreshHandler`. Recorded before polling, so a store that is created but never reaches COMPLETE is still attributed. Also take the helper's arguments as an options object. `validated` is a positional boolean at nine call sites, where `recordStoreFqdnMetadata(store, false)` gives a reader nothing to go on, and the optional id made argument order the only thing distinguishing the two. Co-Authored-By: Claude Opus 5 (1M context) Assisted-By: devx/47074c59-ea41-4708-aac2-7fccae638d6f --- .../generated/create_app_development_store.ts | 2 + .../create_app_development_store.graphql | 1 + .../cli/services/dev/create-dev-store.test.ts | 83 +++++++++++++++++-- .../src/cli/services/dev/create-dev-store.ts | 18 +++- .../cli/services/store-attribution.test.ts} | 16 +++- .../src/cli/services/store-attribution.ts | 29 +++++++ packages/organizations/src/index.ts | 1 + .../src/cli/services/store/attribution.ts | 14 +--- .../services/store/auth/admin-session.test.ts | 2 +- .../cli/services/store/auth/admin-session.ts | 2 +- .../src/cli/services/store/auth/index.test.ts | 20 ++--- .../src/cli/services/store/auth/index.ts | 4 +- .../store/bulk/bulk-admin-context.test.ts | 2 +- .../services/store/bulk/bulk-admin-context.ts | 2 +- .../store/create/preview/index.test.ts | 12 ++- .../services/store/create/preview/index.ts | 6 +- .../cli/services/store/execute/index.test.ts | 4 +- .../src/cli/services/store/execute/index.ts | 2 +- .../src/cli/services/store/info/index.test.ts | 6 +- .../src/cli/services/store/info/index.ts | 8 +- 20 files changed, 183 insertions(+), 51 deletions(-) rename packages/{store/src/cli/services/store/attribution.test.ts => organizations/src/cli/services/store-attribution.test.ts} (67%) create mode 100644 packages/organizations/src/cli/services/store-attribution.ts diff --git a/packages/organizations/src/cli/api/graphql/business-platform-organizations/generated/create_app_development_store.ts b/packages/organizations/src/cli/api/graphql/business-platform-organizations/generated/create_app_development_store.ts index dc279d48a68..4cfc3366a30 100644 --- a/packages/organizations/src/cli/api/graphql/business-platform-organizations/generated/create_app_development_store.ts +++ b/packages/organizations/src/cli/api/graphql/business-platform-organizations/generated/create_app_development_store.ts @@ -15,6 +15,7 @@ export type CreateAppDevelopmentStoreMutation = { createAppDevelopmentStore: { shopAdminUrl?: string | null shopDomain?: string | null + shopifyShopId?: string | null userErrors?: {code?: string | null; field: string[]; message: string}[] | null } } @@ -91,6 +92,7 @@ export const CreateAppDevelopmentStore = { selections: [ {kind: 'Field', name: {kind: 'Name', value: 'shopAdminUrl'}}, {kind: 'Field', name: {kind: 'Name', value: 'shopDomain'}}, + {kind: 'Field', name: {kind: 'Name', value: 'shopifyShopId'}}, { kind: 'Field', name: {kind: 'Name', value: 'userErrors'}, diff --git a/packages/organizations/src/cli/api/graphql/business-platform-organizations/mutations/create_app_development_store.graphql b/packages/organizations/src/cli/api/graphql/business-platform-organizations/mutations/create_app_development_store.graphql index b6da18eb81e..d73d811f876 100644 --- a/packages/organizations/src/cli/api/graphql/business-platform-organizations/mutations/create_app_development_store.graphql +++ b/packages/organizations/src/cli/api/graphql/business-platform-organizations/mutations/create_app_development_store.graphql @@ -8,6 +8,7 @@ mutation CreateAppDevelopmentStore($shopName: String!, $priceLookupKey: String!, ) { shopAdminUrl shopDomain + shopifyShopId userErrors { code field diff --git a/packages/organizations/src/cli/services/dev/create-dev-store.test.ts b/packages/organizations/src/cli/services/dev/create-dev-store.test.ts index d6da30ff688..8bdbe3318dd 100644 --- a/packages/organizations/src/cli/services/dev/create-dev-store.test.ts +++ b/packages/organizations/src/cli/services/dev/create-dev-store.test.ts @@ -1,4 +1,5 @@ import {createDevStore} from './create-dev-store.js' +import {recordStoreFqdnMetadata} from '../store-attribution.js' import {describe, expect, test, vi, beforeEach} from 'vitest' import {businessPlatformOrganizationsRequestDoc} from '@shopify/cli-kit/node/api/business-platform' @@ -32,13 +33,21 @@ vi.mock('@shopify/cli-kit/node/system', () => ({ sleep: vi.fn(), })) +vi.mock('../store-attribution.js') + const defaultOrg = {id: '123', businessName: 'Test Org'} -const defaultMutationResult = { - createAppDevelopmentStore: { - shopAdminUrl: 'https://test-store.myshopify.com/admin', - shopDomain: 'test-store.myshopify.com', - userErrors: [], - }, + +// The default shop id is a global id because that's what Business Platform returns. Tests that +// assert on the recorded id pass their own, so the value under test sits next to its expectation. +function mutationResult(shopifyShopId: string | null = 'gid://shopify/Shop/456') { + return { + createAppDevelopmentStore: { + shopAdminUrl: 'https://test-store.myshopify.com/admin', + shopDomain: 'test-store.myshopify.com', + shopifyShopId, + userErrors: [], + }, + } } beforeEach(() => { @@ -52,7 +61,7 @@ beforeEach(() => { describe('createDevStore', () => { test('returns the polled shop domain without rendering output when summary is false', async () => { vi.mocked(businessPlatformOrganizationsRequestDoc) - .mockResolvedValueOnce(defaultMutationResult) + .mockResolvedValueOnce(mutationResult()) .mockResolvedValueOnce({ organization: {id: '123', storeCreation: {status: 'COMPLETE'}}, }) @@ -75,4 +84,64 @@ describe('createDevStore', () => { expect(renderSuccess).not.toHaveBeenCalled() expect(outputResult).not.toHaveBeenCalled() }) + + test('records the created store with the numeric id decoded from the returned global id', async () => { + vi.mocked(businessPlatformOrganizationsRequestDoc) + .mockResolvedValueOnce(mutationResult('gid://shopify/Shop/456')) + .mockResolvedValueOnce({organization: {id: '123', storeCreation: {status: 'COMPLETE'}}}) + + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', summary: false}) + + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({ + storeFqdn: 'test-store.myshopify.com', + validated: true, + storeId: '456', + }) + }) + + test('records a shop id that is already numeric unchanged', async () => { + vi.mocked(businessPlatformOrganizationsRequestDoc) + .mockResolvedValueOnce(mutationResult('456')) + .mockResolvedValueOnce({organization: {id: '123', storeCreation: {status: 'COMPLETE'}}}) + + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', summary: false}) + + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({ + storeFqdn: 'test-store.myshopify.com', + validated: true, + storeId: '456', + }) + }) + + test('records the store domain without an id when no shop id is returned', async () => { + vi.mocked(businessPlatformOrganizationsRequestDoc) + .mockResolvedValueOnce(mutationResult(null)) + .mockResolvedValueOnce({organization: {id: '123', storeCreation: {status: 'COMPLETE'}}}) + + await createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', summary: false}) + + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({ + storeFqdn: 'test-store.myshopify.com', + validated: true, + storeId: undefined, + }) + }) + + // The store exists server-side even when polling never reports COMPLETE, so attribution has to be + // recorded before the wait rather than after it. + test('records the created store even when polling reports a failure', async () => { + vi.mocked(businessPlatformOrganizationsRequestDoc) + .mockResolvedValueOnce(mutationResult('456')) + .mockResolvedValueOnce({organization: {id: '123', storeCreation: {status: 'FAILED'}}}) + + await expect( + createDevStore({name: 'test-store', organization: defaultOrg, plan: 'plus', summary: false}), + ).rejects.toThrow('Store creation failed with status: FAILED') + + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({ + storeFqdn: 'test-store.myshopify.com', + validated: true, + storeId: '456', + }) + }) }) diff --git a/packages/organizations/src/cli/services/dev/create-dev-store.ts b/packages/organizations/src/cli/services/dev/create-dev-store.ts index 68c6fbe710e..1e8571dc6e2 100644 --- a/packages/organizations/src/cli/services/dev/create-dev-store.ts +++ b/packages/organizations/src/cli/services/dev/create-dev-store.ts @@ -5,7 +5,9 @@ import { } from '../../api/graphql/business-platform-organizations/generated/poll_store_creation.js' import {Organization} from '../../models/organization.js' import {businessPlatformTokenRefreshHandler} from '../business-platform.js' +import {recordStoreFqdnMetadata} from '../store-attribution.js' import {businessPlatformOrganizationsRequestDoc} from '@shopify/cli-kit/node/api/business-platform' +import {numericIdFromGid} from '@shopify/cli-kit/common/gid' import {AbortError} from '@shopify/cli-kit/node/error' import {outputContent, outputResult} from '@shopify/cli-kit/node/output' import {sleep} from '@shopify/cli-kit/node/system' @@ -89,11 +91,16 @@ export async function createDevStore(options: CreateDevStoreOptions): Promise { @@ -175,6 +182,15 @@ export async function createDevStore(options: CreateDevStoreOptions): Promise { }) test('records the sensitive, hashed, validation, and public store domain for a store fqdn', async () => { - await recordStoreFqdnMetadata('shop.myshopify.com', true) + await recordStoreFqdnMetadata({storeFqdn: 'shop.myshopify.com', validated: true}) expect(addSensitiveMetadata).toHaveBeenCalledWith(expect.any(Function)) expect(vi.mocked(addSensitiveMetadata).mock.calls[0]![0]()).toEqual({store_fqdn: 'shop.myshopify.com'}) @@ -26,7 +26,7 @@ describe('store command attribution', () => { }) test('records the numeric store id when provided', async () => { - await recordStoreFqdnMetadata('shop.myshopify.com', true, '123') + await recordStoreFqdnMetadata({storeFqdn: 'shop.myshopify.com', validated: true, storeId: '123'}) expect(vi.mocked(addPublicMetadata).mock.calls[0]![0]()).toEqual({ store_fqdn_hash: 'hashed-store', @@ -35,4 +35,14 @@ describe('store command attribution', () => { store_id: 123, }) }) + + test('omits the store id when it is not numeric', async () => { + await recordStoreFqdnMetadata({storeFqdn: 'shop.myshopify.com', validated: true, storeId: 'gid://shopify/Shop/123'}) + + expect(vi.mocked(addPublicMetadata).mock.calls[0]![0]()).toEqual({ + store_fqdn_hash: 'hashed-store', + store_fqdn_validated: true, + store_domain: 'shop.myshopify.com', + }) + }) }) diff --git a/packages/organizations/src/cli/services/store-attribution.ts b/packages/organizations/src/cli/services/store-attribution.ts new file mode 100644 index 00000000000..1db0b04704f --- /dev/null +++ b/packages/organizations/src/cli/services/store-attribution.ts @@ -0,0 +1,29 @@ +import {hashString} from '@shopify/cli-kit/node/crypto' +import {addPublicMetadata, addSensitiveMetadata} from '@shopify/cli-kit/node/metadata' +import {tryParseInt} from '@shopify/cli-kit/common/string' + +interface RecordStoreFqdnMetadataOptions { + /** The store's fully qualified domain name. */ + storeFqdn: string + /** Whether the fqdn was confirmed against the platform, rather than taken from user input as-is. */ + validated: boolean + /** The store's numeric id as a string. Ignored when it isn't numeric. */ + storeId?: string +} + +/** + * Records the store a command acted on, so command analytics can be grouped by store. + * + * @param options - The store to attribute the command to. + */ +export async function recordStoreFqdnMetadata(options: RecordStoreFqdnMetadataOptions): Promise { + const {storeFqdn, validated, storeId} = options + + await addSensitiveMetadata(() => ({store_fqdn: storeFqdn})) + await addPublicMetadata(() => ({ + store_fqdn_hash: hashString(storeFqdn), + store_fqdn_validated: validated, + store_domain: storeFqdn, + store_id: tryParseInt(storeId), + })) +} diff --git a/packages/organizations/src/index.ts b/packages/organizations/src/index.ts index 4d6a46a5f57..7511ff6817d 100644 --- a/packages/organizations/src/index.ts +++ b/packages/organizations/src/index.ts @@ -3,6 +3,7 @@ export {selectOrg} from './cli/services/select.js' export {selectOrganizationPrompt} from './cli/prompts/organization.js' export type {Organization} from './cli/models/organization.js' export {businessPlatformTokenRefreshHandler} from './cli/services/business-platform.js' +export {recordStoreFqdnMetadata} from './cli/services/store-attribution.js' export {createDevStore, devStorePlanHandles} from './cli/services/dev/create-dev-store.js' export type {CreateDevStoreOptions, DevStorePlan} from './cli/services/dev/create-dev-store.js' export {devStoreNamePrompt, devStorePlanPrompt, devStoreDemoDataPrompt} from './cli/prompts/dev.js' diff --git a/packages/store/src/cli/services/store/attribution.ts b/packages/store/src/cli/services/store/attribution.ts index db19d8aef6f..c2ca5406eee 100644 --- a/packages/store/src/cli/services/store/attribution.ts +++ b/packages/store/src/cli/services/store/attribution.ts @@ -1,13 +1 @@ -import {hashString} from '@shopify/cli-kit/node/crypto' -import {addPublicMetadata, addSensitiveMetadata} from '@shopify/cli-kit/node/metadata' -import {tryParseInt} from '@shopify/cli-kit/common/string' - -export async function recordStoreFqdnMetadata(storeFqdn: string, validated: boolean, storeId?: string): Promise { - await addSensitiveMetadata(() => ({store_fqdn: storeFqdn})) - await addPublicMetadata(() => ({ - store_fqdn_hash: hashString(storeFqdn), - store_fqdn_validated: validated, - store_domain: storeFqdn, - store_id: tryParseInt(storeId), - })) -} +export {recordStoreFqdnMetadata} from '@shopify/organizations' diff --git a/packages/store/src/cli/services/store/auth/admin-session.test.ts b/packages/store/src/cli/services/store/auth/admin-session.test.ts index 35773362d74..213d5285dc6 100644 --- a/packages/store/src/cli/services/store/auth/admin-session.test.ts +++ b/packages/store/src/cli/services/store/auth/admin-session.test.ts @@ -29,7 +29,7 @@ describe('loadAdminSessionFromStoreAuth', () => { const got = await loadAdminSessionFromStoreAuth('https://preview.myshopify.com/admin') expect(loadStoredStoreSession).toHaveBeenCalledWith('preview.myshopify.com') - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('preview.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'preview.myshopify.com', validated: true}) expect(setLastSeenUserId).toHaveBeenCalledWith('preview:123') expect(got).toEqual({ adminSession: {token: 'shpat_token', storeFqdn: 'preview.myshopify.com'}, diff --git a/packages/store/src/cli/services/store/auth/admin-session.ts b/packages/store/src/cli/services/store/auth/admin-session.ts index e83239eb946..bced63f6972 100644 --- a/packages/store/src/cli/services/store/auth/admin-session.ts +++ b/packages/store/src/cli/services/store/auth/admin-session.ts @@ -10,7 +10,7 @@ export async function loadAdminSessionFromStoreAuth(store: string): Promise<{ session: StoredStoreAppSession }> { const session = await loadStoredStoreSession(normalizeStoreFqdn(store)) - await recordStoreFqdnMetadata(session.store, true) + await recordStoreFqdnMetadata({storeFqdn: session.store, validated: true}) setLastSeenUserId(session.userId) return { diff --git a/packages/store/src/cli/services/store/auth/index.test.ts b/packages/store/src/cli/services/store/auth/index.test.ts index 63dad5722a1..e664bb49d4f 100644 --- a/packages/store/src/cli/services/store/auth/index.test.ts +++ b/packages/store/src/cli/services/store/auth/index.test.ts @@ -56,8 +56,8 @@ describe('store auth service', () => { }), ) expect(presenter.success).toHaveBeenCalledWith(result) - expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(1, 'shop.myshopify.com', false) - expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(2, 'shop.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(1, {storeFqdn: 'shop.myshopify.com', validated: false}) + expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(2, {storeFqdn: 'shop.myshopify.com', validated: true}) expect(setLastSeenUserId).toHaveBeenCalledWith('42') const storedSession = vi.mocked(setStoredStoreAppSession).mock.calls[0]![0] @@ -370,8 +370,8 @@ describe('store auth service', () => { ), ).rejects.toThrow('scope lookup failed') - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('shop.myshopify.com', false) - expect(recordStoreFqdnMetadata).not.toHaveBeenCalledWith('shop.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: false}) + expect(recordStoreFqdnMetadata).not.toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: true}) expect(setLastSeenUserId).not.toHaveBeenCalled() expect(setStoredStoreAppSession).not.toHaveBeenCalled() }) @@ -398,8 +398,8 @@ describe('store auth service', () => { ), ).rejects.toThrow('callback failed') - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('shop.myshopify.com', false) - expect(recordStoreFqdnMetadata).not.toHaveBeenCalledWith('shop.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: false}) + expect(recordStoreFqdnMetadata).not.toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: true}) expect(setStoredStoreAppSession).not.toHaveBeenCalled() }) @@ -428,8 +428,8 @@ describe('store auth service', () => { ), ).rejects.toThrow('token exchange failed') - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('shop.myshopify.com', false) - expect(recordStoreFqdnMetadata).not.toHaveBeenCalledWith('shop.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: false}) + expect(recordStoreFqdnMetadata).not.toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: true}) expect(setLastSeenUserId).not.toHaveBeenCalled() expect(setStoredStoreAppSession).not.toHaveBeenCalled() }) @@ -463,8 +463,8 @@ describe('store auth service', () => { ), ).rejects.toThrow('Shopify did not return associated user information for the online access token.') - expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(1, 'shop.myshopify.com', false) - expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(2, 'shop.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(1, {storeFqdn: 'shop.myshopify.com', validated: false}) + expect(recordStoreFqdnMetadata).toHaveBeenNthCalledWith(2, {storeFqdn: 'shop.myshopify.com', validated: true}) expect(setLastSeenUserId).not.toHaveBeenCalled() expect(setStoredStoreAppSession).not.toHaveBeenCalled() }) diff --git a/packages/store/src/cli/services/store/auth/index.ts b/packages/store/src/cli/services/store/auth/index.ts index dbf6443f345..2e50cb962c6 100644 --- a/packages/store/src/cli/services/store/auth/index.ts +++ b/packages/store/src/cli/services/store/auth/index.ts @@ -48,7 +48,7 @@ export async function authenticateStoreWithApp( throwIfPreviewStore(store, resolvedDependencies) - await recordStoreFqdnMetadata(store, false) + await recordStoreFqdnMetadata({storeFqdn: store, validated: false}) const requestedScopes = parseStoreAuthScopes(input.scopes) const existingScopeResolution = await resolvedDependencies.resolveExistingScopes(store) const scopes = mergeRequestedAndStoredScopes(requestedScopes, existingScopeResolution.scopes) @@ -80,7 +80,7 @@ export async function authenticateStoreWithApp( }, }) const tokenResponse = await bootstrap.exchangeCodeForToken(code) - await recordStoreFqdnMetadata(store, true) + await recordStoreFqdnMetadata({storeFqdn: store, validated: true}) const userId = tokenResponse.associated_user?.id?.toString() if (!userId) { diff --git a/packages/store/src/cli/services/store/bulk/bulk-admin-context.test.ts b/packages/store/src/cli/services/store/bulk/bulk-admin-context.test.ts index a2c8bd3acf1..7831d96a8a4 100644 --- a/packages/store/src/cli/services/store/bulk/bulk-admin-context.test.ts +++ b/packages/store/src/cli/services/store/bulk/bulk-admin-context.test.ts @@ -28,7 +28,7 @@ describe('prepareBulkAdminContext', () => { const result = await prepareBulkAdminContext(store) expect(loadStoredStoreSession).toHaveBeenCalledWith(store) - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith(store, true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: store, validated: true}) expect(setLastSeenUserId).toHaveBeenCalledWith('42') expect(result).toEqual({token: 'token', storeFqdn: store}) }) diff --git a/packages/store/src/cli/services/store/bulk/bulk-admin-context.ts b/packages/store/src/cli/services/store/bulk/bulk-admin-context.ts index 3059e4de999..b7ed0ecdb2e 100644 --- a/packages/store/src/cli/services/store/bulk/bulk-admin-context.ts +++ b/packages/store/src/cli/services/store/bulk/bulk-admin-context.ts @@ -13,7 +13,7 @@ import type {AdminSession} from '@shopify/cli-kit/node/session' */ export async function prepareBulkAdminContext(store: string): Promise { const session = await loadStoredStoreSession(store) - await recordStoreFqdnMetadata(session.store, true) + await recordStoreFqdnMetadata({storeFqdn: session.store, validated: true}) setLastSeenUserId(session.userId) return { diff --git a/packages/store/src/cli/services/store/create/preview/index.test.ts b/packages/store/src/cli/services/store/create/preview/index.test.ts index a1e46275219..d2a6499df4e 100644 --- a/packages/store/src/cli/services/store/create/preview/index.test.ts +++ b/packages/store/src/cli/services/store/create/preview/index.test.ts @@ -44,7 +44,11 @@ describe('preview store create service', () => { }, }) expect(recordStoreFqdnMetadata).toHaveBeenCalledOnce() - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('x12y45z.myshopify.com', true, '123') + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({ + storeFqdn: 'x12y45z.myshopify.com', + validated: true, + storeId: '123', + }) expect(setLastSeenUserId).toHaveBeenCalledWith('placeholder-uuid') expect(result).toEqual({ status: 'success', @@ -133,7 +137,11 @@ describe('preview store create service', () => { expect(setStoredStoreAppSession).toHaveBeenCalledOnce() expect(setLastSeenUserId).toHaveBeenCalledWith('123') expect(recordStoreFqdnMetadata).toHaveBeenCalledOnce() - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('x12y45z.myshopify.com', true, '123') + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({ + storeFqdn: 'x12y45z.myshopify.com', + validated: true, + storeId: '123', + }) expect(result.status).toBe('success') }) diff --git a/packages/store/src/cli/services/store/create/preview/index.ts b/packages/store/src/cli/services/store/create/preview/index.ts index 4fd2d111156..9a52c73ff59 100644 --- a/packages/store/src/cli/services/store/create/preview/index.ts +++ b/packages/store/src/cli/services/store/create/preview/index.ts @@ -85,7 +85,11 @@ async function persistPreviewStoreSession( }) dependencies.setLastSeenUserId(userId) try { - await dependencies.recordStoreFqdnMetadata(response.shop.domain, true, response.shop.id) + await dependencies.recordStoreFqdnMetadata({ + storeFqdn: response.shop.domain, + validated: true, + storeId: response.shop.id, + }) // eslint-disable-next-line no-catch-all/no-catch-all } catch { // Store metadata is best-effort; credentials and access URL are already persisted. diff --git a/packages/store/src/cli/services/store/execute/index.test.ts b/packages/store/src/cli/services/store/execute/index.test.ts index ddebf81a44e..8ebf6cb571c 100644 --- a/packages/store/src/cli/services/store/execute/index.test.ts +++ b/packages/store/src/cli/services/store/execute/index.test.ts @@ -61,7 +61,7 @@ describe('executeStoreOperation', () => { }), ).resolves.toEqual(result) - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('shop.myshopify.com', false) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: false}) expect(getStoreGraphQLTarget).toHaveBeenCalledWith('admin') expect(prepareStoreExecuteRequest).toHaveBeenCalledWith({ query: 'query { shop { name } }', @@ -96,7 +96,7 @@ describe('executeStoreOperation', () => { }), ).rejects.toThrow('Query should have a value') - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('shop.myshopify.com', false) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'shop.myshopify.com', validated: false}) expect(target.prepareContext).not.toHaveBeenCalled() expect(target.execute).not.toHaveBeenCalled() }) diff --git a/packages/store/src/cli/services/store/execute/index.ts b/packages/store/src/cli/services/store/execute/index.ts index 985801ae8fd..253ec07b48f 100644 --- a/packages/store/src/cli/services/store/execute/index.ts +++ b/packages/store/src/cli/services/store/execute/index.ts @@ -16,7 +16,7 @@ interface ExecuteStoreOperationInput { } export async function executeStoreOperation(input: ExecuteStoreOperationInput): Promise { - await recordStoreFqdnMetadata(input.store, false) + await recordStoreFqdnMetadata({storeFqdn: input.store, validated: false}) const target = getStoreGraphQLTarget(input.api ?? 'admin') const request = await prepareStoreExecuteRequest({ diff --git a/packages/store/src/cli/services/store/info/index.test.ts b/packages/store/src/cli/services/store/info/index.test.ts index 9d5dfef1ba4..6814969772a 100644 --- a/packages/store/src/cli/services/store/info/index.test.ts +++ b/packages/store/src/cli/services/store/info/index.test.ts @@ -178,7 +178,7 @@ describe('getStoreInfo', () => { expect(fetchDestinationsContext).not.toHaveBeenCalled() expect(fetchOrganizationShop).not.toHaveBeenCalled() - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith(SHOP, true, '123') + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: SHOP, validated: true, storeId: '123'}) expect(setLastSeenUserId).toHaveBeenCalledWith('placeholder-uuid') expect(getPreviewStore).toHaveBeenCalledWith({ shopId: '123', @@ -354,7 +354,7 @@ describe('getStoreInfo', () => { expect(fetchDestinationsContext).toHaveBeenCalledWith({store: SHOP, noPrompt: true}) expect(fetchOrganizationShop).not.toHaveBeenCalled() expect(loadStoredStoreSession).toHaveBeenCalledWith(SHOP) - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith(SHOP, true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: SHOP, validated: true}) expect(setLastSeenUserId).toHaveBeenCalledWith('42') expect(adminUrl).toHaveBeenCalledWith(SHOP, 'unstable') expect(graphqlRequest).toHaveBeenCalledWith({ @@ -418,7 +418,7 @@ The CLI is currently unable to prompt for reauthentication.`) const result = await getStoreInfo({store: SHOP}) - expect(recordStoreFqdnMetadata).toHaveBeenCalledWith('permanent-shop.myshopify.com', true) + expect(recordStoreFqdnMetadata).toHaveBeenCalledWith({storeFqdn: 'permanent-shop.myshopify.com', validated: true}) expect(adminUrl).toHaveBeenCalledWith('permanent-shop.myshopify.com', 'unstable') expect(graphqlRequest).toHaveBeenCalledWith(expect.objectContaining({token: 'fresh-token'})) expect(result.subdomain).toBe('permanent-shop.myshopify.com') diff --git a/packages/store/src/cli/services/store/info/index.ts b/packages/store/src/cli/services/store/info/index.ts index f0499157d43..39ced67e70d 100644 --- a/packages/store/src/cli/services/store/info/index.ts +++ b/packages/store/src/cli/services/store/info/index.ts @@ -65,7 +65,11 @@ export async function getStoreInfo(options: GetStoreInfoOptions): Promise { const session = await loadStoredStoreSession(store) - await recordStoreFqdnMetadata(session.store, true) + await recordStoreFqdnMetadata({storeFqdn: session.store, validated: true}) setLastSeenUserId(session.userId) const shop = await fetchAdminShopInfo(session)