Skip to content

Commit 081820f

Browse files
Merge pull request #6287 from Shopify/08-20-fix_issue_when_trying_to_import_and_already_imported_extension
Fix issue when trying to import an already imported extension
2 parents 356726b + 5884efc commit 081820f

File tree

5 files changed

+57
-12
lines changed

5 files changed

+57
-12
lines changed

packages/app/src/cli/models/app/identifiers.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export interface Identifiers {
3333
}
3434

3535
type UuidOnlyIdentifiers = Omit<Identifiers, 'extensionIds' | 'extensionsNonUuidManaged'>
36-
type UpdateAppIdentifiersCommand = 'dev' | 'deploy' | 'release'
36+
type UpdateAppIdentifiersCommand = 'dev' | 'deploy' | 'release' | 'import-extensions'
3737
interface UpdateAppIdentifiersOptions {
3838
app: AppInterface
3939
identifiers: UuidOnlyIdentifiers
@@ -72,9 +72,11 @@ export async function updateAppIdentifiers(
7272

7373
const contentIsEqual = deepCompare(dotenvFile.variables, updatedVariables)
7474
const writeToFile =
75-
!contentIsEqual &&
76-
(command === 'deploy' || command === 'release') &&
77-
!developerPlatformClient.supportsAtomicDeployments
75+
(!contentIsEqual &&
76+
(command === 'deploy' || command === 'release') &&
77+
!developerPlatformClient.supportsAtomicDeployments) ||
78+
command === 'import-extensions'
79+
7880
dotenvFile.variables = updatedVariables
7981

8082
if (writeToFile) {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {ensureDeployContext} from './context.js'
22
import {deploy, importExtensionsIfNeeded} from './deploy.js'
33
import {uploadExtensionsBundle} from './deploy/upload.js'
44
import {bundleAndBuildExtensions} from './deploy/bundle.js'
5-
import {importAllExtensions, allExtensionTypes} from './import-extensions.js'
5+
import {importAllExtensions, allExtensionTypes, filterOutImportedExtensions} from './import-extensions.js'
66
import {getExtensions} from './fetch-extensions.js'
77
import {reloadApp} from '../models/app/loader.js'
88
import {
@@ -75,6 +75,9 @@ beforeEach(() => {
7575

7676
// Mock getExtensions to return empty arrays by default
7777
vi.mocked(getExtensions).mockResolvedValue([])
78+
79+
// Mock filterOutImportedExtensions to return the extensions by default
80+
vi.mocked(filterOutImportedExtensions).mockImplementation((_app, extensions) => extensions)
7881
})
7982

8083
describe('deploy', () => {
@@ -840,6 +843,7 @@ describe('ImportExtensionsIfNeeded', () => {
840843

841844
vi.mocked(isTTY).mockReturnValue(true)
842845
vi.mocked(getExtensions).mockResolvedValue([])
846+
vi.mocked(filterOutImportedExtensions).mockReturnValue([])
843847

844848
// When
845849
const result = await importExtensionsIfNeeded({

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {uploadExtensionsBundle, UploadExtensionsBundleOutput} from './deploy/upl
22

33
import {ensureDeployContext} from './context.js'
44
import {bundleAndBuildExtensions} from './deploy/bundle.js'
5-
import {allExtensionTypes, importAllExtensions} from './import-extensions.js'
5+
import {allExtensionTypes, filterOutImportedExtensions, importAllExtensions} from './import-extensions.js'
66
import {getExtensions} from './fetch-extensions.js'
77
import {AppLinkedInterface} from '../models/app/app.js'
88
import {updateAppIdentifiers} from '../models/app/identifiers.js'
@@ -151,19 +151,21 @@ export async function importExtensionsIfNeeded(options: ImportExtensionsIfNeeded
151151
onlyDashboardManaged: true,
152152
})
153153

154-
if (extensions.length === 0) {
154+
const extensionsNotImportedYet = filterOutImportedExtensions(options.app, extensions)
155+
156+
if (extensionsNotImportedYet.length === 0) {
155157
return app
156158
}
157159

158160
if (developerPlatformClient.supportsDashboardManagedExtensions) {
159161
return handleSupportedDashboardExtensions({
160162
...options,
161-
extensions,
163+
extensions: extensionsNotImportedYet,
162164
})
163165
} else {
164166
return handleUnsupportedDashboardExtensions({
165167
...options,
166-
extensions,
168+
extensions: extensionsNotImportedYet,
167169
})
168170
}
169171
}

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {importExtensions} from './import-extensions.js'
1+
import {importExtensions, filterOutImportedExtensions} from './import-extensions.js'
22
import {buildTomlObject} from './flow/extension-to-toml.js'
3-
import {testAppLinked, testDeveloperPlatformClient} from '../models/app/app.test-data.js'
3+
import {testAppLinked, testDeveloperPlatformClient, testUIExtension} from '../models/app/app.test-data.js'
44
import {OrganizationApp} from '../models/organization.js'
55
import {ExtensionRegistration} from '../api/graphql/all_app_extension_registrations.js'
66
import {describe, expect, test, vi, beforeEach} from 'vitest'
@@ -237,3 +237,31 @@ describe('import-extensions', () => {
237237
})
238238
})
239239
})
240+
241+
describe('filterOutImportedExtensions', () => {
242+
test('filters out extensions that are already imported', async () => {
243+
// Given
244+
const extensionA = await testUIExtension({handle: 'my-extension-a'})
245+
const extensionB = await testUIExtension({handle: 'my-extension-b'})
246+
247+
const app = testAppLinked({
248+
dotenv: {
249+
path: '.env',
250+
variables: {
251+
SHOPIFY_MY_EXTENSION_A_ID: 'uuidA',
252+
SHOPIFY_MY_EXTENSION_B_ID: 'uuidB',
253+
SHOPIFY_SOME_OTHER_ID: 'someOtherId',
254+
},
255+
},
256+
allExtensions: [extensionA, extensionB],
257+
})
258+
259+
const extensions = [flowExtensionA, flowExtensionB, marketingActivityExtension]
260+
261+
// When
262+
const result = filterOutImportedExtensions(app, extensions)
263+
264+
// Then
265+
expect(result).toEqual([marketingActivityExtension])
266+
})
267+
})

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export async function importExtensions(options: ImportOptions) {
3737
const {app, remoteApp, developerPlatformClient, extensionTypes, extensions, buildTomlObject, all} = options
3838

3939
let extensionsToMigrate = extensions.filter((ext) => extensionTypes.includes(ext.type.toLowerCase()))
40+
extensionsToMigrate = filterOutImportedExtensions(app, extensionsToMigrate)
4041

4142
if (extensionsToMigrate.length === 0) {
4243
throw new AbortError('No extensions to migrate')
@@ -79,11 +80,19 @@ export async function importExtensions(options: ImportOptions) {
7980
extensions: extensionUuids,
8081
app: remoteApp.apiKey,
8182
},
82-
command: 'deploy',
83+
command: 'import-extensions',
8384
developerPlatformClient,
8485
})
8586
}
8687

88+
// import-extensions updates the .env file with the new UUIDs. we can use that to know if an extension was already imported.
89+
// From the app loaded extensions, get the UUID and compare with the pending extensions.
90+
export function filterOutImportedExtensions(app: AppLinkedInterface, extensions: ExtensionRegistration[]) {
91+
const cachedUUIDs = app.dotenv?.variables ?? {}
92+
const localExtensionUUIDs = app.allExtensions.map((ext) => cachedUUIDs[ext.idEnvironmentVariableName])
93+
return extensions.filter((ext) => !localExtensionUUIDs.includes(ext.uuid))
94+
}
95+
8796
export async function importAllExtensions(options: ImportAllOptions) {
8897
const migrationChoices = getMigrationChoices(options.extensions)
8998
await Promise.all(

0 commit comments

Comments
 (0)