From b81ec3645efcfe7387bd8341b505a75abffb3eff Mon Sep 17 00:00:00 2001 From: Ariel Caplan Date: Sun, 1 Dec 2024 23:35:09 +0200 Subject: [PATCH] Centralize gating of the App Management API --- .../src/cli/services/deploy/bundle.test.ts | 4 +-- .../app/src/cli/services/deploy/bundle.ts | 4 +-- .../app/src/cli/services/dev/fetch.test.ts | 4 +-- .../utilities/developer-platform-client.ts | 6 ++--- .../cli-kit/src/private/node/constants.ts | 1 + packages/cli-kit/src/private/node/session.ts | 5 ++-- .../src/private/node/session/exchange.ts | 4 +-- .../src/private/node/session/scopes.test.ts | 3 ++- .../src/private/node/session/scopes.ts | 8 +++--- .../src/public/node/context/local.test.ts | 25 +++++++++++++++++++ .../cli-kit/src/public/node/context/local.ts | 10 ++++++++ 11 files changed, 54 insertions(+), 20 deletions(-) diff --git a/packages/app/src/cli/services/deploy/bundle.test.ts b/packages/app/src/cli/services/deploy/bundle.test.ts index 1552f4b02dc..72c1faa0ab7 100644 --- a/packages/app/src/cli/services/deploy/bundle.test.ts +++ b/packages/app/src/cli/services/deploy/bundle.test.ts @@ -8,7 +8,7 @@ import {joinPath} from '@shopify/cli-kit/node/path' describe('bundleAndBuildExtensions', () => { let app: AppInterface - test('generates a manifest.json when USE_APP_MANAGEMENT_API is enabled', async () => { + test('generates a manifest.json when App Management is enabled', async () => { await file.inTemporaryDirectory(async (tmpDir: string) => { // Given vi.spyOn(file, 'writeFileSync').mockResolvedValue(undefined) @@ -73,7 +73,7 @@ describe('bundleAndBuildExtensions', () => { }) }) - test('does not generate the manifest.json when USE_APP_MANAGEMENT_API is disabled', async () => { + test('does not generate the manifest.json when App Management is disabled', async () => { await file.inTemporaryDirectory(async (tmpDir: string) => { // Given vi.spyOn(file, 'writeFileSync').mockResolvedValue(undefined) diff --git a/packages/app/src/cli/services/deploy/bundle.ts b/packages/app/src/cli/services/deploy/bundle.ts index dd533a895be..e550bf91372 100644 --- a/packages/app/src/cli/services/deploy/bundle.ts +++ b/packages/app/src/cli/services/deploy/bundle.ts @@ -5,8 +5,8 @@ import {zip} from '@shopify/cli-kit/node/archiver' import {AbortSignal} from '@shopify/cli-kit/node/abort' import {inTemporaryDirectory, mkdirSync, touchFile, writeFileSync} from '@shopify/cli-kit/node/fs' import {joinPath} from '@shopify/cli-kit/node/path' -import {isTruthy} from '@shopify/cli-kit/node/context/utilities' import {renderConcurrent} from '@shopify/cli-kit/node/ui' +import {isAppManagementEnabled} from '@shopify/cli-kit/node/context/local' import {Writable} from 'stream' interface BundleOptions { @@ -21,7 +21,7 @@ export async function bundleAndBuildExtensions(options: BundleOptions, systemEnv mkdirSync(bundleDirectory) await touchFile(joinPath(bundleDirectory, '.shopify')) - if (isTruthy(systemEnvironment.USE_APP_MANAGEMENT_API)) { + if (isAppManagementEnabled(systemEnvironment)) { // Include manifest in bundle const appManifest = await options.app.manifest() const manifestPath = joinPath(bundleDirectory, 'manifest.json') diff --git a/packages/app/src/cli/services/dev/fetch.test.ts b/packages/app/src/cli/services/dev/fetch.test.ts index 19a3c263331..12665ef2650 100644 --- a/packages/app/src/cli/services/dev/fetch.test.ts +++ b/packages/app/src/cli/services/dev/fetch.test.ts @@ -54,7 +54,7 @@ afterEach(() => { }) describe('fetchOrganizations', async () => { - test('returns fetched organizations from Partners without USE_APP_MANAGEMENT_API', async () => { + test('returns fetched organizations from Partners when App Management is disabled', async () => { // Given const partnersClient: PartnersClient = testDeveloperPlatformClient({ organizations: () => Promise.resolve([ORG1]), @@ -74,7 +74,7 @@ describe('fetchOrganizations', async () => { expect(appManagementClient.organizations).not.toHaveBeenCalled() }) - test('returns fetched organizations from Partners and App Management with USE_APP_MANAGEMENT_API', async () => { + test('returns fetched organizations from Partners and App Management when App Management is enabled', async () => { // Given vi.stubEnv('USE_APP_MANAGEMENT_API', '1') const partnersClient: PartnersClient = testDeveloperPlatformClient({ diff --git a/packages/app/src/cli/utilities/developer-platform-client.ts b/packages/app/src/cli/utilities/developer-platform-client.ts index 9aab9a65acf..e630f926f46 100644 --- a/packages/app/src/cli/utilities/developer-platform-client.ts +++ b/packages/app/src/cli/utilities/developer-platform-client.ts @@ -54,7 +54,7 @@ import { import {DevSessionCreateMutation} from '../api/graphql/app-dev/generated/dev-session-create.js' import {DevSessionUpdateMutation} from '../api/graphql/app-dev/generated/dev-session-update.js' import {DevSessionDeleteMutation} from '../api/graphql/app-dev/generated/dev-session-delete.js' -import {isTruthy} from '@shopify/cli-kit/node/context/utilities' +import {isAppManagementEnabled} from '@shopify/cli-kit/node/context/local' export enum ClientName { AppManagement = 'app-management', @@ -77,7 +77,7 @@ export interface AppVersionIdentifiers { export function allDeveloperPlatformClients(): DeveloperPlatformClient[] { const clients: DeveloperPlatformClient[] = [new PartnersClient()] - if (isTruthy(process.env.USE_APP_MANAGEMENT_API)) clients.push(new AppManagementClient()) + if (isAppManagementEnabled()) clients.push(new AppManagementClient()) return clients } @@ -117,7 +117,7 @@ export function selectDeveloperPlatformClient({ configuration, organization, }: SelectDeveloperPlatformClientOptions = {}): DeveloperPlatformClient { - if (isTruthy(process.env.USE_APP_MANAGEMENT_API)) { + if (isAppManagementEnabled()) { if (organization) return selectDeveloperPlatformClientByOrg(organization) return selectDeveloperPlatformClientByConfig(configuration) } diff --git a/packages/cli-kit/src/private/node/constants.ts b/packages/cli-kit/src/private/node/constants.ts index faee18c688e..c9a4ab91517 100644 --- a/packages/cli-kit/src/private/node/constants.ts +++ b/packages/cli-kit/src/private/node/constants.ts @@ -45,6 +45,7 @@ export const environmentVariables = { otelURL: 'SHOPIFY_CLI_OTEL_EXPORTER_OTLP_ENDPOINT', themeKitAccessDomain: 'SHOPIFY_CLI_THEME_KIT_ACCESS_DOMAIN', json: 'SHOPIFY_FLAG_JSON', + useAppManagement: 'USE_APP_MANAGEMENT_API', } export const defaultThemeKitAccessDomain = 'theme-kit-access.shopifyapps.com' diff --git a/packages/cli-kit/src/private/node/session.ts b/packages/cli-kit/src/private/node/session.ts index cb1d1d7b028..041df38d971 100644 --- a/packages/cli-kit/src/private/node/session.ts +++ b/packages/cli-kit/src/private/node/session.ts @@ -16,7 +16,7 @@ import {RequestClientError} from './api/headers.js' import {getCachedPartnerAccountStatus, setCachedPartnerAccountStatus} from './conf-store.js' import {isThemeAccessSession} from './api/rest.js' import {outputContent, outputToken, outputDebug} from '../../public/node/output.js' -import {firstPartyDev, themeToken} from '../../public/node/context/local.js' +import {firstPartyDev, isAppManagementEnabled, themeToken} from '../../public/node/context/local.js' import {AbortError, BugError} from '../../public/node/error.js' import {partnersRequest} from '../../public/node/api/partners.js' import {normalizeStoreFqdn, partnersFqdn, identityFqdn} from '../../public/node/context/fqdn.js' @@ -26,7 +26,6 @@ import {getIdentityTokenInformation, getPartnersToken} from '../../public/node/e import {gql} from 'graphql-request' import {AdminSession} from '@shopify/cli-kit/node/session' import {outputCompleted, outputInfo, outputWarn} from '@shopify/cli-kit/node/output' -import {isTruthy} from '@shopify/cli-kit/node/context/utilities' import {isSpin} from '@shopify/cli-kit/node/context/spin' import {nonRandomUUID} from '@shopify/cli-kit/node/crypto' @@ -310,7 +309,7 @@ async function executeCompleteFlow(applications: OAuthApplications, identityFqdn * @param partnersToken - Partners token. */ async function ensureUserHasPartnerAccount(partnersToken: string, userId: string | undefined) { - if (isTruthy(process.env.USE_APP_MANAGEMENT_API)) return + if (isAppManagementEnabled()) return outputDebug(outputContent`Verifying that the user has a Partner organization`) if (!(await hasPartnerAccount(partnersToken, userId))) { diff --git a/packages/cli-kit/src/private/node/session/exchange.ts b/packages/cli-kit/src/private/node/session/exchange.ts index 10185d532d0..2e3b8a540a4 100644 --- a/packages/cli-kit/src/private/node/session/exchange.ts +++ b/packages/cli-kit/src/private/node/session/exchange.ts @@ -5,8 +5,8 @@ import {identityFqdn} from '../../../public/node/context/fqdn.js' import {shopifyFetch} from '../../../public/node/http.js' import {err, ok, Result} from '../../../public/node/result.js' import {AbortError, BugError, ExtendableError} from '../../../public/node/error.js' +import {isAppManagementEnabled} from '../../../public/node/context/local.js' import {setLastSeenAuthMethod, setLastSeenUserIdAfterAuth} from '../session.js' -import {isTruthy} from '@shopify/cli-kit/node/context/utilities' import * as jose from 'jose' import {nonRandomUUID} from '@shopify/cli-kit/node/crypto' @@ -34,7 +34,7 @@ export async function exchangeAccessForApplicationTokens( store?: string, ): Promise<{[x: string]: ApplicationToken}> { const token = identityToken.accessToken - const appManagementEnabled = isTruthy(process.env.USE_APP_MANAGEMENT_API) + const appManagementEnabled = isAppManagementEnabled() const [partners, storefront, businessPlatform, admin, appManagement] = await Promise.all([ requestAppToken('partners', token, scopes.partners), diff --git a/packages/cli-kit/src/private/node/session/scopes.test.ts b/packages/cli-kit/src/private/node/session/scopes.test.ts index fee1ba818c0..d7f88fd9b53 100644 --- a/packages/cli-kit/src/private/node/session/scopes.test.ts +++ b/packages/cli-kit/src/private/node/session/scopes.test.ts @@ -1,4 +1,5 @@ import {allDefaultScopes, apiScopes} from './scopes.js' +import {environmentVariables} from '../constants.js' import {describe, expect, test} from 'vitest' describe('allDefaultScopes', () => { @@ -25,7 +26,7 @@ describe('allDefaultScopes', () => { test('includes App Management and Store Management when the required env var is defined', async () => { // Given - const envVars = {USE_APP_MANAGEMENT_API: 'true'} + const envVars = {[environmentVariables.useAppManagement]: 'true'} // When const got = allDefaultScopes([], envVars) diff --git a/packages/cli-kit/src/private/node/session/scopes.ts b/packages/cli-kit/src/private/node/session/scopes.ts index fc8a313fb30..072fb29d2fc 100644 --- a/packages/cli-kit/src/private/node/session/scopes.ts +++ b/packages/cli-kit/src/private/node/session/scopes.ts @@ -1,6 +1,6 @@ import {allAPIs, API} from '../api.js' import {BugError} from '../../../public/node/error.js' -import {isTruthy} from '@shopify/cli-kit/node/context/utilities' +import {isAppManagementEnabled} from '../../../public/node/context/local.js' /** * Generate a flat array with all the default scopes for all the APIs plus @@ -35,11 +35,9 @@ function defaultApiScopes(api: API, systemEnvironment = process.env): string[] { case 'partners': return ['cli'] case 'business-platform': - return isTruthy(systemEnvironment.USE_APP_MANAGEMENT_API) - ? ['destinations', 'store-management'] - : ['destinations'] + return isAppManagementEnabled(systemEnvironment) ? ['destinations', 'store-management'] : ['destinations'] case 'app-management': - return isTruthy(systemEnvironment.USE_APP_MANAGEMENT_API) ? ['app-management'] : [] + return isAppManagementEnabled(systemEnvironment) ? ['app-management'] : [] default: throw new BugError(`Unknown API: ${api}`) } diff --git a/packages/cli-kit/src/public/node/context/local.test.ts b/packages/cli-kit/src/public/node/context/local.test.ts index 2c687428e11..14c69b10fbe 100644 --- a/packages/cli-kit/src/public/node/context/local.test.ts +++ b/packages/cli-kit/src/public/node/context/local.test.ts @@ -4,6 +4,7 @@ import { isDevelopment, isShopify, isUnitTest, + isAppManagementEnabled, analyticsDisabled, cloudEnvironment, macAddress, @@ -99,6 +100,30 @@ describe('hasGit', () => { }) }) +describe('isAppManagementEnabled', () => { + test('returns true when USE_APP_MANAGEMENT_API is truthy', () => { + // Given + const env = {USE_APP_MANAGEMENT_API: '1'} + + // When + const got = isAppManagementEnabled(env) + + // Then + expect(got).toBe(true) + }) + + test('returns false when USE_APP_MANAGEMENT_API is falsy', () => { + // Given + const env = {USE_APP_MANAGEMENT_API: '0'} + + // When + const got = isAppManagementEnabled(env) + + // Then + expect(got).toBe(false) + }) +}) + describe('analitycsDisabled', () => { test('returns true when SHOPIFY_CLI_NO_ANALYTICS is truthy', () => { // Given diff --git a/packages/cli-kit/src/public/node/context/local.ts b/packages/cli-kit/src/public/node/context/local.ts index cc8a73b2db2..d4c1de54eee 100644 --- a/packages/cli-kit/src/public/node/context/local.ts +++ b/packages/cli-kit/src/public/node/context/local.ts @@ -46,6 +46,16 @@ export function isVerbose(env = process.env): boolean { return isTruthy(env[environmentVariables.verbose]) || process.argv.includes('--verbose') } +/** + * It returns true if the App Management API is available. + * + * @param env - The environment variables from the environment of the current process. + * @returns True if the App Management API is available. + */ +export function isAppManagementEnabled(env = process.env): boolean { + return isTruthy(env[environmentVariables.useAppManagement]) +} + /** * Returns true if the environment in which the CLI is running is either * a local environment (where dev is present) or a cloud environment (spin).