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

oprajs / opra / 19958387866

05 Dec 2025 09:19AM UTC coverage: 80.99% (-0.2%) from 81.23%
19958387866

push

github

erayhanoglu
chore: Updated deps

3534 of 4540 branches covered (77.84%)

Branch coverage included in aggregate %.

31111 of 38237 relevant lines covered (81.36%)

218.64 hits per line

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

94.28
/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 extends Pick<
1✔
28
    OpraSchema.HttpController,
1✔
29
    'description' | 'path'
1✔
30
  > {
1✔
31
    name: string;
1✔
32
    controllers?: (Type | ((parent: any) => any))[];
1✔
33
    types?: ThunkAsync<Type | EnumType.EnumObject | EnumType.EnumArray>[];
1✔
34
    operations?: Record<string, HttpOperation.Metadata>;
1✔
35
    parameters?: HttpParameter.Metadata[];
1✔
36
  }
1✔
37

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

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

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

1✔
69
  prototype: HttpController;
1✔
70
}
1✔
71

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

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

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

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

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

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

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

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

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

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

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

1✔
251
HttpController.prototype = HttpControllerClass.prototype;
1✔
252
Object.assign(HttpController, HttpControllerDecoratorFactory);
1✔
253
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