Skip to content

Commit d31ea11

Browse files
Merge pull request #6105 from Shopify/07-14-send_uuid_on_app_version_create_to_do_app_matching
Send uuid on app version create to do app matching
2 parents 78b1d3e + 7a09ce3 commit d31ea11

File tree

15 files changed

+73
-71
lines changed

15 files changed

+73
-71
lines changed

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,12 @@ describe('manifest', () => {
616616
})
617617

618618
// When
619-
const manifest = await app.manifest()
619+
const manifest = await app.manifest({
620+
app: 'API_KEY',
621+
extensions: {app_access: 'UUID_A'},
622+
extensionIds: {},
623+
extensionsNonUuidManaged: {},
624+
})
620625

621626
// Then
622627
expect(manifest).toEqual({
@@ -627,6 +632,7 @@ describe('manifest', () => {
627632
type: 'app_access_external',
628633
handle: 'app_access',
629634
uid: appAccessModule.uid,
635+
uuid: 'UUID_A',
630636
assets: appAccessModule.uid,
631637
target: appAccessModule.contextValue,
632638
config: expect.objectContaining({
@@ -664,7 +670,7 @@ describe('manifest', () => {
664670
})
665671

666672
// When
667-
const manifest = await app.manifest()
673+
const manifest = await app.manifest(undefined)
668674

669675
// Then
670676
expect(manifest).toEqual({
@@ -722,7 +728,7 @@ describe('manifest', () => {
722728
})
723729

724730
// When
725-
const manifest = await app.manifest()
731+
const manifest = await app.manifest(undefined)
726732

727733
// Then
728734
expect(manifest).toEqual({

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-non-null-assertion */
22
import {AppErrors, isWebType} from './loader.js'
33
import {ensurePathStartsWithSlash} from './validation/common.js'
4+
import {Identifiers} from './identifiers.js'
45
import {ExtensionInstance} from '../extensions/extension-instance.js'
56
import {isType} from '../../utilities/types.js'
67
import {FunctionConfigType} from '../extensions/specifications/function.js'
@@ -313,7 +314,7 @@ export interface AppInterface<
313314
* If creating an app on the platform based on this app and its configuration, what default options should the app take?
314315
*/
315316
creationDefaultOptions(): CreateAppOptions
316-
manifest: () => Promise<AppManifest>
317+
manifest: (identifiers: Identifiers | undefined) => Promise<AppManifest>
317318
removeExtension: (extensionUid: string) => void
318319
updateHiddenConfig: (values: Partial<AppHiddenConfig>) => Promise<void>
319320
setDevApplicationURLs: (devApplicationURLs: ApplicationURLs) => void
@@ -415,7 +416,7 @@ export class App<
415416
this.realExtensions.forEach((ext) => ext.patchWithAppDevURLs(devApplicationURLs))
416417
}
417418

418-
async manifest(): Promise<AppManifest> {
419+
async manifest(identifiers: Identifiers | undefined): Promise<AppManifest> {
419420
const modules = await Promise.all(
420421
this.realExtensions.map(async (module) => {
421422
const config = await module.deployConfig({
@@ -426,6 +427,7 @@ export class App<
426427
type: module.externalType,
427428
handle: module.handle,
428429
uid: module.uid,
430+
uuid: identifiers?.extensions[module.localIdentifier],
429431
assets: module.uid,
430432
target: module.contextValue,
431433
config: (config ?? {}) as JsonMapType,

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

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,9 @@ describe('getAppIdentifiers', () => {
208208
})
209209

210210
// When
211-
const got = getAppIdentifiers(
212-
{
213-
app,
214-
},
215-
testDeveloperPlatformClient(),
216-
)
211+
const got = getAppIdentifiers({
212+
app,
213+
})
217214

218215
// Then
219216
expect(got.app).toEqual('FOO')
@@ -238,7 +235,6 @@ describe('getAppIdentifiers', () => {
238235
{
239236
app,
240237
},
241-
testDeveloperPlatformClient(),
242238
{SHOPIFY_API_KEY: 'FOO', SHOPIFY_TEST_UI_EXTENSION_ID: 'BAR'},
243239
)
244240

@@ -247,34 +243,4 @@ describe('getAppIdentifiers', () => {
247243
expect((got.extensions ?? {})['test-ui-extension']).toEqual('BAR')
248244
})
249245
})
250-
251-
test('returns the UIDs when Atomic Deployments is enabled', async () => {
252-
await inTemporaryDirectory(async (tmpDir: string) => {
253-
// Given
254-
const uiExtension = await testUIExtension({
255-
directory: '/tmp/project/extensions/my-extension',
256-
idEnvironmentVariableName: 'SHOPIFY_MY_EXTENSION_ID',
257-
})
258-
const app = testApp({
259-
directory: tmpDir,
260-
dotenv: {
261-
path: joinPath(tmpDir, '.env'),
262-
variables: {SHOPIFY_API_KEY: 'FOO', SHOPIFY_TEST_UI_EXTENSION_ID: 'BAR'},
263-
},
264-
allExtensions: [uiExtension],
265-
})
266-
267-
// When
268-
const got = getAppIdentifiers(
269-
{
270-
app,
271-
},
272-
testDeveloperPlatformClient({supportsAtomicDeployments: true}),
273-
)
274-
275-
// Then
276-
expect(got.app).toEqual('FOO')
277-
expect((got.extensions ?? {})['test-ui-extension']).toEqual(uiExtension.uid)
278-
})
279-
})
280246
})

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ interface UpdateAppIdentifiersOptions {
4747
* @returns An copy of the app with the environment updated to reflect the updated identifiers.
4848
*/
4949
export async function updateAppIdentifiers(
50-
{app, identifiers, command}: UpdateAppIdentifiersOptions,
50+
{app, identifiers, command, developerPlatformClient}: UpdateAppIdentifiersOptions,
5151
systemEnvironment = process.env,
5252
): Promise<AppInterface> {
5353
let dotenvFile = app.dotenv
@@ -71,7 +71,10 @@ export async function updateAppIdentifiers(
7171
})
7272

7373
const contentIsEqual = deepCompare(dotenvFile.variables, updatedVariables)
74-
const writeToFile = !contentIsEqual && (command === 'deploy' || command === 'release')
74+
const writeToFile =
75+
!contentIsEqual &&
76+
(command === 'deploy' || command === 'release') &&
77+
!developerPlatformClient.supportsAtomicDeployments
7578
dotenvFile.variables = updatedVariables
7679

7780
if (writeToFile) {
@@ -95,7 +98,6 @@ interface GetAppIdentifiersOptions {
9598
*/
9699
export function getAppIdentifiers(
97100
{app}: GetAppIdentifiersOptions,
98-
developerPlatformClient: DeveloperPlatformClient,
99101
systemEnvironment = process.env,
100102
): Partial<UuidOnlyIdentifiers> {
101103
const envVariables = {
@@ -108,9 +110,6 @@ export function getAppIdentifiers(
108110
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
109111
extensionsIdentifiers[extension.localIdentifier] = envVariables[extension.idEnvironmentVariableName]!
110112
}
111-
if (developerPlatformClient.supportsAtomicDeployments) {
112-
extensionsIdentifiers[extension.localIdentifier] = extension.uid
113-
}
114113
}
115114
app.allExtensions.forEach(processExtension)
116115

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('writeManifestToBundle', () => {
2525
} as unknown as AppInterface
2626

2727
// When
28-
await writeManifestToBundle(mockApp, tmpDir)
28+
await writeManifestToBundle(mockApp, tmpDir, undefined)
2929

3030
// Then
3131
const manifestPath = joinPath(tmpDir, 'manifest.json')

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
import {AppInterface} from '../models/app/app.js'
33
import {AssetUrlSchema, DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
44
import {MinimalAppIdentifiers} from '../models/organization.js'
5+
import {Identifiers} from '../models/app/identifiers.js'
56
import {joinPath} from '@shopify/cli-kit/node/path'
67
import {brotliCompress, zip} from '@shopify/cli-kit/node/archiver'
78
import {formData, fetch} from '@shopify/cli-kit/node/http'
89
import {readFileSync} from '@shopify/cli-kit/node/fs'
910
import {AbortError} from '@shopify/cli-kit/node/error'
1011
import {writeFile} from 'fs/promises'
1112

12-
export async function writeManifestToBundle(app: AppInterface, bundlePath: string) {
13-
const appManifest = await app.manifest()
13+
export async function writeManifestToBundle(
14+
app: AppInterface,
15+
bundlePath: string,
16+
identifiers: Identifiers | undefined,
17+
) {
18+
const appManifest = await app.manifest(identifiers)
1419
const manifestPath = joinPath(bundlePath, 'manifest.json')
1520
await writeFile(manifestPath, JSON.stringify(appManifest, null, 2))
1621
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export async function ensureDeployContext(options: DeployOptions): Promise<Ident
153153
force,
154154
release: !noRelease,
155155
developerPlatformClient,
156-
envIdentifiers: getAppIdentifiers({app}, developerPlatformClient),
156+
envIdentifiers: getAppIdentifiers({app}),
157157
remoteApp,
158158
activeAppVersion,
159159
})

packages/app/src/cli/services/context/breakdown-extensions.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,20 +368,24 @@ function loadExtensionsIdentifiersBreakdown(
368368
(ext) => extensionTypeStrategy(specs, ext.specification?.identifier) === 'uuid',
369369
)
370370

371+
function moduleHasUUIDorUID(module: AppModuleVersion, identifier: string) {
372+
return module.registrationUuid === identifier || module.registrationId === identifier
373+
}
374+
371375
const extensionsToUpdate = Object.entries(localRegistration)
372-
.filter(([_identifier, uuid]) => extensionModules.map((module) => module.registrationUuid!).includes(uuid))
376+
.filter(([_identifier, uuid]) => extensionModules.some((module) => moduleHasUUIDorUID(module, uuid)))
373377
.map(([identifier, _uuid]) => identifier)
374378

375379
let extensionsToCreate = Object.entries(localRegistration)
376-
.filter(([_identifier, uuid]) => !extensionModules.map((module) => module.registrationUuid!).includes(uuid))
380+
.filter(([_identifier, uuid]) => !extensionModules.some((module) => moduleHasUUIDorUID(module, uuid)))
377381
.map(([identifier, _uuid]) => identifier)
378382
extensionsToCreate = Array.from(new Set(extensionsToCreate.concat(toCreate.map((source) => source.localIdentifier))))
379383

380384
const extensionsOnlyRemote = extensionModules
381385
.filter(
382386
(module) =>
383-
!Object.values(localRegistration).includes(module.registrationUuid!) &&
384-
!toCreate.map((source) => source.localIdentifier).includes(module.registrationUuid!),
387+
!Object.values(localRegistration).some((uuid) => moduleHasUUIDorUID(module, uuid)) &&
388+
!toCreate.map((source) => source.localIdentifier).some((identifier) => moduleHasUUIDorUID(module, identifier)),
385389
)
386390
.map((module) => module.registrationTitle)
387391

packages/app/src/cli/services/context/id-matching.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ describe('automaticMatchmaking: with Atomic Deployments enabled', () => {
781781
const got = await automaticMatchmaking(
782782
[EXTENSION_A, EXTENSION_A_2],
783783
[REGISTRATION_A],
784-
{},
784+
{'extension-a': 'UUID_A'},
785785
testDeveloperPlatformClient({supportsAtomicDeployments: true}),
786786
)
787787

@@ -792,6 +792,7 @@ describe('automaticMatchmaking: with Atomic Deployments enabled', () => {
792792
toCreate: [EXTENSION_A_2],
793793
toManualMatch: {local: [], remote: []},
794794
}
795+
795796
expect(got).toEqual(expected)
796797
})
797798
})

packages/app/src/cli/services/context/id-matching.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ interface MatchResult {
2222
* Filter function to match a local and a remote source by type and handle
2323
*/
2424
const sameTypeAndName = (local: LocalSource, remote: RemoteSource) => {
25-
return remote.type === local.graphQLType && slugify(remote.title) === slugify(local.handle)
25+
return (
26+
remote.type.toLowerCase() === local.graphQLType.toLowerCase() && slugify(remote.title) === slugify(local.handle)
27+
)
2628
}
2729

2830
/**
@@ -67,26 +69,42 @@ function matchByNameAndType(
6769
/**
6870
* Automatically match local and remote sources if they have the same UID
6971
*/
70-
function matchByUUID(
72+
function matchByUIDandUUID(
7173
local: LocalSource[],
7274
remote: RemoteSource[],
75+
ids: IdentifiersExtensions,
7376
): {
7477
matched: IdentifiersExtensions
7578
toCreate: LocalSource[]
7679
toConfirm: {local: LocalSource; remote: RemoteSource}[]
7780
toManualMatch: {local: LocalSource[]; remote: RemoteSource[]}
7881
} {
7982
const matched: IdentifiersExtensions = {}
83+
const pendingLocal: LocalSource[] = []
8084

85+
// First, try to match by UID, then by UUID.
8186
local.forEach((localSource) => {
82-
const possibleMatch = remote.find((remoteSource) => remoteSource.uuid === localSource.uid)
83-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84-
if (possibleMatch) matched[localSource.localIdentifier] = possibleMatch.uuid!
87+
const matchByUID = remote.find((remoteSource) => remoteSource.id === localSource.uid)
88+
const matchByUUID = remote.find((remoteSource) => remoteSource.uuid === ids[localSource.localIdentifier])
89+
90+
if (matchByUID) {
91+
matched[localSource.localIdentifier] = matchByUID.id
92+
} else if (matchByUUID) {
93+
matched[localSource.localIdentifier] = matchByUUID.uuid
94+
} else {
95+
pendingLocal.push(localSource)
96+
}
8597
})
8698

87-
const toCreate = local.filter((elem) => !matched[elem.localIdentifier])
99+
const pendingRemote = remote.filter(
100+
(remoteSource) =>
101+
!Object.values(matched).includes(remoteSource.uuid) && !Object.values(matched).includes(remoteSource.id),
102+
)
103+
104+
// Then, try to match by name and type as a last resort.
105+
const {matched: matchedByName, toCreate, toConfirm, toManualMatch} = matchByNameAndType(pendingLocal, pendingRemote)
88106

89-
return {matched, toCreate, toConfirm: [], toManualMatch: {local: [], remote: []}}
107+
return {matched: {...matched, ...matchedByName}, toCreate, toConfirm, toManualMatch}
90108
}
91109

92110
function migrateLegacyFunctions(
@@ -225,7 +243,7 @@ export async function automaticMatchmaking(
225243
const {local, remote} = pendingAfterMigratingFunctions
226244

227245
const {matched, toCreate, toConfirm, toManualMatch} = useUuidMatching
228-
? matchByUUID(local, remote)
246+
? matchByUIDandUUID(localSources, remoteSources, ids)
229247
: matchByNameAndType(local, remote)
230248

231249
return {

0 commit comments

Comments
 (0)