import { Either, ErrorSource, type IActivity, type IEither, type ITraceable, jsonModel, JsonResponse, LogLevel, LogMetricTraceSupplier, Metric, MetricsTraceSupplier, PenguenoError, type PenguenoRequest, type ServerTrace, TraceUtil, validateExecutionEntries, } from '@emprespresso/pengueno'; import { isJob, type Job } from '@emprespresso/ci_model'; import { IJobQueuer } from './queue.js'; const wellFormedJobMetric = Metric.fromName('Job.WellFormed').asResult(); const jobJsonTransformer = (j: ITraceable): IEither => j .flatMap(TraceUtil.withMetricTrace(wellFormedJobMetric)) .map((tJson): IEither => { const tJob = tJson.get(); if (!isJob(tJob) || !validateExecutionEntries(tJob)) { const err = 'seems like a pwetty mawfomed job (-.-)'; tJson.trace.traceScope(LogLevel.WARN).trace(err); return Either.left(new PenguenoError(err, 400)); } return Either.right(tJob); }) .peek(TraceUtil.traceResultingEither(wellFormedJobMetric)) .get(); export interface IJobHookActivity { processHook: IActivity; } const jobHookRequestMetric = Metric.fromName('JobHook.process').asResult(); export class JobHookActivityImpl implements IJobHookActivity { constructor(private readonly queuer: IJobQueuer>) {} private trace(r: ITraceable) { return r.flatMap(TraceUtil.withClassTrace(this)).flatMap(TraceUtil.withMetricTrace(jobHookRequestMetric)); } public processHook(r: ITraceable) { return this.trace(r) .map(jsonModel(jobJsonTransformer)) .map(async (tEitherJobJson) => { const eitherJob = await tEitherJobJson.get(); return eitherJob.flatMapAsync(async (job) => { const eitherQueued = await tEitherJobJson .move(job) .map((job) => this.queuer.queue(job)) .get(); return eitherQueued.mapLeft((e) => new PenguenoError(e.message, 500)); }); }) .flatMapAsync( TraceUtil.promiseify((tEitherQueued) => { const errorSource = tEitherQueued .get() .left() .map(({ source }) => source) .orSome(() => ErrorSource.SYSTEM) .get(); const shouldWarn = errorSource === ErrorSource.USER; return TraceUtil.traceResultingEither( jobHookRequestMetric, shouldWarn, )(tEitherQueued); }), ) .peek( TraceUtil.promiseify((tJob) => tJob.get().mapRight((job) => tJob.trace.trace(`all queued up and weady to go :D !! ${job}`)), ), ) .map( TraceUtil.promiseify( (tEitherQueuedJob) => new JsonResponse(r, tEitherQueuedJob.get(), { status: tEitherQueuedJob.get().fold( ({ status }) => status, () => 200, ), }), ), ) .get(); } }