refactor(grpc-reflection): simplified request handling and file dependency logic

This commit is contained in:
Justin Timmons 2023-11-15 08:28:47 -05:00
parent 89a5cbbdf4
commit 2449abe398
1 changed files with 45 additions and 37 deletions

View File

@ -26,7 +26,7 @@ export class ReflectionError extends Error {
} }
} }
/** Analyzes a gRPC server and exposes methods to reflect on it /** Analyzes a gRPC package definition and exposes methods to reflect on it
* *
* NOTE: the files returned by this service may not match the handwritten ones 1:1. * NOTE: the files returned by this service may not match the handwritten ones 1:1.
* This is because proto-loader reorients files based on their package definition, * This is because proto-loader reorients files based on their package definition,
@ -37,7 +37,7 @@ export class ReflectionError extends Error {
*/ */
export class ReflectionV1Implementation { export class ReflectionV1Implementation {
/** The full list of proto files (including imported deps) that the gRPC server includes */ /** The full list of proto files (including imported deps) that the gRPC package includes */
private readonly fileDescriptorSet = new FileDescriptorSet(); private readonly fileDescriptorSet = new FileDescriptorSet();
/** An index of proto files by file name (eg. 'sample.proto') */ /** An index of proto files by file name (eg. 'sample.proto') */
@ -172,32 +172,34 @@ export class ReflectionV1Implementation {
handleServerReflectionRequest(message: ServerReflectionRequest): ServerReflectionResponse { handleServerReflectionRequest(message: ServerReflectionRequest): ServerReflectionResponse {
const response: ServerReflectionResponse = { const response: ServerReflectionResponse = {
validHost: message.host, validHost: message.host,
originalRequest: message, originalRequest: message
fileDescriptorResponse: undefined,
allExtensionNumbersResponse: undefined,
listServicesResponse: undefined,
errorResponse: undefined,
}; };
try { try {
if (message.listServices !== undefined) { switch(message.messageRequest) {
response.listServicesResponse = this.listServices(message.listServices); case 'listServices':
} else if (message.fileContainingSymbol !== undefined) { response.listServicesResponse = this.listServices(message.listServices || '');
response.fileDescriptorResponse = this.fileContainingSymbol(message.fileContainingSymbol); break;
} else if (message.fileByFilename !== undefined) { case 'fileContainingSymbol':
response.fileDescriptorResponse = this.fileByFilename(message.fileByFilename); response.fileDescriptorResponse = this.fileContainingSymbol(message.fileContainingSymbol || '');
} else if (message.fileContainingExtension !== undefined) { break;
response.fileDescriptorResponse = this.fileContainingExtension( case 'fileByFilename':
message.fileContainingExtension?.containingType || '', response.fileDescriptorResponse = this.fileByFilename(message.fileByFilename || '');
message.fileContainingExtension?.extensionNumber || -1 break;
); case 'fileContainingExtension':
} else if (message.allExtensionNumbersOfType) { response.fileDescriptorResponse = this.fileContainingExtension(
response.allExtensionNumbersResponse = this.allExtensionNumbersOfType(message.allExtensionNumbersOfType); message.fileContainingExtension?.containingType || '',
} else { message.fileContainingExtension?.extensionNumber || -1
throw new ReflectionError( );
grpc.status.UNIMPLEMENTED, break;
`Unimplemented method for request: ${message}`, case 'allExtensionNumbersOfType':
); response.allExtensionNumbersResponse = this.allExtensionNumbersOfType(message.allExtensionNumbersOfType || '');
break;
default:
throw new ReflectionError(
grpc.status.UNIMPLEMENTED,
`Unimplemented method for request: ${message.messageRequest}`,
);
} }
} catch (e) { } catch (e) {
if (e instanceof ReflectionError) { if (e instanceof ReflectionError) {
@ -315,20 +317,26 @@ export class ReflectionV1Implementation {
}; };
} }
private getFileDependencies( private getFileDependencies(file: FileDescriptorProto): FileDescriptorProto[] {
file: FileDescriptorProto, const visited: Set<FileDescriptorProto> = new Set();
visited: Set<FileDescriptorProto> = new Set(), const toVisit: FileDescriptorProto[] = file.getDependencyList().map((dep) => this.fileNameIndex[dep]);
): FileDescriptorProto[] {
const newVisited = visited.add(file);
const directDeps = file.getDependencyList().map((dep) => this.fileNameIndex[dep]); while (toVisit.length > 0) {
const transitiveDeps = directDeps const current = toVisit.pop();
.filter((dep) => !newVisited.has(dep))
.map((dep) => this.getFileDependencies(dep, newVisited))
.flat();
const allDeps = [...directDeps, ...transitiveDeps]; if (!current || visited.has(current)) {
continue;
}
return [...new Set(allDeps)]; visited.add(current);
toVisit.push(
...current.getDependencyList()
.map((dep) => this.fileNameIndex[dep])
.filter((dep) => !visited.has(dep))
);
}
return Array.from(visited);
} }
} }