@graphprotocol/graph-cli 0.37.1 → 0.37.2-alpha-20221219142030-0cf2a37
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 +7 -0
- package/package.json +1 -2
- package/src/codegen/schema.js +49 -58
- package/src/codegen/schema.test.js +32 -27
- package/src/codegen/template.js +1 -2
- package/src/codegen/types/conversions.js +43 -14
- package/src/codegen/types/index.js +20 -23
- package/src/codegen/typescript.js +0 -3
- package/src/command-helpers/abi.js +2 -3
- package/src/command-helpers/data-sources.js +3 -4
- package/src/command-helpers/scaffold.js +18 -14
- package/src/command-helpers/spinner.js +0 -1
- package/src/commands/add.js +42 -25
- package/src/compiler/index.js +12 -13
- package/src/protocols/arweave/subgraph.js +2 -7
- package/src/protocols/cosmos/subgraph.js +2 -9
- package/src/protocols/ethereum/abi.js +6 -7
- package/src/protocols/ethereum/codegen/abi.js +217 -223
- package/src/protocols/ethereum/codegen/abi.test.js +15 -16
- package/src/protocols/ethereum/subgraph.js +55 -67
- package/src/protocols/ethereum/type-generator.js +16 -25
- package/src/protocols/index.js +5 -6
- package/src/protocols/near/subgraph.js +3 -5
- package/src/protocols/substreams/subgraph.js +2 -4
- package/src/scaffold/cosmos.test.js +0 -1
- package/src/scaffold/ethereum.test.js +2 -3
- package/src/scaffold/near.test.js +0 -1
- package/src/schema.js +1 -2
- package/src/subgraph.js +44 -53
- package/src/type-generator.js +6 -7
- package/src/validation/contract.js +6 -11
- package/src/validation/manifest.js +77 -84
- package/src/validation/schema.js +284 -294
package/src/validation/schema.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const graphql = require('graphql/language')
|
|
3
|
-
const immutable = require('immutable')
|
|
4
|
-
|
|
5
|
-
const List = immutable.List
|
|
6
|
-
const Set = immutable.Set
|
|
7
3
|
|
|
8
4
|
// Builtin scalar types
|
|
9
5
|
const BUILTIN_SCALAR_TYPES = [
|
|
@@ -76,26 +72,26 @@ const parseSchema = doc => {
|
|
|
76
72
|
|
|
77
73
|
const validateEntityDirective = def =>
|
|
78
74
|
def.directives.find(directive => directive.name.value === 'entity')
|
|
79
|
-
?
|
|
80
|
-
:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
? []
|
|
76
|
+
: [
|
|
77
|
+
{
|
|
78
|
+
loc: def.loc,
|
|
79
|
+
entity: def.name.value,
|
|
80
|
+
message: `Defined without @entity directive`,
|
|
81
|
+
},
|
|
82
|
+
]
|
|
87
83
|
|
|
88
84
|
const validateEntityID = def => {
|
|
89
85
|
let idField = def.fields.find(field => field.name.value === 'id')
|
|
90
86
|
|
|
91
87
|
if (idField === undefined) {
|
|
92
|
-
return
|
|
88
|
+
return [
|
|
93
89
|
{
|
|
94
90
|
loc: def.loc,
|
|
95
91
|
entity: def.name.value,
|
|
96
92
|
message: `Missing field: id: ID!`,
|
|
97
93
|
},
|
|
98
|
-
]
|
|
94
|
+
]
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
if (
|
|
@@ -105,36 +101,36 @@ const validateEntityID = def => {
|
|
|
105
101
|
idField.type.type.name.value === 'Bytes' ||
|
|
106
102
|
idField.type.type.name.value === 'String')
|
|
107
103
|
) {
|
|
108
|
-
return
|
|
104
|
+
return []
|
|
109
105
|
} else {
|
|
110
|
-
return
|
|
106
|
+
return [
|
|
111
107
|
{
|
|
112
108
|
loc: idField.loc,
|
|
113
109
|
entity: def.name.value,
|
|
114
110
|
message: `Field 'id': Entity ids must be of type Bytes! or String!`,
|
|
115
111
|
},
|
|
116
|
-
]
|
|
112
|
+
]
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
const validateListFieldType = (def, field) =>
|
|
121
117
|
field.type.kind === 'NonNullType' &&
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
?
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
field.type.kind === 'ListType' &&
|
|
119
|
+
field.type.type.kind !== 'NonNullType'
|
|
120
|
+
? [
|
|
121
|
+
{
|
|
122
|
+
loc: field.loc,
|
|
123
|
+
entity: def.name.value,
|
|
124
|
+
message: `\
|
|
129
125
|
Field '${field.name.value}':
|
|
130
126
|
Field has type [${field.type.type.name.value}]! but
|
|
131
127
|
must have type [${field.type.type.name.value}!]!
|
|
132
128
|
|
|
133
129
|
Reason: Lists with null elements are not supported.`,
|
|
134
|
-
|
|
135
|
-
|
|
130
|
+
},
|
|
131
|
+
]
|
|
136
132
|
: field.type.kind === 'ListType' && field.type.type.kind !== 'NonNullType'
|
|
137
|
-
|
|
133
|
+
? [
|
|
138
134
|
{
|
|
139
135
|
loc: field.loc,
|
|
140
136
|
entity: def.name.value,
|
|
@@ -145,8 +141,8 @@ must have type [${field.type.type.name.value}!]
|
|
|
145
141
|
|
|
146
142
|
Reason: Lists with null elements are not supported.`,
|
|
147
143
|
},
|
|
148
|
-
]
|
|
149
|
-
|
|
144
|
+
]
|
|
145
|
+
: []
|
|
150
146
|
|
|
151
147
|
const unwrapType = type => {
|
|
152
148
|
let innerTypeFromList = listType =>
|
|
@@ -163,8 +159,8 @@ const unwrapType = type => {
|
|
|
163
159
|
return type.kind === 'NonNullType'
|
|
164
160
|
? innerTypeFromNonNull(type)
|
|
165
161
|
: type.kind === 'ListType'
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
? innerTypeFromList(type)
|
|
163
|
+
: type
|
|
168
164
|
}
|
|
169
165
|
|
|
170
166
|
const gatherLocalTypes = defs =>
|
|
@@ -210,8 +206,8 @@ const gatherImportedTypes = defs =>
|
|
|
210
206
|
type.fields.find(
|
|
211
207
|
field => field.name.value == 'as' && field.value.kind == 'StringValue',
|
|
212
208
|
)
|
|
213
|
-
|
|
214
|
-
|
|
209
|
+
? type.fields.find(field => field.name.value == 'as').value.value
|
|
210
|
+
: undefined,
|
|
215
211
|
),
|
|
216
212
|
),
|
|
217
213
|
)
|
|
@@ -231,7 +227,8 @@ const entityTypeByName = (defs, name) =>
|
|
|
231
227
|
.filter(
|
|
232
228
|
def =>
|
|
233
229
|
def.kind === 'InterfaceTypeDefinition' ||
|
|
234
|
-
(def.kind === 'ObjectTypeDefinition' &&
|
|
230
|
+
(def.kind === 'ObjectTypeDefinition' &&
|
|
231
|
+
def.directives.find(directive => directive.name.value === 'entity')),
|
|
235
232
|
)
|
|
236
233
|
.find(def => def.name.value === name)
|
|
237
234
|
|
|
@@ -258,17 +255,18 @@ const validateInnerFieldType = (defs, def, field) => {
|
|
|
258
255
|
|
|
259
256
|
// Check whether the type name is available, otherwise return an error
|
|
260
257
|
return availableTypes.includes(typeName)
|
|
261
|
-
?
|
|
262
|
-
:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
258
|
+
? []
|
|
259
|
+
: [
|
|
260
|
+
{
|
|
261
|
+
loc: field.loc,
|
|
262
|
+
entity: def.name.value,
|
|
263
|
+
message: `\
|
|
267
264
|
Field '${field.name.value}': \
|
|
268
|
-
Unknown type '${typeName}'.${
|
|
265
|
+
Unknown type '${typeName}'.${
|
|
266
|
+
suggestion !== undefined ? ` Did you mean '${suggestion}'?` : ''
|
|
269
267
|
}`,
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
},
|
|
269
|
+
]
|
|
272
270
|
}
|
|
273
271
|
|
|
274
272
|
const validateEntityFieldType = (defs, def, field) =>
|
|
@@ -279,16 +277,16 @@ const validateEntityFieldType = (defs, def, field) =>
|
|
|
279
277
|
|
|
280
278
|
const validateEntityFieldArguments = (defs, def, field) =>
|
|
281
279
|
field.arguments.length > 0
|
|
282
|
-
?
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
280
|
+
? [
|
|
281
|
+
{
|
|
282
|
+
loc: field.loc,
|
|
283
|
+
entity: def.name.value,
|
|
284
|
+
message: `\
|
|
287
285
|
Field '${field.name.value}': \
|
|
288
286
|
Field arguments are not supported.`,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
:
|
|
287
|
+
},
|
|
288
|
+
]
|
|
289
|
+
: []
|
|
292
290
|
|
|
293
291
|
const entityFieldExists = (entityDef, name) =>
|
|
294
292
|
entityDef.fields.find(field => field.name.value === name) !== undefined
|
|
@@ -296,7 +294,7 @@ const entityFieldExists = (entityDef, name) =>
|
|
|
296
294
|
const validateDerivedFromDirective = (defs, def, field, directive) => {
|
|
297
295
|
// Validate that there is a `field` argument and nothing else
|
|
298
296
|
if (directive.arguments.length !== 1 || directive.arguments[0].name.value !== 'field') {
|
|
299
|
-
return
|
|
297
|
+
return [
|
|
300
298
|
{
|
|
301
299
|
loc: directive.loc,
|
|
302
300
|
entity: def.name.value,
|
|
@@ -304,12 +302,12 @@ const validateDerivedFromDirective = (defs, def, field, directive) => {
|
|
|
304
302
|
Field '${field.name.value}': \
|
|
305
303
|
@derivedFrom directive must have a 'field' argument`,
|
|
306
304
|
},
|
|
307
|
-
]
|
|
305
|
+
]
|
|
308
306
|
}
|
|
309
307
|
|
|
310
308
|
// Validate that the "field" argument value is a string
|
|
311
309
|
if (directive.arguments[0].value.kind !== 'StringValue') {
|
|
312
|
-
return
|
|
310
|
+
return [
|
|
313
311
|
{
|
|
314
312
|
loc: directive.loc,
|
|
315
313
|
entity: def.name.value,
|
|
@@ -317,7 +315,7 @@ Field '${field.name.value}': \
|
|
|
317
315
|
Field '${field.name.value}': \
|
|
318
316
|
Value of the @derivedFrom 'field' argument must be a string`,
|
|
319
317
|
},
|
|
320
|
-
]
|
|
318
|
+
]
|
|
321
319
|
}
|
|
322
320
|
|
|
323
321
|
let targetEntity = fieldTargetEntity(defs, field)
|
|
@@ -325,7 +323,7 @@ Value of the @derivedFrom 'field' argument must be a string`,
|
|
|
325
323
|
// This is handled in `validateInnerFieldType` but if we don't catch
|
|
326
324
|
// the undefined case here, the code below will throw, as it assumes
|
|
327
325
|
// the target entity exists
|
|
328
|
-
return
|
|
326
|
+
return []
|
|
329
327
|
}
|
|
330
328
|
|
|
331
329
|
let derivedFromField = targetEntity.fields.find(
|
|
@@ -333,7 +331,7 @@ Value of the @derivedFrom 'field' argument must be a string`,
|
|
|
333
331
|
)
|
|
334
332
|
|
|
335
333
|
if (derivedFromField === undefined) {
|
|
336
|
-
return
|
|
334
|
+
return [
|
|
337
335
|
{
|
|
338
336
|
loc: directive.loc,
|
|
339
337
|
entity: def.name.value,
|
|
@@ -342,7 +340,7 @@ Field '${field.name.value}': \
|
|
|
342
340
|
@derivedFrom field '${directive.arguments[0].value.value}' \
|
|
343
341
|
does not exist on type '${targetEntity.name.value}'`,
|
|
344
342
|
},
|
|
345
|
-
]
|
|
343
|
+
]
|
|
346
344
|
}
|
|
347
345
|
|
|
348
346
|
let backrefTypeName = unwrapType(derivedFromField.type)
|
|
@@ -350,8 +348,12 @@ does not exist on type '${targetEntity.name.value}'`,
|
|
|
350
348
|
|
|
351
349
|
// The field we are deriving from must either have type 'def' or one of the
|
|
352
350
|
// interface types that 'def' is implementing
|
|
353
|
-
if (
|
|
354
|
-
|
|
351
|
+
if (
|
|
352
|
+
!backRefEntity ||
|
|
353
|
+
(backRefEntity.name.value !== def.name.value &&
|
|
354
|
+
!def.interfaces.find(intf => intf.name.value === backRefEntity.name.value))
|
|
355
|
+
) {
|
|
356
|
+
return [
|
|
355
357
|
{
|
|
356
358
|
loc: directive.loc,
|
|
357
359
|
entity: def.name.value,
|
|
@@ -362,22 +364,22 @@ on type '${targetEntity.name.value}' must have the type \
|
|
|
362
364
|
'${def.name.value}', '${def.name.value}!', '[${def.name.value}!]!', \
|
|
363
365
|
or one of the interface types that '${def.name.value}' implements`,
|
|
364
366
|
},
|
|
365
|
-
]
|
|
367
|
+
]
|
|
366
368
|
}
|
|
367
369
|
|
|
368
|
-
return
|
|
370
|
+
return []
|
|
369
371
|
}
|
|
370
372
|
|
|
371
373
|
const validateEntityFieldDirective = (defs, def, field, directive) =>
|
|
372
374
|
directive.name.value === 'derivedFrom'
|
|
373
375
|
? validateDerivedFromDirective(defs, def, field, directive)
|
|
374
|
-
:
|
|
376
|
+
: []
|
|
375
377
|
|
|
376
378
|
const validateEntityFieldDirectives = (defs, def, field) =>
|
|
377
379
|
field.directives.reduce(
|
|
378
380
|
(errors, directive) =>
|
|
379
381
|
errors.concat(validateEntityFieldDirective(defs, def, field, directive)),
|
|
380
|
-
|
|
382
|
+
[],
|
|
381
383
|
)
|
|
382
384
|
|
|
383
385
|
const validateEntityFields = (defs, def) =>
|
|
@@ -387,73 +389,73 @@ const validateEntityFields = (defs, def) =>
|
|
|
387
389
|
.concat(validateEntityFieldType(defs, def, field))
|
|
388
390
|
.concat(validateEntityFieldArguments(defs, def, field))
|
|
389
391
|
.concat(validateEntityFieldDirectives(defs, def, field)),
|
|
390
|
-
|
|
392
|
+
[],
|
|
391
393
|
)
|
|
392
394
|
|
|
393
395
|
const validateNoImportDirective = def =>
|
|
394
396
|
def.directives.find(directive => directive.name.value == 'import')
|
|
395
|
-
?
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
:
|
|
397
|
+
? [
|
|
398
|
+
{
|
|
399
|
+
loc: def.name.loc,
|
|
400
|
+
entity: def.name.value,
|
|
401
|
+
message: `@import directive only allowed on '${RESERVED_TYPE}' type`,
|
|
402
|
+
},
|
|
403
|
+
]
|
|
404
|
+
: []
|
|
403
405
|
|
|
404
406
|
const validateNoFulltext = def =>
|
|
405
407
|
def.directives.find(directive => directive.name.value == 'fulltext')
|
|
406
|
-
?
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
:
|
|
408
|
+
? [
|
|
409
|
+
{
|
|
410
|
+
loc: def.name.loc,
|
|
411
|
+
entity: def.name.value,
|
|
412
|
+
message: `@fulltext directive only allowed on '${RESERVED_TYPE}' type`,
|
|
413
|
+
},
|
|
414
|
+
]
|
|
415
|
+
: []
|
|
414
416
|
|
|
415
417
|
const validateFulltextFields = (def, directive) => {
|
|
416
418
|
return directive.arguments.reduce((errors, argument) => {
|
|
417
419
|
return errors.concat(
|
|
418
420
|
['name', 'language', 'algorithm', 'include'].includes(argument.name.value)
|
|
419
|
-
?
|
|
420
|
-
:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
421
|
+
? []
|
|
422
|
+
: [
|
|
423
|
+
{
|
|
424
|
+
loc: directive.name.loc,
|
|
425
|
+
entity: def.name.value,
|
|
426
|
+
directive: fulltextDirectiveName(directive),
|
|
427
|
+
message: `found invalid argument: '${argument.name.value}', @fulltext directives only allow 'name', 'language', 'algorithm', and 'includes' arguments`,
|
|
428
|
+
},
|
|
429
|
+
],
|
|
428
430
|
)
|
|
429
|
-
},
|
|
431
|
+
}, [])
|
|
430
432
|
}
|
|
431
433
|
|
|
432
434
|
const validateFulltextName = (def, directive) => {
|
|
433
435
|
let name = directive.arguments.find(argument => argument.name.value == 'name')
|
|
434
436
|
return name
|
|
435
437
|
? validateFulltextArgumentName(def, directive, name)
|
|
436
|
-
:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
438
|
+
: [
|
|
439
|
+
{
|
|
440
|
+
loc: directive.name.loc,
|
|
441
|
+
entity: def.name.value,
|
|
442
|
+
directive: fulltextDirectiveName(directive),
|
|
443
|
+
message: `@fulltext argument 'name' must be specified`,
|
|
444
|
+
},
|
|
445
|
+
]
|
|
444
446
|
}
|
|
445
447
|
|
|
446
448
|
const validateFulltextArgumentName = (def, directive, argument) => {
|
|
447
449
|
return argument.value.kind != 'StringValue'
|
|
448
|
-
?
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
:
|
|
450
|
+
? [
|
|
451
|
+
{
|
|
452
|
+
loc: directive.name.loc,
|
|
453
|
+
entity: def.name.value,
|
|
454
|
+
directive: fulltextDirectiveName(directive),
|
|
455
|
+
message: `@fulltext argument 'name' must be a string`,
|
|
456
|
+
},
|
|
457
|
+
]
|
|
458
|
+
: []
|
|
457
459
|
}
|
|
458
460
|
|
|
459
461
|
const fulltextDirectiveName = directive => {
|
|
@@ -465,14 +467,14 @@ const validateFulltextLanguage = (def, directive) => {
|
|
|
465
467
|
let language = directive.arguments.find(argument => argument.name.value == 'language')
|
|
466
468
|
return language
|
|
467
469
|
? validateFulltextArgumentLanguage(def, directive, language)
|
|
468
|
-
:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
470
|
+
: [
|
|
471
|
+
{
|
|
472
|
+
loc: directive.name.loc,
|
|
473
|
+
entity: def.name.value,
|
|
474
|
+
directive: fulltextDirectiveName(directive),
|
|
475
|
+
message: `@fulltext argument 'language' must be specified`,
|
|
476
|
+
},
|
|
477
|
+
]
|
|
476
478
|
}
|
|
477
479
|
|
|
478
480
|
const validateFulltextArgumentLanguage = (def, directive, argument) => {
|
|
@@ -495,27 +497,27 @@ const validateFulltextArgumentLanguage = (def, directive, argument) => {
|
|
|
495
497
|
'tr',
|
|
496
498
|
]
|
|
497
499
|
if (argument.value.kind != 'EnumValue') {
|
|
498
|
-
return
|
|
499
|
-
|
|
500
|
+
return [
|
|
501
|
+
{
|
|
500
502
|
loc: directive.name.loc,
|
|
501
503
|
entity: def.name.value,
|
|
502
504
|
directive: fulltextDirectiveName(directive),
|
|
503
505
|
message: `@fulltext 'language' value must be one of: ${languages.join(', ')}`,
|
|
504
|
-
}
|
|
505
|
-
]
|
|
506
|
+
},
|
|
507
|
+
]
|
|
506
508
|
} else if (!languages.includes(argument.value.value)) {
|
|
507
|
-
return
|
|
508
|
-
|
|
509
|
+
return [
|
|
510
|
+
{
|
|
509
511
|
loc: directive.name.loc,
|
|
510
512
|
entity: def.name.value,
|
|
511
513
|
directive: fulltextDirectiveName(directive),
|
|
512
514
|
message: `@fulltext directive 'language' value must be one of: ${languages.join(
|
|
513
515
|
', ',
|
|
514
516
|
)}`,
|
|
515
|
-
}
|
|
516
|
-
]
|
|
517
|
+
},
|
|
518
|
+
]
|
|
517
519
|
} else {
|
|
518
|
-
return
|
|
520
|
+
return []
|
|
519
521
|
}
|
|
520
522
|
}
|
|
521
523
|
|
|
@@ -523,35 +525,35 @@ const validateFulltextAlgorithm = (def, directive) => {
|
|
|
523
525
|
let algorithm = directive.arguments.find(argument => argument.name.value == 'algorithm')
|
|
524
526
|
return algorithm
|
|
525
527
|
? validateFulltextArgumentAlgorithm(def, directive, algorithm)
|
|
526
|
-
:
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
528
|
+
: [
|
|
529
|
+
{
|
|
530
|
+
loc: directive.name.loc,
|
|
531
|
+
entity: def.name.value,
|
|
532
|
+
directive: fulltextDirectiveName(directive),
|
|
533
|
+
message: `@fulltext argument 'algorithm' must be specified`,
|
|
534
|
+
},
|
|
535
|
+
]
|
|
534
536
|
}
|
|
535
537
|
|
|
536
538
|
const validateFulltextArgumentAlgorithm = (def, directive, argument) => {
|
|
537
539
|
if (argument.value.kind != 'EnumValue') {
|
|
538
|
-
return
|
|
539
|
-
|
|
540
|
+
return [
|
|
541
|
+
{
|
|
540
542
|
loc: directive.name.loc,
|
|
541
543
|
entity: def.name.value,
|
|
542
544
|
directive: fulltextDirectiveName(directive),
|
|
543
545
|
message: `@fulltext argument 'algorithm' must be one of: rank, proximityRank`,
|
|
544
|
-
}
|
|
545
|
-
]
|
|
546
|
+
},
|
|
547
|
+
]
|
|
546
548
|
} else if (!['rank', 'proximityRank'].includes(argument.value.value)) {
|
|
547
|
-
return
|
|
548
|
-
|
|
549
|
+
return [
|
|
550
|
+
{
|
|
549
551
|
loc: directive.name.loc,
|
|
550
552
|
entity: def.name.value,
|
|
551
553
|
directive: fulltextDirectiveName(directive),
|
|
552
554
|
message: `@fulltext 'algorithm' value, '${argument.value.value}', must be one of: rank, proximityRank`,
|
|
553
|
-
}
|
|
554
|
-
]
|
|
555
|
+
},
|
|
556
|
+
]
|
|
555
557
|
} else {
|
|
556
558
|
return List([])
|
|
557
559
|
}
|
|
@@ -561,52 +563,52 @@ const validateFulltextInclude = (def, directive) => {
|
|
|
561
563
|
let include = directive.arguments.find(argument => argument.name.value == 'include')
|
|
562
564
|
if (include) {
|
|
563
565
|
if (include.value.kind != 'ListValue') {
|
|
564
|
-
return
|
|
565
|
-
|
|
566
|
+
return [
|
|
567
|
+
{
|
|
566
568
|
loc: directive.name.loc,
|
|
567
569
|
entity: def.name.value,
|
|
568
570
|
directive: fulltextDirectiveName(directive),
|
|
569
571
|
message: `@fulltext argument 'include' must be a list`,
|
|
570
|
-
}
|
|
571
|
-
]
|
|
572
|
+
},
|
|
573
|
+
]
|
|
572
574
|
}
|
|
573
575
|
return include.value.values.reduce(
|
|
574
576
|
(errors, type) =>
|
|
575
577
|
errors.concat(validateFulltextArgumentInclude(def, directive, type)),
|
|
576
|
-
|
|
578
|
+
[],
|
|
577
579
|
)
|
|
578
580
|
} else {
|
|
579
|
-
return
|
|
580
|
-
|
|
581
|
+
return [
|
|
582
|
+
{
|
|
581
583
|
loc: directive.name.loc,
|
|
582
584
|
entity: def.name.value,
|
|
583
585
|
directive: fulltextDirectiveName(directive),
|
|
584
586
|
message: `@fulltext argument 'include' must be specified`,
|
|
585
|
-
}
|
|
586
|
-
]
|
|
587
|
+
},
|
|
588
|
+
]
|
|
587
589
|
}
|
|
588
590
|
}
|
|
589
591
|
|
|
590
592
|
const validateFulltextArgumentInclude = (def, directive, argument) => {
|
|
591
593
|
if (argument.kind != 'ObjectValue') {
|
|
592
|
-
return
|
|
593
|
-
|
|
594
|
+
return [
|
|
595
|
+
{
|
|
594
596
|
loc: directive.name.loc,
|
|
595
597
|
entity: def.name.value,
|
|
596
598
|
directive: fulltextDirectiveName(directive),
|
|
597
599
|
message: `@fulltext argument 'include' must have the form '[{entity: "entityName", fields: [{name: "fieldName"}, ...]} ...]`,
|
|
598
|
-
}
|
|
599
|
-
]
|
|
600
|
+
},
|
|
601
|
+
]
|
|
600
602
|
}
|
|
601
603
|
if (argument.fields.length != 2) {
|
|
602
|
-
return
|
|
603
|
-
|
|
604
|
+
return [
|
|
605
|
+
{
|
|
604
606
|
loc: directive.name.loc,
|
|
605
607
|
entity: def.name.value,
|
|
606
608
|
directive: fulltextDirectiveName(directive),
|
|
607
609
|
message: `@fulltext argument include must have two fields, 'entity' and 'fields'`,
|
|
608
|
-
}
|
|
609
|
-
]
|
|
610
|
+
},
|
|
611
|
+
]
|
|
610
612
|
}
|
|
611
613
|
return argument.fields.reduce(
|
|
612
614
|
(errors, field) =>
|
|
@@ -617,33 +619,33 @@ const validateFulltextArgumentInclude = (def, directive, argument) => {
|
|
|
617
619
|
|
|
618
620
|
const validateFulltextArgumentIncludeFields = (def, directive, field) => {
|
|
619
621
|
if (!['entity', 'fields'].includes(field.name.value)) {
|
|
620
|
-
return
|
|
621
|
-
|
|
622
|
+
return [
|
|
623
|
+
{
|
|
622
624
|
loc: directive.name.loc,
|
|
623
625
|
entity: def.name.value,
|
|
624
626
|
directive: fulltextDirectiveName(directive),
|
|
625
627
|
message: `@fulltext argument 'include > ${field.name.value}' must be be one of: entity, fields`,
|
|
626
|
-
}
|
|
627
|
-
]
|
|
628
|
+
},
|
|
629
|
+
]
|
|
628
630
|
}
|
|
629
631
|
if (field.name.value == 'entity' && field.value.kind != 'StringValue') {
|
|
630
|
-
return
|
|
631
|
-
|
|
632
|
+
return [
|
|
633
|
+
{
|
|
632
634
|
loc: directive.name.loc,
|
|
633
635
|
entity: def.name.value,
|
|
634
636
|
directive: fulltextDirectiveName(directive),
|
|
635
637
|
message: `@fulltext argument 'include > entity' must be the name of an entity in the schema enclosed in double quotes`,
|
|
636
|
-
}
|
|
637
|
-
]
|
|
638
|
+
},
|
|
639
|
+
]
|
|
638
640
|
} else if (field.name.value == 'fields' && field.value.kind != 'ListValue') {
|
|
639
|
-
return
|
|
640
|
-
|
|
641
|
+
return [
|
|
642
|
+
{
|
|
641
643
|
loc: directive.name.loc,
|
|
642
644
|
entity: def.name.value,
|
|
643
645
|
directive: fulltextDirectiveName(directive),
|
|
644
646
|
message: `@fulltext argument 'include > fields' must be a list`,
|
|
645
|
-
}
|
|
646
|
-
]
|
|
647
|
+
},
|
|
648
|
+
]
|
|
647
649
|
} else if (field.name.value == 'fields' && field.value.kind == 'ListValue') {
|
|
648
650
|
return field.value.values.reduce(
|
|
649
651
|
(errors, field) =>
|
|
@@ -659,80 +661,74 @@ const validateFulltextArgumentIncludeFields = (def, directive, field) => {
|
|
|
659
661
|
|
|
660
662
|
const validateFulltextArgumentIncludeFieldsObjects = (def, directive, argument) => {
|
|
661
663
|
if (argument.kind != 'ObjectValue') {
|
|
662
|
-
return
|
|
663
|
-
|
|
664
|
+
return [
|
|
665
|
+
{
|
|
664
666
|
loc: directive.name.loc,
|
|
665
667
|
entity: def.name.value,
|
|
666
668
|
directive: fulltextDirectiveName(directive),
|
|
667
669
|
message: `@fulltext argument 'include > fields' must have the form '[{ name: "fieldName" }, ...]`,
|
|
668
|
-
}
|
|
669
|
-
]
|
|
670
|
+
},
|
|
671
|
+
]
|
|
670
672
|
} else {
|
|
671
673
|
return argument.fields.reduce(
|
|
672
674
|
(errors, field) =>
|
|
673
675
|
errors.concat(
|
|
674
676
|
validateFulltextArgumentIncludeArgumentFieldsObject(def, directive, field),
|
|
675
677
|
),
|
|
676
|
-
|
|
678
|
+
[],
|
|
677
679
|
)
|
|
678
680
|
}
|
|
679
681
|
}
|
|
680
682
|
|
|
681
683
|
const validateFulltextArgumentIncludeArgumentFieldsObject = (def, directive, field) => {
|
|
682
684
|
if (!['name'].includes(field.name.value)) {
|
|
683
|
-
return
|
|
684
|
-
|
|
685
|
+
return [
|
|
686
|
+
{
|
|
685
687
|
loc: directive.name.loc,
|
|
686
688
|
entity: def.name.value,
|
|
687
689
|
directive: fulltextDirectiveName(directive),
|
|
688
690
|
message: `@fulltext argument 'include > fields' has invalid member '${field.name.value}', must be one of: name`,
|
|
689
|
-
}
|
|
690
|
-
]
|
|
691
|
+
},
|
|
692
|
+
]
|
|
691
693
|
} else if (field.name.value == 'name' && field.value.kind != 'StringValue') {
|
|
692
|
-
return
|
|
693
|
-
|
|
694
|
+
return [
|
|
695
|
+
{
|
|
694
696
|
loc: directive.name.loc,
|
|
695
697
|
entity: def.name.value,
|
|
696
698
|
directive: fulltextDirectiveName(directive),
|
|
697
699
|
message: `@fulltext argument 'include > fields > name' must be the name of an entity field enclosed in double quotes`,
|
|
698
|
-
}
|
|
699
|
-
]
|
|
700
|
+
},
|
|
701
|
+
]
|
|
700
702
|
} else {
|
|
701
703
|
return List([])
|
|
702
704
|
}
|
|
703
705
|
}
|
|
704
706
|
|
|
705
707
|
const importDirectiveTypeValidators = {
|
|
706
|
-
StringValue: (_def, _directive, _type) =>
|
|
708
|
+
StringValue: (_def, _directive, _type) => [],
|
|
707
709
|
ObjectValue: (def, directive, type) => {
|
|
708
|
-
let errors =
|
|
710
|
+
let errors = []
|
|
709
711
|
if (type.fields.length != 2) {
|
|
710
|
-
return errors.push(
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}),
|
|
716
|
-
)
|
|
712
|
+
return errors.push({
|
|
713
|
+
loc: directive.name.loc,
|
|
714
|
+
entity: def.name.value,
|
|
715
|
+
message: `Import must be one of "Name" or { name: "Name", as: "Alias" }`,
|
|
716
|
+
})
|
|
717
717
|
}
|
|
718
718
|
return type.fields.reduce((errors, field) => {
|
|
719
719
|
if (!['name', 'as'].includes(field.name.value)) {
|
|
720
|
-
return errors.push(
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}),
|
|
726
|
-
)
|
|
720
|
+
return errors.push({
|
|
721
|
+
loc: directive.name.loc,
|
|
722
|
+
entity: def.name.value,
|
|
723
|
+
message: `@import field '${field.name.value}' invalid, may only be one of: name, as`,
|
|
724
|
+
})
|
|
727
725
|
}
|
|
728
726
|
if (field.value.kind != 'StringValue') {
|
|
729
|
-
return errors.push(
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}),
|
|
735
|
-
)
|
|
727
|
+
return errors.push({
|
|
728
|
+
loc: directive.name.loc,
|
|
729
|
+
entity: def.name.value,
|
|
730
|
+
message: `@import fields [name, as] must be strings`,
|
|
731
|
+
})
|
|
736
732
|
}
|
|
737
733
|
return errors
|
|
738
734
|
}, errors)
|
|
@@ -742,116 +738,112 @@ const importDirectiveTypeValidators = {
|
|
|
742
738
|
const validateImportDirectiveType = (def, directive, type) => {
|
|
743
739
|
return importDirectiveTypeValidators[type.kind]
|
|
744
740
|
? importDirectiveTypeValidators[type.kind](def, directive, type)
|
|
745
|
-
:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
741
|
+
: [
|
|
742
|
+
{
|
|
743
|
+
loc: directive.name.loc,
|
|
744
|
+
entity: def.name.value,
|
|
745
|
+
message: `Import must be one of "Name" or { name: "Name", as: "Alias" }`,
|
|
746
|
+
},
|
|
747
|
+
]
|
|
752
748
|
}
|
|
753
749
|
|
|
754
750
|
const validateImportDirectiveArgumentTypes = (def, directive, argument) => {
|
|
755
751
|
if (argument.value.kind != 'ListValue') {
|
|
756
|
-
return
|
|
757
|
-
|
|
752
|
+
return [
|
|
753
|
+
{
|
|
758
754
|
loc: directive.name.loc,
|
|
759
755
|
entity: def.name.value,
|
|
760
756
|
message: `@import argument 'types' must be an list`,
|
|
761
|
-
}
|
|
762
|
-
]
|
|
757
|
+
},
|
|
758
|
+
]
|
|
763
759
|
}
|
|
764
760
|
|
|
765
761
|
return argument.value.values.reduce(
|
|
766
762
|
(errors, type) => errors.concat(validateImportDirectiveType(def, directive, type)),
|
|
767
|
-
|
|
763
|
+
[],
|
|
768
764
|
)
|
|
769
765
|
}
|
|
770
766
|
|
|
771
767
|
const validateImportDirectiveArgumentFrom = (def, directive, argument) => {
|
|
772
768
|
if (argument.value.kind != 'ObjectValue') {
|
|
773
|
-
return
|
|
774
|
-
|
|
769
|
+
return [
|
|
770
|
+
{
|
|
775
771
|
loc: directive.name.loc,
|
|
776
772
|
entity: def.name.value,
|
|
777
773
|
message: `@import argument 'from' must be an object`,
|
|
778
|
-
}
|
|
779
|
-
]
|
|
774
|
+
},
|
|
775
|
+
]
|
|
780
776
|
}
|
|
781
777
|
|
|
782
778
|
if (argument.value.fields.length != 1) {
|
|
783
|
-
return
|
|
784
|
-
|
|
779
|
+
return [
|
|
780
|
+
{
|
|
785
781
|
loc: directive.name.loc,
|
|
786
782
|
entity: def.name.value,
|
|
787
783
|
message: `@import argument 'from' must have an 'id' or 'name' field`,
|
|
788
|
-
}
|
|
789
|
-
]
|
|
784
|
+
},
|
|
785
|
+
]
|
|
790
786
|
}
|
|
791
787
|
|
|
792
788
|
return argument.value.fields.reduce((errors, field) => {
|
|
793
789
|
if (!['name', 'id'].includes(field.name.value)) {
|
|
794
|
-
return errors.push(
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
}),
|
|
800
|
-
)
|
|
790
|
+
return errors.push({
|
|
791
|
+
loc: field.name.loc,
|
|
792
|
+
entity: def.name.value,
|
|
793
|
+
message: `@import argument 'from' must be one of { name: "Name" } or { id: "ID" }`,
|
|
794
|
+
})
|
|
801
795
|
}
|
|
802
796
|
if (field.value.kind != 'StringValue') {
|
|
803
|
-
return errors.push(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
}),
|
|
809
|
-
)
|
|
797
|
+
return errors.push({
|
|
798
|
+
loc: field.name.loc,
|
|
799
|
+
entity: def.name.value,
|
|
800
|
+
message: `@import argument 'from' must be one of { name: "Name" } or { id: "ID" } with string values`,
|
|
801
|
+
})
|
|
810
802
|
}
|
|
811
803
|
return errors
|
|
812
|
-
},
|
|
804
|
+
}, [])
|
|
813
805
|
}
|
|
814
806
|
|
|
815
807
|
const validateImportDirectiveFields = (def, directive) => {
|
|
816
808
|
return directive.arguments.reduce((errors, argument) => {
|
|
817
809
|
return errors.concat(
|
|
818
810
|
['types', 'from'].includes(argument.name.value)
|
|
819
|
-
?
|
|
820
|
-
:
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
811
|
+
? []
|
|
812
|
+
: [
|
|
813
|
+
{
|
|
814
|
+
loc: directive.name.loc,
|
|
815
|
+
entity: def.name.value,
|
|
816
|
+
message: `found invalid argument: '${argument.name.value}', @import directives only allow 'types' and 'from' arguments`,
|
|
817
|
+
},
|
|
818
|
+
],
|
|
827
819
|
)
|
|
828
|
-
},
|
|
820
|
+
}, [])
|
|
829
821
|
}
|
|
830
822
|
|
|
831
823
|
const validateImportDirectiveTypes = (def, directive) => {
|
|
832
824
|
let types = directive.arguments.find(argument => argument.name.value == 'types')
|
|
833
825
|
return types
|
|
834
826
|
? validateImportDirectiveArgumentTypes(def, directive, types)
|
|
835
|
-
:
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
827
|
+
: [
|
|
828
|
+
{
|
|
829
|
+
loc: directive.name.loc,
|
|
830
|
+
entity: def.name.value,
|
|
831
|
+
message: `@import argument 'types' must be specified`,
|
|
832
|
+
},
|
|
833
|
+
]
|
|
842
834
|
}
|
|
843
835
|
|
|
844
836
|
const validateImportDirectiveFrom = (def, directive) => {
|
|
845
837
|
let from = directive.arguments.find(argument => argument.name.value == 'from')
|
|
846
838
|
return from
|
|
847
839
|
? validateImportDirectiveArgumentFrom(def, directive, from)
|
|
848
|
-
:
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
840
|
+
: [
|
|
841
|
+
{
|
|
842
|
+
loc: directive.name.loc,
|
|
843
|
+
entity: def.name.value,
|
|
844
|
+
message: `@import argument 'from' must be specified`,
|
|
845
|
+
},
|
|
846
|
+
]
|
|
855
847
|
}
|
|
856
848
|
|
|
857
849
|
const validateImportDirective = (def, directive) =>
|
|
@@ -876,75 +868,73 @@ const validateSubgraphSchemaDirective = (def, directive) => {
|
|
|
876
868
|
} else if (directive.name.value == 'fulltext') {
|
|
877
869
|
return validateFulltext(def, directive)
|
|
878
870
|
} else {
|
|
879
|
-
return
|
|
880
|
-
|
|
871
|
+
return [
|
|
872
|
+
{
|
|
881
873
|
loc: directive.name.loc,
|
|
882
874
|
entity: def.name.value,
|
|
883
875
|
message: `${RESERVED_TYPE} type only allows @import and @fulltext directives`,
|
|
884
|
-
}
|
|
885
|
-
]
|
|
876
|
+
},
|
|
877
|
+
]
|
|
886
878
|
}
|
|
887
879
|
}
|
|
888
880
|
|
|
889
881
|
const validateSubgraphSchemaDirectives = def =>
|
|
890
882
|
def.directives.reduce(
|
|
891
883
|
(errors, directive) => errors.concat(validateSubgraphSchemaDirective(def, directive)),
|
|
892
|
-
|
|
884
|
+
[],
|
|
893
885
|
)
|
|
894
886
|
|
|
895
887
|
const validateTypeHasNoFields = def =>
|
|
896
888
|
def.fields.length
|
|
897
|
-
?
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
:
|
|
889
|
+
? [
|
|
890
|
+
{
|
|
891
|
+
loc: def.name.loc,
|
|
892
|
+
entity: def.name.value,
|
|
893
|
+
message: `${def.name.value} type is not allowed any fields by convention`,
|
|
894
|
+
},
|
|
895
|
+
]
|
|
896
|
+
: []
|
|
905
897
|
|
|
906
|
-
const validateAtLeastOneExtensionField = def =>
|
|
898
|
+
const validateAtLeastOneExtensionField = def => []
|
|
907
899
|
|
|
908
900
|
const typeDefinitionValidators = {
|
|
909
901
|
ObjectTypeDefinition: (defs, def) =>
|
|
910
902
|
def.name && def.name.value == RESERVED_TYPE
|
|
911
903
|
? List.of(...validateSubgraphSchemaDirectives(def), ...validateTypeHasNoFields(def))
|
|
912
904
|
: List.of(
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
905
|
+
...validateEntityDirective(def),
|
|
906
|
+
...validateEntityID(def),
|
|
907
|
+
...validateEntityFields(defs, def),
|
|
908
|
+
...validateNoImportDirective(def),
|
|
909
|
+
...validateNoFulltext(def),
|
|
910
|
+
),
|
|
919
911
|
ObjectTypeExtension: (_defs, def) => validateAtLeastOneExtensionField(def),
|
|
920
912
|
}
|
|
921
913
|
|
|
922
914
|
const validateTypeDefinition = (defs, def) =>
|
|
923
915
|
typeDefinitionValidators[def.kind] !== undefined
|
|
924
916
|
? typeDefinitionValidators[def.kind](defs, def)
|
|
925
|
-
:
|
|
917
|
+
: []
|
|
926
918
|
|
|
927
919
|
const validateTypeDefinitions = defs =>
|
|
928
|
-
defs.reduce((errors, def) => errors.concat(validateTypeDefinition(defs, def)),
|
|
920
|
+
defs.reduce((errors, def) => errors.concat(validateTypeDefinition(defs, def)), [])
|
|
929
921
|
|
|
930
922
|
const validateNamingCollisionsInTypes = types => {
|
|
931
923
|
let seen = Set()
|
|
932
924
|
let conflicting = Set()
|
|
933
925
|
return types.reduce((errors, type) => {
|
|
934
926
|
if (seen.has(type) && !conflicting.has(type)) {
|
|
935
|
-
errors = errors.push(
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
}),
|
|
941
|
-
)
|
|
927
|
+
errors = errors.push({
|
|
928
|
+
loc: { start: 1, end: 1 },
|
|
929
|
+
entity: type,
|
|
930
|
+
message: `Type '${type}' is defined more than once`,
|
|
931
|
+
})
|
|
942
932
|
conflicting = conflicting.add(type)
|
|
943
933
|
} else {
|
|
944
934
|
seen = seen.add(type)
|
|
945
935
|
}
|
|
946
936
|
return errors
|
|
947
|
-
},
|
|
937
|
+
}, [])
|
|
948
938
|
}
|
|
949
939
|
|
|
950
940
|
const validateNamingCollisions = (local, imported) =>
|