Skip to content

Commit 32dd132

Browse files
authored
Merge pull request #638 from Shopify/functions-id-matching
Refactor extensions ID-matching
2 parents 958e49f + 2df045c commit 32dd132

File tree

11 files changed

+628
-442
lines changed

11 files changed

+628
-442
lines changed

packages/app/src/cli/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export const extensionGraphqlId = (type: ExtensionTypes) => {
310310
case 'delivery_customization':
311311
case 'shipping_rate_presenter':
312312
// As we add new extensions, this bug will force us to add a new case here.
313-
return type
313+
return type.toUpperCase()
314314
}
315315
}
316316

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe('manualMatch: when all extensions are matched', () => {
9191
vi.mocked(ui.prompt).mockResolvedValueOnce({uuid: 'UUID_A_2'})
9292

9393
// When
94-
const got = await manualMatchIds([EXTENSION_A, EXTENSION_A_2], [REGISTRATION_A, REGISTRATION_A_2])
94+
const got = await manualMatchIds({local: [EXTENSION_A, EXTENSION_A_2], remote: [REGISTRATION_A, REGISTRATION_A_2]})
9595

9696
// Then
9797
const expected: ManualMatchResult = {
@@ -109,7 +109,7 @@ describe('manualMatch: when there are more local extensions', () => {
109109
vi.mocked(ui.prompt).mockResolvedValueOnce({uuid: 'UUID_A'})
110110

111111
// When
112-
const got = await manualMatchIds([EXTENSION_A, EXTENSION_A_2], [REGISTRATION_A])
112+
const got = await manualMatchIds({local: [EXTENSION_A, EXTENSION_A_2], remote: [REGISTRATION_A]})
113113

114114
// Then
115115
const expected: ManualMatchResult = {
@@ -129,7 +129,10 @@ describe('manualMatch: when there are more local extensions and user selects to
129129
vi.mocked(ui.prompt).mockResolvedValueOnce({uuid: 'UUID_A_2'})
130130

131131
// When
132-
const got = await manualMatchIds([EXTENSION_A, EXTENSION_A_2, EXTENSION_B], [REGISTRATION_A, REGISTRATION_A_2])
132+
const got = await manualMatchIds({
133+
local: [EXTENSION_A, EXTENSION_A_2, EXTENSION_B],
134+
remote: [REGISTRATION_A, REGISTRATION_A_2],
135+
})
133136

134137
// Then
135138
const expected: ManualMatchResult = {
@@ -149,7 +152,10 @@ describe('manualMatch: when not all remote extensions are matched', () => {
149152
vi.mocked(ui.prompt).mockResolvedValueOnce({uuid: 'create'})
150153

151154
// When
152-
const got = await manualMatchIds([EXTENSION_A, EXTENSION_A_2, EXTENSION_B], [REGISTRATION_A, REGISTRATION_A_2])
155+
const got = await manualMatchIds({
156+
local: [EXTENSION_A, EXTENSION_A_2, EXTENSION_B],
157+
remote: [REGISTRATION_A, REGISTRATION_A_2],
158+
})
153159

154160
// Then
155161
const expected: ManualMatchResult = {result: 'pending-remote'}
Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import {ExtensionRegistration} from '../dev/create-extension.js'
1+
import {LocalExtension} from './id-matching.js'
2+
import {selectRegistrationPrompt} from './prompts.js'
3+
import {RemoteRegistration} from './identifiers.js'
24
import {IdentifiersExtensions} from '../../models/app/identifiers.js'
3-
import {Extension} from '../../models/app/extensions.js'
4-
import {ui} from '@shopify/cli-kit'
55

66
export type ManualMatchResult =
77
| {
88
result: 'ok'
99
identifiers: IdentifiersExtensions
10-
toCreate: Extension[]
10+
toCreate: LocalExtension[]
1111
}
1212
| {result: 'pending-remote'}
1313

@@ -16,49 +16,33 @@ export type ManualMatchResult =
1616
* The user can also select to create a new remote extension instead of selecting an existing one.
1717
* Manual matching will only show extensions of the same type as possible matches.
1818
* At the end of this process, all remote extensions must be matched to suceed.
19-
* @param localExtensions - The local extensions to match
20-
* @param remoteExtensions - The remote extensions to match
19+
* @param local - The local extensions to match
20+
* @param remote - The remote extensions to match
2121
* @returns The result of the manual matching
2222
*/
2323
export async function manualMatchIds(
24-
localExtensions: Extension[],
25-
remoteExtensions: ExtensionRegistration[],
24+
options: {
25+
local: LocalExtension[]
26+
remote: RemoteRegistration[]
27+
},
28+
registrationIdField: 'id' | 'uuid' = 'uuid',
2629
): Promise<ManualMatchResult> {
2730
const identifiers: {[key: string]: string} = {}
28-
let pendingRemote = remoteExtensions
29-
let pendingLocal = localExtensions
30-
for (const extension of localExtensions) {
31+
let pendingRemote = options.remote
32+
let pendingLocal = options.local
33+
const idField = registrationIdField
34+
for (const extension of options.local) {
3135
const registrationsForType = pendingRemote.filter((reg) => reg.type === extension.graphQLType)
3236
if (registrationsForType.length === 0) continue
3337
// eslint-disable-next-line no-await-in-loop
34-
const selected = await selectRegistrationPrompt(extension, registrationsForType)
38+
const selected = await selectRegistrationPrompt(extension, registrationsForType, idField)
3539
if (!selected) continue
3640

37-
identifiers[extension.localIdentifier] = selected.uuid
38-
pendingRemote = pendingRemote.filter((reg) => reg.uuid !== selected.uuid)
41+
identifiers[extension.localIdentifier] = selected[idField]
42+
pendingRemote = pendingRemote.filter((reg) => reg[idField] !== selected[idField])
3943
pendingLocal = pendingLocal.filter((reg) => reg.localIdentifier !== extension.localIdentifier)
4044
}
4145

4246
if (pendingRemote.length > 0) return {result: 'pending-remote'}
4347
return {result: 'ok', identifiers, toCreate: pendingLocal}
4448
}
45-
46-
export async function selectRegistrationPrompt(
47-
extension: Extension,
48-
registrations: ExtensionRegistration[],
49-
): Promise<ExtensionRegistration> {
50-
const registrationList = registrations.map((reg) => ({
51-
name: `Match it to ${reg.title} (ID: ${reg.id} on Shopify Partners)`,
52-
value: reg.uuid,
53-
}))
54-
registrationList.push({name: 'Create new extension', value: 'create'})
55-
const choice: {uuid: string} = await ui.prompt([
56-
{
57-
type: 'autocomplete',
58-
name: 'uuid',
59-
message: `How would you like to deploy your "${extension.localIdentifier}"?`,
60-
choices: registrationList,
61-
},
62-
])
63-
return registrations.find((reg) => reg.uuid === choice.uuid)!
64-
}

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

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const EXTENSION_A: UIExtension = {
6161
type: 'checkout_post_purchase',
6262
graphQLType: 'CHECKOUT_POST_PURCHASE',
6363
configuration: {
64-
name: '',
64+
name: 'EXTENSION A',
6565
type: 'checkout_post_purchase',
6666
metafields: [],
6767
capabilities: {network_access: false, block_progress: false},
@@ -79,7 +79,7 @@ const EXTENSION_A_2: UIExtension = {
7979
type: 'checkout_post_purchase',
8080
graphQLType: 'CHECKOUT_POST_PURCHASE',
8181
configuration: {
82-
name: '',
82+
name: 'EXTENSION A 2',
8383
type: 'checkout_post_purchase',
8484
metafields: [],
8585
capabilities: {network_access: false, block_progress: false},
@@ -97,7 +97,7 @@ const EXTENSION_B: UIExtension = {
9797
type: 'product_subscription',
9898
graphQLType: 'SUBSCRIPTION_MANAGEMENT',
9999
configuration: {
100-
name: '',
100+
name: 'EXTENSION B',
101101
type: 'checkout_post_purchase',
102102
metafields: [],
103103
capabilities: {network_access: false, block_progress: false},
@@ -115,7 +115,7 @@ const EXTENSION_C: UIExtension = {
115115
type: 'theme',
116116
graphQLType: 'THEME_APP_EXTENSION',
117117
configuration: {
118-
name: '',
118+
name: 'EXTENSION C',
119119
type: 'checkout_post_purchase',
120120
metafields: [],
121121
capabilities: {network_access: false, block_progress: false},
@@ -133,7 +133,7 @@ const EXTENSION_D: UIExtension = {
133133
type: 'web_pixel_extension',
134134
graphQLType: 'WEB_PIXEL_EXTENSION',
135135
configuration: {
136-
name: '',
136+
name: 'EXTENSION D',
137137
type: 'checkout_post_purchase',
138138
metafields: [],
139139
capabilities: {network_access: false, block_progress: false},
@@ -146,7 +146,7 @@ const EXTENSION_D: UIExtension = {
146146
describe('automaticMatchmaking: case 3 some local extensions, no remote ones', () => {
147147
it('success and creates all local extensions', async () => {
148148
// When
149-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [], {})
149+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [], {}, 'uuid')
150150

151151
// Then
152152
const expected = ok({
@@ -162,7 +162,7 @@ describe('automaticMatchmaking: case 3 some local extensions, no remote ones', (
162162
describe('automaticMatchmaking: case 3b some local extensions of the same type, no remote ones', () => {
163163
it('success and creates all local extensions', async () => {
164164
// When
165-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_A_2], [], {})
165+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_A_2], [], {}, 'uuid')
166166

167167
// Then
168168
const expected = ok({
@@ -175,10 +175,26 @@ describe('automaticMatchmaking: case 3b some local extensions of the same type,
175175
})
176176
})
177177

178+
describe('automaticMatchmaking: case 3c some local extensions of the same type, only one remote', () => {
179+
it('success and creates all local extensions', async () => {
180+
// When
181+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_A_2], [REGISTRATION_A], {}, 'uuid')
182+
183+
// Then
184+
const expected = ok({
185+
identifiers: {EXTENSION_A: 'UUID_A'},
186+
pendingConfirmation: [],
187+
toCreate: [EXTENSION_A_2],
188+
toManualMatch: {local: [], remote: []},
189+
})
190+
expect(got).toEqual(expected)
191+
})
192+
})
193+
178194
describe('automaticMatchmaking: case 4 same number of extensions local and remote with matching types', () => {
179195
it('suceeds automatically', async () => {
180196
// When
181-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_A, REGISTRATION_B], {})
197+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_A, REGISTRATION_B], {}, 'uuid')
182198

183199
// Then
184200
const expected = ok({
@@ -198,6 +214,7 @@ describe('automaticMatchmaking: case 5 more extensions local than remote, all re
198214
[EXTENSION_A, EXTENSION_B, EXTENSION_C, EXTENSION_D],
199215
[REGISTRATION_A, REGISTRATION_B],
200216
{},
217+
'uuid',
201218
)
202219

203220
// Then
@@ -214,27 +231,27 @@ describe('automaticMatchmaking: case 5 more extensions local than remote, all re
214231
describe('automaticMatchmaking: case 6 remote extensions have types not present locally', () => {
215232
it('throw error, invalid local environment', async () => {
216233
// When
217-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_C, REGISTRATION_D], {})
234+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_C, REGISTRATION_D], {}, 'uuid')
218235

219236
// Then
220-
expect(got).toEqual(err(new Error('invalid-environment')))
237+
expect(got).toEqual(err('invalid-environment'))
221238
})
222239
})
223240

224241
describe('automaticMatchmaking: case 7 some extensions match, but other are missing', () => {
225242
it('throw error, invalid local environment', async () => {
226243
// When
227-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_A, REGISTRATION_C], {})
244+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_A, REGISTRATION_C], {}, 'uuid')
228245

229246
// Then
230-
expect(got).toEqual(err(new Error('invalid-environment')))
247+
expect(got).toEqual(err('invalid-environment'))
231248
})
232249
})
233250

234251
describe('automaticMatchmaking: case 8 multiple extensions of the same type locally and remotely', () => {
235252
it('success and returns extensions pending manual match', async () => {
236253
// When
237-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_A_2], [REGISTRATION_A, REGISTRATION_A_2], {})
254+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_A_2], [REGISTRATION_A, REGISTRATION_A_2], {}, 'uuid')
238255

239256
// Then
240257
const expected = ok({
@@ -254,6 +271,7 @@ describe('automaticMatchmaking: case 9 multiple extensions of the same type loca
254271
[EXTENSION_A, EXTENSION_A_2, EXTENSION_B],
255272
[REGISTRATION_A, REGISTRATION_A_2],
256273
{},
274+
'uuid',
257275
)
258276

259277
// Then
@@ -270,19 +288,24 @@ describe('automaticMatchmaking: case 9 multiple extensions of the same type loca
270288
describe('automaticMatchmaking: case 10 there are more remote than local extensions', () => {
271289
it('throw error, invalid local environment', async () => {
272290
// When
273-
const got = await automaticMatchmaking([EXTENSION_A], [REGISTRATION_A, REGISTRATION_A_2], {})
291+
const got = await automaticMatchmaking([EXTENSION_A], [REGISTRATION_A, REGISTRATION_A_2], {}, 'uuid')
274292

275293
// Then
276-
expect(got).toEqual(err(new Error('invalid-environment')))
294+
expect(got).toEqual(err('invalid-environment'))
277295
})
278296
})
279297

280298
describe('automaticMatchmaking: case 11 some extension have uuid, others can be matched', () => {
281299
it('suceeds automatically', async () => {
282300
// When
283-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_A, REGISTRATION_B], {
284-
EXTENSION_A: 'UUID_A',
285-
})
301+
const got = await automaticMatchmaking(
302+
[EXTENSION_A, EXTENSION_B],
303+
[REGISTRATION_A, REGISTRATION_B],
304+
{
305+
EXTENSION_A: 'UUID_A',
306+
},
307+
'uuid',
308+
)
286309

287310
// Then
288311
const expected = ok({
@@ -298,9 +321,14 @@ describe('automaticMatchmaking: case 11 some extension have uuid, others can be
298321
describe("automaticMatchmaking: case 12 some extension have uuid, but doesn't match a remote one", () => {
299322
it('suceeds rematching the extension to the correct UUID if the type is valid', async () => {
300323
// When
301-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [REGISTRATION_A, REGISTRATION_B], {
302-
EXTENSION_A: 'UUID_WRONG',
303-
})
324+
const got = await automaticMatchmaking(
325+
[EXTENSION_A, EXTENSION_B],
326+
[REGISTRATION_A, REGISTRATION_B],
327+
{
328+
EXTENSION_A: 'UUID_WRONG',
329+
},
330+
'uuid',
331+
)
304332

305333
// Then
306334
const expected = ok({
@@ -322,6 +350,7 @@ describe('automaticMatchmaking: case 13 duplicated extension types but some of t
322350
{
323351
EXTENSION_A: 'UUID_A',
324352
},
353+
'uuid',
325354
)
326355

327356
// Then
@@ -344,6 +373,7 @@ describe('automaticMatchmaking: case 14 a bit of everything', () => {
344373
{
345374
EXTENSION_D: 'UUID_D',
346375
},
376+
'uuid',
347377
)
348378

349379
// Then
@@ -362,7 +392,7 @@ describe('automaticMatchmaking: case 15 automatic matches with different names',
362392
// When
363393
const registrationNewA = {...REGISTRATION_A, title: 'A_NEW'}
364394
const registrationNewB = {...REGISTRATION_B, title: 'B_NEW'}
365-
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [registrationNewA, registrationNewB], {})
395+
const got = await automaticMatchmaking([EXTENSION_A, EXTENSION_B], [registrationNewA, registrationNewB], {}, 'uuid')
366396

367397
// Then
368398
const expected = ok({
@@ -381,9 +411,9 @@ describe('automaticMatchmaking: case 15 automatic matches with different names',
381411
describe('automaticMatchmaking: case 16 more remote than local extensions', () => {
382412
it('throw error, invalid local environment', async () => {
383413
// When
384-
const got = await automaticMatchmaking([EXTENSION_A], [REGISTRATION_A, REGISTRATION_B], {})
414+
const got = await automaticMatchmaking([EXTENSION_A], [REGISTRATION_A, REGISTRATION_B], {}, 'uuid')
385415

386416
// Then
387-
expect(got).toEqual(err(new Error('invalid-environment')))
417+
expect(got).toEqual(err('invalid-environment'))
388418
})
389419
})

0 commit comments

Comments
 (0)