@backstage/backend-openapi-utils 0.2.1-next.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @backstage/backend-openapi-utils
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1440232: Adds a new `createValidatedOpenApiRouterFromGeneratedEndpointMap` function that uses the new static server generation in `backstage-cli package schema openapi generate --server` to create a typed express router.
8
+
9
+ ### Patch Changes
10
+
11
+ - f01787a: Moves msw from dependencies to devDependencies
12
+ - Updated dependencies
13
+ - @backstage/types@1.2.0
14
+ - @backstage/backend-plugin-api@1.0.2
15
+ - @backstage/errors@1.2.5
16
+
17
+ ## 0.3.0-next.2
18
+
19
+ ### Minor Changes
20
+
21
+ - 1440232: Adds a new `createValidatedOpenApiRouterFromGeneratedEndpointMap` function that uses the new static server generation in `backstage-cli package schema openapi generate --server` to create a typed express router.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+ - @backstage/backend-plugin-api@1.0.2-next.2
27
+ - @backstage/errors@1.2.4
28
+ - @backstage/types@1.1.1
29
+
3
30
  ## 0.2.1-next.1
4
31
 
5
32
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -1,14 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  var index = require('./types/index.cjs.js');
4
- var stub = require('./stub.cjs.js');
5
4
  var testUtils = require('./testUtils.cjs.js');
5
+ var stub = require('./stub.cjs.js');
6
6
 
7
7
 
8
8
 
9
9
  exports.internal = index;
10
- exports.createValidatedOpenApiRouter = stub.createValidatedOpenApiRouter;
11
- exports.getOpenApiSpecRoute = stub.getOpenApiSpecRoute;
12
10
  exports.wrapInOpenApiTestServer = testUtils.wrapInOpenApiTestServer;
13
11
  exports.wrapServer = testUtils.wrapServer;
12
+ exports.createValidatedOpenApiRouter = stub.createValidatedOpenApiRouter;
13
+ exports.createValidatedOpenApiRouterFromGeneratedEndpointMap = stub.createValidatedOpenApiRouterFromGeneratedEndpointMap;
14
+ exports.getOpenApiSpecRoute = stub.getOpenApiSpecRoute;
14
15
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /// <reference types="node" />
2
2
  import { JSONSchema, FromSchema } from 'json-schema-to-ts';
3
- import { ReferenceObject, OpenAPIObject, ContentObject, RequestBodyObject, ResponseObject, ParameterObject, SchemaObject } from 'openapi3-ts';
3
+ import { OpenAPIObject, ReferenceObject, ContentObject, ParameterObject, SchemaObject, ResponseObject, RequestBodyObject } from 'openapi3-ts';
4
4
  import core from 'express-serve-static-core';
5
- import { Router, RequestHandler, Express } from 'express';
6
- import { middleware } from 'express-openapi-validator';
5
+ import { Router, Express, RequestHandler } from 'express';
7
6
  import { Server } from 'http';
7
+ import { middleware } from 'express-openapi-validator';
8
8
 
9
9
  /**
10
10
  * This file is meant to hold Immutable overwrites of the values provided by the `openapi3-ts`
@@ -391,12 +391,100 @@ interface DocRequestMatcher<Doc extends RequiredDoc, T, Method extends 'all' | '
391
391
  <Path extends MethodAwareDocPath<Doc, PathTemplate<Extract<keyof Doc['paths'], string>>, Method>>(path: Path, ...handlers: Array<DocRequestHandlerParams<Doc, TemplateToDocPath<Doc, Path>, Method>>): T;
392
392
  }
393
393
 
394
+ /**
395
+ * @public
396
+ */
397
+ type EndpointMap = Record<string, {
398
+ query?: object;
399
+ body?: object;
400
+ response?: object | void;
401
+ path?: object;
402
+ }>;
403
+ /**
404
+ * @public
405
+ */
406
+ type HttpMethods = 'all' | 'put' | 'get' | 'post' | '_delete';
407
+ /**
408
+ * @public
409
+ */
410
+ type StaticPathParamsSchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = `#${Method}|${Endpoint}` extends keyof Doc ? 'path' extends keyof Doc[`#${Method}|${Endpoint}`] ? Doc[`#${Method}|${Endpoint}`]['path'] : never : never;
411
+ /**
412
+ * @public
413
+ */
414
+ type StaticRequestBodySchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = `#${Method}|${Endpoint}` extends keyof Doc ? 'body' extends keyof Doc[`#${Method}|${Endpoint}`] ? Doc[`#${Method}|${Endpoint}`]['body'] : unknown : unknown;
415
+ /**
416
+ * @public
417
+ */
418
+ type StaticResponseSchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = `#${Method}|${Endpoint}` extends keyof Doc ? 'response' extends keyof Doc[`#${Method}|${Endpoint}`] ? Doc[`#${Method}|${Endpoint}`]['response'] : unknown : unknown;
419
+ /**
420
+ * @public
421
+ */
422
+ type StaticQueryParamsSchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = `#${Method}|${Endpoint}` extends keyof Doc ? 'query' extends keyof Doc[`#${Method}|${Endpoint}`] ? Doc[`#${Method}|${Endpoint}`]['query'] : never : never;
423
+ /**
424
+ * Typed express request handler.
425
+ * @public
426
+ */
427
+ type EndpointMapRequestHandler<Doc extends EndpointMap, Path extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Path>> = core.RequestHandler<StaticPathParamsSchema<Doc, Path, Method>, StaticResponseSchema<Doc, Path, Method>, StaticRequestBodySchema<Doc, Path, Method>, StaticQueryParamsSchema<Doc, Path, Method>, Record<string, string>>;
428
+ /**
429
+ * Typed express error handler / request handler union type.
430
+ * @public
431
+ */
432
+ type EndpointMapRequestHandlerParams<Doc extends EndpointMap, Path extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Path>> = core.RequestHandlerParams<StaticPathParamsSchema<Doc, Path, Method>, StaticResponseSchema<Doc, Path, Method>, StaticRequestBodySchema<Doc, Path, Method>, StaticQueryParamsSchema<Doc, Path, Method>, Record<string, string>>;
433
+ /**
434
+ * @public
435
+ */
436
+ type DocEndpoint<Doc extends EndpointMap> = ValueOf<{
437
+ [Template in keyof Doc]: Template extends `#${string}|${infer Endpoint}` ? Endpoint : never;
438
+ }>;
439
+ /**
440
+ * @public
441
+ */
442
+ type DocEndpointMethod<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>> = ValueOf<{
443
+ [Template in keyof Doc]: Template extends `#${infer Method}|${Endpoint}` ? Method : never;
444
+ }>;
445
+ /**
446
+ * @public
447
+ */
448
+ type MethodAwareDocEndpoints<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = ValueOf<{
449
+ [Template in keyof Doc]: Template extends `#${Method}|${infer E}` ? E extends DocEndpoint<Doc> ? PathTemplate<E> : never : never;
450
+ }>;
451
+ /**
452
+ * @public
453
+ */
454
+ type DocEndpointTemplate<Doc extends EndpointMap> = PathTemplate<DocEndpoint<Doc>>;
455
+ /**
456
+ * @public
457
+ */
458
+ type TemplateToDocEndpoint<Doc extends EndpointMap, Path extends DocEndpointTemplate<Doc>> = ValueOf<{
459
+ [Template in DocEndpoint<Doc>]: Path extends PathTemplate<Template> ? Template : never;
460
+ }>;
461
+ /**
462
+ * Superset of the express router path matcher that enforces typed request and response bodies.
463
+ * @public
464
+ */
465
+ interface EndpointMapRequestMatcher<Doc extends EndpointMap, T, Method extends HttpMethods> {
466
+ <TPath extends MethodAwareDocEndpoints<Doc, DocEndpoint<Doc>, Method & DocEndpointMethod<Doc, DocEndpoint<Doc>>>, TMethod extends Method & DocEndpointMethod<Doc, TemplateToDocEndpoint<Doc, TPath>>>(path: TPath, ...handlers: Array<EndpointMapRequestHandler<Doc, TemplateToDocEndpoint<Doc, TPath>, TMethod>>): T;
467
+ <TPath extends MethodAwareDocEndpoints<Doc, DocEndpoint<Doc>, Method & DocEndpointMethod<Doc, DocEndpoint<Doc>>>, TMethod extends Method & DocEndpointMethod<Doc, TemplateToDocEndpoint<Doc, TPath>>>(path: TPath, ...handlers: Array<EndpointMapRequestHandlerParams<Doc, TemplateToDocEndpoint<Doc, TPath>, TMethod>>): T;
468
+ }
469
+ /**
470
+ * @public
471
+ */
472
+ interface TypedRouter<GeneratedEndpointMap extends EndpointMap> extends Router {
473
+ get: EndpointMapRequestMatcher<GeneratedEndpointMap, this, 'get'>;
474
+ post: EndpointMapRequestMatcher<GeneratedEndpointMap, this, 'post'>;
475
+ put: EndpointMapRequestMatcher<GeneratedEndpointMap, this, 'put'>;
476
+ delete: EndpointMapRequestMatcher<GeneratedEndpointMap, this, '_delete'>;
477
+ }
478
+
394
479
  type index_d_ComponentRef<Doc extends RequiredDoc, Type extends ComponentTypes<Doc>, Ref extends ImmutableReferenceObject> = ComponentRef<Doc, Type, Ref>;
395
480
  type index_d_ComponentTypes<Doc extends RequiredDoc> = ComponentTypes<Doc>;
396
481
  type index_d_ConvertAll<T extends ReadonlyArray<unknown>> = ConvertAll<T>;
397
482
  type index_d_CookieObject = CookieObject;
398
483
  type index_d_CookieSchema<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>> = CookieSchema<Doc, Path, Method>;
399
484
  type index_d_DiscriminateUnion<T, K extends keyof T, V extends T[K]> = DiscriminateUnion<T, K, V>;
485
+ type index_d_DocEndpoint<Doc extends EndpointMap> = DocEndpoint<Doc>;
486
+ type index_d_DocEndpointMethod<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>> = DocEndpointMethod<Doc, Endpoint>;
487
+ type index_d_DocEndpointTemplate<Doc extends EndpointMap> = DocEndpointTemplate<Doc>;
400
488
  type index_d_DocOperation<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends keyof Doc['paths'][Path]> = DocOperation<Doc, Path, Method>;
401
489
  type index_d_DocParameter<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>, Parameter extends keyof DocOperation<Doc, Path, Method>['parameters']> = DocParameter<Doc, Path, Method, Parameter>;
402
490
  type index_d_DocParameters<Doc extends RequiredDoc, Path extends Extract<keyof Doc['paths'], string>, Method extends keyof Doc['paths'][Path]> = DocParameters<Doc, Path, Method>;
@@ -407,6 +495,10 @@ type index_d_DocPathTemplateMethod<Doc extends Pick<RequiredDoc, 'paths'>, Path
407
495
  type index_d_DocRequestHandler<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>> = DocRequestHandler<Doc, Path, Method>;
408
496
  type index_d_DocRequestHandlerParams<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>> = DocRequestHandlerParams<Doc, Path, Method>;
409
497
  type index_d_DocRequestMatcher<Doc extends RequiredDoc, T, Method extends 'all' | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head'> = DocRequestMatcher<Doc, T, Method>;
498
+ type index_d_EndpointMap = EndpointMap;
499
+ type index_d_EndpointMapRequestHandler<Doc extends EndpointMap, Path extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Path>> = EndpointMapRequestHandler<Doc, Path, Method>;
500
+ type index_d_EndpointMapRequestHandlerParams<Doc extends EndpointMap, Path extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Path>> = EndpointMapRequestHandlerParams<Doc, Path, Method>;
501
+ type index_d_EndpointMapRequestMatcher<Doc extends EndpointMap, T, Method extends HttpMethods> = EndpointMapRequestMatcher<Doc, T, Method>;
410
502
  type index_d_Filter<T, U> = Filter<T, U>;
411
503
  type index_d_FromNumberStringToNumber<NumberString extends string | number | symbol> = FromNumberStringToNumber<NumberString>;
