UNPKG

26.1 kBJavaScriptView Raw
1import { parse, SyntaxType } from '@creditkarma/thrift-parser';
2import { readFileOrUrlWithCache, parseInterpolationStrings, getInterpolatedHeadersFactory } from '@graphql-mesh/utils';
3import AggregateError from '@ardatan/aggregate-error';
4import { GraphQLNonNull, GraphQLObjectType, GraphQLInputObjectType, GraphQLEnumType, GraphQLSchema, GraphQLList, GraphQLInt, GraphQLBoolean, GraphQLFloat, GraphQLString } from 'graphql';
5import { GraphQLJSON, GraphQLBigInt, GraphQLByte, GraphQLVoid } from 'graphql-scalars';
6import { createHttpClient } from '@creditkarma/thrift-client';
7import { TType, ThriftClient, MessageType, TApplicationExceptionCodec, TApplicationException, TApplicationExceptionType } from '@creditkarma/thrift-server-core';
8
9/*! *****************************************************************************
10Copyright (c) Microsoft Corporation.
11
12Permission to use, copy, modify, and/or distribute this software for any
13purpose with or without fee is hereby granted.
14
15THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
16REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
18INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21PERFORMANCE OF THIS SOFTWARE.
22***************************************************************************** */
23
24var __assign = function() {
25 __assign = Object.assign || function __assign(t) {
26 for (var s, i = 1, n = arguments.length; i < n; i++) {
27 s = arguments[i];
28 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
29 }
30 return t;
31 };
32 return __assign.apply(this, arguments);
33};
34
35/**
36 * Source: ftp://ftp.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt
37 */
38/**
39 * Lower case as a function.
40 */
41function lowerCase(str) {
42 return str.toLowerCase();
43}
44
45// Support camel case ("camelCase" -> "camel Case" and "CAMELCase" -> "CAMEL Case").
46var DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g];
47// Remove all non-word characters.
48var DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;
49/**
50 * Normalize the string into something other libraries can manipulate easier.
51 */
52function noCase(input, options) {
53 if (options === void 0) { options = {}; }
54 var _a = options.splitRegexp, splitRegexp = _a === void 0 ? DEFAULT_SPLIT_REGEXP : _a, _b = options.stripRegexp, stripRegexp = _b === void 0 ? DEFAULT_STRIP_REGEXP : _b, _c = options.transform, transform = _c === void 0 ? lowerCase : _c, _d = options.delimiter, delimiter = _d === void 0 ? " " : _d;
55 var result = replace(replace(input, splitRegexp, "$1\0$2"), stripRegexp, "\0");
56 var start = 0;
57 var end = result.length;
58 // Trim the delimiter from around the output string.
59 while (result.charAt(start) === "\0")
60 start++;
61 while (result.charAt(end - 1) === "\0")
62 end--;
63 // Transform each token independently.
64 return result.slice(start, end).split("\0").map(transform).join(delimiter);
65}
66/**
67 * Replace `re` in the input string with the replacement value.
68 */
69function replace(input, re, value) {
70 if (re instanceof RegExp)
71 return input.replace(re, value);
72 return re.reduce(function (input, re) { return input.replace(re, value); }, input);
73}
74
75function pascalCaseTransform(input, index) {
76 var firstChar = input.charAt(0);
77 var lowerChars = input.substr(1).toLowerCase();
78 if (index > 0 && firstChar >= "0" && firstChar <= "9") {
79 return "_" + firstChar + lowerChars;
80 }
81 return "" + firstChar.toUpperCase() + lowerChars;
82}
83function pascalCase(input, options) {
84 if (options === void 0) { options = {}; }
85 return noCase(input, __assign({ delimiter: "", transform: pascalCaseTransform }, options));
86}
87
88class ThriftHandler {
89 constructor({ config, cache }) {
90 this.config = config;
91 this.cache = cache;
92 }
93 async getMeshSource() {
94 var _a, _b;
95 const { schemaHeaders, serviceName, operationHeaders } = this.config;
96 const rawThrift = await readFileOrUrlWithCache(this.config.idl, this.cache, {
97 allowUnknownExtensions: true,
98 headers: schemaHeaders,
99 });
100 const thriftAST = parse(rawThrift, { organize: false });
101 const enumTypeMap = new Map();
102 const outputTypeMap = new Map();
103 const inputTypeMap = new Map();
104 const rootFields = {};
105 const annotations = {};
106 const methodAnnotations = {};
107 const methodNames = [];
108 const methodParameters = {};
109 const topTypeMap = {};
110 class MeshThriftClient extends ThriftClient {
111 constructor() {
112 super(...arguments);
113 this._serviceName = serviceName;
114 this._annotations = annotations;
115 this._methodAnnotations = methodAnnotations;
116 this._methodNames = methodNames;
117 this._methodParameters = methodParameters;
118 }
119 writeType(typeVal, value, output) {
120 switch (typeVal.type) {
121 case TType.BOOL:
122 output.writeBool(value);
123 break;
124 case TType.BYTE:
125 output.writeByte(value);
126 break;
127 case TType.DOUBLE:
128 output.writeDouble(value);
129 break;
130 case TType.I16:
131 output.writeI16(value);
132 break;
133 case TType.I32:
134 output.writeI32(value);
135 break;
136 case TType.I64:
137 output.writeI64(value.toString());
138 break;
139 case TType.STRING:
140 output.writeString(value);
141 break;
142 case TType.STRUCT: {
143 output.writeStructBegin(typeVal.name);
144 const typeMap = typeVal.fields;
145 for (const argName in value) {
146 const argType = typeMap[argName];
147 const argVal = value[argName];
148 if (argType) {
149 output.writeFieldBegin(argName, argType.type, argType.id);
150 this.writeType(argType, argVal, output);
151 output.writeFieldEnd();
152 }
153 }
154 output.writeFieldStop();
155 output.writeStructEnd();
156 break;
157 }
158 case TType.ENUM:
159 // TODO: A
160 break;
161 case TType.MAP: {
162 const keys = Object.keys(value);
163 output.writeMapBegin(typeVal.keyType.type, typeVal.valType.type, keys.length);
164 for (const key of keys) {
165 this.writeType(typeVal.keyType, key, output);
166 const val = value[key];
167 this.writeType(typeVal.valType, val, output);
168 }
169 output.writeMapEnd();
170 break;
171 }
172 case TType.LIST:
173 output.writeListBegin(typeVal.elementType.type, value.length);
174 for (const element of value) {
175 this.writeType(typeVal.elementType, element, output);
176 }
177 output.writeListEnd();
178 break;
179 case TType.SET:
180 output.writeSetBegin(typeVal.elementType.type, value.length);
181 for (const element of value) {
182 this.writeType(typeVal.elementType, element, output);
183 }
184 output.writeSetEnd();
185 break;
186 }
187 }
188 readType(type, input) {
189 switch (type) {
190 case TType.BOOL:
191 return input.readBool();
192 case TType.BYTE:
193 return input.readByte();
194 case TType.DOUBLE:
195 return input.readDouble();
196 case TType.I16:
197 return input.readI16();
198 case TType.I32:
199 return input.readI32();
200 case TType.I64:
201 return BigInt(input.readI64().toString());
202 case TType.STRING:
203 return input.readString();
204 case TType.STRUCT: {
205 const result = {};
206 input.readStructBegin();
207 while (true) {
208 const field = input.readFieldBegin();
209 const fieldType = field.fieldType;
210 const fieldName = field.fieldName || 'success';
211 if (fieldType === TType.STOP) {
212 break;
213 }
214 result[fieldName] = this.readType(fieldType, input);
215 input.readFieldEnd();
216 }
217 input.readStructEnd();
218 return result;
219 }
220 case TType.ENUM:
221 // TODO: A
222 break;
223 case TType.MAP: {
224 const result = {};
225 const map = input.readMapBegin();
226 for (let i = 0; i < map.size; i++) {
227 const key = this.readType(map.keyType, input);
228 const value = this.readType(map.valueType, input);
229 result[key] = value;
230 }
231 input.readMapEnd();
232 return result;
233 }
234 case TType.LIST: {
235 const result = [];
236 const list = input.readListBegin();
237 for (let i = 0; i < list.size; i++) {
238 const element = this.readType(list.elementType, input);
239 result.push(element);
240 }
241 input.readListEnd();
242 return result;
243 }
244 case TType.SET: {
245 const result = [];
246 const list = input.readSetBegin();
247 for (let i = 0; i < list.size; i++) {
248 const element = this.readType(list.elementType, input);
249 result.push(element);
250 }
251 input.readSetEnd();
252 return result;
253 }
254 }
255 }
256 async doRequest(methodName, args, fields, context) {
257 const Transport = this.transport;
258 const Protocol = this.protocol;
259 const writer = new Transport();
260 const output = new Protocol(writer);
261 const id = this.incrementRequestId();
262 output.writeMessageBegin(methodName, MessageType.CALL, id);
263 this.writeType({
264 name: pascalCase(methodName) + '__Args',
265 type: TType.STRUCT,
266 fields,
267 id,
268 }, args, output);
269 output.writeMessageEnd();
270 const data = await this.connection.send(writer.flush(), context);
271 const reader = this.transport.receiver(data);
272 const input = new Protocol(reader);
273 const { fieldName, messageType } = input.readMessageBegin();
274 if (fieldName === methodName) {
275 if (messageType === MessageType.EXCEPTION) {
276 const err = TApplicationExceptionCodec.decode(input);
277 input.readMessageEnd();
278 return Promise.reject(err);
279 }
280 else {
281 const result = this.readType(TType.STRUCT, input);
282 input.readMessageEnd();
283 if (result.success != null) {
284 return result.success;
285 }
286 else {
287 throw new TApplicationException(TApplicationExceptionType.UNKNOWN, methodName + ' failed: unknown result');
288 }
289 }
290 }
291 else {
292 throw new TApplicationException(TApplicationExceptionType.WRONG_METHOD_NAME, 'Received a response to an unknown RPC function: ' + fieldName);
293 }
294 }
295 }
296 MeshThriftClient.serviceName = serviceName;
297 MeshThriftClient.annotations = annotations;
298 MeshThriftClient.methodAnnotations = methodAnnotations;
299 MeshThriftClient.methodNames = methodNames;
300 const thriftHttpClient = createHttpClient(MeshThriftClient, {
301 ...this.config,
302 requestOptions: {
303 headers: operationHeaders,
304 },
305 });
306 function processComments(comments) {
307 return comments.map(comment => comment.value).join('\n');
308 }
309 function getGraphQLFunctionType(functionType, id = Math.random()) {
310 let inputType;
311 let outputType;
312 let typeVal;
313 switch (functionType.type) {
314 case SyntaxType.BinaryKeyword:
315 case SyntaxType.StringKeyword:
316 inputType = GraphQLString;
317 outputType = GraphQLString;
318 break;
319 case SyntaxType.DoubleKeyword:
320 inputType = GraphQLFloat;
321 outputType = GraphQLFloat;
322 typeVal = typeVal || { type: TType.DOUBLE };
323 break;
324 case SyntaxType.VoidKeyword:
325 typeVal = typeVal || { type: TType.VOID };
326 inputType = GraphQLVoid;
327 outputType = GraphQLVoid;
328 break;
329 case SyntaxType.BoolKeyword:
330 typeVal = typeVal || { type: TType.BOOL };
331 inputType = GraphQLBoolean;
332 outputType = GraphQLBoolean;
333 break;
334 case SyntaxType.I8Keyword:
335 inputType = GraphQLInt;
336 outputType = GraphQLInt;
337 typeVal = typeVal || { type: TType.I08 };
338 break;
339 case SyntaxType.I16Keyword:
340 inputType = GraphQLInt;
341 outputType = GraphQLInt;
342 typeVal = typeVal || { type: TType.I16 };
343 break;
344 case SyntaxType.I32Keyword:
345 inputType = GraphQLInt;
346 outputType = GraphQLInt;
347 typeVal = typeVal || { type: TType.I32 };
348 break;
349 case SyntaxType.ByteKeyword:
350 inputType = GraphQLByte;
351 outputType = GraphQLByte;
352 typeVal = typeVal || { type: TType.BYTE };
353 break;
354 case SyntaxType.I64Keyword:
355 inputType = GraphQLBigInt;
356 outputType = GraphQLBigInt;
357 typeVal = typeVal || { type: TType.I64 };
358 break;
359 case SyntaxType.ListType: {
360 const ofTypeList = getGraphQLFunctionType(functionType.valueType, id);
361 inputType = new GraphQLList(ofTypeList.inputType);
362 outputType = new GraphQLList(ofTypeList.outputType);
363 typeVal = typeVal || { type: TType.LIST, elementType: ofTypeList.typeVal };
364 break;
365 }
366 case SyntaxType.SetType: {
367 const ofSetType = getGraphQLFunctionType(functionType.valueType, id);
368 inputType = new GraphQLList(ofSetType.inputType);
369 outputType = new GraphQLList(ofSetType.outputType);
370 typeVal = typeVal || { type: TType.SET, elementType: ofSetType.typeVal };
371 break;
372 }
373 case SyntaxType.MapType: {
374 inputType = GraphQLJSON;
375 outputType = GraphQLJSON;
376 const ofTypeKey = getGraphQLFunctionType(functionType.keyType, id);
377 const ofTypeValue = getGraphQLFunctionType(functionType.valueType, id);
378 typeVal = typeVal || { type: TType.MAP, keyType: ofTypeKey.typeVal, valType: ofTypeValue.typeVal };
379 break;
380 }
381 case SyntaxType.Identifier: {
382 const typeName = functionType.value;
383 if (enumTypeMap.has(typeName)) {
384 const enumType = enumTypeMap.get(typeName);
385 inputType = enumType;
386 outputType = enumType;
387 }
388 if (inputTypeMap.has(typeName)) {
389 inputType = inputTypeMap.get(typeName);
390 }
391 if (outputTypeMap.has(typeName)) {
392 outputType = outputTypeMap.get(typeName);
393 }
394 typeVal = topTypeMap[typeName];
395 break;
396 }
397 default:
398 throw new Error(`Unknown function type: ${JSON.stringify(functionType, null, 2)}!`);
399 }
400 return {
401 inputType: inputType,
402 outputType: outputType,
403 typeVal: {
404 ...typeVal,
405 id,
406 },
407 };
408 }
409 const { args: commonArgs, contextVariables } = parseInterpolationStrings(Object.values(operationHeaders || {}));
410 const headersFactory = getInterpolatedHeadersFactory(operationHeaders);
411 switch (thriftAST.type) {
412 case SyntaxType.ThriftDocument: {
413 for (const statement of thriftAST.body) {
414 switch (statement.type) {
415 case SyntaxType.EnumDefinition:
416 enumTypeMap.set(statement.name.value, new GraphQLEnumType({
417 name: statement.name.value,
418 description: processComments(statement.comments),
419 values: statement.members.reduce((prev, curr) => ({
420 ...prev,
421 [curr.name.value]: {
422 description: processComments(curr.comments),
423 value: curr.name.value,
424 },
425 }), {}),
426 }));
427 break;
428 case SyntaxType.StructDefinition: {
429 const structName = statement.name.value;
430 const description = processComments(statement.comments);
431 const objectFields = {};
432 const inputObjectFields = {};
433 const structTypeVal = {
434 id: Math.random(),
435 name: structName,
436 type: TType.STRUCT,
437 fields: {},
438 };
439 topTypeMap[structName] = structTypeVal;
440 const structFieldTypeMap = structTypeVal.fields;
441 for (const field of statement.fields) {
442 const fieldName = field.name.value;
443 let fieldOutputType;
444 let fieldInputType;
445 const description = processComments(field.comments);
446 const processedFieldTypes = getGraphQLFunctionType(field.fieldType, (_a = field.fieldID) === null || _a === void 0 ? void 0 : _a.value);
447 fieldOutputType = processedFieldTypes.outputType;
448 fieldInputType = processedFieldTypes.inputType;
449 if (field.requiredness === 'required') {
450 fieldOutputType = new GraphQLNonNull(fieldOutputType);
451 fieldInputType = new GraphQLNonNull(fieldInputType);
452 }
453 objectFields[fieldName] = {
454 type: fieldOutputType,
455 description,
456 };
457 inputObjectFields[fieldName] = {
458 type: fieldInputType,
459 description,
460 };
461 structFieldTypeMap[fieldName] = processedFieldTypes.typeVal;
462 }
463 outputTypeMap.set(structName, new GraphQLObjectType({
464 name: structName,
465 description,
466 fields: objectFields,
467 }));
468 inputTypeMap.set(structName, new GraphQLInputObjectType({
469 name: structName + 'Input',
470 description,
471 fields: inputObjectFields,
472 }));
473 break;
474 }
475 case SyntaxType.ServiceDefinition:
476 for (const fnIndex in statement.functions) {
477 const fn = statement.functions[fnIndex];
478 const fnName = fn.name.value;
479 const description = processComments(fn.comments);
480 const { outputType: returnType } = getGraphQLFunctionType(fn.returnType, Number(fnIndex) + 1);
481 const args = {
482 ...commonArgs,
483 };
484 const fieldTypeMap = {};
485 for (const field of fn.fields) {
486 const fieldName = field.name.value;
487 const fieldDescription = processComments(field.comments);
488 let { inputType: fieldType, typeVal } = getGraphQLFunctionType(field.fieldType, (_b = field.fieldID) === null || _b === void 0 ? void 0 : _b.value);
489 if (field.requiredness === 'required') {
490 fieldType = new GraphQLNonNull(fieldType);
491 }
492 args[fieldName] = {
493 type: fieldType,
494 description: fieldDescription,
495 };
496 fieldTypeMap[fieldName] = typeVal;
497 }
498 rootFields[fnName] = {
499 type: returnType,
500 description,
501 args,
502 resolve: async (root, args, context, info) => thriftHttpClient.doRequest(fnName, args, fieldTypeMap, {
503 headers: headersFactory({ root, args, context, info }),
504 }),
505 };
506 methodNames.push(fnName);
507 methodAnnotations[fnName] = { annotations: {}, fieldAnnotations: {} };
508 methodParameters[fnName] = fn.fields.length + 1;
509 }
510 break;
511 case SyntaxType.TypedefDefinition: {
512 const { inputType, outputType } = getGraphQLFunctionType(statement.definitionType, Math.random());
513 const typeName = statement.name.value;
514 inputTypeMap.set(typeName, inputType);
515 outputTypeMap.set(typeName, outputType);
516 break;
517 }
518 }
519 }
520 const queryObjectType = new GraphQLObjectType({
521 name: 'Query',
522 fields: rootFields,
523 });
524 const schema = new GraphQLSchema({
525 query: queryObjectType,
526 });
527 return {
528 schema,
529 contextVariables,
530 };
531 }
532 // break;
533 case SyntaxType.ThriftErrors:
534 throw new AggregateError(thriftAST.errors);
535 }
536 }
537}
538
539export default ThriftHandler;
540//# sourceMappingURL=index.esm.js.map