@graphprotocol/graph-cli 0.56.0 → 0.56.1-alpha-20230831035057-3d319d2
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 +13 -0
- package/dist/debug.js +6 -0
- package/dist/scaffold/schema.js +6 -2
- package/dist/subgraph.js +28 -4
- package/dist/validation/schema.js +152 -70
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @graphprotocol/graph-cli
|
|
2
2
|
|
|
3
|
+
## 0.56.1-alpha-20230831035057-3d319d2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1433](https://github.com/graphprotocol/graph-tooling/pull/1433)
|
|
8
|
+
[`3d319d2`](https://github.com/graphprotocol/graph-tooling/commit/3d319d2bf39856b1e8e106b16302bbe40d3028b5)
|
|
9
|
+
Thanks [@saihaj](https://github.com/saihaj)! - when generating nested tuples make sure they are
|
|
10
|
+
non-nullable
|
|
11
|
+
|
|
12
|
+
- [#1433](https://github.com/graphprotocol/graph-tooling/pull/1433)
|
|
13
|
+
[`8287a05`](https://github.com/graphprotocol/graph-tooling/commit/8287a058f9991bb7fa5591142af6dd17cf2fb77d)
|
|
14
|
+
Thanks [@saihaj](https://github.com/saihaj)! - 2D arrays are valid entities
|
|
15
|
+
|
|
3
16
|
## 0.56.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/dist/debug.js
CHANGED
|
@@ -11,6 +11,12 @@ debug_1.default.formatters.M = immutableMap => {
|
|
|
11
11
|
if (immutableMap.toMap != null) {
|
|
12
12
|
return JSON.stringify(immutableMap.toMap());
|
|
13
13
|
}
|
|
14
|
+
if (typeof immutableMap.toJS === 'function') {
|
|
15
|
+
return JSON.stringify(immutableMap.toJS());
|
|
16
|
+
}
|
|
17
|
+
if (typeof immutableMap === 'object') {
|
|
18
|
+
return JSON.stringify(immutableMap);
|
|
19
|
+
}
|
|
14
20
|
return immutableMap;
|
|
15
21
|
};
|
|
16
22
|
exports.default = debug_1.default;
|
package/dist/scaffold/schema.js
CHANGED
|
@@ -46,14 +46,18 @@ const protocolTypeToGraphQL = (protocol, name) => {
|
|
|
46
46
|
// TODO: this is a hack to make array type non-nullable
|
|
47
47
|
// We should refactor the way we convert the Values from ASC to GraphQL
|
|
48
48
|
// For arrays we always want non-nullable children
|
|
49
|
-
return convertedType.endsWith(']') ? convertedType.replace(
|
|
49
|
+
return convertedType.endsWith(']') ? convertedType.replace(/\]/g, '!]') : convertedType;
|
|
50
50
|
};
|
|
51
51
|
exports.protocolTypeToGraphQL = protocolTypeToGraphQL;
|
|
52
52
|
const generateField = ({ name, type, protocolName, }) => `${name}: ${(0, exports.protocolTypeToGraphQL)(protocolName, type)}! # ${type}`;
|
|
53
53
|
exports.generateField = generateField;
|
|
54
54
|
const generateEventFields = ({ index, input, protocolName, }) => input.type == 'tuple'
|
|
55
55
|
? util
|
|
56
|
-
.unrollTuple({
|
|
56
|
+
.unrollTuple({
|
|
57
|
+
value: input,
|
|
58
|
+
path: [input.name || `param${index}`],
|
|
59
|
+
index,
|
|
60
|
+
})
|
|
57
61
|
.map(({ path, type }) => (0, exports.generateField)({ name: path.join('_'), type, protocolName }))
|
|
58
62
|
: [
|
|
59
63
|
(0, exports.generateField)({
|
package/dist/subgraph.js
CHANGED
|
@@ -49,8 +49,9 @@ const buildCombinedWarning = (filename, warnings) => warnings.size > 0
|
|
|
49
49
|
: null;
|
|
50
50
|
class Subgraph {
|
|
51
51
|
static async validate(data, protocol, { resolveFile }) {
|
|
52
|
-
subgraphDebug(
|
|
52
|
+
subgraphDebug.extend('validate')('Validating Subgraph with protocol %M', protocol);
|
|
53
53
|
if (protocol.name == null) {
|
|
54
|
+
subgraphDebug.extend('validate')('Protocol has no name, skipping validation');
|
|
54
55
|
return immutable_1.default.fromJS([
|
|
55
56
|
{
|
|
56
57
|
path: [],
|
|
@@ -66,13 +67,18 @@ class Subgraph {
|
|
|
66
67
|
return definition.name.value === 'SubgraphManifest';
|
|
67
68
|
});
|
|
68
69
|
// Validate the subgraph manifest using this schema
|
|
69
|
-
return validation.validateManifest(data, rootType, schema, protocol, {
|
|
70
|
+
return validation.validateManifest(data, rootType, schema, protocol, {
|
|
71
|
+
resolveFile,
|
|
72
|
+
});
|
|
70
73
|
}
|
|
71
74
|
static validateSchema(manifest, { resolveFile }) {
|
|
75
|
+
subgraphDebug.extend('validate')('Validating schema in manifest');
|
|
72
76
|
const filename = resolveFile(manifest.getIn(['schema', 'file']));
|
|
77
|
+
subgraphDebug.extend('validate')('Loaded schema from %s', filename);
|
|
73
78
|
const validationErrors = validation.validateSchema(filename);
|
|
74
79
|
let errors;
|
|
75
80
|
if (validationErrors.size > 0) {
|
|
81
|
+
subgraphDebug.extend('validate')('Schema validation failed for %s', filename);
|
|
76
82
|
errors = validationErrors.groupBy(error => error.get('entity')).sort();
|
|
77
83
|
const msg = errors.reduce((msg, errors, entity) => {
|
|
78
84
|
errors = errors.groupBy((error) => error.get('directive'));
|
|
@@ -94,6 +100,7 @@ class Subgraph {
|
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
static validateRepository(manifest) {
|
|
103
|
+
subgraphDebug.extend('validate')('Validating repository in manifest');
|
|
97
104
|
const repository = manifest.get('repository');
|
|
98
105
|
return /^https:\/\/github\.com\/graphprotocol\/graph-tooling?$/.test(repository) ||
|
|
99
106
|
// For legacy reasons, we should error on example subgraphs
|
|
@@ -107,6 +114,7 @@ Please replace it with a link to your subgraph source code.`,
|
|
|
107
114
|
: immutable_1.default.List();
|
|
108
115
|
}
|
|
109
116
|
static validateDescription(manifest) {
|
|
117
|
+
subgraphDebug.extend('validate')('Validating description in manifest');
|
|
110
118
|
// TODO: Maybe implement this in the future for each protocol example description
|
|
111
119
|
return manifest.get('description', '').startsWith('Gravatar for ')
|
|
112
120
|
? immutable_1.default.List().push(immutable_1.default.fromJS({
|
|
@@ -118,6 +126,7 @@ Please update it to tell users more about your subgraph.`,
|
|
|
118
126
|
: immutable_1.default.List();
|
|
119
127
|
}
|
|
120
128
|
static validateHandlers(manifest, protocol, protocolSubgraph) {
|
|
129
|
+
subgraphDebug.extend('validate')('Validating handlers for protocol %s', protocol?.name);
|
|
121
130
|
return manifest
|
|
122
131
|
.get('dataSources')
|
|
123
132
|
.filter((dataSource) => protocol.isValidKindName(dataSource.get('kind')))
|
|
@@ -125,7 +134,7 @@ Please update it to tell users more about your subgraph.`,
|
|
|
125
134
|
const path = ['dataSources', dataSourceIndex, 'mapping'];
|
|
126
135
|
const mapping = dataSource.get('mapping');
|
|
127
136
|
const handlerTypes = protocolSubgraph.handlerTypes();
|
|
128
|
-
subgraphDebug('Validating dataSource "%s" handlers with %d handlers types defined for protocol', dataSource.get('name'), handlerTypes.size);
|
|
137
|
+
subgraphDebug.extend('validate')('Validating dataSource "%s" handlers with %d handlers types defined for protocol', dataSource.get('name'), handlerTypes.size);
|
|
129
138
|
if (handlerTypes.size == 0) {
|
|
130
139
|
return errors;
|
|
131
140
|
}
|
|
@@ -144,13 +153,16 @@ At least one such handler must be defined.`,
|
|
|
144
153
|
}, immutable_1.default.List());
|
|
145
154
|
}
|
|
146
155
|
static validateContractValues(manifest, protocol) {
|
|
156
|
+
subgraphDebug.extend('validate')('Validating contract values for protocol %s', protocol?.name);
|
|
147
157
|
if (!protocol.hasContract()) {
|
|
158
|
+
subgraphDebug.extend('validate')('Protocol has no contract, skipping validation');
|
|
148
159
|
return immutable_1.default.List();
|
|
149
160
|
}
|
|
150
161
|
return validation.validateContractValues(manifest, protocol);
|
|
151
162
|
}
|
|
152
163
|
// Validate that data source names are unique, so they don't overwrite each other.
|
|
153
164
|
static validateUniqueDataSourceNames(manifest) {
|
|
165
|
+
subgraphDebug.extend('validate')('Validating that data source names are unique');
|
|
154
166
|
const names = [];
|
|
155
167
|
return manifest
|
|
156
168
|
.get('dataSources')
|
|
@@ -158,6 +170,7 @@ At least one such handler must be defined.`,
|
|
|
158
170
|
const path = ['dataSources', dataSourceIndex, 'name'];
|
|
159
171
|
const name = dataSource.get('name');
|
|
160
172
|
if (names.includes(name)) {
|
|
173
|
+
subgraphDebug.extend('validate')("Found duplicate data source name '%s', adding error");
|
|
161
174
|
errors = errors.push(immutable_1.default.fromJS({
|
|
162
175
|
path,
|
|
163
176
|
message: `\
|
|
@@ -169,6 +182,7 @@ More than one data source named '${name}', data source names must be unique.`,
|
|
|
169
182
|
}, immutable_1.default.List());
|
|
170
183
|
}
|
|
171
184
|
static validateUniqueTemplateNames(manifest) {
|
|
185
|
+
subgraphDebug.extend('validate')('Validating that template names are unique');
|
|
172
186
|
const names = [];
|
|
173
187
|
return manifest
|
|
174
188
|
.get('templates', immutable_1.default.List())
|
|
@@ -176,6 +190,7 @@ More than one data source named '${name}', data source names must be unique.`,
|
|
|
176
190
|
const path = ['templates', templateIndex, 'name'];
|
|
177
191
|
const name = template.get('name');
|
|
178
192
|
if (names.includes(name)) {
|
|
193
|
+
subgraphDebug.extend('validate')("Found duplicate template name '%s', adding error");
|
|
179
194
|
errors = errors.push(immutable_1.default.fromJS({
|
|
180
195
|
path,
|
|
181
196
|
message: `\
|
|
@@ -202,21 +217,30 @@ More than one template named '${name}', template names must be unique.`,
|
|
|
202
217
|
data = require(path_1.default.resolve(filename));
|
|
203
218
|
}
|
|
204
219
|
else {
|
|
220
|
+
subgraphDebug('Loading manifest from %s', filename);
|
|
205
221
|
const raw_data = await fs_extra_1.default.readFile(filename, 'utf-8');
|
|
222
|
+
subgraphDebug('Checking for file data sources in %s', filename);
|
|
206
223
|
has_file_data_sources = raw_data.includes('kind: file');
|
|
224
|
+
subgraphDebug('Parsing manifest from %s', filename);
|
|
207
225
|
data = yaml_1.default.parse(raw_data);
|
|
208
226
|
}
|
|
209
227
|
// Helper to resolve files relative to the subgraph manifest
|
|
210
228
|
const resolveFile = maybeRelativeFile => path_1.default.resolve(path_1.default.dirname(filename), maybeRelativeFile);
|
|
211
229
|
// TODO: Validation for file data sources
|
|
212
230
|
if (!has_file_data_sources) {
|
|
213
|
-
|
|
231
|
+
subgraphDebug('Validating manifest from %s', filename);
|
|
232
|
+
const manifestErrors = await Subgraph.validate(data, protocol, {
|
|
233
|
+
resolveFile,
|
|
234
|
+
});
|
|
214
235
|
if (manifestErrors.size > 0) {
|
|
236
|
+
subgraphDebug('Manifest validation failed for %s', filename);
|
|
215
237
|
throwCombinedError(filename, manifestErrors);
|
|
216
238
|
}
|
|
217
239
|
}
|
|
218
240
|
const manifest = immutable_1.default.fromJS(data);
|
|
241
|
+
subgraphDebug.extend('manifest')('Loaded: %M', manifest);
|
|
219
242
|
// Validate the schema
|
|
243
|
+
subgraphDebug.extend('manifest')('Validating schema');
|
|
220
244
|
Subgraph.validateSchema(manifest, { resolveFile });
|
|
221
245
|
// Perform other validations
|
|
222
246
|
const protocolSubgraph = protocol.getSubgraph({
|
|
@@ -30,6 +30,8 @@ exports.validateSchema = exports.typeSuggestion = void 0;
|
|
|
30
30
|
const fs_1 = __importDefault(require("fs"));
|
|
31
31
|
const graphql = __importStar(require("graphql/language"));
|
|
32
32
|
const immutable_1 = __importDefault(require("immutable"));
|
|
33
|
+
const debug_1 = __importDefault(require("../debug"));
|
|
34
|
+
const validateDebugger = (0, debug_1.default)('graph-cli:validation');
|
|
33
35
|
const List = immutable_1.default.List;
|
|
34
36
|
const Set = immutable_1.default.Set;
|
|
35
37
|
// Builtin scalar types
|
|
@@ -101,18 +103,23 @@ const parseSchema = (doc) => {
|
|
|
101
103
|
throw new Error(`Invalid GraphQL schema: ${e}`);
|
|
102
104
|
}
|
|
103
105
|
};
|
|
104
|
-
const validateEntityDirective = (def) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
const validateEntityDirective = (def) => {
|
|
107
|
+
validateDebugger('Validating entity directive for %s', def?.name?.value);
|
|
108
|
+
return def.directives.find((directive) => directive.name.value === 'entity')
|
|
109
|
+
? List()
|
|
110
|
+
: immutable_1.default.fromJS([
|
|
111
|
+
{
|
|
112
|
+
loc: def.loc,
|
|
113
|
+
entity: def.name.value,
|
|
114
|
+
message: `Defined without @entity directive`,
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
};
|
|
113
118
|
const validateEntityID = (def) => {
|
|
119
|
+
validateDebugger('Validating entity ID for %s', def?.name?.value);
|
|
114
120
|
const idField = def.fields.find((field) => field.name.value === 'id');
|
|
115
121
|
if (idField === undefined) {
|
|
122
|
+
validateDebugger('Entity %s has no id field', def?.name?.value);
|
|
116
123
|
return immutable_1.default.fromJS([
|
|
117
124
|
{
|
|
118
125
|
loc: def.loc,
|
|
@@ -126,8 +133,10 @@ const validateEntityID = (def) => {
|
|
|
126
133
|
(idField.type.type.name.value === 'ID' ||
|
|
127
134
|
idField.type.type.name.value === 'Bytes' ||
|
|
128
135
|
idField.type.type.name.value === 'String')) {
|
|
136
|
+
validateDebugger('Entity %s has valid id field', def?.name?.value);
|
|
129
137
|
return List();
|
|
130
138
|
}
|
|
139
|
+
validateDebugger('Entity %s has invalid id field', def?.name?.value);
|
|
131
140
|
return immutable_1.default.fromJS([
|
|
132
141
|
{
|
|
133
142
|
loc: idField.loc,
|
|
@@ -136,52 +145,80 @@ const validateEntityID = (def) => {
|
|
|
136
145
|
},
|
|
137
146
|
]);
|
|
138
147
|
};
|
|
139
|
-
const validateListFieldType = (def, field) =>
|
|
140
|
-
field
|
|
141
|
-
field.type.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
loc: field.loc,
|
|
145
|
-
entity: def.name.value,
|
|
146
|
-
message: `\
|
|
147
|
-
Field '${field.name.value}':
|
|
148
|
-
Field has type [${field.type.type.name.value}]! but
|
|
149
|
-
must have type [${field.type.type.name.value}!]!
|
|
150
|
-
|
|
151
|
-
Reason: Lists with null elements are not supported.`,
|
|
152
|
-
},
|
|
153
|
-
])
|
|
154
|
-
: field.type.kind === 'ListType' && field.type.type.kind !== 'NonNullType'
|
|
148
|
+
const validateListFieldType = (def, field) => {
|
|
149
|
+
validateDebugger('Validating list field type for %s', def?.name?.value);
|
|
150
|
+
return field.type.kind === 'NonNullType' &&
|
|
151
|
+
field.type.kind === 'ListType' &&
|
|
152
|
+
field.type.type.kind !== 'NonNullType'
|
|
155
153
|
? immutable_1.default.fromJS([
|
|
156
154
|
{
|
|
157
155
|
loc: field.loc,
|
|
158
156
|
entity: def.name.value,
|
|
159
157
|
message: `\
|
|
160
158
|
Field '${field.name.value}':
|
|
161
|
-
Field has type [${field.type.type.name.value}] but
|
|
162
|
-
must have type [${field.type.type.name.value}!]
|
|
159
|
+
Field has type [${field.type.type.name.value}]! but
|
|
160
|
+
must have type [${field.type.type.name.value}!]!
|
|
163
161
|
|
|
164
162
|
Reason: Lists with null elements are not supported.`,
|
|
165
163
|
},
|
|
166
164
|
])
|
|
167
|
-
:
|
|
165
|
+
: field.type.kind === 'ListType' && field.type.type.kind !== 'NonNullType'
|
|
166
|
+
? immutable_1.default.fromJS([
|
|
167
|
+
{
|
|
168
|
+
loc: field.loc,
|
|
169
|
+
entity: def.name.value,
|
|
170
|
+
message: `\
|
|
171
|
+
Field '${field.name.value}':
|
|
172
|
+
Field has type [${field.type.type.name.value}] but
|
|
173
|
+
must have type [${field.type.type.name.value}!]
|
|
174
|
+
|
|
175
|
+
Reason: Lists with null elements are not supported.`,
|
|
176
|
+
},
|
|
177
|
+
])
|
|
178
|
+
: List();
|
|
179
|
+
};
|
|
168
180
|
const unwrapType = (type) => {
|
|
169
|
-
|
|
170
|
-
const
|
|
181
|
+
validateDebugger.extend('definition')('Unwrapping type %M', type);
|
|
182
|
+
const innerTypeFromList = (listType) => {
|
|
183
|
+
validateDebugger.extend('unwrapType').extend('definition')('Unwrapping list type %M', listType);
|
|
184
|
+
if (listType.type.kind === 'NonNullType') {
|
|
185
|
+
validateDebugger.extend('unwrapType').extend('innerTypeFromList')('Returning non-null list type');
|
|
186
|
+
return innerTypeFromNonNull(listType.type);
|
|
187
|
+
}
|
|
188
|
+
validateDebugger.extend('unwrapType').extend('innerTypeFromList')('Returning list type');
|
|
189
|
+
return unwrapType(listType.type);
|
|
190
|
+
};
|
|
191
|
+
const innerTypeFromNonNull = (nonNullType) => {
|
|
192
|
+
validateDebugger.extend('unwrapType').extend('definition')('Unwrapping non-null type %M', nonNullType);
|
|
193
|
+
if (nonNullType.type.kind === 'ListType') {
|
|
194
|
+
validateDebugger.extend('unwrapType').extend('innerTypeFromNonNull')('Returning non-null list type');
|
|
195
|
+
return innerTypeFromList(nonNullType.type);
|
|
196
|
+
}
|
|
197
|
+
validateDebugger.extend('unwrapType').extend('innerTypeFromNonNull')('Returning non-null type');
|
|
198
|
+
return unwrapType(nonNullType.type);
|
|
199
|
+
};
|
|
171
200
|
// Obtain the inner-most type from the field
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
201
|
+
if (type.kind === 'NonNullType') {
|
|
202
|
+
validateDebugger('Returning inner type from non-null type');
|
|
203
|
+
return innerTypeFromNonNull(type);
|
|
204
|
+
}
|
|
205
|
+
if (type.kind === 'ListType') {
|
|
206
|
+
validateDebugger('Returning inner type from list type');
|
|
207
|
+
return innerTypeFromList(type);
|
|
208
|
+
}
|
|
209
|
+
validateDebugger('Returning inner type');
|
|
210
|
+
return type;
|
|
211
|
+
};
|
|
212
|
+
const gatherLocalTypes = (defs) => {
|
|
213
|
+
validateDebugger('Gathering local types');
|
|
214
|
+
return defs
|
|
215
|
+
.filter(def => def.kind === 'ObjectTypeDefinition' ||
|
|
216
|
+
def.kind === 'EnumTypeDefinition' ||
|
|
217
|
+
def.kind === 'InterfaceTypeDefinition')
|
|
218
|
+
.map(def =>
|
|
219
|
+
// @ts-expect-error TODO: name field does not exist on definition, really?
|
|
220
|
+
def.name.value);
|
|
221
|
+
};
|
|
185
222
|
const gatherImportedTypes = (defs) => defs
|
|
186
223
|
.filter(def => def.kind === 'ObjectTypeDefinition' &&
|
|
187
224
|
def.name.value == RESERVED_TYPE &&
|
|
@@ -206,19 +243,28 @@ def.directives
|
|
|
206
243
|
}
|
|
207
244
|
return flattened;
|
|
208
245
|
}, [])), []);
|
|
209
|
-
const entityTypeByName = (defs, name) =>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
246
|
+
const entityTypeByName = (defs, name) => {
|
|
247
|
+
validateDebugger('Looking up entity type %s', name);
|
|
248
|
+
return defs
|
|
249
|
+
.filter(def => def.kind === 'InterfaceTypeDefinition' ||
|
|
250
|
+
(def.kind === 'ObjectTypeDefinition' &&
|
|
251
|
+
def.directives.find((directive) => directive.name.value === 'entity')))
|
|
252
|
+
.find(def => def.name.value === name);
|
|
253
|
+
};
|
|
254
|
+
const fieldTargetEntityName = (field) => {
|
|
255
|
+
validateDebugger('Looking up field target entity name for %s', field?.name?.value);
|
|
256
|
+
return unwrapType(field.type).name.value;
|
|
257
|
+
};
|
|
215
258
|
const fieldTargetEntity = (defs, field) => entityTypeByName(defs, fieldTargetEntityName(field));
|
|
216
259
|
const validateInnerFieldType = (defs, def, field) => {
|
|
260
|
+
validateDebugger('Validating inner field type for %s', def?.name?.value);
|
|
217
261
|
const innerType = unwrapType(field.type);
|
|
218
262
|
// Get the name of the type
|
|
219
263
|
const typeName = innerType.name.value;
|
|
264
|
+
validateDebugger('Inner field type name: %s', typeName);
|
|
220
265
|
// Look up a possible suggestion for the type to catch common mistakes
|
|
221
266
|
const suggestion = (0, exports.typeSuggestion)(typeName);
|
|
267
|
+
validateDebugger('Inner field type suggestion: %s', suggestion);
|
|
222
268
|
// Collect all types that we can use here: built-ins + entities + enums + interfaces
|
|
223
269
|
const availableTypes = List.of(...BUILTIN_SCALAR_TYPES, ...gatherLocalTypes(defs), ...gatherImportedTypes(defs));
|
|
224
270
|
// Check whether the type name is available, otherwise return an error
|
|
@@ -292,6 +338,7 @@ does not exist on type '${targetEntity.name.value}'`,
|
|
|
292
338
|
]);
|
|
293
339
|
}
|
|
294
340
|
const backrefTypeName = unwrapType(derivedFromField.type);
|
|
341
|
+
validateDebugger.extend('definition')('Backref type name: %M', backrefTypeName);
|
|
295
342
|
const backRefEntity = entityTypeByName(defs, backrefTypeName.name.value);
|
|
296
343
|
// The field we are deriving from must either have type 'def' or one of the
|
|
297
344
|
// interface types that 'def' is implementing
|
|
@@ -682,6 +729,7 @@ const validateImportDirectiveArgumentFrom = (def, directive, argument) => {
|
|
|
682
729
|
}, List());
|
|
683
730
|
};
|
|
684
731
|
const validateImportDirectiveFields = (def, directive) => {
|
|
732
|
+
validateDebugger('Validating import directive fields: %s', def?.name?.value);
|
|
685
733
|
return directive.arguments.reduce((errors, argument) => {
|
|
686
734
|
return errors.concat(['types', 'from'].includes(argument.name.value)
|
|
687
735
|
? List([])
|
|
@@ -695,6 +743,7 @@ const validateImportDirectiveFields = (def, directive) => {
|
|
|
695
743
|
}, List([]));
|
|
696
744
|
};
|
|
697
745
|
const validateImportDirectiveTypes = (def, directive) => {
|
|
746
|
+
validateDebugger('Validating import directive types: %s', def?.name?.value);
|
|
698
747
|
const types = directive.arguments.find((argument) => argument.name.value == 'types');
|
|
699
748
|
return types
|
|
700
749
|
? validateImportDirectiveArgumentTypes(def, directive, types)
|
|
@@ -707,6 +756,7 @@ const validateImportDirectiveTypes = (def, directive) => {
|
|
|
707
756
|
]);
|
|
708
757
|
};
|
|
709
758
|
const validateImportDirectiveFrom = (def, directive) => {
|
|
759
|
+
validateDebugger('Validating import directive from: %s', def?.name?.value);
|
|
710
760
|
const from = directive.arguments.find((argument) => argument.name.value == 'from');
|
|
711
761
|
return from
|
|
712
762
|
? validateImportDirectiveArgumentFrom(def, directive, from)
|
|
@@ -718,13 +768,19 @@ const validateImportDirectiveFrom = (def, directive) => {
|
|
|
718
768
|
}),
|
|
719
769
|
]);
|
|
720
770
|
};
|
|
721
|
-
const validateImportDirective = (def, directive) =>
|
|
771
|
+
const validateImportDirective = (def, directive) => {
|
|
772
|
+
validateDebugger('Validating import directive: %s', def?.name?.value);
|
|
773
|
+
return List.of(...validateImportDirectiveFields(def, directive), ...validateImportDirectiveTypes(def, directive), ...validateImportDirectiveFrom(def, directive));
|
|
774
|
+
};
|
|
722
775
|
const validateFulltext = (def, directive) => List.of(...validateFulltextFields(def, directive), ...validateFulltextName(def, directive), ...validateFulltextLanguage(def, directive), ...validateFulltextAlgorithm(def, directive), ...validateFulltextInclude(def, directive));
|
|
723
776
|
const validateSubgraphSchemaDirective = (def, directive) => {
|
|
777
|
+
validateDebugger('Validating subgraph schema directive: %s', def?.name?.value);
|
|
724
778
|
if (directive.name.value == 'import') {
|
|
779
|
+
validateDebugger('Validating import directive: %s', def?.name?.value);
|
|
725
780
|
return validateImportDirective(def, directive);
|
|
726
781
|
}
|
|
727
782
|
if (directive.name.value == 'fulltext') {
|
|
783
|
+
validateDebugger('Validating fulltext directive: %s', def?.name?.value);
|
|
728
784
|
return validateFulltext(def, directive);
|
|
729
785
|
}
|
|
730
786
|
return List([
|
|
@@ -735,32 +791,52 @@ const validateSubgraphSchemaDirective = (def, directive) => {
|
|
|
735
791
|
}),
|
|
736
792
|
]);
|
|
737
793
|
};
|
|
738
|
-
const validateSubgraphSchemaDirectives = (def) =>
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
794
|
+
const validateSubgraphSchemaDirectives = (def) => {
|
|
795
|
+
validateDebugger('Validating subgraph schema directives: %s', def?.name?.value);
|
|
796
|
+
return def.directives.reduce((errors, directive) => errors.concat(validateSubgraphSchemaDirective(def, directive)), List());
|
|
797
|
+
};
|
|
798
|
+
const validateTypeHasNoFields = (def) => {
|
|
799
|
+
validateDebugger('Validating type has no fields: %s', def?.name?.value);
|
|
800
|
+
return def.fields.length
|
|
801
|
+
? List([
|
|
802
|
+
immutable_1.default.fromJS({
|
|
803
|
+
loc: def.name.loc,
|
|
804
|
+
entity: def.name.value,
|
|
805
|
+
message: `${def.name.value} type is not allowed any fields by convention`,
|
|
806
|
+
}),
|
|
807
|
+
])
|
|
808
|
+
: List();
|
|
809
|
+
};
|
|
748
810
|
const validateAtLeastOneExtensionField = (_def) => List();
|
|
749
811
|
const typeDefinitionValidators = {
|
|
750
|
-
ObjectTypeDefinition: (defs, def) =>
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
812
|
+
ObjectTypeDefinition: (defs, def) => {
|
|
813
|
+
validateDebugger('Validating object type definition: %s', def?.name?.value);
|
|
814
|
+
return def.name && def.name.value == RESERVED_TYPE
|
|
815
|
+
? List.of(...validateSubgraphSchemaDirectives(def), ...validateTypeHasNoFields(def))
|
|
816
|
+
: List.of(...validateEntityDirective(def), ...validateEntityID(def), ...validateEntityFields(defs, def), ...validateNoImportDirective(def), ...validateNoFulltext(def));
|
|
817
|
+
},
|
|
818
|
+
ObjectTypeExtension: (_defs, def) => {
|
|
819
|
+
validateDebugger('Validating object type extension: %s', def?.name?.value);
|
|
820
|
+
return validateAtLeastOneExtensionField(def);
|
|
821
|
+
},
|
|
822
|
+
};
|
|
823
|
+
const validateTypeDefinition = (defs, def) => {
|
|
824
|
+
validateDebugger.extend('definition')('Validating type definition: %M', def);
|
|
825
|
+
return typeDefinitionValidators[def.kind] === undefined
|
|
826
|
+
? List()
|
|
827
|
+
: typeDefinitionValidators[def.kind](defs, def);
|
|
828
|
+
};
|
|
829
|
+
const validateTypeDefinitions = (defs) => {
|
|
830
|
+
validateDebugger('Validating type definitions');
|
|
831
|
+
return defs.reduce((errors, def) => errors.concat(validateTypeDefinition(defs, def)), List());
|
|
832
|
+
};
|
|
759
833
|
const validateNamingCollisionsInTypes = (types) => {
|
|
834
|
+
validateDebugger('Validating naming collisions in types');
|
|
760
835
|
let seen = Set();
|
|
761
836
|
let conflicting = Set();
|
|
762
837
|
return types.reduce((errors, type) => {
|
|
763
838
|
if (seen.has(type) && !conflicting.has(type)) {
|
|
839
|
+
validateDebugger('Found naming collision');
|
|
764
840
|
errors = errors.push(immutable_1.default.fromJS({
|
|
765
841
|
loc: { start: 1, end: 1 },
|
|
766
842
|
entity: type,
|
|
@@ -774,10 +850,16 @@ const validateNamingCollisionsInTypes = (types) => {
|
|
|
774
850
|
return errors;
|
|
775
851
|
}, List());
|
|
776
852
|
};
|
|
777
|
-
const validateNamingCollisions = (local, imported) =>
|
|
853
|
+
const validateNamingCollisions = (local, imported) => {
|
|
854
|
+
validateDebugger('Validating naming collisions');
|
|
855
|
+
return validateNamingCollisionsInTypes(local.concat(imported));
|
|
856
|
+
};
|
|
778
857
|
const validateSchema = (filename) => {
|
|
858
|
+
validateDebugger('Validating schema: %s', filename);
|
|
779
859
|
const doc = loadSchema(filename);
|
|
860
|
+
validateDebugger('Loaded schema: %s', filename);
|
|
780
861
|
const schema = parseSchema(doc);
|
|
862
|
+
validateDebugger.extend('schema')('Parsed schema: %M', schema);
|
|
781
863
|
return List.of(...validateTypeDefinitions(schema.definitions), ...validateNamingCollisions(gatherLocalTypes(schema.definitions), gatherImportedTypes(schema.definitions)));
|
|
782
864
|
};
|
|
783
865
|
exports.validateSchema = validateSchema;
|
package/oclif.manifest.json
CHANGED