@thymian/rules-api-description-validation 0.1.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.
Files changed (30) hide show
  1. package/README.md +11 -0
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +6 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/rules/request-body-conforms-to-schema.rule.d.ts +3 -0
  7. package/dist/rules/request-body-conforms-to-schema.rule.d.ts.map +1 -0
  8. package/dist/rules/request-body-conforms-to-schema.rule.js +24 -0
  9. package/dist/rules/request-body-conforms-to-schema.rule.js.map +1 -0
  10. package/dist/rules/request-headers-conform-to-schema.rule.d.ts +3 -0
  11. package/dist/rules/request-headers-conform-to-schema.rule.d.ts.map +1 -0
  12. package/dist/rules/request-headers-conform-to-schema.rule.js +25 -0
  13. package/dist/rules/request-headers-conform-to-schema.rule.js.map +1 -0
  14. package/dist/rules/request-path-parameters-conform-to-schema.rule.d.ts +3 -0
  15. package/dist/rules/request-path-parameters-conform-to-schema.rule.d.ts.map +1 -0
  16. package/dist/rules/request-path-parameters-conform-to-schema.rule.js +25 -0
  17. package/dist/rules/request-path-parameters-conform-to-schema.rule.js.map +1 -0
  18. package/dist/rules/request-query-parameters-conform-to-schema.rule.d.ts +3 -0
  19. package/dist/rules/request-query-parameters-conform-to-schema.rule.d.ts.map +1 -0
  20. package/dist/rules/request-query-parameters-conform-to-schema.rule.js +25 -0
  21. package/dist/rules/request-query-parameters-conform-to-schema.rule.js.map +1 -0
  22. package/dist/rules/response-body-conforms-to-schema.rule.d.ts +3 -0
  23. package/dist/rules/response-body-conforms-to-schema.rule.d.ts.map +1 -0
  24. package/dist/rules/response-body-conforms-to-schema.rule.js +43 -0
  25. package/dist/rules/response-body-conforms-to-schema.rule.js.map +1 -0
  26. package/dist/rules/response-headers-conform-to-schema.rule.d.ts +3 -0
  27. package/dist/rules/response-headers-conform-to-schema.rule.d.ts.map +1 -0
  28. package/dist/rules/response-headers-conform-to-schema.rule.js +43 -0
  29. package/dist/rules/response-headers-conform-to-schema.rule.js.map +1 -0
  30. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Thymian
