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

GEWIS / sudosos-backend / 25753937432

12 May 2026 09:17AM UTC coverage: 88.117% (-1.0%) from 89.089%
25753937432

push

github

web-flow
chore(deps): fix missing dependencies for running docs:dev (#911)

3925 of 4574 branches covered (85.81%)

Branch coverage included in aggregate %.

20093 of 22683 relevant lines covered (88.58%)

1125.83 hits per line

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

77.03
/src/middleware/async-validator-middleware.ts
1
/**
1✔
2
 *  SudoSOS back-end API service.
3
 *  Copyright (C) 2026 Study association GEWIS
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU Affero General Public License as published
7
 *  by the Free Software Foundation, either version 3 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU Affero General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU Affero General Public License
16
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 *
18
 *  @license
19
 */
1✔
20

21
/**
1✔
22
 * This is the module page of the async-validator-middleware.
23
 *
24
 * @module internal/middleware
25
 */
1✔
26

27
import { RequestHandler, Response } from 'express';
28
import { RequestWithToken } from './token-middleware';
29
import { BodyValidator } from '../controller/policy';
30
import AsyncValidatorRegistry from './async-validator-registry';
31
import { isFail, validateSpecification } from '../helpers/specification-validation';
32

33
/**
1✔
34
 * Middleware that runs async (potentially DB-hitting) validation specs against the
35
 * request body before passing to the handler. Runs after RequestValidatorMiddleware
36
 * (structural Swagger check) and returns the same { valid, errors[] } shape on failure,
37
 * ensuring a consistent 400 response regardless of which layer caught the error.
38
 *
39
 * If no spec is registered in the registry for the given model name, the middleware
40
 * is a no-op and calls next() immediately.
41
 */
1✔
42
export default class AsyncValidatorMiddleware {
1✔
43
  private readonly registry: AsyncValidatorRegistry;
44

45
  private readonly validator: BodyValidator;
46

47
  /**
1✔
48
   * Creates a new async validator middleware instance.
49
   * @param registry - The registry to look up specs from.
50
   * @param validator - The BodyValidator containing the model name to look up.
51
   */
1✔
52
  public constructor(registry: AsyncValidatorRegistry, validator: BodyValidator) {
1✔
53
    this.registry = registry;
165✔
54
    this.validator = validator;
165✔
55
  }
165✔
56

57
  /**
1✔
58
   * Middleware handler. Looks up the spec for the model name and validates the
59
   * request. When a `buildTarget` is registered, it constructs the validation
60
   * target from the full request (params, token, body); otherwise `req.body`
61
   * is validated directly.
62
   * @param req - the express request to handle.
63
   * @param res - the express response object.
64
   * @param next - the express next function to continue processing of the request.
65
   */
1✔
66
  public async handle(req: RequestWithToken, res: Response, next: Function): Promise<void> {
1✔
67
    const entry = this.registry.get(this.validator.modelName);
298✔
68
    if (!entry) {
298✔
69
      next();
187✔
70
      return;
187✔
71
    }
187✔
72

73
    // Mirror RequestValidatorMiddleware behavior: if this endpoint allows a blank
111✔
74
    // body and the incoming body is empty/undefined, skip async validation — but
111✔
75
    // only when no buildTarget is registered, since buildTarget may construct a
111✔
76
    // valid target from route params or token data even with an empty body.
111✔
77
    if (this.validator.allowBlankTarget && !entry.buildTarget) {
298!
78
      const body = req.body;
×
79
      const isUndefinedOrNull = body === undefined || body === null;
×
80
      const isEmptyObject =
×
81
        typeof body === 'object' &&
×
82
        body !== null &&
×
83
        !Array.isArray(body) &&
×
84
        Object.keys(body).length === 0;
×
85

86
      if (isUndefinedOrNull || isEmptyObject) {
×
87
        next();
×
88
        return;
×
89
      }
×
90
    }
✔
91

92
    try {
111✔
93
      const spec = entry.factory();
111✔
94
      const target = entry.buildTarget ? entry.buildTarget(req) : req.body;
298✔
95
      const result = await validateSpecification(target, spec);
298✔
96
      if (isFail(result)) {
156✔
97
        res.status(400).json({ valid: false, errors: [String(result.fail.value)] });
58✔
98
        return;
58✔
99
      }
58✔
100

101
      next();
53✔
102
    } catch (err) {
156!
103
      next(err);
×
104
    }
×
105
  }
298✔
106

107
  /**
1✔
108
   * @returns a middleware handler to be used by express.
109
   */
1✔
110
  public getMiddleware(): RequestHandler {
1✔
111
    return this.handle.bind(this);
165✔
112
  }
165✔
113
}
1✔
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

© 2026 Coveralls, Inc