412
504
  type index_d_FullMap<T extends {
@@ -414,6 +506,7 @@ type index_d_FullMap<T extends {
414
506
  }> = FullMap<T>;
415
507
  type index_d_HeaderObject = HeaderObject;
416
508
  type index_d_HeaderSchema<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>> = HeaderSchema<Doc, Path, Method>;
509
+ type index_d_HttpMethods = HttpMethods;
417
510
  type index_d_Immutable<T> = Immutable<T>;
418
511
  type index_d_ImmutableContentObject = ImmutableContentObject;
419
512
  type index_d_ImmutableCookieObject = ImmutableCookieObject;
@@ -430,6 +523,7 @@ type index_d_ImmutableSchemaObject = ImmutableSchemaObject;
430
523
  type index_d_LastOf<T> = LastOf<T>;
431
524
  type index_d_MapDiscriminatedUnion<T extends Record<K, string>, K extends keyof T> = MapDiscriminatedUnion<T, K>;
432
525
  type index_d_MapToSchema<Doc extends RequiredDoc, T extends Record<string, ImmutableParameterObject>> = MapToSchema<Doc, T>;
526
+ type index_d_MethodAwareDocEndpoints<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = MethodAwareDocEndpoints<Doc, Endpoint, Method>;
433
527
  type index_d_MethodAwareDocPath<Doc extends PathDoc, Path extends DocPathTemplate<Doc>, Method extends DocPathTemplateMethod<Doc, Path>> = MethodAwareDocPath<Doc, Path, Method>;
434
528
  type index_d_ObjectWithContentSchema<Doc extends RequiredDoc, Object extends {
435
529
  content?: ImmutableContentObject;
@@ -462,14 +556,20 @@ type index_d_RequiredMap<T extends {
462
556
  type index_d_ResponseBodyToJsonSchema<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>> = ResponseBodyToJsonSchema<Doc, Path, Method>;
463
557
  type index_d_ResponseSchemas<Doc extends RequiredDoc, Path extends DocPath<Doc>, Method extends DocPathMethod<Doc, Path>> = ResponseSchemas<Doc, Path, Method>;
464
558
  type index_d_SchemaRef<Doc extends RequiredDoc, Schema> = SchemaRef<Doc, Schema>;
559
+ type index_d_StaticPathParamsSchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = StaticPathParamsSchema<Doc, Endpoint, Method>;
560
+ type index_d_StaticQueryParamsSchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = StaticQueryParamsSchema<Doc, Endpoint, Method>;
561
+ type index_d_StaticRequestBodySchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = StaticRequestBodySchema<Doc, Endpoint, Method>;
562
+ type index_d_StaticResponseSchema<Doc extends EndpointMap, Endpoint extends DocEndpoint<Doc>, Method extends DocEndpointMethod<Doc, Endpoint>> = StaticResponseSchema<Doc, Endpoint, Method>;
563
+ type index_d_TemplateToDocEndpoint<Doc extends EndpointMap, Path extends DocEndpointTemplate<Doc>> = TemplateToDocEndpoint<Doc, Path>;
465
564
  type index_d_TemplateToDocPath<Doc extends PathDoc, Path extends DocPathTemplate<Doc>> = TemplateToDocPath<Doc, Path>;
466
565
  type index_d_ToTypeSafe<T> = ToTypeSafe<T>;
467
566
  type index_d_TuplifyUnion<T, L = LastOf<T>, N = [T] extends [never] ? true : false> = TuplifyUnion<T, L, N>;
567
+ type index_d_TypedRouter<GeneratedEndpointMap extends EndpointMap> = TypedRouter<GeneratedEndpointMap>;
468
568
  type index_d_UnionToIntersection<U> = UnionToIntersection<U>;
469
569
  type index_d_UnknownIfNever<P> = UnknownIfNever<P>;
470
570
  type index_d_ValueOf<T> = ValueOf<T>;
471
571
  declare namespace index_d {
472
- export type { index_d_ComponentRef as ComponentRef, index_d_ComponentTypes as ComponentTypes, index_d_ConvertAll as ConvertAll, index_d_CookieObject as CookieObject, index_d_CookieSchema as CookieSchema, index_d_DiscriminateUnion as DiscriminateUnion, index_d_DocOperation as DocOperation, index_d_DocParameter as DocParameter, index_d_DocParameters as DocParameters, index_d_DocPath as DocPath, index_d_DocPathMethod as DocPathMethod, index_d_DocPathTemplate as DocPathTemplate, index_d_DocPathTemplateMethod as DocPathTemplateMethod, index_d_DocRequestHandler as DocRequestHandler, index_d_DocRequestHandlerParams as DocRequestHandlerParams, index_d_DocRequestMatcher as DocRequestMatcher, index_d_Filter as Filter, index_d_FromNumberStringToNumber as FromNumberStringToNumber, index_d_FullMap as FullMap, index_d_HeaderObject as HeaderObject, index_d_HeaderSchema as HeaderSchema, index_d_Immutable as Immutable, index_d_ImmutableContentObject as ImmutableContentObject, index_d_ImmutableCookieObject as ImmutableCookieObject, index_d_ImmutableHeaderObject as ImmutableHeaderObject, index_d_ImmutableObject as ImmutableObject, index_d_ImmutableOpenAPIObject as ImmutableOpenAPIObject, index_d_ImmutableParameterObject as ImmutableParameterObject, index_d_ImmutablePathObject as ImmutablePathObject, index_d_ImmutableQueryObject as ImmutableQueryObject, index_d_ImmutableReferenceObject as ImmutableReferenceObject, index_d_ImmutableRequestBodyObject as ImmutableRequestBodyObject, index_d_ImmutableResponseObject as ImmutableResponseObject, index_d_ImmutableSchemaObject as ImmutableSchemaObject, index_d_LastOf as LastOf, index_d_MapDiscriminatedUnion as MapDiscriminatedUnion, index_d_MapToSchema as MapToSchema, index_d_MethodAwareDocPath as MethodAwareDocPath, index_d_ObjectWithContentSchema as ObjectWithContentSchema, index_d_OptionalMap as OptionalMap, index_d_ParameterSchema as ParameterSchema, index_d_ParametersSchema as ParametersSchema, index_d_PathDoc as PathDoc, index_d_PathObject as PathObject, index_d_PathSchema as PathSchema, index_d_PathTemplate as PathTemplate, index_d_PickOptionalKeys as PickOptionalKeys, index_d_PickRequiredKeys as PickRequiredKeys, index_d_Push as Push, index_d_QueryObject as QueryObject, index_d_QuerySchema as QuerySchema, index_d_RequestBody as RequestBody, index_d_RequestBodySchema as RequestBodySchema, index_d_RequestBodyToJsonSchema as RequestBodyToJsonSchema, index_d_RequiredDoc as RequiredDoc, index_d_RequiredMap as RequiredMap, Response$1 as Response, index_d_ResponseBodyToJsonSchema as ResponseBodyToJsonSchema, index_d_ResponseSchemas as ResponseSchemas, index_d_SchemaRef as SchemaRef, index_d_TemplateToDocPath as TemplateToDocPath, index_d_ToTypeSafe as ToTypeSafe, index_d_TuplifyUnion as TuplifyUnion, index_d_UnionToIntersection as UnionToIntersection, index_d_UnknownIfNever as UnknownIfNever, index_d_ValueOf as ValueOf };
572
+ export type { index_d_ComponentRef as ComponentRef, index_d_ComponentTypes as ComponentTypes, index_d_ConvertAll as ConvertAll, index_d_CookieObject as CookieObject, index_d_CookieSchema as CookieSchema, index_d_DiscriminateUnion as DiscriminateUnion, index_d_DocEndpoint as DocEndpoint, index_d_DocEndpointMethod as DocEndpointMethod, index_d_DocEndpointTemplate as DocEndpointTemplate, index_d_DocOperation as DocOperation, index_d_DocParameter as DocParameter, index_d_DocParameters as DocParameters, index_d_DocPath as DocPath, index_d_DocPathMethod as DocPathMethod, index_d_DocPathTemplate as DocPathTemplate, index_d_DocPathTemplateMethod as DocPathTemplateMethod, index_d_DocRequestHandler as DocRequestHandler, index_d_DocRequestHandlerParams as DocRequestHandlerParams, index_d_DocRequestMatcher as DocRequestMatcher, index_d_EndpointMap as EndpointMap, index_d_EndpointMapRequestHandler as EndpointMapRequestHandler, index_d_EndpointMapRequestHandlerParams as EndpointMapRequestHandlerParams, index_d_EndpointMapRequestMatcher as EndpointMapRequestMatcher, index_d_Filter as Filter, index_d_FromNumberStringToNumber as FromNumberStringToNumber, index_d_FullMap as FullMap, index_d_HeaderObject as HeaderObject, index_d_HeaderSchema as HeaderSchema, index_d_HttpMethods as HttpMethods, index_d_Immutable as Immutable, index_d_ImmutableContentObject as ImmutableContentObject, index_d_ImmutableCookieObject as ImmutableCookieObject, index_d_ImmutableHeaderObject as ImmutableHeaderObject, index_d_ImmutableObject as ImmutableObject, index_d_ImmutableOpenAPIObject as ImmutableOpenAPIObject, index_d_ImmutableParameterObject as ImmutableParameterObject, index_d_ImmutablePathObject as ImmutablePathObject, index_d_ImmutableQueryObject as ImmutableQueryObject, index_d_ImmutableReferenceObject as ImmutableReferenceObject, index_d_ImmutableRequestBodyObject as ImmutableRequestBodyObject, index_d_ImmutableResponseObject as ImmutableResponseObject, index_d_ImmutableSchemaObject as ImmutableSchemaObject, index_d_LastOf as LastOf, index_d_MapDiscriminatedUnion as MapDiscriminatedUnion, index_d_MapToSchema as MapToSchema, index_d_MethodAwareDocEndpoints as MethodAwareDocEndpoints, index_d_MethodAwareDocPath as MethodAwareDocPath, index_d_ObjectWithContentSchema as ObjectWithContentSchema, index_d_OptionalMap as OptionalMap, index_d_ParameterSchema as ParameterSchema, index_d_ParametersSchema as ParametersSchema, index_d_PathDoc as PathDoc, index_d_PathObject as PathObject, index_d_PathSchema as PathSchema, index_d_PathTemplate as PathTemplate, index_d_PickOptionalKeys as PickOptionalKeys, index_d_PickRequiredKeys as PickRequiredKeys, index_d_Push as Push, index_d_QueryObject as QueryObject, index_d_QuerySchema as QuerySchema, index_d_RequestBody as RequestBody, index_d_RequestBodySchema as RequestBodySchema, index_d_RequestBodyToJsonSchema as RequestBodyToJsonSchema, index_d_RequiredDoc as RequiredDoc, index_d_RequiredMap as RequiredMap, Response$1 as Response, index_d_ResponseBodyToJsonSchema as ResponseBodyToJsonSchema, index_d_ResponseSchemas as ResponseSchemas, index_d_SchemaRef as SchemaRef, index_d_StaticPathParamsSchema as StaticPathParamsSchema, index_d_StaticQueryParamsSchema as StaticQueryParamsSchema, index_d_StaticRequestBodySchema as StaticRequestBodySchema, index_d_StaticResponseSchema as StaticResponseSchema, index_d_TemplateToDocEndpoint as TemplateToDocEndpoint, index_d_TemplateToDocPath as TemplateToDocPath, index_d_ToTypeSafe as ToTypeSafe, index_d_TuplifyUnion as TuplifyUnion, index_d_TypedRouter as TypedRouter, index_d_UnionToIntersection as UnionToIntersection, index_d_UnknownIfNever as UnknownIfNever, index_d_ValueOf as ValueOf };
473
573
  }
474
574
 
475
575
  /**
@@ -512,6 +612,25 @@ interface ApiRouter<Doc extends RequiredDoc> extends Router {
512
612
  head: DocRequestMatcher<Doc, this, 'head'>;
513
613
  }
514
614
 
615
+ /**
616
+ * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!
617
+ * Setup a server with a custom OpenAPI proxy. This proxy will capture all requests and responses and make sure they
618
+ * conform to the spec.
619
+ * @param app - express server, needed to ensure we have the correct ports for the proxy.
620
+ * @returns - a configured HTTP server that should be used with supertest.
621
+ * @public
622
+ */
623
+ declare function wrapServer(app: Express): Promise<Server>;
624
+ /**
625
+ * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!
626
+ * Running against supertest, we need some way to hit the optic proxy. This ensures that
627
+ * that happens at runtime when in the context of a `yarn optic capture` command.
628
+ * @param app - Express router that would be passed to supertest's `request`.
629
+ * @returns A wrapper around the express router (or the router untouched) that still works with supertest.
630
+ * @public
631
+ */
632
+ declare const wrapInOpenApiTestServer: (app: Express) => Server | Express;
633
+
515
634
  /**
516
635
  * Given a base url for a plugin, find the given OpenAPI spec for that plugin.
517
636
  * @param baseUrl - Plugin base url.
@@ -530,24 +649,16 @@ declare function createValidatedOpenApiRouter<T extends RequiredDoc>(spec: T, op
530
649
  validatorOptions?: Partial<Parameters<typeof middleware>['0']>;
531
650
  middleware?: RequestHandler[];
532
651
  }): ApiRouter<T>;
533
-
534
- /**
535
- * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!
536
- * Setup a server with a custom OpenAPI proxy. This proxy will capture all requests and responses and make sure they
537
- * conform to the spec.
538
- * @param app - express server, needed to ensure we have the correct ports for the proxy.
539
- * @returns - a configured HTTP server that should be used with supertest.
540
- * @public
541
- */
542
- declare function wrapServer(app: Express): Promise<Server>;
543
652
  /**
544
- * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!
545
- * Running against supertest, we need some way to hit the optic proxy. This ensures that
546
- * that happens at runtime when in the context of a `yarn optic capture` command.
547
- * @param app - Express router that would be passed to supertest's `request`.
548
- * @returns A wrapper around the express router (or the router untouched) that still works with supertest.
653
+ * Create a new OpenAPI router with some default middleware.
654
+ * @param spec - Your OpenAPI spec imported as a JSON object.
655
+ * @param validatorOptions - `openapi-express-validator` options to override the defaults.
656
+ * @returns A new express router with validation middleware.
549
657
  * @public
550
658
  */
551
- declare const wrapInOpenApiTestServer: (app: Express) => Server | Express;
659
+ declare function createValidatedOpenApiRouterFromGeneratedEndpointMap<T extends EndpointMap>(spec: RequiredDoc, options?: {
660
+ validatorOptions?: Partial<Parameters<typeof middleware>['0']>;
661
+ middleware?: RequestHandler[];
662
+ }): TypedRouter<T>;
552
663
 
553
- export { type ApiRouter, type CookieParameters, type HeaderParameters, type PathParameters, type QueryParameters, type Request, type Response, createValidatedOpenApiRouter, getOpenApiSpecRoute, index_d as internal, wrapInOpenApiTestServer, wrapServer };
664
+ export { type ApiRouter, type CookieParameters, type HeaderParameters, type PathParameters, type PathTemplate, type QueryParameters, type Request, type Response, createValidatedOpenApiRouter, createValidatedOpenApiRouterFromGeneratedEndpointMap, getOpenApiSpecRoute, index_d as internal, wrapInOpenApiTestServer, wrapServer };
@@ -1 +1 @@
1
- {"version":3,"file":"setup.cjs.js","sources":["../../src/proxy/setup.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as mockttp from 'mockttp';\nimport { OpenApiProxyValidator } from '../schema/validation';\nimport getPort from 'get-port';\nimport { Server } from 'http';\n\nexport class Proxy {\n server: mockttp.Mockttp;\n #openRequests: Record<string, mockttp.CompletedRequest> = {};\n requestResponsePairs = new Map<\n mockttp.CompletedRequest,\n mockttp.CompletedResponse\n >();\n validator: OpenApiProxyValidator;\n public forwardTo: { port: number } = { port: 0 };\n express: { server: Server | undefined } = { server: undefined };\n constructor() {\n this.server = mockttp.getLocal();\n this.validator = new OpenApiProxyValidator();\n }\n\n async setup() {\n await this.server.start();\n this.forwardTo.port = await getPort();\n this.server\n .forAnyRequest()\n .thenForwardTo(`http://localhost:${this.forwardTo.port}`);\n await this.server.on('request', request => {\n this.#openRequests[request.id] = request;\n });\n await this.server.on('response', response => {\n const request = this.#openRequests[response.id];\n if (request) {\n this.requestResponsePairs.set(request, response);\n }\n delete this.#openRequests[response.id];\n this.validator.validate(request, response);\n });\n }\n\n async initialize(url: string, server: Server) {\n await this.validator.initialize(`${url}/openapi.json`);\n this.express.server = server;\n }\n\n stop() {\n if (Object.keys(this.#openRequests).length > 0) {\n throw new Error('There are still open requests');\n }\n this.server.stop();\n\n // If this isn't expressly closed, it will cause a jest memory leak warning.\n this.express.server?.close();\n }\n\n get url() {\n return this.server.proxyEnv.HTTP_PROXY;\n }\n}\n"],"names":["mockttp","OpenApiProxyValidator","getPort"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBO,MAAM,KAAM,CAAA;AAAA,EACjB,MAAA,CAAA;AAAA,EACA,gBAA0D,EAAC,CAAA;AAAA,EAC3D,oBAAA,uBAA2B,GAGzB,EAAA,CAAA;AAAA,EACF,SAAA,CAAA;AAAA,EACO,SAAA,GAA8B,EAAE,IAAA,EAAM,CAAE,EAAA,CAAA;AAAA,EAC/C,OAAA,GAA0C,EAAE,MAAA,EAAQ,KAAU,CAAA,EAAA,CAAA;AAAA,EAC9D,WAAc,GAAA;AACZ,IAAK,IAAA,CAAA,MAAA,GAASA,mBAAQ,QAAS,EAAA,CAAA;AAC/B,IAAK,IAAA,CAAA,SAAA,GAAY,IAAIC,gCAAsB,EAAA,CAAA;AAAA,GAC7C;AAAA,EAEA,MAAM,KAAQ,GAAA;AACZ,IAAM,MAAA,IAAA,CAAK,OAAO,KAAM,EAAA,CAAA;AACxB,IAAK,IAAA,CAAA,SAAA,CAAU,IAAO,GAAA,MAAMC,wBAAQ,EAAA,CAAA;AACpC,IAAK,IAAA,CAAA,MAAA,CACF,eACA,CAAA,aAAA,CAAc,oBAAoB,IAAK,CAAA,SAAA,CAAU,IAAI,CAAE,CAAA,CAAA,CAAA;AAC1D,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,EAAG,CAAA,SAAA,EAAW,CAAW,OAAA,KAAA;AACzC,MAAK,IAAA,CAAA,aAAA,CAAc,OAAQ,CAAA,EAAE,CAAI,GAAA,OAAA,CAAA;AAAA,KAClC,CAAA,CAAA;AACD,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,EAAG,CAAA,UAAA,EAAY,CAAY,QAAA,KAAA;AAC3C,MAAA,MAAM,OAAU,GAAA,IAAA,CAAK,aAAc,CAAA,QAAA,CAAS,EAAE,CAAA,CAAA;AAC9C,MAAA,IAAI,OAAS,EAAA;AACX,QAAK,IAAA,CAAA,oBAAA,CAAqB,GAAI,CAAA,OAAA,EAAS,QAAQ,CAAA,CAAA;AAAA,OACjD;AACA,MAAO,OAAA,IAAA,CAAK,aAAc,CAAA,QAAA,CAAS,EAAE,CAAA,CAAA;AACrC,MAAK,IAAA,CAAA,SAAA,CAAU,QAAS,CAAA,OAAA,EAAS,QAAQ,CAAA,CAAA;AAAA,KAC1C,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,UAAW,CAAA,GAAA,EAAa,MAAgB,EAAA;AAC5C,IAAA,MAAM,IAAK,CAAA,SAAA,CAAU,UAAW,CAAA,CAAA,EAAG,GAAG,CAAe,aAAA,CAAA,CAAA,CAAA;AACrD,IAAA,IAAA,CAAK,QAAQ,MAAS,GAAA,MAAA,CAAA;AAAA,GACxB;AAAA,EAEA,IAAO,GAAA;AACL,IAAA,IAAI,OAAO,IAAK,CAAA,IAAA,CAAK,aAAa,CAAA,CAAE,SAAS,CAAG,EAAA;AAC9C,MAAM,MAAA,IAAI,MAAM,+BAA+B,CAAA,CAAA;AAAA,KACjD;AACA,IAAA,IAAA,CAAK,OAAO,IAAK,EAAA,CAAA;AAGjB,IAAK,IAAA,CAAA,OAAA,CAAQ,QAAQ,KAAM,EAAA,CAAA;AAAA,GAC7B;AAAA,EAEA,IAAI,GAAM,GAAA;AACR,IAAO,OAAA,IAAA,CAAK,OAAO,QAAS,CAAA,UAAA,CAAA;AAAA,GAC9B;AACF;;;;"}
1
+ {"version":3,"file":"setup.cjs.js","sources":["../../src/proxy/setup.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as mockttp from 'mockttp';\nimport { OpenApiProxyValidator } from '../schema/validation';\nimport getPort from 'get-port';\nimport { Server } from 'http';\n\nexport class Proxy {\n server: mockttp.Mockttp;\n #openRequests: Record<string, mockttp.CompletedRequest> = {};\n requestResponsePairs = new Map<\n mockttp.CompletedRequest,\n mockttp.CompletedResponse\n >();\n validator: OpenApiProxyValidator;\n public forwardTo: { port: number } = { port: 0 };\n express: { server: Server | undefined } = { server: undefined };\n constructor() {\n this.server = mockttp.getLocal();\n this.validator = new OpenApiProxyValidator();\n }\n\n async setup() {\n await this.server.start();\n this.forwardTo.port = await getPort();\n this.server\n .forAnyRequest()\n .thenForwardTo(`http://localhost:${this.forwardTo.port}`);\n await this.server.on('request', request => {\n this.#openRequests[request.id] = request;\n });\n await this.server.on('response', response => {\n const request = this.#openRequests[response.id];\n if (request) {\n this.requestResponsePairs.set(request, response);\n }\n delete this.#openRequests[response.id];\n this.validator.validate(request, response);\n });\n }\n\n async initialize(url: string, server: Server) {\n await this.validator.initialize(`${url}/openapi.json`);\n this.express.server = server;\n }\n\n stop() {\n if (Object.keys(this.#openRequests).length > 0) {\n throw new Error('There are still open requests');\n }\n this.server.stop();\n\n // If this isn't expressly closed, it will cause a jest memory leak warning.\n this.express.server?.close();\n }\n\n get url() {\n return this.server.proxyEnv.HTTP_PROXY;\n }\n}\n"],"names":["mockttp","OpenApiProxyValidator","getPort"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBO,MAAM,KAAM,CAAA;AAAA,EACjB,MAAA;AAAA,EACA,gBAA0D,EAAC;AAAA,EAC3D,oBAAA,uBAA2B,GAGzB,EAAA;AAAA,EACF,SAAA;AAAA,EACO,SAAA,GAA8B,EAAE,IAAA,EAAM,CAAE,EAAA;AAAA,EAC/C,OAAA,GAA0C,EAAE,MAAA,EAAQ,KAAU,CAAA,EAAA;AAAA,EAC9D,WAAc,GAAA;AACZ,IAAK,IAAA,CAAA,MAAA,GAASA,mBAAQ,QAAS,EAAA;AAC/B,IAAK,IAAA,CAAA,SAAA,GAAY,IAAIC,gCAAsB,EAAA;AAAA;AAC7C,EAEA,MAAM,KAAQ,GAAA;AACZ,IAAM,MAAA,IAAA,CAAK,OAAO,KAAM,EAAA;AACxB,IAAK,IAAA,CAAA,SAAA,CAAU,IAAO,GAAA,MAAMC,wBAAQ,EAAA;AACpC,IAAK,IAAA,CAAA,MAAA,CACF,eACA,CAAA,aAAA,CAAc,oBAAoB,IAAK,CAAA,SAAA,CAAU,IAAI,CAAE,CAAA,CAAA;AAC1D,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,EAAG,CAAA,SAAA,EAAW,CAAW,OAAA,KAAA;AACzC,MAAK,IAAA,CAAA,aAAA,CAAc,OAAQ,CAAA,EAAE,CAAI,GAAA,OAAA;AAAA,KAClC,CAAA;AACD,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,EAAG,CAAA,UAAA,EAAY,CAAY,QAAA,KAAA;AAC3C,MAAA,MAAM,OAAU,GAAA,IAAA,CAAK,aAAc,CAAA,QAAA,CAAS,EAAE,CAAA;AAC9C,MAAA,IAAI,OAAS,EAAA;AACX,QAAK,IAAA,CAAA,oBAAA,CAAqB,GAAI,CAAA,OAAA,EAAS,QAAQ,CAAA;AAAA;AAEjD,MAAO,OAAA,IAAA,CAAK,aAAc,CAAA,QAAA,CAAS,EAAE,CAAA;AACrC,MAAK,IAAA,CAAA,SAAA,CAAU,QAAS,CAAA,OAAA,EAAS,QAAQ,CAAA;AAAA,KAC1C,CAAA;AAAA;AACH,EAEA,MAAM,UAAW,CAAA,GAAA,EAAa,MAAgB,EAAA;AAC5C,IAAA,MAAM,IAAK,CAAA,SAAA,CAAU,UAAW,CAAA,CAAA,EAAG,GAAG,CAAe,aAAA,CAAA,CAAA;AACrD,IAAA,IAAA,CAAK,QAAQ,MAAS,GAAA,MAAA;AAAA;AACxB,EAEA,IAAO,GAAA;AACL,IAAA,IAAI,OAAO,IAAK,CAAA,IAAA,CAAK,aAAa,CAAA,CAAE,SAAS,CAAG,EAAA;AAC9C,MAAM,MAAA,IAAI,MAAM,+BAA+B,CAAA;AAAA;AAEjD,IAAA,IAAA,CAAK,OAAO,IAAK,EAAA;AAGjB,IAAK,IAAA,CAAA,OAAA,CAAQ,QAAQ,KAAM,EAAA;AAAA;AAC7B,EAEA,IAAI,GAAM,GAAA;AACR,IAAO,OAAA,IAAA,CAAK,OAAO,QAAS,CAAA,UAAA;AAAA;AAEhC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs.js","sources":["../../src/schema/errors.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Operation } from './types';\nimport { ErrorObject } from 'ajv';\nimport { humanifyAjvError } from './utils';\n\nexport class OperationError extends Error {\n constructor(operation: Operation, message: string) {\n super(\n `[\"${operation.method.toLocaleUpperCase('en-US')} ${\n operation.path\n }\"] ${message}`,\n );\n }\n}\n\nexport class OperationResponseError extends Error {\n constructor(operation: Operation, response: Response, message: string) {\n super(\n `[\"${operation.method.toLocaleUpperCase('en-US')} ${operation.path}\" (${\n response.status\n })]: ${message}`,\n );\n }\n}\n\nexport class OperationParsingError extends OperationError {\n constructor(operation: Operation, type: string, errors: ErrorObject[]) {\n super(\n operation,\n `${type} validation failed.\\n - ${errors\n .map(humanifyAjvError)\n .join('\\n - ')}`,\n );\n }\n}\n\nexport class OperationParsingResponseError extends OperationResponseError {\n constructor(\n operation: Operation,\n response: Response,\n type: string,\n errors: ErrorObject[],\n ) {\n super(\n operation,\n response,\n `${type} validation failed.\\n - ${errors\n .map(humanifyAjvError)\n .join('\\n - ')}`,\n );\n }\n}\n"],"names":["humanifyAjvError"],"mappings":";;;;AAoBO,MAAM,uBAAuB,KAAM,CAAA;AAAA,EACxC,WAAA,CAAY,WAAsB,OAAiB,EAAA;AACjD,IAAA,KAAA;AAAA,MACE,CAAA,EAAA,EAAK,SAAU,CAAA,MAAA,CAAO,iBAAkB,CAAA,OAAO,CAAC,CAC9C,CAAA,EAAA,SAAA,CAAU,IACZ,CAAA,GAAA,EAAM,OAAO,CAAA,CAAA;AAAA,KACf,CAAA;AAAA,GACF;AACF,CAAA;AAEO,MAAM,+BAA+B,KAAM,CAAA;AAAA,EAChD,WAAA,CAAY,SAAsB,EAAA,QAAA,EAAoB,OAAiB,EAAA;AACrE,IAAA,KAAA;AAAA,MACE,CAAK,EAAA,EAAA,SAAA,CAAU,MAAO,CAAA,iBAAA,CAAkB,OAAO,CAAC,CAAI,CAAA,EAAA,SAAA,CAAU,IAAI,CAAA,GAAA,EAChE,QAAS,CAAA,MACX,OAAO,OAAO,CAAA,CAAA;AAAA,KAChB,CAAA;AAAA,GACF;AACF,CAAA;AAEO,MAAM,8BAA8B,cAAe,CAAA;AAAA,EACxD,WAAA,CAAY,SAAsB,EAAA,IAAA,EAAc,MAAuB,EAAA;AACrE,IAAA,KAAA;AAAA,MACE,SAAA;AAAA,MACA,GAAG,IAAI,CAAA;AAAA,GAAA,EAA2B,OAC/B,GAAI,CAAAA,sBAAgB,CACpB,CAAA,IAAA,CAAK,OAAO,CAAC,CAAA,CAAA;AAAA,KAClB,CAAA;AAAA,GACF;AACF,CAAA;AAEO,MAAM,sCAAsC,sBAAuB,CAAA;AAAA,EACxE,WACE,CAAA,SAAA,EACA,QACA,EAAA,IAAA,EACA,MACA,EAAA;AACA,IAAA,KAAA;AAAA,MACE,SAAA;AAAA,MACA,QAAA;AAAA,MACA,GAAG,IAAI,CAAA;AAAA,GAAA,EAA2B,OAC/B,GAAI,CAAAA,sBAAgB,CACpB,CAAA,IAAA,CAAK,OAAO,CAAC,CAAA,CAAA;AAAA,KAClB,CAAA;AAAA,GACF;AACF;;;;;;;"}
1
+ {"version":3,"file":"errors.cjs.js","sources":["../../src/schema/errors.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Operation } from './types';\nimport { ErrorObject } from 'ajv';\nimport { humanifyAjvError } from './utils';\n\nexport class OperationError extends Error {\n constructor(operation: Operation, message: string) {\n super(\n `[\"${operation.method.toLocaleUpperCase('en-US')} ${\n operation.path\n }\"] ${message}`,\n );\n }\n}\n\nexport class OperationResponseError extends Error {\n constructor(operation: Operation, response: Response, message: string) {\n super(\n `[\"${operation.method.toLocaleUpperCase('en-US')} ${operation.path}\" (${\n response.status\n })]: ${message}`,\n );\n }\n}\n\nexport class OperationParsingError extends OperationError {\n constructor(operation: Operation, type: string, errors: ErrorObject[]) {\n super(\n operation,\n `${type} validation failed.\\n - ${errors\n .map(humanifyAjvError)\n .join('\\n - ')}`,\n );\n }\n}\n\nexport class OperationParsingResponseError extends OperationResponseError {\n constructor(\n operation: Operation,\n response: Response,\n type: string,\n errors: ErrorObject[],\n ) {\n super(\n operation,\n response,\n `${type} validation failed.\\n - ${errors\n .map(humanifyAjvError)\n .join('\\n - ')}`,\n );\n }\n}\n"],"names":["humanifyAjvError"],"mappings":";;;;AAoBO,MAAM,uBAAuB,KAAM,CAAA;AAAA,EACxC,WAAA,CAAY,WAAsB,OAAiB,EAAA;AACjD,IAAA,KAAA;AAAA,MACE,CAAA,EAAA,EAAK,SAAU,CAAA,MAAA,CAAO,iBAAkB,CAAA,OAAO,CAAC,CAC9C,CAAA,EAAA,SAAA,CAAU,IACZ,CAAA,GAAA,EAAM,OAAO,CAAA;AAAA,KACf;AAAA;AAEJ;AAEO,MAAM,+BAA+B,KAAM,CAAA;AAAA,EAChD,WAAA,CAAY,SAAsB,EAAA,QAAA,EAAoB,OAAiB,EAAA;AACrE,IAAA,KAAA;AAAA,MACE,CAAK,EAAA,EAAA,SAAA,CAAU,MAAO,CAAA,iBAAA,CAAkB,OAAO,CAAC,CAAI,CAAA,EAAA,SAAA,CAAU,IAAI,CAAA,GAAA,EAChE,QAAS,CAAA,MACX,OAAO,OAAO,CAAA;AAAA,KAChB;AAAA;AAEJ;AAEO,MAAM,8BAA8B,cAAe,CAAA;AAAA,EACxD,WAAA,CAAY,SAAsB,EAAA,IAAA,EAAc,MAAuB,EAAA;AACrE,IAAA,KAAA;AAAA,MACE,SAAA;AAAA,MACA,GAAG,IAAI,CAAA;AAAA,GAAA,EAA2B,OAC/B,GAAI,CAAAA,sBAAgB,CACpB,CAAA,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA,KAClB;AAAA;AAEJ;AAEO,MAAM,sCAAsC,sBAAuB,CAAA;AAAA,EACxE,WACE,CAAA,SAAA,EACA,QACA,EAAA,IAAA,EACA,MACA,EAAA;AACA,IAAA,KAAA;AAAA,MACE,SAAA;AAAA,MACA,QAAA;AAAA,MACA,GAAG,IAAI,CAAA;AAAA,GAAA,EAA2B,OAC/B,GAAI,CAAAA,sBAAgB,CACpB,CAAA,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA,KAClB;AAAA;AAEJ;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"parameter-validation.cjs.js","sources":["../../src/schema/parameter-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { OpenAPIObject, ParameterObject, SchemaObject } from 'openapi3-ts';\nimport {\n Operation,\n ParserOptions,\n RequestParser,\n Validator,\n ValidatorParams,\n} from './types';\nimport Ajv from 'ajv';\nimport { OperationError, OperationParsingError } from './errors';\nimport { mockttpToFetchRequest } from './utils';\n\ntype ReferencelessSchemaObject = SchemaObject & { $ref?: never };\n\ntype ReferencelessParameterObject = Omit<ParameterObject, 'schema'> & {\n schema: ReferencelessSchemaObject;\n};\n\nclass BaseParameterParser {\n ajv: Ajv;\n operation: Operation;\n parameters: Record<string, ReferencelessParameterObject> = {};\n constructor(\n parameterIn: string,\n operation: Operation,\n options: ParserOptions,\n ) {\n this.ajv = options.ajv;\n this.operation = operation;\n const { schema, path, method } = operation;\n const parameters = schema.parameters || [];\n for (const parameter of parameters) {\n if ('$ref' in parameter) {\n throw new Error(\n `[(${method}) ${path}] Reference objects are not supported`,\n );\n }\n\n if (!parameter.schema) {\n throw new OperationError(\n operation,\n 'Schema not found for path parameter',\n );\n }\n if ('$ref' in parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported for parameters',\n );\n }\n if (parameter.in === parameterIn) {\n this.parameters[parameter.name] =\n parameter as ReferencelessParameterObject;\n }\n }\n }\n\n /**\n * Attempt to transform a string value to its expected type, this allows Ajv to perform validation. As these are parameters,\n * support for edge cases like nested type casting is not currently supported.\n * @param value\n * @param schema\n * @returns\n */\n optimisticallyParseValue(value: string, schema: SchemaObject) {\n if (schema.type === 'integer') {\n return parseInt(value, 10);\n }\n if (schema.type === 'number') {\n return parseFloat(value);\n }\n if (schema.type === 'boolean') {\n if (['true', 'false'].includes(value)) {\n return value === 'true';\n }\n throw new Error('Invalid boolean value must be either \"true\" or \"false\"');\n }\n return value;\n }\n}\n\nexport class QueryParameterParser\n extends BaseParameterParser\n implements RequestParser<Record<string, any>>\n{\n constructor(operation: Operation, options: ParserOptions) {\n super('query', operation, options);\n }\n async parse(request: Request) {\n const { searchParams } = new URL(request.url);\n const remainingQueryParameters = new Set<string>(searchParams.keys());\n const queryParameters: Record<string, any> = {};\n\n let parameterIterator = Object.entries(this.parameters);\n\n const isFormExplode = (parameter: ReferencelessParameterObject) => {\n return (\n parameter.schema?.type === 'object' &&\n (parameter.style === 'form' || !parameter.style) &&\n parameter.explode\n );\n };\n\n const regularParameters = parameterIterator.filter(\n ([_, parameter]) => !isFormExplode(parameter),\n );\n\n const formExplodeParameters = parameterIterator.filter(([_, parameter]) =>\n isFormExplode(parameter),\n );\n\n if (formExplodeParameters.length > 1) {\n throw new OperationError(\n this.operation,\n 'Ambiguous query parameters, you cannot have 2 form explode parameters',\n );\n }\n\n // Sort the parameters so that form explode parameters are processed last.\n parameterIterator = [...regularParameters, ...formExplodeParameters];\n\n for (const [name, parameter] of parameterIterator) {\n if (!parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Schema not found for query parameter',\n );\n }\n if ('$ref' in parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported for parameters',\n );\n }\n // eslint-disable-next-line prefer-const\n let [param, indices]: [any | null, string[]] = this.#findQueryParameters(\n this.parameters,\n remainingQueryParameters,\n searchParams,\n name,\n );\n if (!!param) {\n indices.forEach(index => remainingQueryParameters.delete(index));\n }\n\n // The query parameters can be either a single value or an array of values, try to wrangle them into the expected format if they're not explicitly an array.\n if (parameter.schema.type !== 'array' && Array.isArray(param)) {\n param = param.length > 0 ? param[0] : undefined;\n }\n if (\n parameter.required &&\n !indices.some(index => searchParams.has(index))\n ) {\n throw new OperationError(\n this.operation,\n `Required query parameter ${name} not found`,\n );\n } else if (!param && !parameter.required) {\n continue;\n }\n if (param) {\n // We do this here because all query parameters are strings but the schema will expect the real value.\n param = this.optimisticallyParseValue(param, parameter.schema);\n }\n const validate = this.ajv.compile(parameter.schema);\n const valid = validate(param);\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n 'Query parameter',\n validate.errors!,\n );\n }\n queryParameters[name] = param;\n }\n if (remainingQueryParameters.size > 0) {\n throw new OperationError(\n this.operation,\n `Unexpected query parameters: ${Array.from(\n remainingQueryParameters,\n ).join(', ')}`,\n );\n }\n return queryParameters;\n }\n\n #findQueryParameters(\n parameters: Record<string, ParameterObject>,\n remainingQueryParameters: Set<string>,\n searchParams: URLSearchParams,\n name: string,\n ): [any | null, string[]] {\n const parameter = parameters[name];\n const schema = parameter.schema as SchemaObject;\n\n // Since getAll will return an empty array if the key is not found, we need to check if the key exists first.\n const getIfExists = (key: string) =>\n searchParams.has(key) ? searchParams.getAll(key) : null;\n\n if (schema.type === 'array') {\n // Form is the default array format.\n if (\n parameter.style === 'form' ||\n typeof parameter.style === 'undefined'\n ) {\n // As is explode = true.\n if (parameter.explode || typeof parameter.explode === 'undefined') {\n // Support for qs explode format. Every value is stored as a separate query parameter.\n if (!searchParams.has(name) && searchParams.has(`${name}[0]`)) {\n const values: string[] = [];\n const indices: string[] = [];\n let index = 0;\n while (searchParams.has(`${name}[${index}]`)) {\n values.push(searchParams.get(`${name}[${index}]`)!);\n indices.push(`${name}[${index}]`);\n index++;\n }\n return [values, indices];\n }\n // If not qs format, grab all values with the same name from search params.\n return [getIfExists(name), [name]];\n }\n // Add support for qs non-standard array format. This is helpful for search-backend, since that uses qs still.\n if (!searchParams.has(name) && searchParams.has(`${name}[]`)) {\n return [searchParams.get(`${name}[]`)?.split(','), [`${name}[]`]];\n }\n // Non-explode arrays should be comma separated.\n if (searchParams.has(name) && searchParams.getAll(name).length > 1) {\n throw new OperationError(\n this.operation,\n 'Arrays must be comma separated in non-explode mode',\n );\n }\n return [searchParams.get(name)?.split(','), [name]];\n } else if (parameter.style === 'spaceDelimited') {\n return [searchParams.get(name)?.split(' '), [name]];\n } else if (parameter.style === 'pipeDelimited') {\n return [searchParams.get(name)?.split('|'), [name]];\n }\n throw new OperationError(\n this.operation,\n 'Unsupported style for array parameter',\n );\n }\n if (schema.type === 'object') {\n // Form is the default object format.\n if (\n parameter.style === 'form' ||\n typeof parameter.style === 'undefined'\n ) {\n if (parameter.explode) {\n // Object form/explode is a collection of disjoint keys, there's no mapping for what they are so we collect all of them.\n // This means we need to run this as the last query parameter that is processed.\n const obj: Record<string, string> = {};\n const indices: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n // Have we processed this query parameter as part of another parameter parsing? If not, consider it to be a part of this object.\n if (!remainingQueryParameters.has(key)) {\n continue;\n }\n indices.push(key);\n obj[key] = value;\n }\n return [obj, indices];\n }\n // For non-explode, the schema is comma separated key,value \"pairs\", so filter=key1,value1,key2,value2 would parse to {key1: value1, key2: value2}.\n const obj: Record<string, string> = {};\n const value = searchParams.get(name);\n if (value) {\n const parts = value.split(',');\n if (parts.length % 2 !== 0) {\n throw new OperationError(\n this.operation,\n 'Invalid object query parameter, must have an even number of key-value pairs',\n );\n }\n for (let i = 0; i < parts.length; i += 2) {\n obj[parts[i]] = parts[i + 1];\n }\n }\n return [obj, [name]];\n } else if (parameter.style === 'deepObject') {\n // Deep object is a nested object structure, so we need to parse the keys to build the object.\n // example: ?filter[key1]=value1&filter[key2]=value2 => { key1: value1, key2: value2 }\n const obj: Record<string, any> = {};\n const indices: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n if (key.startsWith(`${name}[`)) {\n indices.push(key);\n const parts = key.split('[');\n let currentLayer = obj;\n for (let partIndex = 1; partIndex < parts.length - 1; partIndex++) {\n const part = parts[partIndex];\n if (!part.includes(']')) {\n throw new OperationError(\n this.operation,\n `Invalid object parameter, missing closing bracket for key \"${key}\"`,\n );\n }\n const objKey = part.split(']')[0];\n if (!currentLayer[objKey]) {\n currentLayer[objKey] = {};\n }\n currentLayer = currentLayer[objKey];\n }\n const lastPart = parts[parts.length - 1];\n if (!lastPart.includes(']')) {\n throw new OperationError(\n this.operation,\n `Invalid object parameter, missing closing bracket for key \"${key}\"`,\n );\n }\n currentLayer[lastPart.split(']')[0]] = value;\n }\n }\n return [obj, indices];\n }\n throw new OperationError(\n this.operation,\n `Unsupported style for object parameter, \"${parameter.style}\"`,\n );\n }\n // For everything else, just return the value.\n return [getIfExists(name), [name]];\n }\n}\n\nexport class HeaderParameterParser\n extends BaseParameterParser\n implements RequestParser<Record<string, any>>\n{\n constructor(operation: Operation, options: ParserOptions) {\n super('header', operation, options);\n }\n async parse(request: Request) {\n const headerParameters: Record<string, any> = {};\n for (const [name, parameter] of Object.entries(this.parameters)) {\n const header = request.headers.get(name);\n if (!header) {\n if (parameter.required) {\n throw new OperationError(\n this.operation,\n `Header parameter ${name} not found`,\n );\n }\n continue;\n }\n if (!parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Schema not found for header parameter',\n );\n }\n if ('$ref' in parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported for parameters',\n );\n }\n const validate = this.ajv.compile(parameter.schema);\n const valid = validate(header);\n\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n 'Header parameter',\n validate.errors!,\n );\n }\n headerParameters[name] = header;\n }\n return headerParameters;\n }\n}\n\nexport class PathParameterParser\n extends BaseParameterParser\n implements RequestParser<Record<string, any>>\n{\n constructor(operation: Operation, options: ParserOptions) {\n super('path', operation, options);\n }\n async parse(request: Request) {\n const { pathname } = new URL(request.url);\n const params = PathParameterParser.parsePath({\n operation: this.operation,\n path: pathname,\n schema: this.operation.path,\n });\n const pathParameters: Record<string, any> = {};\n for (const [name, parameter] of Object.entries(this.parameters)) {\n let param: string | number | boolean = params[name];\n if (!param && parameter.required) {\n throw new OperationError(\n this.operation,\n `Path parameter ${name} not found`,\n );\n } else if (!params[name] && !parameter.required) {\n continue;\n }\n\n if (param) {\n param = this.optimisticallyParseValue(param, parameter.schema);\n }\n\n const validate = this.ajv.compile(parameter.schema);\n const valid = validate(param);\n\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n 'Path parameter',\n validate.errors!,\n );\n }\n pathParameters[name] = param;\n }\n return pathParameters;\n }\n\n static parsePath({\n operation,\n schema,\n path,\n }: {\n operation: Operation;\n schema: string;\n path: string;\n }) {\n const parts = path.split('/');\n const pathParts = schema.split('/');\n if (parts.length !== pathParts.length) {\n throw new OperationError(operation, 'Path parts do not match');\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < parts.length; i++) {\n if (pathParts[i] === parts[i]) {\n continue;\n }\n if (pathParts[i].startsWith('{') && pathParts[i].endsWith('}')) {\n params[pathParts[i].slice(1, -1)] = parts[i];\n continue;\n }\n break;\n }\n return params;\n }\n}\n\nexport class ParameterValidator implements Validator {\n schema: OpenAPIObject;\n cache: Record<string, any> = {};\n constructor(schema: OpenAPIObject) {\n this.schema = schema;\n }\n\n async validate({ pair: { request, response }, operation }: ValidatorParams) {\n if (response.statusCode === 400) {\n // If the response is a 400, then the request is invalid and we shouldn't validate the parameters\n return;\n }\n\n const ajv = new Ajv();\n const queryParser = new QueryParameterParser(operation, { ajv });\n const headerParser = new HeaderParameterParser(operation, { ajv });\n const pathParser = new PathParameterParser(operation, { ajv });\n\n const fetchRequest = mockttpToFetchRequest(request);\n\n await Promise.all([\n queryParser.parse(fetchRequest),\n headerParser.parse(fetchRequest),\n pathParser.parse(fetchRequest),\n ]);\n }\n}\n"],"names":["OperationError","OperationParsingError","obj","value","Ajv","mockttpToFetchRequest"],"mappings":"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"}
1
+ {"version":3,"file":"parameter-validation.cjs.js","sources":["../../src/schema/parameter-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { OpenAPIObject, ParameterObject, SchemaObject } from 'openapi3-ts';\nimport {\n Operation,\n ParserOptions,\n RequestParser,\n Validator,\n ValidatorParams,\n} from './types';\nimport Ajv from 'ajv';\nimport { OperationError, OperationParsingError } from './errors';\nimport { mockttpToFetchRequest } from './utils';\n\ntype ReferencelessSchemaObject = SchemaObject & { $ref?: never };\n\ntype ReferencelessParameterObject = Omit<ParameterObject, 'schema'> & {\n schema: ReferencelessSchemaObject;\n};\n\nclass BaseParameterParser {\n ajv: Ajv;\n operation: Operation;\n parameters: Record<string, ReferencelessParameterObject> = {};\n constructor(\n parameterIn: string,\n operation: Operation,\n options: ParserOptions,\n ) {\n this.ajv = options.ajv;\n this.operation = operation;\n const { schema, path, method } = operation;\n const parameters = schema.parameters || [];\n for (const parameter of parameters) {\n if ('$ref' in parameter) {\n throw new Error(\n `[(${method}) ${path}] Reference objects are not supported`,\n );\n }\n\n if (!parameter.schema) {\n throw new OperationError(\n operation,\n 'Schema not found for path parameter',\n );\n }\n if ('$ref' in parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported for parameters',\n );\n }\n if (parameter.in === parameterIn) {\n this.parameters[parameter.name] =\n parameter as ReferencelessParameterObject;\n }\n }\n }\n\n /**\n * Attempt to transform a string value to its expected type, this allows Ajv to perform validation. As these are parameters,\n * support for edge cases like nested type casting is not currently supported.\n * @param value\n * @param schema\n * @returns\n */\n optimisticallyParseValue(value: string, schema: SchemaObject) {\n if (schema.type === 'integer') {\n return parseInt(value, 10);\n }\n if (schema.type === 'number') {\n return parseFloat(value);\n }\n if (schema.type === 'boolean') {\n if (['true', 'false'].includes(value)) {\n return value === 'true';\n }\n throw new Error('Invalid boolean value must be either \"true\" or \"false\"');\n }\n return value;\n }\n}\n\nexport class QueryParameterParser\n extends BaseParameterParser\n implements RequestParser<Record<string, any>>\n{\n constructor(operation: Operation, options: ParserOptions) {\n super('query', operation, options);\n }\n async parse(request: Request) {\n const { searchParams } = new URL(request.url);\n const remainingQueryParameters = new Set<string>(searchParams.keys());\n const queryParameters: Record<string, any> = {};\n\n let parameterIterator = Object.entries(this.parameters);\n\n const isFormExplode = (parameter: ReferencelessParameterObject) => {\n return (\n parameter.schema?.type === 'object' &&\n (parameter.style === 'form' || !parameter.style) &&\n parameter.explode\n );\n };\n\n const regularParameters = parameterIterator.filter(\n ([_, parameter]) => !isFormExplode(parameter),\n );\n\n const formExplodeParameters = parameterIterator.filter(([_, parameter]) =>\n isFormExplode(parameter),\n );\n\n if (formExplodeParameters.length > 1) {\n throw new OperationError(\n this.operation,\n 'Ambiguous query parameters, you cannot have 2 form explode parameters',\n );\n }\n\n // Sort the parameters so that form explode parameters are processed last.\n parameterIterator = [...regularParameters, ...formExplodeParameters];\n\n for (const [name, parameter] of parameterIterator) {\n if (!parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Schema not found for query parameter',\n );\n }\n if ('$ref' in parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported for parameters',\n );\n }\n // eslint-disable-next-line prefer-const\n let [param, indices]: [any | null, string[]] = this.#findQueryParameters(\n this.parameters,\n remainingQueryParameters,\n searchParams,\n name,\n );\n if (!!param) {\n indices.forEach(index => remainingQueryParameters.delete(index));\n }\n\n // The query parameters can be either a single value or an array of values, try to wrangle them into the expected format if they're not explicitly an array.\n if (parameter.schema.type !== 'array' && Array.isArray(param)) {\n param = param.length > 0 ? param[0] : undefined;\n }\n if (\n parameter.required &&\n !indices.some(index => searchParams.has(index))\n ) {\n throw new OperationError(\n this.operation,\n `Required query parameter ${name} not found`,\n );\n } else if (!param && !parameter.required) {\n continue;\n }\n if (param) {\n // We do this here because all query parameters are strings but the schema will expect the real value.\n param = this.optimisticallyParseValue(param, parameter.schema);\n }\n const validate = this.ajv.compile(parameter.schema);\n const valid = validate(param);\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n 'Query parameter',\n validate.errors!,\n );\n }\n queryParameters[name] = param;\n }\n if (remainingQueryParameters.size > 0) {\n throw new OperationError(\n this.operation,\n `Unexpected query parameters: ${Array.from(\n remainingQueryParameters,\n ).join(', ')}`,\n );\n }\n return queryParameters;\n }\n\n #findQueryParameters(\n parameters: Record<string, ParameterObject>,\n remainingQueryParameters: Set<string>,\n searchParams: URLSearchParams,\n name: string,\n ): [any | null, string[]] {\n const parameter = parameters[name];\n const schema = parameter.schema as SchemaObject;\n\n // Since getAll will return an empty array if the key is not found, we need to check if the key exists first.\n const getIfExists = (key: string) =>\n searchParams.has(key) ? searchParams.getAll(key) : null;\n\n if (schema.type === 'array') {\n // Form is the default array format.\n if (\n parameter.style === 'form' ||\n typeof parameter.style === 'undefined'\n ) {\n // As is explode = true.\n if (parameter.explode || typeof parameter.explode === 'undefined') {\n // Support for qs explode format. Every value is stored as a separate query parameter.\n if (!searchParams.has(name) && searchParams.has(`${name}[0]`)) {\n const values: string[] = [];\n const indices: string[] = [];\n let index = 0;\n while (searchParams.has(`${name}[${index}]`)) {\n values.push(searchParams.get(`${name}[${index}]`)!);\n indices.push(`${name}[${index}]`);\n index++;\n }\n return [values, indices];\n }\n // If not qs format, grab all values with the same name from search params.\n return [getIfExists(name), [name]];\n }\n // Add support for qs non-standard array format. This is helpful for search-backend, since that uses qs still.\n if (!searchParams.has(name) && searchParams.has(`${name}[]`)) {\n return [searchParams.get(`${name}[]`)?.split(','), [`${name}[]`]];\n }\n // Non-explode arrays should be comma separated.\n if (searchParams.has(name) && searchParams.getAll(name).length > 1) {\n throw new OperationError(\n this.operation,\n 'Arrays must be comma separated in non-explode mode',\n );\n }\n return [searchParams.get(name)?.split(','), [name]];\n } else if (parameter.style === 'spaceDelimited') {\n return [searchParams.get(name)?.split(' '), [name]];\n } else if (parameter.style === 'pipeDelimited') {\n return [searchParams.get(name)?.split('|'), [name]];\n }\n throw new OperationError(\n this.operation,\n 'Unsupported style for array parameter',\n );\n }\n if (schema.type === 'object') {\n // Form is the default object format.\n if (\n parameter.style === 'form' ||\n typeof parameter.style === 'undefined'\n ) {\n if (parameter.explode) {\n // Object form/explode is a collection of disjoint keys, there's no mapping for what they are so we collect all of them.\n // This means we need to run this as the last query parameter that is processed.\n const obj: Record<string, string> = {};\n const indices: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n // Have we processed this query parameter as part of another parameter parsing? If not, consider it to be a part of this object.\n if (!remainingQueryParameters.has(key)) {\n continue;\n }\n indices.push(key);\n obj[key] = value;\n }\n return [obj, indices];\n }\n // For non-explode, the schema is comma separated key,value \"pairs\", so filter=key1,value1,key2,value2 would parse to {key1: value1, key2: value2}.\n const obj: Record<string, string> = {};\n const value = searchParams.get(name);\n if (value) {\n const parts = value.split(',');\n if (parts.length % 2 !== 0) {\n throw new OperationError(\n this.operation,\n 'Invalid object query parameter, must have an even number of key-value pairs',\n );\n }\n for (let i = 0; i < parts.length; i += 2) {\n obj[parts[i]] = parts[i + 1];\n }\n }\n return [obj, [name]];\n } else if (parameter.style === 'deepObject') {\n // Deep object is a nested object structure, so we need to parse the keys to build the object.\n // example: ?filter[key1]=value1&filter[key2]=value2 => { key1: value1, key2: value2 }\n const obj: Record<string, any> = {};\n const indices: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n if (key.startsWith(`${name}[`)) {\n indices.push(key);\n const parts = key.split('[');\n let currentLayer = obj;\n for (let partIndex = 1; partIndex < parts.length - 1; partIndex++) {\n const part = parts[partIndex];\n if (!part.includes(']')) {\n throw new OperationError(\n this.operation,\n `Invalid object parameter, missing closing bracket for key \"${key}\"`,\n );\n }\n const objKey = part.split(']')[0];\n if (!currentLayer[objKey]) {\n currentLayer[objKey] = {};\n }\n currentLayer = currentLayer[objKey];\n }\n const lastPart = parts[parts.length - 1];\n if (!lastPart.includes(']')) {\n throw new OperationError(\n this.operation,\n `Invalid object parameter, missing closing bracket for key \"${key}\"`,\n );\n }\n currentLayer[lastPart.split(']')[0]] = value;\n }\n }\n return [obj, indices];\n }\n throw new OperationError(\n this.operation,\n `Unsupported style for object parameter, \"${parameter.style}\"`,\n );\n }\n // For everything else, just return the value.\n return [getIfExists(name), [name]];\n }\n}\n\nexport class HeaderParameterParser\n extends BaseParameterParser\n implements RequestParser<Record<string, any>>\n{\n constructor(operation: Operation, options: ParserOptions) {\n super('header', operation, options);\n }\n async parse(request: Request) {\n const headerParameters: Record<string, any> = {};\n for (const [name, parameter] of Object.entries(this.parameters)) {\n const header = request.headers.get(name);\n if (!header) {\n if (parameter.required) {\n throw new OperationError(\n this.operation,\n `Header parameter ${name} not found`,\n );\n }\n continue;\n }\n if (!parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Schema not found for header parameter',\n );\n }\n if ('$ref' in parameter.schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported for parameters',\n );\n }\n const validate = this.ajv.compile(parameter.schema);\n const valid = validate(header);\n\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n 'Header parameter',\n validate.errors!,\n );\n }\n headerParameters[name] = header;\n }\n return headerParameters;\n }\n}\n\nexport class PathParameterParser\n extends BaseParameterParser\n implements RequestParser<Record<string, any>>\n{\n constructor(operation: Operation, options: ParserOptions) {\n super('path', operation, options);\n }\n async parse(request: Request) {\n const { pathname } = new URL(request.url);\n const params = PathParameterParser.parsePath({\n operation: this.operation,\n path: pathname,\n schema: this.operation.path,\n });\n const pathParameters: Record<string, any> = {};\n for (const [name, parameter] of Object.entries(this.parameters)) {\n let param: string | number | boolean = params[name];\n if (!param && parameter.required) {\n throw new OperationError(\n this.operation,\n `Path parameter ${name} not found`,\n );\n } else if (!params[name] && !parameter.required) {\n continue;\n }\n\n if (param) {\n param = this.optimisticallyParseValue(param, parameter.schema);\n }\n\n const validate = this.ajv.compile(parameter.schema);\n const valid = validate(param);\n\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n 'Path parameter',\n validate.errors!,\n );\n }\n pathParameters[name] = param;\n }\n return pathParameters;\n }\n\n static parsePath({\n operation,\n schema,\n path,\n }: {\n operation: Operation;\n schema: string;\n path: string;\n }) {\n const parts = path.split('/');\n const pathParts = schema.split('/');\n if (parts.length !== pathParts.length) {\n throw new OperationError(operation, 'Path parts do not match');\n }\n const params: Record<string, string> = {};\n for (let i = 0; i < parts.length; i++) {\n if (pathParts[i] === parts[i]) {\n continue;\n }\n if (pathParts[i].startsWith('{') && pathParts[i].endsWith('}')) {\n params[pathParts[i].slice(1, -1)] = parts[i];\n continue;\n }\n break;\n }\n return params;\n }\n}\n\nexport class ParameterValidator implements Validator {\n schema: OpenAPIObject;\n cache: Record<string, any> = {};\n constructor(schema: OpenAPIObject) {\n this.schema = schema;\n }\n\n async validate({ pair: { request, response }, operation }: ValidatorParams) {\n if (response.statusCode === 400) {\n // If the response is a 400, then the request is invalid and we shouldn't validate the parameters\n return;\n }\n\n const ajv = new Ajv();\n const queryParser = new QueryParameterParser(operation, { ajv });\n const headerParser = new HeaderParameterParser(operation, { ajv });\n const pathParser = new PathParameterParser(operation, { ajv });\n\n const fetchRequest = mockttpToFetchRequest(request);\n\n await Promise.all([\n queryParser.parse(fetchRequest),\n headerParser.parse(fetchRequest),\n pathParser.parse(fetchRequest),\n ]);\n }\n}\n"],"names":["OperationError","OperationParsingError","obj","value","Ajv","mockttpToFetchRequest"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"request-body-validation.cjs.js","sources":["../../src/schema/request-body-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { Operation, ParserOptions, RequestParser } from './types';\nimport { ValidateFunction } from 'ajv';\nimport { OperationError, OperationParsingError } from './errors';\nimport { RequestBodyObject, SchemaObject } from 'openapi3-ts';\n\nclass DisabledRequestBodyParser\n implements RequestParser<JsonObject | undefined>\n{\n operation: Operation;\n constructor(operation: Operation) {\n this.operation = operation;\n }\n async parse(request: Request): Promise<JsonObject | undefined> {\n const bodyText = await request.text();\n if (bodyText?.length) {\n throw new OperationError(\n this.operation,\n 'Received a body but no schema was found',\n );\n }\n return undefined;\n }\n}\nexport class RequestBodyParser\n implements RequestParser<JsonObject | undefined>\n{\n operation: Operation;\n disabled: boolean = false;\n validate!: ValidateFunction;\n schema!: SchemaObject;\n requestBodySchema!: RequestBodyObject;\n\n static fromOperation(operation: Operation, options: ParserOptions) {\n return operation.schema.requestBody\n ? new RequestBodyParser(operation, options)\n : new DisabledRequestBodyParser(operation);\n }\n\n constructor(operation: Operation, options: ParserOptions) {\n this.operation = operation;\n const { schema: operationSchema } = this.operation;\n const requestBody = operationSchema.requestBody;\n\n if (!requestBody) {\n throw new OperationError(\n this.operation,\n 'No request body found in operation',\n );\n }\n\n if ('$ref' in requestBody!) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported',\n );\n }\n if (!requestBody!.content) {\n throw new OperationError(\n this.operation,\n 'No content found in request body',\n );\n }\n const contentTypes = requestBody!.content;\n const jsonContentType = Object.keys(contentTypes).find(contentType =>\n contentType.split(';').includes('application/json'),\n );\n if (!jsonContentType) {\n throw new OperationError(\n this.operation,\n 'No application/json content type found in request body',\n );\n }\n const schema = requestBody!.content[jsonContentType].schema;\n if (!schema) {\n throw new OperationError(\n this.operation,\n 'No JSON schema found in request body',\n );\n }\n if ('$ref' in schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported',\n );\n }\n this.validate = options.ajv.compile(schema);\n this.schema = schema;\n this.requestBodySchema = requestBody;\n }\n async parse(request: Request): Promise<JsonObject | undefined> {\n const bodyText = await request.text();\n if (this.requestBodySchema.required && !bodyText?.length) {\n throw new OperationError(\n this.operation,\n `No request body found for ${request.url}`,\n );\n }\n\n const contentType =\n request.headers.get('content-type') || 'application/json';\n if (!contentType.split(';').includes('application/json')) {\n throw new OperationError(\n this.operation,\n 'Content type is not application/json',\n );\n }\n const body = (await request.json()) as JsonObject;\n const valid = this.validate(body);\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n `Request body`,\n this.validate.errors!,\n );\n }\n return body;\n }\n}\n"],"names":["OperationError","OperationParsingError"],"mappings":";;;;AAsBA,MAAM,yBAEN,CAAA;AAAA,EACE,SAAA,CAAA;AAAA,EACA,YAAY,SAAsB,EAAA;AAChC,IAAA,IAAA,CAAK,SAAY,GAAA,SAAA,CAAA;AAAA,GACnB;AAAA,EACA,MAAM,MAAM,OAAmD,EAAA;AAC7D,IAAM,MAAA,QAAA,GAAW,MAAM,OAAA,CAAQ,IAAK,EAAA,CAAA;AACpC,IAAA,IAAI,UAAU,MAAQ,EAAA;AACpB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,yCAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAO,OAAA,KAAA,CAAA,CAAA;AAAA,GACT;AACF,CAAA;AACO,MAAM,iBAEb,CAAA;AAAA,EACE,SAAA,CAAA;AAAA,EACA,QAAoB,GAAA,KAAA,CAAA;AAAA,EACpB,QAAA,CAAA;AAAA,EACA,MAAA,CAAA;AAAA,EACA,iBAAA,CAAA;AAAA,EAEA,OAAO,aAAc,CAAA,SAAA,EAAsB,OAAwB,EAAA;AACjE,IAAO,OAAA,SAAA,CAAU,MAAO,CAAA,WAAA,GACpB,IAAI,iBAAA,CAAkB,WAAW,OAAO,CAAA,GACxC,IAAI,yBAAA,CAA0B,SAAS,CAAA,CAAA;AAAA,GAC7C;AAAA,EAEA,WAAA,CAAY,WAAsB,OAAwB,EAAA;AACxD,IAAA,IAAA,CAAK,SAAY,GAAA,SAAA,CAAA;AACjB,IAAA,MAAM,EAAE,MAAA,EAAQ,eAAgB,EAAA,GAAI,IAAK,CAAA,SAAA,CAAA;AACzC,IAAA,MAAM,cAAc,eAAgB,CAAA,WAAA,CAAA;AAEpC,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,oCAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,IAAI,UAAU,WAAc,EAAA;AAC1B,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,qCAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAI,IAAA,CAAC,YAAa,OAAS,EAAA;AACzB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,kCAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,MAAM,eAAe,WAAa,CAAA,OAAA,CAAA;AAClC,IAAA,MAAM,eAAkB,GAAA,MAAA,CAAO,IAAK,CAAA,YAAY,CAAE,CAAA,IAAA;AAAA,MAAK,iBACrD,WAAY,CAAA,KAAA,CAAM,GAAG,CAAA,CAAE,SAAS,kBAAkB,CAAA;AAAA,KACpD,CAAA;AACA,IAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,wDAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,MAAM,MAAS,GAAA,WAAA,CAAa,OAAQ,CAAA,eAAe,CAAE,CAAA,MAAA,CAAA;AACrD,IAAA,IAAI,CAAC,MAAQ,EAAA;AACX,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,sCAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,IAAI,UAAU,MAAQ,EAAA;AACpB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,qCAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,IAAA,CAAK,QAAW,GAAA,OAAA,CAAQ,GAAI,CAAA,OAAA,CAAQ,MAAM,CAAA,CAAA;AAC1C,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AACd,IAAA,IAAA,CAAK,iBAAoB,GAAA,WAAA,CAAA;AAAA,GAC3B;AAAA,EACA,MAAM,MAAM,OAAmD,EAAA;AAC7D,IAAM,MAAA,QAAA,GAAW,MAAM,OAAA,CAAQ,IAAK,EAAA,CAAA;AACpC,IAAA,IAAI,IAAK,CAAA,iBAAA,CAAkB,QAAY,IAAA,CAAC,UAAU,MAAQ,EAAA;AACxD,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,CAAA,0BAAA,EAA6B,QAAQ,GAAG,CAAA,CAAA;AAAA,OAC1C,CAAA;AAAA,KACF;AAEA,IAAA,MAAM,WACJ,GAAA,OAAA,CAAQ,OAAQ,CAAA,GAAA,CAAI,cAAc,CAAK,IAAA,kBAAA,CAAA;AACzC,IAAA,IAAI,CAAC,WAAY,CAAA,KAAA,CAAM,GAAG,CAAE,CAAA,QAAA,CAAS,kBAAkB,CAAG,EAAA;AACxD,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,sCAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,OAAA,CAAQ,IAAK,EAAA,CAAA;AACjC,IAAM,MAAA,KAAA,GAAQ,IAAK,CAAA,QAAA,CAAS,IAAI,CAAA,CAAA;AAChC,IAAA,IAAI,CAAC,KAAO,EAAA;AACV,MAAA,MAAM,IAAIC,4BAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,CAAA,YAAA,CAAA;AAAA,QACA,KAAK,QAAS,CAAA,MAAA;AAAA,OAChB,CAAA;AAAA,KACF;AACA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AACF;;;;"}
1
+ {"version":3,"file":"request-body-validation.cjs.js","sources":["../../src/schema/request-body-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { Operation, ParserOptions, RequestParser } from './types';\nimport { ValidateFunction } from 'ajv';\nimport { OperationError, OperationParsingError } from './errors';\nimport { RequestBodyObject, SchemaObject } from 'openapi3-ts';\n\nclass DisabledRequestBodyParser\n implements RequestParser<JsonObject | undefined>\n{\n operation: Operation;\n constructor(operation: Operation) {\n this.operation = operation;\n }\n async parse(request: Request): Promise<JsonObject | undefined> {\n const bodyText = await request.text();\n if (bodyText?.length) {\n throw new OperationError(\n this.operation,\n 'Received a body but no schema was found',\n );\n }\n return undefined;\n }\n}\nexport class RequestBodyParser\n implements RequestParser<JsonObject | undefined>\n{\n operation: Operation;\n disabled: boolean = false;\n validate!: ValidateFunction;\n schema!: SchemaObject;\n requestBodySchema!: RequestBodyObject;\n\n static fromOperation(operation: Operation, options: ParserOptions) {\n return operation.schema.requestBody\n ? new RequestBodyParser(operation, options)\n : new DisabledRequestBodyParser(operation);\n }\n\n constructor(operation: Operation, options: ParserOptions) {\n this.operation = operation;\n const { schema: operationSchema } = this.operation;\n const requestBody = operationSchema.requestBody;\n\n if (!requestBody) {\n throw new OperationError(\n this.operation,\n 'No request body found in operation',\n );\n }\n\n if ('$ref' in requestBody!) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported',\n );\n }\n if (!requestBody!.content) {\n throw new OperationError(\n this.operation,\n 'No content found in request body',\n );\n }\n const contentTypes = requestBody!.content;\n const jsonContentType = Object.keys(contentTypes).find(contentType =>\n contentType.split(';').includes('application/json'),\n );\n if (!jsonContentType) {\n throw new OperationError(\n this.operation,\n 'No application/json content type found in request body',\n );\n }\n const schema = requestBody!.content[jsonContentType].schema;\n if (!schema) {\n throw new OperationError(\n this.operation,\n 'No JSON schema found in request body',\n );\n }\n if ('$ref' in schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported',\n );\n }\n this.validate = options.ajv.compile(schema);\n this.schema = schema;\n this.requestBodySchema = requestBody;\n }\n async parse(request: Request): Promise<JsonObject | undefined> {\n const bodyText = await request.text();\n if (this.requestBodySchema.required && !bodyText?.length) {\n throw new OperationError(\n this.operation,\n `No request body found for ${request.url}`,\n );\n }\n\n const contentType =\n request.headers.get('content-type') || 'application/json';\n if (!contentType.split(';').includes('application/json')) {\n throw new OperationError(\n this.operation,\n 'Content type is not application/json',\n );\n }\n const body = (await request.json()) as JsonObject;\n const valid = this.validate(body);\n if (!valid) {\n throw new OperationParsingError(\n this.operation,\n `Request body`,\n this.validate.errors!,\n );\n }\n return body;\n }\n}\n"],"names":["OperationError","OperationParsingError"],"mappings":";;;;AAsBA,MAAM,yBAEN,CAAA;AAAA,EACE,SAAA;AAAA,EACA,YAAY,SAAsB,EAAA;AAChC,IAAA,IAAA,CAAK,SAAY,GAAA,SAAA;AAAA;AACnB,EACA,MAAM,MAAM,OAAmD,EAAA;AAC7D,IAAM,MAAA,QAAA,GAAW,MAAM,OAAA,CAAQ,IAAK,EAAA;AACpC,IAAA,IAAI,UAAU,MAAQ,EAAA;AACpB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAO,OAAA,KAAA,CAAA;AAAA;AAEX;AACO,MAAM,iBAEb,CAAA;AAAA,EACE,SAAA;AAAA,EACA,QAAoB,GAAA,KAAA;AAAA,EACpB,QAAA;AAAA,EACA,MAAA;AAAA,EACA,iBAAA;AAAA,EAEA,OAAO,aAAc,CAAA,SAAA,EAAsB,OAAwB,EAAA;AACjE,IAAO,OAAA,SAAA,CAAU,MAAO,CAAA,WAAA,GACpB,IAAI,iBAAA,CAAkB,WAAW,OAAO,CAAA,GACxC,IAAI,yBAAA,CAA0B,SAAS,CAAA;AAAA;AAC7C,EAEA,WAAA,CAAY,WAAsB,OAAwB,EAAA;AACxD,IAAA,IAAA,CAAK,SAAY,GAAA,SAAA;AACjB,IAAA,MAAM,EAAE,MAAA,EAAQ,eAAgB,EAAA,GAAI,IAAK,CAAA,SAAA;AACzC,IAAA,MAAM,cAAc,eAAgB,CAAA,WAAA;AAEpC,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAGF,IAAA,IAAI,UAAU,WAAc,EAAA;AAC1B,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAI,IAAA,CAAC,YAAa,OAAS,EAAA;AACzB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAA,MAAM,eAAe,WAAa,CAAA,OAAA;AAClC,IAAA,MAAM,eAAkB,GAAA,MAAA,CAAO,IAAK,CAAA,YAAY,CAAE,CAAA,IAAA;AAAA,MAAK,iBACrD,WAAY,CAAA,KAAA,CAAM,GAAG,CAAA,CAAE,SAAS,kBAAkB;AAAA,KACpD;AACA,IAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAA,MAAM,MAAS,GAAA,WAAA,CAAa,OAAQ,CAAA,eAAe,CAAE,CAAA,MAAA;AACrD,IAAA,IAAI,CAAC,MAAQ,EAAA;AACX,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAA,IAAI,UAAU,MAAQ,EAAA;AACpB,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAA,IAAA,CAAK,QAAW,GAAA,OAAA,CAAQ,GAAI,CAAA,OAAA,CAAQ,MAAM,CAAA;AAC1C,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA;AACd,IAAA,IAAA,CAAK,iBAAoB,GAAA,WAAA;AAAA;AAC3B,EACA,MAAM,MAAM,OAAmD,EAAA;AAC7D,IAAM,MAAA,QAAA,GAAW,MAAM,OAAA,CAAQ,IAAK,EAAA;AACpC,IAAA,IAAI,IAAK,CAAA,iBAAA,CAAkB,QAAY,IAAA,CAAC,UAAU,MAAQ,EAAA;AACxD,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,CAAA,0BAAA,EAA6B,QAAQ,GAAG,CAAA;AAAA,OAC1C;AAAA;AAGF,IAAA,MAAM,WACJ,GAAA,OAAA,CAAQ,OAAQ,CAAA,GAAA,CAAI,cAAc,CAAK,IAAA,kBAAA;AACzC,IAAA,IAAI,CAAC,WAAY,CAAA,KAAA,CAAM,GAAG,CAAE,CAAA,QAAA,CAAS,kBAAkB,CAAG,EAAA;AACxD,MAAA,MAAM,IAAIA,qBAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL;AAAA,OACF;AAAA;AAEF,IAAM,MAAA,IAAA,GAAQ,MAAM,OAAA,CAAQ,IAAK,EAAA;AACjC,IAAM,MAAA,KAAA,GAAQ,IAAK,CAAA,QAAA,CAAS,IAAI,CAAA;AAChC,IAAA,IAAI,CAAC,KAAO,EAAA;AACV,MAAA,MAAM,IAAIC,4BAAA;AAAA,QACR,IAAK,CAAA,SAAA;AAAA,QACL,CAAA,YAAA,CAAA;AAAA,QACA,KAAK,QAAS,CAAA;AAAA,OAChB;AAAA;AAEF,IAAO,OAAA,IAAA;AAAA;AAEX;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"response-body-validation.cjs.js","sources":["../../src/schema/response-body-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { Operation, ParserOptions, ResponseParser } from './types';\nimport {\n OperationError,\n OperationParsingResponseError,\n OperationResponseError,\n} from './errors';\nimport Ajv from 'ajv';\nimport { OperationObject, ResponseObject } from 'openapi3-ts';\n\nclass DisabledResponseBodyParser\n implements ResponseParser<JsonObject | undefined>\n{\n operation: Operation;\n constructor(operation: Operation) {\n this.operation = operation;\n }\n async parse(response: Response): Promise<JsonObject | undefined> {\n const body = await response.text();\n if (body?.length) {\n throw new OperationError(\n this.operation,\n 'Received a body but no schema was found',\n );\n }\n return undefined;\n }\n}\n\nexport class ResponseBodyParser\n implements ResponseParser<JsonObject | undefined>\n{\n operation: Operation;\n ajv: Ajv;\n\n static fromOperation(operation: Operation, options: ParserOptions) {\n return operation.schema.responses &&\n Object.keys(operation.schema.responses).length\n ? new ResponseBodyParser(operation, options)\n : new DisabledResponseBodyParser(operation);\n }\n\n constructor(operation: Operation, options: ParserOptions) {\n this.operation = operation;\n this.ajv = options.ajv;\n const responseSchemas = operation.schema.responses;\n for (const [statusCode, schema] of Object.entries(responseSchemas)) {\n const contentTypes = schema.content;\n if (!contentTypes) {\n // Skip responses without content, eg 204 No Content.\n continue;\n }\n const jsonContentType = Object.keys(contentTypes).find(contentType =>\n contentType.split(';').includes('application/json'),\n );\n if (!jsonContentType) {\n throw new OperationError(\n this.operation,\n `No application/json content type found in response for status code ${statusCode}`,\n );\n } else if ('$ref' in contentTypes[jsonContentType].schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported',\n );\n }\n }\n }\n\n async parse(response: Response): Promise<JsonObject | undefined> {\n const body = await response.text();\n const responseSchema = this.findResponseSchema(\n this.operation.schema,\n response,\n );\n if (!responseSchema?.content && !body?.length) {\n // If there is no content in the response schema and no body in the response, then the response is valid.\n // eg 204 No Content\n return undefined;\n }\n if (!responseSchema) {\n throw new OperationResponseError(\n this.operation,\n response,\n `No schema found.`,\n );\n }\n\n const contentTypes = responseSchema.content;\n if (!contentTypes && body?.length) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'Received a body but no schema was found',\n );\n }\n const jsonContentType = Object.keys(contentTypes ?? {}).find(contentType =>\n contentType.split(';').includes('application/json'),\n );\n if (!jsonContentType) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'No application/json content type found in response',\n );\n }\n const schema = responseSchema.content![jsonContentType].schema;\n // This is a bit of type laziness. Ideally, this would be a type-narrowing function, but I wasn't able to get the types to work.\n if (!schema) {\n throw new OperationError(this.operation, 'No schema found in response');\n }\n if ('$ref' in schema) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'Reference objects are not supported',\n );\n }\n\n if (!schema.required && !body?.length) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'Response body is required but missing',\n );\n } else if (!schema.required && !body?.length) {\n // If there is no content in the response schema and no body in the response, then the response is valid\n return undefined;\n }\n\n const validate = this.ajv.compile(schema);\n const jsonBody = (await response.json()) as JsonObject;\n const valid = validate(jsonBody);\n if (!valid) {\n throw new OperationParsingResponseError(\n this.operation,\n response,\n 'Response body',\n validate.errors!,\n );\n }\n return jsonBody;\n }\n\n private findResponseSchema(\n operationSchema: OperationObject,\n { status }: Response,\n ): ResponseObject | undefined {\n return (\n operationSchema.responses?.[status] ?? operationSchema.responses?.default\n );\n }\n}\n"],"names":["OperationError","OperationResponseError","OperationParsingResponseError"],"mappings":"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"}
1
+ {"version":3,"file":"response-body-validation.cjs.js","sources":["../../src/schema/response-body-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { Operation, ParserOptions, ResponseParser } from './types';\nimport {\n OperationError,\n OperationParsingResponseError,\n OperationResponseError,\n} from './errors';\nimport Ajv from 'ajv';\nimport { OperationObject, ResponseObject } from 'openapi3-ts';\n\nclass DisabledResponseBodyParser\n implements ResponseParser<JsonObject | undefined>\n{\n operation: Operation;\n constructor(operation: Operation) {\n this.operation = operation;\n }\n async parse(response: Response): Promise<JsonObject | undefined> {\n const body = await response.text();\n if (body?.length) {\n throw new OperationError(\n this.operation,\n 'Received a body but no schema was found',\n );\n }\n return undefined;\n }\n}\n\nexport class ResponseBodyParser\n implements ResponseParser<JsonObject | undefined>\n{\n operation: Operation;\n ajv: Ajv;\n\n static fromOperation(operation: Operation, options: ParserOptions) {\n return operation.schema.responses &&\n Object.keys(operation.schema.responses).length\n ? new ResponseBodyParser(operation, options)\n : new DisabledResponseBodyParser(operation);\n }\n\n constructor(operation: Operation, options: ParserOptions) {\n this.operation = operation;\n this.ajv = options.ajv;\n const responseSchemas = operation.schema.responses;\n for (const [statusCode, schema] of Object.entries(responseSchemas)) {\n const contentTypes = schema.content;\n if (!contentTypes) {\n // Skip responses without content, eg 204 No Content.\n continue;\n }\n const jsonContentType = Object.keys(contentTypes).find(contentType =>\n contentType.split(';').includes('application/json'),\n );\n if (!jsonContentType) {\n throw new OperationError(\n this.operation,\n `No application/json content type found in response for status code ${statusCode}`,\n );\n } else if ('$ref' in contentTypes[jsonContentType].schema) {\n throw new OperationError(\n this.operation,\n 'Reference objects are not supported',\n );\n }\n }\n }\n\n async parse(response: Response): Promise<JsonObject | undefined> {\n const body = await response.text();\n const responseSchema = this.findResponseSchema(\n this.operation.schema,\n response,\n );\n if (!responseSchema?.content && !body?.length) {\n // If there is no content in the response schema and no body in the response, then the response is valid.\n // eg 204 No Content\n return undefined;\n }\n if (!responseSchema) {\n throw new OperationResponseError(\n this.operation,\n response,\n `No schema found.`,\n );\n }\n\n const contentTypes = responseSchema.content;\n if (!contentTypes && body?.length) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'Received a body but no schema was found',\n );\n }\n const jsonContentType = Object.keys(contentTypes ?? {}).find(contentType =>\n contentType.split(';').includes('application/json'),\n );\n if (!jsonContentType) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'No application/json content type found in response',\n );\n }\n const schema = responseSchema.content![jsonContentType].schema;\n // This is a bit of type laziness. Ideally, this would be a type-narrowing function, but I wasn't able to get the types to work.\n if (!schema) {\n throw new OperationError(this.operation, 'No schema found in response');\n }\n if ('$ref' in schema) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'Reference objects are not supported',\n );\n }\n\n if (!schema.required && !body?.length) {\n throw new OperationResponseError(\n this.operation,\n response,\n 'Response body is required but missing',\n );\n } else if (!schema.required && !body?.length) {\n // If there is no content in the response schema and no body in the response, then the response is valid\n return undefined;\n }\n\n const validate = this.ajv.compile(schema);\n const jsonBody = (await response.json()) as JsonObject;\n const valid = validate(jsonBody);\n if (!valid) {\n throw new OperationParsingResponseError(\n this.operation,\n response,\n 'Response body',\n validate.errors!,\n );\n }\n return jsonBody;\n }\n\n private findResponseSchema(\n operationSchema: OperationObject,\n { status }: Response,\n ): ResponseObject | undefined {\n return (\n operationSchema.responses?.[status] ?? operationSchema.responses?.default\n );\n }\n}\n"],"names":["OperationError","OperationResponseError","OperationParsingResponseError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs.js","sources":["../../src/schema/utils.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompletedRequest, CompletedResponse } from 'mockttp';\nimport { ErrorObject } from 'ajv';\n\nexport function mockttpToFetchRequest(request: CompletedRequest) {\n const headers = new Headers(request.rawHeaders);\n return {\n url: request.url,\n method: request.method,\n headers,\n json: () => request.body.getJson(),\n text: () => request.body.getText(),\n } as Request;\n}\nexport function mockttpToFetchResponse(response: CompletedResponse) {\n const headers = new Headers(response.rawHeaders);\n return {\n status: response.statusCode,\n headers,\n json: () => response.body?.getJson(),\n text: () => response.body?.getText(),\n } as Response;\n}\n\nexport function humanifyAjvError(error: ErrorObject) {\n switch (error.keyword) {\n case 'required':\n return `The \"${error.params.missingProperty}\" property is required`;\n case 'type':\n return `${\n error.instancePath ? `\"${error.instancePath}\"` : 'Value'\n } should be of type ${error.params.type}`;\n case 'additionalProperties':\n return `The \"${error.params.additionalProperty}\" property is not allowed`;\n default:\n return error.message;\n }\n}\n"],"names":[],"mappings":";;AAkBO,SAAS,sBAAsB,OAA2B,EAAA;AAC/D,EAAA,MAAM,OAAU,GAAA,IAAI,OAAQ,CAAA,OAAA,CAAQ,UAAU,CAAA,CAAA;AAC9C,EAAO,OAAA;AAAA,IACL,KAAK,OAAQ,CAAA,GAAA;AAAA,IACb,QAAQ,OAAQ,CAAA,MAAA;AAAA,IAChB,OAAA;AAAA,IACA,IAAM,EAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,OAAQ,EAAA;AAAA,IACjC,IAAM,EAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,OAAQ,EAAA;AAAA,GACnC,CAAA;AACF,CAAA;AACO,SAAS,uBAAuB,QAA6B,EAAA;AAClE,EAAA,MAAM,OAAU,GAAA,IAAI,OAAQ,CAAA,QAAA,CAAS,UAAU,CAAA,CAAA;AAC/C,EAAO,OAAA;AAAA,IACL,QAAQ,QAAS,CAAA,UAAA;AAAA,IACjB,OAAA;AAAA,IACA,IAAM,EAAA,MAAM,QAAS,CAAA,IAAA,EAAM,OAAQ,EAAA;AAAA,IACnC,IAAM,EAAA,MAAM,QAAS,CAAA,IAAA,EAAM,OAAQ,EAAA;AAAA,GACrC,CAAA;AACF,CAAA;AAEO,SAAS,iBAAiB,KAAoB,EAAA;AACnD,EAAA,QAAQ,MAAM,OAAS;AAAA,IACrB,KAAK,UAAA;AACH,MAAO,OAAA,CAAA,KAAA,EAAQ,KAAM,CAAA,MAAA,CAAO,eAAe,CAAA,sBAAA,CAAA,CAAA;AAAA,IAC7C,KAAK,MAAA;AACH,MAAO,OAAA,CAAA,EACL,KAAM,CAAA,YAAA,GAAe,CAAI,CAAA,EAAA,KAAA,CAAM,YAAY,CAAA,CAAA,CAAA,GAAM,OACnD,CAAA,mBAAA,EAAsB,KAAM,CAAA,MAAA,CAAO,IAAI,CAAA,CAAA,CAAA;AAAA,IACzC,KAAK,sBAAA;AACH,MAAO,OAAA,CAAA,KAAA,EAAQ,KAAM,CAAA,MAAA,CAAO,kBAAkB,CAAA,yBAAA,CAAA,CAAA;AAAA,IAChD;AACE,MAAA,OAAO,KAAM,CAAA,OAAA,CAAA;AAAA,GACjB;AACF;;;;;;"}
1
+ {"version":3,"file":"utils.cjs.js","sources":["../../src/schema/utils.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompletedRequest, CompletedResponse } from 'mockttp';\nimport { ErrorObject } from 'ajv';\n\nexport function mockttpToFetchRequest(request: CompletedRequest) {\n const headers = new Headers(request.rawHeaders);\n return {\n url: request.url,\n method: request.method,\n headers,\n json: () => request.body.getJson(),\n text: () => request.body.getText(),\n } as Request;\n}\nexport function mockttpToFetchResponse(response: CompletedResponse) {\n const headers = new Headers(response.rawHeaders);\n return {\n status: response.statusCode,\n headers,\n json: () => response.body?.getJson(),\n text: () => response.body?.getText(),\n } as Response;\n}\n\nexport function humanifyAjvError(error: ErrorObject) {\n switch (error.keyword) {\n case 'required':\n return `The \"${error.params.missingProperty}\" property is required`;\n case 'type':\n return `${\n error.instancePath ? `\"${error.instancePath}\"` : 'Value'\n } should be of type ${error.params.type}`;\n case 'additionalProperties':\n return `The \"${error.params.additionalProperty}\" property is not allowed`;\n default:\n return error.message;\n }\n}\n"],"names":[],"mappings":";;AAkBO,SAAS,sBAAsB,OAA2B,EAAA;AAC/D,EAAA,MAAM,OAAU,GAAA,IAAI,OAAQ,CAAA,OAAA,CAAQ,UAAU,CAAA;AAC9C,EAAO,OAAA;AAAA,IACL,KAAK,OAAQ,CAAA,GAAA;AAAA,IACb,QAAQ,OAAQ,CAAA,MAAA;AAAA,IAChB,OAAA;AAAA,IACA,IAAM,EAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,OAAQ,EAAA;AAAA,IACjC,IAAM,EAAA,MAAM,OAAQ,CAAA,IAAA,CAAK,OAAQ;AAAA,GACnC;AACF;AACO,SAAS,uBAAuB,QAA6B,EAAA;AAClE,EAAA,MAAM,OAAU,GAAA,IAAI,OAAQ,CAAA,QAAA,CAAS,UAAU,CAAA;AAC/C,EAAO,OAAA;AAAA,IACL,QAAQ,QAAS,CAAA,UAAA;AAAA,IACjB,OAAA;AAAA,IACA,IAAM,EAAA,MAAM,QAAS,CAAA,IAAA,EAAM,OAAQ,EAAA;AAAA,IACnC,IAAM,EAAA,MAAM,QAAS,CAAA,IAAA,EAAM,OAAQ;AAAA,GACrC;AACF;AAEO,SAAS,iBAAiB,KAAoB,EAAA;AACnD,EAAA,QAAQ,MAAM,OAAS;AAAA,IACrB,KAAK,UAAA;AACH,MAAO,OAAA,CAAA,KAAA,EAAQ,KAAM,CAAA,MAAA,CAAO,eAAe,CAAA,sBAAA,CAAA;AAAA,IAC7C,KAAK,MAAA;AACH,MAAO,OAAA,CAAA,EACL,KAAM,CAAA,YAAA,GAAe,CAAI,CAAA,EAAA,KAAA,CAAM,YAAY,CAAA,CAAA,CAAA,GAAM,OACnD,CAAA,mBAAA,EAAsB,KAAM,CAAA,MAAA,CAAO,IAAI,CAAA,CAAA;AAAA,IACzC,KAAK,sBAAA;AACH,MAAO,OAAA,CAAA,KAAA,EAAQ,KAAM,CAAA,MAAA,CAAO,kBAAkB,CAAA,yBAAA,CAAA;AAAA,IAChD;AACE,MAAA,OAAO,KAAM,CAAA,OAAA;AAAA;AAEnB;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"validation.cjs.js","sources":["../../src/schema/validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompletedRequest, CompletedResponse } from 'mockttp';\nimport { OpenAPIObject, OperationObject } from 'openapi3-ts';\nimport Ajv from 'ajv';\nimport Parser from '@apidevtools/swagger-parser';\nimport { Operation, Validator, ValidatorParams } from './types';\nimport { ParameterValidator } from './parameter-validation';\nimport { OperationError } from './errors';\nimport { RequestBodyParser } from './request-body-validation';\nimport { mockttpToFetchRequest, mockttpToFetchResponse } from './utils';\nimport { ResponseBodyParser } from './response-body-validation';\n\nconst ajv = new Ajv({ allErrors: true });\n\nclass RequestBodyValidator implements Validator {\n schema: OpenAPIObject;\n constructor(schema: OpenAPIObject) {\n this.schema = schema;\n }\n\n async validate({ pair, operation }: ValidatorParams) {\n const { request, response } = pair;\n if (response.statusCode === 400) {\n // If the response is a 400, then the request is invalid and we shouldn't validate the parameters\n return;\n }\n\n // NOTE: There may be a worthwhile optimization here to cache these results to avoid re-parsing the schema for every request. As is, I don't think this is a big deal.\n const parser = RequestBodyParser.fromOperation(operation, { ajv });\n const fetchRequest = mockttpToFetchRequest(request);\n await parser.parse(fetchRequest);\n }\n}\n\nclass ResponseBodyValidator implements Validator {\n schema: OpenAPIObject;\n constructor(schema: OpenAPIObject) {\n this.schema = schema;\n }\n\n async validate({ pair, operation }: ValidatorParams) {\n const { response } = pair;\n // NOTE: There may be a worthwhile optimization here to cache these results to avoid re-parsing the schema for every request. As is, I don't think this is a big deal.\n const parser = ResponseBodyParser.fromOperation(operation, { ajv });\n const fetchResponse = mockttpToFetchResponse(response);\n await parser.parse(fetchResponse);\n }\n}\n\n/**\n * Find an operation in an OpenAPI schema that matches a request. This is done by comparing the request URL to the paths in the schema.\n * @param openApiSchema - The OpenAPI schema to search for the operation in.\n * @param request - The request to find the operation for.\n * @returns A tuple of the path and the operation object that matches the request.\n */\nexport function findOperationByRequest(\n openApiSchema: OpenAPIObject,\n request: CompletedRequest,\n): [string, OperationObject] | undefined {\n const { url } = request;\n const { pathname } = new URL(url);\n\n const parts = pathname.split('/');\n for (const [path, schema] of Object.entries(openApiSchema.paths)) {\n const pathParts = path.split('/');\n if (parts.length !== pathParts.length) {\n continue;\n }\n let found = true;\n for (let i = 0; i < parts.length; i++) {\n if (pathParts[i] === parts[i]) {\n continue;\n }\n // If the path part is a parameter, we can count it as a match. eg /api/{id} will match /api/1\n if (pathParts[i].startsWith('{') && pathParts[i].endsWith('}')) {\n continue;\n }\n found = false;\n break;\n }\n if (!found) {\n continue;\n }\n let matchingOperationType: OperationObject | undefined = undefined;\n for (const [operationType, operation] of Object.entries(schema)) {\n if (operationType === request.method.toLowerCase()) {\n matchingOperationType = operation as OperationObject;\n break;\n }\n }\n if (!matchingOperationType) {\n continue;\n }\n return [path, matchingOperationType];\n }\n\n return undefined;\n}\n\nexport class OpenApiProxyValidator {\n schema: OpenAPIObject | undefined;\n validators: Validator[] | undefined;\n\n async initialize(url: string) {\n this.schema = (await Parser.dereference(url)) as unknown as OpenAPIObject;\n this.validators = [\n new ParameterValidator(this.schema),\n new RequestBodyValidator(this.schema),\n new ResponseBodyValidator(this.schema),\n ];\n }\n\n async validate(request: CompletedRequest, response: CompletedResponse) {\n const operationPathTuple = findOperationByRequest(this.schema!, request);\n if (!operationPathTuple) {\n throw new OperationError(\n { path: request.path, method: request.method } as Operation,\n `No operation schema found for ${request.url}`,\n );\n }\n\n const [path, operationSchema] = operationPathTuple;\n const operation = { path, method: request.method, schema: operationSchema };\n\n const validators = this.validators!;\n await Promise.all(\n validators.map(validator =>\n validator.validate({\n pair: { request, response },\n operation,\n }),\n ),\n );\n }\n}\n"],"names":["Ajv","RequestBodyParser","mockttpToFetchRequest","ResponseBodyParser","mockttpToFetchResponse","Parser","ParameterValidator","OperationError"],"mappings":"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"}
1
+ {"version":3,"file":"validation.cjs.js","sources":["../../src/schema/validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompletedRequest, CompletedResponse } from 'mockttp';\nimport { OpenAPIObject, OperationObject } from 'openapi3-ts';\nimport Ajv from 'ajv';\nimport Parser from '@apidevtools/swagger-parser';\nimport { Operation, Validator, ValidatorParams } from './types';\nimport { ParameterValidator } from './parameter-validation';\nimport { OperationError } from './errors';\nimport { RequestBodyParser } from './request-body-validation';\nimport { mockttpToFetchRequest, mockttpToFetchResponse } from './utils';\nimport { ResponseBodyParser } from './response-body-validation';\n\nconst ajv = new Ajv({ allErrors: true });\n\nclass RequestBodyValidator implements Validator {\n schema: OpenAPIObject;\n constructor(schema: OpenAPIObject) {\n this.schema = schema;\n }\n\n async validate({ pair, operation }: ValidatorParams) {\n const { request, response } = pair;\n if (response.statusCode === 400) {\n // If the response is a 400, then the request is invalid and we shouldn't validate the parameters\n return;\n }\n\n // NOTE: There may be a worthwhile optimization here to cache these results to avoid re-parsing the schema for every request. As is, I don't think this is a big deal.\n const parser = RequestBodyParser.fromOperation(operation, { ajv });\n const fetchRequest = mockttpToFetchRequest(request);\n await parser.parse(fetchRequest);\n }\n}\n\nclass ResponseBodyValidator implements Validator {\n schema: OpenAPIObject;\n constructor(schema: OpenAPIObject) {\n this.schema = schema;\n }\n\n async validate({ pair, operation }: ValidatorParams) {\n const { response } = pair;\n // NOTE: There may be a worthwhile optimization here to cache these results to avoid re-parsing the schema for every request. As is, I don't think this is a big deal.\n const parser = ResponseBodyParser.fromOperation(operation, { ajv });\n const fetchResponse = mockttpToFetchResponse(response);\n await parser.parse(fetchResponse);\n }\n}\n\n/**\n * Find an operation in an OpenAPI schema that matches a request. This is done by comparing the request URL to the paths in the schema.\n * @param openApiSchema - The OpenAPI schema to search for the operation in.\n * @param request - The request to find the operation for.\n * @returns A tuple of the path and the operation object that matches the request.\n */\nexport function findOperationByRequest(\n openApiSchema: OpenAPIObject,\n request: CompletedRequest,\n): [string, OperationObject] | undefined {\n const { url } = request;\n const { pathname } = new URL(url);\n\n const parts = pathname.split('/');\n for (const [path, schema] of Object.entries(openApiSchema.paths)) {\n const pathParts = path.split('/');\n if (parts.length !== pathParts.length) {\n continue;\n }\n let found = true;\n for (let i = 0; i < parts.length; i++) {\n if (pathParts[i] === parts[i]) {\n continue;\n }\n // If the path part is a parameter, we can count it as a match. eg /api/{id} will match /api/1\n if (pathParts[i].startsWith('{') && pathParts[i].endsWith('}')) {\n continue;\n }\n found = false;\n break;\n }\n if (!found) {\n continue;\n }\n let matchingOperationType: OperationObject | undefined = undefined;\n for (const [operationType, operation] of Object.entries(schema)) {\n if (operationType === request.method.toLowerCase()) {\n matchingOperationType = operation as OperationObject;\n break;\n }\n }\n if (!matchingOperationType) {\n continue;\n }\n return [path, matchingOperationType];\n }\n\n return undefined;\n}\n\nexport class OpenApiProxyValidator {\n schema: OpenAPIObject | undefined;\n validators: Validator[] | undefined;\n\n async initialize(url: string) {\n this.schema = (await Parser.dereference(url)) as unknown as OpenAPIObject;\n this.validators = [\n new ParameterValidator(this.schema),\n new RequestBodyValidator(this.schema),\n new ResponseBodyValidator(this.schema),\n ];\n }\n\n async validate(request: CompletedRequest, response: CompletedResponse) {\n const operationPathTuple = findOperationByRequest(this.schema!, request);\n if (!operationPathTuple) {\n throw new OperationError(\n { path: request.path, method: request.method } as Operation,\n `No operation schema found for ${request.url}`,\n );\n }\n\n const [path, operationSchema] = operationPathTuple;\n const operation = { path, method: request.method, schema: operationSchema };\n\n const validators = this.validators!;\n await Promise.all(\n validators.map(validator =>\n validator.validate({\n pair: { request, response },\n operation,\n }),\n ),\n );\n }\n}\n"],"names":["Ajv","RequestBodyParser","mockttpToFetchRequest","ResponseBodyParser","mockttpToFetchResponse","Parser","ParameterValidator","OperationError"],"mappings":"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"}
package/dist/stub.cjs.js CHANGED
@@ -24,7 +24,7 @@ function getDefaultRouterMiddleware() {
24
24
  function getOpenApiSpecRoute(baseUrl) {
25
25
  return `${baseUrl}${constants.OPENAPI_SPEC_ROUTE}`;
26
26
  }
27
- function createValidatedOpenApiRouter(spec, options) {
27
+ function createRouterWithValidation(spec, options) {
28
28
  const router = PromiseRouter__default.default();
29
29
  router.use(options?.middleware || getDefaultRouterMiddleware());
30
30
  router.use((req, _, next) => {
@@ -82,8 +82,15 @@ function createValidatedOpenApiRouter(spec, options) {
82
82
  });
83
83
  return router;
84
84
  }
85
+ function createValidatedOpenApiRouter(spec, options) {
86
+ return createRouterWithValidation(spec, options);
87
+ }
88
+ function createValidatedOpenApiRouterFromGeneratedEndpointMap(spec, options) {
89
+ return createRouterWithValidation(spec, options);
90
+ }
85
91
 
86
92
  exports.createValidatedOpenApiRouter = createValidatedOpenApiRouter;
93
+ exports.createValidatedOpenApiRouterFromGeneratedEndpointMap = createValidatedOpenApiRouterFromGeneratedEndpointMap;
87
94
  exports.getDefaultRouterMiddleware = getDefaultRouterMiddleware;
88
95
  exports.getOpenApiSpecRoute = getOpenApiSpecRoute;
89
96
  //# sourceMappingURL=stub.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stub.cjs.js","sources":["../src/stub.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport PromiseRouter from 'express-promise-router';\nimport { ApiRouter } from './router';\nimport { RequiredDoc } from './types';\nimport {\n ErrorRequestHandler,\n RequestHandler,\n NextFunction,\n Request,\n Response,\n json,\n} from 'express';\nimport { InputError } from '@backstage/errors';\nimport { middleware as OpenApiValidator } from 'express-openapi-validator';\nimport { OPENAPI_SPEC_ROUTE } from './constants';\nimport { isErrorResult, merge } from 'openapi-merge';\n\ntype PropertyOverrideRequest = Request & {\n [key: symbol]: string;\n};\n\nconst baseUrlSymbol = Symbol();\nconst originalUrlSymbol = Symbol();\n\nfunction validatorErrorTransformer(): ErrorRequestHandler {\n return (error: Error, _: Request, _2: Response, next: NextFunction) => {\n next(new InputError(error.message));\n };\n}\n\nexport function getDefaultRouterMiddleware() {\n return [json()];\n}\n\n/**\n * Given a base url for a plugin, find the given OpenAPI spec for that plugin.\n * @param baseUrl - Plugin base url.\n * @returns OpenAPI spec route for the base url.\n * @public\n */\nexport function getOpenApiSpecRoute(baseUrl: string) {\n return `${baseUrl}${OPENAPI_SPEC_ROUTE}`;\n}\n\n/**\n * Create a new OpenAPI router with some default middleware.\n * @param spec - Your OpenAPI spec imported as a JSON object.\n * @param validatorOptions - `openapi-express-validator` options to override the defaults.\n * @returns A new express router with validation middleware.\n * @public\n */\nexport function createValidatedOpenApiRouter<T extends RequiredDoc>(\n spec: T,\n options?: {\n validatorOptions?: Partial<Parameters<typeof OpenApiValidator>['0']>;\n middleware?: RequestHandler[];\n },\n) {\n const router = PromiseRouter();\n router.use(options?.middleware || getDefaultRouterMiddleware());\n\n /**\n * Middleware to setup the routing for OpenApiValidator. OpenApiValidator expects `req.originalUrl`\n * and `req.baseUrl` to be the full path. We adjust them here to basically be nothing and then\n * revive the old values in the last function in this method. We could instead update `req.path`\n * but that might affect the routing and I'd rather not.\n *\n * TODO: I opened https://github.com/cdimascio/express-openapi-validator/issues/843\n * to track this on the middleware side, but there was a similar ticket, https://github.com/cdimascio/express-openapi-validator/issues/113\n * that has had minimal activity. If that changes, update this to use a new option on their side.\n */\n router.use((req: Request, _, next) => {\n /**\n * Express typings are weird. They don't recognize PropertyOverrideRequest as a valid\n * Request child and try to overload as PathParams. Just cast it here, since we know\n * what we're doing.\n */\n const customRequest = req as PropertyOverrideRequest;\n customRequest[baseUrlSymbol] = customRequest.baseUrl;\n customRequest.baseUrl = '';\n customRequest[originalUrlSymbol] = customRequest.originalUrl;\n customRequest.originalUrl = customRequest.url;\n next();\n });\n\n // TODO: Handle errors by converting from OpenApiValidator errors to known @backstage/errors errors.\n router.use(\n OpenApiValidator({\n validateRequests: {\n coerceTypes: false,\n allowUnknownQueryParameters: false,\n },\n ignoreUndocumented: true,\n validateResponses: false,\n ...options?.validatorOptions,\n apiSpec: spec as any,\n }),\n );\n\n /**\n * Revert `req.baseUrl` and `req.originalUrl` changes. This ensures that any further usage\n * of these variables will be unchanged.\n */\n router.use((req: Request, _, next) => {\n const customRequest = req as PropertyOverrideRequest;\n customRequest.baseUrl = customRequest[baseUrlSymbol];\n customRequest.originalUrl = customRequest[originalUrlSymbol];\n delete customRequest[baseUrlSymbol];\n delete customRequest[originalUrlSymbol];\n next();\n });\n\n // Any errors from the middleware get through here.\n router.use(validatorErrorTransformer());\n\n router.get(OPENAPI_SPEC_ROUTE, async (req, res) => {\n const mergeOutput = merge([\n {\n oas: spec as any,\n pathModification: {\n /**\n * Get the route that this OpenAPI spec is hosted on. The other\n * approach of using the discovery API increases the router constructor\n * significantly and since we're just looking for path and not full URL,\n * this works.\n *\n * If we wanted to add a list of servers, there may be a case for adding\n * discovery API to get an exhaustive list of upstream servers, but that's\n * also not currently supported.\n */\n prepend: req.originalUrl.replace(OPENAPI_SPEC_ROUTE, ''),\n },\n },\n ]);\n if (isErrorResult(mergeOutput)) {\n throw new InputError('Invalid spec defined');\n }\n res.json(mergeOutput.output);\n });\n\n return router as ApiRouter<typeof spec>;\n}\n"],"names":["InputError","json","OPENAPI_SPEC_ROUTE","PromiseRouter","OpenApiValidator","merge","isErrorResult"],"mappings":"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"}
1
+ {"version":3,"file":"stub.cjs.js","sources":["../src/stub.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport PromiseRouter from 'express-promise-router';\nimport { ApiRouter } from './router';\nimport { EndpointMap, RequiredDoc, TypedRouter } from './types';\nimport {\n ErrorRequestHandler,\n RequestHandler,\n NextFunction,\n Request,\n Response,\n json,\n Router,\n} from 'express';\nimport { InputError } from '@backstage/errors';\nimport { middleware as OpenApiValidator } from 'express-openapi-validator';\nimport { OPENAPI_SPEC_ROUTE } from './constants';\nimport { isErrorResult, merge } from 'openapi-merge';\n\ntype PropertyOverrideRequest = Request & {\n [key: symbol]: string;\n};\n\nconst baseUrlSymbol = Symbol();\nconst originalUrlSymbol = Symbol();\n\nfunction validatorErrorTransformer(): ErrorRequestHandler {\n return (error: Error, _: Request, _2: Response, next: NextFunction) => {\n next(new InputError(error.message));\n };\n}\n\nexport function getDefaultRouterMiddleware() {\n return [json()];\n}\n\n/**\n * Given a base url for a plugin, find the given OpenAPI spec for that plugin.\n * @param baseUrl - Plugin base url.\n * @returns OpenAPI spec route for the base url.\n * @public\n */\nexport function getOpenApiSpecRoute(baseUrl: string) {\n return `${baseUrl}${OPENAPI_SPEC_ROUTE}`;\n}\n\n/**\n * Create a router with validation middleware. This is used by typing methods to create an\n * \"OpenAPI router\" with all of the expected validation + metadata.\n * @param spec - Your OpenAPI spec imported as a JSON object.\n * @param validatorOptions - `openapi-express-validator` options to override the defaults.\n * @returns A new express router with validation middleware.\n */\nfunction createRouterWithValidation(\n spec: RequiredDoc,\n options?: {\n validatorOptions?: Partial<Parameters<typeof OpenApiValidator>['0']>;\n middleware?: RequestHandler[];\n },\n): Router {\n const router = PromiseRouter();\n router.use(options?.middleware || getDefaultRouterMiddleware());\n\n /**\n * Middleware to setup the routing for OpenApiValidator. OpenApiValidator expects `req.originalUrl`\n * and `req.baseUrl` to be the full path. We adjust them here to basically be nothing and then\n * revive the old values in the last function in this method. We could instead update `req.path`\n * but that might affect the routing and I'd rather not.\n *\n * TODO: I opened https://github.com/cdimascio/express-openapi-validator/issues/843\n * to track this on the middleware side, but there was a similar ticket, https://github.com/cdimascio/express-openapi-validator/issues/113\n * that has had minimal activity. If that changes, update this to use a new option on their side.\n */\n router.use((req: Request, _, next) => {\n /**\n * Express typings are weird. They don't recognize PropertyOverrideRequest as a valid\n * Request child and try to overload as PathParams. Just cast it here, since we know\n * what we're doing.\n */\n const customRequest = req as PropertyOverrideRequest;\n customRequest[baseUrlSymbol] = customRequest.baseUrl;\n customRequest.baseUrl = '';\n customRequest[originalUrlSymbol] = customRequest.originalUrl;\n customRequest.originalUrl = customRequest.url;\n next();\n });\n\n // TODO: Handle errors by converting from OpenApiValidator errors to known @backstage/errors errors.\n router.use(\n OpenApiValidator({\n validateRequests: {\n coerceTypes: false,\n allowUnknownQueryParameters: false,\n },\n ignoreUndocumented: true,\n validateResponses: false,\n ...options?.validatorOptions,\n apiSpec: spec as any,\n }),\n );\n\n /**\n * Revert `req.baseUrl` and `req.originalUrl` changes. This ensures that any further usage\n * of these variables will be unchanged.\n */\n router.use((req: Request, _, next) => {\n const customRequest = req as PropertyOverrideRequest;\n customRequest.baseUrl = customRequest[baseUrlSymbol];\n customRequest.originalUrl = customRequest[originalUrlSymbol];\n delete customRequest[baseUrlSymbol];\n delete customRequest[originalUrlSymbol];\n next();\n });\n\n // Any errors from the middleware get through here.\n router.use(validatorErrorTransformer());\n\n router.get(OPENAPI_SPEC_ROUTE, async (req, res) => {\n const mergeOutput = merge([\n {\n oas: spec as any,\n pathModification: {\n /**\n * Get the route that this OpenAPI spec is hosted on. The other\n * approach of using the discovery API increases the router constructor\n * significantly and since we're just looking for path and not full URL,\n * this works.\n *\n * If we wanted to add a list of servers, there may be a case for adding\n * discovery API to get an exhaustive list of upstream servers, but that's\n * also not currently supported.\n */\n prepend: req.originalUrl.replace(OPENAPI_SPEC_ROUTE, ''),\n },\n },\n ]);\n if (isErrorResult(mergeOutput)) {\n throw new InputError('Invalid spec defined');\n }\n res.json(mergeOutput.output);\n });\n return router;\n}\n\n/**\n * Create a new OpenAPI router with some default middleware.\n * @param spec - Your OpenAPI spec imported as a JSON object.\n * @param validatorOptions - `openapi-express-validator` options to override the defaults.\n * @returns A new express router with validation middleware.\n * @public\n */\nexport function createValidatedOpenApiRouter<T extends RequiredDoc>(\n spec: T,\n options?: {\n validatorOptions?: Partial<Parameters<typeof OpenApiValidator>['0']>;\n middleware?: RequestHandler[];\n },\n) {\n return createRouterWithValidation(spec, options) as ApiRouter<typeof spec>;\n}\n\n/**\n * Create a new OpenAPI router with some default middleware.\n * @param spec - Your OpenAPI spec imported as a JSON object.\n * @param validatorOptions - `openapi-express-validator` options to override the defaults.\n * @returns A new express router with validation middleware.\n * @public\n */\nexport function createValidatedOpenApiRouterFromGeneratedEndpointMap<\n T extends EndpointMap,\n>(\n spec: RequiredDoc,\n options?: {\n validatorOptions?: Partial<Parameters<typeof OpenApiValidator>['0']>;\n middleware?: RequestHandler[];\n },\n) {\n return createRouterWithValidation(spec, options) as TypedRouter<T>;\n}\n"],"names":["InputError","json","OPENAPI_SPEC_ROUTE","PromiseRouter","OpenApiValidator","merge","isErrorResult"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.cjs.js","sources":["../src/testUtils.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Express } from 'express';\nimport { Server } from 'http';\nimport { Proxy } from './proxy/setup';\n\nconst proxiesToCleanup: Proxy[] = [];\n\n/**\n * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!\n * Setup a server with a custom OpenAPI proxy. This proxy will capture all requests and responses and make sure they\n * conform to the spec.\n * @param app - express server, needed to ensure we have the correct ports for the proxy.\n * @returns - a configured HTTP server that should be used with supertest.\n * @public\n */\nexport async function wrapServer(app: Express): Promise<Server> {\n const proxy = new Proxy();\n proxiesToCleanup.push(proxy);\n await proxy.setup();\n\n const server = app.listen(proxy.forwardTo.port);\n await proxy.initialize(`http://localhost:${proxy.forwardTo.port}`, server);\n\n return { ...server, address: () => new URL(proxy.url) } as any;\n}\n\nlet registered = false;\nfunction registerHooks() {\n if (typeof afterAll !== 'function' || typeof beforeAll !== 'function') {\n return;\n }\n if (registered) {\n return;\n }\n registered = true;\n\n afterAll(() => {\n for (const proxy of proxiesToCleanup) {\n proxy.stop();\n }\n });\n}\n\nregisterHooks();\n\n/**\n * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!\n * Running against supertest, we need some way to hit the optic proxy. This ensures that\n * that happens at runtime when in the context of a `yarn optic capture` command.\n * @param app - Express router that would be passed to supertest's `request`.\n * @returns A wrapper around the express router (or the router untouched) that still works with supertest.\n * @public\n */\nexport const wrapInOpenApiTestServer = (app: Express): Server | Express => {\n if (process.env.OPTIC_PROXY) {\n const server = app.listen(+process.env.PORT!);\n return {\n ...server,\n address: () => new URL(process.env.OPTIC_PROXY!),\n } as any;\n }\n return app;\n};\n"],"names":["Proxy"],"mappings":";;;;AAmBA,MAAM,mBAA4B,EAAC,CAAA;AAUnC,eAAsB,WAAW,GAA+B,EAAA;AAC9D,EAAM,MAAA,KAAA,GAAQ,IAAIA,WAAM,EAAA,CAAA;AACxB,EAAA,gBAAA,CAAiB,KAAK,KAAK,CAAA,CAAA;AAC3B,EAAA,MAAM,MAAM,KAAM,EAAA,CAAA;AAElB,EAAA,MAAM,MAAS,GAAA,GAAA,CAAI,MAAO,CAAA,KAAA,CAAM,UAAU,IAAI,CAAA,CAAA;AAC9C,EAAA,MAAM,MAAM,UAAW,CAAA,CAAA,iBAAA,EAAoB,MAAM,SAAU,CAAA,IAAI,IAAI,MAAM,CAAA,CAAA;AAEzE,EAAO,OAAA,EAAE,GAAG,MAAQ,EAAA,OAAA,EAAS,MAAM,IAAI,GAAA,CAAI,KAAM,CAAA,GAAG,CAAE,EAAA,CAAA;AACxD,CAAA;AAEA,IAAI,UAAa,GAAA,KAAA,CAAA;AACjB,SAAS,aAAgB,GAAA;AACvB,EAAA,IAAI,OAAO,QAAA,KAAa,UAAc,IAAA,OAAO,cAAc,UAAY,EAAA;AACrE,IAAA,OAAA;AAAA,GACF;AACA,EAAA,IAAI,UAAY,EAAA;AACd,IAAA,OAAA;AAAA,GACF;AACA,EAAa,UAAA,GAAA,IAAA,CAAA;AAEb,EAAA,QAAA,CAAS,MAAM;AACb,IAAA,KAAA,MAAW,SAAS,gBAAkB,EAAA;AACpC,MAAA,KAAA,CAAM,IAAK,EAAA,CAAA;AAAA,KACb;AAAA,GACD,CAAA,CAAA;AACH,CAAA;AAEA,aAAc,EAAA,CAAA;AAUD,MAAA,uBAAA,GAA0B,CAAC,GAAmC,KAAA;AACzE,EAAI,IAAA,OAAA,CAAQ,IAAI,WAAa,EAAA;AAC3B,IAAA,MAAM,SAAS,GAAI,CAAA,MAAA,CAAO,CAAC,OAAA,CAAQ,IAAI,IAAK,CAAA,CAAA;AAC5C,IAAO,OAAA;AAAA,MACL,GAAG,MAAA;AAAA,MACH,SAAS,MAAM,IAAI,GAAI,CAAA,OAAA,CAAQ,IAAI,WAAY,CAAA;AAAA,KACjD,CAAA;AAAA,GACF;AACA,EAAO,OAAA,GAAA,CAAA;AACT;;;;;"}
1
+ {"version":3,"file":"testUtils.cjs.js","sources":["../src/testUtils.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Express } from 'express';\nimport { Server } from 'http';\nimport { Proxy } from './proxy/setup';\n\nconst proxiesToCleanup: Proxy[] = [];\n\n/**\n * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!\n * Setup a server with a custom OpenAPI proxy. This proxy will capture all requests and responses and make sure they\n * conform to the spec.\n * @param app - express server, needed to ensure we have the correct ports for the proxy.\n * @returns - a configured HTTP server that should be used with supertest.\n * @public\n */\nexport async function wrapServer(app: Express): Promise<Server> {\n const proxy = new Proxy();\n proxiesToCleanup.push(proxy);\n await proxy.setup();\n\n const server = app.listen(proxy.forwardTo.port);\n await proxy.initialize(`http://localhost:${proxy.forwardTo.port}`, server);\n\n return { ...server, address: () => new URL(proxy.url) } as any;\n}\n\nlet registered = false;\nfunction registerHooks() {\n if (typeof afterAll !== 'function' || typeof beforeAll !== 'function') {\n return;\n }\n if (registered) {\n return;\n }\n registered = true;\n\n afterAll(() => {\n for (const proxy of proxiesToCleanup) {\n proxy.stop();\n }\n });\n}\n\nregisterHooks();\n\n/**\n * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!\n * Running against supertest, we need some way to hit the optic proxy. This ensures that\n * that happens at runtime when in the context of a `yarn optic capture` command.\n * @param app - Express router that would be passed to supertest's `request`.\n * @returns A wrapper around the express router (or the router untouched) that still works with supertest.\n * @public\n */\nexport const wrapInOpenApiTestServer = (app: Express): Server | Express => {\n if (process.env.OPTIC_PROXY) {\n const server = app.listen(+process.env.PORT!);\n return {\n ...server,\n address: () => new URL(process.env.OPTIC_PROXY!),\n } as any;\n }\n return app;\n};\n"],"names":["Proxy"],"mappings":";;;;AAmBA,MAAM,mBAA4B,EAAC;AAUnC,eAAsB,WAAW,GAA+B,EAAA;AAC9D,EAAM,MAAA,KAAA,GAAQ,IAAIA,WAAM,EAAA;AACxB,EAAA,gBAAA,CAAiB,KAAK,KAAK,CAAA;AAC3B,EAAA,MAAM,MAAM,KAAM,EAAA;AAElB,EAAA,MAAM,MAAS,GAAA,GAAA,CAAI,MAAO,CAAA,KAAA,CAAM,UAAU,IAAI,CAAA;AAC9C,EAAA,MAAM,MAAM,UAAW,CAAA,CAAA,iBAAA,EAAoB,MAAM,SAAU,CAAA,IAAI,IAAI,MAAM,CAAA;AAEzE,EAAO,OAAA,EAAE,GAAG,MAAQ,EAAA,OAAA,EAAS,MAAM,IAAI,GAAA,CAAI,KAAM,CAAA,GAAG,CAAE,EAAA;AACxD;AAEA,IAAI,UAAa,GAAA,KAAA;AACjB,SAAS,aAAgB,GAAA;AACvB,EAAA,IAAI,OAAO,QAAA,KAAa,UAAc,IAAA,OAAO,cAAc,UAAY,EAAA;AACrE,IAAA;AAAA;AAEF,EAAA,IAAI,UAAY,EAAA;AACd,IAAA;AAAA;AAEF,EAAa,UAAA,GAAA,IAAA;AAEb,EAAA,QAAA,CAAS,MAAM;AACb,IAAA,KAAA,MAAW,SAAS,gBAAkB,EAAA;AACpC,MAAA,KAAA,CAAM,IAAK,EAAA;AAAA;AACb,GACD,CAAA;AACH;AAEA,aAAc,EAAA;AAUD,MAAA,uBAAA,GAA0B,CAAC,GAAmC,KAAA;AACzE,EAAI,IAAA,OAAA,CAAQ,IAAI,WAAa,EAAA;AAC3B,IAAA,MAAM,SAAS,GAAI,CAAA,MAAA,CAAO,CAAC,OAAA,CAAQ,IAAI,IAAK,CAAA;AAC5C,IAAO,OAAA;AAAA,MACL,GAAG,MAAA;AAAA,MACH,SAAS,MAAM,IAAI,GAAI,CAAA,OAAA,CAAQ,IAAI,WAAY;AAAA,KACjD;AAAA;AAEF,EAAO,OAAA,GAAA;AACT;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-openapi-utils",
3
- "version": "0.2.1-next.1",
3
+ "version": "0.3.0",
4
4
  "description": "OpenAPI typescript support.",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -34,9 +34,9 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@apidevtools/swagger-parser": "^10.1.0",
37
- "@backstage/backend-plugin-api": "1.0.2-next.1",
38
- "@backstage/errors": "1.2.4",
39
- "@backstage/types": "1.1.1",
37
+ "@backstage/backend-plugin-api": "^1.0.2",
38
+ "@backstage/errors": "^1.2.5",
39
+ "@backstage/types": "^1.2.0",
40
40
  "@types/express": "^4.17.6",
41
41
  "@types/express-serve-static-core": "^4.17.5",
42
42
  "ajv": "^8.16.0",
@@ -51,10 +51,17 @@
51
51
  "openapi3-ts": "^3.1.2"
52
52
  },
53
53
  "devDependencies": {
54
- "@backstage/cli": "0.29.0-next.1",
55
- "@backstage/test-utils": "1.7.1-next.0",
54
+ "@backstage/cli": "^0.29.0",
55
+ "@backstage/test-utils": "^1.7.1",
56
56
  "msw": "^1.0.0",
57
57
  "supertest": "^7.0.0"
58
58
  },
59
+ "typesVersions": {
60
+ "*": {
61
+ "index": [
62
+ "dist/index.d.ts"
63
+ ]
64
+ }
65
+ },
59
66
  "module": "dist/index.esm.js"
60
67
  }