@tellann/backend-sdk 0.2.0 → 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/dist/core/TELLANN.d.ts +19 -0
- package/dist/core/TELLANN.js +19 -0
- package/dist/core/capture.d.ts +50 -0
- package/dist/core/capture.js +186 -0
- package/dist/core/captureError.d.ts +8 -0
- package/dist/core/captureError.js +19 -7
- package/dist/core/qaEvidence.d.ts +31 -0
- package/dist/core/qaEvidence.js +79 -0
- package/dist/core/requestContext.d.ts +49 -0
- package/dist/core/requestContext.js +80 -0
- package/dist/core/trackApi.d.ts +25 -0
- package/dist/core/trackApi.js +72 -17
- package/dist/core/trackDataAccess.d.ts +34 -0
- package/dist/core/trackDataAccess.js +118 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +17 -0
- package/dist/integrations/express/index.d.ts +13 -1
- package/dist/integrations/express/index.js +77 -2
- package/dist/integrations/fastify/index.js +39 -1
- package/dist/integrations/hapi/index.d.ts +4 -0
- package/dist/integrations/hapi/index.js +25 -1
- package/dist/integrations/koa/index.d.ts +7 -0
- package/dist/integrations/koa/index.js +46 -24
- package/dist/integrations/prisma/index.d.ts +46 -0
- package/dist/integrations/prisma/index.js +69 -0
- package/package.json +1 -1
|
@@ -11,8 +11,15 @@ export type TellannKoaContext = {
|
|
|
11
11
|
status: number;
|
|
12
12
|
/** Set by `koa-router`; the matched pattern rather than the concrete path. */
|
|
13
13
|
_matchedRoute?: string;
|
|
14
|
+
/** Present when a body parser is registered. */
|
|
15
|
+
body?: unknown;
|
|
16
|
+
query?: Record<string, unknown>;
|
|
14
17
|
request: {
|
|
15
18
|
headers: Record<string, any>;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
};
|
|
21
|
+
response?: {
|
|
22
|
+
headers?: Record<string, any>;
|
|
16
23
|
};
|
|
17
24
|
state: Record<string, any>;
|
|
18
25
|
};
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.tellannKoaMiddleware = tellannKoaMiddleware;
|
|
4
4
|
const TELLANN_1 = require("../../core/TELLANN");
|
|
5
5
|
const express_1 = require("../express");
|
|
6
|
+
const requestContext_1 = require("../../core/requestContext");
|
|
6
7
|
/**
|
|
7
8
|
* Track every request, and re-throw whatever the downstream middleware threw.
|
|
8
9
|
*
|
|
@@ -15,29 +16,50 @@ function tellannKoaMiddleware() {
|
|
|
15
16
|
const start = Date.now();
|
|
16
17
|
const correlation = (0, express_1.extractCorrelationContext)(context.request?.headers ?? {});
|
|
17
18
|
context.state.tellann = correlation;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
19
|
+
const tellannContext = {
|
|
20
|
+
...correlation,
|
|
21
|
+
method: context.method,
|
|
22
|
+
route: context._matchedRoute ?? context.path,
|
|
23
|
+
dataAccess: [],
|
|
24
|
+
};
|
|
25
|
+
await (0, requestContext_1.runInRequestContext)(tellannContext, async () => {
|
|
26
|
+
try {
|
|
27
|
+
await next();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
await TELLANN_1.TELLANN.captureError({
|
|
31
|
+
error: error,
|
|
32
|
+
sessionId: correlation.sessionId,
|
|
33
|
+
runId: correlation.runId,
|
|
34
|
+
traceId: correlation.traceId,
|
|
35
|
+
eventType: 'SERVER_ERROR',
|
|
36
|
+
route: context._matchedRoute ?? context.path,
|
|
37
|
+
method: context.method,
|
|
38
|
+
statusCode: context.status,
|
|
39
|
+
});
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await TELLANN_1.TELLANN.trackApi({
|
|
44
|
+
endpoint: context.path,
|
|
45
|
+
// Read after `next`, by which point the router has matched.
|
|
46
|
+
route: context._matchedRoute ?? context.path,
|
|
47
|
+
method: context.method,
|
|
48
|
+
statusCode: context.status,
|
|
49
|
+
durationMs: Date.now() - start,
|
|
50
|
+
sessionId: correlation.sessionId,
|
|
51
|
+
runId: correlation.runId,
|
|
52
|
+
traceId: correlation.traceId,
|
|
53
|
+
framework: 'koa',
|
|
54
|
+
models: (0, requestContext_1.summarizeDataAccess)(tellannContext.dataAccess),
|
|
55
|
+
query: context.query,
|
|
56
|
+
requestBody: context.request?.body,
|
|
57
|
+
responseBody: context.body,
|
|
58
|
+
requestHeaders: context.request?.headers,
|
|
59
|
+
responseHeaders: context.response?.headers,
|
|
60
|
+
});
|
|
61
|
+
await TELLANN_1.TELLANN.flushDataAccess(tellannContext);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
42
64
|
};
|
|
43
65
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma integration.
|
|
3
|
+
*
|
|
4
|
+
* Prisma's types are described structurally rather than imported, so this SDK
|
|
5
|
+
* never pulls `@prisma/client` into a project that does not use it. Both of
|
|
6
|
+
* Prisma's interception points are supported because which one a project can
|
|
7
|
+
* use depends on its version: `$extends` on 4.16 and later, `$use` before it.
|
|
8
|
+
*/
|
|
9
|
+
export type TellannPrismaOperationArgs = {
|
|
10
|
+
model?: string | null;
|
|
11
|
+
operation: string;
|
|
12
|
+
args: unknown;
|
|
13
|
+
query: (args: unknown) => Promise<unknown>;
|
|
14
|
+
};
|
|
15
|
+
export type TellannPrismaMiddlewareParams = {
|
|
16
|
+
model?: string | null;
|
|
17
|
+
action: string;
|
|
18
|
+
args?: unknown;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A Prisma client extension that reports every query as a data-access event.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* const prisma = new PrismaClient().$extends(tellannPrismaExtension());
|
|
25
|
+
*
|
|
26
|
+
* The extension reports the model and the operation, never the arguments: a
|
|
27
|
+
* `where` clause routinely contains the identifiers a QA run is required not
|
|
28
|
+
* to keep in the clear, and the request's own captured payload already says
|
|
29
|
+
* what was asked for.
|
|
30
|
+
*/
|
|
31
|
+
export declare function tellannPrismaExtension(): {
|
|
32
|
+
name: string;
|
|
33
|
+
query: {
|
|
34
|
+
$allModels: {
|
|
35
|
+
$allOperations({ model, operation, args, query }: TellannPrismaOperationArgs): Promise<unknown>;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The same reporting for Prisma clients older than 4.16, which have `$use`
|
|
41
|
+
* rather than `$extends`.
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* prisma.$use(tellannPrismaMiddleware());
|
|
45
|
+
*/
|
|
46
|
+
export declare function tellannPrismaMiddleware(): (params: TellannPrismaMiddlewareParams, next: (params: TellannPrismaMiddlewareParams) => Promise<unknown>) => Promise<unknown>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tellannPrismaExtension = tellannPrismaExtension;
|
|
4
|
+
exports.tellannPrismaMiddleware = tellannPrismaMiddleware;
|
|
5
|
+
const TELLANN_1 = require("../../core/TELLANN");
|
|
6
|
+
const trackDataAccess_1 = require("../../core/trackDataAccess");
|
|
7
|
+
function recordCount(result) {
|
|
8
|
+
if (Array.isArray(result))
|
|
9
|
+
return result.length;
|
|
10
|
+
if (result && typeof result === 'object') {
|
|
11
|
+
const count = result.count;
|
|
12
|
+
if (typeof count === 'number')
|
|
13
|
+
return count;
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
return result === null || result === undefined ? 0 : 1;
|
|
17
|
+
}
|
|
18
|
+
async function report(model, operation, startedAt, result) {
|
|
19
|
+
if (!model)
|
|
20
|
+
return;
|
|
21
|
+
await TELLANN_1.TELLANN.trackDataAccess({
|
|
22
|
+
model,
|
|
23
|
+
operation,
|
|
24
|
+
records: recordCount(result),
|
|
25
|
+
durationMs: Date.now() - startedAt,
|
|
26
|
+
mutation: (0, trackDataAccess_1.isMutationOperation)(operation),
|
|
27
|
+
}).catch(() => undefined);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A Prisma client extension that reports every query as a data-access event.
|
|
31
|
+
*
|
|
32
|
+
* Usage:
|
|
33
|
+
* const prisma = new PrismaClient().$extends(tellannPrismaExtension());
|
|
34
|
+
*
|
|
35
|
+
* The extension reports the model and the operation, never the arguments: a
|
|
36
|
+
* `where` clause routinely contains the identifiers a QA run is required not
|
|
37
|
+
* to keep in the clear, and the request's own captured payload already says
|
|
38
|
+
* what was asked for.
|
|
39
|
+
*/
|
|
40
|
+
function tellannPrismaExtension() {
|
|
41
|
+
return {
|
|
42
|
+
name: 'tellann',
|
|
43
|
+
query: {
|
|
44
|
+
$allModels: {
|
|
45
|
+
async $allOperations({ model, operation, args, query }) {
|
|
46
|
+
const startedAt = Date.now();
|
|
47
|
+
const result = await query(args);
|
|
48
|
+
void report(model, operation, startedAt, result);
|
|
49
|
+
return result;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The same reporting for Prisma clients older than 4.16, which have `$use`
|
|
57
|
+
* rather than `$extends`.
|
|
58
|
+
*
|
|
59
|
+
* Usage:
|
|
60
|
+
* prisma.$use(tellannPrismaMiddleware());
|
|
61
|
+
*/
|
|
62
|
+
function tellannPrismaMiddleware() {
|
|
63
|
+
return async (params, next) => {
|
|
64
|
+
const startedAt = Date.now();
|
|
65
|
+
const result = await next(params);
|
|
66
|
+
void report(params.model, params.action, startedAt, result);
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
}
|