78 lines
2.4 KiB
TypeScript

/**
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License-AGPL.txt in the project root for license information.
*/
import { RpcServer } from "./rpc-server";
import { Message, Channel } from "amqplib";
export interface JsonRpcRequest {
jsonrpc: "2.0",
method: string,
params?: any,
id: string | number | null
}
export interface JsonRpcResponse {
jsonrpc: "2.0",
result?: any,
error?: {
code: number,
message: string,
data?: any
},
id: string | number | null
}
export class JsonRpcServer extends RpcServer<JsonRpcRequest, JsonRpcResponse> {
constructor(protected readonly delegate: any, channel: Channel, queueName: string) {
super(channel, queueName);
}
protected async doHandle(msg: Message) {
let req: JsonRpcRequest;
try {
req = JSON.parse(msg.content.toString()) as JsonRpcRequest;
} catch(err) {
await this.reply(this.buildResponse({ error: { code: -32700, message: "Parse error" } }), msg);
return;
}
try {
const resp = await this.handleRequest(req, msg);
await this.reply(resp, msg);
} catch(err) {
await this.reply(this.buildResponse({ error: { code: -32603, message: "Internal error" } }, req), msg);
}
}
protected async handleRequest(req: JsonRpcRequest, _msg: Message): Promise<JsonRpcResponse> {
if(req.method in this.delegate) {
try {
const result = await this.delegate[req.method](...req.params);
return this.buildResponse({ result }, req);
} catch(err) {
return this.buildResponse({ error: { code: 0, message: err.message, data: err } }, req);
}
} else {
return this.buildResponse({ error: { code: -32601, message: "Method not found" } }, req);
}
}
protected buildResponse(content: Partial<JsonRpcResponse>, req?: JsonRpcRequest): JsonRpcResponse {
return {
jsonrpc: "2.0",
id: req ? req.id : null,
...content
}
}
protected async reply(resp: JsonRpcResponse, msg: Message) {
this.channel.publish('', msg.properties.replyTo, new Buffer(JSON.stringify(resp)), {
correlationId: msg.properties.correlationId
});
}
}