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

oprajs / opra / 14419802659

12 Apr 2025 12:55PM UTC coverage: 82.088% (+4.2%) from 77.908%
14419802659

push

github

web-flow
Merge pull request #27 from oprajs/dev

Dev

3458 of 4432 branches covered (78.02%)

Branch coverage included in aggregate %.

16 of 16 new or added lines in 10 files covered. (100.0%)

1793 existing lines in 129 files now uncovered.

29424 of 35625 relevant lines covered (82.59%)

183.2 hits per line

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

94.24
/packages/common/src/document/http/http-controller.ts
1
import nodePath from 'node:path';
1✔
2
import { omitUndefined } from '@jsopen/objects';
1✔
3
import type { Combine, ThunkAsync, Type } from 'ts-gems';
1✔
4
import { asMutable } from 'ts-gems';
1✔
5
import { ResponsiveMap } from '../../helpers/index.js';
1✔
6
import { OpraSchema } from '../../schema/index.js';
1✔
7
import type { ApiDocument } from '../api-document.js';
1✔
8
import { DataTypeMap } from '../common/data-type-map.js';
1✔
9
import { DocumentElement } from '../common/document-element.js';
1✔
10
import { CLASS_NAME_PATTERN, DECORATOR, kDataTypeMap } from '../constants.js';
1✔
11
import type { EnumType } from '../data-type/enum-type.js';
1✔
12
import { HttpControllerDecoratorFactory } from '../decorators/http-controller.decorator.js';
1✔
13
import {
1✔
14
  colorFgMagenta,
1✔
15
  colorFgYellow,
1✔
16
  colorReset,
1✔
17
  nodeInspectCustom,
1✔
18
} from '../utils/inspect.util.js';
1✔
19
import type { HttpApi } from './http-api.js';
1✔
20
import type { HttpOperation } from './http-operation';
1✔
21
import { HttpParameter } from './http-parameter.js';
1✔
22

1✔
23
/**
1✔
24
 * @namespace HttpController
1✔
25
 */
1✔
26
export namespace HttpController {
1✔
27
  export interface Metadata
1✔
28
    extends Pick<OpraSchema.HttpController, 'description' | 'path'> {
1✔
29
    name: string;
1✔
30
    controllers?: (Type | ((parent: any) => any))[];
1✔
31
    types?: ThunkAsync<Type | EnumType.EnumObject | EnumType.EnumArray>[];
1✔
32
    operations?: Record<string, HttpOperation.Metadata>;
1✔
33
    parameters?: HttpParameter.Metadata[];
1✔
34
  }
1✔
35

1✔
36
  export interface Options
1✔
37
    extends Partial<Pick<OpraSchema.HttpController, 'description' | 'path'>> {
1✔
38
    name?: string;
1✔
39
    controllers?: (Type | ((parent: any) => any))[];
1✔
40
  }
1✔
41

1✔
42
  export interface InitArguments
1✔
43
    extends Combine<
1✔
44
      {
1✔
45
        instance?: object;
1✔
46
        ctor?: Type;
1✔
47
      },
1✔
48
      Pick<Metadata, 'name' | 'description' | 'path'>
1✔
49
    > {}
1✔
50
}
1✔
51

1✔
52
/**
1✔
53
 * Type definition for HttpController
1✔
54
 * @class HttpController
1✔
55
 */
1✔
56
export interface HttpControllerStatic extends HttpControllerDecoratorFactory {
1✔
57
  /**
1✔
58
   * Class constructor of HttpController
1✔
59
   * @param owner
1✔
60
   * @param args
1✔
61
   */
1✔
62
  new (
1✔
63
    owner: HttpApi | HttpController,
1✔
64
    args: HttpController.InitArguments,
1✔
65
  ): HttpController;
1✔
66

1✔
67
  prototype: HttpController;
1✔
68
}
1✔
69

1✔
70
/**
1✔
71
 * Type definition of HttpController prototype
1✔
72
 * @interface HttpController
1✔
73
 */
1✔
74
export interface HttpController extends HttpControllerClass {}
1✔
75

