@thymian/plugin-http-analyzer 0.0.0-PLACEHOLDER
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/README.md +59 -0
- package/dist/analytics-api-context.d.ts +31 -0
- package/dist/analytics-api-context.d.ts.map +1 -0
- package/dist/analytics-api-context.js +167 -0
- package/dist/analytics-api-context.js.map +1 -0
- package/dist/db/http-filter-to-groupby-clause.d.ts +6 -0
- package/dist/db/http-filter-to-groupby-clause.d.ts.map +1 -0
- package/dist/db/http-filter-to-groupby-clause.js +64 -0
- package/dist/db/http-filter-to-groupby-clause.js.map +1 -0
- package/dist/db/http-filter-to-where-clause.d.ts +5 -0
- package/dist/db/http-filter-to-where-clause.d.ts.map +1 -0
- package/dist/db/http-filter-to-where-clause.js +188 -0
- package/dist/db/http-filter-to-where-clause.js.map +1 -0
- package/dist/db/http-transaction-repository.d.ts +15 -0
- package/dist/db/http-transaction-repository.d.ts.map +1 -0
- package/dist/db/http-transaction-repository.js +2 -0
- package/dist/db/http-transaction-repository.js.map +1 -0
- package/dist/db/init.sql +87 -0
- package/dist/db/sqlite-http-transaction-repository.d.ts +32 -0
- package/dist/db/sqlite-http-transaction-repository.d.ts.map +1 -0
- package/dist/db/sqlite-http-transaction-repository.js +472 -0
- package/dist/db/sqlite-http-transaction-repository.js.map +1 -0
- package/dist/db/types.d.ts +5 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/db/utils.d.ts +6 -0
- package/dist/db/utils.d.ts.map +1 -0
- package/dist/db/utils.js +2 -0
- package/dist/db/utils.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +136 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!-- HEADER:START - Do not remove or modify this section -->
|
|
2
|
+
<div align="center">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/thymianofficial/thymian/main/astro-docs/src/assets/logo.svg" alt="Thymian Logo" width="200"/>
|
|
4
|
+
|
|
5
|
+
# Thymian
|
|
6
|
+
|
|
7
|
+
**Add resilience and HTTP conformance to your API development workflow**
|
|
8
|
+
|
|
9
|
+
[](https://github.com/thymianofficial/thymian/blob/main/LICENSE)
|
|
10
|
+
[](https://github.com/thymianofficial/thymian/actions/workflows/ci.yaml)
|
|
11
|
+
[](https://thymian.dev)
|
|
12
|
+
[](https://discord.gg/TRSwCxbz9f)
|
|
13
|
+
[](https://www.reddit.com/r/ThymianOfficial/)
|
|
14
|
+
[](https://x.com/thymiandev)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
<!-- HEADER:END -->
|
|
18
|
+
|
|
19
|
+
## @thymian/plugin-http-analyzer
|
|
20
|
+
|
|
21
|
+
Captures and stores HTTP transactions in a SQLite database for post-facto analysis. Powers `thymian analyze` by running validation rules against recorded production traffic, detecting HTTP conformance issues in real deployments where proxies, CDNs, and load balancers affect HTTP semantics.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @thymian/plugin-http-analyzer
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **Getting started with Thymian?** See the [main Thymian package](https://www.npmjs.com/package/thymian) for quick installation and first-run instructions.
|
|
30
|
+
|
|
31
|
+
<!-- FOOTER:START - Do not remove or modify this section -->
|
|
32
|
+
|
|
33
|
+
## 📚 Documentation
|
|
34
|
+
|
|
35
|
+
- **[Getting Started](https://thymian.dev/introduction/getting-started)** - Set up Thymian in minutes
|
|
36
|
+
- **[Documentation](https://thymian.dev)** - Comprehensive guides and API reference
|
|
37
|
+
- **[CLI Reference](https://thymian.dev/references/cli)** - Complete CLI command documentation
|
|
38
|
+
- **[HTTP Rules](https://thymian.dev/guides/http-rules/how-to-use-rules)** - Learn about HTTP conformance validation
|
|
39
|
+
|
|
40
|
+
## 🏢 Enterprise Support
|
|
41
|
+
|
|
42
|
+
Get professional consulting and dedicated support from the creators of Thymian. We offer:
|
|
43
|
+
|
|
44
|
+
- API design and governance strategies
|
|
45
|
+
- HTTP standards compliance auditing
|
|
46
|
+
- Custom plugin development
|
|
47
|
+
- Custom rule development
|
|
48
|
+
- Team training and workshops
|
|
49
|
+
|
|
50
|
+
**[Learn more about Enterprise Support](https://thymian.dev/enterprise)** | **Email: [support@thymian.dev](mailto:support@thymian.dev)**
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<div align="center">
|
|
55
|
+
|
|
56
|
+
**Shipped with 🌱 by [qupaya](https://qupaya.com)**
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
<!-- FOOTER:END -->
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type AnalyzeContext, type CapturedTrace, type CapturedTransaction, type CommonHttpRequest, type CommonHttpResponse, type HttpFilterExpression, type HttpParticipantRole, type HttpRequest, type HttpResponse, type Logger, type ReportFn, type RuleFnResult, type RuleViolation, type RuleViolationLocation, ThymianFormat, type ValidationFn } from '@thymian/core';
|
|
2
|
+
import type { HttpTransactionRepository } from './db/http-transaction-repository.js';
|
|
3
|
+
export declare class AnalyticsApiContext implements AnalyzeContext {
|
|
4
|
+
readonly repository: HttpTransactionRepository;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
readonly format: ThymianFormat;
|
|
7
|
+
readonly report: ReportFn;
|
|
8
|
+
private readonly violations;
|
|
9
|
+
private readonly roles?;
|
|
10
|
+
private readonly skippedOrigins;
|
|
11
|
+
constructor(repository: HttpTransactionRepository, logger: Logger, format: ThymianFormat, reportFn?: ReportFn, roles?: HttpParticipantRole[], skippedOrigins?: string[]);
|
|
12
|
+
reportViolation(violation: RuleViolation): void;
|
|
13
|
+
private addOriginsToFilter;
|
|
14
|
+
validateCommonHttpTransactions(filter: HttpFilterExpression, validate?: ValidationFn<[
|
|
15
|
+
CommonHttpRequest,
|
|
16
|
+
CommonHttpResponse,
|
|
17
|
+
RuleViolationLocation
|
|
18
|
+
]> | HttpFilterExpression): Promise<RuleFnResult> | RuleFnResult;
|
|
19
|
+
validateGroupedCommonHttpTransactions(filter: HttpFilterExpression, groupBy: HttpFilterExpression, validationFn: ValidationFn<[
|
|
20
|
+
string,
|
|
21
|
+
[
|
|
22
|
+
CommonHttpRequest,
|
|
23
|
+
CommonHttpResponse,
|
|
24
|
+
RuleViolationLocation
|
|
25
|
+
][]
|
|
26
|
+
], RuleViolation | undefined>): Promise<RuleFnResult> | RuleFnResult;
|
|
27
|
+
validateHttpTransactions(filter: HttpFilterExpression, validation?: ValidationFn<[HttpRequest, HttpResponse, RuleViolationLocation]> | HttpFilterExpression): Promise<RuleFnResult> | RuleFnResult;
|
|
28
|
+
validateCapturedHttpTransactions(filter: HttpFilterExpression, validate: ValidationFn<[CapturedTransaction, string]>): Promise<RuleFnResult> | RuleFnResult;
|
|
29
|
+
validateCapturedHttpTraces(validate: ValidationFn<[CapturedTrace, string]>): RuleFnResult;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=analytics-api-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics-api-context.d.ts","sourceRoot":"","sources":["../src/analytics-api-context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,aAAa,EAElB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAEvB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAEhB,KAAK,YAAY,EAGjB,KAAK,MAAM,EAIX,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,aAAa,EAEb,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,qBAAa,mBAAoB,YAAW,cAAc;IAQtD,QAAQ,CAAC,UAAU,EAAE,yBAAyB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAW;gBAG/B,UAAU,EAAE,yBAAyB,EAC7B,MAAM,EAAE,MAAM,EAC/B,MAAM,EAAE,aAAa,EACrB,QAAQ,CAAC,EAAE,QAAQ,EACnB,KAAK,CAAC,EAAE,mBAAmB,EAAE,EAC7B,cAAc,CAAC,EAAE,MAAM,EAAE;IA2B3B,eAAe,CAAC,SAAS,EAAE,aAAa,GAAG,IAAI;IAI/C,OAAO,CAAC,kBAAkB;IAa1B,8BAA8B,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,GACJ,YAAY,CACV;QAAC,iBAAiB;QAAE,kBAAkB;QAAE,qBAAqB;KAAC,CAC/D,GACD,oBAA6B,GAChC,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY;IAkDvC,qCAAqC,CACnC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,oBAAoB,EAC7B,YAAY,EAAE,YAAY,CACxB;QACE,MAAM;QACN;YAAC,iBAAiB;YAAE,kBAAkB;YAAE,qBAAqB;SAAC,EAAE;KACjE,EACD,aAAa,GAAG,SAAS,CAC1B,GACA,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY;IA4BvC,wBAAwB,CACtB,MAAM,EAAE,oBAAoB,EAC5B,UAAU,GACN,YAAY,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,qBAAqB,CAAC,CAAC,GAChE,oBAA6B,GAChC,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY;IAqDvC,gCAAgC,CAC9B,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,YAAY,CAAC,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,GACpD,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY;IAiCvC,0BAA0B,CACxB,QAAQ,EAAE,YAAY,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAC9C,YAAY;CAwBhB"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { and, capturedTraceToString, createRegExpFromOriginWildcard, httpRequestToCommonHttpRequest, httpResponseToCommonHttpResponse, httpTransactionToLabel, matchesOrigin, not, or, ThymianFormat, thymianRequestToOrigin, } from '@thymian/core';
|
|
2
|
+
export class AnalyticsApiContext {
|
|
3
|
+
repository;
|
|
4
|
+
logger;
|
|
5
|
+
format;
|
|
6
|
+
report;
|
|
7
|
+
violations = [];
|
|
8
|
+
roles;
|
|
9
|
+
skippedOrigins;
|
|
10
|
+
constructor(repository, logger, format, reportFn, roles, skippedOrigins) {
|
|
11
|
+
this.repository = repository;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.report = reportFn ?? (() => undefined);
|
|
14
|
+
this.skippedOrigins = skippedOrigins ?? [];
|
|
15
|
+
if (this.skippedOrigins.length === 0) {
|
|
16
|
+
this.format = format;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const regExps = this.skippedOrigins.map(createRegExpFromOriginWildcard);
|
|
20
|
+
this.format = format.filter(({ thymianReq }) => !regExps.some((regExp) => regExp.test(thymianRequestToOrigin(thymianReq))));
|
|
21
|
+
}
|
|
22
|
+
if (roles) {
|
|
23
|
+
this.roles = roles.flatMap((role) => role === 'intermediary'
|
|
24
|
+
? ['proxy', 'tunnel', 'gateway', 'intermediary']
|
|
25
|
+
: role);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
reportViolation(violation) {
|
|
29
|
+
this.violations.push(violation);
|
|
30
|
+
}
|
|
31
|
+
addOriginsToFilter(filter) {
|
|
32
|
+
return this.skippedOrigins.length === 0
|
|
33
|
+
? filter
|
|
34
|
+
: and(filter, not(or(...this.skippedOrigins.map((origin) => matchesOrigin(origin)))));
|
|
35
|
+
}
|
|
36
|
+
validateCommonHttpTransactions(filter, validate = filter) {
|
|
37
|
+
let finalFilter;
|
|
38
|
+
let validateFn;
|
|
39
|
+
if (typeof validate === 'function') {
|
|
40
|
+
finalFilter = filter;
|
|
41
|
+
validateFn = validate;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
finalFilter = and(filter, validate);
|
|
45
|
+
validateFn = () => true;
|
|
46
|
+
}
|
|
47
|
+
finalFilter = this.addOriginsToFilter(finalFilter);
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const { request, response, } of this.repository.readTransactionsByHttpFilter(finalFilter, this.roles)) {
|
|
50
|
+
const violation = validateFn(httpRequestToCommonHttpRequest(request.data), httpResponseToCommonHttpResponse(response.data), httpTransactionToLabel(request.data, response.data));
|
|
51
|
+
if (violation) {
|
|
52
|
+
const location = httpTransactionToLabel(request.data, response.data);
|
|
53
|
+
if (violation === true) {
|
|
54
|
+
results.push({
|
|
55
|
+
location,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
results.push({
|
|
60
|
+
location,
|
|
61
|
+
...violation,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return results.concat(this.violations);
|
|
67
|
+
}
|
|
68
|
+
validateGroupedCommonHttpTransactions(filter, groupBy, validationFn) {
|
|
69
|
+
const results = [];
|
|
70
|
+
const finalFilter = this.addOriginsToFilter(filter);
|
|
71
|
+
const groups = this.repository.readAndGroupTransactionsByHttpFilter(finalFilter, groupBy, this.roles);
|
|
72
|
+
for (const [key, transactions] of groups) {
|
|
73
|
+
const violation = validationFn(key, transactions.map((t) => [
|
|
74
|
+
httpRequestToCommonHttpRequest(t.request.data),
|
|
75
|
+
httpResponseToCommonHttpResponse(t.response.data),
|
|
76
|
+
httpTransactionToLabel(t.request.data, t.response.data),
|
|
77
|
+
]));
|
|
78
|
+
if (violation) {
|
|
79
|
+
results.push(violation);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
84
|
+
validateHttpTransactions(filter, validation = filter) {
|
|
85
|
+
let finalFilter;
|
|
86
|
+
let validateFn;
|
|
87
|
+
if (typeof validation === 'function') {
|
|
88
|
+
finalFilter = filter;
|
|
89
|
+
validateFn = validation;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
finalFilter = and(filter, validation);
|
|
93
|
+
validateFn = () => true;
|
|
94
|
+
}
|
|
95
|
+
finalFilter = this.addOriginsToFilter(finalFilter);
|
|
96
|
+
const results = [];
|
|
97
|
+
for (const { request, response, } of this.repository.readTransactionsByHttpFilter(finalFilter, this.roles)) {
|
|
98
|
+
const matched = this.format.matchTransaction(request.data, response.data);
|
|
99
|
+
const location = matched
|
|
100
|
+
? {
|
|
101
|
+
elementType: 'edge',
|
|
102
|
+
elementId: matched[0],
|
|
103
|
+
label: httpTransactionToLabel(request.data, response.data),
|
|
104
|
+
}
|
|
105
|
+
: httpTransactionToLabel(request.data, response.data);
|
|
106
|
+
const violation = validateFn(request.data, response.data, location);
|
|
107
|
+
if (violation) {
|
|
108
|
+
if (violation === true) {
|
|
109
|
+
results.push({
|
|
110
|
+
location,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
results.push({
|
|
115
|
+
location,
|
|
116
|
+
...violation,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return results.concat(this.violations);
|
|
122
|
+
}
|
|
123
|
+
validateCapturedHttpTransactions(filter, validate) {
|
|
124
|
+
const finalFilter = this.addOriginsToFilter(filter);
|
|
125
|
+
const results = [];
|
|
126
|
+
for (const transaction of this.repository.readTransactionsByHttpFilter(finalFilter, this.roles)) {
|
|
127
|
+
const location = httpTransactionToLabel(transaction.request.data, transaction.response.data);
|
|
128
|
+
const violation = validate(transaction, location);
|
|
129
|
+
if (violation) {
|
|
130
|
+
if (violation === true) {
|
|
131
|
+
results.push({
|
|
132
|
+
location,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
results.push({
|
|
137
|
+
location,
|
|
138
|
+
...violation,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return results.concat(this.violations);
|
|
144
|
+
}
|
|
145
|
+
validateCapturedHttpTraces(validate) {
|
|
146
|
+
const results = [];
|
|
147
|
+
for (const trace of this.repository.readAllHttpTraces()) {
|
|
148
|
+
const location = capturedTraceToString(trace);
|
|
149
|
+
const violation = validate(trace, location);
|
|
150
|
+
if (violation) {
|
|
151
|
+
if (violation === true) {
|
|
152
|
+
results.push({
|
|
153
|
+
location,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
results.push({
|
|
158
|
+
location,
|
|
159
|
+
...violation,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return results.concat(this.violations);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=analytics-api-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics-api-context.js","sourceRoot":"","sources":["../src/analytics-api-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type HttpFilterExpression } from '@thymian/core';
|
|
2
|
+
import type { TableNames } from './types.js';
|
|
3
|
+
import type { SqlFragment } from './utils.js';
|
|
4
|
+
export declare function httpFilterToGroupByClause(expression: HttpFilterExpression, tables: TableNames): SqlFragment;
|
|
5
|
+
export declare function httpFilterToGroupingKey(expression: HttpFilterExpression, tables: TableNames): string;
|
|
6
|
+
//# sourceMappingURL=http-filter-to-groupby-clause.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-filter-to-groupby-clause.d.ts","sourceRoot":"","sources":["../../src/db/http-filter-to-groupby-clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAK1B,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,UAAU,GACjB,WAAW,CAyCb;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,UAAU,GACjB,MAAM,CAyBR"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { and, origin, path, visitHttpFilter, } from '@thymian/core';
|
|
2
|
+
export function httpFilterToGroupByClause(expression, tables) {
|
|
3
|
+
const params = [];
|
|
4
|
+
switch (expression.type) {
|
|
5
|
+
case 'url':
|
|
6
|
+
// port is embedded in origin (e.g. "https://example.com:8080"), so
|
|
7
|
+
// grouping by origin + path is sufficient to identify a URL.
|
|
8
|
+
return httpFilterToGroupByClause(and(origin(), path()), tables);
|
|
9
|
+
case 'origin':
|
|
10
|
+
return {
|
|
11
|
+
sql: `${tables.requests}.origin`,
|
|
12
|
+
params,
|
|
13
|
+
};
|
|
14
|
+
case 'method':
|
|
15
|
+
return {
|
|
16
|
+
sql: `${tables.requests}.method`,
|
|
17
|
+
params,
|
|
18
|
+
};
|
|
19
|
+
case 'path':
|
|
20
|
+
return {
|
|
21
|
+
sql: `${tables.requests}.path`,
|
|
22
|
+
params,
|
|
23
|
+
};
|
|
24
|
+
case 'statusCode':
|
|
25
|
+
return {
|
|
26
|
+
sql: `${tables.responses}.status_code`,
|
|
27
|
+
params,
|
|
28
|
+
};
|
|
29
|
+
case 'and': {
|
|
30
|
+
const parts = expression.filters.map((expr) => httpFilterToGroupByClause(expr, tables));
|
|
31
|
+
return {
|
|
32
|
+
sql: parts.map((p) => p.sql).join(', '),
|
|
33
|
+
params,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(`Unsupported group by expression: ${expression.type}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function httpFilterToGroupingKey(expression, tables) {
|
|
41
|
+
// Pre-expand url() to and(origin(), path()) so the visitor does not need a
|
|
42
|
+
// visitUrl handler (port is already embedded in origin).
|
|
43
|
+
const resolved = expression.type === 'url' ? and(origin(), path()) : expression;
|
|
44
|
+
return visitHttpFilter(resolved, {
|
|
45
|
+
visitAnd(expr) {
|
|
46
|
+
return expr.filters
|
|
47
|
+
.map((filter) => httpFilterToGroupingKey(filter, tables))
|
|
48
|
+
.join(' || ');
|
|
49
|
+
},
|
|
50
|
+
visitOrigin() {
|
|
51
|
+
return `COALESCE(${tables.requests}.origin, '')`;
|
|
52
|
+
},
|
|
53
|
+
visitMethod() {
|
|
54
|
+
return `COALESCE(${tables.requests}.method, '')`;
|
|
55
|
+
},
|
|
56
|
+
visitStatusCode() {
|
|
57
|
+
return `COALESCE(${tables.responses}.status_code, '')`;
|
|
58
|
+
},
|
|
59
|
+
visitPath() {
|
|
60
|
+
return `COALESCE(${tables.requests}.path, '')`;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=http-filter-to-groupby-clause.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-filter-to-groupby-clause.js","sourceRoot":"","sources":["../../src/db/http-filter-to-groupby-clause.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EAGH,MAAM,EACN,IAAI,EACJ,eAAe,GAChB,MAAM,eAAe,CAAC;AAKvB,MAAM,UAAU,yBAAyB,CACvC,UAAgC,EAChC,MAAkB;IAElB,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,KAAK;YACR,mEAAmE;YACnE,6DAA6D;YAC7D,OAAO,yBAAyB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAClE,KAAK,QAAQ;YACX,OAAO;gBACL,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,SAAS;gBAChC,MAAM;aACP,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,SAAS;gBAChC,MAAM;aACP,CAAC;QACJ,KAAK,MAAM;YACT,OAAO;gBACL,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,OAAO;gBAC9B,MAAM;aACP,CAAC;QACJ,KAAK,YAAY;YACf,OAAO;gBACL,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,cAAc;gBACtC,MAAM;aACP,CAAC;QACJ,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,CACxC,CAAC;YAEF,OAAO;gBACL,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvC,MAAM;aACP,CAAC;QACJ,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,UAAgC,EAChC,MAAkB;IAElB,2EAA2E;IAC3E,yDAAyD;IACzD,MAAM,QAAQ,GACZ,UAAU,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IAEjE,OAAO,eAAe,CAAC,QAAQ,EAAE;QAC/B,QAAQ,CAAC,IAAiD;YACxD,OAAO,IAAI,CAAC,OAAO;iBAChB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;iBACxD,IAAI,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC;QACD,WAAW;YACT,OAAO,YAAY,MAAM,CAAC,QAAQ,cAAc,CAAC;QACnD,CAAC;QACD,WAAW;YACT,OAAO,YAAY,MAAM,CAAC,QAAQ,cAAc,CAAC;QACnD,CAAC;QACD,eAAe;YACb,OAAO,YAAY,MAAM,CAAC,SAAS,mBAAmB,CAAC;QACzD,CAAC;QACD,SAAS;YACP,OAAO,YAAY,MAAM,CAAC,QAAQ,YAAY,CAAC;QACjD,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type HttpFilterExpression } from '@thymian/core';
|
|
2
|
+
import type { TableNames } from './types.js';
|
|
3
|
+
import { type SqlFragment } from './utils.js';
|
|
4
|
+
export declare function compileHttpFilterToWhereClause(filter: HttpFilterExpression, names?: Partial<TableNames>): SqlFragment;
|
|
5
|
+
//# sourceMappingURL=http-filter-to-where-clause.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-filter-to-where-clause.d.ts","sourceRoot":"","sources":["../../src/db/http-filter-to-where-clause.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,oBAAoB,EAAgB,MAAM,eAAe,CAAC;AAE7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,oBAAoB,EAC5B,KAAK,GAAE,OAAO,CAAC,UAAU,CAAM,GAC9B,WAAW,CAwNb"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { and, origin, path } from '@thymian/core';
|
|
2
|
+
import { parenthesize } from './utils.js';
|
|
3
|
+
export function compileHttpFilterToWhereClause(filter, names = {}) {
|
|
4
|
+
const tableNames = {
|
|
5
|
+
requests: 'req',
|
|
6
|
+
responses: 'res',
|
|
7
|
+
...names,
|
|
8
|
+
};
|
|
9
|
+
switch (filter.type) {
|
|
10
|
+
case 'origin':
|
|
11
|
+
return {
|
|
12
|
+
sql: `${tableNames.requests}.origin = ?`,
|
|
13
|
+
params: [filter.origin],
|
|
14
|
+
};
|
|
15
|
+
case 'method':
|
|
16
|
+
return {
|
|
17
|
+
sql: `${tableNames.requests}.method = ? COLLATE NOCASE`,
|
|
18
|
+
params: [filter.method],
|
|
19
|
+
};
|
|
20
|
+
case 'path':
|
|
21
|
+
return { sql: `${tableNames.requests}.path = ?`, params: [filter.path] };
|
|
22
|
+
case 'requestHeader': {
|
|
23
|
+
const base = `
|
|
24
|
+
EXISTS (
|
|
25
|
+
SELECT 1
|
|
26
|
+
FROM http_request_header reqHeader
|
|
27
|
+
WHERE reqHeader.request_id = ${tableNames.requests}.id
|
|
28
|
+
AND reqHeader.name = ? COLLATE NOCASE`;
|
|
29
|
+
if (typeof filter.value === 'undefined') {
|
|
30
|
+
return { sql: `${base})`, params: [filter.header] };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
sql: `${base} AND reqHeader.value = ?)`,
|
|
34
|
+
params: [filter.header, String(filter.value)],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
case 'responseHeader': {
|
|
38
|
+
const base = `
|
|
39
|
+
EXISTS (
|
|
40
|
+
SELECT 1
|
|
41
|
+
FROM http_response_header resHeader
|
|
42
|
+
WHERE resHeader.response_id = res.id
|
|
43
|
+
AND resHeader.name = ? COLLATE NOCASE`;
|
|
44
|
+
if (typeof filter.value === 'undefined') {
|
|
45
|
+
return { sql: `${base})`, params: [filter.header] };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
sql: `${base} AND resHeader.value = ?)`,
|
|
49
|
+
params: [filter.header, String(filter.value)],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
case 'responseTrailer': {
|
|
53
|
+
const base = `
|
|
54
|
+
EXISTS (
|
|
55
|
+
SELECT 1
|
|
56
|
+
FROM http_response_trailer resTrailer
|
|
57
|
+
WHERE resTrailer.response_id = res.id
|
|
58
|
+
AND resTrailer.name = ? COLLATE NOCASE`;
|
|
59
|
+
if (typeof filter.value === 'undefined') {
|
|
60
|
+
return { sql: `${base})`, params: [filter.trailer] };
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
sql: `${base} AND resTrailer.value = ?)`,
|
|
64
|
+
params: [filter.trailer, String(filter.value)],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
case 'statusCode':
|
|
68
|
+
return {
|
|
69
|
+
sql: `${tableNames.responses}.status_code = ?`,
|
|
70
|
+
params: [filter.code],
|
|
71
|
+
};
|
|
72
|
+
case 'statusCodeRange':
|
|
73
|
+
return {
|
|
74
|
+
sql: `(${tableNames.responses}.status_code BETWEEN ? AND ?)`,
|
|
75
|
+
params: [filter.start, filter.end],
|
|
76
|
+
};
|
|
77
|
+
case 'hasBody':
|
|
78
|
+
return { sql: `${tableNames.requests}.body IS NOT NULL`, params: [] };
|
|
79
|
+
case 'hasResponseBody':
|
|
80
|
+
return { sql: `${tableNames.responses}.body IS NOT NULL`, params: [] };
|
|
81
|
+
case 'requestMediaType': {
|
|
82
|
+
const where = `
|
|
83
|
+
EXISTS (
|
|
84
|
+
SELECT 1
|
|
85
|
+
FROM http_request_header reqHeader
|
|
86
|
+
WHERE reqHeader.request_id = req.id
|
|
87
|
+
AND reqHeader.name = 'content-type' COLLATE NOCASE
|
|
88
|
+
AND LOWER(reqHeader.value) LIKE LOWER(?)
|
|
89
|
+
)`;
|
|
90
|
+
return { sql: where, params: [filter.mediaType] };
|
|
91
|
+
}
|
|
92
|
+
case 'responseMediaType': {
|
|
93
|
+
const where = `
|
|
94
|
+
EXISTS (
|
|
95
|
+
SELECT 1
|
|
96
|
+
FROM http_response_header resHeader
|
|
97
|
+
WHERE resHeader.response_id = res.id
|
|
98
|
+
AND resHeader.name = 'content-type' COLLATE NOCASE
|
|
99
|
+
AND LOWER(resHeader.value) LIKE LOWER(?)
|
|
100
|
+
)`;
|
|
101
|
+
return { sql: where, params: [filter.mediaType] };
|
|
102
|
+
}
|
|
103
|
+
case 'constant':
|
|
104
|
+
return { sql: filter.value ? '1=1' : '0=1', params: [] };
|
|
105
|
+
case 'and': {
|
|
106
|
+
const parts = filter.filters.map((expr) => compileHttpFilterToWhereClause(expr, tableNames));
|
|
107
|
+
return {
|
|
108
|
+
sql: parts.map((p) => parenthesize(p.sql)).join(' AND '),
|
|
109
|
+
params: parts.flatMap((p) => p.params),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
case 'or': {
|
|
113
|
+
const parts = filter.filters.map((expr) => compileHttpFilterToWhereClause(expr, tableNames));
|
|
114
|
+
return {
|
|
115
|
+
sql: parts.map((p) => parenthesize(p.sql)).join(' OR '),
|
|
116
|
+
params: parts.flatMap((p) => p.params),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
case 'not': {
|
|
120
|
+
const inner = compileHttpFilterToWhereClause(filter.filter);
|
|
121
|
+
return {
|
|
122
|
+
sql: `NOT ${parenthesize(inner.sql)}`,
|
|
123
|
+
params: inner.params,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
case 'xor': {
|
|
127
|
+
const [a, b] = filter.filters;
|
|
128
|
+
const sa = compileHttpFilterToWhereClause(a);
|
|
129
|
+
const sb = compileHttpFilterToWhereClause(b);
|
|
130
|
+
const where = `
|
|
131
|
+
(${parenthesize(sa.sql)} AND NOT ${parenthesize(sb.sql)})
|
|
132
|
+
OR
|
|
133
|
+
(${parenthesize(sb.sql)} AND NOT ${parenthesize(sa.sql)})
|
|
134
|
+
`;
|
|
135
|
+
return {
|
|
136
|
+
sql: where,
|
|
137
|
+
params: [...sa.params, ...sb.params, ...sb.params, ...sa.params],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case 'queryParam': {
|
|
141
|
+
if (typeof filter.param === 'undefined') {
|
|
142
|
+
return { sql: '1=1', params: [] };
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
sql: `
|
|
146
|
+
EXISTS (
|
|
147
|
+
SELECT 1
|
|
148
|
+
FROM http_request_query_parameter reqParam
|
|
149
|
+
WHERE
|
|
150
|
+
reqParam.request_id = reqId AND
|
|
151
|
+
reqParam.name = ? COLLATE NOCASE AND
|
|
152
|
+
reqParam.value = ?
|
|
153
|
+
)`,
|
|
154
|
+
params: [filter.param, filter.value],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
case 'url': {
|
|
158
|
+
if (typeof filter.url === 'undefined') {
|
|
159
|
+
return { sql: '1=1', params: [] };
|
|
160
|
+
}
|
|
161
|
+
const url = new URL(filter.url);
|
|
162
|
+
return compileHttpFilterToWhereClause(and(origin(url.origin), path(url.pathname)), tableNames);
|
|
163
|
+
}
|
|
164
|
+
case 'protocol': {
|
|
165
|
+
if (typeof filter.protocol === 'undefined') {
|
|
166
|
+
return { sql: '1=1', params: [] };
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
sql: `${tableNames.requests}.origin LIKE ? || '://%'`,
|
|
170
|
+
params: [filter.protocol],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
case 'matches-origin': {
|
|
174
|
+
if (typeof filter.origin === 'undefined') {
|
|
175
|
+
return { sql: '1=1', params: [] };
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
sql: `${tableNames.requests}.origin LIKE ?`,
|
|
179
|
+
params: [`%${filter.origin.replaceAll('*', '%')}%`],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
case 'hasResponse':
|
|
183
|
+
case 'isAuthorized':
|
|
184
|
+
case 'port':
|
|
185
|
+
throw new Error(`HTTP filter expression "${filter.type}" is not supported for SQL translation.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=http-filter-to-where-clause.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-filter-to-where-clause.js","sourceRoot":"","sources":["../../src/db/http-filter-to-where-clause.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HttpFilterExpression } from '@thymian/core';
|
|
2
|
+
import type { CapturedTrace, CapturedTransaction, HttpParticipantRole } from '@thymian/core';
|
|
3
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
4
|
+
export interface HttpTransactionRepository {
|
|
5
|
+
init(): Awaitable<void>;
|
|
6
|
+
close(): Awaitable<void>;
|
|
7
|
+
insertHttpTrace(trace: CapturedTrace): Awaitable<number>;
|
|
8
|
+
readTraceById(id: number): Awaitable<CapturedTrace | undefined>;
|
|
9
|
+
insertHttpTransaction(transaction: CapturedTransaction): Awaitable<number>;
|
|
10
|
+
readTransactionById(id: number): Awaitable<CapturedTransaction | undefined>;
|
|
11
|
+
readTransactionsByHttpFilter(filter: HttpFilterExpression, role?: HttpParticipantRole[]): IterableIterator<CapturedTransaction, void, unknown>;
|
|
12
|
+
readAndGroupTransactionsByHttpFilter(filter: HttpFilterExpression, groupBy: HttpFilterExpression, role?: HttpParticipantRole[]): IterableIterator<[string, CapturedTransaction[]], void, unknown>;
|
|
13
|
+
readAllHttpTraces(): IterableIterator<CapturedTrace, void, unknown>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=http-transaction-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-transaction-repository.d.ts","sourceRoot":"","sources":["../../src/db/http-transaction-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE1C,MAAM,WAAW,yBAAyB;IACxC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAExB,KAAK,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAEzB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAEzD,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAEhE,qBAAqB,CAAC,WAAW,EAAE,mBAAmB,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAE3E,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;IAE5E,4BAA4B,CAC1B,MAAM,EAAE,oBAAoB,EAC5B,IAAI,CAAC,EAAE,mBAAmB,EAAE,GAC3B,gBAAgB,CAAC,mBAAmB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAExD,oCAAoC,CAClC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,oBAAoB,EAC7B,IAAI,CAAC,EAAE,mBAAmB,EAAE,GAC3B,gBAAgB,CAAC,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAEpE,iBAAiB,IAAI,gBAAgB,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;CACrE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-transaction-repository.js","sourceRoot":"","sources":["../../src/db/http-transaction-repository.ts"],"names":[],"mappings":""}
|