Skip to content

Commit 9ba7fb1

Browse files
Store session globally to avoid multiple instances
1 parent fa48688 commit 9ba7fb1

File tree

13 files changed

+149
-67
lines changed

13 files changed

+149
-67
lines changed

packages/app/src/cli/api/graphql/current_account_info.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import {AccountInfo} from '../../services/context/partner-account-info.js'
21
import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
2+
import {AccountInfo} from '@shopify/cli-kit/node/session'
33
import {AbortError} from '@shopify/cli-kit/node/error'
44
import {gql} from 'graphql-request'
55

6+
// eslint-disable-next-line @shopify/cli/no-inline-graphql
67
export const CurrentAccountInfoQuery = gql`
78
query currentAccountInfo {
89
currentAccountInfo {

packages/app/src/cli/models/app/app.test-data.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {ExtensionInstance} from '../extensions/extension-instance.js'
2424
import {loadLocalExtensionsSpecifications} from '../extensions/load-specifications.js'
2525
import {FunctionConfigType} from '../extensions/specifications/function.js'
2626
import {BaseConfigType} from '../extensions/schemas.js'
27-
import {PartnersSession} from '../../services/context/partner-account-info.js'
2827
import {WebhooksConfig} from '../extensions/specifications/types/app_config_webhook.js'
2928
import {PaymentsAppExtensionConfigType} from '../extensions/specifications/payments_app_extension.js'
3029
import {
@@ -79,6 +78,7 @@ import {AppProxySpecIdentifier} from '../extensions/specifications/app_config_ap
7978
import {ExtensionSpecification} from '../extensions/specification.js'
8079
import {AppLogsOptions} from '../../services/app-logs/utils.js'
8180
import {AppLogsSubscribeMutationVariables} from '../../api/graphql/app-management/generated/app-logs-subscribe.js'
81+
import {Session} from '@shopify/cli-kit/node/session'
8282
import {vi} from 'vitest'
8383
import {joinPath} from '@shopify/cli-kit/node/path'
8484

@@ -1223,7 +1223,7 @@ export const testRemoteExtensionTemplates: ExtensionTemplate[] = [
12231223
themeAppExtensionTemplate,
12241224
]
12251225

1226-
export const testPartnersUserSession: PartnersSession = {
1226+
export const testPartnersUserSession: Session = {
12271227
token: 'token',
12281228
businessPlatformToken: 'businessPlatformToken',
12291229
accountInfo: {
@@ -1520,7 +1520,7 @@ export function testDeveloperPlatformClient(stubs: Partial<DeveloperPlatformClie
15201520
return retVal as DeveloperPlatformClient
15211521
}
15221522

1523-
export const testPartnersServiceSession: PartnersSession = {
1523+
export const testPartnersServiceSession: Session = {
15241524
token: 'partnersToken',
15251525
businessPlatformToken: 'businessPlatformToken',
15261526
accountInfo: {

packages/app/src/cli/services/context/partner-account-info.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {AccountInfo, fetchCurrentAccountInformation} from './partner-account-info.js'
1+
import {fetchCurrentAccountInformation} from './partner-account-info.js'
22
import {testDeveloperPlatformClient} from '../../models/app/app.test-data.js'
33
import {getCurrentAccountInfo} from '../../api/graphql/current_account_info.js'
44
import {clearCachedAccountInfo, getCachedAccountInfo, setCachedAccountInfo} from '../../utilities/app-conf-store.js'
5+
import {AccountInfo} from '@shopify/cli-kit/node/session'
56
import {beforeEach, describe, expect, test, vi} from 'vitest'
67
import {AbortError} from '@shopify/cli-kit/node/error'
78
import {outputDebug} from '@shopify/cli-kit/node/output'

packages/app/src/cli/services/context/partner-account-info.ts

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,9 @@ import {getCurrentAccountInfo} from '../../api/graphql/current_account_info.js'
22
import {getCachedAccountInfo, setCachedAccountInfo} from '../../utilities/app-conf-store.js'
33
import {DeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
44
import {outputDebug} from '@shopify/cli-kit/node/output'
5+
import {AccountInfo, isUserAccount, isServiceAccount} from '@shopify/cli-kit/node/session'
56

6-
export interface PartnersSession {
7-
token: string
8-
businessPlatformToken: string
9-
accountInfo: AccountInfo
10-
userId: string
11-
}
12-
13-
export type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo
14-
15-
interface UserAccountInfo {
16-
type: 'UserAccount'
17-
email: string
18-
}
19-
20-
interface ServiceAccountInfo {
21-
type: 'ServiceAccount'
22-
orgName: string
23-
}
24-
25-
interface UnknownAccountInfo {
26-
type: 'UnknownAccount'
27-
}
28-
29-
export function isUserAccount(account: AccountInfo): account is UserAccountInfo {
30-
return account.type === 'UserAccount'
31-
}
32-
33-
export function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {
34-
return account.type === 'ServiceAccount'
35-
}
7+
export {AccountInfo, isUserAccount, isServiceAccount}
368

379
export async function fetchCurrentAccountInformation(
3810
developerPlatformClient: DeveloperPlatformClient,

packages/app/src/cli/utilities/app-conf-store.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
setCachedAccountInfo,
55
clearCachedAccountInfo,
66
} from './app-conf-store.js'
7-
import {AccountInfo} from '../services/context/partner-account-info.js'
7+
import {AccountInfo} from '@shopify/cli-kit/node/session'
88
import {vi, describe, test, expect, beforeEach, afterEach} from 'vitest'
99
import {LocalStorage} from '@shopify/cli-kit/node/local-storage'
1010
import {inTemporaryDirectory} from '@shopify/cli-kit/node/fs'

packages/app/src/cli/utilities/app-conf-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {AccountInfo} from '../services/context/partner-account-info.js'
1+
import {AccountInfo} from '@shopify/cli-kit/node/session'
22
import {LocalStorage} from '@shopify/cli-kit/node/local-storage'
33

44
// max age is 72 hours (3 days)

packages/app/src/cli/utilities/developer-platform-client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {PartnersClient} from './developer-platform-client/partners-client.js'
22
import {AppManagementClient} from './developer-platform-client/app-management-client.js'
3-
import {PartnersSession} from '../../cli/services/context/partner-account-info.js'
43
import {
54
MinimalAppIdentifiers,
65
MinimalOrganizationApp,
@@ -57,6 +56,7 @@ import {
5756
AppLogsSubscribeMutation,
5857
AppLogsSubscribeMutationVariables,
5958
} from '../api/graphql/app-management/generated/app-logs-subscribe.js'
59+
import {Session} from '@shopify/cli-kit/node/session'
6060
import {TokenItem} from '@shopify/cli-kit/node/ui'
6161
import {blockPartnersAccess} from '@shopify/cli-kit/node/environment'
6262
import {UnauthorizedHandler} from '@shopify/cli-kit/node/api/graphql'
@@ -226,14 +226,14 @@ export interface DeveloperPlatformClient {
226226
readonly organizationSource: OrganizationSource
227227
readonly bundleFormat: 'zip' | 'br'
228228
readonly supportsDashboardManagedExtensions: boolean
229-
session: () => Promise<PartnersSession>
229+
session: () => Promise<Session>
230230
/**
231231
* This is an unsafe method that should only be used when the session is expired.
232232
* It is not safe to use this method in other contexts as it may lead to race conditions.
233233
* Use only if you know what you are doing.
234234
*/
235235
unsafeRefreshToken: () => Promise<string>
236-
accountInfo: () => Promise<PartnersSession['accountInfo']>
236+
accountInfo: () => Promise<Session['accountInfo']>
237237
appFromIdentifiers: (apiKey: string) => Promise<OrganizationApp | undefined>
238238
organizations: () => Promise<Organization[]>
239239
orgFromId: (orgId: string) => Promise<Organization | undefined>

packages/app/src/cli/utilities/developer-platform-client/app-management-client.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
encodedGidFromShopId,
88
versionDeepLink,
99
} from './app-management-client.js'
10+
import {clearAllGlobalSessions} from './global-session-store.js'
1011
import {OrganizationBetaFlagsQuerySchema} from './app-management-client/graphql/organization_beta_flags.js'
1112
import {
1213
testUIExtension,
@@ -31,7 +32,7 @@ import {MinimalAppIdentifiers} from '../../models/organization.js'
3132
import {CreateAssetUrl} from '../../api/graphql/app-management/generated/create-asset-url.js'
3233
import {SourceExtension} from '../../api/graphql/app-management/generated/types.js'
3334
import {ListOrganizations} from '../../api/graphql/business-platform-destinations/generated/organizations.js'
34-
import {describe, expect, test, vi} from 'vitest'
35+
import {describe, expect, test, vi, beforeEach} from 'vitest'
3536
import {CLI_KIT_VERSION} from '@shopify/cli-kit/common/version'
3637
import {fetch} from '@shopify/cli-kit/node/http'
3738
import {
@@ -93,6 +94,11 @@ function moduleFromExtension(extension: ExtensionInstance) {
9394
}
9495
}
9596

97+
beforeEach(() => {
98+
// Clear global sessions before each test
99+
clearAllGlobalSessions()
100+
})
101+
96102
describe('diffAppModules', () => {
97103
test('extracts the added, removed and updated modules between two releases', () => {
98104
// Given

packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
OrganizationBetaFlagsQueryVariables,
55
organizationBetaFlagsQuery,
66
} from './app-management-client/graphql/organization_beta_flags.js'
7+
import {getGlobalSession, setGlobalSession} from './global-session-store.js'
78
import {environmentVariableNames} from '../../constants.js'
89
import {RemoteSpecification} from '../../api/graphql/extension_specifications.js'
910
import {
@@ -25,7 +26,6 @@ import {
2526
DevSessionDeleteOptions,
2627
UserError,
2728
} from '../developer-platform-client.js'
28-
import {PartnersSession} from '../../services/context/partner-account-info.js'
2929
import {
3030
MinimalAppIdentifiers,
3131
MinimalOrganizationApp,
@@ -139,7 +139,7 @@ import {
139139
} from '../../api/graphql/app-management/generated/app-logs-subscribe.js'
140140
import {SourceExtension} from '../../api/graphql/app-management/generated/types.js'
141141
import {getPartnersToken} from '@shopify/cli-kit/node/environment'
142-
import {ensureAuthenticatedAppManagementAndBusinessPlatform} from '@shopify/cli-kit/node/session'
142+
import {ensureAuthenticatedAppManagementAndBusinessPlatform, Session} from '@shopify/cli-kit/node/session'
143143
import {isUnitTest} from '@shopify/cli-kit/node/context/local'
144144
import {AbortError, BugError} from '@shopify/cli-kit/node/error'
145145
import {fetch, shopifyFetch, Response} from '@shopify/cli-kit/node/http'
@@ -191,10 +191,10 @@ export class AppManagementClient implements DeveloperPlatformClient {
191191
public readonly organizationSource = OrganizationSource.BusinessPlatform
192192
public readonly bundleFormat = 'br'
193193
public readonly supportsDashboardManagedExtensions = false
194-
private _session: PartnersSession | undefined
195-
196-
constructor(session?: PartnersSession) {
197-
this._session = session
194+
constructor(session?: Session) {
195+
if (session) {
196+
setGlobalSession(this.clientName, session)
197+
}
198198
}
199199

200200
async subscribeToAppLogs(
@@ -256,8 +256,9 @@ export class AppManagementClient implements DeveloperPlatformClient {
256256
}
257257
}
258258

259-
async session(): Promise<PartnersSession> {
260-
if (!this._session) {
259+
async session(): Promise<Session> {
260+
const existingSession = getGlobalSession(this.clientName)
261+
if (!existingSession) {
261262
if (isUnitTest()) {
262263
throw new Error('AppManagementClient.session() should not be invoked dynamically in a unit test')
263264
}
@@ -285,7 +286,7 @@ export class AppManagementClient implements DeveloperPlatformClient {
285286
throw new BugError('Multiple organizations found for the CLI token')
286287
}
287288

288-
this._session = {
289+
const session: Session = {
289290
token: appManagementToken,
290291
businessPlatformToken,
291292
accountInfo: {
@@ -294,8 +295,9 @@ export class AppManagementClient implements DeveloperPlatformClient {
294295
},
295296
userId,
296297
}
298+
setGlobalSession(this.clientName, session)
297299
} else if (userInfoResult.currentUserAccount) {
298-
this._session = {
300+
const session: Session = {
299301
token: appManagementToken,
300302
businessPlatformToken,
301303
accountInfo: {
@@ -304,18 +306,20 @@ export class AppManagementClient implements DeveloperPlatformClient {
304306
},
305307
userId,
306308
}
309+
setGlobalSession(this.clientName, session)
307310
} else {
308-
this._session = {
311+
const session: Session = {
309312
token: appManagementToken,
310313
businessPlatformToken,
311314
accountInfo: {
312315
type: 'UnknownAccount',
313316
},
314317
userId,
315318
}
319+
setGlobalSession(this.clientName, session)
316320
}
317321
}
318-
return this._session
322+
return getGlobalSession(this.clientName)!
319323
}
320324

321325
async token(): Promise<string> {
@@ -335,7 +339,7 @@ export class AppManagementClient implements DeveloperPlatformClient {
335339
return session.token
336340
}
337341

338-
async accountInfo(): Promise<PartnersSession['accountInfo']> {
342+
async accountInfo(): Promise<Session['accountInfo']> {
339343
return (await this.session()).accountInfo
340344
}
341345

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import {ClientName} from '../developer-platform-client.js'
2+
import {Session} from '@shopify/cli-kit/node/session'
3+
4+
interface GlobalSessionStore {
5+
[ClientName.AppManagement]?: Session
6+
[ClientName.Partners]?: Session
7+
}
8+
9+
// Global in-memory session store shared across all client instances
10+
const globalSessionStore: GlobalSessionStore = {}
11+
12+
/**
13+
* Get a session from the global store
14+
*/
15+
export function getGlobalSession(clientName: ClientName): Session | undefined {
16+
return globalSessionStore[clientName]
17+
}
18+
19+
/**
20+
* Set a session in the global store
21+
*/
22+
export function setGlobalSession(clientName: ClientName, session: Session): void {
23+
globalSessionStore[clientName] = session
24+
}
25+
26+
/**
27+
* Clear a session from the global store
28+
*/
29+
export function clearGlobalSession(clientName: ClientName): void {
30+
globalSessionStore[clientName] = undefined
31+
}
32+
33+
/**
34+
* Clear all sessions from the global store (useful for testing)
35+
*/
36+
export function clearAllGlobalSessions(): void {
37+
globalSessionStore[ClientName.AppManagement] = undefined
38+
globalSessionStore[ClientName.Partners] = undefined
39+
}

0 commit comments

Comments
 (0)