@graphql-mesh/grpc 1.0.0-alpha-20230424111644-b7c761da0 → 1.0.0-alpha-20230522105300-fe9c79867
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/cjs/directives.js +61 -0
- package/cjs/index.js +228 -126
- package/esm/directives.js +58 -0
- package/esm/index.js +229 -127
- package/package.json +8 -7
- package/typings/directives.d.cts +5 -0
- package/typings/directives.d.ts +5 -0
- package/typings/index.d.cts +44 -13
- package/typings/index.d.ts +44 -13
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.grpcRootJsonDirective = exports.ObjMapScalar = exports.grpcConnectivityStateDirective = exports.grpcMethodDirective = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
exports.grpcMethodDirective = new graphql_1.GraphQLDirective({
|
|
6
|
+
name: 'grpcMethod',
|
|
7
|
+
locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION],
|
|
8
|
+
args: {
|
|
9
|
+
rootJsonName: {
|
|
10
|
+
type: graphql_1.GraphQLString,
|
|
11
|
+
},
|
|
12
|
+
objPath: {
|
|
13
|
+
type: graphql_1.GraphQLString,
|
|
14
|
+
},
|
|
15
|
+
methodName: {
|
|
16
|
+
type: graphql_1.GraphQLString,
|
|
17
|
+
},
|
|
18
|
+
responseStream: {
|
|
19
|
+
type: graphql_1.GraphQLBoolean,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
exports.grpcConnectivityStateDirective = new graphql_1.GraphQLDirective({
|
|
24
|
+
name: 'grpcConnectivityState',
|
|
25
|
+
locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION],
|
|
26
|
+
args: {
|
|
27
|
+
rootJsonName: {
|
|
28
|
+
type: graphql_1.GraphQLString,
|
|
29
|
+
},
|
|
30
|
+
objPath: {
|
|
31
|
+
type: graphql_1.GraphQLString,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
exports.ObjMapScalar = new graphql_1.GraphQLScalarType({
|
|
36
|
+
name: 'ObjMap',
|
|
37
|
+
serialize: value => JSON.stringify(value),
|
|
38
|
+
parseValue: value => JSON.parse(value.toString()),
|
|
39
|
+
parseLiteral: ast => {
|
|
40
|
+
if (ast.kind === 'StringValue') {
|
|
41
|
+
return JSON.parse(ast.value);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
exports.grpcRootJsonDirective = new graphql_1.GraphQLDirective({
|
|
47
|
+
name: 'grpcRootJson',
|
|
48
|
+
locations: [graphql_1.DirectiveLocation.OBJECT],
|
|
49
|
+
args: {
|
|
50
|
+
name: {
|
|
51
|
+
type: graphql_1.GraphQLString,
|
|
52
|
+
},
|
|
53
|
+
rootJson: {
|
|
54
|
+
type: exports.ObjMapScalar,
|
|
55
|
+
},
|
|
56
|
+
loadOptions: {
|
|
57
|
+
type: exports.ObjMapScalar,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
isRepeatable: true,
|
|
61
|
+
});
|
package/cjs/index.js
CHANGED
|
@@ -13,49 +13,27 @@ const index_js_1 = tslib_1.__importDefault(require("protobufjs/ext/descriptor/in
|
|
|
13
13
|
const grpc_reflection_js_1 = require("@ardatan/grpc-reflection-js");
|
|
14
14
|
const cross_helpers_1 = require("@graphql-mesh/cross-helpers");
|
|
15
15
|
const cross_helpers_2 = require("@graphql-mesh/cross-helpers");
|
|
16
|
+
const store_1 = require("@graphql-mesh/store");
|
|
16
17
|
const string_interpolation_1 = require("@graphql-mesh/string-interpolation");
|
|
18
|
+
const utils_1 = require("@graphql-mesh/utils");
|
|
19
|
+
const utils_2 = require("@graphql-tools/utils");
|
|
17
20
|
const grpc_js_1 = require("@grpc/grpc-js");
|
|
18
21
|
const proto_loader_1 = require("@grpc/proto-loader");
|
|
22
|
+
const directives_js_1 = require("./directives.js");
|
|
19
23
|
require("./patchLongJs.js");
|
|
20
24
|
const utils_js_1 = require("./utils.js");
|
|
21
25
|
const { Root } = protobufjs_1.default;
|
|
22
26
|
const QUERY_METHOD_PREFIXES = ['get', 'list', 'search'];
|
|
23
27
|
class GrpcHandler {
|
|
24
|
-
constructor({ config, baseDir, store, logger, pubsub, }) {
|
|
28
|
+
constructor({ config, baseDir, store, logger, pubsub, importFn, }) {
|
|
29
|
+
this.grpcObjectByserviceClientByObjPath = new WeakMap();
|
|
25
30
|
this.schemaComposer = new graphql_compose_1.SchemaComposer();
|
|
26
31
|
this.logger = logger;
|
|
27
32
|
this.config = config;
|
|
28
33
|
this.baseDir = baseDir;
|
|
29
|
-
this.
|
|
30
|
-
codify: rootJsonEntries => `
|
|
31
|
-
export default [
|
|
32
|
-
${rootJsonEntries
|
|
33
|
-
.map(({ name, rootJson }) => `
|
|
34
|
-
{
|
|
35
|
-
name: ${JSON.stringify(name)},
|
|
36
|
-
rootJson: ${JSON.stringify(rootJson, null, 2)},
|
|
37
|
-
},
|
|
38
|
-
`)
|
|
39
|
-
.join('\n')}
|
|
40
|
-
];
|
|
41
|
-
`.trim(),
|
|
42
|
-
fromJSON: jsonData => {
|
|
43
|
-
return jsonData.map(({ name, rootJson }) => ({
|
|
44
|
-
name,
|
|
45
|
-
rootJson,
|
|
46
|
-
}));
|
|
47
|
-
},
|
|
48
|
-
toJSON: rootJsonEntries => {
|
|
49
|
-
return rootJsonEntries.map(({ name, rootJson }) => {
|
|
50
|
-
return {
|
|
51
|
-
name,
|
|
52
|
-
rootJson,
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
},
|
|
56
|
-
validate: () => { },
|
|
57
|
-
});
|
|
34
|
+
this.schemaWithAnnotationsProxy = store.proxy('schemaWithAnnotations', store_1.PredefinedProxyOptions.GraphQLSchemaWithDiffing);
|
|
58
35
|
this.pubsub = pubsub;
|
|
36
|
+
this.importFn = importFn;
|
|
59
37
|
}
|
|
60
38
|
async processReflection(creds) {
|
|
61
39
|
this.logger.debug(`Using the reflection`);
|
|
@@ -147,38 +125,36 @@ ${rootJsonEntries
|
|
|
147
125
|
this.logger.debug(`Adding proto content to the root`);
|
|
148
126
|
return protoRoot;
|
|
149
127
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
rootPromises.push(this.processDescriptorFile());
|
|
158
|
-
}
|
|
159
|
-
else if (filePath.endsWith('proto')) {
|
|
160
|
-
rootPromises.push(this.processProtoFile());
|
|
161
|
-
}
|
|
128
|
+
async getDescriptorSets(creds) {
|
|
129
|
+
const rootPromises = [];
|
|
130
|
+
this.logger.debug(`Building Roots`);
|
|
131
|
+
if (this.config.source) {
|
|
132
|
+
const filePath = typeof this.config.source === 'string' ? this.config.source : this.config.source.file;
|
|
133
|
+
if (filePath.endsWith('json')) {
|
|
134
|
+
rootPromises.push(this.processDescriptorFile());
|
|
162
135
|
}
|
|
163
|
-
else {
|
|
164
|
-
|
|
165
|
-
rootPromises.push(...reflectionPromises);
|
|
136
|
+
else if (filePath.endsWith('proto')) {
|
|
137
|
+
rootPromises.push(this.processProtoFile());
|
|
166
138
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const reflectionPromises = await this.processReflection(creds);
|
|
142
|
+
rootPromises.push(...reflectionPromises);
|
|
143
|
+
}
|
|
144
|
+
return Promise.all(rootPromises.map(async (root$, i) => {
|
|
145
|
+
const root = await root$;
|
|
146
|
+
const rootName = root.name || `Root${i}`;
|
|
147
|
+
const rootLogger = this.logger.child(rootName);
|
|
148
|
+
rootLogger.debug(`Resolving entire the root tree`);
|
|
149
|
+
root.resolveAll();
|
|
150
|
+
rootLogger.debug(`Creating artifacts from descriptor set and root`);
|
|
151
|
+
return {
|
|
152
|
+
name: rootName,
|
|
153
|
+
rootJson: root.toJSON({
|
|
154
|
+
keepComments: true,
|
|
155
|
+
}),
|
|
156
|
+
};
|
|
157
|
+
}));
|
|
182
158
|
}
|
|
183
159
|
async getCredentials() {
|
|
184
160
|
if (this.config.credentialsSsl) {
|
|
@@ -219,8 +195,102 @@ ${rootJsonEntries
|
|
|
219
195
|
}
|
|
220
196
|
return currentWalkingPath.concat(baseTypePath);
|
|
221
197
|
}
|
|
222
|
-
|
|
198
|
+
getGrpcObject({ rootJson, loadOptions, rootLogger, }) {
|
|
199
|
+
const packageDefinition = (0, proto_loader_1.fromJSON)(rootJson, loadOptions);
|
|
200
|
+
rootLogger.debug(`Creating service client for package definition`);
|
|
201
|
+
const grpcObject = (0, grpc_js_1.loadPackageDefinition)(packageDefinition);
|
|
202
|
+
return grpcObject;
|
|
203
|
+
}
|
|
204
|
+
getServiceClient({ grpcObject, objPath, creds, }) {
|
|
223
205
|
var _a;
|
|
206
|
+
let serviceClientByObjPath = this.grpcObjectByserviceClientByObjPath.get(grpcObject);
|
|
207
|
+
if (!serviceClientByObjPath) {
|
|
208
|
+
serviceClientByObjPath = new Map();
|
|
209
|
+
this.grpcObjectByserviceClientByObjPath.set(grpcObject, serviceClientByObjPath);
|
|
210
|
+
}
|
|
211
|
+
let client = serviceClientByObjPath.get(objPath);
|
|
212
|
+
if (!client) {
|
|
213
|
+
const ServiceClient = (0, lodash_get_1.default)(grpcObject, objPath);
|
|
214
|
+
if (typeof ServiceClient !== 'function') {
|
|
215
|
+
throw new Error(`Object at path ${objPath} is not a Service constructor`);
|
|
216
|
+
}
|
|
217
|
+
client = new ServiceClient((_a = string_interpolation_1.stringInterpolator.parse(this.config.endpoint, { env: cross_helpers_1.process.env })) !== null && _a !== void 0 ? _a : this.config.endpoint, creds);
|
|
218
|
+
const subId = this.pubsub.subscribe('destroy', () => {
|
|
219
|
+
client.close();
|
|
220
|
+
this.pubsub.unsubscribe(subId);
|
|
221
|
+
});
|
|
222
|
+
serviceClientByObjPath.set(objPath, client);
|
|
223
|
+
}
|
|
224
|
+
return client;
|
|
225
|
+
}
|
|
226
|
+
getFieldResolver({ client, methodName, isResponseStream, }) {
|
|
227
|
+
const metaData = this.config.metaData;
|
|
228
|
+
const clientMethod = client[methodName].bind(client);
|
|
229
|
+
return function grpcFieldResolver(root, args, context) {
|
|
230
|
+
return (0, utils_js_1.addMetaDataToCall)(clientMethod, args.input, {
|
|
231
|
+
root,
|
|
232
|
+
args,
|
|
233
|
+
context,
|
|
234
|
+
env: cross_helpers_1.process.env,
|
|
235
|
+
}, metaData, isResponseStream);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
getConnectivityStateResolver({ client, }) {
|
|
239
|
+
return function connectivityStateResolver(_, { tryToConnect }) {
|
|
240
|
+
return client.getChannel().getConnectivityState(tryToConnect);
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
processDirectives({ schema, creds }) {
|
|
244
|
+
const queryType = schema.getQueryType();
|
|
245
|
+
const rootJsonAnnotations = (0, utils_2.getDirective)(schema, queryType, 'grpcRootJson');
|
|
246
|
+
const rootJsonMap = new Map();
|
|
247
|
+
const grpcObjectByRootJsonName = new Map();
|
|
248
|
+
for (const { name, rootJson, loadOptions } of rootJsonAnnotations) {
|
|
249
|
+
rootJsonMap.set(name, rootJson);
|
|
250
|
+
const rootLogger = this.logger.child(name);
|
|
251
|
+
grpcObjectByRootJsonName.set(name, this.getGrpcObject({ rootJson, loadOptions, rootLogger }));
|
|
252
|
+
}
|
|
253
|
+
const rootTypes = (0, utils_2.getRootTypes)(schema);
|
|
254
|
+
for (const rootType of rootTypes) {
|
|
255
|
+
for (const fieldName in rootType.getFields()) {
|
|
256
|
+
const field = rootType.getFields()[fieldName];
|
|
257
|
+
const directives = (0, utils_2.getDirectives)(schema, field);
|
|
258
|
+
if (directives === null || directives === void 0 ? void 0 : directives.length) {
|
|
259
|
+
for (const directiveObj of directives) {
|
|
260
|
+
switch (directiveObj.name) {
|
|
261
|
+
case 'grpcMethod': {
|
|
262
|
+
const { rootJsonName, objPath, methodName, responseStream } = directiveObj.args;
|
|
263
|
+
const grpcObject = grpcObjectByRootJsonName.get(rootJsonName);
|
|
264
|
+
const client = this.getServiceClient({
|
|
265
|
+
grpcObject,
|
|
266
|
+
objPath,
|
|
267
|
+
creds,
|
|
268
|
+
});
|
|
269
|
+
field.resolve = this.getFieldResolver({
|
|
270
|
+
client,
|
|
271
|
+
methodName,
|
|
272
|
+
isResponseStream: responseStream,
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case 'grpcConnectivityState': {
|
|
277
|
+
const { rootJsonName, objPath } = directiveObj.args;
|
|
278
|
+
const grpcObject = grpcObjectByRootJsonName.get(rootJsonName);
|
|
279
|
+
const client = this.getServiceClient({
|
|
280
|
+
grpcObject,
|
|
281
|
+
objPath,
|
|
282
|
+
creds,
|
|
283
|
+
});
|
|
284
|
+
field.resolve = this.getConnectivityStateResolver({ client });
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
visit({ nested, name, currentPath, rootJsonName, rootJson, rootLogger: logger, loadOptions, }) {
|
|
224
294
|
const pathWithName = [...currentPath, ...name.split('.')].filter(Boolean);
|
|
225
295
|
if ('nested' in nested) {
|
|
226
296
|
for (const key in nested.nested) {
|
|
@@ -230,9 +300,8 @@ ${rootJsonEntries
|
|
|
230
300
|
nested: currentNested,
|
|
231
301
|
name: key,
|
|
232
302
|
currentPath: pathWithName,
|
|
303
|
+
rootJsonName,
|
|
233
304
|
rootJson,
|
|
234
|
-
creds,
|
|
235
|
-
grpcObject,
|
|
236
305
|
rootLogger: logger,
|
|
237
306
|
});
|
|
238
307
|
}
|
|
@@ -322,15 +391,6 @@ ${rootJsonEntries
|
|
|
322
391
|
}
|
|
323
392
|
else if ('methods' in nested) {
|
|
324
393
|
const objPath = pathWithName.join('.');
|
|
325
|
-
const ServiceClient = (0, lodash_get_1.default)(grpcObject, objPath);
|
|
326
|
-
if (typeof ServiceClient !== 'function') {
|
|
327
|
-
throw new Error(`Object at path ${objPath} is not a Service constructor`);
|
|
328
|
-
}
|
|
329
|
-
const client = new ServiceClient((_a = string_interpolation_1.stringInterpolator.parse(this.config.endpoint, { env: cross_helpers_1.process.env })) !== null && _a !== void 0 ? _a : this.config.endpoint, creds);
|
|
330
|
-
const subId = this.pubsub.subscribe('destroy', () => {
|
|
331
|
-
client.close();
|
|
332
|
-
this.pubsub.unsubscribe(subId);
|
|
333
|
-
});
|
|
334
394
|
for (const methodName in nested.methods) {
|
|
335
395
|
const method = nested.methods[methodName];
|
|
336
396
|
const rootFieldName = [...pathWithName, methodName].join('_');
|
|
@@ -370,19 +430,26 @@ ${rootJsonEntries
|
|
|
370
430
|
const rootTypeComposer = prefixQueryMethod.some(prefix => methodNameLowerCased.startsWith(prefix))
|
|
371
431
|
? this.schemaComposer.Query
|
|
372
432
|
: this.schemaComposer.Mutation;
|
|
433
|
+
this.schemaComposer.addDirective(directives_js_1.grpcMethodDirective);
|
|
373
434
|
rootTypeComposer.addFields({
|
|
374
435
|
[rootFieldName]: {
|
|
375
436
|
...fieldConfig,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
437
|
+
directives: [
|
|
438
|
+
{
|
|
439
|
+
name: 'grpcMethod',
|
|
440
|
+
args: {
|
|
441
|
+
rootJsonName,
|
|
442
|
+
objPath,
|
|
443
|
+
methodName,
|
|
444
|
+
responseStream: !!method.responseStream,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
],
|
|
382
448
|
},
|
|
383
449
|
});
|
|
384
450
|
}
|
|
385
451
|
const connectivityStateFieldName = pathWithName.join('_') + '_connectivityState';
|
|
452
|
+
this.schemaComposer.addDirective(directives_js_1.grpcConnectivityStateDirective);
|
|
386
453
|
this.schemaComposer.Query.addFields({
|
|
387
454
|
[connectivityStateFieldName]: {
|
|
388
455
|
type: 'ConnectivityState',
|
|
@@ -391,61 +458,96 @@ ${rootJsonEntries
|
|
|
391
458
|
type: 'Boolean',
|
|
392
459
|
},
|
|
393
460
|
},
|
|
394
|
-
|
|
461
|
+
directives: [
|
|
462
|
+
{
|
|
463
|
+
name: 'grpcConnectivityState',
|
|
464
|
+
args: {
|
|
465
|
+
rootJsonName,
|
|
466
|
+
objPath,
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
],
|
|
395
470
|
},
|
|
396
471
|
});
|
|
397
472
|
}
|
|
398
473
|
}
|
|
399
|
-
async
|
|
400
|
-
|
|
401
|
-
this.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
474
|
+
async getCachedNonExecutableSchema(creds) {
|
|
475
|
+
var _a;
|
|
476
|
+
const interpolatedSource = (_a = this.config.source) === null || _a === void 0 ? void 0 : _a.toString();
|
|
477
|
+
if (interpolatedSource === null || interpolatedSource === void 0 ? void 0 : interpolatedSource.endsWith('.graphql')) {
|
|
478
|
+
this.logger.info(`Fetching GraphQL Schema with annotations`);
|
|
479
|
+
const sdl = await (0, utils_1.readFileOrUrl)(interpolatedSource, {
|
|
480
|
+
allowUnknownExtensions: true,
|
|
481
|
+
cwd: this.baseDir,
|
|
482
|
+
fetch: this.fetchFn,
|
|
483
|
+
importFn: this.importFn,
|
|
484
|
+
logger: this.logger,
|
|
485
|
+
headers: this.config.schemaHeaders,
|
|
486
|
+
});
|
|
487
|
+
return (0, graphql_1.buildSchema)(sdl, {
|
|
488
|
+
assumeValidSDL: true,
|
|
489
|
+
assumeValid: true,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
return this.schemaWithAnnotationsProxy.getWithSet(async () => {
|
|
493
|
+
this.schemaComposer.add(graphql_scalars_1.GraphQLBigInt);
|
|
494
|
+
this.schemaComposer.add(graphql_scalars_1.GraphQLByte);
|
|
495
|
+
this.schemaComposer.add(graphql_scalars_1.GraphQLUnsignedInt);
|
|
496
|
+
this.schemaComposer.add(graphql_scalars_1.GraphQLVoid);
|
|
497
|
+
this.schemaComposer.add(graphql_scalars_1.GraphQLJSON);
|
|
498
|
+
this.schemaComposer.createScalarTC({
|
|
499
|
+
name: 'File',
|
|
500
|
+
});
|
|
501
|
+
// identical of grpc's ConnectivityState
|
|
502
|
+
this.schemaComposer.createEnumTC({
|
|
503
|
+
name: 'ConnectivityState',
|
|
504
|
+
values: {
|
|
505
|
+
IDLE: { value: 0 },
|
|
506
|
+
CONNECTING: { value: 1 },
|
|
507
|
+
READY: { value: 2 },
|
|
508
|
+
TRANSIENT_FAILURE: { value: 3 },
|
|
509
|
+
SHUTDOWN: { value: 4 },
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
this.logger.debug(`Getting stored root and decoded descriptor set objects`);
|
|
513
|
+
const descriptorSets = await this.getDescriptorSets(creds);
|
|
514
|
+
for (const { name: rootJsonName, rootJson } of descriptorSets) {
|
|
515
|
+
const rootLogger = this.logger.child(rootJsonName);
|
|
516
|
+
rootLogger.debug(`Creating package definition from file descriptor set object`);
|
|
517
|
+
let loadOptions;
|
|
518
|
+
if (typeof this.config.source === 'object') {
|
|
519
|
+
loadOptions = this.config.source.load;
|
|
520
|
+
}
|
|
521
|
+
this.logger.debug(`Building the schema structure based on the root object`);
|
|
522
|
+
this.visit({
|
|
523
|
+
nested: rootJson,
|
|
524
|
+
name: '',
|
|
525
|
+
currentPath: [],
|
|
526
|
+
rootJsonName,
|
|
527
|
+
rootJson,
|
|
528
|
+
rootLogger,
|
|
529
|
+
loadOptions,
|
|
530
|
+
});
|
|
531
|
+
this.schemaComposer.addDirective(directives_js_1.grpcRootJsonDirective);
|
|
532
|
+
this.schemaComposer.Query.setDirectiveByName('grpcRootJson', {
|
|
533
|
+
name: rootJsonName,
|
|
534
|
+
rootJson,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
// graphql-compose doesn't add @defer and @stream to the schema
|
|
538
|
+
graphql_1.specifiedDirectives.forEach(directive => this.schemaComposer.addDirective(directive));
|
|
539
|
+
this.logger.debug(`Building the final GraphQL Schema`);
|
|
540
|
+
const schema = this.schemaComposer.buildSchema();
|
|
541
|
+
return schema;
|
|
419
542
|
});
|
|
543
|
+
}
|
|
544
|
+
async getMeshSource({ fetchFn }) {
|
|
545
|
+
this.fetchFn = fetchFn;
|
|
546
|
+
this.config.requestTimeout = this.config.requestTimeout || 200000;
|
|
420
547
|
this.logger.debug(`Getting channel credentials`);
|
|
421
548
|
const creds = await this.getCredentials();
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
for (const { name, rootJson } of artifacts) {
|
|
425
|
-
const rootLogger = this.logger.child(name);
|
|
426
|
-
rootLogger.debug(`Creating package definition from file descriptor set object`);
|
|
427
|
-
let options;
|
|
428
|
-
if (typeof this.config.source === 'object') {
|
|
429
|
-
options = this.config.source.load;
|
|
430
|
-
}
|
|
431
|
-
const packageDefinition = (0, proto_loader_1.fromJSON)(rootJson, options);
|
|
432
|
-
rootLogger.debug(`Creating service client for package definition`);
|
|
433
|
-
const grpcObject = (0, grpc_js_1.loadPackageDefinition)(packageDefinition);
|
|
434
|
-
this.logger.debug(`Building the schema structure based on the root object`);
|
|
435
|
-
this.visit({
|
|
436
|
-
nested: rootJson,
|
|
437
|
-
name: '',
|
|
438
|
-
currentPath: [],
|
|
439
|
-
rootJson,
|
|
440
|
-
creds,
|
|
441
|
-
grpcObject,
|
|
442
|
-
rootLogger,
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
// graphql-compose doesn't add @defer and @stream to the schema
|
|
446
|
-
graphql_1.specifiedDirectives.forEach(directive => this.schemaComposer.addDirective(directive));
|
|
447
|
-
this.logger.debug(`Building the final GraphQL Schema`);
|
|
448
|
-
const schema = this.schemaComposer.buildSchema();
|
|
549
|
+
const schema = await this.getCachedNonExecutableSchema(creds);
|
|
550
|
+
this.processDirectives({ schema, creds });
|
|
449
551
|
return {
|
|
450
552
|
schema,
|
|
451
553
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { DirectiveLocation, GraphQLBoolean, GraphQLDirective, GraphQLScalarType, GraphQLString, } from 'graphql';
|
|
2
|
+
export const grpcMethodDirective = new GraphQLDirective({
|
|
3
|
+
name: 'grpcMethod',
|
|
4
|
+
locations: [DirectiveLocation.FIELD_DEFINITION],
|
|
5
|
+
args: {
|
|
6
|
+
rootJsonName: {
|
|
7
|
+
type: GraphQLString,
|
|
8
|
+
},
|
|
9
|
+
objPath: {
|
|
10
|
+
type: GraphQLString,
|
|
11
|
+
},
|
|
12
|
+
methodName: {
|
|
13
|
+
type: GraphQLString,
|
|
14
|
+
},
|
|
15
|
+
responseStream: {
|
|
16
|
+
type: GraphQLBoolean,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
export const grpcConnectivityStateDirective = new GraphQLDirective({
|
|
21
|
+
name: 'grpcConnectivityState',
|
|
22
|
+
locations: [DirectiveLocation.FIELD_DEFINITION],
|
|
23
|
+
args: {
|
|
24
|
+
rootJsonName: {
|
|
25
|
+
type: GraphQLString,
|
|
26
|
+
},
|
|
27
|
+
objPath: {
|
|
28
|
+
type: GraphQLString,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
export const ObjMapScalar = new GraphQLScalarType({
|
|
33
|
+
name: 'ObjMap',
|
|
34
|
+
serialize: value => JSON.stringify(value),
|
|
35
|
+
parseValue: value => JSON.parse(value.toString()),
|
|
36
|
+
parseLiteral: ast => {
|
|
37
|
+
if (ast.kind === 'StringValue') {
|
|
38
|
+
return JSON.parse(ast.value);
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
export const grpcRootJsonDirective = new GraphQLDirective({
|
|
44
|
+
name: 'grpcRootJson',
|
|
45
|
+
locations: [DirectiveLocation.OBJECT],
|
|
46
|
+
args: {
|
|
47
|
+
name: {
|
|
48
|
+
type: GraphQLString,
|
|
49
|
+
},
|
|
50
|
+
rootJson: {
|
|
51
|
+
type: ObjMapScalar,
|
|
52
|
+
},
|
|
53
|
+
loadOptions: {
|
|
54
|
+
type: ObjMapScalar,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
isRepeatable: true,
|
|
58
|
+
});
|
package/esm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable import/no-duplicates */
|
|
2
2
|
import globby from 'globby';
|
|
3
|
-
import { specifiedDirectives } from 'graphql';
|
|
3
|
+
import { buildSchema, specifiedDirectives, } from 'graphql';
|
|
4
4
|
import { SchemaComposer } from 'graphql-compose';
|
|
5
5
|
import { GraphQLBigInt, GraphQLByte, GraphQLJSON, GraphQLUnsignedInt, GraphQLVoid, } from 'graphql-scalars';
|
|
6
6
|
import lodashGet from 'lodash.get';
|
|
@@ -10,49 +10,27 @@ import descriptor from 'protobufjs/ext/descriptor/index.js';
|
|
|
10
10
|
import { Client } from '@ardatan/grpc-reflection-js';
|
|
11
11
|
import { path, process } from '@graphql-mesh/cross-helpers';
|
|
12
12
|
import { fs } from '@graphql-mesh/cross-helpers';
|
|
13
|
+
import { PredefinedProxyOptions } from '@graphql-mesh/store';
|
|
13
14
|
import { stringInterpolator } from '@graphql-mesh/string-interpolation';
|
|
15
|
+
import { readFileOrUrl } from '@graphql-mesh/utils';
|
|
16
|
+
import { getDirective, getDirectives, getRootTypes } from '@graphql-tools/utils';
|
|
14
17
|
import { credentials, loadPackageDefinition } from '@grpc/grpc-js';
|
|
15
18
|
import { fromJSON } from '@grpc/proto-loader';
|
|
19
|
+
import { grpcConnectivityStateDirective, grpcMethodDirective, grpcRootJsonDirective, } from './directives.js';
|
|
16
20
|
import './patchLongJs.js';
|
|
17
21
|
import { addIncludePathResolver, addMetaDataToCall, getTypeName } from './utils.js';
|
|
18
22
|
const { Root } = protobufjs;
|
|
19
23
|
const QUERY_METHOD_PREFIXES = ['get', 'list', 'search'];
|
|
20
24
|
export default class GrpcHandler {
|
|
21
|
-
constructor({ config, baseDir, store, logger, pubsub, }) {
|
|
25
|
+
constructor({ config, baseDir, store, logger, pubsub, importFn, }) {
|
|
26
|
+
this.grpcObjectByserviceClientByObjPath = new WeakMap();
|
|
22
27
|
this.schemaComposer = new SchemaComposer();
|
|
23
28
|
this.logger = logger;
|
|
24
29
|
this.config = config;
|
|
25
30
|
this.baseDir = baseDir;
|
|
26
|
-
this.
|
|
27
|
-
codify: rootJsonEntries => `
|
|
28
|
-
export default [
|
|
29
|
-
${rootJsonEntries
|
|
30
|
-
.map(({ name, rootJson }) => `
|
|
31
|
-
{
|
|
32
|
-
name: ${JSON.stringify(name)},
|
|
33
|
-
rootJson: ${JSON.stringify(rootJson, null, 2)},
|
|
34
|
-
},
|
|
35
|
-
`)
|
|
36
|
-
.join('\n')}
|
|
37
|
-
];
|
|
38
|
-
`.trim(),
|
|
39
|
-
fromJSON: jsonData => {
|
|
40
|
-
return jsonData.map(({ name, rootJson }) => ({
|
|
41
|
-
name,
|
|
42
|
-
rootJson,
|
|
43
|
-
}));
|
|
44
|
-
},
|
|
45
|
-
toJSON: rootJsonEntries => {
|
|
46
|
-
return rootJsonEntries.map(({ name, rootJson }) => {
|
|
47
|
-
return {
|
|
48
|
-
name,
|
|
49
|
-
rootJson,
|
|
50
|
-
};
|
|
51
|
-
});
|
|
52
|
-
},
|
|
53
|
-
validate: () => { },
|
|
54
|
-
});
|
|
31
|
+
this.schemaWithAnnotationsProxy = store.proxy('schemaWithAnnotations', PredefinedProxyOptions.GraphQLSchemaWithDiffing);
|
|
55
32
|
this.pubsub = pubsub;
|
|
33
|
+
this.importFn = importFn;
|
|
56
34
|
}
|
|
57
35
|
async processReflection(creds) {
|
|
58
36
|
this.logger.debug(`Using the reflection`);
|
|
@@ -144,38 +122,36 @@ ${rootJsonEntries
|
|
|
144
122
|
this.logger.debug(`Adding proto content to the root`);
|
|
145
123
|
return protoRoot;
|
|
146
124
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
rootPromises.push(this.processDescriptorFile());
|
|
155
|
-
}
|
|
156
|
-
else if (filePath.endsWith('proto')) {
|
|
157
|
-
rootPromises.push(this.processProtoFile());
|
|
158
|
-
}
|
|
125
|
+
async getDescriptorSets(creds) {
|
|
126
|
+
const rootPromises = [];
|
|
127
|
+
this.logger.debug(`Building Roots`);
|
|
128
|
+
if (this.config.source) {
|
|
129
|
+
const filePath = typeof this.config.source === 'string' ? this.config.source : this.config.source.file;
|
|
130
|
+
if (filePath.endsWith('json')) {
|
|
131
|
+
rootPromises.push(this.processDescriptorFile());
|
|
159
132
|
}
|
|
160
|
-
else {
|
|
161
|
-
|
|
162
|
-
rootPromises.push(...reflectionPromises);
|
|
133
|
+
else if (filePath.endsWith('proto')) {
|
|
134
|
+
rootPromises.push(this.processProtoFile());
|
|
163
135
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const reflectionPromises = await this.processReflection(creds);
|
|
139
|
+
rootPromises.push(...reflectionPromises);
|
|
140
|
+
}
|
|
141
|
+
return Promise.all(rootPromises.map(async (root$, i) => {
|
|
142
|
+
const root = await root$;
|
|
143
|
+
const rootName = root.name || `Root${i}`;
|
|
144
|
+
const rootLogger = this.logger.child(rootName);
|
|
145
|
+
rootLogger.debug(`Resolving entire the root tree`);
|
|
146
|
+
root.resolveAll();
|
|
147
|
+
rootLogger.debug(`Creating artifacts from descriptor set and root`);
|
|
148
|
+
return {
|
|
149
|
+
name: rootName,
|
|
150
|
+
rootJson: root.toJSON({
|
|
151
|
+
keepComments: true,
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
}));
|
|
179
155
|
}
|
|
180
156
|
async getCredentials() {
|
|
181
157
|
if (this.config.credentialsSsl) {
|
|
@@ -216,8 +192,102 @@ ${rootJsonEntries
|
|
|
216
192
|
}
|
|
217
193
|
return currentWalkingPath.concat(baseTypePath);
|
|
218
194
|
}
|
|
219
|
-
|
|
195
|
+
getGrpcObject({ rootJson, loadOptions, rootLogger, }) {
|
|
196
|
+
const packageDefinition = fromJSON(rootJson, loadOptions);
|
|
197
|
+
rootLogger.debug(`Creating service client for package definition`);
|
|
198
|
+
const grpcObject = loadPackageDefinition(packageDefinition);
|
|
199
|
+
return grpcObject;
|
|
200
|
+
}
|
|
201
|
+
getServiceClient({ grpcObject, objPath, creds, }) {
|
|
220
202
|
var _a;
|
|
203
|
+
let serviceClientByObjPath = this.grpcObjectByserviceClientByObjPath.get(grpcObject);
|
|
204
|
+
if (!serviceClientByObjPath) {
|
|
205
|
+
serviceClientByObjPath = new Map();
|
|
206
|
+
this.grpcObjectByserviceClientByObjPath.set(grpcObject, serviceClientByObjPath);
|
|
207
|
+
}
|
|
208
|
+
let client = serviceClientByObjPath.get(objPath);
|
|
209
|
+
if (!client) {
|
|
210
|
+
const ServiceClient = lodashGet(grpcObject, objPath);
|
|
211
|
+
if (typeof ServiceClient !== 'function') {
|
|
212
|
+
throw new Error(`Object at path ${objPath} is not a Service constructor`);
|
|
213
|
+
}
|
|
214
|
+
client = new ServiceClient((_a = stringInterpolator.parse(this.config.endpoint, { env: process.env })) !== null && _a !== void 0 ? _a : this.config.endpoint, creds);
|
|
215
|
+
const subId = this.pubsub.subscribe('destroy', () => {
|
|
216
|
+
client.close();
|
|
217
|
+
this.pubsub.unsubscribe(subId);
|
|
218
|
+
});
|
|
219
|
+
serviceClientByObjPath.set(objPath, client);
|
|
220
|
+
}
|
|
221
|
+
return client;
|
|
222
|
+
}
|
|
223
|
+
getFieldResolver({ client, methodName, isResponseStream, }) {
|
|
224
|
+
const metaData = this.config.metaData;
|
|
225
|
+
const clientMethod = client[methodName].bind(client);
|
|
226
|
+
return function grpcFieldResolver(root, args, context) {
|
|
227
|
+
return addMetaDataToCall(clientMethod, args.input, {
|
|
228
|
+
root,
|
|
229
|
+
args,
|
|
230
|
+
context,
|
|
231
|
+
env: process.env,
|
|
232
|
+
}, metaData, isResponseStream);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
getConnectivityStateResolver({ client, }) {
|
|
236
|
+
return function connectivityStateResolver(_, { tryToConnect }) {
|
|
237
|
+
return client.getChannel().getConnectivityState(tryToConnect);
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
processDirectives({ schema, creds }) {
|
|
241
|
+
const queryType = schema.getQueryType();
|
|
242
|
+
const rootJsonAnnotations = getDirective(schema, queryType, 'grpcRootJson');
|
|
243
|
+
const rootJsonMap = new Map();
|
|
244
|
+
const grpcObjectByRootJsonName = new Map();
|
|
245
|
+
for (const { name, rootJson, loadOptions } of rootJsonAnnotations) {
|
|
246
|
+
rootJsonMap.set(name, rootJson);
|
|
247
|
+
const rootLogger = this.logger.child(name);
|
|
248
|
+
grpcObjectByRootJsonName.set(name, this.getGrpcObject({ rootJson, loadOptions, rootLogger }));
|
|
249
|
+
}
|
|
250
|
+
const rootTypes = getRootTypes(schema);
|
|
251
|
+
for (const rootType of rootTypes) {
|
|
252
|
+
for (const fieldName in rootType.getFields()) {
|
|
253
|
+
const field = rootType.getFields()[fieldName];
|
|
254
|
+
const directives = getDirectives(schema, field);
|
|
255
|
+
if (directives === null || directives === void 0 ? void 0 : directives.length) {
|
|
256
|
+
for (const directiveObj of directives) {
|
|
257
|
+
switch (directiveObj.name) {
|
|
258
|
+
case 'grpcMethod': {
|
|
259
|
+
const { rootJsonName, objPath, methodName, responseStream } = directiveObj.args;
|
|
260
|
+
const grpcObject = grpcObjectByRootJsonName.get(rootJsonName);
|
|
261
|
+
const client = this.getServiceClient({
|
|
262
|
+
grpcObject,
|
|
263
|
+
objPath,
|
|
264
|
+
creds,
|
|
265
|
+
});
|
|
266
|
+
field.resolve = this.getFieldResolver({
|
|
267
|
+
client,
|
|
268
|
+
methodName,
|
|
269
|
+
isResponseStream: responseStream,
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'grpcConnectivityState': {
|
|
274
|
+
const { rootJsonName, objPath } = directiveObj.args;
|
|
275
|
+
const grpcObject = grpcObjectByRootJsonName.get(rootJsonName);
|
|
276
|
+
const client = this.getServiceClient({
|
|
277
|
+
grpcObject,
|
|
278
|
+
objPath,
|
|
279
|
+
creds,
|
|
280
|
+
});
|
|
281
|
+
field.resolve = this.getConnectivityStateResolver({ client });
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
visit({ nested, name, currentPath, rootJsonName, rootJson, rootLogger: logger, loadOptions, }) {
|
|
221
291
|
const pathWithName = [...currentPath, ...name.split('.')].filter(Boolean);
|
|
222
292
|
if ('nested' in nested) {
|
|
223
293
|
for (const key in nested.nested) {
|
|
@@ -227,9 +297,8 @@ ${rootJsonEntries
|
|
|
227
297
|
nested: currentNested,
|
|
228
298
|
name: key,
|
|
229
299
|
currentPath: pathWithName,
|
|
300
|
+
rootJsonName,
|
|
230
301
|
rootJson,
|
|
231
|
-
creds,
|
|
232
|
-
grpcObject,
|
|
233
302
|
rootLogger: logger,
|
|
234
303
|
});
|
|
235
304
|
}
|
|
@@ -319,15 +388,6 @@ ${rootJsonEntries
|
|
|
319
388
|
}
|
|
320
389
|
else if ('methods' in nested) {
|
|
321
390
|
const objPath = pathWithName.join('.');
|
|
322
|
-
const ServiceClient = lodashGet(grpcObject, objPath);
|
|
323
|
-
if (typeof ServiceClient !== 'function') {
|
|
324
|
-
throw new Error(`Object at path ${objPath} is not a Service constructor`);
|
|
325
|
-
}
|
|
326
|
-
const client = new ServiceClient((_a = stringInterpolator.parse(this.config.endpoint, { env: process.env })) !== null && _a !== void 0 ? _a : this.config.endpoint, creds);
|
|
327
|
-
const subId = this.pubsub.subscribe('destroy', () => {
|
|
328
|
-
client.close();
|
|
329
|
-
this.pubsub.unsubscribe(subId);
|
|
330
|
-
});
|
|
331
391
|
for (const methodName in nested.methods) {
|
|
332
392
|
const method = nested.methods[methodName];
|
|
333
393
|
const rootFieldName = [...pathWithName, methodName].join('_');
|
|
@@ -367,19 +427,26 @@ ${rootJsonEntries
|
|
|
367
427
|
const rootTypeComposer = prefixQueryMethod.some(prefix => methodNameLowerCased.startsWith(prefix))
|
|
368
428
|
? this.schemaComposer.Query
|
|
369
429
|
: this.schemaComposer.Mutation;
|
|
430
|
+
this.schemaComposer.addDirective(grpcMethodDirective);
|
|
370
431
|
rootTypeComposer.addFields({
|
|
371
432
|
[rootFieldName]: {
|
|
372
433
|
...fieldConfig,
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
434
|
+
directives: [
|
|
435
|
+
{
|
|
436
|
+
name: 'grpcMethod',
|
|
437
|
+
args: {
|
|
438
|
+
rootJsonName,
|
|
439
|
+
objPath,
|
|
440
|
+
methodName,
|
|
441
|
+
responseStream: !!method.responseStream,
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
],
|
|
379
445
|
},
|
|
380
446
|
});
|
|
381
447
|
}
|
|
382
448
|
const connectivityStateFieldName = pathWithName.join('_') + '_connectivityState';
|
|
449
|
+
this.schemaComposer.addDirective(grpcConnectivityStateDirective);
|
|
383
450
|
this.schemaComposer.Query.addFields({
|
|
384
451
|
[connectivityStateFieldName]: {
|
|
385
452
|
type: 'ConnectivityState',
|
|
@@ -388,61 +455,96 @@ ${rootJsonEntries
|
|
|
388
455
|
type: 'Boolean',
|
|
389
456
|
},
|
|
390
457
|
},
|
|
391
|
-
|
|
458
|
+
directives: [
|
|
459
|
+
{
|
|
460
|
+
name: 'grpcConnectivityState',
|
|
461
|
+
args: {
|
|
462
|
+
rootJsonName,
|
|
463
|
+
objPath,
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
],
|
|
392
467
|
},
|
|
393
468
|
});
|
|
394
469
|
}
|
|
395
470
|
}
|
|
396
|
-
async
|
|
397
|
-
|
|
398
|
-
this.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
471
|
+
async getCachedNonExecutableSchema(creds) {
|
|
472
|
+
var _a;
|
|
473
|
+
const interpolatedSource = (_a = this.config.source) === null || _a === void 0 ? void 0 : _a.toString();
|
|
474
|
+
if (interpolatedSource === null || interpolatedSource === void 0 ? void 0 : interpolatedSource.endsWith('.graphql')) {
|
|
475
|
+
this.logger.info(`Fetching GraphQL Schema with annotations`);
|
|
476
|
+
const sdl = await readFileOrUrl(interpolatedSource, {
|
|
477
|
+
allowUnknownExtensions: true,
|
|
478
|
+
cwd: this.baseDir,
|
|
479
|
+
fetch: this.fetchFn,
|
|
480
|
+
importFn: this.importFn,
|
|
481
|
+
logger: this.logger,
|
|
482
|
+
headers: this.config.schemaHeaders,
|
|
483
|
+
});
|
|
484
|
+
return buildSchema(sdl, {
|
|
485
|
+
assumeValidSDL: true,
|
|
486
|
+
assumeValid: true,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
return this.schemaWithAnnotationsProxy.getWithSet(async () => {
|
|
490
|
+
this.schemaComposer.add(GraphQLBigInt);
|
|
491
|
+
this.schemaComposer.add(GraphQLByte);
|
|
492
|
+
this.schemaComposer.add(GraphQLUnsignedInt);
|
|
493
|
+
this.schemaComposer.add(GraphQLVoid);
|
|
494
|
+
this.schemaComposer.add(GraphQLJSON);
|
|
495
|
+
this.schemaComposer.createScalarTC({
|
|
496
|
+
name: 'File',
|
|
497
|
+
});
|
|
498
|
+
// identical of grpc's ConnectivityState
|
|
499
|
+
this.schemaComposer.createEnumTC({
|
|
500
|
+
name: 'ConnectivityState',
|
|
501
|
+
values: {
|
|
502
|
+
IDLE: { value: 0 },
|
|
503
|
+
CONNECTING: { value: 1 },
|
|
504
|
+
READY: { value: 2 },
|
|
505
|
+
TRANSIENT_FAILURE: { value: 3 },
|
|
506
|
+
SHUTDOWN: { value: 4 },
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
this.logger.debug(`Getting stored root and decoded descriptor set objects`);
|
|
510
|
+
const descriptorSets = await this.getDescriptorSets(creds);
|
|
511
|
+
for (const { name: rootJsonName, rootJson } of descriptorSets) {
|
|
512
|
+
const rootLogger = this.logger.child(rootJsonName);
|
|
513
|
+
rootLogger.debug(`Creating package definition from file descriptor set object`);
|
|
514
|
+
let loadOptions;
|
|
515
|
+
if (typeof this.config.source === 'object') {
|
|
516
|
+
loadOptions = this.config.source.load;
|
|
517
|
+
}
|
|
518
|
+
this.logger.debug(`Building the schema structure based on the root object`);
|
|
519
|
+
this.visit({
|
|
520
|
+
nested: rootJson,
|
|
521
|
+
name: '',
|
|
522
|
+
currentPath: [],
|
|
523
|
+
rootJsonName,
|
|
524
|
+
rootJson,
|
|
525
|
+
rootLogger,
|
|
526
|
+
loadOptions,
|
|
527
|
+
});
|
|
528
|
+
this.schemaComposer.addDirective(grpcRootJsonDirective);
|
|
529
|
+
this.schemaComposer.Query.setDirectiveByName('grpcRootJson', {
|
|
530
|
+
name: rootJsonName,
|
|
531
|
+
rootJson,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
// graphql-compose doesn't add @defer and @stream to the schema
|
|
535
|
+
specifiedDirectives.forEach(directive => this.schemaComposer.addDirective(directive));
|
|
536
|
+
this.logger.debug(`Building the final GraphQL Schema`);
|
|
537
|
+
const schema = this.schemaComposer.buildSchema();
|
|
538
|
+
return schema;
|
|
416
539
|
});
|
|
540
|
+
}
|
|
541
|
+
async getMeshSource({ fetchFn }) {
|
|
542
|
+
this.fetchFn = fetchFn;
|
|
543
|
+
this.config.requestTimeout = this.config.requestTimeout || 200000;
|
|
417
544
|
this.logger.debug(`Getting channel credentials`);
|
|
418
545
|
const creds = await this.getCredentials();
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
for (const { name, rootJson } of artifacts) {
|
|
422
|
-
const rootLogger = this.logger.child(name);
|
|
423
|
-
rootLogger.debug(`Creating package definition from file descriptor set object`);
|
|
424
|
-
let options;
|
|
425
|
-
if (typeof this.config.source === 'object') {
|
|
426
|
-
options = this.config.source.load;
|
|
427
|
-
}
|
|
428
|
-
const packageDefinition = fromJSON(rootJson, options);
|
|
429
|
-
rootLogger.debug(`Creating service client for package definition`);
|
|
430
|
-
const grpcObject = loadPackageDefinition(packageDefinition);
|
|
431
|
-
this.logger.debug(`Building the schema structure based on the root object`);
|
|
432
|
-
this.visit({
|
|
433
|
-
nested: rootJson,
|
|
434
|
-
name: '',
|
|
435
|
-
currentPath: [],
|
|
436
|
-
rootJson,
|
|
437
|
-
creds,
|
|
438
|
-
grpcObject,
|
|
439
|
-
rootLogger,
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
// graphql-compose doesn't add @defer and @stream to the schema
|
|
443
|
-
specifiedDirectives.forEach(directive => this.schemaComposer.addDirective(directive));
|
|
444
|
-
this.logger.debug(`Building the final GraphQL Schema`);
|
|
445
|
-
const schema = this.schemaComposer.buildSchema();
|
|
546
|
+
const schema = await this.getCachedNonExecutableSchema(creds);
|
|
547
|
+
this.processDirectives({ schema, creds });
|
|
446
548
|
return {
|
|
447
549
|
schema,
|
|
448
550
|
};
|
package/package.json
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-mesh/grpc",
|
|
3
|
-
"version": "1.0.0-alpha-
|
|
3
|
+
"version": "1.0.0-alpha-20230522105300-fe9c79867",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@graphql-mesh/cross-helpers": "
|
|
7
|
-
"@graphql-mesh/store": "1.0.0-alpha-
|
|
8
|
-
"@graphql-mesh/types": "1.0.0-alpha-
|
|
9
|
-
"@graphql-mesh/utils": "1.0.0-alpha-
|
|
6
|
+
"@graphql-mesh/cross-helpers": "0.4.0-alpha-20230522105300-fe9c79867",
|
|
7
|
+
"@graphql-mesh/store": "1.0.0-alpha-20230522105300-fe9c79867",
|
|
8
|
+
"@graphql-mesh/types": "1.0.0-alpha-20230522105300-fe9c79867",
|
|
9
|
+
"@graphql-mesh/utils": "1.0.0-alpha-20230522105300-fe9c79867",
|
|
10
|
+
"@graphql-tools/utils": "^9.2.1",
|
|
10
11
|
"graphql": "*",
|
|
11
12
|
"tslib": "^2.4.0"
|
|
12
13
|
},
|
|
13
14
|
"dependencies": {
|
|
14
15
|
"@ardatan/grpc-reflection-js": "0.0.2",
|
|
15
|
-
"@graphql-mesh/string-interpolation": "0.
|
|
16
|
+
"@graphql-mesh/string-interpolation": "0.5.0-alpha-20230522105300-fe9c79867",
|
|
16
17
|
"@grpc/grpc-js": "^1.1.7",
|
|
17
|
-
"@grpc/proto-loader": "0.7.
|
|
18
|
+
"@grpc/proto-loader": "0.7.7",
|
|
18
19
|
"globby": "11.1.0",
|
|
19
20
|
"graphql-compose": "9.0.10",
|
|
20
21
|
"graphql-scalars": "^1.20.4",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { GraphQLDirective, GraphQLScalarType } from 'graphql';
|
|
2
|
+
export declare const grpcMethodDirective: GraphQLDirective;
|
|
3
|
+
export declare const grpcConnectivityStateDirective: GraphQLDirective;
|
|
4
|
+
export declare const ObjMapScalar: GraphQLScalarType<any, string>;
|
|
5
|
+
export declare const grpcRootJsonDirective: GraphQLDirective;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { GraphQLDirective, GraphQLScalarType } from 'graphql';
|
|
2
|
+
export declare const grpcMethodDirective: GraphQLDirective;
|
|
3
|
+
export declare const grpcConnectivityStateDirective: GraphQLDirective;
|
|
4
|
+
export declare const ObjMapScalar: GraphQLScalarType<any, string>;
|
|
5
|
+
export declare const grpcRootJsonDirective: GraphQLDirective;
|
package/typings/index.d.cts
CHANGED
|
@@ -1,37 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphQLFieldResolver, GraphQLSchema } from 'graphql';
|
|
2
|
+
import { AnyNestedObject, IParseOptions } from 'protobufjs';
|
|
2
3
|
import protobufjs from 'protobufjs';
|
|
3
4
|
import { Logger, MeshHandler, MeshHandlerOptions, YamlConfig } from '@graphql-mesh/types';
|
|
5
|
+
import { GetMeshSourcePayload } from '@graphql-mesh/types';
|
|
4
6
|
import { ChannelCredentials, loadPackageDefinition } from '@grpc/grpc-js';
|
|
7
|
+
import { ServiceClient } from '@grpc/grpc-js/build/src/make-client.cjs';
|
|
5
8
|
import './patchLongJs.cjs';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
9
|
+
interface LoadOptions extends IParseOptions {
|
|
10
|
+
includeDirs?: string[];
|
|
11
|
+
}
|
|
10
12
|
export default class GrpcHandler implements MeshHandler {
|
|
11
13
|
private config;
|
|
12
14
|
private baseDir;
|
|
13
|
-
private
|
|
15
|
+
private schemaWithAnnotationsProxy;
|
|
14
16
|
private logger;
|
|
15
17
|
private pubsub;
|
|
16
|
-
|
|
18
|
+
private fetchFn;
|
|
19
|
+
private importFn;
|
|
20
|
+
constructor({ config, baseDir, store, logger, pubsub, importFn, }: MeshHandlerOptions<YamlConfig.GrpcHandler>);
|
|
17
21
|
processReflection(creds: ChannelCredentials): Promise<Promise<protobufjs.Root>[]>;
|
|
18
22
|
processDescriptorFile(): Promise<protobufjs.Root>;
|
|
19
23
|
processProtoFile(): Promise<protobufjs.Root>;
|
|
20
|
-
|
|
24
|
+
getDescriptorSets(creds: ChannelCredentials): Promise<{
|
|
25
|
+
name: string;
|
|
26
|
+
rootJson: protobufjs.INamespace;
|
|
27
|
+
}[]>;
|
|
21
28
|
getCredentials(): Promise<ChannelCredentials>;
|
|
22
29
|
walkToFindTypePath(rootJson: protobufjs.INamespace, pathWithName: string[], baseTypePath: string[]): string[];
|
|
23
|
-
|
|
30
|
+
getGrpcObject({ rootJson, loadOptions, rootLogger, }: {
|
|
31
|
+
rootJson: protobufjs.INamespace;
|
|
32
|
+
loadOptions: LoadOptions;
|
|
33
|
+
rootLogger: Logger;
|
|
34
|
+
}): import("@grpc/grpc-js").GrpcObject;
|
|
35
|
+
private grpcObjectByserviceClientByObjPath;
|
|
36
|
+
getServiceClient({ grpcObject, objPath, creds, }: {
|
|
37
|
+
grpcObject: ReturnType<typeof loadPackageDefinition>;
|
|
38
|
+
objPath: string;
|
|
39
|
+
creds: ChannelCredentials;
|
|
40
|
+
}): ServiceClient;
|
|
41
|
+
getFieldResolver({ client, methodName, isResponseStream, }: {
|
|
42
|
+
client: ServiceClient;
|
|
43
|
+
methodName: string;
|
|
44
|
+
isResponseStream: boolean;
|
|
45
|
+
}): GraphQLFieldResolver<any, any>;
|
|
46
|
+
getConnectivityStateResolver({ client, }: {
|
|
47
|
+
client: ServiceClient;
|
|
48
|
+
}): GraphQLFieldResolver<any, any>;
|
|
49
|
+
processDirectives({ schema, creds }: {
|
|
50
|
+
schema: GraphQLSchema;
|
|
51
|
+
creds: ChannelCredentials;
|
|
52
|
+
}): void;
|
|
53
|
+
visit({ nested, name, currentPath, rootJsonName, rootJson, rootLogger: logger, loadOptions, }: {
|
|
24
54
|
nested: AnyNestedObject;
|
|
25
55
|
name: string;
|
|
26
56
|
currentPath: string[];
|
|
57
|
+
rootJsonName: string;
|
|
27
58
|
rootJson: protobufjs.INamespace;
|
|
28
|
-
creds: ChannelCredentials;
|
|
29
|
-
grpcObject: ReturnType<typeof loadPackageDefinition>;
|
|
30
59
|
rootLogger: Logger;
|
|
60
|
+
loadOptions?: LoadOptions;
|
|
31
61
|
}): void;
|
|
32
62
|
private schemaComposer;
|
|
33
|
-
|
|
34
|
-
|
|
63
|
+
private getCachedNonExecutableSchema;
|
|
64
|
+
getMeshSource({ fetchFn }: GetMeshSourcePayload): Promise<{
|
|
65
|
+
schema: GraphQLSchema;
|
|
35
66
|
}>;
|
|
36
67
|
}
|
|
37
68
|
export {};
|
package/typings/index.d.ts
CHANGED
|
@@ -1,37 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphQLFieldResolver, GraphQLSchema } from 'graphql';
|
|
2
|
+
import { AnyNestedObject, IParseOptions } from 'protobufjs';
|
|
2
3
|
import protobufjs from 'protobufjs';
|
|
3
4
|
import { Logger, MeshHandler, MeshHandlerOptions, YamlConfig } from '@graphql-mesh/types';
|
|
5
|
+
import { GetMeshSourcePayload } from '@graphql-mesh/types';
|
|
4
6
|
import { ChannelCredentials, loadPackageDefinition } from '@grpc/grpc-js';
|
|
7
|
+
import { ServiceClient } from '@grpc/grpc-js/build/src/make-client.js';
|
|
5
8
|
import './patchLongJs.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
9
|
+
interface LoadOptions extends IParseOptions {
|
|
10
|
+
includeDirs?: string[];
|
|
11
|
+
}
|
|
10
12
|
export default class GrpcHandler implements MeshHandler {
|
|
11
13
|
private config;
|
|
12
14
|
private baseDir;
|
|
13
|
-
private
|
|
15
|
+
private schemaWithAnnotationsProxy;
|
|
14
16
|
private logger;
|
|
15
17
|
private pubsub;
|
|
16
|
-
|
|
18
|
+
private fetchFn;
|
|
19
|
+
private importFn;
|
|
20
|
+
constructor({ config, baseDir, store, logger, pubsub, importFn, }: MeshHandlerOptions<YamlConfig.GrpcHandler>);
|
|
17
21
|
processReflection(creds: ChannelCredentials): Promise<Promise<protobufjs.Root>[]>;
|
|
18
22
|
processDescriptorFile(): Promise<protobufjs.Root>;
|
|
19
23
|
processProtoFile(): Promise<protobufjs.Root>;
|
|
20
|
-
|
|
24
|
+
getDescriptorSets(creds: ChannelCredentials): Promise<{
|
|
25
|
+
name: string;
|
|
26
|
+
rootJson: protobufjs.INamespace;
|
|
27
|
+
}[]>;
|
|
21
28
|
getCredentials(): Promise<ChannelCredentials>;
|
|
22
29
|
walkToFindTypePath(rootJson: protobufjs.INamespace, pathWithName: string[], baseTypePath: string[]): string[];
|
|
23
|
-
|
|
30
|
+
getGrpcObject({ rootJson, loadOptions, rootLogger, }: {
|
|
31
|
+
rootJson: protobufjs.INamespace;
|
|
32
|
+
loadOptions: LoadOptions;
|
|
33
|
+
rootLogger: Logger;
|
|
34
|
+
}): import("@grpc/grpc-js").GrpcObject;
|
|
35
|
+
private grpcObjectByserviceClientByObjPath;
|
|
36
|
+
getServiceClient({ grpcObject, objPath, creds, }: {
|
|
37
|
+
grpcObject: ReturnType<typeof loadPackageDefinition>;
|
|
38
|
+
objPath: string;
|
|
39
|
+
creds: ChannelCredentials;
|
|
40
|
+
}): ServiceClient;
|
|
41
|
+
getFieldResolver({ client, methodName, isResponseStream, }: {
|
|
42
|
+
client: ServiceClient;
|
|
43
|
+
methodName: string;
|
|
44
|
+
isResponseStream: boolean;
|
|
45
|
+
}): GraphQLFieldResolver<any, any>;
|
|
46
|
+
getConnectivityStateResolver({ client, }: {
|
|
47
|
+
client: ServiceClient;
|
|
48
|
+
}): GraphQLFieldResolver<any, any>;
|
|
49
|
+
processDirectives({ schema, creds }: {
|
|
50
|
+
schema: GraphQLSchema;
|
|
51
|
+
creds: ChannelCredentials;
|
|
52
|
+
}): void;
|
|
53
|
+
visit({ nested, name, currentPath, rootJsonName, rootJson, rootLogger: logger, loadOptions, }: {
|
|
24
54
|
nested: AnyNestedObject;
|
|
25
55
|
name: string;
|
|
26
56
|
currentPath: string[];
|
|
57
|
+
rootJsonName: string;
|
|
27
58
|
rootJson: protobufjs.INamespace;
|
|
28
|
-
creds: ChannelCredentials;
|
|
29
|
-
grpcObject: ReturnType<typeof loadPackageDefinition>;
|
|
30
59
|
rootLogger: Logger;
|
|
60
|
+
loadOptions?: LoadOptions;
|
|
31
61
|
}): void;
|
|
32
62
|
private schemaComposer;
|
|
33
|
-
|
|
34
|
-
|
|
63
|
+
private getCachedNonExecutableSchema;
|
|
64
|
+
getMeshSource({ fetchFn }: GetMeshSourcePayload): Promise<{
|
|
65
|
+
schema: GraphQLSchema;
|
|
35
66
|
}>;
|
|
36
67
|
}
|
|
37
68
|
export {};
|