nano-service/src/service.ts

161 lines
3.8 KiB
TypeScript
Raw Normal View History

2024-03-20 20:21:35 +01:00
import type { z } from "zod";
2024-01-30 21:50:19 +01:00
import type Adaptor from "./adaptors/adaptor";
import { RequestError } from "./error";
import {
type InternalRequest,
InternalRequestSchema,
type InternalResponse,
InternalResponseSchema,
type Request,
type Response,
} from "./messages";
import type { RouteSubscribeTypeFn } from "./types";
export default class Service {
2024-01-30 21:50:19 +01:00
private name: string;
private adaptors: Record<string, Adaptor>;
constructor(name: string) {
this.name = name;
this.adaptors = {};
}
public addAdaptor(name: string, adaptor: Adaptor) {
this.adaptors[name] = adaptor;
}
public subscribe<T extends z.ZodType<O>, O, U>(
adaptor: string,
subject: string,
fn: RouteSubscribeTypeFn<z.infer<T>, U>,
schema?: T,
2024-01-30 21:50:19 +01:00
) {
this.adaptors[adaptor].subscribe(
`${this.name}.${subject}`,
async (rawReq) => {
const rawReqJson = JSON.parse(rawReq);
const internalRequestJson = InternalRequestSchema.safeParse(rawReqJson);
2024-03-20 20:21:35 +01:00
2024-01-30 21:50:19 +01:00
if (!internalRequestJson.success) {
return JSON.stringify({
statusCode: 400,
data: "bad request structure",
} satisfies InternalResponse);
}
const internalRequest = internalRequestJson.data;
const req = {
service: internalRequest.from,
subject: subject,
} as Request<z.infer<T>>;
if (internalRequest.data && schema) {
const validate = schema.safeParse(internalRequest.data);
2024-03-20 20:21:35 +01:00
2024-01-30 21:50:19 +01:00
if (!validate.success) {
return JSON.stringify({
statusCode: 400,
data: validate.error,
} satisfies InternalResponse);
}
2024-03-20 20:21:35 +01:00
2024-01-30 21:50:19 +01:00
req.data = validate.data;
}
try {
const res = await fn(this, req);
2024-01-30 21:50:19 +01:00
const internalResponse = {
statusCode: res.statusCode,
data: res.data,
} satisfies InternalResponse;
return JSON.stringify(internalResponse);
} catch (err) {
if (err instanceof RequestError) {
return JSON.stringify({
statusCode: err.statusCode,
data: err.message,
} satisfies InternalResponse);
}
2024-03-20 20:21:35 +01:00
2024-01-30 21:50:19 +01:00
return JSON.stringify({
statusCode: 500,
data: "unknow error apend",
} satisfies InternalResponse);
}
},
);
}
public async request<T, O, U extends z.ZodType<O>>(
adaptor: string,
req: Request<T>,
schema?: z.ZodType<O>,
): Promise<Response<z.infer<U>>> {
if (!this.adaptors[adaptor]) {
throw new Error(`${adaptor} adaptor not exist`);
}
const internalRequest = {
from: this.name,
data: req.data,
} satisfies InternalRequest;
const internalRequestJson = JSON.stringify(internalRequest);
try {
const rawRes = await this.adaptors[adaptor].request(
`${req.service}.${req.subject}`,
internalRequestJson,
);
const rawResJson: unknown = JSON.parse(rawRes);
const internalResponseJson = InternalResponseSchema.safeParse(rawResJson);
if (!internalResponseJson.success) {
throw new RequestError(internalResponseJson.error.toString(), 500);
}
const internalResponse = internalResponseJson.data;
if (
internalResponse.statusCode < 200 ||
internalResponse.statusCode >= 299
) {
throw new RequestError(
internalResponse.data,
internalResponse.statusCode,
);
}
const res: Response<z.infer<U>> = {
statusCode: internalResponse.statusCode,
};
if (internalResponse.data && schema) {
const validate = schema.safeParse(internalResponse.data);
if (!validate.success) {
throw new RequestError(validate.error.message, 400);
}
res.data = validate.data;
}
return res;
} catch (err) {
if (err instanceof RequestError) {
throw err;
}
throw new RequestError("unexpected error", 500);
}
}
public async listen() {
for (const index in this.adaptors) {
await this.adaptors[index].listen(this.name);
}
}
public async stop() {
for (const index in this.adaptors) {
await this.adaptors[index].stop();
}
}
2024-01-04 21:20:47 +01:00
}