1✔
76
/**
1✔
77
 * HttpController
1✔
78
 */
1✔
79
export const HttpController = function (
1✔
80
  this: HttpController | void,
235✔
81
  ...args: any[]
235✔
82
) {
235✔
83
  // ClassDecorator
235✔
84
  if (!this) return HttpController[DECORATOR].apply(undefined, args);
235✔
85

192✔
86
  // Constructor
192✔
87
  const [owner, initArgs] = args as [
192✔
88
    HttpApi | HttpController,
192✔
89
    HttpController.InitArguments,
192✔
90
  ];
192✔
91
  DocumentElement.call(this, owner);
192✔
92
  if (!CLASS_NAME_PATTERN.test(initArgs.name))
192✔
93
    throw new TypeError(`Invalid resource name (${initArgs.name})`);
235!
94
  const _this = asMutable(this);
192✔
95
  _this.kind = OpraSchema.HttpController.Kind;
192✔
96
  _this.types = _this.node[kDataTypeMap] = new DataTypeMap();
192✔
97
  _this.operations = new ResponsiveMap();
192✔
98
  _this.controllers = new ResponsiveMap();
192✔
99
  _this.parameters = [];
192✔
100
  _this.name = initArgs.name;
192✔
101
  _this.description = initArgs.description;
192✔
102
  _this.path = initArgs.path ?? initArgs.name;
235✔
103
  _this.instance = initArgs.instance;
235✔
104
  _this.ctor = initArgs.ctor;
235✔
105
  (_this as any)._controllerReverseMap = new WeakMap();
235✔
106
  (_this as any)._initialize?.(initArgs);
235!
107
} as HttpControllerStatic;
235✔
108

1✔
109
/**
1✔
110
 *
1✔
111
 * @class HttpController
1✔
112
 */
