grpc-js-reflection-client
Version:
Package use Grpc reflection api to download grpc proto descriptor. Now you don't have to add proto file to each package. Simple direct download proto package from example microservice without any files.
152 lines • 6.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GrpcReflection = void 0;
const Exceptions_1 = require("./Exceptions");
const protobufjs = require("protobufjs");
const Descriptor_1 = require("./Descriptor");
const descriptor_1 = require("protobufjs/ext/descriptor");
const lodash_1 = require("lodash");
const v1 = require("./Proto/v1");
const v1alpha = require("./Proto/v1alpha");
class GrpcReflection {
constructor(host, credentials, options = {}, version = "v1alpha") {
this.version = version;
this.setProtoReflectionClient(host, credentials, options);
}
async listServices(prefix = '*', options = {}) {
const response = await this.request({
listServices: prefix
}, options);
return response.listServicesResponse.service.map(service => service.name);
}
async listMethods(service, options = {}) {
const descriptor = await this.getDescriptorBySymbol(service, options);
const packageObject = descriptor.getPackageObject({
keepCase: true,
enums: String,
longs: String
});
return this.getServiceMethods(packageObject, service);
}
generateServicePath(service) {
return service.split('.').reverse();
}
getServiceMethods(descriptor, service) {
let anti_recursive = 0;
let actualDescriptor = descriptor;
const path = this.generateServicePath(service);
do {
const serviceName = path.pop();
if (serviceName && serviceName in actualDescriptor) {
actualDescriptor = actualDescriptor[serviceName];
}
anti_recursive++;
} while (path.length > 0 && anti_recursive < 100);
if ('service' in actualDescriptor) {
return Object.entries(actualDescriptor.service)
.map(([methodName, methodDefinition]) => ({
name: methodName,
definition: methodDefinition
}));
}
throw new Exceptions_1.ReflectionRequestException('Not found service');
}
async getDescriptorByFileName(file_name, options = {}) {
const descriptor = await this.getProtoDescriptorByFileName(file_name, options);
return await this.resolveFileDescriptorSet(descriptor, options);
}
async getDescriptorBySymbol(symbol, options = {}) {
const descriptor = await this.getProtoDescriptorBySymbol(symbol, options);
return await this.resolveFileDescriptorSet(descriptor, options);
}
async getAllExtensionNumbersOfType(package_type, options = {}) {
const response = await this.request({
allExtensionNumbersOfType: package_type
}, options);
return {
base_type_name: response.allExtensionNumbersResponse.baseTypeName,
extension_number: typeof (response.allExtensionNumbersResponse.extensionNumber) !== "undefined" ? response.allExtensionNumbersResponse.extensionNumber : []
};
}
async request(payload, options) {
return new Promise((resolve, reject) => {
const payloadObject = this.reflectionRequestConstructor(payload);
const call = this.client.ServerReflectionInfo(options);
call.on('data', (data) => {
if (data.errorResponse) {
reject(new Exceptions_1.ReflectionRequestException(data.errorResponse.errorMessage));
return;
}
resolve(data);
});
call.on('error', (err) => {
reject(new Exceptions_1.ReflectionRequestException(err));
});
call.on('end', () => { });
call.write(payloadObject);
call.end();
});
}
async resolveFileDescriptorSet(fileDescriptorProtoBytes, options) {
const fileDescriptorSet = descriptor_1.FileDescriptorSet.create();
const fileDescriptorProtos = await this.resolveDescriptorRecursive(fileDescriptorProtoBytes, options);
(0, lodash_1.set)(fileDescriptorSet, 'file', Array.from(fileDescriptorProtos.values()));
return new Descriptor_1.Descriptor(protobufjs.Root.fromDescriptor(fileDescriptorSet));
}
async resolveDescriptorRecursive(fileDescriptorProtoBytes, options) {
let fileDescriptorProtos = new Map();
let needsDependencyResolution = new Set();
for (const item of fileDescriptorProtoBytes) {
const fileDescriptorProto = descriptor_1.FileDescriptorProto.decode(item);
if (fileDescriptorProto.dependency) {
const dependencies = fileDescriptorProto.dependency;
for (const dep of dependencies) {
needsDependencyResolution.add(dep);
}
}
if (!fileDescriptorProtos.has(fileDescriptorProto.name)) {
fileDescriptorProtos.set(fileDescriptorProto.name, fileDescriptorProto);
}
}
for (const dep of needsDependencyResolution) {
if (fileDescriptorProtos.has(dep)) {
continue;
}
const depProtoBytes = await this.getProtoDescriptorByFileName(dep, options);
const protoDependencies = await this.resolveDescriptorRecursive(depProtoBytes, options);
fileDescriptorProtos = new Map([
...fileDescriptorProtos,
...protoDependencies,
]);
}
return fileDescriptorProtos;
}
async getProtoDescriptorBySymbol(symbol, options) {
const response = await this.request({
fileContainingSymbol: symbol
}, options);
return response.fileDescriptorResponse.fileDescriptorProto;
}
async getProtoDescriptorByFileName(file_name, options) {
const response = await this.request({
fileByFilename: file_name
}, options);
return response.fileDescriptorResponse.fileDescriptorProto;
}
setProtoReflectionClient(host, credentials, options = {}) {
switch (this.version) {
case 'v1':
this.client = new v1.grpc.reflection.v1.ServerReflectionClient(host, credentials, options);
this.reflectionRequestConstructor = v1.grpc.reflection.v1.ServerReflectionRequest.fromObject;
break;
case 'v1alpha':
this.client = new v1alpha.grpc.reflection.v1alpha.ServerReflectionClient(host, credentials, options);
this.reflectionRequestConstructor = v1alpha.grpc.reflection.v1alpha.ServerReflectionRequest.fromObject;
break;
default:
throw new Exceptions_1.ReflectionRequestException('Unknown proto version available: [v1, v1alpha]');
}
}
}
exports.GrpcReflection = GrpcReflection;
//# sourceMappingURL=GrpcReflection.js.map