Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"@shopify/cli",
"@shopify/app",
"@shopify/store",
"@shopify/organizations",
"@shopify/create-app",
"@shopify/cli-kit",
"@shopify/theme",
Expand Down
5 changes: 4 additions & 1 deletion bin/get-graphql-schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ const schemas = [
owner: 'shop',
repo: 'world',
pathToFile: 'areas/platforms/organizations/db/graphql/destinations_schema.graphql',
localPaths: ['./packages/app/src/cli/api/graphql/business-platform-destinations/destinations_schema.graphql'],
localPaths: [
'./packages/app/src/cli/api/graphql/business-platform-destinations/destinations_schema.graphql',
'./packages/organizations/src/cli/api/graphql/business-platform-destinations/destinations_schema.graphql',
],
},
{
owner: 'shop',
Expand Down
2 changes: 2 additions & 0 deletions configurations/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,7 @@ export const aliases = (packagePath: string) => {
},
},
{find: '@shopify/theme', replacement: path.join(packagePath, '../theme/src/index')},
{find: '@shopify/organizations', replacement: path.join(packagePath, '../organizations/src/index')},
{find: '@shopify/store', replacement: path.join(packagePath, '../store/src/index')},
]
}
1 change: 1 addition & 0 deletions graphql.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,6 @@ export default {
webhooks: projectFactory('webhooks', 'webhooks_schema.graphql'),
functions: projectFactory('functions', 'functions_cli_schema.graphql', 'app'),
adminAsApp: projectFactory('admin', 'admin_schema.graphql'),
organizationsDestinations: projectFactory('business-platform-destinations', 'destinations_schema.graphql', 'organizations'),
},
}
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,23 @@
]
}
},
"packages/organizations": {
"entry": [
"**/index.ts!"
],
"project": "**/*.ts!",
"ignore": [
"**/graphql/**/generated/*.ts"
],
"ignoreDependencies": [
"@graphql-typed-document-node/core"
],
"vite": {
"config": [
"vite.config.ts"
]
}
},
"packages/cli": {
"entry": [
"**/{commands,hooks}/**/*.ts!",
Expand Down
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@luckycatfactory/esbuild-graphql-loader": "3.8.1",
"@oclif/core": "4.5.3",
"@shopify/cli-kit": "3.93.0",
"@shopify/organizations": "3.93.0",
"@shopify/plugin-cloudflare": "3.93.0",
"@shopify/polaris": "12.27.0",
"@shopify/polaris-icons": "8.11.1",
Expand Down
5 changes: 2 additions & 3 deletions packages/app/src/cli/models/organization.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import {AppConfigurationUsedByCli} from './extensions/specifications/types/app_config.js'
import {Flag, DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
import {Organization as BaseOrganization} from '@shopify/organizations'

export enum OrganizationSource {
Partners = 'Partners',
BusinessPlatform = 'BusinessPlatform',
}

export interface Organization {
id: string
businessName: string
export interface Organization extends BaseOrganization {
source: OrganizationSource
}

Expand Down
80 changes: 0 additions & 80 deletions packages/app/src/cli/prompts/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
createAsNewAppPrompt,
reloadStoreListPrompt,
selectAppPrompt,
selectOrganizationPrompt,
selectStorePrompt,
updateURLsPrompt,
} from './dev.js'
Expand Down Expand Up @@ -68,85 +67,6 @@ beforeEach(() => {
vi.mocked(getTomls).mockResolvedValue({})
})

describe('selectOrganization', () => {
test('request org selection if passing more than 1 org', async () => {
// Given
vi.mocked(renderAutocompletePrompt).mockResolvedValue('1')

// When
const got = await selectOrganizationPrompt([ORG1, ORG2])

// Then
expect(got).toEqual(ORG1)
expect(renderAutocompletePrompt).toHaveBeenCalledWith({
message: 'Which organization is this work for?',
choices: [
{label: 'org1', value: '1'},
{label: 'org2', value: '2'},
],
})
})

test('returns directly if passing only 1 org', async () => {
// Given
const orgs = [ORG2]

// When
const got = await selectOrganizationPrompt(orgs)

// Then
expect(got).toEqual(ORG2)
expect(renderAutocompletePrompt).not.toBeCalled()
})

// Intentional: when ANY duplicates exist, ALL orgs get ID suffix for consistent formatting
test('appends ID to label when duplicate names exist', async () => {
// Given
const orgsWithDuplicates = [
{id: '1', businessName: 'My Org', source: OrganizationSource.BusinessPlatform},
{id: '2', businessName: 'My Org', source: OrganizationSource.BusinessPlatform},
{id: '3', businessName: 'Other Org', source: OrganizationSource.BusinessPlatform},
]
vi.mocked(renderAutocompletePrompt).mockResolvedValue('1')

// When
await selectOrganizationPrompt(orgsWithDuplicates)

// Then - note: Other Org also gets ID suffix for consistency
expect(renderAutocompletePrompt).toHaveBeenCalledWith({
message: 'Which organization is this work for?',
choices: [
{label: 'My Org (1)', value: '1'},
{label: 'My Org (2)', value: '2'},
{label: 'Other Org (3)', value: '3'},
],
})
})

test('appends ID to all labels when all names are identical', async () => {
// Given
const orgsAllSameName = [
{id: '1', businessName: 'Same Org', source: OrganizationSource.BusinessPlatform},
{id: '2', businessName: 'Same Org', source: OrganizationSource.BusinessPlatform},
{id: '3', businessName: 'Same Org', source: OrganizationSource.BusinessPlatform},
]
vi.mocked(renderAutocompletePrompt).mockResolvedValue('2')

// When
await selectOrganizationPrompt(orgsAllSameName)

// Then
expect(renderAutocompletePrompt).toHaveBeenCalledWith({
message: 'Which organization is this work for?',
choices: [
{label: 'Same Org (1)', value: '1'},
{label: 'Same Org (2)', value: '2'},
{label: 'Same Org (3)', value: '3'},
],
})
})
})

describe('selectApp', () => {
test('returns app if user selects one', async () => {
// Given
Expand Down
19 changes: 0 additions & 19 deletions packages/app/src/cli/prompts/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,6 @@ import {
} from '@shopify/cli-kit/node/ui'
import {outputCompleted} from '@shopify/cli-kit/node/output'

export async function selectOrganizationPrompt(organizations: Organization[]): Promise<Organization> {
if (organizations.length === 1) {
return organizations[0]!
}

// Add ID suffix to disambiguate when duplicate names exist
const uniqueNames = new Set(organizations.map((org) => org.businessName))
const hasDuplicates = uniqueNames.size < organizations.length
const orgList = organizations.map((org) => ({
label: hasDuplicates ? `${org.businessName} (${org.id})` : org.businessName,
value: org.id,
}))
const id = await renderAutocompletePrompt({
message: `Which organization is this work for?`,
choices: orgList,
})
return organizations.find((org) => org.id === id)!
}

export async function selectAppPrompt(
onSearchForAppsByName: (term: string) => Promise<{apps: MinimalOrganizationApp[]; hasMorePages: boolean}>,
apps: MinimalOrganizationApp[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import link from './link.js'
import {testOrganizationApp, testDeveloperPlatformClient} from '../../../models/app/app.test-data.js'
import {DeveloperPlatformClient, selectDeveloperPlatformClient} from '../../../utilities/developer-platform-client.js'
import {OrganizationApp, OrganizationSource} from '../../../models/organization.js'
import {appNamePrompt, createAsNewAppPrompt, selectOrganizationPrompt} from '../../../prompts/dev.js'
import {appNamePrompt, createAsNewAppPrompt} from '../../../prompts/dev.js'
import {selectConfigName} from '../../../prompts/config.js'
import {selectOrganizationPrompt} from '@shopify/organizations'
import {beforeEach, describe, expect, test, vi} from 'vitest'
import {inTemporaryDirectory, readFile, writeFileSync} from '@shopify/cli-kit/node/fs'
import {joinPath} from '@shopify/cli-kit/node/path'

vi.mock('./use.js')
vi.mock('../../../prompts/dev.js')
vi.mock('@shopify/organizations')
vi.mock('../../../prompts/config.js')
vi.mock('../../local-storage')
vi.mock('@shopify/cli-kit/node/ui')
Expand Down Expand Up @@ -84,7 +86,6 @@ api_version = "2024-01"
vi.mocked(selectOrganizationPrompt).mockResolvedValue({
id: '12345',
businessName: 'test',
source: OrganizationSource.BusinessPlatform,
})
vi.mocked(selectConfigName).mockResolvedValue('shopify.app.toml')

Expand Down Expand Up @@ -178,7 +179,6 @@ api_version = "2025-07"
vi.mocked(selectOrganizationPrompt).mockResolvedValue({
id: '12345',
businessName: 'test',
source: OrganizationSource.BusinessPlatform,
})

const options = {
Expand Down Expand Up @@ -227,7 +227,6 @@ required = true
vi.mocked(selectOrganizationPrompt).mockResolvedValue({
id: '12345',
businessName: 'test',
source: OrganizationSource.BusinessPlatform,
})

const options = {
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/cli/services/app/env/show.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {showEnv} from './show.js'
import {fetchOrganizations} from '../../dev/fetch.js'
import {AppInterface} from '../../../models/app/app.js'
import {selectOrganizationPrompt} from '../../../prompts/dev.js'
import {testApp, testOrganizationApp} from '../../../models/app/app.test-data.js'
import {OrganizationSource} from '../../../models/organization.js'
import {selectOrganizationPrompt} from '@shopify/organizations'
import {describe, expect, vi, test} from 'vitest'
import * as file from '@shopify/cli-kit/node/fs'
import {stringifyMessage, unstyled} from '@shopify/cli-kit/node/output'

vi.mock('../../dev/fetch.js')
vi.mock('../../../prompts/dev.js')
vi.mock('@shopify/organizations')
vi.mock('@shopify/cli-kit/node/node-package-manager')

describe('env show', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/cli/services/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
OrganizationStore,
} from '../models/organization.js'
import {getAppIdentifiers} from '../models/app/identifiers.js'
import {selectOrganizationPrompt} from '../prompts/dev.js'
import {
testDeveloperPlatformClient,
testAppWithConfig,
Expand All @@ -34,6 +33,7 @@ import {
selectDeveloperPlatformClient,
} from '../utilities/developer-platform-client.js'
import {RemoteAwareExtensionSpecification} from '../models/extensions/specification.js'
import {selectOrganizationPrompt} from '@shopify/organizations'
import {TomlFile} from '@shopify/cli-kit/node/toml/toml-file'
import {isServiceAccount, isUserAccount} from '@shopify/cli-kit/node/session'
import {afterEach, beforeAll, beforeEach, describe, expect, test, vi} from 'vitest'
Expand Down Expand Up @@ -118,6 +118,7 @@ vi.mock('./dev/create-extension')
vi.mock('./dev/select-app')
vi.mock('./dev/select-store')
vi.mock('../prompts/dev')
vi.mock('@shopify/organizations')
vi.mock('../models/app/identifiers')
vi.mock('./context/identifiers')
vi.mock('../models/app/loader.js')
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/cli/services/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {createExtension} from './dev/create-extension.js'
import {CachedAppInfo} from './local-storage.js'
import {DeployOptions} from './deploy.js'
import {formatConfigInfoBody} from './format-config-info-body.js'
import {selectOrganizationPrompt} from '../prompts/dev.js'
import {AppInterface, AppLinkedInterface} from '../models/app/app.js'
import {Identifiers, updateAppIdentifiers, getAppIdentifiers} from '../models/app/identifiers.js'
import {Organization, OrganizationApp, OrganizationSource, OrganizationStore} from '../models/organization.js'
Expand All @@ -24,6 +23,7 @@ import {
DeveloperPlatformClient,
selectDeveloperPlatformClient,
} from '../utilities/developer-platform-client.js'
import {selectOrganizationPrompt} from '@shopify/organizations'
import {TomlFile} from '@shopify/cli-kit/node/toml/toml-file'
import {isServiceAccount, isUserAccount} from '@shopify/cli-kit/node/session'
import {tryParseInt} from '@shopify/cli-kit/common/string'
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/cli/services/info.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {InfoOptions, info} from './info.js'
import {AppInterface, AppLinkedInterface} from '../models/app/app.js'
import {OrganizationApp, OrganizationSource} from '../models/organization.js'
import {selectOrganizationPrompt} from '../prompts/dev.js'
import {
testDeveloperPlatformClient,
testOrganizationApp,
Expand All @@ -12,13 +11,14 @@ import {
} from '../models/app/app.test-data.js'
import {AppErrors} from '../models/app/loader.js'
import {DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
import {selectOrganizationPrompt} from '@shopify/organizations'
import {describe, expect, vi, test} from 'vitest'
import {joinPath} from '@shopify/cli-kit/node/path'
import {OutputMessage, TokenizedString, stringifyMessage, unstyled} from '@shopify/cli-kit/node/output'
import {inTemporaryDirectory, writeFileSync} from '@shopify/cli-kit/node/fs'
import {AlertCustomSection, InlineToken} from '@shopify/cli-kit/node/ui'

vi.mock('../prompts/dev.js')
vi.mock('@shopify/organizations')
vi.mock('@shopify/cli-kit/node/node-package-manager')
vi.mock('../utilities/developer-platform-client.js')

Expand Down
Loading
Loading