@graphql-mesh/grpc 1.0.0-alpha-3fc47d119.0 → 1.0.0-alpha-20230420181317-a95037648
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/index.js +454 -0
- package/cjs/package.json +1 -0
- package/cjs/patchLongJs.js +14 -0
- package/cjs/scalars.js +32 -0
- package/cjs/utils.js +96 -0
- package/{index.mjs → esm/index.js} +120 -213
- package/esm/patchLongJs.js +11 -0
- package/esm/scalars.js +27 -0
- package/esm/utils.js +89 -0
- package/package.json +30 -23
- package/typings/index.d.cts +37 -0
- package/{index.d.ts → typings/index.d.ts} +12 -14
- package/typings/patchLongJs.d.ts +1 -0
- package/typings/scalars.d.ts +2 -0
- package/typings/utils.d.cts +7 -0
- package/{utils.d.ts → typings/utils.d.ts} +2 -1
- package/index.js +0 -548
- /package/{patchLongJs.d.ts → typings/patchLongJs.d.cts} +0 -0
- /package/{scalars.d.ts → typings/scalars.d.cts} +0 -0
|
@@ -1,160 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { loadFileDescriptorSetFromObject } from '@grpc/proto-loader';
|
|
1
|
+
/* eslint-disable import/no-duplicates */
|
|
2
|
+
import globby from 'globby';
|
|
3
|
+
import { specifiedDirectives } from 'graphql';
|
|
5
4
|
import { SchemaComposer } from 'graphql-compose';
|
|
6
|
-
import {
|
|
5
|
+
import { GraphQLBigInt, GraphQLByte, GraphQLJSON, GraphQLUnsignedInt, GraphQLVoid, } from 'graphql-scalars';
|
|
7
6
|
import lodashGet from 'lodash.get';
|
|
8
7
|
import lodashHas from 'lodash.has';
|
|
9
8
|
import protobufjs from 'protobufjs';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Long.fromValue = (value) => {
|
|
20
|
-
if (typeof value === 'bigint') {
|
|
21
|
-
return Long.fromValue(value.toString());
|
|
22
|
-
}
|
|
23
|
-
return originalLongFromValue(value);
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
patchLongJs();
|
|
27
|
-
|
|
28
|
-
const SCALARS = new Map([
|
|
29
|
-
['bool', 'Boolean'],
|
|
30
|
-
['bytes', 'Byte'],
|
|
31
|
-
['double', 'Float'],
|
|
32
|
-
['fixed32', 'Int'],
|
|
33
|
-
['fixed64', 'BigInt'],
|
|
34
|
-
['float', 'Float'],
|
|
35
|
-
['int32', 'Int'],
|
|
36
|
-
['int64', 'BigInt'],
|
|
37
|
-
['sfixed32', 'Int'],
|
|
38
|
-
['sfixed64', 'BigInt'],
|
|
39
|
-
['sint32', 'Int'],
|
|
40
|
-
['sint64', 'BigInt'],
|
|
41
|
-
['string', 'String'],
|
|
42
|
-
['uint32', 'UnsignedInt'],
|
|
43
|
-
['uint64', 'BigInt'], // A new scalar might be needed
|
|
44
|
-
]);
|
|
45
|
-
function isScalarType(type) {
|
|
46
|
-
return SCALARS.has(type);
|
|
47
|
-
}
|
|
48
|
-
function getGraphQLScalar(scalarType) {
|
|
49
|
-
const gqlScalar = SCALARS.get(scalarType);
|
|
50
|
-
if (!gqlScalar) {
|
|
51
|
-
throw new Error(`Could not find GraphQL Scalar for type ${scalarType}`);
|
|
52
|
-
}
|
|
53
|
-
return SCALARS.get(scalarType);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function getTypeName(schemaComposer, pathWithName, isInput) {
|
|
57
|
-
if (pathWithName === null || pathWithName === void 0 ? void 0 : pathWithName.length) {
|
|
58
|
-
const baseTypeName = pathWithName.filter(Boolean).join('_');
|
|
59
|
-
if (isScalarType(baseTypeName)) {
|
|
60
|
-
return getGraphQLScalar(baseTypeName);
|
|
61
|
-
}
|
|
62
|
-
if (schemaComposer.isEnumType(baseTypeName)) {
|
|
63
|
-
return baseTypeName;
|
|
64
|
-
}
|
|
65
|
-
return isInput ? baseTypeName + '_Input' : baseTypeName;
|
|
66
|
-
}
|
|
67
|
-
return 'Void';
|
|
68
|
-
}
|
|
69
|
-
function addIncludePathResolver(root, includePaths) {
|
|
70
|
-
const originalResolvePath = root.resolvePath;
|
|
71
|
-
root.resolvePath = (origin, target) => {
|
|
72
|
-
if (path.isAbsolute(target)) {
|
|
73
|
-
return target;
|
|
74
|
-
}
|
|
75
|
-
for (const directory of includePaths) {
|
|
76
|
-
const fullPath = path.join(directory, target);
|
|
77
|
-
if (fs.existsSync(fullPath)) {
|
|
78
|
-
return fullPath;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const path$1 = originalResolvePath(origin, target);
|
|
82
|
-
if (path$1 === null) {
|
|
83
|
-
console.warn(`${target} not found in any of the include paths ${includePaths}`);
|
|
84
|
-
}
|
|
85
|
-
return path$1;
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
function isBlob(input) {
|
|
89
|
-
return input != null && input.stream instanceof Function;
|
|
90
|
-
}
|
|
91
|
-
function addMetaDataToCall(callFn, input, context, metaData, isResponseStream = false) {
|
|
92
|
-
const callFnArguments = [];
|
|
93
|
-
if (!isBlob(input)) {
|
|
94
|
-
callFnArguments.push(input);
|
|
95
|
-
}
|
|
96
|
-
if (metaData) {
|
|
97
|
-
const meta = new Metadata();
|
|
98
|
-
for (const [key, value] of Object.entries(metaData)) {
|
|
99
|
-
let metaValue = value;
|
|
100
|
-
if (Array.isArray(value)) {
|
|
101
|
-
// Extract data from context
|
|
102
|
-
metaValue = lodashGet(context, value);
|
|
103
|
-
}
|
|
104
|
-
// Ensure that the metadata is compatible with what node-grpc expects
|
|
105
|
-
if (typeof metaValue !== 'string' && !(metaValue instanceof Buffer)) {
|
|
106
|
-
metaValue = JSON.stringify(metaValue);
|
|
107
|
-
}
|
|
108
|
-
if (typeof metaValue === 'string') {
|
|
109
|
-
metaValue = stringInterpolator.parse(metaValue, context);
|
|
110
|
-
}
|
|
111
|
-
meta.add(key, metaValue);
|
|
112
|
-
}
|
|
113
|
-
callFnArguments.push(meta);
|
|
114
|
-
}
|
|
115
|
-
return new Promise((resolve, reject) => {
|
|
116
|
-
const call = callFn(...callFnArguments, (error, response) => {
|
|
117
|
-
if (error) {
|
|
118
|
-
reject(error);
|
|
119
|
-
}
|
|
120
|
-
resolve(response);
|
|
121
|
-
});
|
|
122
|
-
if (isResponseStream) {
|
|
123
|
-
let isCancelled = false;
|
|
124
|
-
const responseStreamWithCancel = withCancel(call, () => {
|
|
125
|
-
var _a;
|
|
126
|
-
if (!isCancelled) {
|
|
127
|
-
(_a = call.call) === null || _a === void 0 ? void 0 : _a.cancelWithStatus(0, 'Cancelled by GraphQL Mesh');
|
|
128
|
-
isCancelled = true;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
resolve(responseStreamWithCancel);
|
|
132
|
-
if (isBlob(input)) {
|
|
133
|
-
input.stream().pipe(call);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/* eslint-disable import/no-duplicates */
|
|
9
|
+
import descriptor from 'protobufjs/ext/descriptor/index.js';
|
|
10
|
+
import { Client } from '@ardatan/grpc-reflection-js';
|
|
11
|
+
import { path, process } from '@graphql-mesh/cross-helpers';
|
|
12
|
+
import { fs } from '@graphql-mesh/cross-helpers';
|
|
13
|
+
import { stringInterpolator } from '@graphql-mesh/string-interpolation';
|
|
14
|
+
import { credentials, loadPackageDefinition } from '@grpc/grpc-js';
|
|
15
|
+
import { fromJSON } from '@grpc/proto-loader';
|
|
16
|
+
import './patchLongJs.js';
|
|
17
|
+
import { addIncludePathResolver, addMetaDataToCall, getTypeName } from './utils.js';
|
|
140
18
|
const { Root } = protobufjs;
|
|
141
|
-
const QUERY_METHOD_PREFIXES = ['get', 'list'];
|
|
142
|
-
class GrpcHandler {
|
|
143
|
-
constructor({ config, baseDir, store, logger }) {
|
|
19
|
+
const QUERY_METHOD_PREFIXES = ['get', 'list', 'search'];
|
|
20
|
+
export default class GrpcHandler {
|
|
21
|
+
constructor({ config, baseDir, store, logger, pubsub, }) {
|
|
144
22
|
this.schemaComposer = new SchemaComposer();
|
|
145
23
|
this.logger = logger;
|
|
146
24
|
this.config = config;
|
|
147
25
|
this.baseDir = baseDir;
|
|
148
|
-
this.
|
|
149
|
-
codify:
|
|
150
|
-
import { FileDescriptorSet } from 'protobufjs/ext/descriptor/index.js';
|
|
151
|
-
|
|
26
|
+
this.rootJsonEntries = store.proxy('rootJsonEntries', {
|
|
27
|
+
codify: rootJsonEntries => `
|
|
152
28
|
export default [
|
|
153
|
-
${
|
|
154
|
-
.map(({ name, rootJson
|
|
29
|
+
${rootJsonEntries
|
|
30
|
+
.map(({ name, rootJson }) => `
|
|
155
31
|
{
|
|
156
32
|
name: ${JSON.stringify(name)},
|
|
157
|
-
decodedDescriptorSet: FileDescriptorSet.fromObject(${JSON.stringify(decodedDescriptorSet.toJSON(), null, 2)}),
|
|
158
33
|
rootJson: ${JSON.stringify(rootJson, null, 2)},
|
|
159
34
|
},
|
|
160
35
|
`)
|
|
@@ -162,29 +37,32 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
162
37
|
];
|
|
163
38
|
`.trim(),
|
|
164
39
|
fromJSON: jsonData => {
|
|
165
|
-
return jsonData.map(({ name, rootJson
|
|
40
|
+
return jsonData.map(({ name, rootJson }) => ({
|
|
166
41
|
name,
|
|
167
42
|
rootJson,
|
|
168
|
-
decodedDescriptorSet: FileDescriptorSet.fromObject(decodedDescriptorSet),
|
|
169
43
|
}));
|
|
170
44
|
},
|
|
171
|
-
toJSON:
|
|
172
|
-
return
|
|
45
|
+
toJSON: rootJsonEntries => {
|
|
46
|
+
return rootJsonEntries.map(({ name, rootJson }) => {
|
|
173
47
|
return {
|
|
174
48
|
name,
|
|
175
49
|
rootJson,
|
|
176
|
-
decodedDescriptorSet: decodedDescriptorSet.toJSON(),
|
|
177
50
|
};
|
|
178
51
|
});
|
|
179
52
|
},
|
|
180
53
|
validate: () => { },
|
|
181
54
|
});
|
|
55
|
+
this.pubsub = pubsub;
|
|
182
56
|
}
|
|
183
|
-
async
|
|
57
|
+
async processReflection(creds) {
|
|
184
58
|
this.logger.debug(`Using the reflection`);
|
|
185
59
|
const grpcReflectionServer = this.config.endpoint;
|
|
186
60
|
this.logger.debug(`Creating gRPC Reflection Client`);
|
|
187
|
-
const reflectionClient = new
|
|
61
|
+
const reflectionClient = new Client(grpcReflectionServer, creds);
|
|
62
|
+
const subId = this.pubsub.subscribe('destroy', () => {
|
|
63
|
+
reflectionClient.grpcClient.close();
|
|
64
|
+
this.pubsub.unsubscribe(subId);
|
|
65
|
+
});
|
|
188
66
|
const services = await reflectionClient.listServices();
|
|
189
67
|
const userServices = services.filter(service => service && !(service === null || service === void 0 ? void 0 : service.startsWith('grpc.')));
|
|
190
68
|
return userServices.map(async (service) => {
|
|
@@ -193,21 +71,23 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
193
71
|
return serviceRoot;
|
|
194
72
|
});
|
|
195
73
|
}
|
|
196
|
-
async
|
|
74
|
+
async processDescriptorFile() {
|
|
197
75
|
var _a;
|
|
198
76
|
let fileName;
|
|
199
77
|
let options;
|
|
200
|
-
if (typeof this.config.
|
|
201
|
-
fileName = this.config.
|
|
78
|
+
if (typeof this.config.source === 'object') {
|
|
79
|
+
fileName = this.config.source.file;
|
|
202
80
|
options = {
|
|
203
|
-
...this.config.
|
|
204
|
-
includeDirs: (_a = this.config.
|
|
81
|
+
...this.config.source.load,
|
|
82
|
+
includeDirs: (_a = this.config.source.load.includeDirs) === null || _a === void 0 ? void 0 : _a.map(includeDir => path.isAbsolute(includeDir) ? includeDir : path.join(this.baseDir, includeDir)),
|
|
205
83
|
};
|
|
206
84
|
}
|
|
207
85
|
else {
|
|
208
|
-
fileName = this.config.
|
|
86
|
+
fileName = this.config.source;
|
|
209
87
|
}
|
|
210
|
-
const absoluteFilePath = path.isAbsolute(fileName)
|
|
88
|
+
const absoluteFilePath = path.isAbsolute(fileName)
|
|
89
|
+
? fileName
|
|
90
|
+
: path.join(this.baseDir, fileName);
|
|
211
91
|
this.logger.debug(`Using the descriptor set from ${absoluteFilePath} `);
|
|
212
92
|
const descriptorSetBuffer = await fs.promises.readFile(absoluteFilePath);
|
|
213
93
|
this.logger.debug(`Reading ${absoluteFilePath} `);
|
|
@@ -230,7 +110,7 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
230
110
|
}
|
|
231
111
|
return rootFromDescriptor;
|
|
232
112
|
}
|
|
233
|
-
async
|
|
113
|
+
async processProtoFile() {
|
|
234
114
|
var _a, _b;
|
|
235
115
|
this.logger.debug(`Using proto file(s)`);
|
|
236
116
|
let protoRoot = new Root();
|
|
@@ -239,46 +119,47 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
239
119
|
keepCase: true,
|
|
240
120
|
alternateCommentMode: true,
|
|
241
121
|
};
|
|
242
|
-
if (typeof this.config.
|
|
243
|
-
fileGlob = this.config.
|
|
122
|
+
if (typeof this.config.source === 'object') {
|
|
123
|
+
fileGlob = this.config.source.file;
|
|
244
124
|
options = {
|
|
245
125
|
...options,
|
|
246
|
-
...this.config.
|
|
247
|
-
includeDirs: (_b = (_a = this.config.
|
|
126
|
+
...this.config.source.load,
|
|
127
|
+
includeDirs: (_b = (_a = this.config.source.load) === null || _a === void 0 ? void 0 : _a.includeDirs) === null || _b === void 0 ? void 0 : _b.map(includeDir => path.isAbsolute(includeDir) ? includeDir : path.join(this.baseDir, includeDir)),
|
|
248
128
|
};
|
|
249
129
|
if (options.includeDirs) {
|
|
250
130
|
if (!Array.isArray(options.includeDirs)) {
|
|
251
|
-
|
|
131
|
+
throw new Error('The includeDirs option must be an array');
|
|
252
132
|
}
|
|
253
133
|
addIncludePathResolver(protoRoot, options.includeDirs);
|
|
254
134
|
}
|
|
255
135
|
}
|
|
256
136
|
else {
|
|
257
|
-
fileGlob = this.config.
|
|
137
|
+
fileGlob = this.config.source;
|
|
258
138
|
}
|
|
259
139
|
const fileNames = await globby(fileGlob, {
|
|
260
140
|
cwd: this.baseDir,
|
|
261
141
|
});
|
|
262
142
|
this.logger.debug(`Loading proto files(${fileGlob}); \n ${fileNames.join('\n')} `);
|
|
263
|
-
protoRoot = await protoRoot.load(fileNames.map(filePath =>
|
|
143
|
+
protoRoot = await protoRoot.load(fileNames.map(filePath => path.isAbsolute(filePath) ? filePath : path.join(this.baseDir, filePath)), options);
|
|
264
144
|
this.logger.debug(`Adding proto content to the root`);
|
|
265
145
|
return protoRoot;
|
|
266
146
|
}
|
|
267
147
|
getCachedDescriptorSets(creds) {
|
|
268
|
-
return this.
|
|
148
|
+
return this.rootJsonEntries.getWithSet(async () => {
|
|
269
149
|
const rootPromises = [];
|
|
270
150
|
this.logger.debug(`Building Roots`);
|
|
271
|
-
if (this.config.
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
151
|
+
if (this.config.source) {
|
|
152
|
+
const filePath = typeof this.config.source === 'string' ? this.config.source : this.config.source.file;
|
|
153
|
+
if (filePath.endsWith('json')) {
|
|
154
|
+
rootPromises.push(this.processDescriptorFile());
|
|
155
|
+
}
|
|
156
|
+
else if (filePath.endsWith('proto')) {
|
|
157
|
+
rootPromises.push(this.processProtoFile());
|
|
158
|
+
}
|
|
278
159
|
}
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
rootPromises.push(
|
|
160
|
+
else {
|
|
161
|
+
const reflectionPromises = await this.processReflection(creds);
|
|
162
|
+
rootPromises.push(...reflectionPromises);
|
|
282
163
|
}
|
|
283
164
|
return Promise.all(rootPromises.map(async (root$, i) => {
|
|
284
165
|
const root = await root$;
|
|
@@ -292,7 +173,6 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
292
173
|
rootJson: root.toJSON({
|
|
293
174
|
keepComments: true,
|
|
294
175
|
}),
|
|
295
|
-
decodedDescriptorSet: root.toDescriptor('proto3'),
|
|
296
176
|
};
|
|
297
177
|
}));
|
|
298
178
|
});
|
|
@@ -306,7 +186,10 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
306
186
|
const absoluteCertChainPath = path.isAbsolute(this.config.credentialsSsl.certChain)
|
|
307
187
|
? this.config.credentialsSsl.certChain
|
|
308
188
|
: path.join(this.baseDir, this.config.credentialsSsl.certChain);
|
|
309
|
-
const sslFiles = [
|
|
189
|
+
const sslFiles = [
|
|
190
|
+
fs.promises.readFile(absolutePrivateKeyPath),
|
|
191
|
+
fs.promises.readFile(absoluteCertChainPath),
|
|
192
|
+
];
|
|
310
193
|
if (this.config.credentialsSsl.rootCA !== 'rootCA') {
|
|
311
194
|
const absoluteRootCAPath = path.isAbsolute(this.config.credentialsSsl.rootCA)
|
|
312
195
|
? this.config.credentialsSsl.rootCA
|
|
@@ -384,14 +267,20 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
384
267
|
description,
|
|
385
268
|
fields: {},
|
|
386
269
|
});
|
|
387
|
-
for (const [fieldName, { type, rule, comment }] of fieldEntries) {
|
|
270
|
+
for (const [fieldName, { type, rule, comment, keyType }] of fieldEntries) {
|
|
388
271
|
logger.debug(`Visiting ${currentPath}.nested.fields[${fieldName}]`);
|
|
389
272
|
const baseFieldTypePath = type.split('.');
|
|
390
273
|
inputTC.addFields({
|
|
391
274
|
[fieldName]: {
|
|
392
275
|
type: () => {
|
|
393
|
-
|
|
394
|
-
|
|
276
|
+
let fieldInputTypeName;
|
|
277
|
+
if (keyType) {
|
|
278
|
+
fieldInputTypeName = 'JSON';
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
const fieldTypePath = this.walkToFindTypePath(rootJson, pathWithName, baseFieldTypePath);
|
|
282
|
+
fieldInputTypeName = getTypeName(this.schemaComposer, fieldTypePath, true);
|
|
283
|
+
}
|
|
395
284
|
return rule === 'repeated' ? `[${fieldInputTypeName}]` : fieldInputTypeName;
|
|
396
285
|
},
|
|
397
286
|
description: comment,
|
|
@@ -400,8 +289,14 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
400
289
|
outputTC.addFields({
|
|
401
290
|
[fieldName]: {
|
|
402
291
|
type: () => {
|
|
403
|
-
|
|
404
|
-
|
|
292
|
+
let fieldTypeName;
|
|
293
|
+
if (keyType) {
|
|
294
|
+
fieldTypeName = 'JSON';
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const fieldTypePath = this.walkToFindTypePath(rootJson, pathWithName, baseFieldTypePath);
|
|
298
|
+
fieldTypeName = getTypeName(this.schemaComposer, fieldTypePath, false);
|
|
299
|
+
}
|
|
405
300
|
return rule === 'repeated' ? `[${fieldTypeName}]` : fieldTypeName;
|
|
406
301
|
},
|
|
407
302
|
description: comment,
|
|
@@ -429,6 +324,10 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
429
324
|
throw new Error(`Object at path ${objPath} is not a Service constructor`);
|
|
430
325
|
}
|
|
431
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
|
+
});
|
|
432
331
|
for (const methodName in nested.methods) {
|
|
433
332
|
const method = nested.methods[methodName];
|
|
434
333
|
const rootFieldName = [...pathWithName, methodName].join('_');
|
|
@@ -438,7 +337,11 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
438
337
|
const baseResponseTypePath = (_a = method.responseType) === null || _a === void 0 ? void 0 : _a.split('.');
|
|
439
338
|
if (baseResponseTypePath) {
|
|
440
339
|
const responseTypePath = this.walkToFindTypePath(rootJson, pathWithName, baseResponseTypePath);
|
|
441
|
-
|
|
340
|
+
let typeName = getTypeName(this.schemaComposer, responseTypePath, false);
|
|
341
|
+
if (method.responseStream) {
|
|
342
|
+
typeName = `[${typeName}]`;
|
|
343
|
+
}
|
|
344
|
+
return typeName;
|
|
442
345
|
}
|
|
443
346
|
return 'Void';
|
|
444
347
|
},
|
|
@@ -459,28 +362,22 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
459
362
|
return undefined;
|
|
460
363
|
},
|
|
461
364
|
};
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
[rootFieldName]: {
|
|
479
|
-
...fieldConfig,
|
|
480
|
-
resolve: (_, args, context) => addMetaDataToCall(client[methodName].bind(client), args.input, context, this.config.metaData),
|
|
481
|
-
},
|
|
482
|
-
});
|
|
483
|
-
}
|
|
365
|
+
const methodNameLowerCased = methodName.toLowerCase();
|
|
366
|
+
const prefixQueryMethod = this.config.prefixQueryMethod || QUERY_METHOD_PREFIXES;
|
|
367
|
+
const rootTypeComposer = prefixQueryMethod.some(prefix => methodNameLowerCased.startsWith(prefix))
|
|
368
|
+
? this.schemaComposer.Query
|
|
369
|
+
: this.schemaComposer.Mutation;
|
|
370
|
+
rootTypeComposer.addFields({
|
|
371
|
+
[rootFieldName]: {
|
|
372
|
+
...fieldConfig,
|
|
373
|
+
resolve: (root, args, context, info) => addMetaDataToCall(client[methodName].bind(client), args.input, {
|
|
374
|
+
root,
|
|
375
|
+
args,
|
|
376
|
+
context,
|
|
377
|
+
env: process.env,
|
|
378
|
+
}, this.config.metaData, !!method.responseStream),
|
|
379
|
+
},
|
|
380
|
+
});
|
|
484
381
|
}
|
|
485
382
|
const connectivityStateFieldName = pathWithName.join('_') + '_connectivityState';
|
|
486
383
|
this.schemaComposer.Query.addFields({
|
|
@@ -521,14 +418,26 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
521
418
|
const creds = await this.getCredentials();
|
|
522
419
|
this.logger.debug(`Getting stored root and decoded descriptor set objects`);
|
|
523
420
|
const artifacts = await this.getCachedDescriptorSets(creds);
|
|
524
|
-
for (const { name, rootJson
|
|
421
|
+
for (const { name, rootJson } of artifacts) {
|
|
525
422
|
const rootLogger = this.logger.child(name);
|
|
526
423
|
rootLogger.debug(`Creating package definition from file descriptor set object`);
|
|
527
|
-
|
|
424
|
+
let options;
|
|
425
|
+
if (typeof this.config.source === 'object') {
|
|
426
|
+
options = this.config.source.load;
|
|
427
|
+
}
|
|
428
|
+
const packageDefinition = fromJSON(rootJson, options);
|
|
528
429
|
rootLogger.debug(`Creating service client for package definition`);
|
|
529
430
|
const grpcObject = loadPackageDefinition(packageDefinition);
|
|
530
431
|
this.logger.debug(`Building the schema structure based on the root object`);
|
|
531
|
-
this.visit({
|
|
432
|
+
this.visit({
|
|
433
|
+
nested: rootJson,
|
|
434
|
+
name: '',
|
|
435
|
+
currentPath: [],
|
|
436
|
+
rootJson,
|
|
437
|
+
creds,
|
|
438
|
+
grpcObject,
|
|
439
|
+
rootLogger,
|
|
440
|
+
});
|
|
532
441
|
}
|
|
533
442
|
// graphql-compose doesn't add @defer and @stream to the schema
|
|
534
443
|
specifiedDirectives.forEach(directive => this.schemaComposer.addDirective(directive));
|
|
@@ -539,5 +448,3 @@ ${rootJsonAndDecodedDescriptorSets
|
|
|
539
448
|
};
|
|
540
449
|
}
|
|
541
450
|
}
|
|
542
|
-
|
|
543
|
-
export default GrpcHandler;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Long from 'long';
|
|
2
|
+
function patchLongJs() {
|
|
3
|
+
const originalLongFromValue = Long.fromValue.bind(Long);
|
|
4
|
+
Long.fromValue = (value) => {
|
|
5
|
+
if (typeof value === 'bigint') {
|
|
6
|
+
return Long.fromValue(value.toString());
|
|
7
|
+
}
|
|
8
|
+
return originalLongFromValue(value);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
patchLongJs();
|
package/esm/scalars.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const SCALARS = new Map([
|
|
2
|
+
['bool', 'Boolean'],
|
|
3
|
+
['bytes', 'Byte'],
|
|
4
|
+
['double', 'Float'],
|
|
5
|
+
['fixed32', 'Int'],
|
|
6
|
+
['fixed64', 'BigInt'],
|
|
7
|
+
['float', 'Float'],
|
|
8
|
+
['int32', 'Int'],
|
|
9
|
+
['int64', 'BigInt'],
|
|
10
|
+
['sfixed32', 'Int'],
|
|
11
|
+
['sfixed64', 'BigInt'],
|
|
12
|
+
['sint32', 'Int'],
|
|
13
|
+
['sint64', 'BigInt'],
|
|
14
|
+
['string', 'String'],
|
|
15
|
+
['uint32', 'UnsignedInt'],
|
|
16
|
+
['uint64', 'BigInt'], // A new scalar might be needed
|
|
17
|
+
]);
|
|
18
|
+
export function isScalarType(type) {
|
|
19
|
+
return SCALARS.has(type);
|
|
20
|
+
}
|
|
21
|
+
export function getGraphQLScalar(scalarType) {
|
|
22
|
+
const gqlScalar = SCALARS.get(scalarType);
|
|
23
|
+
if (!gqlScalar) {
|
|
24
|
+
throw new Error(`Could not find GraphQL Scalar for type ${scalarType}`);
|
|
25
|
+
}
|
|
26
|
+
return SCALARS.get(scalarType);
|
|
27
|
+
}
|
package/esm/utils.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import lodashGet from 'lodash.get';
|
|
2
|
+
import { fs, path as pathModule } from '@graphql-mesh/cross-helpers';
|
|
3
|
+
import { stringInterpolator } from '@graphql-mesh/string-interpolation';
|
|
4
|
+
import { withCancel } from '@graphql-mesh/utils';
|
|
5
|
+
import { Metadata, } from '@grpc/grpc-js';
|
|
6
|
+
import { getGraphQLScalar, isScalarType } from './scalars.js';
|
|
7
|
+
export function getTypeName(schemaComposer, pathWithName, isInput) {
|
|
8
|
+
if (pathWithName === null || pathWithName === void 0 ? void 0 : pathWithName.length) {
|
|
9
|
+
const baseTypeName = pathWithName.filter(Boolean).join('_');
|
|
10
|
+
if (isScalarType(baseTypeName)) {
|
|
11
|
+
return getGraphQLScalar(baseTypeName);
|
|
12
|
+
}
|
|
13
|
+
if (schemaComposer.isEnumType(baseTypeName)) {
|
|
14
|
+
return baseTypeName;
|
|
15
|
+
}
|
|
16
|
+
return isInput ? baseTypeName + '_Input' : baseTypeName;
|
|
17
|
+
}
|
|
18
|
+
return 'Void';
|
|
19
|
+
}
|
|
20
|
+
export function addIncludePathResolver(root, includePaths) {
|
|
21
|
+
const originalResolvePath = root.resolvePath;
|
|
22
|
+
root.resolvePath = (origin, target) => {
|
|
23
|
+
if (pathModule.isAbsolute(target)) {
|
|
24
|
+
return target;
|
|
25
|
+
}
|
|
26
|
+
for (const directory of includePaths) {
|
|
27
|
+
const fullPath = pathModule.join(directory, target);
|
|
28
|
+
if (fs.existsSync(fullPath)) {
|
|
29
|
+
return fullPath;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const path = originalResolvePath(origin, target);
|
|
33
|
+
if (path === null) {
|
|
34
|
+
console.warn(`${target} not found in any of the include paths ${includePaths}`);
|
|
35
|
+
}
|
|
36
|
+
return path;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function isBlob(input) {
|
|
40
|
+
return input != null && input.stream instanceof Function;
|
|
41
|
+
}
|
|
42
|
+
export function addMetaDataToCall(callFn, input, resolverData, metaData, isResponseStream = false) {
|
|
43
|
+
const callFnArguments = [];
|
|
44
|
+
if (!isBlob(input)) {
|
|
45
|
+
callFnArguments.push(input);
|
|
46
|
+
}
|
|
47
|
+
if (metaData) {
|
|
48
|
+
const meta = new Metadata();
|
|
49
|
+
for (const [key, value] of Object.entries(metaData)) {
|
|
50
|
+
let metaValue = value;
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
// Extract data from context
|
|
53
|
+
metaValue = lodashGet(resolverData.context, value);
|
|
54
|
+
}
|
|
55
|
+
// Ensure that the metadata is compatible with what node-grpc expects
|
|
56
|
+
if (typeof metaValue !== 'string' && !(metaValue instanceof Buffer)) {
|
|
57
|
+
metaValue = JSON.stringify(metaValue);
|
|
58
|
+
}
|
|
59
|
+
if (typeof metaValue === 'string') {
|
|
60
|
+
metaValue = stringInterpolator.parse(metaValue, resolverData);
|
|
61
|
+
}
|
|
62
|
+
meta.add(key, metaValue);
|
|
63
|
+
}
|
|
64
|
+
callFnArguments.push(meta);
|
|
65
|
+
}
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const call = callFn(...callFnArguments, (error, response) => {
|
|
68
|
+
if (error) {
|
|
69
|
+
reject(error);
|
|
70
|
+
}
|
|
71
|
+
resolve(response);
|
|
72
|
+
});
|
|
73
|
+
if (isResponseStream) {
|
|
74
|
+
let isCancelled = false;
|
|
75
|
+
const responseStreamWithCancel = withCancel(call, () => {
|
|
76
|
+
var _a;
|
|
77
|
+
if (!isCancelled) {
|
|
78
|
+
(_a = call.call) === null || _a === void 0 ? void 0 : _a.cancelWithStatus(0, 'Cancelled by GraphQL Mesh');
|
|
79
|
+
isCancelled = true;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
resolve(responseStreamWithCancel);
|
|
83
|
+
if (isBlob(input)) {
|
|
84
|
+
const blobStream = input.stream();
|
|
85
|
+
blobStream.pipe(call);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|