summaryrefslogtreecommitdiff
path: root/u/server/filter
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2025-05-12 09:40:12 -0700
committerElizabeth <me@liz.coffee>2025-05-26 14:15:42 -0700
commitd51c9d74857aca3c2f172609297266968bc7f809 (patch)
tree64327f9cc4219729aa11af32d7d4c70cddfc2292 /u/server/filter
parent30729a0cf707d9022bae0a7baaba77379dc31fd5 (diff)
downloadci-d51c9d74857aca3c2f172609297266968bc7f809.tar.gz
ci-d51c9d74857aca3c2f172609297266968bc7f809.zip
The big refactor TM
Diffstat (limited to 'u/server/filter')
-rw-r--r--u/server/filter/json.ts51
-rw-r--r--u/server/filter/method.ts41
-rw-r--r--u/server/filter/mod.ts33
3 files changed, 125 insertions, 0 deletions
diff --git a/u/server/filter/json.ts b/u/server/filter/json.ts
new file mode 100644
index 0000000..4a2961e
--- /dev/null
+++ b/u/server/filter/json.ts
@@ -0,0 +1,51 @@
+import {
+ Either,
+ type IEither,
+ type ITraceable,
+ LogLevel,
+ Metric,
+ PenguenoError,
+ type PenguenoRequest,
+ type RequestFilter,
+ type ServerTrace,
+ TraceUtil,
+} from "@emprespresso/pengueno";
+
+export interface JsonTransformer<R, ParsedJson = unknown> {
+ (json: ITraceable<ParsedJson, ServerTrace>): IEither<PenguenoError, R>;
+}
+
+const ParseJsonMetric = Metric.fromName("JsonParse");
+export const jsonModel = <MessageT>(
+ jsonTransformer: JsonTransformer<MessageT>,
+): RequestFilter<MessageT> =>
+(r: ITraceable<PenguenoRequest, ServerTrace>) =>
+ r.bimap(TraceUtil.withMetricTrace(ParseJsonMetric))
+ .map((j) =>
+ Either.fromFailableAsync<Error, MessageT>(j.get().json())
+ .then((either) =>
+ either.mapLeft((errReason) => {
+ j.trace.addTrace(LogLevel.WARN).trace(`${errReason}`);
+ return new PenguenoError(
+ "seems to be invalid JSON (>//<) can you fix?",
+ 400,
+ );
+ })
+ )
+ )
+ .peek(
+ TraceUtil.promiseify((traceableEither) =>
+ traceableEither.get().mapBoth(
+ () => traceableEither.trace.trace(ParseJsonMetric.failure),
+ () => traceableEither.trace.trace(ParseJsonMetric.success),
+ )
+ ),
+ )
+ .map(
+ TraceUtil.promiseify((traceableEitherJson) =>
+ traceableEitherJson.get()
+ .mapRight(traceableEitherJson.move)
+ .flatMap(jsonTransformer)
+ ),
+ )
+ .get();
diff --git a/u/server/filter/method.ts b/u/server/filter/method.ts
new file mode 100644
index 0000000..6b0419d
--- /dev/null
+++ b/u/server/filter/method.ts
@@ -0,0 +1,41 @@
+import {
+ Either,
+ type ITraceable,
+ LogLevel,
+ PenguenoError,
+ type PenguenoRequest,
+ type RequestFilter,
+ type ServerTrace,
+ TraceUtil,
+} from "@emprespresso/pengueno";
+
+type HttpMethod =
+ | "POST"
+ | "GET"
+ | "HEAD"
+ | "PUT"
+ | "DELETE"
+ | "CONNECT"
+ | "OPTIONS"
+ | "TRACE"
+ | "PATCH";
+
+export const requireMethod = (
+ methods: Array<HttpMethod>,
+): RequestFilter<HttpMethod> =>
+(req: ITraceable<PenguenoRequest, ServerTrace>) =>
+ req.bimap(TraceUtil.withFunctionTrace(requireMethod))
+ .move(Promise.resolve(req.get()))
+ .map(TraceUtil.promiseify((t) => {
+ const { method: _method } = t.get();
+ const method = <HttpMethod> _method;
+ if (!methods.includes(method)) {
+ const msg = "that's not how you pet me (â‹Ÿīšâ‹ž)~";
+ t.trace.addTrace(LogLevel.WARN).trace(msg);
+ return Either.left<PenguenoError, HttpMethod>(
+ new PenguenoError(msg, 405),
+ );
+ }
+ return Either.right<PenguenoError, HttpMethod>(method);
+ }))
+ .get();
diff --git a/u/server/filter/mod.ts b/u/server/filter/mod.ts
new file mode 100644
index 0000000..bbf37df
--- /dev/null
+++ b/u/server/filter/mod.ts
@@ -0,0 +1,33 @@
+import {
+ type IEither,
+ type ITraceable,
+ LogLevel,
+ type PenguenoRequest,
+ type ServerTrace,
+} from "@emprespresso/pengueno";
+
+export enum ErrorSource {
+ USER = LogLevel.WARN,
+ SYSTEM = LogLevel.ERROR,
+}
+
+export class PenguenoError extends Error {
+ public readonly source: ErrorSource;
+ constructor(message: string, public readonly status: number) {
+ super(message);
+ this.source = Math.floor(status / 100) === 4
+ ? ErrorSource.USER
+ : ErrorSource.SYSTEM;
+ }
+}
+
+export interface RequestFilter<
+ T,
+ Err extends PenguenoError = PenguenoError,
+ RIn = ITraceable<PenguenoRequest, ServerTrace>,
+> {
+ (req: RIn): Promise<IEither<Err, T>>;
+}
+
+export * from "./method.ts";
+export * from "./json.ts";