• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobinTail / express-zod-api / 14625934837

23 Apr 2025 06:57PM UTC coverage: 99.975%. Remained the same
14625934837

Pull #2573

github

web-flow
Merge 287b126f3 into 83dfadd5f
Pull Request #2573: Improve handling of `ZodError` by `ensureError`

1203 of 1240 branches covered (97.02%)

1 of 1 new or added line in 1 file covered. (100.0%)

4063 of 4064 relevant lines covered (99.98%)

258.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/express-zod-api/src/diagnostics.ts
1
import * as R from "ramda";
4✔
2
import type { $ZodShape } from "@zod/core";
3
import { z } from "zod";
4✔
4
import { responseVariants } from "./api-response";
4✔
5
import { FlatObject, getRoutePathParams } from "./common-helpers";
4✔
6
import { contentTypes } from "./content-type";
4✔
7
import { assertJsonCompatible } from "./deep-checks";
4✔
8
import { AbstractEndpoint } from "./endpoint";
9
import { extractObjectSchema } from "./io-schema";
4✔
10
import { ActualLogger } from "./logger-helpers";
11

12
export class Diagnostics {
4✔
13
  /** @desc (catcher)(...args) => bool | ReturnValue<typeof catcher> */
14
  readonly #trier = R.tryCatch(assertJsonCompatible);
124✔
15
  #verifiedEndpoints = new WeakSet<AbstractEndpoint>();
124✔
16
  #verifiedPaths = new WeakMap<
124✔
17
    AbstractEndpoint,
18
    { shape: $ZodShape; paths: string[] }
19
  >();
124✔
20

21
  constructor(protected logger: ActualLogger) {}
124✔
22

23
  public checkSchema(endpoint: AbstractEndpoint, ctx: FlatObject): void {
124✔
24
    if (this.#verifiedEndpoints.has(endpoint)) return;
184✔
25
    for (const dir of ["input", "output"] as const) {
184✔
26
      const stack = [
296✔
27
        z.toJSONSchema(endpoint[`${dir}Schema`], { unrepresentable: "any" }),
296✔
28
      ];
296✔
29
      while (stack.length > 0) {
296✔
30
        const entry = stack.shift()!;
296✔
31
        if (entry.type && entry.type !== "object")
296✔
32
          this.logger.warn(`Endpoint ${dir} schema is not object-based`, ctx);
296✔
33
        for (const prop of ["allOf", "oneOf", "anyOf"] as const)
296✔
34
          if (entry[prop]) stack.push(...entry[prop]);
296!
35
      }
296✔
36
    }
296✔
37
    if (endpoint.requestType === "json") {
160✔
38
      this.#trier((reason) =>
128✔
39
        this.logger.warn(
32✔
40
          "The final input schema of the endpoint contains an unsupported JSON payload type.",
32✔
41
          Object.assign(ctx, { reason }),
32✔
42
        ),
32✔
43
      )(endpoint.inputSchema, "in");
128✔
44
    }
128✔
45
    for (const variant of responseVariants) {
184✔
46
      const catcher = this.#trier((reason) =>
296✔
47
        this.logger.warn(
32✔
48
          `The final ${variant} response schema of the endpoint contains an unsupported JSON payload type.`,
32✔
49
          Object.assign(ctx, { reason }),
32✔
50
        ),
32✔
51
      );
296✔
52
      for (const { mimeTypes, schema } of endpoint.getResponses(variant)) {
296✔
53
        if (!mimeTypes?.includes(contentTypes.json)) continue;
296!
54
        catcher(schema, "out");
296✔
55
      }
296✔
56
    }
296✔
57
    this.#verifiedEndpoints.add(endpoint);
148✔
58
  }
184✔
59

60
  public checkPathParams(
124✔
61
    path: string,
184✔
62
    endpoint: AbstractEndpoint,
184✔
63
    ctx: FlatObject,
184✔
64
  ): void {
184✔
65
    const ref = this.#verifiedPaths.get(endpoint);
184✔
66
    if (ref?.paths.includes(path)) return;
184!
67
    const params = getRoutePathParams(path);
184✔
68
    if (params.length === 0) return; // next statement can be expensive
184✔
69
    const { shape } = ref || extractObjectSchema(endpoint.inputSchema);
132✔
70
    for (const param of params) {
184✔
71
      if (param in shape) continue;
16!
72
      this.logger.warn(
16✔
73
        "The input schema of the endpoint is most likely missing the parameter of the path it's assigned to.",
16✔
74
        Object.assign(ctx, { path, param }),
16✔
75
      );
16✔
76
    }
16✔
77
    if (ref) ref.paths.push(path);
132✔
78
    else this.#verifiedPaths.set(endpoint, { shape, paths: [path] });
12✔
79
  }
184✔
80
}
124✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc