@backstage/backend-openapi-utils 0.1.19-next.0 → 0.2.0-next.1

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,18 @@
1
1
  # @backstage/backend-openapi-utils
2
2
 
3
+ ## 0.2.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - 66af016: Improved support for OpenAPI validation during Jest tests. Now, OpenAPI validation can happen as you are writing your Jest tests - you no longer have to run `repo schema openapi test`.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @backstage/backend-plugin-api@1.0.1-next.1
13
+ - @backstage/errors@1.2.4
14
+ - @backstage/types@1.1.1
15
+
3
16
  ## 0.1.19-next.0
4
17
 
5
18
  ### Patch Changes
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ const OPENAPI_SPEC_ROUTE = "/openapi.json";
4
+
5
+ exports.OPENAPI_SPEC_ROUTE = OPENAPI_SPEC_ROUTE;
6
+ //# sourceMappingURL=constants.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.cjs.js","sources":["../src/constants.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\n/**\n * The route that all OpenAPI specs should be served from.\n * @public\n */\nexport const OPENAPI_SPEC_ROUTE = '/openapi.json';\n"],"names":[],"mappings":";;AAoBO,MAAM,kBAAqB,GAAA;;;;"}
package/dist/index.cjs.js CHANGED
@@ -1,106 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var PromiseRouter = require('express-promise-router');
4
- var express = require('express');
5
- var errors = require('@backstage/errors');
6
- var expressOpenapiValidator = require('express-openapi-validator');
7
- var openapiMerge = require('openapi-merge');
3
+ var index = require('./types/index.cjs.js');
4
+ var stub = require('./stub.cjs.js');
5
+ var testUtils = require('./testUtils.cjs.js');
8
6
 
9
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
10
7
 
11
- var PromiseRouter__default = /*#__PURE__*/_interopDefaultCompat(PromiseRouter);
12
8
 
13
- var index = /*#__PURE__*/Object.freeze({
14
- __proto__: null
15
- });
16
-
17
- const OPENAPI_SPEC_ROUTE = "/openapi.json";
18
-
19
- const baseUrlSymbol = Symbol();
20
- const originalUrlSymbol = Symbol();
21
- function validatorErrorTransformer() {
22
- return (error, _, _2, next) => {
23
- next(new errors.InputError(error.message));
24
- };
25
- }
26
- function getDefaultRouterMiddleware() {
27
- return [express.json()];
28
- }
29
- function getOpenApiSpecRoute(baseUrl) {
30
- return `${baseUrl}${OPENAPI_SPEC_ROUTE}`;
31
- }
32
- function createValidatedOpenApiRouter(spec, options) {
33
- const router = PromiseRouter__default.default();
34
- router.use(options?.middleware || getDefaultRouterMiddleware());
35
- router.use((req, _, next) => {
36
- const customRequest = req;
37
- customRequest[baseUrlSymbol] = customRequest.baseUrl;
38
- customRequest.baseUrl = "";
39
- customRequest[originalUrlSymbol] = customRequest.originalUrl;
40
- customRequest.originalUrl = customRequest.url;
41
- next();
42
- });
43
- router.use(
44
- expressOpenapiValidator.middleware({
45
- validateRequests: {
46
- coerceTypes: false,
47
- allowUnknownQueryParameters: false
48
- },
49
- ignoreUndocumented: true,
50
- validateResponses: false,
51
- ...options?.validatorOptions,
52
- apiSpec: spec
53
- })
54
- );
55
- router.use((req, _, next) => {
56
- const customRequest = req;
57
- customRequest.baseUrl = customRequest[baseUrlSymbol];
58
- customRequest.originalUrl = customRequest[originalUrlSymbol];
59
- delete customRequest[baseUrlSymbol];
60
- delete customRequest[originalUrlSymbol];
61
- next();
62
- });
63
- router.use(validatorErrorTransformer());
64
- router.get(OPENAPI_SPEC_ROUTE, async (req, res) => {
65
- const mergeOutput = openapiMerge.merge([
66
- {
67
- oas: spec,
68
- pathModification: {
69
- /**
70
- * Get the route that this OpenAPI spec is hosted on. The other
71
- * approach of using the discovery API increases the router constructor
72
- * significantly and since we're just looking for path and not full URL,
73
- * this works.
74
- *
75
- * If we wanted to add a list of servers, there may be a case for adding
76
- * discovery API to get an exhaustive list of upstream servers, but that's
77
- * also not currently supported.
78
- */
79
- prepend: req.originalUrl.replace(OPENAPI_SPEC_ROUTE, "")
80
- }
81
- }
82
- ]);
83
- if (openapiMerge.isErrorResult(mergeOutput)) {
84
- throw new errors.InputError("Invalid spec defined");
85
- }
86
- res.json(mergeOutput.output);
87
- });
88
- return router;
89
- }
90
-
91
- const wrapInOpenApiTestServer = (app) => {
92
- if (process.env.OPTIC_PROXY) {
93
- const server = app.listen(+process.env.PORT);
94
- return {
95
- ...server,
96
- address: () => new URL(process.env.OPTIC_PROXY)
97
- };
98
- }
99
- return app;
100
- };
101
-
102
- exports.createValidatedOpenApiRouter = createValidatedOpenApiRouter;
103
- exports.getOpenApiSpecRoute = getOpenApiSpecRoute;
104
9
  exports.internal = index;
105
- exports.wrapInOpenApiTestServer = wrapInOpenApiTestServer;
10
+ exports.createValidatedOpenApiRouter = stub.createValidatedOpenApiRouter;
11
+ exports.getOpenApiSpecRoute = stub.getOpenApiSpecRoute;
12
+ exports.wrapInOpenApiTestServer = testUtils.wrapInOpenApiTestServer;
13
+ exports.wrapServer = testUtils.wrapServer;
106
14
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/stub.ts","../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 */\n\n/**\n * The route that all OpenAPI specs should be served from.\n * @public\n */\nexport const OPENAPI_SPEC_ROUTE = '/openapi.json';\n","/*\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","/*\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';\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":["InputError","json","PromiseRouter","OpenApiValidator","merge","isErrorResult"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { JSONSchema, FromSchema } from 'json-schema-to-ts';
2
3
  import { ReferenceObject, OpenAPIObject, ContentObject, RequestBodyObject, ResponseObject, ParameterObject, SchemaObject } from 'openapi3-ts';
3
4
  import core from 'express-serve-static-core';
@@ -530,6 +531,15 @@ declare function createValidatedOpenApiRouter<T extends RequiredDoc>(spec: T, op
530
531
  middleware?: RequestHandler[];
531
532
  }): ApiRouter<T>;
532
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>;
533
543
  /**
534
544
  * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!!
535
545
  * Running against supertest, we need some way to hit the optic proxy. This ensures that
@@ -540,4 +550,4 @@ declare function createValidatedOpenApiRouter<T extends RequiredDoc>(spec: T, op
540
550
  */
541
551
  declare const wrapInOpenApiTestServer: (app: Express) => Server | Express;
542
552
 
543
- export { type ApiRouter, type CookieParameters, type HeaderParameters, type PathParameters, type QueryParameters, type Request, type Response, createValidatedOpenApiRouter, getOpenApiSpecRoute, index_d as internal, wrapInOpenApiTestServer };
553
+ export { type ApiRouter, type CookieParameters, type HeaderParameters, type PathParameters, type QueryParameters, type Request, type Response, createValidatedOpenApiRouter, getOpenApiSpecRoute, index_d as internal, wrapInOpenApiTestServer, wrapServer };
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ var mockttp = require('mockttp');
4
+ var validation = require('../schema/validation.cjs.js');
5
+ var getPort = require('get-port');
6
+
7
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
8
+
9
+ function _interopNamespaceCompat(e) {
10
+ if (e && typeof e === 'object' && 'default' in e) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var mockttp__namespace = /*#__PURE__*/_interopNamespaceCompat(mockttp);
28
+ var getPort__default = /*#__PURE__*/_interopDefaultCompat(getPort);
29
+
30
+ class Proxy {
31
+ server;
32
+ #openRequests = {};
33
+ requestResponsePairs = /* @__PURE__ */ new Map();
34
+ validator;
35
+ forwardTo = { port: 0 };
36
+ express = { server: void 0 };
37
+ constructor() {
38
+ this.server = mockttp__namespace.getLocal();
39
+ this.validator = new validation.OpenApiProxyValidator();
40
+ }
41
+ async setup() {
42
+ await this.server.start();
43
+ this.forwardTo.port = await getPort__default.default();
44
+ this.server.forAnyRequest().thenForwardTo(`http://localhost:${this.forwardTo.port}`);
45
+ await this.server.on("request", (request) => {
46
+ this.#openRequests[request.id] = request;
47
+ });
48
+ await this.server.on("response", (response) => {
49
+ const request = this.#openRequests[response.id];
50
+ if (request) {
51
+ this.requestResponsePairs.set(request, response);
52
+ }
53
+ delete this.#openRequests[response.id];
54
+ this.validator.validate(request, response);
55
+ });
56
+ }
57
+ async initialize(url, server) {
58
+ await this.validator.initialize(`${url}/openapi.json`);
59
+ this.express.server = server;
60
+ }
61
+ stop() {
62
+ if (Object.keys(this.#openRequests).length > 0) {
63
+ throw new Error("There are still open requests");
64
+ }
65
+ this.server.stop();
66
+ this.express.server?.close();
67
+ }
68
+ get url() {
69
+ return this.server.proxyEnv.HTTP_PROXY;
70
+ }
71
+ }
72
+
73
+ exports.Proxy = Proxy;
74
+ //# sourceMappingURL=setup.cjs.js.map
@@ -0,0 +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;;;;"}
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ var utils = require('./utils.cjs.js');
4
+
5
+ class OperationError extends Error {
6
+ constructor(operation, message) {
7
+ super(
8
+ `["${operation.method.toLocaleUpperCase("en-US")} ${operation.path}"] ${message}`
9
+ );
10
+ }
11
+ }
12
+ class OperationResponseError extends Error {
13
+ constructor(operation, response, message) {
14
+ super(
15
+ `["${operation.method.toLocaleUpperCase("en-US")} ${operation.path}" (${response.status})]: ${message}`
16
+ );
17
+ }
18
+ }
19
+ class OperationParsingError extends OperationError {
20
+ constructor(operation, type, errors) {
21
+ super(
22
+ operation,
23
+ `${type} validation failed.
24
+ - ${errors.map(utils.humanifyAjvError).join("\n - ")}`
25
+ );
26
+ }
27
+ }
28
+ class OperationParsingResponseError extends OperationResponseError {
29
+ constructor(operation, response, type, errors) {
30
+ super(
31
+ operation,
32
+ response,
33
+ `${type} validation failed.
34
+ - ${errors.map(utils.humanifyAjvError).join("\n - ")}`
35
+ );
36
+ }
37
+ }
38
+
39
+ exports.OperationError = OperationError;
40
+ exports.OperationParsingError = OperationParsingError;
41
+ exports.OperationParsingResponseError = OperationParsingResponseError;
42
+ exports.OperationResponseError = OperationResponseError;
43
+ //# sourceMappingURL=errors.cjs.js.map
@@ -0,0 +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;;;;;;;"}