@graphql-mesh/transport-grpc
Version:
205 lines (204 loc) • 10.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GrpcTransportHelper = void 0;
const tslib_1 = require("tslib");
const graphql_1 = require("graphql");
const graphql_scalars_1 = require("graphql-scalars");
const lodash_get_1 = tslib_1.__importDefault(require("lodash.get"));
const cross_helpers_1 = require("@graphql-mesh/cross-helpers");
const string_interpolation_1 = require("@graphql-mesh/string-interpolation");
const transport_common_1 = require("@graphql-mesh/transport-common");
const utils_1 = require("@graphql-tools/utils");
const grpc_js_1 = require("@grpc/grpc-js");
const proto_loader_1 = require("@grpc/proto-loader");
const disposablestack_1 = require("@whatwg-node/disposablestack");
const utils_js_1 = require("./utils.js");
require("./patchLongJs.js");
class GrpcTransportHelper extends disposablestack_1.DisposableStack {
constructor(baseDir, logger, endpoint, config) {
super();
this.baseDir = baseDir;
this.logger = logger;
this.endpoint = endpoint;
this.config = config;
this.grpcObjectByserviceClientByObjPath = new WeakMap();
}
getCredentials() {
this.logger.debug(`Getting channel credentials`);
if (this.config.credentialsSsl) {
this.logger.debug(() => `Using SSL Connection with credentials at ${this.config.credentialsSsl.privateKey} & ${this.config.credentialsSsl.certChain}`);
const absolutePrivateKeyPath = cross_helpers_1.path.isAbsolute(this.config.credentialsSsl.privateKey)
? this.config.credentialsSsl.privateKey
: cross_helpers_1.path.join(this.baseDir, this.config.credentialsSsl.privateKey);
const absoluteCertChainPath = cross_helpers_1.path.isAbsolute(this.config.credentialsSsl.certChain)
? this.config.credentialsSsl.certChain
: cross_helpers_1.path.join(this.baseDir, this.config.credentialsSsl.certChain);
const sslFiles = [
cross_helpers_1.fs.promises.readFile(absolutePrivateKeyPath),
cross_helpers_1.fs.promises.readFile(absoluteCertChainPath),
];
if (this.config.credentialsSsl.rootCA !== 'rootCA') {
const absoluteRootCAPath = cross_helpers_1.path.isAbsolute(this.config.credentialsSsl.rootCA)
? this.config.credentialsSsl.rootCA
: cross_helpers_1.path.join(this.baseDir, this.config.credentialsSsl.rootCA);
sslFiles.unshift(cross_helpers_1.fs.promises.readFile(absoluteRootCAPath));
}
return Promise.all(sslFiles).then(([rootCA, privateKey, certChain]) => grpc_js_1.credentials.createSsl(rootCA, privateKey, certChain));
}
else if (this.config.useHTTPS) {
this.logger.debug(`Using SSL Connection`);
return grpc_js_1.credentials.createSsl();
}
this.logger.debug(`Using insecure connection`);
return grpc_js_1.credentials.createInsecure();
}
getGrpcObject({ rootJson, loadOptions, rootLogger, }) {
const packageDefinition = (0, proto_loader_1.fromJSON)(rootJson, loadOptions);
rootLogger.debug(`Creating service client for package definition`);
const grpcObject = (0, grpc_js_1.loadPackageDefinition)(packageDefinition);
return grpcObject;
}
getServiceClient({ grpcObject, objPath, creds, }) {
let serviceClientByObjPath = this.grpcObjectByserviceClientByObjPath.get(grpcObject);
if (!serviceClientByObjPath) {
serviceClientByObjPath = new Map();
this.grpcObjectByserviceClientByObjPath.set(grpcObject, serviceClientByObjPath);
}
let client = serviceClientByObjPath.get(objPath);
if (!client) {
const ServiceClient = (0, lodash_get_1.default)(grpcObject, objPath);
if (typeof ServiceClient !== 'function') {
throw new Error(`Object at path ${objPath} is not a Service constructor`);
}
client = new ServiceClient(string_interpolation_1.stringInterpolator.parse(this.endpoint, { env: cross_helpers_1.process.env }) ?? this.endpoint, creds);
this.defer(() => client.close());
serviceClientByObjPath.set(objPath, client);
}
return client;
}
getFieldResolver({ client, methodName, isResponseStream, }) {
const metaData = this.config.metaData;
const clientMethod = client[methodName].bind(client);
return function grpcFieldResolver(root, args, context) {
return (0, utils_js_1.addMetaDataToCall)(clientMethod, args.input, {
root,
args,
context,
env: cross_helpers_1.process.env,
}, metaData, isResponseStream);
};
}
getConnectivityStateResolver({ client, }) {
return function connectivityStateResolver(_, { tryToConnect }) {
return client.getChannel().getConnectivityState(tryToConnect);
};
}
processDirectives({ schema, creds }) {
const schemaTypeMap = schema.getTypeMap();
for (const scalarTypeName in graphql_scalars_1.resolvers) {
if (scalarTypeName in schemaTypeMap) {
(0, utils_js_1.addExecutionLogicToScalar)(schemaTypeMap[scalarTypeName], graphql_scalars_1.resolvers[scalarTypeName]);
}
}
if ('ObjMap' in schemaTypeMap) {
(0, utils_js_1.addExecutionLogicToScalar)(schemaTypeMap.ObjMap, transport_common_1.ObjMapScalar);
}
const queryType = schema.getQueryType();
const rootJsonAnnotations = (0, utils_1.getDirective)(schema, queryType, 'grpcRootJson');
const rootJsonMap = new Map();
const grpcObjectByRootJsonName = new Map();
for (let { name, rootJson, loadOptions } of rootJsonAnnotations) {
rootJson = typeof rootJson === 'string' ? JSON.parse(rootJson) : rootJson;
rootJsonMap.set(name, rootJson);
const rootLogger = this.logger.child(name);
grpcObjectByRootJsonName.set(name, this.getGrpcObject({ rootJson, loadOptions, rootLogger }));
}
const rootTypes = (0, utils_1.getRootTypes)(schema);
for (const rootType of rootTypes) {
const rootTypeFields = rootType.getFields();
for (const fieldName in rootTypeFields) {
const field = rootTypeFields[fieldName];
const directives = (0, utils_1.getDirectives)(schema, field);
if (directives?.length) {
for (const directiveObj of directives) {
switch (directiveObj.name) {
case 'grpcMethod': {
const { rootJsonName, objPath, methodName, responseStream } = directiveObj.args;
const grpcObject = grpcObjectByRootJsonName.get(rootJsonName);
const client = this.getServiceClient({
grpcObject,
objPath,
creds,
});
if (rootType.name === 'Subscription') {
field.subscribe = this.getFieldResolver({
client,
methodName,
isResponseStream: responseStream,
});
field.resolve = function identityFn(root) {
return root;
};
}
else {
field.resolve = this.getFieldResolver({
client,
methodName,
isResponseStream: responseStream,
});
}
break;
}
case 'grpcConnectivityState': {
const { rootJsonName, objPath } = directiveObj.args;
const grpcObject = grpcObjectByRootJsonName.get(rootJsonName);
const client = this.getServiceClient({
grpcObject,
objPath,
creds,
});
field.resolve = this.getConnectivityStateResolver({ client });
break;
}
}
}
}
}
}
const typeMap = schema.getTypeMap();
for (const typeName in typeMap) {
const type = typeMap[typeName];
if ((0, graphql_1.isEnumType)(type)) {
const values = type.getValues();
for (const value of values) {
const enumAnnotations = (0, utils_1.getDirective)(schema, value, 'enum');
if (enumAnnotations?.length) {
for (const enumAnnotation of enumAnnotations) {
const enumSerializedValue = enumAnnotation.value;
if (enumSerializedValue) {
const serializedValue = JSON.parse(enumSerializedValue);
value.value = serializedValue;
let valueLookup = type._valueLookup;
if (!valueLookup) {
valueLookup = new Map(type.getValues().map(enumValue => [enumValue.value, enumValue]));
type._valueLookup = valueLookup;
}
type._valueLookup.set(serializedValue, value);
}
}
}
}
}
}
}
}
exports.GrpcTransportHelper = GrpcTransportHelper;
exports.default = {
getSubgraphExecutor({ transportEntry, subgraph, cwd, logger }) {
const transport = new GrpcTransportHelper(cwd, logger, transportEntry.location, transportEntry.options);
return (0, utils_1.mapMaybePromise)(transport.getCredentials(), creds => {
transport.processDirectives({ schema: subgraph, creds });
return (0, transport_common_1.createDefaultExecutor)(subgraph);
});
},
};