Skip to content

Commit 14612d3

Browse files
Merge pull request #5193 from Shopify/01-14-closes_https__github.com_shopify_develop-app-inner-loop_issues_2460
closes: Shopify/develop-app-inner-loop#2460
2 parents 1a922a6 + 5156a87 commit 14612d3

File tree

3 files changed

+129
-32
lines changed

3 files changed

+129
-32
lines changed

packages/app/src/cli/services/context.test.ts

Lines changed: 86 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import {fetchOrganizations, fetchOrgFromId} from './dev/fetch.js'
22
import {selectOrCreateApp} from './dev/select-app.js'
33
import {selectStore} from './dev/select-store.js'
44
import {ensureDeploymentIdsPresence} from './context/identifiers.js'
5-
import {ensureDeployContext, ensureThemeExtensionDevContext} from './context.js'
5+
import {appFromIdentifiers, ensureDeployContext, ensureThemeExtensionDevContext} from './context.js'
66
import {createExtension} from './dev/create-extension.js'
77
import {CachedAppInfo} from './local-storage.js'
88
import link from './app/config/link.js'
99
import {fetchSpecifications} from './generate/fetch-extension-specifications.js'
1010
import * as patchAppConfigurationFile from './app/patch-app-configuration-file.js'
1111
import {DeployOptions} from './deploy.js'
12+
import {isServiceAccount, isUserAccount} from './context/partner-account-info.js'
1213
import {
1314
MinimalAppIdentifiers,
1415
AppApiKeyAndOrgId,
@@ -22,15 +23,13 @@ import {selectOrganizationPrompt} from '../prompts/dev.js'
2223
import {
2324
DEFAULT_CONFIG,
2425
testDeveloperPlatformClient,
25-
testApp,
2626
testAppWithConfig,
2727
testOrganizationApp,
2828
testThemeExtensions,
29-
buildVersionedAppSchema,
3029
} from '../models/app/app.test-data.js'
3130
import metadata from '../metadata.js'
3231
import {AppConfigurationStateLinked, getAppConfigurationFileName, isWebType, loadApp} from '../models/app/loader.js'
33-
import {AppInterface, AppLinkedInterface} from '../models/app/app.js'
32+
import {AppLinkedInterface} from '../models/app/app.js'
3433
import * as loadSpecifications from '../models/extensions/load-specifications.js'
3534
import {DeveloperPlatformClient, selectDeveloperPlatformClient} from '../utilities/developer-platform-client.js'
3635
import {RemoteAwareExtensionSpecification} from '../models/extensions/specification.js'
@@ -221,11 +220,11 @@ describe('ensureDeployContext', () => {
221220
},
222221
},
223222
'\n',
224-
'You can pass ',
223+
'You can pass',
225224
{
226225
command: '--reset',
227226
},
228-
' to your command to reset your app configuration.',
227+
'to your command to reset your app configuration.',
229228
],
230229
headline: 'Using shopify.app.toml for default values:',
231230
})
@@ -270,11 +269,11 @@ describe('ensureDeployContext', () => {
270269
},
271270
},
272271
'\n',
273-
'You can pass ',
272+
'You can pass',
274273
{
275274
command: '--reset',
276275
},
277-
' to your command to reset your app configuration.',
276+
'to your command to reset your app configuration.',
278277
],
279278
headline: 'Using shopify.app.toml for default values:',
280279
})
@@ -320,11 +319,11 @@ describe('ensureDeployContext', () => {
320319
},
321320
},
322321
'\n',
323-
'You can pass ',
322+
'You can pass',
324323
{
325324
command: '--reset',
326325
},
327-
' to your command to reset your app configuration.',
326+
'to your command to reset your app configuration.',
328327
],
329328
headline: 'Using shopify.app.toml for default values:',
330329
})
@@ -370,11 +369,11 @@ describe('ensureDeployContext', () => {
370369
},
371370
},
372371
'\n',
373-
'You can pass ',
372+
'You can pass',
374373
{
375374
command: '--reset',
376375
},
377-
' to your command to reset your app configuration.',
376+
'to your command to reset your app configuration.',
378377
],
379378
headline: 'Using shopify.app.toml for default values:',
380379
})
@@ -426,11 +425,11 @@ describe('ensureDeployContext', () => {
426425
},
427426
},
428427
'\n',
429-
'You can pass ',
428+
'You can pass',
430429
{
431430
command: '--reset',
432431
},
433-
' to your command to reset your app configuration.',
432+
'to your command to reset your app configuration.',
434433
],
435434
headline: 'Using shopify.app.toml for default values:',
436435
})
@@ -470,11 +469,11 @@ describe('ensureDeployContext', () => {
470469
},
471470
},
472471
'\n',
473-
'You can pass ',
472+
'You can pass',
474473
{
475474
command: '--reset',
476475
},
477-
' to your command to reset your app configuration.',
476+
'to your command to reset your app configuration.',
478477
],
479478
headline: 'Using shopify.app.toml for default values:',
480479
})
@@ -511,11 +510,11 @@ describe('ensureDeployContext', () => {
511510
},
512511
},
513512
'\n',
514-
'You can pass ',
513+
'You can pass',
515514
{
516515
command: '--reset',
517516
},
518-
' to your command to reset your app configuration.',
517+
'to your command to reset your app configuration.',
519518
],
520519
headline: 'Using shopify.app.toml for default values:',
521520
})
@@ -689,11 +688,72 @@ describe('ensureThemeExtensionDevContext', () => {
689688
})
690689
})
691690

