Skip to content

Commit 39510fe

Browse files
committed
Hook DCDD import into import-extensions command
1 parent a61c3ab commit 39510fe

File tree

6 files changed

+118
-45
lines changed

6 files changed

+118
-45
lines changed

packages/app/src/cli/commands/app/deploy.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default class Deploy extends AppLinkedCommand {
8787
}
8888
this.failMissingNonTTYFlags(flags, requiredNonTTYFlags)
8989

90-
const {app, remoteApp, developerPlatformClient, organization} = await linkedAppContext({
90+
const {app, remoteApp, developerPlatformClient, organization, specifications} = await linkedAppContext({
9191
directory: flags.path,
9292
clientId,
9393
forceRelink: flags.reset,
@@ -106,6 +106,7 @@ export default class Deploy extends AppLinkedCommand {
106106
version: flags.version,
107107
commitReference: flags['source-control-url'],
108108
skipBuild: flags['no-build'],
109+
specifications,
109110
})
110111

111112
return {app: result.app}

packages/app/src/cli/commands/app/import-extensions.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {getMigrationChoices, selectMigrationChoice} from '../../prompts/import-e
66
import {getExtensions} from '../../services/fetch-extensions.js'
77
import {Flags} from '@oclif/core'
88
import {globalFlags} from '@shopify/cli-kit/node/cli'
9-
import {renderSuccess} from '@shopify/cli-kit/node/ui'
9+
import {renderSingleTask, renderSuccess} from '@shopify/cli-kit/node/ui'
10+
import {outputContent} from '@shopify/cli-kit/node/output'
1011

1112
export default class ImportExtensions extends AppLinkedCommand {
1213
static description = 'Import dashboard-managed extensions into your app.'
@@ -24,21 +25,31 @@ export default class ImportExtensions extends AppLinkedCommand {
2425

2526
async run(): Promise<AppLinkedCommandOutput> {
2627
const {flags} = await this.parse(ImportExtensions)
27-
const appContext = await linkedAppContext({
28-
directory: flags.path,
29-
clientId: flags['client-id'],
30-
forceRelink: flags.reset,
31-
userProvidedConfigName: flags.config,
32-
})
28+
const {appContext, extensions, migrationChoices} = await renderSingleTask({
29+
title: outputContent`Loading app`,
30+
task: async () => {
31+
const appContext = await linkedAppContext({
32+
directory: flags.path,
33+
clientId: flags['client-id'],
34+
forceRelink: flags.reset,
35+
userProvidedConfigName: flags.config,
36+
})
3337

34-
const extensions = await getExtensions({
35-
developerPlatformClient: appContext.developerPlatformClient,
36-
apiKey: appContext.remoteApp.apiKey,
37-
organizationId: appContext.remoteApp.organizationId,
38-
extensionTypes: allExtensionTypes,
39-
})
38+
const extensions = await getExtensions({
39+
developerPlatformClient: appContext.developerPlatformClient,
40+
apiKey: appContext.remoteApp.apiKey,
41+
organizationId: appContext.remoteApp.organizationId,
42+
extensionTypes: allExtensionTypes,
43+
})
4044

41-
const migrationChoices = getMigrationChoices(extensions)
45+
const migrationChoices = getMigrationChoices(extensions)
46+
return {
47+
appContext,
48+
extensions,
49+
migrationChoices,
50+
}
51+
},
52+
})
4253

4354
if (migrationChoices.length === 0) {
4455
renderSuccess({headline: ['No extensions to migrate.']})
@@ -47,8 +58,7 @@ export default class ImportExtensions extends AppLinkedCommand {
4758
await importExtensions({
4859
...appContext,
4960
extensions,
50-
extensionTypes: migrationChoice.extensionTypes,
51-
buildTomlObject: migrationChoice.buildTomlObject,
61+
migrationChoice,
5262
})
5363
}
5464

packages/app/src/cli/prompts/import-extensions.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import {CurrentAppConfiguration} from '../models/app/app.js'
88
import {AbortError} from '@shopify/cli-kit/node/error'
99
import {renderSelectPrompt} from '@shopify/cli-kit/node/ui'
1010

11-
export interface MigrationChoice {
11+
interface MigrationChoiceCommon {
1212
label: string
1313
value: string
14+
neverSelectAutomatically?: boolean
15+
}
16+
17+
type ExtensionMigrationChoice = MigrationChoiceCommon & {
18+
mode: 'extension'
1419
extensionTypes: string[]
1520
buildTomlObject: (
1621
ext: ExtensionRegistration,
@@ -19,8 +24,19 @@ export interface MigrationChoice {
1924
) => string
2025
}
2126

27+
type SupportedShopImportSources = 'declarative definitions'
28+
29+
export type ShopImportMigrationChoice = MigrationChoiceCommon & {
30+
mode: 'shop-import'
31+
// Only declarative definitions are supported for shop import at present.
32+
value: SupportedShopImportSources
33+
}
34+
35+
export type MigrationChoice = ExtensionMigrationChoice | ShopImportMigrationChoice
36+
2237
export const allMigrationChoices: MigrationChoice[] = [
2338
{
39+
mode: 'extension',
2440
label: 'Payments Extensions',
2541
value: 'payments',
2642
extensionTypes: [
@@ -34,39 +50,51 @@ export const allMigrationChoices: MigrationChoice[] = [
3450
buildTomlObject: buildPaymentsTomlObject,
3551
},
3652
{
53+
mode: 'extension',
3754
label: 'Flow Extensions',
3855
value: 'flow',
3956
extensionTypes: ['flow_action_definition', 'flow_trigger_definition', 'flow_trigger_discovery_webhook'],
4057
buildTomlObject: buildFlowTomlObject,
4158
},
4259
{
60+
mode: 'extension',
4361
label: 'Marketing Activity Extensions',
4462
value: 'marketing activity',
4563
extensionTypes: ['marketing_activity_extension'],
4664
buildTomlObject: buildMarketingActivityTomlObject,
4765
},
4866
{
67+
mode: 'extension',
4968
label: 'Subscription Link Extensions',
5069
value: 'subscription link',
5170
extensionTypes: ['subscription_link', 'subscription_link_extension'],
5271
buildTomlObject: buildSubscriptionLinkTomlObject,
5372
},
5473
{
74+
mode: 'extension',
5575
label: 'Admin Link extensions',
5676
value: 'link extension',
5777
extensionTypes: ['app_link', 'bulk_action'],
5878
buildTomlObject: buildAdminLinkTomlObject,
5979
},
80+
{
81+
mode: 'shop-import',
82+
label: 'Metafields and Metaobject definitions',
83+
value: 'declarative definitions',
84+
neverSelectAutomatically: true,
85+
},
6086
]
6187

6288
export function getMigrationChoices(extensions: ExtensionRegistration[]): MigrationChoice[] {
63-
return allMigrationChoices.filter((choice) =>
64-
choice.extensionTypes.some((type) => extensions.some((ext) => ext.type.toLowerCase() === type.toLowerCase())),
89+
return allMigrationChoices.filter(
90+
(choice) =>
91+
choice.mode === 'shop-import' ||
92+
choice.extensionTypes.some((type) => extensions.some((ext) => ext.type.toLowerCase() === type.toLowerCase())),
6593
)
6694
}
6795

6896
export async function selectMigrationChoice(migrationChoices: MigrationChoice[]): Promise<MigrationChoice> {
69-
if (migrationChoices.length === 1 && migrationChoices[0]) {
97+
if (migrationChoices.length === 1 && migrationChoices[0] && !migrationChoices[0].neverSelectAutomatically) {
7098
return migrationChoices[0]
7199
}
72100

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Organization, OrganizationApp} from '../models/organization.js'
1111
import {reloadApp} from '../models/app/loader.js'
1212
import {ExtensionRegistration} from '../api/graphql/all_app_extension_registrations.js'
1313
import {getTomls} from '../utilities/app/config/getTomls.js'
14+
import {RemoteAwareExtensionSpecification} from '../models/extensions/specification.js'
1415
import {renderInfo, renderSuccess, renderTasks, renderConfirmationPrompt, isTTY} from '@shopify/cli-kit/node/ui'
1516
import {mkdir} from '@shopify/cli-kit/node/fs'
1617
import {joinPath, dirname} from '@shopify/cli-kit/node/path'
@@ -52,6 +53,9 @@ export interface DeployOptions {
5253

5354
/** If true, skip building any elements of the app that require building */
5455
skipBuild: boolean
56+
57+
/** The specifications of the extensions */
58+
specifications: RemoteAwareExtensionSpecification[]
5559
}
5660

5761
interface TasksContext {
@@ -64,14 +68,16 @@ interface ImportExtensionsIfNeededOptions {
6468
remoteApp: OrganizationApp
6569
developerPlatformClient: DeveloperPlatformClient
6670
force: boolean
71+
organization: Organization
72+
specifications: RemoteAwareExtensionSpecification[]
6773
}
6874

6975
async function handleSupportedDashboardExtensions(
7076
options: ImportExtensionsIfNeededOptions & {
7177
extensions: ExtensionRegistration[]
7278
},
7379
): Promise<AppLinkedInterface> {
74-
const {app, remoteApp, developerPlatformClient, force, extensions} = options
80+
const {app, remoteApp, developerPlatformClient, force, extensions, organization, specifications} = options
7581

7682
if (force || !isTTY()) {
7783
return app
@@ -96,6 +102,8 @@ async function handleSupportedDashboardExtensions(
96102
remoteApp,
97103
developerPlatformClient,
98104
extensions,
105+
organization,
106+
specifications,
99107
})
100108
return reloadApp(app)
101109
}
@@ -108,7 +116,7 @@ async function handleUnsupportedDashboardExtensions(
108116
extensions: ExtensionRegistration[]
109117
},
110118
): Promise<AppLinkedInterface> {
111-
const {app, remoteApp, developerPlatformClient, force, extensions} = options
119+
const {app, remoteApp, developerPlatformClient, force, extensions, organization, specifications} = options
112120

113121
const message = [
114122
`App can't be deployed until Partner Dashboard managed extensions are added to your version or removed from your app:\n`,
@@ -133,6 +141,8 @@ async function handleUnsupportedDashboardExtensions(
133141
remoteApp,
134142
developerPlatformClient,
135143
extensions,
144+
organization,
145+
specifications,
136146
})
137147
return reloadApp(app)
138148
} else {
@@ -171,13 +181,15 @@ export async function importExtensionsIfNeeded(options: ImportExtensionsIfNeeded
171181
}
172182

173183
export async function deploy(options: DeployOptions) {
174-
const {remoteApp, developerPlatformClient, noRelease, force} = options
184+
const {remoteApp, developerPlatformClient, noRelease, force, organization, specifications} = options
175185

176186
const app = await importExtensionsIfNeeded({
177187
app: options.app,
178188
remoteApp,
179189
developerPlatformClient,
180190
force,
191+
organization,
192+
specifications,
181193
})
182194

183195
const {identifiers, didMigrateExtensionsToDevDash} = await ensureDeployContext({

packages/app/src/cli/services/generate/shop-import/declarative-definitions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ export function processDeclarativeDefinitionNodes(
200200
}
201201
}
202202

203-
async function _importDeclarativeDefinitions(options: ImportDeclarativeDefinitionsOptions) {
203+
export async function importDeclarativeDefinitions(options: ImportDeclarativeDefinitionsOptions) {
204204
const adminSession = await renderSingleTask({
205205
title: outputContent`Connecting to shop`,
206206
task: async () => {

packages/app/src/cli/services/import-extensions.ts

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
1-
import {AppLinkedInterface, CurrentAppConfiguration} from '../models/app/app.js'
1+
import {importDeclarativeDefinitions} from './generate/shop-import/declarative-definitions.js'
2+
import {AppLinkedInterface} from '../models/app/app.js'
23
import {updateAppIdentifiers, IdentifiersExtensions} from '../models/app/identifiers.js'
34
import {ExtensionRegistration} from '../api/graphql/all_app_extension_registrations.js'
45
import {DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
56
import {MAX_EXTENSION_HANDLE_LENGTH} from '../models/extensions/schemas.js'
6-
import {OrganizationApp} from '../models/organization.js'
7-
import {allMigrationChoices, getMigrationChoices} from '../prompts/import-extensions.js'
7+
import {Organization, OrganizationApp} from '../models/organization.js'
8+
import {
9+
allMigrationChoices,
10+
getMigrationChoices,
11+
MigrationChoice,
12+
ShopImportMigrationChoice,
13+
} from '../prompts/import-extensions.js'
814
import {configurationFileNames, blocks} from '../constants.js'
15+
import {RemoteAwareExtensionSpecification} from '../models/extensions/specification.js'
916
import {renderSelectPrompt, renderSuccess} from '@shopify/cli-kit/node/ui'
1017
import {basename, joinPath} from '@shopify/cli-kit/node/path'
1118
import {removeFile, writeFile, fileExists, mkdir, touchFile} from '@shopify/cli-kit/node/fs'
1219
import {outputContent} from '@shopify/cli-kit/node/output'
1320
import {slugify, hyphenate} from '@shopify/cli-kit/common/string'
14-
import {AbortError, AbortSilentError} from '@shopify/cli-kit/node/error'
21+
import {AbortError, AbortSilentError, BugError} from '@shopify/cli-kit/node/error'
1522

16-
export const allExtensionTypes = allMigrationChoices.flatMap((choice) => choice.extensionTypes)
23+
export const allExtensionTypes = allMigrationChoices.flatMap((choice) =>
24+
choice.mode === 'extension' ? choice.extensionTypes : [],
25+
)
1726

1827
interface ImportAllOptions {
1928
app: AppLinkedInterface
2029
remoteApp: OrganizationApp
2130
developerPlatformClient: DeveloperPlatformClient
2231
extensions: ExtensionRegistration[]
32+
organization: Organization
33+
specifications: RemoteAwareExtensionSpecification[]
2334
}
2435

2536
interface ImportOptions extends ImportAllOptions {
26-
extensionTypes: string[]
27-
buildTomlObject: (
28-
ext: ExtensionRegistration,
29-
allExtensions: ExtensionRegistration[],
30-
appConfig: CurrentAppConfiguration,
31-
) => string
37+
migrationChoice: MigrationChoice
3238
all?: boolean
3339
}
3440

@@ -76,8 +82,23 @@ async function handleExtensionDirectory({
7682
return {directory: extensionDirectory, action: DirectoryAction.Write}
7783
}
7884

85+
async function importConfigurationFromShop(options: ImportOptions & {migrationChoice: ShopImportMigrationChoice}) {
86+
switch (options.migrationChoice.value) {
87+
case 'declarative definitions':
88+
return importDeclarativeDefinitions(options)
89+
default:
90+
throw new BugError(`Unsupported shop import source: ${options.migrationChoice.value}`)
91+
}
92+
}
93+
7994
export async function importExtensions(options: ImportOptions) {
80-
const {app, remoteApp, developerPlatformClient, extensionTypes, extensions, buildTomlObject, all} = options
95+
const {app, remoteApp, developerPlatformClient, migrationChoice, extensions, all} = options
96+
97+
if (migrationChoice.mode === 'shop-import') {
98+
return importConfigurationFromShop({...options, migrationChoice})
99+
}
100+
101+
const {extensionTypes, buildTomlObject} = migrationChoice
81102

82103
let extensionsToMigrate = extensions.filter((ext) => extensionTypes.includes(ext.type.toLowerCase()))
83104
extensionsToMigrate = filterOutImportedExtensions(app, extensionsToMigrate)
@@ -144,14 +165,15 @@ export function filterOutImportedExtensions(app: AppLinkedInterface, extensions:
144165
export async function importAllExtensions(options: ImportAllOptions) {
145166
const migrationChoices = getMigrationChoices(options.extensions)
146167
await Promise.all(
147-
migrationChoices.map(async (choice) => {
148-
return importExtensions({
149-
...options,
150-
extensionTypes: choice.extensionTypes,
151-
buildTomlObject: choice.buildTomlObject,
152-
all: true,
153-
})
154-
}),
168+
migrationChoices
169+
.filter((choice) => choice.mode === 'extension')
170+
.map(async (choice) => {
171+
return importExtensions({
172+
...options,
173+
migrationChoice: choice,
174+
all: true,
175+
})
176+
}),
155177
)
156178
}
157179

0 commit comments

Comments
 (0)