@@ -2,13 +2,14 @@ import {fetchOrganizations, fetchOrgFromId} from './dev/fetch.js'
22import { selectOrCreateApp } from './dev/select-app.js'
33import { selectStore } from './dev/select-store.js'
44import { ensureDeploymentIdsPresence } from './context/identifiers.js'
5- import { ensureDeployContext , ensureThemeExtensionDevContext } from './context.js'
5+ import { appFromIdentifiers , ensureDeployContext , ensureThemeExtensionDevContext } from './context.js'
66import { createExtension } from './dev/create-extension.js'
77import { CachedAppInfo } from './local-storage.js'
88import link from './app/config/link.js'
99import { fetchSpecifications } from './generate/fetch-extension-specifications.js'
1010import * as patchAppConfigurationFile from './app/patch-app-configuration-file.js'
1111import { DeployOptions } from './deploy.js'
12+ import { isServiceAccount , isUserAccount } from './context/partner-account-info.js'
1213import {
1314 MinimalAppIdentifiers ,
1415 AppApiKeyAndOrgId ,
@@ -22,15 +23,13 @@ import {selectOrganizationPrompt} from '../prompts/dev.js'
2223import {
2324 DEFAULT_CONFIG ,
2425 testDeveloperPlatformClient ,
25- testApp ,
2626 testAppWithConfig ,
2727 testOrganizationApp ,
2828 testThemeExtensions ,
29- buildVersionedAppSchema ,
3029} from '../models/app/app.test-data.js'
3130import metadata from '../metadata.js'
3231import { 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'
3433import * as loadSpecifications from '../models/extensions/load-specifications.js'
3534import { DeveloperPlatformClient , selectDeveloperPlatformClient } from '../utilities/developer-platform-client.js'
3635import { 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+ ]
0 commit comments