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

nestjs / nest / 651479c1-93e4-4126-b5fd-374131eeea16

13 Feb 2025 11:13AM UTC coverage: 89.284% (-0.01%) from 89.294%
651479c1-93e4-4126-b5fd-374131eeea16

Pull #14627

circleci

PleBea
fix(core): Handle wildcard routes with global prefix
Pull Request #14627: fix(core): Handle wildcard routes with global prefix

2667 of 3352 branches covered (79.56%)

1 of 2 new or added lines in 1 file covered. (50.0%)

7124 of 7979 relevant lines covered (89.28%)

16.39 hits per line

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

91.11
/packages/core/middleware/route-info-path-extractor.ts
1
import { VersioningType } from '@nestjs/common';
1✔
2
import {
3
  RouteInfo,
4
  VersioningOptions,
5
  VersionValue,
6
} from '@nestjs/common/interfaces';
7
import {
1✔
8
  addLeadingSlash,
9
  stripEndSlash,
10
} from '@nestjs/common/utils/shared.utils';
11
import { ApplicationConfig } from '../application-config';
12
import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface';
13
import { isRouteExcluded } from '../router/utils';
1✔
14
import { RoutePathFactory } from './../router/route-path-factory';
1✔
15

16
export class RouteInfoPathExtractor {
1✔
17
  private readonly routePathFactory: RoutePathFactory;
18
  private readonly prefixPath: string;
19
  private readonly excludedGlobalPrefixRoutes: ExcludeRouteMetadata[];
20
  private readonly versioningConfig?: VersioningOptions;
21

22
  constructor(private readonly applicationConfig: ApplicationConfig) {
17✔
23
    this.routePathFactory = new RoutePathFactory(applicationConfig);
17✔
24
    this.prefixPath = stripEndSlash(
17✔
25
      addLeadingSlash(this.applicationConfig.getGlobalPrefix()),
26
    );
27
    this.excludedGlobalPrefixRoutes =
17✔
28
      this.applicationConfig.getGlobalPrefixOptions().exclude!;
29
    this.versioningConfig = this.applicationConfig.getVersioning();
17✔
30
  }
31

32
  public extractPathsFrom({ path, method, version }: RouteInfo): string[] {
33
    const versionPaths = this.extractVersionPathFrom(version);
10✔
34

35
    if (this.prefixPath && path === '/*') {
10!
NEW
36
      path = '/*path';
×
37
    }
38

39
    if (this.isAWildcard(path)) {
10✔
40
      const entries =
41
        versionPaths.length > 0
6✔
42
          ? versionPaths
6✔
43
              .map(versionPath => [
3✔
44
                this.prefixPath + versionPath + '$',
45
                this.prefixPath + versionPath + addLeadingSlash(path),
46
              ])
47
              .flat()
48
          : this.prefixPath
49
            ? [this.prefixPath + '$', this.prefixPath + addLeadingSlash(path)]
3✔
50
            : [addLeadingSlash(path)];
51

52
      return Array.isArray(this.excludedGlobalPrefixRoutes)
6✔
53
        ? [
6✔
54
            ...entries,
55
            ...this.excludedGlobalPrefixRoutes
56
              .map(route =>
57
                Array.isArray(versionPaths) && versionPaths.length > 0
2✔
58
                  ? versionPaths.map(v => v + addLeadingSlash(route.path))
1✔
59
                  : addLeadingSlash(route.path),
60
              )
61
              .flat(),
62
          ]
63
        : entries;
64
    }
65

66
    return this.extractNonWildcardPathsFrom({ path, method, version });
4✔
67
  }
68

69
  public extractPathFrom(route: RouteInfo): string[] {
70
    if (this.isAWildcard(route.path) && !route.version) {
9✔
71
      return [addLeadingSlash(route.path)];
3✔
72
    }
73

74
    return this.extractNonWildcardPathsFrom(route);
6✔
75
  }
76

77
  private isAWildcard(path: string): boolean {
78
    const isSimpleWildcard = ['*', '/*', '/*/', '(.*)', '/(.*)'];
19✔
79
    if (isSimpleWildcard.includes(path)) {
19✔
80
      return true;
12✔
81
    }
82

83
    const wildcardRegexp = /^\/\{.*\}.*|^\/\*.*$/;
7✔
84
    return wildcardRegexp.test(path);
7✔
85
  }
86

87
  private extractNonWildcardPathsFrom({
88
    path,
89
    method,
90
    version,
91
  }: RouteInfo): string[] {
92
    const versionPaths = this.extractVersionPathFrom(version);
10✔
93

94
    if (
10✔
95
      Array.isArray(this.excludedGlobalPrefixRoutes) &&
15✔
96
      isRouteExcluded(this.excludedGlobalPrefixRoutes, path, method)
97
    ) {
98
      if (!versionPaths.length) {
2!
99
        return [addLeadingSlash(path)];
×
100
      }
101

102
      return versionPaths.map(
2✔
103
        versionPath => versionPath + addLeadingSlash(path),
2✔
104
      );
105
    }
106

107
    if (!versionPaths.length) {
8✔
108
      return [this.prefixPath + addLeadingSlash(path)];
3✔
109
    }
110
    return versionPaths.map(
5✔
111
      versionPath => this.prefixPath + versionPath + addLeadingSlash(path),
5✔
112
    );
113
  }
114

115
  private extractVersionPathFrom(versionValue?: VersionValue): string[] {
116
    if (!versionValue || this.versioningConfig?.type !== VersioningType.URI)
20✔
117
      return [];
8✔
118

119
    const versionPrefix = this.routePathFactory.getVersionPrefix(
12✔
120
      this.versioningConfig,
121
    );
122

123
    if (Array.isArray(versionValue)) {
12!
124
      return versionValue.map(version =>
×
125
        addLeadingSlash(versionPrefix + version.toString()),
×
126
      );
127
    }
128
    return [addLeadingSlash(versionPrefix + versionValue.toString())];
12✔
129
  }
130
}
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