summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-12-15 02:30:42 -0800
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-12-15 02:30:42 -0800
commit2e8f6a1d14a574cc99c4eacfc4ba94cd92c64d99 (patch)
treec2f3207ac37ca94d1a68da0f3ace78374d012733
parentc4385abb3354ca5d4d647f07d2b02bf178dc52f7 (diff)
downloaduptime-2e8f6a1d14a574cc99c4eacfc4ba94cd92c64d99.tar.gz
uptime-2e8f6a1d14a574cc99c4eacfc4ba94cd92c64d99.zip
remove shitty duration parser
-rw-r--r--src/api.ts12
-rw-r--r--src/duration.ts156
-rw-r--r--tst/duration.spec.ts78
3 files changed, 1 insertions, 245 deletions
diff --git a/src/api.ts b/src/api.ts
index 678f40d..6132882 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -1,4 +1,3 @@
-import { parse } from "./duration";
import { perform } from "./email";
import type { EmailJob } from "./job";
import { ConsoleLogger } from "./logger";
@@ -11,16 +10,7 @@ export const main = (port: number) => {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/api/email") {
- const prevalidatedJob = await req.json();
- const interval = parse(prevalidatedJob.readRetry.interval);
- if (interval._tag === "Left") {
- return new Response(interval.left, { status: 400 });
- }
- prevalidatedJob.readRetry.interval = interval;
- const job: EmailJob = {
- ...prevalidatedJob,
- readRetry: { ...prevalidatedJob.readRetry, interval },
- };
+ const job: EmailJob = await req.json();
const jobInsensitive = structuredClone(job);
jobInsensitive.from.username = "****REDACTED****";
diff --git a/src/duration.ts b/src/duration.ts
deleted file mode 100644
index ad19921..0000000
--- a/src/duration.ts
+++ /dev/null
@@ -1,156 +0,0 @@
-import { flow, pipe } from "fp-ts/function";
-import * as E from "fp-ts/lib/Either";
-import * as S from "fp-ts/lib/string";
-import * as O from "fp-ts/lib/Option";
-import * as R from "fp-ts/lib/ReadonlyArray";
-
-export type Duration = number;
-
-export enum DurationUnit {
- MILLISECOND,
- SECOND,
- MINUTE,
- HOUR,
-}
-
-const durationUnitMap: Record<string, DurationUnit> = {
- ms: DurationUnit.MILLISECOND,
- milliseconds: DurationUnit.MILLISECOND,
- sec: DurationUnit.SECOND,
- seconds: DurationUnit.SECOND,
- min: DurationUnit.MINUTE,
- minutes: DurationUnit.MINUTE,
- hr: DurationUnit.HOUR,
- hour: DurationUnit.HOUR,
- hours: DurationUnit.HOUR,
-};
-const getDurationUnit = (key: string): O.Option<DurationUnit> =>
- O.fromNullable(durationUnitMap[key.toLowerCase()]);
-
-export const getMs = (duration: Duration): number => duration;
-export const getSeconds = (duration: Duration): number => duration / 1000;
-export const getMinutes = (duration: Duration): number =>
- getSeconds(duration) / 60;
-export const getHours = (duration: Duration): number =>
- getMinutes(duration) / 60;
-export const format = (duration: Duration): string => {
- const ms = getMs(duration) % 1000;
- const seconds = getSeconds(duration) % 60;
- const minutes = getMinutes(duration) % 60;
- const hours = getHours(duration);
-
- return (
- [hours, minutes, seconds]
- .map((x) => Math.floor(x).toString().padStart(2, "0"))
- .join(":") +
- "." +
- ms.toString().padStart(3, "0")
- );
-};
-
-export interface DurationBuilder {
- readonly millis: number;
- readonly seconds: number;
- readonly minutes: number;
- readonly hours: number;
-}
-export const createDurationBuilder = (): DurationBuilder => ({
- millis: 0,
- seconds: 0,
- minutes: 0,
- hours: 0,
-});
-
-export type DurationBuilderField<T> = (
- arg: T,
-) => (builder: DurationBuilder) => DurationBuilder;
-
-export const withMillis: DurationBuilderField<number> =
- (millis) => (builder) => ({
- ...builder,
- millis,
- });
-
-export const withSeconds: DurationBuilderField<number> =
- (seconds) => (builder) => ({
- ...builder,
- seconds,
- });
-
-export const withMinutes: DurationBuilderField<number> =
- (minutes) => (builder) => ({
- ...builder,
- minutes,
- });
-
-export const withHours: DurationBuilderField<number> =
- (hours) => (builder) => ({
- ...builder,
- hours,
- });
-
-export const build = (builder: DurationBuilder): Duration =>
- builder.millis +
- builder.seconds * 1000 +
- builder.minutes * 60 * 1000 +
- builder.hours * 60 * 60 * 1000;
-
-export const parse = (duration: string): E.Either<string, Duration> => {
- const parts = pipe(
- duration,
- S.split(" "),
- R.map(S.trim),
- R.filter((part) => !S.isEmpty(part)),
- );
-
- const valueUnitPairs = pipe(
- parts,
- R.mapWithIndex((i, part) => {
- const isUnit = i % 2 !== 0;
- if (!isUnit) return E.right(O.none);
-
- const value = Number(parts[i - 1]);
- if (isNaN(value)) return E.left(`bad value: "${parts[i - 1]}"`);
-
- const unit = getDurationUnit(part);
- if (O.isNone(unit)) return E.left(`unknown duration type: ${part}`);
-
- return E.right(O.some([unit.value, value] as [DurationUnit, number]));
- }),
- E.sequenceArray,
- E.map(
- flow(
- R.filter(O.isSome),
- R.map(({ value }) => value),
- ),
- ),
- );
-
- return pipe(
- valueUnitPairs,
- E.flatMap(
- R.reduce(
- E.of<string, DurationBuilder>(createDurationBuilder()),
- (builderEither, [unit, value]) =>
- pipe(
- builderEither,
- E.chain((builder) => {
- switch (unit) {
- case DurationUnit.MILLISECOND:
- return E.right(withMillis(value)(builder));
- case DurationUnit.SECOND:
- return E.right(withSeconds(value)(builder));
- case DurationUnit.MINUTE:
- return E.right(withMinutes(value)(builder));
- case DurationUnit.HOUR:
- return E.right(withHours(value)(builder));
- default:
- return E.left(`unknown unit: ${unit}`);
- }
- }),
- ),
- ),
- ),
- E.map(build),
- );
-};
diff --git a/tst/duration.spec.ts b/tst/duration.spec.ts
deleted file mode 100644
index bcd50f5..0000000
--- a/tst/duration.spec.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { pipe } from "fp-ts/function";
-import * as E from "fp-ts/Either";
-import { describe, test, expect } from "bun:test";
-import * as D from "../src/duration";
-
-describe("Duration Utility", () => {
- test("get unit should convert correctly", () => {
- expect(D.getMs(1000)).toBe(1000);
- expect(D.getSeconds(1000)).toBe(1);
- expect(D.getMinutes(60000)).toBe(1);
- expect(D.getHours(3600000)).toBe(1);
- });
-
- test("format should format duration correctly", () => {
- expect(D.format(3600000 + 237 + 5 * 60 * 1000)).toBe("01:05:00.237");
- });
-});
-
-describe("DurationBuilder", () => {
- test("createDurationBuilder should create a builder with zero values", () => {
- const builder = D.createDurationBuilder();
- expect(builder.millis).toBe(0);
- expect(builder.seconds).toBe(0);
- expect(builder.minutes).toBe(0);
- expect(builder.hours).toBe(0);
- });
-
- test("withMillis should set fields correctly and with precedence", () => {
- const builder = pipe(
- D.createDurationBuilder(),
- D.withMillis(0),
- D.withSeconds(20),
- D.withMinutes(30),
- D.withHours(40),
- D.withMillis(10),
- );
- expect(builder.millis).toBe(10);
- expect(builder.seconds).toBe(20);
- expect(builder.minutes).toBe(30);
- expect(builder.hours).toBe(40);
- });
-
- test("build should calculate total duration correctly", () => {
- const duration = pipe(
- D.createDurationBuilder(),
- D.withMillis(10),
- D.withSeconds(20),
- D.withMinutes(30),
- D.withHours(40),
- D.build,
- );
- expect(duration).toBe(
- 10 + 20 * 1000 + 30 * 60 * 1000 + 40 * 60 * 60 * 1000,
- );
- });
-});
-
-describe("parse", () => {
- test("should return right for a valid duration", () => {
- expect(D.parse("10 seconds 1 hr 30 min")).toEqual(
- E.right(1 * 60 * 60 * 1000 + 30 * 60 * 1000 + 10 * 1000),
- );
- });
-
- test("should operate with order", () => {
- expect(D.parse("1 hr 30 min 2 hours")).toEqual(
- E.right(2 * 60 * 60 * 1000 + 30 * 60 * 1000),
- );
- });
-
- test("returns left for unknown duration unit", () => {
- expect(D.parse("1 xyz")).toEqual(E.left("unknown duration type: xyz"));
- });
-
- test("return left for invalid number", () => {
- expect(D.parse("abc ms")).toEqual(E.left('bad value: "abc"'));
- });
-});