Skip to content

Commit

Permalink
Merge pull request #15 from LOKE/feature/ts-and-codegenable
Browse files Browse the repository at this point in the history
New client, typescript
  • Loading branch information
smithamax authored Oct 10, 2022
2 parents e7e447e + 87d2a5d commit ecf6cc9
Show file tree
Hide file tree
Showing 13 changed files with 2,559 additions and 13,869 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
dist
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/dist
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# RPC Client

Use [loke-cli](https://github.com/LOKE/loke-cli) to generate client code

```
loke rpc install http://localhost:9999/rpc/yourservice
```
7 changes: 7 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { RPCClient } from "./lib/client";

import { load as _load } from "./lib/legacy";

export const load = _load;

export default { load };
119 changes: 119 additions & 0 deletions lib/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { AbortController } from "node-abort-controller";
import fetch from "node-fetch";

import { requestDuration, requestCount, failureCount } from "./metrics";

interface RequestOptions {
timeout?: number;
}

export class RPCClient {
private baseURL: URL;
private serviceName: string;
private service: string;

constructor(baseURL: string, serviceName: string) {
if (!baseURL.endsWith("/")) {
baseURL += "/";
}
this.serviceName = serviceName;
this.baseURL = new URL(baseURL);
this.service = this.baseURL.toString().replace(/\/^/, "");
}
async request(
methodName: string,
params: Record<string, any>,
options: RequestOptions = {}
) {
const url = new URL(methodName, this.baseURL);

const requestMeta = { service: this.service, method: methodName };
const end = requestDuration.startTimer(requestMeta);
requestCount.inc(requestMeta);

const { timeout = 60000 } = options;

const controller = new AbortController();
const tid = setTimeout(() => controller.abort(), timeout);

let status = -1;
try {
const res = await fetch(url.toString(), {
method: "POST",
body: JSON.stringify(params),
headers: {
"Content-Type": "application/json",
},
signal: controller.signal,
});

clearTimeout(tid);

if (res.ok) {
return res.json();
}

status = res.status;

let errResult;
if (res.headers.get("content-type") === "application/json") {
errResult = await res.json();
} else {
errResult = {
expose: false,
message: await res.text(),
};
}
mapError(this.serviceName, methodName, errResult);
} catch (err: any) {
failureCount.inc({ ...requestMeta, status_code: status, type: err.type });
throw err;
} finally {
end();
}
}
}

function mapError(serviceName: string, methodName: string, errResult: any) {
const source = `${serviceName}/${methodName}`;

if (!errResult.type) {
const newErr: any = new Error(errResult.message);
newErr.code = errResult.code;
newErr.expose = errResult.expose;
newErr.source = [source];
throw newErr;
}

throw new RpcResponseError(source, errResult);
}

class RpcResponseError {
source?: string[];

constructor(source: string, responseBody: any) {
Object.defineProperty(this, "name", {
configurable: true,
enumerable: false,
value: this.constructor.name,
writable: true,
});

Object.assign(this, responseBody);
if (!this.source) this.source = [];
this.source = [source, ...(this.source || [])];

Object.defineProperty(this, "stack", {
configurable: true,
enumerable: false,
value:
this.toString() +
"\n" +
[...this.source]
.reverse()
.map((s) => " via " + s)
.join("\n"),
writable: true,
});
}
}
40 changes: 19 additions & 21 deletions index.js → lib/legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,14 @@ const got = require("got");
const findUp = require("find-up");
const pFinally = require("p-finally");

const { Histogram, Counter } = require("prom-client");
const { requestCount, requestDuration, failureCount } = require("./metrics");

const IPC_MANIFESTS_FOLDER = "ipc_manifests";

const requestDuration = new Histogram({
name: "http_rpc_client_request_duration_seconds",
help: "Duration of rpc requests from the client",
labelNames: ["service", "method"],
});
const requestCount = new Counter({
name: "http_rpc_client_requests_total",
help: "The total number of rpc requests from the client",
labelNames: ["service", "method"],
});
const failureCount = new Counter({
name: "http_rpc_client_failures_total",
help: "The total number of rpc failures from the client",
labelNames: ["service", "method", "type", "status_code"],
});

class RpcResponseError {
class RpcResponseError extends Error {
constructor(source, responseBody) {
super();

Object.defineProperty(this, "name", {
configurable: true,
enumerable: false,
Expand Down Expand Up @@ -61,9 +47,17 @@ class RpcResponseError {
}
}

/**
*
* @param {string} host
* @param {string} serviceName
* @param {Object} options
* @param {string=} options.path
* @returns {any}
*/
exports.load = function (host, serviceName, options) {
const metaPath = getMetaPath(serviceName);
const client = new Client(host, options);
const client = new LegacyClient(host, options);

return client.load(metaPath);
};
Expand All @@ -75,7 +69,11 @@ exports.load = function (host, serviceName, options) {
function rootModuleDir() {
let mod = module;

while (mod.parent) {
while (
mod.parent &&
// Hack for tests
!mod.parent.filename.endsWith("ava/lib/worker/subprocess.js")
) {
mod = mod.parent;
}

Expand All @@ -90,7 +88,7 @@ function getMetaPath(serviceName) {
);
}

class Client {
class LegacyClient {
constructor(baseURL, options) {
var parsedURL = url.parse(baseURL);
this.protocol = parsedURL.protocol;
Expand Down
19 changes: 19 additions & 0 deletions lib/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Histogram, Counter } from "prom-client";

export const requestDuration = new Histogram({
name: "http_rpc_client_request_duration_seconds",
help: "Duration of rpc requests from the client",
labelNames: ["service", "method"],
});

export const requestCount = new Counter({
name: "http_rpc_client_requests_total",
help: "The total number of rpc requests from the client",
labelNames: ["service", "method"],
});

export const failureCount = new Counter({
name: "http_rpc_client_failures_total",
help: "The total number of rpc failures from the client",
labelNames: ["service", "method", "type", "status_code"],
});
Loading

0 comments on commit ecf6cc9

Please sign in to comment.