692-
async function mockApp(directory: string, app?: Partial<AppInterface>) {
693-
const versionSchema = await buildVersionedAppSchema()
694-
const localApp = testApp(app)
695-
localApp.configSchema = versionSchema.schema
696-
localApp.specifications = versionSchema.configSpecifications
697-
localApp.directory = directory
698-
return localApp
699-
}
691+
describe('appFromIdentifiers', () => {
692+
test('renders the org name when an app cannot be found and the account is a service account ', async () => {
693+
vi.mocked(isServiceAccount).mockReturnValue(true)
694+
695+
await expect(
696+
appFromIdentifiers({
697+
apiKey: 'apiKey-12345',
698+
developerPlatformClient: testDeveloperPlatformClient({
699+
appFromIdentifiers: () => Promise.resolve(undefined),
700+
accountInfo: () =>
701+
Promise.resolve({
702+
type: 'ServiceAccount',
703+
orgName: 'My Test Org',
704+
}),
705+
}),
706+
organizationId: 'orgId',
707+
}),
708+
).rejects.toThrowError(
709+
expect.objectContaining({
710+
message: 'No app with client ID apiKey-12345 found',
711+
tryMessage: renderTryMessage(true, 'My Test Org'),
712+
}),
713+
)
714+
})
715+
716+
test('renders the user email when an app cannot be found and the account is a user account ', async () => {
717+
vi.mocked(isUserAccount).mockReturnValue(true)
718+
719+
await expect(
720+
appFromIdentifiers({
721+
apiKey: 'apiKey-12345',
722+
developerPlatformClient: testDeveloperPlatformClient({
723+
appFromIdentifiers: () => Promise.resolve(undefined),
724+
accountInfo: () =>
725+
Promise.resolve({
726+
type: 'UserAccount',
727+
728+
}),
729+
}),
730+
organizationId: 'orgId',
731+
}),
732+
).rejects.toThrowError(
733+
expect.objectContaining({
734+
message: 'No app with client ID apiKey-12345 found',
735+
tryMessage: renderTryMessage(false, '[email protected]'),
736+
}),
737+
)
738+
})
739+
})
740+
741+
const renderTryMessage = (isOrg: boolean, identifier: string) => [
742+
{
743+
list: {
744+
title: 'Next steps:',
745+
items: [
746+
'Check that your account has permission to develop apps for this organization or contact the owner of the organization to grant you permission',
747+
[
748+
'Run',
749+
{command: 'shopify auth logout'},
750+
'to log into a different',
751+
isOrg ? 'organization' : 'account',
752+
'than',
753+
{bold: identifier},
754+
],
755+
['Pass', {command: '--reset'}, 'to your command to create a new app'],
756+
],
757+
},
758+
},
759+
]

packages/app/src/cli/services/context.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {createExtension} from './dev/create-extension.js'
55
import {CachedAppInfo} from './local-storage.js'
66
import {patchAppConfigurationFile} from './app/patch-app-configuration-file.js'
77
import {DeployOptions} from './deploy.js'
8+
import {isServiceAccount, isUserAccount} from './context/partner-account-info.js'
89
import {selectOrganizationPrompt} from '../prompts/dev.js'
910
import {
1011
AppInterface,
@@ -38,10 +39,30 @@ export const InvalidApiKeyErrorMessage = (apiKey: string) => {
3839
}
3940
}
4041

41-
export const resetHelpMessage: Token[] = [
42-
'You can pass ',
42+
export const resetHelpMessage = [
43+
'You can pass',
4344
{command: '--reset'},
44-
' to your command to reset your app configuration.',
45+
'to your command to reset your app configuration.',
46+
]
47+
48+
const appNotFoundHelpMessage = (accountIdentifier: string, isOrg = false) => [
49+
{
50+
list: {
51+
title: 'Next steps:',
52+
items: [
53+
'Check that your account has permission to develop apps for this organization or contact the owner of the organization to grant you permission',
54+
[
55+
'Run',
56+
{command: 'shopify auth logout'},
57+
'to log into a different',
58+
isOrg ? 'organization' : 'account',
59+
'than',
60+
{bold: accountIdentifier},
61+
],
62+
['Pass', {command: '--reset'}, 'to your command to create a new app'],
63+
],
64+
},
65+
},
4566
]
4667

4768
interface AppFromIdOptions {
@@ -65,7 +86,23 @@ export const appFromIdentifiers = async (options: AppFromIdOptions): Promise<Org
6586
apiKey: options.apiKey,
6687
organizationId,
6788
})
68-
if (!app) throw new AbortError([`Couldn't find the app with Client ID`, {command: options.apiKey}], resetHelpMessage)
89+
if (!app) {
90+
const accountInfo = await developerPlatformClient.accountInfo()
91+
let identifier = 'Unknown account'
92+
let isOrg = false
93+
94+
if (isServiceAccount(accountInfo)) {
95+
identifier = accountInfo.orgName
96+
isOrg = true
97+
} else if (isUserAccount(accountInfo)) {
98+
identifier = accountInfo.email
99+
}
100+
101+
throw new AbortError(
102+
[`No app with client ID`, {command: options.apiKey}, 'found'],
103+
appNotFoundHelpMessage(identifier, isOrg),
104+
)
105+
}
69106
return app
70107
}
71108

packages/app/src/cli/services/logs.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,11 @@ describe('logs', () => {
237237
},
238238
},
239239
'\n',
240-
'You can pass ',
240+
'You can pass',
241241
{
242242
command: '--reset',
243243
},
244-
' to your command to reset your app configuration.',
244+
'to your command to reset your app configuration.',
245245
],
246246
headline: 'Using shopify.app.toml for default values:',
247247
})

0 commit comments

Comments
 (0)