@graphcommerce/hygraph-cli 9.0.4-canary.9 → 9.1.0-canary.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/index.js +928 -5
- package/package.json +17 -12
- package/tsconfig.json +2 -2
- package/dist/UpsertClient.js +0 -80
- package/dist/migrateHygraphCli.js +0 -88
- package/dist/migrationActionFactory.js +0 -133
- package/dist/migrations/graphcommerce5to6.js +0 -98
- package/dist/migrations/graphcommerce6to7.js +0 -206
- package/dist/migrations/graphcommerce7to8.js +0 -41
- package/dist/migrations/graphcommerce8to9.js +0 -68
- package/dist/migrations/index.js +0 -13
- package/dist/readSchema.js +0 -50
- package/dist/types.js +0 -2
- package/dist/utils/getConfig.js +0 -19
- package/dist/utils/getEndpointUrl.js +0 -43
- package/dist/utils/getManagementClient.js +0 -15
- package/dist/utils/graphCommerceLog.js +0 -15
package/dist/index.js
CHANGED
|
@@ -1,5 +1,928 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { loadConfig } from '@graphcommerce/next-config';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import prompts from 'prompts';
|
|
5
|
+
import { SimpleFieldType, VisibilityTypes, RelationalFieldType, Client } from '@hygraph/management-sdk';
|
|
6
|
+
import { gql, ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
|
|
7
|
+
import gql$1 from 'graphql-tag';
|
|
8
|
+
|
|
9
|
+
const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
10
|
+
const graphcommerceLog = (message, type) => {
|
|
11
|
+
const color = {
|
|
12
|
+
error: "\x1B[31m\x1B[1m%s\x1B[0m",
|
|
13
|
+
warning: "\x1B[33m\x1B[1m%s\x1B[0m",
|
|
14
|
+
info: "\x1B[36m\x1B[1m%s\x1B[0m"
|
|
15
|
+
};
|
|
16
|
+
console.log(type ? color[type] : "", `${message}`);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
dotenv.config();
|
|
20
|
+
function migrationActionFactory(schema, client) {
|
|
21
|
+
const actionMap = client ? {
|
|
22
|
+
create: {
|
|
23
|
+
model: (innerprops) => client.createModel(innerprops),
|
|
24
|
+
component: (innerprops) => client.createComponent(innerprops),
|
|
25
|
+
enumeration: (innerprops) => client.createEnumeration(innerprops),
|
|
26
|
+
simpleField: (innerprops) => client.createSimpleField(innerprops),
|
|
27
|
+
enumerableField: (innerprops) => client.createEnumerableField(innerprops),
|
|
28
|
+
componentField: (innerprops) => client.createComponentField(innerprops),
|
|
29
|
+
relationalField: (innerprops) => client.createRelationalField(innerprops),
|
|
30
|
+
unionField: (innerprops) => client.createUnionField(innerprops),
|
|
31
|
+
componentUnionField: (innerprops) => client.createComponentUnionField(innerprops)
|
|
32
|
+
},
|
|
33
|
+
update: {
|
|
34
|
+
model: (innerprops) => client.updateModel(innerprops),
|
|
35
|
+
component: (innerprops) => client.updateComponent(innerprops),
|
|
36
|
+
enumeration: (innerprops) => client.updateEnumeration(innerprops),
|
|
37
|
+
simpleField: (innerprops) => client.updateSimpleField(innerprops),
|
|
38
|
+
enumerableField: (innerprops) => client.updateEnumerableField(innerprops),
|
|
39
|
+
componentField: (innerprops) => client.updateComponentField(innerprops),
|
|
40
|
+
relationalField: (innerprops) => client.updateRelationalField(innerprops),
|
|
41
|
+
unionField: (innerprops) => client.updateUnionField(innerprops),
|
|
42
|
+
componentUnionField: (innerprops) => client.updateComponentUnionField(innerprops)
|
|
43
|
+
},
|
|
44
|
+
delete: {
|
|
45
|
+
model: (innerprops) => client.deleteModel(innerprops),
|
|
46
|
+
component: (innerprops) => client.deleteComponent(innerprops),
|
|
47
|
+
enumeration: (innerprops) => client.deleteEnumeration(innerprops),
|
|
48
|
+
simpleField: (innerprops) => client.deleteField(innerprops),
|
|
49
|
+
enumerableField: (innerprops) => client.deleteField(innerprops),
|
|
50
|
+
componentField: (innerprops) => client.deleteField(innerprops),
|
|
51
|
+
relationalField: (innerprops) => client.deleteField(innerprops),
|
|
52
|
+
unionField: (innerprops) => client.deleteField(innerprops),
|
|
53
|
+
componentUnionField: (innerprops) => client.deleteField(innerprops)
|
|
54
|
+
}
|
|
55
|
+
} : undefined;
|
|
56
|
+
const migrationAction = (_schema, type, action, props, parentApiId, parentType) => {
|
|
57
|
+
const alreadyExists = () => {
|
|
58
|
+
if (action !== "create") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
switch (type) {
|
|
62
|
+
case "model":
|
|
63
|
+
return schema.models.some((model) => model.apiId === props.apiId);
|
|
64
|
+
case "component":
|
|
65
|
+
return schema.components.some((component) => component.apiId === props.apiId);
|
|
66
|
+
case "enumeration":
|
|
67
|
+
return schema.enumerations.some((enumeration) => enumeration.apiId === props.apiId);
|
|
68
|
+
case "simpleField":
|
|
69
|
+
case "enumerableField":
|
|
70
|
+
case "relationalField":
|
|
71
|
+
case "unionField":
|
|
72
|
+
case "componentUnionField": {
|
|
73
|
+
let parent;
|
|
74
|
+
switch (parentType) {
|
|
75
|
+
case "model": {
|
|
76
|
+
parent = schema.models.find((model) => model.apiId === parentApiId);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "component": {
|
|
80
|
+
parent = schema.components.find((component) => component.apiId === parentApiId);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
default:
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return parent?.fields.some((field) => field.apiId === props.apiId);
|
|
87
|
+
}
|
|
88
|
+
default: {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const actionFunc = actionMap && actionMap[action] && actionMap[action][type];
|
|
94
|
+
if (!alreadyExists()) {
|
|
95
|
+
if (actionFunc) {
|
|
96
|
+
graphcommerceLog(`${capitalize(action)} ${type} with apiId ${props.apiId}...`);
|
|
97
|
+
actionFunc(props);
|
|
98
|
+
} else {
|
|
99
|
+
graphcommerceLog(`Action ${action} is not supported for ${type}`, "error");
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
graphcommerceLog(
|
|
103
|
+
`${capitalize(type)} with apiId ${props.apiId} on ${parentApiId} already exists`,
|
|
104
|
+
"warning"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
migrationAction
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const graphcommerce5to6 = async (schema, client) => {
|
|
114
|
+
const { migrationAction } = migrationActionFactory(schema, client);
|
|
115
|
+
migrationAction(schema, "enumeration", "create", {
|
|
116
|
+
displayName: "Row Links Variants",
|
|
117
|
+
apiId: "RowLinksVariants",
|
|
118
|
+
values: [
|
|
119
|
+
{ displayName: "Inline", apiId: "Inline" },
|
|
120
|
+
{ displayName: "Image Label Swiper", apiId: "ImageLabelSwiper" },
|
|
121
|
+
{ displayName: "Logo Swiper", apiId: "LogoSwiper" },
|
|
122
|
+
{ displayName: "USPS", apiId: "Usps" }
|
|
123
|
+
]
|
|
124
|
+
});
|
|
125
|
+
migrationAction(schema, "model", "create", {
|
|
126
|
+
apiId: "RowLinks",
|
|
127
|
+
apiIdPlural: "RowLinksMultiple",
|
|
128
|
+
displayName: "Row Links",
|
|
129
|
+
description: "Row Links is a Row of PageLinks with different variants"
|
|
130
|
+
});
|
|
131
|
+
migrationAction(
|
|
132
|
+
schema,
|
|
133
|
+
"simpleField",
|
|
134
|
+
"create",
|
|
135
|
+
{
|
|
136
|
+
displayName: "Identity",
|
|
137
|
+
apiId: "identity",
|
|
138
|
+
description: "Only used for internal reference",
|
|
139
|
+
type: SimpleFieldType.String,
|
|
140
|
+
isTitle: true,
|
|
141
|
+
isRequired: true,
|
|
142
|
+
isUnique: true,
|
|
143
|
+
modelApiId: "RowLinks"
|
|
144
|
+
},
|
|
145
|
+
"RowLinks",
|
|
146
|
+
"model"
|
|
147
|
+
);
|
|
148
|
+
migrationAction(
|
|
149
|
+
schema,
|
|
150
|
+
"enumerableField",
|
|
151
|
+
"create",
|
|
152
|
+
{
|
|
153
|
+
displayName: "Variant",
|
|
154
|
+
apiId: "linksVariant",
|
|
155
|
+
parentApiId: "RowLinks",
|
|
156
|
+
enumerationApiId: "RowLinksVariants",
|
|
157
|
+
description: "Different variants for Row Links"
|
|
158
|
+
},
|
|
159
|
+
"RowLinks",
|
|
160
|
+
"model"
|
|
161
|
+
);
|
|
162
|
+
migrationAction(
|
|
163
|
+
schema,
|
|
164
|
+
"simpleField",
|
|
165
|
+
"create",
|
|
166
|
+
{
|
|
167
|
+
displayName: "Title",
|
|
168
|
+
apiId: "title",
|
|
169
|
+
type: SimpleFieldType.String,
|
|
170
|
+
isRequired: true,
|
|
171
|
+
modelApiId: "RowLinks",
|
|
172
|
+
isLocalized: true
|
|
173
|
+
},
|
|
174
|
+
"RowLinks",
|
|
175
|
+
"model"
|
|
176
|
+
);
|
|
177
|
+
migrationAction(
|
|
178
|
+
schema,
|
|
179
|
+
"simpleField",
|
|
180
|
+
"create",
|
|
181
|
+
{
|
|
182
|
+
displayName: "Copy",
|
|
183
|
+
apiId: "rowLinksCopy",
|
|
184
|
+
type: SimpleFieldType.Richtext,
|
|
185
|
+
isLocalized: true,
|
|
186
|
+
modelApiId: "RowLinks"
|
|
187
|
+
},
|
|
188
|
+
"RowLinks",
|
|
189
|
+
"model"
|
|
190
|
+
);
|
|
191
|
+
migrationAction(
|
|
192
|
+
schema,
|
|
193
|
+
"relationalField",
|
|
194
|
+
"create",
|
|
195
|
+
{
|
|
196
|
+
displayName: "Links",
|
|
197
|
+
apiId: "pageLinks",
|
|
198
|
+
modelApiId: "RowLinks",
|
|
199
|
+
type: RelationalFieldType.Relation,
|
|
200
|
+
reverseField: {
|
|
201
|
+
apiId: "rowLinks",
|
|
202
|
+
modelApiId: "PageLink",
|
|
203
|
+
displayName: "RowLinks",
|
|
204
|
+
visibility: VisibilityTypes.Hidden,
|
|
205
|
+
isList: true
|
|
206
|
+
},
|
|
207
|
+
visibility: VisibilityTypes.ReadWrite,
|
|
208
|
+
isList: true
|
|
209
|
+
},
|
|
210
|
+
"RowLinks",
|
|
211
|
+
"model"
|
|
212
|
+
);
|
|
213
|
+
migrationAction(
|
|
214
|
+
schema,
|
|
215
|
+
"unionField",
|
|
216
|
+
"update",
|
|
217
|
+
{
|
|
218
|
+
apiId: "content",
|
|
219
|
+
displayName: "Content",
|
|
220
|
+
modelApiId: "Page",
|
|
221
|
+
reverseField: {
|
|
222
|
+
modelApiIds: [
|
|
223
|
+
"RowLinks",
|
|
224
|
+
"RowServiceOptions",
|
|
225
|
+
"RowSpecialBanner",
|
|
226
|
+
"RowQuote",
|
|
227
|
+
"RowProduct",
|
|
228
|
+
"RowColumnOne",
|
|
229
|
+
"RowColumnTwo",
|
|
230
|
+
"RowColumnThree",
|
|
231
|
+
"RowHeroBanner",
|
|
232
|
+
"RowBlogContent",
|
|
233
|
+
"RowButtonList",
|
|
234
|
+
"RowContentLinks",
|
|
235
|
+
"RowButtonLinkList"
|
|
236
|
+
]
|
|
237
|
+
// visibility: VisibilityTypes.Hidden, => Currently not supported for updateUnionField | https://github.com/hygraph/management-sdk/issues/34
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"Page",
|
|
241
|
+
"model"
|
|
242
|
+
);
|
|
243
|
+
return client.run(true);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const graphcommerce6to7 = async (schema, client) => {
|
|
247
|
+
const { migrationAction } = migrationActionFactory(schema, client);
|
|
248
|
+
migrationAction(schema, "enumeration", "create", {
|
|
249
|
+
displayName: "Row Column One Variants",
|
|
250
|
+
apiId: "RowColumnOneVariants",
|
|
251
|
+
values: [
|
|
252
|
+
{ displayName: "Default", apiId: "Default" },
|
|
253
|
+
{ displayName: "Message", apiId: "Message" }
|
|
254
|
+
]
|
|
255
|
+
});
|
|
256
|
+
migrationAction(schema, "enumeration", "create", {
|
|
257
|
+
displayName: "Dynamic Row Condition Number Operator",
|
|
258
|
+
apiId: "DynamicRowConditionNumberOperator",
|
|
259
|
+
values: [
|
|
260
|
+
{ displayName: "Greater than or equal to", apiId: "GTE" },
|
|
261
|
+
{ displayName: "Less than or equal to", apiId: "LTE" },
|
|
262
|
+
{ displayName: "Equal to", apiId: "EQUAL" }
|
|
263
|
+
]
|
|
264
|
+
});
|
|
265
|
+
migrationAction(schema, "enumeration", "create", {
|
|
266
|
+
displayName: "Dynamic Row Placement",
|
|
267
|
+
apiId: "DynamicRowPlacement",
|
|
268
|
+
values: [
|
|
269
|
+
{ displayName: "Before", apiId: "BEFORE" },
|
|
270
|
+
{ displayName: "After", apiId: "AFTER" },
|
|
271
|
+
{ displayName: "Replace", apiId: "REPLACE" }
|
|
272
|
+
]
|
|
273
|
+
});
|
|
274
|
+
migrationAction(schema, "component", "create", {
|
|
275
|
+
displayName: "Text",
|
|
276
|
+
apiId: "ConditionText",
|
|
277
|
+
apiIdPlural: "ConditionTexts"
|
|
278
|
+
});
|
|
279
|
+
migrationAction(schema, "component", "create", {
|
|
280
|
+
displayName: "Number",
|
|
281
|
+
apiId: "ConditionNumber",
|
|
282
|
+
apiIdPlural: "ConditionNumbers"
|
|
283
|
+
});
|
|
284
|
+
migrationAction(schema, "component", "create", {
|
|
285
|
+
displayName: "AND",
|
|
286
|
+
apiId: "ConditionAnd",
|
|
287
|
+
apiIdPlural: "ConditionAnds",
|
|
288
|
+
description: "All of these conditions must match"
|
|
289
|
+
});
|
|
290
|
+
migrationAction(schema, "component", "create", {
|
|
291
|
+
displayName: "OR",
|
|
292
|
+
apiId: "ConditionOr",
|
|
293
|
+
apiIdPlural: "ConditionOrs",
|
|
294
|
+
description: "One of these conditions must match"
|
|
295
|
+
});
|
|
296
|
+
migrationAction(
|
|
297
|
+
schema,
|
|
298
|
+
"componentUnionField",
|
|
299
|
+
"create",
|
|
300
|
+
{
|
|
301
|
+
displayName: "Conditions",
|
|
302
|
+
apiId: "conditions",
|
|
303
|
+
parentApiId: "ConditionAnd",
|
|
304
|
+
componentApiIds: ["ConditionOr", "ConditionText", "ConditionNumber"],
|
|
305
|
+
isList: true
|
|
306
|
+
},
|
|
307
|
+
"ConditionAnd",
|
|
308
|
+
"component"
|
|
309
|
+
);
|
|
310
|
+
migrationAction(
|
|
311
|
+
schema,
|
|
312
|
+
"simpleField",
|
|
313
|
+
"create",
|
|
314
|
+
{
|
|
315
|
+
displayName: "Property",
|
|
316
|
+
apiId: "property",
|
|
317
|
+
type: SimpleFieldType.String,
|
|
318
|
+
parentApiId: "ConditionText",
|
|
319
|
+
description: "Path to the value of the object being evaluated.\n\nFor products: url_key, category, sku",
|
|
320
|
+
isRequired: true,
|
|
321
|
+
validations: {
|
|
322
|
+
String: {
|
|
323
|
+
matches: {
|
|
324
|
+
flags: ["i", "s"],
|
|
325
|
+
regex: "^[a-z0-9-_.]+$",
|
|
326
|
+
errorMessage: "Only letters, numbers, dashes (-), underscores (_) or dots allowed (.)"
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
"ConditionText",
|
|
332
|
+
"component"
|
|
333
|
+
);
|
|
334
|
+
migrationAction(
|
|
335
|
+
schema,
|
|
336
|
+
"simpleField",
|
|
337
|
+
"create",
|
|
338
|
+
{
|
|
339
|
+
displayName: "Value",
|
|
340
|
+
apiId: "value",
|
|
341
|
+
type: SimpleFieldType.String,
|
|
342
|
+
parentApiId: "ConditionText",
|
|
343
|
+
isRequired: true
|
|
344
|
+
},
|
|
345
|
+
"ConditionText",
|
|
346
|
+
"component"
|
|
347
|
+
);
|
|
348
|
+
migrationAction(
|
|
349
|
+
schema,
|
|
350
|
+
"simpleField",
|
|
351
|
+
"create",
|
|
352
|
+
{
|
|
353
|
+
displayName: "Property",
|
|
354
|
+
apiId: "property",
|
|
355
|
+
type: SimpleFieldType.String,
|
|
356
|
+
parentApiId: "ConditionNumber",
|
|
357
|
+
isRequired: true,
|
|
358
|
+
validations: {
|
|
359
|
+
String: {
|
|
360
|
+
matches: {
|
|
361
|
+
flags: ["i", "s"],
|
|
362
|
+
regex: "^[a-z0-9-_.]+$",
|
|
363
|
+
errorMessage: "Only letters, numbers, dashes (-), underscores (_) or dots allowed (.)"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
"ConditionNumber",
|
|
369
|
+
"component"
|
|
370
|
+
);
|
|
371
|
+
migrationAction(
|
|
372
|
+
schema,
|
|
373
|
+
"enumerableField",
|
|
374
|
+
"create",
|
|
375
|
+
{
|
|
376
|
+
displayName: "Operator",
|
|
377
|
+
apiId: "operator",
|
|
378
|
+
parentApiId: "ConditionNumber",
|
|
379
|
+
enumerationApiId: "DynamicRowConditionNumberOperator",
|
|
380
|
+
isRequired: true
|
|
381
|
+
},
|
|
382
|
+
"ConditionNumber",
|
|
383
|
+
"component"
|
|
384
|
+
);
|
|
385
|
+
migrationAction(
|
|
386
|
+
schema,
|
|
387
|
+
"simpleField",
|
|
388
|
+
"create",
|
|
389
|
+
{
|
|
390
|
+
displayName: "Value",
|
|
391
|
+
apiId: "value",
|
|
392
|
+
type: SimpleFieldType.Float,
|
|
393
|
+
parentApiId: "ConditionNumber",
|
|
394
|
+
isRequired: true
|
|
395
|
+
},
|
|
396
|
+
"ConditionNumber",
|
|
397
|
+
"component"
|
|
398
|
+
);
|
|
399
|
+
migrationAction(schema, "model", "create", {
|
|
400
|
+
displayName: "Dynamic Row",
|
|
401
|
+
apiId: "DynamicRow",
|
|
402
|
+
apiIdPlural: "DynamicRows",
|
|
403
|
+
description: "Dynamic rows allow you to add specific Row models to pages based on the properties of the page"
|
|
404
|
+
});
|
|
405
|
+
migrationAction(
|
|
406
|
+
schema,
|
|
407
|
+
"simpleField",
|
|
408
|
+
"create",
|
|
409
|
+
{
|
|
410
|
+
displayName: "Internal name",
|
|
411
|
+
apiId: "internalName",
|
|
412
|
+
description: "Only used for internal reference",
|
|
413
|
+
type: SimpleFieldType.String,
|
|
414
|
+
isTitle: true,
|
|
415
|
+
isRequired: true,
|
|
416
|
+
isUnique: true,
|
|
417
|
+
modelApiId: "DynamicRow"
|
|
418
|
+
},
|
|
419
|
+
"DynamicRow",
|
|
420
|
+
"model"
|
|
421
|
+
);
|
|
422
|
+
migrationAction(
|
|
423
|
+
schema,
|
|
424
|
+
"unionField",
|
|
425
|
+
"create",
|
|
426
|
+
{
|
|
427
|
+
displayName: "Row",
|
|
428
|
+
apiId: "row",
|
|
429
|
+
reverseField: {
|
|
430
|
+
modelApiIds: ["RowQuote", "RowLinks", "RowColumnOne"],
|
|
431
|
+
apiId: "dynamicRow",
|
|
432
|
+
displayName: "DynamicRows",
|
|
433
|
+
visibility: VisibilityTypes.Hidden,
|
|
434
|
+
isList: true
|
|
435
|
+
},
|
|
436
|
+
parentApiId: "DynamicRow"
|
|
437
|
+
},
|
|
438
|
+
"DynamicRow",
|
|
439
|
+
"model"
|
|
440
|
+
);
|
|
441
|
+
migrationAction(
|
|
442
|
+
schema,
|
|
443
|
+
"enumerableField",
|
|
444
|
+
"create",
|
|
445
|
+
{
|
|
446
|
+
displayName: "Placement",
|
|
447
|
+
apiId: "placement",
|
|
448
|
+
parentApiId: "DynamicRow",
|
|
449
|
+
enumerationApiId: "DynamicRowPlacement",
|
|
450
|
+
description: "Where will the row be placed relative to the target",
|
|
451
|
+
isRequired: true
|
|
452
|
+
},
|
|
453
|
+
"DynamicRow",
|
|
454
|
+
"model"
|
|
455
|
+
);
|
|
456
|
+
migrationAction(
|
|
457
|
+
schema,
|
|
458
|
+
"unionField",
|
|
459
|
+
"create",
|
|
460
|
+
{
|
|
461
|
+
displayName: "Placement target",
|
|
462
|
+
apiId: "target",
|
|
463
|
+
description: "Optional: When the target is left blank it will place the Dynamic Row on the start or end.",
|
|
464
|
+
reverseField: {
|
|
465
|
+
modelApiIds: [
|
|
466
|
+
"RowQuote",
|
|
467
|
+
"RowLinks",
|
|
468
|
+
"RowColumnOne",
|
|
469
|
+
"RowColumnTwo",
|
|
470
|
+
"RowColumnThree",
|
|
471
|
+
"RowServiceOptions",
|
|
472
|
+
"RowContentLinks",
|
|
473
|
+
"RowButtonLinkList",
|
|
474
|
+
"RowProduct",
|
|
475
|
+
"RowSpecialBanner",
|
|
476
|
+
"RowHeroBanner",
|
|
477
|
+
"RowBlogContent"
|
|
478
|
+
],
|
|
479
|
+
apiId: "dynamicRowsTarget",
|
|
480
|
+
displayName: "DynamicRowsTarget",
|
|
481
|
+
visibility: VisibilityTypes.Hidden,
|
|
482
|
+
isList: true
|
|
483
|
+
},
|
|
484
|
+
parentApiId: "DynamicRow"
|
|
485
|
+
},
|
|
486
|
+
"DynamicRow",
|
|
487
|
+
"model"
|
|
488
|
+
);
|
|
489
|
+
migrationAction(
|
|
490
|
+
schema,
|
|
491
|
+
"componentUnionField",
|
|
492
|
+
"create",
|
|
493
|
+
{
|
|
494
|
+
displayName: "Conditions (OR)",
|
|
495
|
+
apiId: "conditions",
|
|
496
|
+
parentApiId: "DynamicRow",
|
|
497
|
+
description: "One of these conditions must match",
|
|
498
|
+
componentApiIds: ["ConditionAnd", "ConditionText", "ConditionNumber"],
|
|
499
|
+
isList: true
|
|
500
|
+
},
|
|
501
|
+
"DynamicRow",
|
|
502
|
+
"model"
|
|
503
|
+
);
|
|
504
|
+
migrationAction(
|
|
505
|
+
schema,
|
|
506
|
+
"enumerableField",
|
|
507
|
+
"create",
|
|
508
|
+
{
|
|
509
|
+
displayName: "Variant",
|
|
510
|
+
apiId: "rowColumnOneVariant",
|
|
511
|
+
enumerationApiId: "RowColumnOneVariants",
|
|
512
|
+
parentApiId: "RowColumnOne"
|
|
513
|
+
},
|
|
514
|
+
"RowColumnOne",
|
|
515
|
+
"model"
|
|
516
|
+
);
|
|
517
|
+
migrationAction(
|
|
518
|
+
schema,
|
|
519
|
+
"componentUnionField",
|
|
520
|
+
"create",
|
|
521
|
+
{
|
|
522
|
+
displayName: "Conditions",
|
|
523
|
+
apiId: "conditions",
|
|
524
|
+
parentApiId: "ConditionOr",
|
|
525
|
+
componentApiIds: ["ConditionText", "ConditionNumber"],
|
|
526
|
+
isList: true
|
|
527
|
+
},
|
|
528
|
+
"ConditionOr",
|
|
529
|
+
"component"
|
|
530
|
+
);
|
|
531
|
+
return client.run(true);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const graphcommerce7to8 = async (schema, client) => {
|
|
535
|
+
const { migrationAction } = migrationActionFactory(schema, client);
|
|
536
|
+
const hasRow = schema.models.find((m) => m.apiId === "DynamicRow")?.fields.some((f) => f.apiId === "row");
|
|
537
|
+
if (hasRow) {
|
|
538
|
+
migrationAction(schema, "unionField", "update", {
|
|
539
|
+
apiId: "row",
|
|
540
|
+
displayName: "Row Deprecated",
|
|
541
|
+
parentApiId: "DynamicRow",
|
|
542
|
+
description: "This field is deprecated. Use Rows instead."
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
migrationAction(
|
|
546
|
+
schema,
|
|
547
|
+
"unionField",
|
|
548
|
+
"create",
|
|
549
|
+
{
|
|
550
|
+
displayName: "Rows",
|
|
551
|
+
apiId: "rows",
|
|
552
|
+
isList: true,
|
|
553
|
+
reverseField: {
|
|
554
|
+
modelApiIds: ["RowQuote", "RowLinks", "RowColumnOne"],
|
|
555
|
+
apiId: "dynamicRows",
|
|
556
|
+
displayName: "Dynamic Rows",
|
|
557
|
+
visibility: VisibilityTypes.Hidden,
|
|
558
|
+
isList: true
|
|
559
|
+
},
|
|
560
|
+
parentApiId: "DynamicRow"
|
|
561
|
+
},
|
|
562
|
+
"DynamicRow",
|
|
563
|
+
"model"
|
|
564
|
+
);
|
|
565
|
+
migrationAction(
|
|
566
|
+
schema,
|
|
567
|
+
"componentUnionField",
|
|
568
|
+
"create",
|
|
569
|
+
{
|
|
570
|
+
displayName: "Conditions",
|
|
571
|
+
apiId: "conditions",
|
|
572
|
+
parentApiId: "ConditionOr",
|
|
573
|
+
componentApiIds: ["ConditionText", "ConditionNumber"],
|
|
574
|
+
isList: true
|
|
575
|
+
},
|
|
576
|
+
"ConditionOr",
|
|
577
|
+
"component"
|
|
578
|
+
);
|
|
579
|
+
return client.run(true);
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const graphcommerce8to9 = async (schema, client) => {
|
|
583
|
+
const { migrationAction } = migrationActionFactory(schema, client);
|
|
584
|
+
const hasRow = schema.models.find((m) => m.apiId === "DynamicRow")?.fields.some((f) => f.apiId === "row");
|
|
585
|
+
if (hasRow) {
|
|
586
|
+
migrationAction(schema, "simpleField", "delete", {
|
|
587
|
+
apiId: "row",
|
|
588
|
+
parentApiId: "DynamicRow"
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
const hasRowCategory = schema.models.some((m) => m.apiId === "RowCategory");
|
|
592
|
+
if (!hasRowCategory) {
|
|
593
|
+
migrationAction(schema, "model", "create", {
|
|
594
|
+
apiId: "RowCategory",
|
|
595
|
+
displayName: "Row Category",
|
|
596
|
+
apiIdPlural: "RowProductLists",
|
|
597
|
+
description: "A model that displays a category"
|
|
598
|
+
});
|
|
599
|
+
migrationAction(
|
|
600
|
+
schema,
|
|
601
|
+
"simpleField",
|
|
602
|
+
"create",
|
|
603
|
+
{
|
|
604
|
+
position: 1,
|
|
605
|
+
type: SimpleFieldType.String,
|
|
606
|
+
formConfig: { renderer: "GCMS_SLUG", config: { isLowercase: true } },
|
|
607
|
+
validations: {
|
|
608
|
+
String: {
|
|
609
|
+
matches: {
|
|
610
|
+
regex: "^[a-z0-9]+(?:[-/][a-z0-9]+)*$",
|
|
611
|
+
errorMessage: "The category URL must be a valid slug"
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
parentApiId: "RowCategory",
|
|
616
|
+
displayName: "Category URL",
|
|
617
|
+
apiId: "categoryUrl",
|
|
618
|
+
description: "The URL of the category, may include slashes",
|
|
619
|
+
isTitle: true,
|
|
620
|
+
isLocalized: true,
|
|
621
|
+
isRequired: true,
|
|
622
|
+
visibility: VisibilityTypes.ReadWrite
|
|
623
|
+
},
|
|
624
|
+
"RowCategory",
|
|
625
|
+
"model"
|
|
626
|
+
);
|
|
627
|
+
migrationAction(schema, "enumeration", "create", {
|
|
628
|
+
displayName: "Row Category Variant",
|
|
629
|
+
apiId: "RowCategoryVariant",
|
|
630
|
+
values: [
|
|
631
|
+
{ displayName: "Backstory", apiId: "Backstory" },
|
|
632
|
+
{ displayName: "Grid", apiId: "Grid" },
|
|
633
|
+
{ displayName: "Swipeable", apiId: "Swipeable" }
|
|
634
|
+
]
|
|
635
|
+
});
|
|
636
|
+
migrationAction(
|
|
637
|
+
schema,
|
|
638
|
+
"enumerableField",
|
|
639
|
+
"create",
|
|
640
|
+
{
|
|
641
|
+
displayName: "Variant",
|
|
642
|
+
apiId: "variant",
|
|
643
|
+
parentApiId: "RowCategory",
|
|
644
|
+
enumerationApiId: "RowCategoryVariant",
|
|
645
|
+
description: "As what variant wil the RowCategory be displayed",
|
|
646
|
+
isRequired: true
|
|
647
|
+
},
|
|
648
|
+
"RowCategory",
|
|
649
|
+
"model"
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
return client.run(true);
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
const availableMigrations = [
|
|
656
|
+
graphcommerce5to6,
|
|
657
|
+
graphcommerce6to7,
|
|
658
|
+
graphcommerce7to8,
|
|
659
|
+
graphcommerce8to9
|
|
660
|
+
];
|
|
661
|
+
|
|
662
|
+
const readSchema = async (managementClient, projectId) => {
|
|
663
|
+
const { data } = await managementClient.query({
|
|
664
|
+
query: gql`
|
|
665
|
+
query getSchema($projectId: ID!) {
|
|
666
|
+
viewer {
|
|
667
|
+
project(id: $projectId) {
|
|
668
|
+
environment(name: "master") {
|
|
669
|
+
contentModel {
|
|
670
|
+
locales {
|
|
671
|
+
id
|
|
672
|
+
apiId
|
|
673
|
+
}
|
|
674
|
+
stages {
|
|
675
|
+
id
|
|
676
|
+
apiId
|
|
677
|
+
}
|
|
678
|
+
models {
|
|
679
|
+
apiId
|
|
680
|
+
apiIdPlural
|
|
681
|
+
fields {
|
|
682
|
+
apiId
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
components {
|
|
686
|
+
apiId
|
|
687
|
+
apiIdPlural
|
|
688
|
+
fields {
|
|
689
|
+
apiId
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
enumerations {
|
|
693
|
+
apiId
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
`,
|
|
701
|
+
variables: {
|
|
702
|
+
projectId
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
return data;
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
class UpsertClient extends Client {
|
|
709
|
+
constructor(params, schema) {
|
|
710
|
+
super(params);
|
|
711
|
+
this.schema = schema;
|
|
712
|
+
}
|
|
713
|
+
/** @public */
|
|
714
|
+
upsertModel(data) {
|
|
715
|
+
const exists = this.schema.models.some((m) => m.apiId === data.apiId);
|
|
716
|
+
return exists ? this.createModel(data) : this.updateModel(data);
|
|
717
|
+
}
|
|
718
|
+
/** @public */
|
|
719
|
+
upsertComponent(data) {
|
|
720
|
+
const exists = this.schema.models.some((m) => m.apiId === data.apiId);
|
|
721
|
+
return exists ? this.createComponent(data) : this.updateComponent(data);
|
|
722
|
+
}
|
|
723
|
+
/** @public */
|
|
724
|
+
upsertSimpleField(data) {
|
|
725
|
+
const model = this.schema.models.find((m) => m.apiId === data.parentApiId);
|
|
726
|
+
const exists = model?.fields.some((f) => f.apiId === data.apiId);
|
|
727
|
+
return exists ? this.createSimpleField(data) : this.updateSimpleField({ ...data, embeddableModels: undefined });
|
|
728
|
+
}
|
|
729
|
+
// upsertRemoteField(data: BatchMigrationCreateRemoteFieldInput) {
|
|
730
|
+
// const model = this.schema.models.find((m) => m.apiId === data.parentApiId)
|
|
731
|
+
// const exists = model?.fields.some((f) => f.apiId === data.apiId)
|
|
732
|
+
// return exists ? this.createRemoteField(data) : this.updateRemoteField(data)
|
|
733
|
+
// }
|
|
734
|
+
/** @public */
|
|
735
|
+
upsertRelationalField(data) {
|
|
736
|
+
const model = this.schema.models.find((m) => m.apiId === data.parentApiId);
|
|
737
|
+
const exists = model?.fields.some((f) => f.apiId === data.apiId);
|
|
738
|
+
return exists ? this.createRelationalField(data) : this.updateRelationalField(data);
|
|
739
|
+
}
|
|
740
|
+
/** @public */
|
|
741
|
+
upsertUnionField(data) {
|
|
742
|
+
const model = this.schema.models.find((m) => m.apiId === data.parentApiId);
|
|
743
|
+
const exists = model?.fields.some((f) => f.apiId === data.apiId);
|
|
744
|
+
return exists ? this.createUnionField(data) : this.updateUnionField(data);
|
|
745
|
+
}
|
|
746
|
+
/** @public */
|
|
747
|
+
upsertComponentField(data) {
|
|
748
|
+
const model = this.schema.models.find((m) => m.apiId === data.parentApiId);
|
|
749
|
+
const exists = model?.fields.some((f) => f.apiId === data.apiId);
|
|
750
|
+
return exists ? this.createComponentField(data) : this.updateComponentField(data);
|
|
751
|
+
}
|
|
752
|
+
/** @public */
|
|
753
|
+
upsertComponentUnionField(data) {
|
|
754
|
+
const model = this.schema.models.find((m) => m.apiId === data.parentApiId);
|
|
755
|
+
const exists = model?.fields.some((f) => f.apiId === data.apiId);
|
|
756
|
+
return exists ? this.createComponentUnionField(data) : this.updateComponentUnionField(data);
|
|
757
|
+
}
|
|
758
|
+
/** @public */
|
|
759
|
+
upsertEnumeration(data) {
|
|
760
|
+
const exists = this.schema.enumerations.some((e) => e.apiId === data.apiId);
|
|
761
|
+
return exists ? this.createEnumeration(data) : this.updateEnumeration(data);
|
|
762
|
+
}
|
|
763
|
+
/** @public */
|
|
764
|
+
upsertEnumerableField(data) {
|
|
765
|
+
const model = this.schema.models.find((m) => m.apiId === data.parentApiId);
|
|
766
|
+
const exists = model?.fields.some((f) => f.apiId === data.apiId);
|
|
767
|
+
return exists ? this.createEnumerableField(data) : this.updateEnumerableField(data);
|
|
768
|
+
}
|
|
769
|
+
/** @public */
|
|
770
|
+
upsertStage(data) {
|
|
771
|
+
const exists = this.schema.stages.some((m) => m.apiId === data.apiId);
|
|
772
|
+
return exists ? this.createStage(data) : this.updateStage(data);
|
|
773
|
+
}
|
|
774
|
+
/** @public */
|
|
775
|
+
upsertLocale(data) {
|
|
776
|
+
const exists = this.schema.locales.some((m) => m.apiId === data.apiId);
|
|
777
|
+
return exists ? this.createLocale(data) : this.updateLocale(data);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function getConfig(config) {
|
|
782
|
+
let {
|
|
783
|
+
hygraphProjectId: projectId,
|
|
784
|
+
hygraphWriteAccessToken: authToken,
|
|
785
|
+
hygraphManagementApi: uri,
|
|
786
|
+
hygraphEndpoint
|
|
787
|
+
} = config;
|
|
788
|
+
if (!authToken) {
|
|
789
|
+
throw new Error("Please provide GC_HYGRAPH_WRITE_ACCESS_TOKEN in your env file.");
|
|
790
|
+
}
|
|
791
|
+
if (!projectId) {
|
|
792
|
+
projectId = new URL(hygraphEndpoint).pathname.split("/")?.[1];
|
|
793
|
+
}
|
|
794
|
+
if (!uri) {
|
|
795
|
+
const endpoint = new URL(hygraphEndpoint);
|
|
796
|
+
endpoint.hostname = `management-${endpoint.hostname}`.replace(".cdn", "");
|
|
797
|
+
endpoint.pathname = "graphql";
|
|
798
|
+
uri = endpoint.toString();
|
|
799
|
+
}
|
|
800
|
+
return { projectId, authToken, uri };
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
async function getEnvironment(client, config) {
|
|
804
|
+
const endpoints = await client.query({
|
|
805
|
+
query: gql$1`
|
|
806
|
+
query Environments($projectId: ID!) {
|
|
807
|
+
viewer {
|
|
808
|
+
id
|
|
809
|
+
project(id: $projectId) {
|
|
810
|
+
environments {
|
|
811
|
+
name
|
|
812
|
+
endpoint
|
|
813
|
+
migrations {
|
|
814
|
+
name
|
|
815
|
+
status
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
`,
|
|
822
|
+
variables: { projectId: config.projectId },
|
|
823
|
+
errorPolicy: "all"
|
|
824
|
+
});
|
|
825
|
+
if (endpoints.errors) {
|
|
826
|
+
const isBadInput = endpoints.errors.some((e) => e.extensions?.code === "BAD_USER_INPUT");
|
|
827
|
+
if (isBadInput) {
|
|
828
|
+
throw Error(`
|
|
829
|
+
Could not find environment for projectId ${config.projectId}.
|
|
830
|
+
Please check your GC_HYGRAPH_PROJECT_ID in your env file.
|
|
831
|
+
`);
|
|
832
|
+
}
|
|
833
|
+
throw new Error(`An error occurred: ${endpoints.errors.map((e) => e.message).join("\n")}`);
|
|
834
|
+
}
|
|
835
|
+
const environment = endpoints.data.viewer.project.environments.find((env) => env.name === "master") ?? endpoints.data.viewer.project.environments?.[0];
|
|
836
|
+
return environment;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function getManagementClient(config) {
|
|
840
|
+
const { authToken: accessToken, uri } = config;
|
|
841
|
+
return new ApolloClient({
|
|
842
|
+
link: new HttpLink({
|
|
843
|
+
uri,
|
|
844
|
+
fetch,
|
|
845
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
846
|
+
}),
|
|
847
|
+
cache: new InMemoryCache()
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
dotenv.config();
|
|
852
|
+
async function migrateHygraphCli() {
|
|
853
|
+
const hygraphConfig = getConfig(loadConfig(process.cwd()));
|
|
854
|
+
const packageJson = fs.readFileSync("package.json", "utf8");
|
|
855
|
+
const packageData = JSON.parse(packageJson);
|
|
856
|
+
const graphcommerceVersion = packageData.dependencies["@graphcommerce/next-ui"];
|
|
857
|
+
graphcommerceLog(`Graphcommerce version: ${graphcommerceVersion}`, "info");
|
|
858
|
+
const mangementClient = getManagementClient(hygraphConfig);
|
|
859
|
+
const schemaViewer = await readSchema(mangementClient, hygraphConfig.projectId);
|
|
860
|
+
const schema = schemaViewer.viewer.project.environment.contentModel;
|
|
861
|
+
const possibleMigrations = Object.entries(availableMigrations);
|
|
862
|
+
const selectMigrationInput = {
|
|
863
|
+
type: "select",
|
|
864
|
+
name: "selectedMigration",
|
|
865
|
+
message: "\x1B[36m\x1B[1m[]: Select migration",
|
|
866
|
+
choices: possibleMigrations.map(([name, migration]) => ({
|
|
867
|
+
title: name,
|
|
868
|
+
value: { name, migration }
|
|
869
|
+
}))
|
|
870
|
+
};
|
|
871
|
+
try {
|
|
872
|
+
graphcommerceLog("Available migrations: ", "info");
|
|
873
|
+
const selectMigrationOutput = await prompts(selectMigrationInput);
|
|
874
|
+
let { migration, name } = selectMigrationOutput.selectedMigration;
|
|
875
|
+
graphcommerceLog(
|
|
876
|
+
`You have selected the ${selectMigrationOutput.selectedMigration.name} migration`,
|
|
877
|
+
"info"
|
|
878
|
+
);
|
|
879
|
+
try {
|
|
880
|
+
const { endpoint, migrations } = await getEnvironment(mangementClient, hygraphConfig);
|
|
881
|
+
const migrationExists = migrations.find(
|
|
882
|
+
(m) => m.name?.startsWith(name) && m.status === "SUCCESS"
|
|
883
|
+
);
|
|
884
|
+
if (migrationExists) {
|
|
885
|
+
if (!process.argv.includes("--force")) {
|
|
886
|
+
graphcommerceLog(
|
|
887
|
+
`Migration ${name} as ${migrationExists.name} already exists in Hygraph with the status SUCCESS. To rerun this migration use the --force option. Exiting now..`,
|
|
888
|
+
"info"
|
|
889
|
+
);
|
|
890
|
+
process.exit(1);
|
|
891
|
+
} else {
|
|
892
|
+
graphcommerceLog(
|
|
893
|
+
`Migration ${name} as ${migrationExists.name} already exists in Hygraph with the status SUCCESS. Using --force, rerunning migration..`,
|
|
894
|
+
"warning"
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
name = `${name}-${Date.now()}`;
|
|
898
|
+
}
|
|
899
|
+
const result = await migration(
|
|
900
|
+
schema,
|
|
901
|
+
new UpsertClient({ authToken: hygraphConfig.authToken, endpoint, name }, schema)
|
|
902
|
+
);
|
|
903
|
+
graphcommerceLog(`Migration result: ${JSON.stringify(result)}`, "info");
|
|
904
|
+
if (!result) {
|
|
905
|
+
graphcommerceLog(
|
|
906
|
+
"No migration client found. Please make sure your GC_HYGRAPH_WRITE_ACCESS_TOKEN in your env file is correct."
|
|
907
|
+
);
|
|
908
|
+
process.exit(1);
|
|
909
|
+
}
|
|
910
|
+
if (result.status !== "SUCCESS") {
|
|
911
|
+
graphcommerceLog(`Migration not successful: ${result.status} ${name}:
|
|
912
|
+
${result.errors}`);
|
|
913
|
+
process.exit(1);
|
|
914
|
+
}
|
|
915
|
+
graphcommerceLog(`Migration successful: ${name}`, "info");
|
|
916
|
+
} catch (err) {
|
|
917
|
+
if (err instanceof Error) {
|
|
918
|
+
const garbledErrorIndex = err.message.indexOf(': {"');
|
|
919
|
+
const msg = garbledErrorIndex > 0 ? err.message.slice(0, garbledErrorIndex) : err.message;
|
|
920
|
+
graphcommerceLog(`${msg}`, "error");
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
} catch (error) {
|
|
924
|
+
graphcommerceLog(`An error occurred: ${error}`, "error");
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export { migrateHygraphCli as migrateHygraph };
|