@graphcommerce/hygraph-cli 8.1.0-canary.9 → 9.0.0-canary.101
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 +187 -6
- package/Config.graphqls +6 -10
- package/dist/UpsertClient.js +69 -0
- package/dist/index.js +2 -2
- package/dist/migrateHygraphCli.js +111 -0
- package/dist/migrationActionFactory.js +133 -0
- package/dist/migrations/graphcommerce5to6.js +12 -14
- package/dist/migrations/graphcommerce6to7.js +25 -27
- package/dist/migrations/graphcommerce7to8.js +7 -9
- package/dist/migrations/graphcommerce8to9.js +54 -8
- package/dist/readSchema.js +10 -21
- package/dist/utils/getConfig.js +19 -0
- package/dist/utils/getEndpointUrl.js +43 -0
- package/dist/utils/getManagementClient.js +15 -0
- package/dist/{log-functions.js → utils/graphCommerceLog.js} +1 -1
- package/package.json +8 -8
- package/src/UpsertClient.ts +99 -0
- package/src/index.ts +1 -1
- package/src/migrateHygraphCli.ts +113 -0
- package/src/migrationActionFactory.ts +230 -0
- package/src/migrations/graphcommerce5to6.ts +4 -6
- package/src/migrations/graphcommerce6to7.ts +4 -6
- package/src/migrations/graphcommerce7to8.ts +4 -6
- package/src/migrations/graphcommerce8to9.ts +79 -8
- package/src/readSchema.ts +24 -29
- package/src/types.ts +6 -1
- package/src/utils/getConfig.ts +33 -0
- package/src/utils/getEndpointUrl.ts +65 -0
- package/src/utils/getManagementClient.ts +15 -0
- package/src/{log-functions.ts → utils/graphCommerceLog.ts} +1 -1
- package/dist/client.js +0 -21
- package/dist/migrateHygraph.js +0 -96
- package/dist/migrationAction.js +0 -133
- package/src/client.ts +0 -25
- package/src/migrateHygraph.ts +0 -86
- package/src/migrationAction.ts +0 -223
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import { loadConfig } from '@graphcommerce/next-config'
|
|
3
|
+
import dotenv from 'dotenv'
|
|
4
|
+
import prompts, { PromptObject } from 'prompts'
|
|
5
|
+
import { UpsertClient } from './UpsertClient'
|
|
6
|
+
import * as availableMigrations from './migrations'
|
|
7
|
+
import { readSchema } from './readSchema'
|
|
8
|
+
import { MigrationFunction, Schema } from './types'
|
|
9
|
+
import { getConfig } from './utils/getConfig'
|
|
10
|
+
import { getEnvironment } from './utils/getEndpointUrl'
|
|
11
|
+
import { getManagementClient } from './utils/getManagementClient'
|
|
12
|
+
import { graphcommerceLog } from './utils/graphCommerceLog'
|
|
13
|
+
|
|
14
|
+
dotenv.config()
|
|
15
|
+
|
|
16
|
+
export async function migrateHygraphCli() {
|
|
17
|
+
const hygraphConfig = getConfig(loadConfig(process.cwd()))
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extracting the current GC version. Are we gonna use the current version to determine which
|
|
21
|
+
* scripts should be runned? Or do we let the user pick the migration from a list? 🤔
|
|
22
|
+
*/
|
|
23
|
+
const packageJson = fs.readFileSync('package.json', 'utf8')
|
|
24
|
+
const packageData = JSON.parse(packageJson)
|
|
25
|
+
const graphcommerceVersion = packageData.dependencies['@graphcommerce/next-ui']
|
|
26
|
+
graphcommerceLog(`Graphcommerce version: ${graphcommerceVersion}`, 'info')
|
|
27
|
+
|
|
28
|
+
const mangementClient = getManagementClient(hygraphConfig)
|
|
29
|
+
// Extract the currently existing models, components and enumerations from the Hygraph schema.
|
|
30
|
+
const schemaViewer = await readSchema(mangementClient, hygraphConfig.projectId)
|
|
31
|
+
const schema: Schema = schemaViewer.viewer.project.environment.contentModel
|
|
32
|
+
|
|
33
|
+
// A list of possible migrations
|
|
34
|
+
const possibleMigrations: [string, MigrationFunction][] = Object.entries(availableMigrations)
|
|
35
|
+
|
|
36
|
+
// Here we setup the list we ask the user to choose from
|
|
37
|
+
const selectMigrationInput: PromptObject<string> = {
|
|
38
|
+
type: 'select',
|
|
39
|
+
name: 'selectedMigration',
|
|
40
|
+
message: '\x1b[36m\x1b[1m[]: Select migration',
|
|
41
|
+
choices: possibleMigrations.map(([name, migration]) => ({
|
|
42
|
+
title: name,
|
|
43
|
+
value: { name, migration },
|
|
44
|
+
})),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Here we ask the user to choose a migration from a list of possible migrations
|
|
48
|
+
try {
|
|
49
|
+
graphcommerceLog('Available migrations: ', 'info')
|
|
50
|
+
const selectMigrationOutput = await prompts(selectMigrationInput)
|
|
51
|
+
|
|
52
|
+
let { migration, name } = selectMigrationOutput.selectedMigration as {
|
|
53
|
+
name: string
|
|
54
|
+
migration: MigrationFunction
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
graphcommerceLog(
|
|
58
|
+
`You have selected the ${selectMigrationOutput.selectedMigration.name} migration`,
|
|
59
|
+
'info',
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const { endpoint, migrations } = await getEnvironment(mangementClient, hygraphConfig)
|
|
64
|
+
|
|
65
|
+
const migrationExists = migrations.find(
|
|
66
|
+
(m) => m.name?.startsWith(name) && m.status === 'SUCCESS',
|
|
67
|
+
)
|
|
68
|
+
if (migrationExists) {
|
|
69
|
+
if (!process.argv.includes('--force')) {
|
|
70
|
+
graphcommerceLog(
|
|
71
|
+
`Migration ${name} as ${migrationExists.name} already exists in Hygraph with the status SUCCESS. To rerun this migration use the --force option. Exiting now..`,
|
|
72
|
+
'info',
|
|
73
|
+
)
|
|
74
|
+
process.exit(1)
|
|
75
|
+
} else {
|
|
76
|
+
graphcommerceLog(
|
|
77
|
+
`Migration ${name} as ${migrationExists.name} already exists in Hygraph with the status SUCCESS. Using --force, rerunning migration..`,
|
|
78
|
+
'warning',
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
name = `${name}-${Date.now()}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Here we try to run the migration
|
|
85
|
+
const result = await migration(
|
|
86
|
+
schema,
|
|
87
|
+
new UpsertClient({ authToken: hygraphConfig.authToken, endpoint, name }, schema),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
graphcommerceLog(`Migration result: ${JSON.stringify(result)}`, 'info')
|
|
91
|
+
if (!result) {
|
|
92
|
+
graphcommerceLog(
|
|
93
|
+
'No migration client found. Please make sure your GC_HYGRAPH_WRITE_ACCESS_TOKEN in your env file is correct.',
|
|
94
|
+
)
|
|
95
|
+
process.exit(1)
|
|
96
|
+
}
|
|
97
|
+
if (result.status !== 'SUCCESS') {
|
|
98
|
+
graphcommerceLog(`Migration not successful: ${result.status} ${name}:\n${result.errors}`)
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
graphcommerceLog(`Migration successful: ${name}`, 'info')
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof Error) {
|
|
105
|
+
const garbledErrorIndex = err.message.indexOf(': {"')
|
|
106
|
+
const msg = garbledErrorIndex > 0 ? err.message.slice(0, garbledErrorIndex) : err.message
|
|
107
|
+
graphcommerceLog(`${msg}`, 'error')
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
graphcommerceLog(`An error occurred: ${error}`, 'error')
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { loadConfig } from '@graphcommerce/next-config'
|
|
2
|
+
import {
|
|
3
|
+
BatchMigrationCreateModelInput,
|
|
4
|
+
BatchMigrationCreateComponentInput,
|
|
5
|
+
BatchMigrationCreateEnumerationInput,
|
|
6
|
+
BatchMigrationCreateSimpleFieldInput,
|
|
7
|
+
BatchMigrationCreateEnumerableFieldInput,
|
|
8
|
+
BatchMigrationCreateRelationalFieldInput,
|
|
9
|
+
BatchMigrationCreateUnionFieldInput,
|
|
10
|
+
BatchMigrationCreateComponentFieldInput,
|
|
11
|
+
BatchMigrationDeleteComponentInput,
|
|
12
|
+
BatchMigrationDeleteEnumerationInput,
|
|
13
|
+
BatchMigrationDeleteModelInput,
|
|
14
|
+
BatchMigrationUpdateComponentFieldInput,
|
|
15
|
+
BatchMigrationUpdateComponentInput,
|
|
16
|
+
BatchMigrationUpdateEnumerableFieldInput,
|
|
17
|
+
BatchMigrationUpdateEnumerationInput,
|
|
18
|
+
BatchMigrationUpdateModelInput,
|
|
19
|
+
BatchMigrationUpdateRelationalFieldInput,
|
|
20
|
+
BatchMigrationUpdateSimpleFieldInput,
|
|
21
|
+
BatchMigrationUpdateUnionFieldInput,
|
|
22
|
+
BatchMigrationDeleteFieldInput,
|
|
23
|
+
BatchMigrationCreateComponentUnionFieldInput,
|
|
24
|
+
BatchMigrationUpdateComponentUnionFieldInput,
|
|
25
|
+
Client,
|
|
26
|
+
} from '@hygraph/management-sdk'
|
|
27
|
+
import dotenv from 'dotenv'
|
|
28
|
+
import { Schema } from './types'
|
|
29
|
+
import { graphcommerceLog, capitalize } from './utils/graphCommerceLog'
|
|
30
|
+
|
|
31
|
+
dotenv.config()
|
|
32
|
+
|
|
33
|
+
type AllHygraphTypes =
|
|
34
|
+
| 'model'
|
|
35
|
+
| 'component'
|
|
36
|
+
| 'enumeration'
|
|
37
|
+
| 'simpleField'
|
|
38
|
+
| 'componentField'
|
|
39
|
+
| 'enumerableField'
|
|
40
|
+
| 'relationalField'
|
|
41
|
+
| 'unionField'
|
|
42
|
+
| 'componentUnionField'
|
|
43
|
+
|
|
44
|
+
type AllActions =
|
|
45
|
+
| BatchMigrationCreateModelInput
|
|
46
|
+
| BatchMigrationUpdateModelInput
|
|
47
|
+
| BatchMigrationDeleteModelInput
|
|
48
|
+
| BatchMigrationCreateComponentInput
|
|
49
|
+
| BatchMigrationUpdateComponentInput
|
|
50
|
+
| BatchMigrationDeleteComponentInput
|
|
51
|
+
| BatchMigrationCreateEnumerationInput
|
|
52
|
+
| BatchMigrationUpdateEnumerationInput
|
|
53
|
+
| BatchMigrationDeleteEnumerationInput
|
|
54
|
+
| BatchMigrationCreateSimpleFieldInput // type: SimpleFieldType
|
|
55
|
+
| BatchMigrationUpdateSimpleFieldInput
|
|
56
|
+
| BatchMigrationCreateEnumerableFieldInput
|
|
57
|
+
| BatchMigrationUpdateEnumerableFieldInput
|
|
58
|
+
| BatchMigrationCreateRelationalFieldInput // type: RelationalFieldType
|
|
59
|
+
| BatchMigrationUpdateRelationalFieldInput
|
|
60
|
+
| BatchMigrationCreateUnionFieldInput
|
|
61
|
+
| BatchMigrationUpdateUnionFieldInput
|
|
62
|
+
| BatchMigrationCreateComponentFieldInput
|
|
63
|
+
| BatchMigrationUpdateComponentFieldInput
|
|
64
|
+
| BatchMigrationDeleteFieldInput
|
|
65
|
+
| BatchMigrationCreateComponentUnionFieldInput
|
|
66
|
+
| BatchMigrationUpdateComponentUnionFieldInput
|
|
67
|
+
|
|
68
|
+
export function migrationActionFactory(schema: Schema, client: Client) {
|
|
69
|
+
/**
|
|
70
|
+
* This constant is used to assign the right action of the management SDK to the migratioAction
|
|
71
|
+
* function
|
|
72
|
+
*/
|
|
73
|
+
const actionMap = client
|
|
74
|
+
? {
|
|
75
|
+
create: {
|
|
76
|
+
model: (innerprops: BatchMigrationCreateModelInput) => client.createModel(innerprops),
|
|
77
|
+
component: (innerprops: BatchMigrationCreateComponentInput) =>
|
|
78
|
+
client.createComponent(innerprops),
|
|
79
|
+
enumeration: (innerprops: BatchMigrationCreateEnumerationInput) =>
|
|
80
|
+
client.createEnumeration(innerprops),
|
|
81
|
+
simpleField: (innerprops: BatchMigrationCreateSimpleFieldInput) =>
|
|
82
|
+
client.createSimpleField(innerprops),
|
|
83
|
+
enumerableField: (innerprops: BatchMigrationCreateEnumerableFieldInput) =>
|
|
84
|
+
client.createEnumerableField(innerprops),
|
|
85
|
+
componentField: (innerprops: BatchMigrationCreateComponentFieldInput) =>
|
|
86
|
+
client.createComponentField(innerprops),
|
|
87
|
+
relationalField: (innerprops: BatchMigrationCreateRelationalFieldInput) =>
|
|
88
|
+
client.createRelationalField(innerprops),
|
|
89
|
+
unionField: (innerprops: BatchMigrationCreateUnionFieldInput) =>
|
|
90
|
+
client.createUnionField(innerprops),
|
|
91
|
+
componentUnionField: (innerprops: BatchMigrationCreateComponentUnionFieldInput) =>
|
|
92
|
+
client.createComponentUnionField(innerprops),
|
|
93
|
+
},
|
|
94
|
+
update: {
|
|
95
|
+
model: (innerprops: BatchMigrationUpdateModelInput) => client.updateModel(innerprops),
|
|
96
|
+
component: (innerprops: BatchMigrationUpdateComponentInput) =>
|
|
97
|
+
client.updateComponent(innerprops),
|
|
98
|
+
enumeration: (innerprops: BatchMigrationUpdateEnumerationInput) =>
|
|
99
|
+
client.updateEnumeration(innerprops),
|
|
100
|
+
simpleField: (innerprops: BatchMigrationUpdateSimpleFieldInput) =>
|
|
101
|
+
client.updateSimpleField(innerprops),
|
|
102
|
+
enumerableField: (innerprops: BatchMigrationUpdateEnumerableFieldInput) =>
|
|
103
|
+
client.updateEnumerableField(innerprops),
|
|
104
|
+
componentField: (innerprops: BatchMigrationUpdateComponentFieldInput) =>
|
|
105
|
+
client.updateComponentField(innerprops),
|
|
106
|
+
relationalField: (innerprops: BatchMigrationUpdateRelationalFieldInput) =>
|
|
107
|
+
client.updateRelationalField(innerprops),
|
|
108
|
+
unionField: (innerprops: BatchMigrationUpdateUnionFieldInput) =>
|
|
109
|
+
client.updateUnionField(innerprops),
|
|
110
|
+
componentUnionField: (innerprops: BatchMigrationUpdateComponentUnionFieldInput) =>
|
|
111
|
+
client.updateComponentUnionField(innerprops),
|
|
112
|
+
},
|
|
113
|
+
delete: {
|
|
114
|
+
model: (innerprops: BatchMigrationDeleteModelInput) => client.deleteModel(innerprops),
|
|
115
|
+
component: (innerprops: BatchMigrationDeleteComponentInput) =>
|
|
116
|
+
client.deleteComponent(innerprops),
|
|
117
|
+
enumeration: (innerprops: BatchMigrationDeleteEnumerationInput) =>
|
|
118
|
+
client.deleteEnumeration(innerprops),
|
|
119
|
+
simpleField: (innerprops: BatchMigrationDeleteFieldInput) =>
|
|
120
|
+
client.deleteField(innerprops),
|
|
121
|
+
enumerableField: (innerprops: BatchMigrationDeleteFieldInput) =>
|
|
122
|
+
client.deleteField(innerprops),
|
|
123
|
+
componentField: (innerprops: BatchMigrationDeleteFieldInput) =>
|
|
124
|
+
client.deleteField(innerprops),
|
|
125
|
+
relationalField: (innerprops: BatchMigrationDeleteFieldInput) =>
|
|
126
|
+
client.deleteField(innerprops),
|
|
127
|
+
unionField: (innerprops: BatchMigrationDeleteFieldInput) =>
|
|
128
|
+
client.deleteField(innerprops),
|
|
129
|
+
componentUnionField: (innerprops: BatchMigrationDeleteFieldInput) =>
|
|
130
|
+
client.deleteField(innerprops),
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
: undefined
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* This function is our variation on the client.migrationAction functions from the hygraph
|
|
137
|
+
* management sdk.
|
|
138
|
+
*
|
|
139
|
+
* MigrationAction() is better suited because it is a single function for all actions. More
|
|
140
|
+
* importantly, if the action fails, because a field with the apiID already exists for instance. it
|
|
141
|
+
* will skip this action but still continue the whole migration, while the management sdk function
|
|
142
|
+
* will bail.
|
|
143
|
+
*
|
|
144
|
+
* It takes the schema as argument, which is always the same.
|
|
145
|
+
*
|
|
146
|
+
* Then it takes the type of schema entity you want to do an action upon, and the action you want to
|
|
147
|
+
* do.
|
|
148
|
+
*
|
|
149
|
+
* The fourth arguments are the props that belong to the action you want to do. For instance, if you
|
|
150
|
+
* want to create a model, you need to pass the props that belong to a model.
|
|
151
|
+
*
|
|
152
|
+
* The last two arguments are optional. If you want to create a field, you need to pass the apiId of
|
|
153
|
+
* the model or component you want to create the field on. If you want to create a field on a
|
|
154
|
+
* component, you also need to pass the parentType, which is either 'model' or 'component'.
|
|
155
|
+
*/
|
|
156
|
+
const migrationAction = (
|
|
157
|
+
_schema: unknown,
|
|
158
|
+
type: AllHygraphTypes,
|
|
159
|
+
action: 'create' | 'update' | 'delete',
|
|
160
|
+
props: AllActions,
|
|
161
|
+
parentApiId?: string,
|
|
162
|
+
parentType?: 'model' | 'component' | 'enumeration',
|
|
163
|
+
) => {
|
|
164
|
+
/**
|
|
165
|
+
* Check if the entity already exists.
|
|
166
|
+
* If an update or deletion is made, it does not matter if the entity already exists
|
|
167
|
+
*/
|
|
168
|
+
const alreadyExists = () => {
|
|
169
|
+
if (action !== 'create') {
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
switch (type) {
|
|
173
|
+
case 'model':
|
|
174
|
+
return schema.models.some((model) => model.apiId === props.apiId)
|
|
175
|
+
|
|
176
|
+
case 'component':
|
|
177
|
+
return schema.components.some((component) => component.apiId === props.apiId)
|
|
178
|
+
|
|
179
|
+
case 'enumeration':
|
|
180
|
+
return schema.enumerations.some((enumeration) => enumeration.apiId === props.apiId)
|
|
181
|
+
|
|
182
|
+
case 'simpleField':
|
|
183
|
+
case 'enumerableField':
|
|
184
|
+
case 'relationalField':
|
|
185
|
+
case 'unionField':
|
|
186
|
+
case 'componentUnionField': {
|
|
187
|
+
let parent
|
|
188
|
+
switch (parentType) {
|
|
189
|
+
case 'model': {
|
|
190
|
+
parent = schema.models.find((model) => model.apiId === parentApiId)
|
|
191
|
+
break
|
|
192
|
+
}
|
|
193
|
+
case 'component': {
|
|
194
|
+
parent = schema.components.find((component) => component.apiId === parentApiId)
|
|
195
|
+
break
|
|
196
|
+
}
|
|
197
|
+
default:
|
|
198
|
+
return false
|
|
199
|
+
}
|
|
200
|
+
return parent?.fields.some((field) => field.apiId === props.apiId)
|
|
201
|
+
}
|
|
202
|
+
default: {
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const actionFunc = actionMap && actionMap[action] && actionMap[action][type]
|
|
209
|
+
|
|
210
|
+
if (!alreadyExists()) {
|
|
211
|
+
if (actionFunc) {
|
|
212
|
+
graphcommerceLog(`${capitalize(action)} ${type} with apiId ${props.apiId}...`)
|
|
213
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
214
|
+
// @ts-ignore | This error is a loss on typescript autocomplete, but the function is called correctly
|
|
215
|
+
actionFunc(props)
|
|
216
|
+
} else {
|
|
217
|
+
graphcommerceLog(`Action ${action} is not supported for ${type}`, 'error')
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
graphcommerceLog(
|
|
221
|
+
`${capitalize(type)} with apiId ${props.apiId} on ${parentApiId} already exists`,
|
|
222
|
+
'warning',
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
migrationAction,
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { RelationalFieldType, SimpleFieldType, VisibilityTypes } from '@hygraph/management-sdk'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { migrationActionFactory } from '../migrationActionFactory'
|
|
3
|
+
import { MigrationFunction } from '../types'
|
|
4
4
|
|
|
5
|
-
export const graphcommerce5to6 = async (schema
|
|
6
|
-
|
|
7
|
-
return 0
|
|
8
|
-
}
|
|
5
|
+
export const graphcommerce5to6: MigrationFunction = async (schema, client) => {
|
|
6
|
+
const { migrationAction } = migrationActionFactory(schema, client)
|
|
9
7
|
|
|
10
8
|
// ? ENUMERATIONS
|
|
11
9
|
migrationAction(schema, 'enumeration', 'create', {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { SimpleFieldType, VisibilityTypes } from '@hygraph/management-sdk'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { migrationActionFactory } from '../migrationActionFactory'
|
|
3
|
+
import { MigrationFunction } from '../types'
|
|
4
4
|
|
|
5
|
-
export const graphcommerce6to7 = async (schema
|
|
6
|
-
|
|
7
|
-
return 0
|
|
8
|
-
}
|
|
5
|
+
export const graphcommerce6to7: MigrationFunction = async (schema, client) => {
|
|
6
|
+
const { migrationAction } = migrationActionFactory(schema, client)
|
|
9
7
|
|
|
10
8
|
// ? ENUMERATIONS
|
|
11
9
|
migrationAction(schema, 'enumeration', 'create', {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { VisibilityTypes } from '@hygraph/management-sdk'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { migrationActionFactory } from '../migrationActionFactory'
|
|
3
|
+
import { MigrationFunction } from '../types'
|
|
4
4
|
|
|
5
|
-
export const graphcommerce7to8 = async (schema
|
|
6
|
-
|
|
7
|
-
return 0
|
|
8
|
-
}
|
|
5
|
+
export const graphcommerce7to8: MigrationFunction = async (schema, client) => {
|
|
6
|
+
const { migrationAction } = migrationActionFactory(schema, client)
|
|
9
7
|
|
|
10
8
|
const hasRow = schema.models
|
|
11
9
|
.find((m) => m.apiId === 'DynamicRow')
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
BatchMigrationCreateEnumerableFieldInput,
|
|
3
|
+
BatchMigrationCreateEnumerationInput,
|
|
4
|
+
BatchMigrationCreateModelInput,
|
|
5
|
+
BatchMigrationCreateSimpleFieldInput,
|
|
6
|
+
SimpleFieldType,
|
|
7
|
+
VisibilityTypes,
|
|
8
|
+
} from '@hygraph/management-sdk'
|
|
9
|
+
import { migrationActionFactory } from '../migrationActionFactory'
|
|
10
|
+
import { MigrationFunction } from '../types'
|
|
3
11
|
|
|
4
|
-
export const graphcommerce8to9 = async (schema
|
|
5
|
-
|
|
6
|
-
return 0
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// This migration is for GC 8.0 and is not yet exported as a usable migration.
|
|
12
|
+
export const graphcommerce8to9: MigrationFunction = async (schema, client) => {
|
|
13
|
+
const { migrationAction } = migrationActionFactory(schema, client)
|
|
10
14
|
|
|
11
15
|
// Removes the deprecated 'Row' field which was deprecated in GC@7.1
|
|
12
16
|
const hasRow = schema.models
|
|
@@ -20,5 +24,72 @@ export const graphcommerce8to9 = async (schema: Schema) => {
|
|
|
20
24
|
})
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
const hasRowCategory = schema.models.some((m) => m.apiId === 'RowCategory')
|
|
28
|
+
|
|
29
|
+
//
|
|
30
|
+
if (!hasRowCategory) {
|
|
31
|
+
migrationAction(schema, 'model', 'create', {
|
|
32
|
+
apiId: 'RowCategory',
|
|
33
|
+
displayName: 'Row Category',
|
|
34
|
+
apiIdPlural: 'RowProductLists',
|
|
35
|
+
description: 'A model that displays a category',
|
|
36
|
+
} satisfies BatchMigrationCreateModelInput)
|
|
37
|
+
|
|
38
|
+
migrationAction(
|
|
39
|
+
schema,
|
|
40
|
+
'simpleField',
|
|
41
|
+
'create',
|
|
42
|
+
{
|
|
43
|
+
position: 1,
|
|
44
|
+
type: SimpleFieldType.String,
|
|
45
|
+
formConfig: { renderer: 'GCMS_SLUG', config: { isLowercase: true } },
|
|
46
|
+
validations: {
|
|
47
|
+
String: {
|
|
48
|
+
matches: {
|
|
49
|
+
regex: '^[a-z0-9]+(?:[-/][a-z0-9]+)*$',
|
|
50
|
+
errorMessage: 'The category URL must be a valid slug',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
parentApiId: 'RowCategory',
|
|
55
|
+
displayName: 'Category URL',
|
|
56
|
+
apiId: 'categoryUrl',
|
|
57
|
+
description: 'The URL of the category, may include slashes',
|
|
58
|
+
isTitle: true,
|
|
59
|
+
isLocalized: true,
|
|
60
|
+
isRequired: true,
|
|
61
|
+
visibility: VisibilityTypes.ReadWrite,
|
|
62
|
+
} satisfies BatchMigrationCreateSimpleFieldInput,
|
|
63
|
+
'RowCategory',
|
|
64
|
+
'model',
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
migrationAction(schema, 'enumeration', 'create', {
|
|
68
|
+
displayName: 'Row Category Variant',
|
|
69
|
+
apiId: 'RowCategoryVariant',
|
|
70
|
+
values: [
|
|
71
|
+
{ displayName: 'Backstory', apiId: 'Backstory' },
|
|
72
|
+
{ displayName: 'Grid', apiId: 'Grid' },
|
|
73
|
+
{ displayName: 'Swipeable', apiId: 'Swipeable' },
|
|
74
|
+
],
|
|
75
|
+
} satisfies BatchMigrationCreateEnumerationInput)
|
|
76
|
+
|
|
77
|
+
migrationAction(
|
|
78
|
+
schema,
|
|
79
|
+
'enumerableField',
|
|
80
|
+
'create',
|
|
81
|
+
{
|
|
82
|
+
displayName: 'Variant',
|
|
83
|
+
apiId: 'variant',
|
|
84
|
+
parentApiId: 'RowCategory',
|
|
85
|
+
enumerationApiId: 'RowCategoryVariant',
|
|
86
|
+
description: 'As what variant wil the RowCategory be displayed',
|
|
87
|
+
isRequired: true,
|
|
88
|
+
} satisfies BatchMigrationCreateEnumerableFieldInput,
|
|
89
|
+
'RowCategory',
|
|
90
|
+
'model',
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
23
94
|
return client.run(true)
|
|
24
95
|
}
|
package/src/readSchema.ts
CHANGED
|
@@ -1,38 +1,25 @@
|
|
|
1
|
-
import { ApolloClient,
|
|
2
|
-
import {
|
|
3
|
-
import { fetch } from '@whatwg-node/fetch'
|
|
1
|
+
import { ApolloClient, gql, NormalizedCacheObject } from '@apollo/client'
|
|
2
|
+
import { Schema } from './types'
|
|
4
3
|
|
|
5
|
-
export const readSchema = async (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (!config.hygraphWriteAccessToken) {
|
|
11
|
-
throw new Error('Please provide GC_HYGRAPH_WRITE_ACCESS_TOKEN in your env file.')
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const projectId = config.hygraphProjectId
|
|
15
|
-
|
|
16
|
-
if (!config.hygraphManagementApi) {
|
|
17
|
-
throw new Error('Please provide GC_HYGRAPH_MANAGEMENT_API in your env file.')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const hygraphClient = new ApolloClient({
|
|
21
|
-
link: new HttpLink({
|
|
22
|
-
uri: config.hygraphManagementApi,
|
|
23
|
-
fetch,
|
|
24
|
-
headers: { Authorization: `Bearer ${config.hygraphWriteAccessToken}` },
|
|
25
|
-
}),
|
|
26
|
-
cache: new InMemoryCache(),
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
const { data } = await hygraphClient.query({
|
|
4
|
+
export const readSchema = async (
|
|
5
|
+
managementClient: ApolloClient<NormalizedCacheObject>,
|
|
6
|
+
projectId: string,
|
|
7
|
+
) => {
|
|
8
|
+
const { data } = await managementClient.query({
|
|
30
9
|
query: gql`
|
|
31
10
|
query getSchema($projectId: ID!) {
|
|
32
11
|
viewer {
|
|
33
12
|
project(id: $projectId) {
|
|
34
13
|
environment(name: "master") {
|
|
35
14
|
contentModel {
|
|
15
|
+
locales {
|
|
16
|
+
id
|
|
17
|
+
apiId
|
|
18
|
+
}
|
|
19
|
+
stages {
|
|
20
|
+
id
|
|
21
|
+
apiId
|
|
22
|
+
}
|
|
36
23
|
models {
|
|
37
24
|
apiId
|
|
38
25
|
apiIdPlural
|
|
@@ -61,5 +48,13 @@ export const readSchema = async (config: GraphCommerceConfig) => {
|
|
|
61
48
|
},
|
|
62
49
|
})
|
|
63
50
|
|
|
64
|
-
return data
|
|
51
|
+
return data as {
|
|
52
|
+
viewer: {
|
|
53
|
+
project: {
|
|
54
|
+
environment: {
|
|
55
|
+
contentModel: Schema
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
65
60
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
import type { Model, Component, Enumeration } from '@hygraph/management-sdk'
|
|
2
|
+
import type { Model, Component, Enumeration, Client, Locale, Stage } from '@hygraph/management-sdk'
|
|
3
3
|
import type { MigrationInfo } from '@hygraph/management-sdk/dist/src/ManagementAPIClient'
|
|
4
|
+
import type { UpsertClient } from './UpsertClient'
|
|
4
5
|
|
|
5
6
|
export type Migration = (name: string | undefined) => Promise<MigrationInfo>
|
|
6
7
|
|
|
8
|
+
export type MigrationFunction = (schema: Schema, client: UpsertClient) => Promise<0 | MigrationInfo>
|
|
9
|
+
|
|
7
10
|
export type Schema = {
|
|
8
11
|
models: Model[]
|
|
9
12
|
components: Component[]
|
|
10
13
|
enumerations: Enumeration[]
|
|
14
|
+
locales: Locale[]
|
|
15
|
+
stages: Stage[]
|
|
11
16
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GraphCommerceConfig } from '@graphcommerce/next-config'
|
|
2
|
+
|
|
3
|
+
export type HygraphConfig = {
|
|
4
|
+
projectId: string
|
|
5
|
+
authToken: string
|
|
6
|
+
uri: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getConfig(config: GraphCommerceConfig) {
|
|
10
|
+
let {
|
|
11
|
+
hygraphProjectId: projectId,
|
|
12
|
+
hygraphWriteAccessToken: authToken,
|
|
13
|
+
hygraphManagementApi: uri,
|
|
14
|
+
hygraphEndpoint,
|
|
15
|
+
} = config
|
|
16
|
+
|
|
17
|
+
if (!authToken) {
|
|
18
|
+
throw new Error('Please provide GC_HYGRAPH_WRITE_ACCESS_TOKEN in your env file.')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!projectId) {
|
|
22
|
+
projectId = new URL(hygraphEndpoint).pathname.split('/')?.[1]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!uri) {
|
|
26
|
+
const endpoint = new URL(hygraphEndpoint)
|
|
27
|
+
endpoint.hostname = `management-${endpoint.hostname}`.replace('.cdn', '')
|
|
28
|
+
endpoint.pathname = 'graphql'
|
|
29
|
+
uri = endpoint.toString()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { projectId, authToken, uri }
|
|
33
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ApolloClient, NormalizedCacheObject } from '@apollo/client'
|
|
2
|
+
import gql from 'graphql-tag'
|
|
3
|
+
import { HygraphConfig } from './getConfig'
|
|
4
|
+
|
|
5
|
+
export async function getEnvironment(
|
|
6
|
+
client: ApolloClient<NormalizedCacheObject>,
|
|
7
|
+
config: HygraphConfig,
|
|
8
|
+
) {
|
|
9
|
+
const endpoints = await client.query<
|
|
10
|
+
{
|
|
11
|
+
viewer: {
|
|
12
|
+
id: string
|
|
13
|
+
project: {
|
|
14
|
+
environments: {
|
|
15
|
+
name: string
|
|
16
|
+
endpoint: string
|
|
17
|
+
migrations: {
|
|
18
|
+
name: string
|
|
19
|
+
status: string
|
|
20
|
+
}[]
|
|
21
|
+
}[]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{ projectId: string }
|
|
26
|
+
>({
|
|
27
|
+
query: gql`
|
|
28
|
+
query Environments($projectId: ID!) {
|
|
29
|
+
viewer {
|
|
30
|
+
id
|
|
31
|
+
project(id: $projectId) {
|
|
32
|
+
environments {
|
|
33
|
+
name
|
|
34
|
+
endpoint
|
|
35
|
+
migrations {
|
|
36
|
+
name
|
|
37
|
+
status
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`,
|
|
44
|
+
variables: { projectId: config.projectId },
|
|
45
|
+
errorPolicy: 'all',
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (endpoints.errors) {
|
|
49
|
+
const isBadInput = endpoints.errors.some((e) => e.extensions?.code === 'BAD_USER_INPUT')
|
|
50
|
+
if (isBadInput) {
|
|
51
|
+
throw Error(`
|
|
52
|
+
Could not find environment for projectId ${config.projectId}.
|
|
53
|
+
Please check your GC_HYGRAPH_PROJECT_ID in your env file.
|
|
54
|
+
`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
throw new Error(`An error occurred: ${endpoints.errors.map((e) => e.message).join('\n')}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const environment =
|
|
61
|
+
endpoints.data.viewer.project.environments.find((env) => env.name === 'master') ??
|
|
62
|
+
endpoints.data.viewer.project.environments?.[0]
|
|
63
|
+
|
|
64
|
+
return environment
|
|
65
|
+
}
|