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