@omnitronix/rng-client-core 1.0.10 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/rng-client-core.d.ts +0 -1
- package/dist/core/rng-client-core.js +1 -1
- package/dist/errors/rng-errors.d.ts +0 -1
- package/dist/errors/rng-errors.js +1 -1
- package/dist/grpc/grpc-rng-client.d.ts +0 -1
- package/dist/grpc/grpc-rng-client.js +1 -1
- package/dist/grpc/grpc-rng-interface.d.ts +0 -1
- package/dist/grpc/grpc-rng-interface.js +1 -1
- package/dist/http/http-client.types.d.ts +0 -1
- package/dist/http/http-client.types.js +1 -1
- package/dist/http/http-rng-client.d.ts +0 -1
- package/dist/http/http-rng-client.js +1 -1
- package/dist/http/rest-client.d.ts +0 -1
- package/dist/http/rest-client.js +1 -1
- package/dist/http/rng-client.interface.d.ts +0 -1
- package/dist/http/rng-client.interface.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/dist/interfaces/client-interfaces.d.ts +6 -3
- package/dist/interfaces/client-interfaces.js +1 -1
- package/dist/interfaces/metrics.d.ts +30 -0
- package/dist/interfaces/metrics.js +3 -0
- package/dist/metrics/default-metrics-handlers.d.ts +2 -0
- package/dist/metrics/default-metrics-handlers.js +23 -0
- package/dist/ring-buffer/interfaces.d.ts +0 -38
- package/dist/ring-buffer/interfaces.js +1 -1
- package/dist/ring-buffer/ring-buffer.d.ts +7 -14
- package/dist/ring-buffer/ring-buffer.js +63 -207
- package/dist/ring-buffer/ring-storage-memory.d.ts +1 -3
- package/dist/ring-buffer/ring-storage-memory.js +1 -25
- package/dist/ring-buffer/ring-storage.d.ts +1 -3
- package/dist/ring-buffer/ring-storage.js +1 -7
- package/dist/rng-client.d.ts +2 -5
- package/dist/rng-client.js +6 -4
- package/package.json +3 -2
- package/src/__tests__/grpc-rng-client.test.ts +167 -0
- package/src/__tests__/rng-client-core.integration.test.ts +117 -0
- package/src/__tests__/rng-client-core.test.ts +249 -0
- package/src/__tests__/rng-client-grpc.test.ts +324 -0
- package/src/__tests__/rng-client.test.ts +296 -0
- package/src/core/rng-client-core.ts +80 -0
- package/src/errors/rng-errors.ts +96 -0
- package/src/grpc/grpc-rng-client.ts +162 -0
- package/src/grpc/grpc-rng-interface.ts +36 -0
- package/src/http/http-client.types.ts +48 -0
- package/src/http/http-rng-client.ts +140 -0
- package/src/http/rest-client.ts +151 -0
- package/src/http/rng-client.interface.ts +22 -0
- package/src/index.ts +21 -0
- package/src/interfaces/client-interfaces.ts +15 -0
- package/src/interfaces/metrics.ts +38 -0
- package/src/metrics/default-metrics-handlers.ts +29 -0
- package/src/ring-buffer/interfaces.ts +9 -0
- package/src/ring-buffer/ring-buffer.ts +214 -0
- package/src/ring-buffer/ring-storage-memory.ts +48 -0
- package/src/ring-buffer/ring-storage.ts +59 -0
- package/src/rng-client.ts +190 -0
- package/src/rng.proto +102 -0
- package/dist/core/rng-client-core.d.ts.map +0 -1
- package/dist/core/rng-client-core.js.map +0 -1
- package/dist/errors/rng-errors.d.ts.map +0 -1
- package/dist/errors/rng-errors.js.map +0 -1
- package/dist/grpc/grpc-rng-client.d.ts.map +0 -1
- package/dist/grpc/grpc-rng-client.js.map +0 -1
- package/dist/grpc/grpc-rng-interface.d.ts.map +0 -1
- package/dist/grpc/grpc-rng-interface.js.map +0 -1
- package/dist/http/http-client.types.d.ts.map +0 -1
- package/dist/http/http-client.types.js.map +0 -1
- package/dist/http/http-rng-client.d.ts.map +0 -1
- package/dist/http/http-rng-client.js.map +0 -1
- package/dist/http/rest-client.d.ts.map +0 -1
- package/dist/http/rest-client.js.map +0 -1
- package/dist/http/rng-client.interface.d.ts.map +0 -1
- package/dist/http/rng-client.interface.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/client-interfaces.d.ts.map +0 -1
- package/dist/interfaces/client-interfaces.js.map +0 -1
- package/dist/ring-buffer/interfaces.d.ts.map +0 -1
- package/dist/ring-buffer/interfaces.js.map +0 -1
- package/dist/ring-buffer/ring-buffer.d.ts.map +0 -1
- package/dist/ring-buffer/ring-buffer.js.map +0 -1
- package/dist/ring-buffer/ring-storage-memory.d.ts.map +0 -1
- package/dist/ring-buffer/ring-storage-memory.js.map +0 -1
- package/dist/ring-buffer/ring-storage.d.ts.map +0 -1
- package/dist/ring-buffer/ring-storage.js.map +0 -1
- package/dist/rng-client.d.ts.map +0 -1
- package/dist/rng-client.js.map +0 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
|
|
3
|
+
export class RngServiceError extends Error {
|
|
4
|
+
constructor(
|
|
5
|
+
message: string,
|
|
6
|
+
public readonly code: string,
|
|
7
|
+
public readonly originalError?: unknown
|
|
8
|
+
) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "RngServiceError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class RngConnectionError extends RngServiceError {
|
|
15
|
+
constructor(
|
|
16
|
+
message: string = "Unable to connect to RNG service",
|
|
17
|
+
originalError?: unknown
|
|
18
|
+
) {
|
|
19
|
+
super(message, "SERVICE_UNAVAILABLE", originalError);
|
|
20
|
+
this.name = "RngConnectionError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class HttpConnectionError extends Error {
|
|
25
|
+
constructor(
|
|
26
|
+
message: string = "Unable to connect to RNG service",
|
|
27
|
+
public readonly originalError?: unknown
|
|
28
|
+
) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "HttpConnectionError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class HttpServiceError extends Error {
|
|
35
|
+
constructor(
|
|
36
|
+
message: string,
|
|
37
|
+
public readonly statusCode: number,
|
|
38
|
+
public readonly originalError?: unknown
|
|
39
|
+
) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "HttpServiceError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function handleGrpcError(
|
|
46
|
+
err: unknown,
|
|
47
|
+
operation: string,
|
|
48
|
+
grpcUrl?: string
|
|
49
|
+
): never {
|
|
50
|
+
console.log(`RNG Client gRPC Error in ${operation}:`, err);
|
|
51
|
+
|
|
52
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
53
|
+
const grpcError = err as grpc.ServiceError;
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
grpcError.code === grpc.status.UNAVAILABLE ||
|
|
57
|
+
grpcError.code === grpc.status.DEADLINE_EXCEEDED ||
|
|
58
|
+
grpcError.code === grpc.status.UNKNOWN ||
|
|
59
|
+
grpcError.code === grpc.status.INTERNAL
|
|
60
|
+
) {
|
|
61
|
+
throw new RngConnectionError(
|
|
62
|
+
grpcUrl
|
|
63
|
+
? `Unable to connect to RNG service at ${grpcUrl}`
|
|
64
|
+
: "Unable to connect to RNG service",
|
|
65
|
+
err
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw new RngServiceError(
|
|
70
|
+
grpcError.details || grpcError.message || "Unknown gRPC error occurred",
|
|
71
|
+
grpcError.code?.toString() || "UNKNOWN",
|
|
72
|
+
err
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (err instanceof Error) {
|
|
77
|
+
if (
|
|
78
|
+
err.message.includes("ECONNREFUSED") ||
|
|
79
|
+
err.message.includes("ENOTFOUND") ||
|
|
80
|
+
err.message.includes("timeout")
|
|
81
|
+
) {
|
|
82
|
+
throw new RngConnectionError(
|
|
83
|
+
grpcUrl
|
|
84
|
+
? `Unable to connect to RNG service at ${grpcUrl}`
|
|
85
|
+
: "Unable to connect to RNG service",
|
|
86
|
+
err
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
throw new RngServiceError(
|
|
92
|
+
"An unexpected error occurred while communicating with the RNG service",
|
|
93
|
+
"UNKNOWN",
|
|
94
|
+
err
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
import * as protoLoader from "@grpc/proto-loader";
|
|
3
|
+
import { RngClientInterface } from "../http/rng-client.interface";
|
|
4
|
+
import {
|
|
5
|
+
RngBatchFloatRequest,
|
|
6
|
+
RngBatchRequest,
|
|
7
|
+
RngBatchResponse,
|
|
8
|
+
RngBatchResponseWithSeeds,
|
|
9
|
+
RngSingleFloatRequest,
|
|
10
|
+
RngSingleRequest,
|
|
11
|
+
RngSingleResponse,
|
|
12
|
+
} from "../http/http-client.types";
|
|
13
|
+
import { GrpcRngClientInterface } from "./grpc-rng-interface";
|
|
14
|
+
import { handleGrpcError } from "../errors/rng-errors";
|
|
15
|
+
|
|
16
|
+
const PROTO_PATH = __dirname + "/../rng.proto";
|
|
17
|
+
|
|
18
|
+
export class GrpcRngClient implements RngClientInterface {
|
|
19
|
+
private readonly client: GrpcRngClientInterface;
|
|
20
|
+
private readonly grpcUrl: string;
|
|
21
|
+
|
|
22
|
+
constructor(grpcUrl: string) {
|
|
23
|
+
this.grpcUrl = grpcUrl;
|
|
24
|
+
|
|
25
|
+
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
|
|
26
|
+
keepCase: true,
|
|
27
|
+
longs: String,
|
|
28
|
+
enums: String,
|
|
29
|
+
defaults: true,
|
|
30
|
+
oneofs: true,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const rngProto = grpc.loadPackageDefinition(packageDefinition)
|
|
34
|
+
.rng as grpc.GrpcObject;
|
|
35
|
+
this.client = new (rngProto as any).RngService(
|
|
36
|
+
this.grpcUrl,
|
|
37
|
+
grpc.credentials.createInsecure()
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getSingleNumber(request: RngSingleRequest): Promise<RngSingleResponse> {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
this.client.Single(
|
|
44
|
+
request,
|
|
45
|
+
(err: unknown, response: RngSingleResponse) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
try {
|
|
48
|
+
handleGrpcError(err, "getSingleNumber", this.grpcUrl);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
reject(error);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
resolve(response);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getBatchNumbers(request: RngBatchRequest): Promise<RngBatchResponse> {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
this.client.SingleBatch(
|
|
63
|
+
request,
|
|
64
|
+
(err: unknown, response: RngBatchResponse) => {
|
|
65
|
+
if (err) {
|
|
66
|
+
try {
|
|
67
|
+
handleGrpcError(err, "getBatchNumbers", this.grpcUrl);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
reject(error);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
resolve(response);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getSingleFloat(
|
|
80
|
+
request: RngSingleFloatRequest
|
|
81
|
+
): Promise<RngSingleResponse> {
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
this.client.Float(
|
|
84
|
+
request,
|
|
85
|
+
(err: unknown, response: RngSingleResponse) => {
|
|
86
|
+
if (err) {
|
|
87
|
+
try {
|
|
88
|
+
handleGrpcError(err, "getSingleFloat", this.grpcUrl);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
reject(error);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
resolve(response);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async getBatchFloats(
|
|
101
|
+
request: RngBatchFloatRequest
|
|
102
|
+
): Promise<RngBatchResponse> {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
this.client.FloatBatch(
|
|
105
|
+
request,
|
|
106
|
+
(err: unknown, response: RngBatchResponse) => {
|
|
107
|
+
if (err) {
|
|
108
|
+
try {
|
|
109
|
+
handleGrpcError(err, "getBatchFloats", this.grpcUrl);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
reject(error);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
resolve(response);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getBatchNumbersWithSeeds(
|
|
122
|
+
request: RngBatchRequest
|
|
123
|
+
): Promise<RngBatchResponseWithSeeds> {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
this.client.SingleBatchWithSeeds(
|
|
126
|
+
request,
|
|
127
|
+
(err: unknown, response: RngBatchResponseWithSeeds) => {
|
|
128
|
+
if (err) {
|
|
129
|
+
try {
|
|
130
|
+
handleGrpcError(err, "getBatchNumbersWithSeeds", this.grpcUrl);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
reject(error);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
resolve(response);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async getBatchFloatWithSeeds(
|
|
143
|
+
request: RngSingleFloatRequest
|
|
144
|
+
): Promise<RngBatchResponseWithSeeds> {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
this.client.FloatBatchWithSeeds(
|
|
147
|
+
request,
|
|
148
|
+
(err: unknown, response: RngBatchResponseWithSeeds) => {
|
|
149
|
+
if (err) {
|
|
150
|
+
try {
|
|
151
|
+
handleGrpcError(err, "getBatchFloatWithSeeds", this.grpcUrl);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
reject(error);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
resolve(response);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RngBatchFloatRequest,
|
|
3
|
+
RngBatchRequest,
|
|
4
|
+
RngBatchResponse,
|
|
5
|
+
RngBatchResponseWithSeeds,
|
|
6
|
+
RngSingleFloatRequest,
|
|
7
|
+
RngSingleRequest,
|
|
8
|
+
RngSingleResponse,
|
|
9
|
+
} from "../http/http-client.types";
|
|
10
|
+
|
|
11
|
+
export interface GrpcRngClientInterface {
|
|
12
|
+
Single(
|
|
13
|
+
request: RngSingleRequest,
|
|
14
|
+
callback: (err: unknown, response: RngSingleResponse) => void
|
|
15
|
+
): Promise<RngSingleResponse>;
|
|
16
|
+
SingleBatch(
|
|
17
|
+
request: RngBatchRequest,
|
|
18
|
+
callback: (err: unknown, response: RngBatchResponse) => void
|
|
19
|
+
): Promise<RngBatchResponse>;
|
|
20
|
+
Float(
|
|
21
|
+
request: RngSingleFloatRequest,
|
|
22
|
+
callback: (err: unknown, response: RngSingleResponse) => void
|
|
23
|
+
): Promise<RngSingleResponse>;
|
|
24
|
+
FloatBatch(
|
|
25
|
+
request: RngBatchFloatRequest,
|
|
26
|
+
callback: (err: unknown, response: RngBatchResponse) => void
|
|
27
|
+
): Promise<RngBatchResponse>;
|
|
28
|
+
SingleBatchWithSeeds(
|
|
29
|
+
request: RngBatchRequest,
|
|
30
|
+
callback: (err: unknown, response: RngBatchResponseWithSeeds) => void
|
|
31
|
+
): Promise<RngBatchResponseWithSeeds>;
|
|
32
|
+
FloatBatchWithSeeds(
|
|
33
|
+
request: RngSingleFloatRequest,
|
|
34
|
+
callback: (err: unknown, response: RngBatchResponseWithSeeds) => void
|
|
35
|
+
): Promise<RngBatchResponseWithSeeds>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface RngSingleRequest {
|
|
2
|
+
min: number;
|
|
3
|
+
max: number;
|
|
4
|
+
seed?: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RngBatchRequest {
|
|
8
|
+
min: number;
|
|
9
|
+
max: number;
|
|
10
|
+
count: number;
|
|
11
|
+
seed?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RngSingleFloatRequest {
|
|
15
|
+
seed?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RngBatchFloatRequest {
|
|
19
|
+
count: number;
|
|
20
|
+
seed?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RngSingleResponse {
|
|
24
|
+
id: string;
|
|
25
|
+
seed: number;
|
|
26
|
+
result: number;
|
|
27
|
+
min: number;
|
|
28
|
+
max: number;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RngBatchResponse {
|
|
33
|
+
id: string;
|
|
34
|
+
seed: number;
|
|
35
|
+
result: number[];
|
|
36
|
+
min: number;
|
|
37
|
+
max: number;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RngBatchResponseWithSeeds {
|
|
42
|
+
id: string;
|
|
43
|
+
seed: number;
|
|
44
|
+
result: { value: number; seed: number }[];
|
|
45
|
+
min: number;
|
|
46
|
+
max: number;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { RngClientInterface } from "./rng-client.interface";
|
|
2
|
+
import {
|
|
3
|
+
RngBatchFloatRequest,
|
|
4
|
+
RngBatchRequest,
|
|
5
|
+
RngBatchResponse,
|
|
6
|
+
RngBatchResponseWithSeeds,
|
|
7
|
+
RngSingleFloatRequest,
|
|
8
|
+
RngSingleRequest,
|
|
9
|
+
RngSingleResponse,
|
|
10
|
+
} from "./http-client.types";
|
|
11
|
+
import { RestClient } from "./rest-client";
|
|
12
|
+
import { HttpConnectionError, HttpServiceError } from "../errors/rng-errors";
|
|
13
|
+
|
|
14
|
+
export class HttpRngClient extends RestClient implements RngClientInterface {
|
|
15
|
+
constructor(baseUrl: string) {
|
|
16
|
+
super(baseUrl);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getSingleNumber(request: RngSingleRequest): Promise<RngSingleResponse> {
|
|
20
|
+
const response = await this.post<RngSingleResponse>(
|
|
21
|
+
"/api/rng/single",
|
|
22
|
+
request
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (!response.success) {
|
|
26
|
+
if (response.error === "ConnectionError" || response.statusCode === 503) {
|
|
27
|
+
throw new HttpConnectionError(response.message);
|
|
28
|
+
}
|
|
29
|
+
throw new HttpServiceError(
|
|
30
|
+
response.message || `HTTP error! status: ${response.statusCode}`,
|
|
31
|
+
response.statusCode
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return response.data;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async getBatchNumbers(request: RngBatchRequest): Promise<RngBatchResponse> {
|
|
39
|
+
const response = await this.post<RngBatchResponse>(
|
|
40
|
+
"/api/rng/batch",
|
|
41
|
+
request
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (!response.success) {
|
|
45
|
+
if (response.error === "ConnectionError" || response.statusCode === 503) {
|
|
46
|
+
throw new HttpConnectionError(response.message);
|
|
47
|
+
}
|
|
48
|
+
throw new HttpServiceError(
|
|
49
|
+
response.message || `HTTP error! status: ${response.statusCode}`,
|
|
50
|
+
response.statusCode
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return response.data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getSingleFloat(
|
|
58
|
+
request: RngSingleFloatRequest
|
|
59
|
+
): Promise<RngSingleResponse> {
|
|
60
|
+
const response = await this.post<RngSingleResponse>(
|
|
61
|
+
"/api/rng/float",
|
|
62
|
+
request
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!response.success) {
|
|
66
|
+
if (response.error === "ConnectionError" || response.statusCode === 503) {
|
|
67
|
+
throw new HttpConnectionError(response.message);
|
|
68
|
+
}
|
|
69
|
+
throw new HttpServiceError(
|
|
70
|
+
response.message || `HTTP error! status: ${response.statusCode}`,
|
|
71
|
+
response.statusCode
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return response.data;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async getBatchFloats(
|
|
79
|
+
request: RngBatchFloatRequest
|
|
80
|
+
): Promise<RngBatchResponse> {
|
|
81
|
+
const response = await this.post<RngBatchResponse>(
|
|
82
|
+
"/api/rng/batch/float",
|
|
83
|
+
request
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (!response.success) {
|
|
87
|
+
if (response.error === "ConnectionError" || response.statusCode === 503) {
|
|
88
|
+
throw new HttpConnectionError(response.message);
|
|
89
|
+
}
|
|
90
|
+
throw new HttpServiceError(
|
|
91
|
+
response.message || `HTTP error! status: ${response.statusCode}`,
|
|
92
|
+
response.statusCode
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return response.data;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async getBatchNumbersWithSeeds(
|
|
100
|
+
request: RngBatchRequest
|
|
101
|
+
): Promise<RngBatchResponseWithSeeds> {
|
|
102
|
+
const response = await this.post<RngBatchResponseWithSeeds>(
|
|
103
|
+
"/api/rng/batch/with-seeds",
|
|
104
|
+
request
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (!response.success) {
|
|
108
|
+
if (response.error === "ConnectionError" || response.statusCode === 503) {
|
|
109
|
+
throw new HttpConnectionError(response.message);
|
|
110
|
+
}
|
|
111
|
+
throw new HttpServiceError(
|
|
112
|
+
response.message || `HTTP error! status: ${response.statusCode}`,
|
|
113
|
+
response.statusCode
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return response.data;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async getBatchFloatWithSeeds(
|
|
121
|
+
request: RngSingleFloatRequest
|
|
122
|
+
): Promise<RngBatchResponseWithSeeds> {
|
|
123
|
+
const response = await this.post<RngBatchResponseWithSeeds>(
|
|
124
|
+
"/api/rng/batch/float/with-seeds",
|
|
125
|
+
request
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (!response.success) {
|
|
129
|
+
if (response.error === "ConnectionError" || response.statusCode === 503) {
|
|
130
|
+
throw new HttpConnectionError(response.message);
|
|
131
|
+
}
|
|
132
|
+
throw new HttpServiceError(
|
|
133
|
+
response.message || `HTTP error! status: ${response.statusCode}`,
|
|
134
|
+
response.statusCode
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return response.data;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import axios, { AxiosError, AxiosInstance } from "axios";
|
|
2
|
+
|
|
3
|
+
export interface ApiErrorData {
|
|
4
|
+
internalCode?: number;
|
|
5
|
+
message?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ApiSuccessResponse<T> {
|
|
9
|
+
success: true;
|
|
10
|
+
data: T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ApiErrorResponse<E = null> {
|
|
14
|
+
success: false;
|
|
15
|
+
error: string;
|
|
16
|
+
message: string;
|
|
17
|
+
statusCode: number;
|
|
18
|
+
internalCode?: number;
|
|
19
|
+
data?: E;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ApiResponse<T, E = null> =
|
|
23
|
+
| ApiSuccessResponse<T>
|
|
24
|
+
| ApiErrorResponse<E>;
|
|
25
|
+
|
|
26
|
+
export class RestClient {
|
|
27
|
+
private httpClient: AxiosInstance;
|
|
28
|
+
|
|
29
|
+
constructor(baseUrl: string) {
|
|
30
|
+
this.httpClient = axios.create({
|
|
31
|
+
baseURL: baseUrl,
|
|
32
|
+
timeout: 5000,
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private isApiErrorData(data: any): data is ApiErrorData {
|
|
40
|
+
return data && typeof data === "object" && "internalCode" in data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private mapSuccessResponse<T>(data: T): ApiSuccessResponse<T> {
|
|
44
|
+
return { success: true, data };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private mapErrorResponse<E>(error: unknown): ApiErrorResponse<E> {
|
|
48
|
+
if (axios.isAxiosError(error)) {
|
|
49
|
+
const axiosError = error as AxiosError;
|
|
50
|
+
const status = axiosError.response?.status || 500;
|
|
51
|
+
const data = axiosError.response?.data ?? {};
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
axiosError.code === "ECONNREFUSED" ||
|
|
55
|
+
axiosError.code === "ENOTFOUND" ||
|
|
56
|
+
axiosError.code === "ETIMEDOUT" ||
|
|
57
|
+
axiosError.message.includes("timeout") ||
|
|
58
|
+
axiosError.message.includes("ECONNREFUSED") ||
|
|
59
|
+
axiosError.message.includes("ENOTFOUND")
|
|
60
|
+
) {
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
error: "ConnectionError",
|
|
64
|
+
message: "Unable to connect to RNG service",
|
|
65
|
+
statusCode: 503,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this.isApiErrorData(data)) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: "ApiError",
|
|
73
|
+
message: data.message ?? "An unexpected error occurred.",
|
|
74
|
+
statusCode: status,
|
|
75
|
+
internalCode: data.internalCode,
|
|
76
|
+
data: data as E,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: "UnknownError",
|
|
83
|
+
message: "An unexpected error occurred.",
|
|
84
|
+
statusCode: status,
|
|
85
|
+
};
|
|
86
|
+
} else if (error instanceof Error) {
|
|
87
|
+
if (
|
|
88
|
+
error.message.includes("ECONNREFUSED") ||
|
|
89
|
+
error.message.includes("ENOTFOUND") ||
|
|
90
|
+
error.message.includes("timeout")
|
|
91
|
+
) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
error: "ConnectionError",
|
|
95
|
+
message: "Unable to connect to RNG service",
|
|
96
|
+
statusCode: 503,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: "UnexpectedError",
|
|
103
|
+
message: error.message || "An unknown error occurred.",
|
|
104
|
+
statusCode: 500,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: "UnknownError",
|
|
111
|
+
message: "An unknown error occurred.",
|
|
112
|
+
statusCode: 500,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async handleRequest<T, E = null>(
|
|
117
|
+
request: Promise<{ data: T }>
|
|
118
|
+
): Promise<ApiResponse<T, E>> {
|
|
119
|
+
try {
|
|
120
|
+
const response = await request;
|
|
121
|
+
return this.mapSuccessResponse(response.data);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return this.mapErrorResponse<E>(error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public async get<T, E = null>(
|
|
128
|
+
url: string,
|
|
129
|
+
params?: object
|
|
130
|
+
): Promise<ApiResponse<T, E>> {
|
|
131
|
+
return this.handleRequest(this.httpClient.get<T>(url, { params }));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public async post<T, E = null>(
|
|
135
|
+
url: string,
|
|
136
|
+
data?: object
|
|
137
|
+
): Promise<ApiResponse<T, E>> {
|
|
138
|
+
return this.handleRequest(this.httpClient.post<T>(url, data));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public async put<T, E = null>(
|
|
142
|
+
url: string,
|
|
143
|
+
data?: object
|
|
144
|
+
): Promise<ApiResponse<T, E>> {
|
|
145
|
+
return this.handleRequest(this.httpClient.put<T>(url, data));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public async delete<T, E = null>(url: string): Promise<ApiResponse<T, E>> {
|
|
149
|
+
return this.handleRequest(this.httpClient.delete<T>(url));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RngBatchFloatRequest,
|
|
3
|
+
RngBatchRequest,
|
|
4
|
+
RngBatchResponse,
|
|
5
|
+
RngBatchResponseWithSeeds,
|
|
6
|
+
RngSingleFloatRequest,
|
|
7
|
+
RngSingleRequest,
|
|
8
|
+
RngSingleResponse,
|
|
9
|
+
} from "./http-client.types";
|
|
10
|
+
|
|
11
|
+
export interface RngClientInterface {
|
|
12
|
+
getSingleNumber(request: RngSingleRequest): Promise<RngSingleResponse>;
|
|
13
|
+
getBatchNumbers(request: RngBatchRequest): Promise<RngBatchResponse>;
|
|
14
|
+
getSingleFloat(request: RngSingleFloatRequest): Promise<RngSingleResponse>;
|
|
15
|
+
getBatchFloats(request: RngBatchFloatRequest): Promise<RngBatchResponse>;
|
|
16
|
+
getBatchNumbersWithSeeds(
|
|
17
|
+
request: RngBatchRequest
|
|
18
|
+
): Promise<RngBatchResponseWithSeeds>;
|
|
19
|
+
getBatchFloatWithSeeds(
|
|
20
|
+
request: RngSingleFloatRequest
|
|
21
|
+
): Promise<RngBatchResponseWithSeeds>;
|
|
22
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Main API
|
|
2
|
+
export { RngClient } from './rng-client';
|
|
3
|
+
|
|
4
|
+
// Clients
|
|
5
|
+
export { HttpRngClient } from './http/http-rng-client';
|
|
6
|
+
export { GrpcRngClient } from './grpc/grpc-rng-client';
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
RngServiceError,
|
|
10
|
+
RngConnectionError,
|
|
11
|
+
HttpConnectionError,
|
|
12
|
+
HttpServiceError,
|
|
13
|
+
handleGrpcError,
|
|
14
|
+
} from './errors/rng-errors';
|
|
15
|
+
|
|
16
|
+
// Configuration
|
|
17
|
+
export type {
|
|
18
|
+
RngClientConfig,
|
|
19
|
+
ClientMethod,
|
|
20
|
+
} from './interfaces/client-interfaces';
|
|
21
|
+
export type { RngClientMetricsHandlers as MetricsHandlers } from './interfaces/metrics';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StorageType } from '../ring-buffer/ring-storage';
|
|
2
|
+
import { RngClientMetricsHandlers } from './metrics';
|
|
3
|
+
|
|
4
|
+
export type ClientMethod = 'rest' | 'grpc';
|
|
5
|
+
|
|
6
|
+
export interface RngClientConfig {
|
|
7
|
+
readonly serverUrl: string;
|
|
8
|
+
readonly grpcUrl: string;
|
|
9
|
+
readonly clientMethod: ClientMethod;
|
|
10
|
+
readonly poolingSize?: number;
|
|
11
|
+
readonly storageType?: StorageType;
|
|
12
|
+
readonly metrics?: {
|
|
13
|
+
handlers?: Partial<RngClientMetricsHandlers>;
|
|
14
|
+
};
|
|
15
|
+
}
|