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
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import * as Types from './types.js'

import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core'

export type AppInstallCountQueryVariables = Types.Exact<{
appId: Types.Scalars['ID']['input']
}>

export type AppInstallCountQuery = {app: {installCount: number}}

export const AppInstallCount = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: {kind: 'Name', value: 'AppInstallCount'},
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'appId'}},
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'ID'}}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'app'},
arguments: [
{
kind: 'Argument',
name: {kind: 'Name', value: 'id'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'appId'}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'installCount'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
],
},
},
],
} as unknown as DocumentNode<AppInstallCountQuery, AppInstallCountQueryVariables>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
query AppInstallCount($appId: ID!) {
app(id: $appId) {
installCount
}
}
1 change: 1 addition & 0 deletions packages/app/src/cli/models/app/app.test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,7 @@ export function testDeveloperPlatformClient(stubs: Partial<DeveloperPlatformClie
orgFromId: (_organizationId: string) => Promise.resolve(testOrganization()),
appsForOrg: (_organizationId: string) => Promise.resolve({apps: [testOrganizationApp()], hasMorePages: false}),
specifications: (_app: MinimalAppIdentifiers) => Promise.resolve(testRemoteSpecifications),
appInstallCount: (_app: MinimalAppIdentifiers) => Promise.resolve(0),
templateSpecifications: (_app: MinimalAppIdentifiers) =>
Promise.resolve({templates: testRemoteExtensionTemplates, groupOrder: []}),
orgAndApps: (_orgId: string) =>
Expand Down
11 changes: 11 additions & 0 deletions packages/app/src/cli/prompts/deploy-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface DeployOrReleaseConfirmationPromptOptions {
/** If true, allow removing extensions and configuration without user confirmation */
allowDeletes?: boolean
showConfig?: boolean
installCount?: number
}

interface DeployConfirmationPromptOptions {
Expand All @@ -36,6 +37,7 @@ interface DeployConfirmationPromptOptions {
configInfoTable: InfoTableSection
}
release: boolean
installCount?: number
}

/**
Expand Down Expand Up @@ -97,6 +99,7 @@ export async function deployOrReleaseConfirmationPrompt({
configExtensionIdentifiersBreakdown,
appTitle,
release,
installCount,
}: DeployOrReleaseConfirmationPromptOptions): Promise<boolean> {
await metadata.addPublicMetadata(() => buildConfigurationBreakdownMetadata(configExtensionIdentifiersBreakdown))

Expand All @@ -117,6 +120,7 @@ export async function deployOrReleaseConfirmationPrompt({
extensionsContentPrompt,
configContentPrompt,
release,
installCount,
})
}

Expand All @@ -125,6 +129,7 @@ async function deployConfirmationPrompt({
extensionsContentPrompt: {extensionsInfoTable, hasDeletedExtensions},
configContentPrompt,
release,
installCount,
}: DeployConfirmationPromptOptions): Promise<boolean> {
const timeBeforeConfirmationMs = new Date().valueOf()
let confirmationResponse = true
Expand All @@ -149,11 +154,17 @@ async function deployConfirmationPrompt({
}

const question = `${release ? 'Release' : 'Create'} a new version${appTitle ? ` of ${appTitle}` : ''}?`
const showInstallCountWarning = hasDeletedExtensions && installCount !== undefined && installCount > 0
if (isDangerous) {
confirmationResponse = await renderDangerousConfirmationPrompt({
message: question,
infoTable,
confirmation: appTitle,
...(showInstallCountWarning
? {
body: `This release removes extensions and related data from ${installCount} app installations.\nUse caution as this may include production data on live stores.`,
}
: {}),
})
} else {
confirmationResponse = await renderConfirmationPrompt({
Expand Down
15 changes: 15 additions & 0 deletions packages/app/src/cli/services/context/identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ export async function ensureDeploymentIdsPresence(options: EnsureDeploymentIdsPr
activeAppVersion: options.activeAppVersion,
})

let installCount: number | undefined
if (extensionIdentifiersBreakdown.onlyRemote.length > 0) {
try {
installCount = await options.developerPlatformClient.appInstallCount({
id: options.appId,
apiKey: options.remoteApp.apiKey,
organizationId: options.remoteApp.organizationId,
})
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (_error) {
installCount = undefined
}
}

const confirmed = await deployOrReleaseConfirmationPrompt({
extensionIdentifiersBreakdown,
configExtensionIdentifiersBreakdown,
Expand All @@ -66,6 +80,7 @@ export async function ensureDeploymentIdsPresence(options: EnsureDeploymentIdsPr
force: options.force,
allowUpdates: options.allowUpdates,
allowDeletes: options.allowDeletes,
installCount,
})
if (!confirmed) throw new AbortSilentError()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export interface DeveloperPlatformClient {
activeAppVersion?: AppVersion,
) => Promise<AllAppExtensionRegistrationsQuerySchema>
appVersions: (app: OrganizationApp) => Promise<AppVersionsQuerySchema>
appInstallCount: (app: MinimalAppIdentifiers) => Promise<number>
activeAppVersion: (app: MinimalAppIdentifiers) => Promise<AppVersion | undefined>
appVersionByTag: (app: MinimalOrganizationApp, tag: string) => Promise<AppVersionWithContext>
appVersionsDiff: (app: MinimalOrganizationApp, version: AppVersionIdentifiers) => Promise<AppVersionsDiffSchema>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ import {
import {CreateAssetUrl} from '../../api/graphql/app-management/generated/create-asset-url.js'
import {AppVersionById} from '../../api/graphql/app-management/generated/app-version-by-id.js'
import {AppVersions} from '../../api/graphql/app-management/generated/app-versions.js'
import {AppInstallCount} from '../../api/graphql/app-management/generated/app-install-count.js'
import {CreateApp, CreateAppMutationVariables} from '../../api/graphql/app-management/generated/create-app.js'
import {FetchSpecifications} from '../../api/graphql/app-management/generated/specifications.js'
import {ListApps} from '../../api/graphql/app-management/generated/apps.js'
Expand Down Expand Up @@ -650,6 +651,13 @@ export class AppManagementClient implements DeveloperPlatformClient {
}
}

async appInstallCount({id}: MinimalAppIdentifiers): Promise<number> {
const query = AppInstallCount
const variables = {appId: id}
const result = await this.appManagementRequest({query, variables})
return result.app.installCount
}

async appVersionByTag(
{id: appId, organizationId}: MinimalOrganizationApp,
versionTag: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,11 @@ export class PartnersClient implements DeveloperPlatformClient {
return this.request(AppVersionsQuery, variables)
}

async appInstallCount(_app: MinimalAppIdentifiers): Promise<number> {
// Install count is not supported in partners client.
throw new Error('Unsupported operation')
}

async appVersionByTag({apiKey}: MinimalOrganizationApp, versionTag: string): Promise<AppVersionWithContext> {
const input: AppVersionByTagVariables = {apiKey, versionTag}
const result: AppVersionByTagSchema = await this.request(AppVersionByTagQuery, input)
Expand Down
Loading