1✔
113
class HttpControllerClass extends DocumentElement {
1✔
114
  declare protected _controllerReverseMap: WeakMap<Type, HttpController | null>;
1✔
115
  declare readonly kind: OpraSchema.HttpController.Kind;
1✔
116
  declare readonly name: string;
1✔
117
  declare description?: string;
1✔
118
  declare path: string;
1✔
119
  declare instance?: any;
1✔
120
  declare ctor?: Type;
1✔
121
  declare parameters: HttpParameter[];
1✔
122
  declare operations: ResponsiveMap<HttpOperation>;
1✔
123
  declare controllers: ResponsiveMap<HttpController>;
1✔
124
  declare types: DataTypeMap;
1✔
125

1✔
126
  /**
1✔
127
   * @property isRoot
1✔
128
   */
1✔
129
  get isRoot(): boolean {
1✔
130
    return !(this.owner instanceof HttpController);
×
UNCOV
131
  }
×
132

1✔
133
  findController(controller: Type): HttpController | undefined;
1✔
134
  findController(resourcePath: string): HttpController | undefined;
1✔
135
  findController(arg0: string | Type): HttpController | undefined {
1✔
136
    if (typeof arg0 === 'function') {
57✔
137
      /** Check for cached mapping */
14✔
138
      let controller = this._controllerReverseMap.get(arg0);
14✔
139
      if (controller != null) return controller;
14✔
140
      /** Lookup for ctor in all controllers */
6✔
141
      for (const c of this.controllers.values()) {
14✔
142
        if (c.ctor === arg0) {
8✔
143
          this._controllerReverseMap.set(arg0, c);
5✔
144
          return c;
5✔
145
        }
5✔
146
        if (c.controllers.size) {
8✔
147
          controller = c.findController(arg0);
1✔
148
          if (controller) {
1✔
149
            this._controllerReverseMap.set(arg0, controller);
1✔
150
            return controller;
1✔
151
          }
1✔
152
        }
1✔
153
      }
8!
154
      this._controllerReverseMap.set(arg0, null);
×
155
      return;
×
UNCOV
156
    }
✔
157
    if (arg0.startsWith('/')) arg0 = arg0.substring(1);
57✔
158
    if (arg0.includes('/')) {
57✔
159
      const a = arg0.split('/');
3✔
160
      let r: HttpController | undefined = this;
3✔
161
      while (r && a.length > 0) {
3✔
162
        r = r.controllers.get(a.shift()!);
6✔
163
      }
6✔
164
      return r;
3✔
165
    }
3✔
166
    return this.controllers.get(arg0);
40✔
167
  }
40✔
168

1✔
169
  findParameter(
1✔
170
    paramName: string,
1,959✔
171
    location?: OpraSchema.HttpParameterLocation,
1,959✔
172
  ): HttpParameter | undefined {
1,959✔
173
    const paramNameLower = paramName.toLowerCase();
1,959✔
174
    let prm: any;
1,959✔
175
    for (prm of this.parameters) {
1,959✔
176
      if (location && location !== prm.location) continue;
698✔
177
      if (typeof prm.name === 'string') {
352✔
178
        prm._nameLower = prm._nameLower || prm.name.toLowerCase();
352✔
179
        if (prm._nameLower === paramNameLower) return prm;
352✔
180
      }
352✔
181
      if (prm.name instanceof RegExp && prm.name.test(paramName)) return prm;
698!
182
    }
698✔
183
    if (
1,903✔
184
      this.node.parent &&
1,903✔
185
      this.node.parent.element instanceof HttpController
1,903✔
186
    ) {
1,959✔
187
      return this.node.parent.element.findParameter(paramName, location);
551✔
188
    }
551✔
189
  }
1,959✔
190

1✔
191
  getFullUrl(): string {
1✔
192
    return nodePath.posix.join(
×
UNCOV
193
      this.owner instanceof HttpController ? this.owner.getFullUrl() : '/',
×
UNCOV
194
      this.path,
×
UNCOV
195
    );
×
UNCOV
196
  }
×
197

1✔
198
  /**
1✔
199
   *
1✔
200
   */
1✔
201
  toString(): string {
1✔
202
    return `[HttpController ${this.name}]`;
1✔
203
  }
1✔
204

1✔
205
  /**
1✔
206
   *
1✔
207
   */
1✔
208
  toJSON(options?: ApiDocument.ExportOptions): OpraSchema.HttpController {
1✔
209
    const out = omitUndefined<OpraSchema.HttpController>({
213✔
210
      kind: this.kind,
213✔
211
      description: this.description,
213✔
212
      path: this.path,
213✔
213
    });
213✔
214
    if (this.operations.size) {
213✔
215
      out.operations = {};
201✔
216
      for (const v of this.operations.values()) {
201✔
217
        out.operations[v.name] = v.toJSON(options);
551✔
218
      }
551✔
219
    }
201✔
220
    if (this.controllers.size) {
213✔
221
      out.controllers = {};
39✔
222
      for (const v of this.controllers.values()) {
39✔
223
        out.controllers[v.name] = v.toJSON(options);
48✔
224
      }
48✔
225
    }
39✔
226
    if (this.types.size) {
213✔
227
      out.types = {};
9✔
228
      for (const v of this.types.values()) {
9✔
229
        out.types[v.name!] = v.toJSON(options);
9✔
230
      }
9✔
231
    }
9✔
232
    if (this.parameters.length) {
213✔
233
      out.parameters = [];
61✔
234
      for (const prm of this.parameters) {
61✔
235
        out.parameters.push(prm.toJSON(options));
79✔
236
      }
79✔
237
    }
61✔
238
    return out;
213✔
239
  }
213✔
240

1✔
241
  /**
1✔
242
   *
1✔
243
   */
1✔
244
  protected [nodeInspectCustom](): string {
1✔
245
    return `[${colorFgYellow}HttpController${colorFgMagenta + this.name + colorReset}]`;
×
UNCOV
246
  }
×
247
}
1✔
248

1✔
249
HttpController.prototype = HttpControllerClass.prototype;
1✔
250
Object.assign(HttpController, HttpControllerDecoratorFactory);
1✔
251
HttpController[DECORATOR] = HttpControllerDecoratorFactory;
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