2
+
3
+ Add resilience and HTTP conformance to your API development workflow. Thymian is a lightweight, language-agnostic library that helps you build robust APIs.
4
+
5
+ ## @thymian/api-description-validation-rules
6
+
7
+ Validates JSON and YAML format structures.
8
+
9
+ ## Documentation
10
+
11
+ For comprehensive documentation, visit [Thymian Documentation](https://thymian.dev/).
@@ -0,0 +1,4 @@
1
+ import type { RuleSet } from '@thymian/core';
2
+ declare const apiDescriptionValidation: RuleSet;
3
+ export default apiDescriptionValidation;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,QAAA,MAAM,wBAAwB,EAAE,OAG/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ const apiDescriptionValidation = {
2
+ name: '@thymian/rules-api-description-validation',
3
+ pattern: 'rules/**/*.rule.js',
4
+ };
5
+ export default apiDescriptionValidation;
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,wBAAwB,GAAY;IACxC,IAAI,EAAE,2CAA2C;IACjD,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@thymian/core").Rule<Record<PropertyKey, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=request-body-conforms-to-schema.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-body-conforms-to-schema.rule.d.ts","sourceRoot":"","sources":["../../src/rules/request-body-conforms-to-schema.rule.ts"],"names":[],"mappings":";AASA,wBAwCU"}
@@ -0,0 +1,24 @@
1
+ import { constant, httpRule, validateBodyForRequest, } from '@thymian/core';
2
+ export default httpRule('thymian/request-body-must-conform-to-schema')
3
+ .severity('error')
4
+ .type('analytics')
5
+ .description('Request body must conform to the API description schema.')
6
+ .rule((ctx) => ctx.validateHttpTransactions(constant(true), (request, _response, location) => {
7
+ if (typeof location === 'string') {
8
+ return false;
9
+ }
10
+ const transaction = ctx.format.getThymianHttpTransactionById(location.elementId);
11
+ if (!transaction) {
12
+ return false;
13
+ }
14
+ const results = validateBodyForRequest(request.body, transaction.thymianReq);
15
+ const failures = results.filter((r) => r.type === 'assertion-failure');
16
+ if (failures.length > 0) {
17
+ return {
18
+ message: failures.map((f) => f.message).join('\n'),
19
+ };
20
+ }
21
+ return false;
22
+ }))
23
+ .done();
24
+ //# sourceMappingURL=request-body-conforms-to-schema.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-body-conforms-to-schema.rule.js","sourceRoot":"","sources":["../../src/rules/request-body-conforms-to-schema.rule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAGR,QAAQ,EAER,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,eAAe,QAAQ,CAAC,6CAA6C,CAAC;KACnE,QAAQ,CAAC,OAAO,CAAC;KACjB,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,0DAA0D,CAAC;KACvE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACZ,GAAG,CAAC,wBAAwB,CAC1B,QAAQ,CAAC,IAAI,CAAC,EACd,CACE,OAAoB,EACpB,SAAuB,EACvB,QAA+B,EAC/B,EAAE;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAC1D,QAAQ,CAAC,SAAS,CACnB,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,sBAAsB,CACpC,OAAO,CAAC,IAAI,EACZ,WAAW,CAAC,UAAU,CACvB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CACF,CACF;KACA,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@thymian/core").Rule<Record<PropertyKey, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=request-headers-conform-to-schema.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-headers-conform-to-schema.rule.d.ts","sourceRoot":"","sources":["../../src/rules/request-headers-conform-to-schema.rule.ts"],"names":[],"mappings":";AASA,wBA2CU"}
@@ -0,0 +1,25 @@
1
+ import { constant, httpRule, validateRequestHeaders, } from '@thymian/core';
2
+ export default httpRule('thymian/request-headers-must-conform-to-schema')
3
+ .severity('error')
4
+ .type('analytics')
5
+ .description('Request headers must conform to the API description schema. Checks for missing required headers, additional undocumented headers, and validates existing headers against their schema.')
6
+ .summary('Request headers must conform to the API description schema.')
7
+ .rule((ctx) => ctx.validateHttpTransactions(constant(true), (request, _response, location) => {
8
+ if (typeof location === 'string') {
9
+ return false;
10
+ }
11
+ const transaction = ctx.format.getThymianHttpTransactionById(location.elementId);
12
+ if (!transaction) {
13
+ return false;
14
+ }
15
+ const results = validateRequestHeaders(request.headers ?? {}, transaction.thymianReq);
16
+ const failures = results.filter((r) => r.type === 'assertion-failure');
17
+ if (failures.length > 0) {
18
+ return {
19
+ message: failures.map((f) => f.message).join('\n'),
20
+ };
21
+ }
22
+ return false;
23
+ }))
24
+ .done();
25
+ //# sourceMappingURL=request-headers-conform-to-schema.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-headers-conform-to-schema.rule.js","sourceRoot":"","sources":["../../src/rules/request-headers-conform-to-schema.rule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAGR,QAAQ,EAER,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,eAAe,QAAQ,CAAC,gDAAgD,CAAC;KACtE,QAAQ,CAAC,OAAO,CAAC;KACjB,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CACV,wLAAwL,CACzL;KACA,OAAO,CAAC,6DAA6D,CAAC;KACtE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACZ,GAAG,CAAC,wBAAwB,CAC1B,QAAQ,CAAC,IAAI,CAAC,EACd,CACE,OAAoB,EACpB,SAAuB,EACvB,QAA+B,EAC/B,EAAE;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAC1D,QAAQ,CAAC,SAAS,CACnB,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,sBAAsB,CACpC,OAAO,CAAC,OAAO,IAAI,EAAE,EACrB,WAAW,CAAC,UAAU,CACvB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CACF,CACF;KACA,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@thymian/core").Rule<Record<PropertyKey, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=request-path-parameters-conform-to-schema.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-path-parameters-conform-to-schema.rule.d.ts","sourceRoot":"","sources":["../../src/rules/request-path-parameters-conform-to-schema.rule.ts"],"names":[],"mappings":";AASA,wBA6CU"}
@@ -0,0 +1,25 @@
1
+ import { constant, httpRule, validateRequestPathParameters, } from '@thymian/core';
2
+ export default httpRule('thymian/request-path-parameters-must-conform-to-schema')
3
+ .severity('error')
4
+ .type('analytics')
5
+ .description('Request path parameters must conform to the API description schema. Validates extracted path parameters against their schema definitions.')
6
+ .summary('Request path parameters must conform to the API description schema')
7
+ .rule((ctx) => ctx.validateHttpTransactions(constant(true), (request, _response, location) => {
8
+ if (typeof location === 'string') {
9
+ return false;
10
+ }
11
+ const transaction = ctx.format.getThymianHttpTransactionById(location.elementId);
12
+ if (!transaction) {
13
+ return false;
14
+ }
15
+ const results = validateRequestPathParameters(request.path, transaction.thymianReq);
16
+ const failures = results.filter((r) => r.type === 'assertion-failure');
17
+ if (failures.length > 0) {
18
+ return {
19
+ message: failures.map((f) => f.message).join('\n'),
20
+ };
21
+ }
22
+ return false;
23
+ }))
24
+ .done();
25
+ //# sourceMappingURL=request-path-parameters-conform-to-schema.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-path-parameters-conform-to-schema.rule.js","sourceRoot":"","sources":["../../src/rules/request-path-parameters-conform-to-schema.rule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAGR,QAAQ,EAER,6BAA6B,GAC9B,MAAM,eAAe,CAAC;AAEvB,eAAe,QAAQ,CACrB,wDAAwD,CACzD;KACE,QAAQ,CAAC,OAAO,CAAC;KACjB,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CACV,2IAA2I,CAC5I;KACA,OAAO,CAAC,oEAAoE,CAAC;KAC7E,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACZ,GAAG,CAAC,wBAAwB,CAC1B,QAAQ,CAAC,IAAI,CAAC,EACd,CACE,OAAoB,EACpB,SAAuB,EACvB,QAA+B,EAC/B,EAAE;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAC1D,QAAQ,CAAC,SAAS,CACnB,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,6BAA6B,CAC3C,OAAO,CAAC,IAAI,EACZ,WAAW,CAAC,UAAU,CACvB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CACF,CACF;KACA,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@thymian/core").Rule<Record<PropertyKey, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=request-query-parameters-conform-to-schema.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-query-parameters-conform-to-schema.rule.d.ts","sourceRoot":"","sources":["../../src/rules/request-query-parameters-conform-to-schema.rule.ts"],"names":[],"mappings":";AASA,wBA+CU"}
@@ -0,0 +1,25 @@
1
+ import { constant, httpRule, validateRequestQueryParameters, } from '@thymian/core';
2
+ export default httpRule('thymian/request-query-parameters-must-conform-to-schema')
3
+ .severity('error')
4
+ .type('analytics')
5
+ .description('Request query parameters must conform to the API description schema. Checks for missing required parameters, additional undocumented parameters, and validates existing parameters against their schema.')
6
+ .summary('Request query parameters must conform to the API description schema')
7
+ .rule((ctx) => ctx.validateHttpTransactions(constant(true), (request, _response, location) => {
8
+ if (typeof location === 'string') {
9
+ return false;
10
+ }
11
+ const transaction = ctx.format.getThymianHttpTransactionById(location.elementId);
12
+ if (!transaction) {
13
+ return false;
14
+ }
15
+ const results = validateRequestQueryParameters(request.path, transaction.thymianReq);
16
+ const failures = results.filter((r) => r.type === 'assertion-failure');
17
+ if (failures.length > 0) {
18
+ return {
19
+ message: failures.map((f) => f.message).join('\n'),
20
+ };
21
+ }
22
+ return false;
23
+ }))
24
+ .done();
25
+ //# sourceMappingURL=request-query-parameters-conform-to-schema.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-query-parameters-conform-to-schema.rule.js","sourceRoot":"","sources":["../../src/rules/request-query-parameters-conform-to-schema.rule.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAGR,QAAQ,EAER,8BAA8B,GAC/B,MAAM,eAAe,CAAC;AAEvB,eAAe,QAAQ,CACrB,yDAAyD,CAC1D;KACE,QAAQ,CAAC,OAAO,CAAC;KACjB,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CACV,0MAA0M,CAC3M;KACA,OAAO,CACN,qEAAqE,CACtE;KACA,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACZ,GAAG,CAAC,wBAAwB,CAC1B,QAAQ,CAAC,IAAI,CAAC,EACd,CACE,OAAoB,EACpB,SAAuB,EACvB,QAA+B,EAC/B,EAAE;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAC1D,QAAQ,CAAC,SAAS,CACnB,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,8BAA8B,CAC5C,OAAO,CAAC,IAAI,EACZ,WAAW,CAAC,UAAU,CACvB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CACF,CACF;KACA,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@thymian/core").Rule<Record<PropertyKey, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=response-body-conforms-to-schema.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-body-conforms-to-schema.rule.d.ts","sourceRoot":"","sources":["../../src/rules/response-body-conforms-to-schema.rule.ts"],"names":[],"mappings":";AAgBA,wBAyEU"}
@@ -0,0 +1,43 @@
1
+ import { httpRule, or, singleTestCase, statusCodeRange, successfulStatusCode, validateBodyForResponse, } from '@thymian/core';
2
+ export default httpRule('thymian/response-body-must-conforms-to-schema')
3
+ .severity('error')
4
+ .type('test', 'analytics')
5
+ .description('Response body for 2xx and 4xx responses must conform to the API description schema.')
6
+ .summary('Response body must conform to the API description schema.')
7
+ .rule((ctx) => ctx.validateHttpTransactions(or(successfulStatusCode(), statusCodeRange(400, 499)), (_request, response, location) => {
8
+ if (typeof location === 'string') {
9
+ return false;
10
+ }
11
+ const transaction = ctx.format.getThymianHttpTransactionById(location.elementId);
12
+ if (!transaction) {
13
+ return false;
14
+ }
15
+ const results = validateBodyForResponse(response.body, transaction.thymianRes);
16
+ const failures = results.filter((r) => r.type === 'assertion-failure');
17
+ if (failures.length > 0) {
18
+ return {
19
+ message: failures.map((f) => f.message).join('\n'),
20
+ };
21
+ }
22
+ return false;
23
+ }))
24
+ .overrideTest(async (ctx) => {
25
+ const testResult = await ctx.runHttpTest(singleTestCase()
26
+ .forTransactionsWith(or(successfulStatusCode(), statusCodeRange(400, 499)))
27
+ .run({ checkBody: true })
28
+ .done());
29
+ return testResult.cases
30
+ .filter((testCase) => testCase.status === 'failed')
31
+ .flatMap((testCase) => {
32
+ const failures = testCase.results.filter((r) => r.type === 'assertion-failure' && !!r.transaction);
33
+ return failures.map((failure) => ({
34
+ location: {
35
+ elementType: 'edge',
36
+ elementId: failure.transaction.transactionId,
37
+ },
38
+ message: failure.message,
39
+ }));
40
+ });
41
+ })
42
+ .done();
43
+ //# sourceMappingURL=response-body-conforms-to-schema.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-body-conforms-to-schema.rule.js","sourceRoot":"","sources":["../../src/rules/response-body-conforms-to-schema.rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,QAAQ,EAER,EAAE,EAGF,cAAc,EACd,eAAe,EACf,oBAAoB,EAEpB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AAEvB,eAAe,QAAQ,CAAC,+CAA+C,CAAC;KACrE,QAAQ,CAAC,OAAO,CAAC;KACjB,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;KACzB,WAAW,CACV,qFAAqF,CACtF;KACA,OAAO,CAAC,2DAA2D,CAAC;KACpE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACZ,GAAG,CAAC,wBAAwB,CAC1B,EAAE,CAAC,oBAAoB,EAAE,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EACrD,CACE,QAAqB,EACrB,QAAsB,EACtB,QAA+B,EAC/B,EAAE;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAC1D,QAAQ,CAAC,SAAS,CACnB,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,uBAAuB,CACrC,QAAQ,CAAC,IAAI,EACb,WAAW,CAAC,UAAU,CACvB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CACF,CACF;KACA,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC1B,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,WAAW,CACtC,cAAc,EAAE;SACb,mBAAmB,CAClB,EAAE,CAAC,oBAAoB,EAAE,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CACtD;SACA,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SACxB,IAAI,EAAE,CACV,CAAC;IAEF,OAAO,UAAU,CAAC,KAAK;SACpB,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC;SAClD,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CACtC,CACE,CAAC,EAE0D,EAAE,CAC7D,CAAC,CAAC,IAAI,KAAK,mBAAmB,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CACpD,CAAC;QAEF,OAAO,QAAQ,CAAC,GAAG,CAAgB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC/C,QAAQ,EAAE;gBACR,WAAW,EAAE,MAAM;gBACnB,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,aAAa;aAC7C;YACD,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC,CAAC,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;KACD,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("@thymian/core").Rule<Record<PropertyKey, unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=response-headers-conform-to-schema.rule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-headers-conform-to-schema.rule.d.ts","sourceRoot":"","sources":["../../src/rules/response-headers-conform-to-schema.rule.ts"],"names":[],"mappings":";AAgBA,wBAyEU"}
@@ -0,0 +1,43 @@
1
+ import { httpRule, or, singleTestCase, statusCodeRange, successfulStatusCode, validateHeaders, } from '@thymian/core';
2
+ export default httpRule('thymian/response-headers-must-conform-to-schema')
3
+ .severity('error')
4
+ .type('test', 'analytics')
5
+ .description('Response headers must conform to the API description schema. Checks for missing required headers, additional undocumented headers, and validates existing headers against their schema.')
6
+ .summary('Response headers must conform to the API description schema')
7
+ .rule((ctx) => ctx.validateHttpTransactions(or(successfulStatusCode(), statusCodeRange(400, 499)), (_request, response, location) => {
8
+ if (typeof location === 'string') {
9
+ return false;
10
+ }
11
+ const transaction = ctx.format.getThymianHttpTransactionById(location.elementId);
12
+ if (!transaction) {
13
+ return false;
14
+ }
15
+ const results = validateHeaders(response.headers, transaction.thymianRes);
16
+ const failures = results.filter((r) => r.type === 'assertion-failure');
17
+ if (failures.length > 0) {
18
+ return {
19
+ message: failures.map((f) => f.message).join('\n'),
20
+ };
21
+ }
22
+ return false;
23
+ }))
24
+ .overrideTest(async (ctx) => {
25
+ const testResult = await ctx.runHttpTest(singleTestCase()
26
+ .forTransactionsWith(or(successfulStatusCode(), statusCodeRange(400, 499)))
27
+ .run({ checkHeaders: true })
28
+ .done());
29
+ return testResult.cases
30
+ .filter((testCase) => testCase.status === 'failed')
31
+ .flatMap((testCase) => {
32
+ const failures = testCase.results.filter((r) => r.type === 'assertion-failure' && !!r.transaction);
33
+ return failures.map((failure) => ({
34
+ location: {
35
+ elementType: 'edge',
36
+ elementId: failure.transaction.transactionId,
37
+ },
38
+ message: failure.message,
39
+ }));
40
+ });
41
+ })
42
+ .done();
43
+ //# sourceMappingURL=response-headers-conform-to-schema.rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-headers-conform-to-schema.rule.js","sourceRoot":"","sources":["../../src/rules/response-headers-conform-to-schema.rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,QAAQ,EAER,EAAE,EAGF,cAAc,EACd,eAAe,EACf,oBAAoB,EAEpB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,eAAe,QAAQ,CAAC,iDAAiD,CAAC;KACvE,QAAQ,CAAC,OAAO,CAAC;KACjB,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;KACzB,WAAW,CACV,yLAAyL,CAC1L;KACA,OAAO,CAAC,6DAA6D,CAAC;KACtE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACZ,GAAG,CAAC,wBAAwB,CAC1B,EAAE,CAAC,oBAAoB,EAAE,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EACrD,CACE,QAAqB,EACrB,QAAsB,EACtB,QAA+B,EAC/B,EAAE;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,6BAA6B,CAC1D,QAAQ,CAAC,SAAS,CACnB,CAAC;IAEF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,eAAe,CAC7B,QAAQ,CAAC,OAAO,EAChB,WAAW,CAAC,UAAU,CACvB,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CACF,CACF;KACA,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC1B,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,WAAW,CACtC,cAAc,EAAE;SACb,mBAAmB,CAClB,EAAE,CAAC,oBAAoB,EAAE,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CACtD;SACA,GAAG,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;SAC3B,IAAI,EAAE,CACV,CAAC;IAEF,OAAO,UAAU,CAAC,KAAK;SACpB,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC;SAClD,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CACtC,CACE,CAAC,EAE0D,EAAE,CAC7D,CAAC,CAAC,IAAI,KAAK,mBAAmB,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CACpD,CAAC;QAEF,OAAO,QAAQ,CAAC,GAAG,CAAgB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC/C,QAAQ,EAAE;gBACR,WAAW,EAAE,MAAM;gBACnB,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,aAAa;aAC7C;YACD,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC,CAAC,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;KACD,IAAI,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@thymian/rules-api-description-validation",
3
+ "version": "0.1.0",
4
+ "license": "AGPL-3.0-only",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "type": "module",
12
+ "exports": {
13
+ "./package.json": "./package.json",
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "!**/*.tsbuildinfo"
23
+ ],
24
+ "dependencies": {
25
+ "@thymian/core": "0.1.0"
26
+ }
27
+ }