File size: 2,132 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { ErrorCode } from '../protocol/error-codes';
import type { z } from 'zod';

interface ValidationRequest {
  id: string;
  body?: unknown;
  query?: unknown;
  params?: unknown;
}

interface ValidationReply {
  send(payload: unknown): unknown;
}

type PreHandlerHook = (
  req: ValidationRequest,
  reply: ValidationReply,
  done: (err?: Error) => void,
) => void;

interface ValidationDetailItem {
  path: string;
  message: string;
}

function zodIssuesToDetails(error: z.ZodError): ValidationDetailItem[] {
  return error.issues.map((issue) => ({
    path: issue.path.join('.'),
    message: issue.message,
  }));
}

function buildValidationEnvelope(
  details: ValidationDetailItem[],
  requestId: string,
): {
  code: number;
  msg: string;
  data: null;
  request_id: string;
  details: ValidationDetailItem[];
} {
  const first = details[0];
  const msg = first === undefined
    ? 'validation failed'
    : first.path === ''
      ? first.message
      : `${first.path}: ${first.message}`;
  return {
    code: ErrorCode.VALIDATION_FAILED,
    msg,
    data: null,
    request_id: requestId,
    details,
  };
}

export function validateBody<T>(schema: z.ZodType<T>): PreHandlerHook {
  return (req, reply, done) => {
    const result = schema.safeParse(req.body);
    if (!result.success) {
      reply.send(buildValidationEnvelope(zodIssuesToDetails(result.error), req.id));
      return;
    }
    req.body = result.data;
    done();
  };
}

export function validateQuery<T>(schema: z.ZodType<T>): PreHandlerHook {
  return (req, reply, done) => {
    const result = schema.safeParse(req.query);
    if (!result.success) {
      reply.send(buildValidationEnvelope(zodIssuesToDetails(result.error), req.id));
      return;
    }
    req.query = result.data;
    done();
  };
}

export function validateParams<T>(schema: z.ZodType<T>): PreHandlerHook {
  return (req, reply, done) => {
    const result = schema.safeParse(req.params);
    if (!result.success) {
      reply.send(buildValidationEnvelope(zodIssuesToDetails(result.error), req.id));
      return;
    }
    req.params = result.data;
    done();
  };
}