@unito/integration-sdk 5.0.0 → 5.1.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/dist/src/handler.js +11 -0
- package/dist/src/index.cjs +114 -5
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/integration.d.ts +1 -0
- package/dist/src/integration.js +2 -0
- package/dist/src/middlewares/finish.d.ts +0 -1
- package/dist/src/middlewares/finish.js +4 -1
- package/dist/src/middlewares/start.d.ts +2 -1
- package/dist/src/middlewares/start.js +2 -1
- package/dist/src/resources/context.d.ts +6 -0
- package/dist/src/resources/provider.d.ts +3 -0
- package/dist/src/resources/provider.js +15 -3
- package/dist/src/resources/requestMetrics.d.ts +24 -0
- package/dist/src/resources/requestMetrics.js +33 -0
- package/dist/src/resources/tracer.d.ts +16 -0
- package/dist/src/resources/tracer.js +45 -0
- package/dist/test/middlewares/finish.test.js +5 -4
- package/dist/test/middlewares/start.test.js +13 -2
- package/dist/test/resources/provider.test.js +95 -0
- package/dist/test/resources/requestMetrics.test.d.ts +1 -0
- package/dist/test/resources/requestMetrics.test.js +45 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/handler.ts +11 -0
- package/src/index.ts +2 -0
- package/src/integration.ts +3 -0
- package/src/middlewares/finish.ts +4 -2
- package/src/middlewares/start.ts +3 -2
- package/src/resources/context.ts +6 -0
- package/src/resources/provider.ts +20 -3
- package/src/resources/requestMetrics.ts +37 -0
- package/src/resources/tracer.ts +50 -0
- package/test/middlewares/finish.test.ts +13 -12
- package/test/middlewares/start.test.ts +13 -2
- package/test/resources/provider.test.ts +126 -0
- package/test/resources/requestMetrics.test.ts +50 -0
|
@@ -2,6 +2,7 @@ import express from 'express';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { describe, it } from 'node:test';
|
|
4
4
|
import onFinish from '../../src/middlewares/finish.js';
|
|
5
|
+
import { RequestMetrics } from '../../src/resources/requestMetrics.js';
|
|
5
6
|
|
|
6
7
|
describe('finish middleware', () => {
|
|
7
8
|
it('logs info', () => {
|
|
@@ -10,11 +11,11 @@ describe('finish middleware', () => {
|
|
|
10
11
|
|
|
11
12
|
const request = { originalUrl: '/' } as express.Request;
|
|
12
13
|
const response = {
|
|
13
|
-
on: (_event, func: () => void) => {
|
|
14
|
+
on: (_event: string, func: () => void) => {
|
|
14
15
|
eventHandler = func;
|
|
15
16
|
},
|
|
16
17
|
locals: {
|
|
17
|
-
|
|
18
|
+
requestMetrics: RequestMetrics.startRequest(),
|
|
18
19
|
logger: {
|
|
19
20
|
info: (_message: string) => {
|
|
20
21
|
expected = 'works!';
|
|
@@ -22,7 +23,7 @@ describe('finish middleware', () => {
|
|
|
22
23
|
},
|
|
23
24
|
},
|
|
24
25
|
statusCode: 200,
|
|
25
|
-
} as express.Response;
|
|
26
|
+
} as unknown as express.Response;
|
|
26
27
|
|
|
27
28
|
onFinish(request, response, () => {});
|
|
28
29
|
eventHandler();
|
|
@@ -36,11 +37,11 @@ describe('finish middleware', () => {
|
|
|
36
37
|
|
|
37
38
|
const request = { originalUrl: '/' } as express.Request;
|
|
38
39
|
const response = {
|
|
39
|
-
on: (_event, func: () => void) => {
|
|
40
|
+
on: (_event: string, func: () => void) => {
|
|
40
41
|
eventHandler = func;
|
|
41
42
|
},
|
|
42
43
|
locals: {
|
|
43
|
-
|
|
44
|
+
requestMetrics: RequestMetrics.startRequest(),
|
|
44
45
|
logger: {
|
|
45
46
|
error: (_message: string) => {
|
|
46
47
|
expected = 'works!';
|
|
@@ -48,7 +49,7 @@ describe('finish middleware', () => {
|
|
|
48
49
|
},
|
|
49
50
|
},
|
|
50
51
|
statusCode: 500,
|
|
51
|
-
} as express.Response;
|
|
52
|
+
} as unknown as express.Response;
|
|
52
53
|
|
|
53
54
|
onFinish(request, response, () => {});
|
|
54
55
|
eventHandler();
|
|
@@ -62,11 +63,11 @@ describe('finish middleware', () => {
|
|
|
62
63
|
|
|
63
64
|
const request = { originalUrl: '/health' } as express.Request;
|
|
64
65
|
const response = {
|
|
65
|
-
on: (_event, func: () => void) => {
|
|
66
|
+
on: (_event: string, func: () => void) => {
|
|
66
67
|
eventHandler = func;
|
|
67
68
|
},
|
|
68
69
|
locals: {
|
|
69
|
-
|
|
70
|
+
requestMetrics: RequestMetrics.startRequest(),
|
|
70
71
|
logger: {
|
|
71
72
|
info: (_message: string) => {
|
|
72
73
|
expected = 'ohoh!';
|
|
@@ -74,7 +75,7 @@ describe('finish middleware', () => {
|
|
|
74
75
|
},
|
|
75
76
|
},
|
|
76
77
|
statusCode: 200,
|
|
77
|
-
} as express.Response;
|
|
78
|
+
} as unknown as express.Response;
|
|
78
79
|
|
|
79
80
|
onFinish(request, response, () => {});
|
|
80
81
|
eventHandler();
|
|
@@ -88,11 +89,11 @@ describe('finish middleware', () => {
|
|
|
88
89
|
|
|
89
90
|
const request = { originalUrl: '/health' } as express.Request;
|
|
90
91
|
const response = {
|
|
91
|
-
on: (_event, func: () => void) => {
|
|
92
|
+
on: (_event: string, func: () => void) => {
|
|
92
93
|
eventHandler = func;
|
|
93
94
|
},
|
|
94
95
|
locals: {
|
|
95
|
-
|
|
96
|
+
requestMetrics: RequestMetrics.startRequest(),
|
|
96
97
|
logger: {
|
|
97
98
|
error: (_message: string) => {
|
|
98
99
|
expected = 'works!';
|
|
@@ -100,7 +101,7 @@ describe('finish middleware', () => {
|
|
|
100
101
|
},
|
|
101
102
|
},
|
|
102
103
|
statusCode: 500,
|
|
103
|
-
} as express.Response;
|
|
104
|
+
} as unknown as express.Response;
|
|
104
105
|
|
|
105
106
|
onFinish(request, response, () => {});
|
|
106
107
|
eventHandler();
|
|
@@ -2,13 +2,24 @@ import express from 'express';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { describe, it } from 'node:test';
|
|
4
4
|
import start from '../../src/middlewares/start.js';
|
|
5
|
+
import { RequestMetrics } from '../../src/resources/requestMetrics.js';
|
|
5
6
|
|
|
6
7
|
describe('start middleware', () => {
|
|
7
|
-
it('
|
|
8
|
+
it('initializes request metrics on locals', () => {
|
|
8
9
|
const request = {} as express.Request;
|
|
9
10
|
const response = { locals: {} } as express.Response;
|
|
11
|
+
start(request, response, () => {});
|
|
12
|
+
assert.ok(response.locals.requestMetrics instanceof RequestMetrics);
|
|
13
|
+
});
|
|
10
14
|
|
|
15
|
+
it('records request start time in metrics', () => {
|
|
16
|
+
const request = {} as express.Request;
|
|
17
|
+
const response = { locals: {} } as express.Response;
|
|
18
|
+
const before = process.hrtime.bigint();
|
|
11
19
|
start(request, response, () => {});
|
|
12
|
-
|
|
20
|
+
const result = response.locals.requestMetrics.endRequest();
|
|
21
|
+
const after = process.hrtime.bigint();
|
|
22
|
+
assert.ok(result.durationNs >= 0n);
|
|
23
|
+
assert.ok(result.durationNs <= after - before);
|
|
13
24
|
});
|
|
14
25
|
});
|
|
@@ -8,6 +8,7 @@ import https from 'https';
|
|
|
8
8
|
import { Provider } from '../../src/resources/provider.js';
|
|
9
9
|
import * as HttpErrors from '../../src/httpErrors.js';
|
|
10
10
|
import Logger from '../../src/resources/logger.js';
|
|
11
|
+
import { RequestMetrics } from '../../src/resources/requestMetrics.js';
|
|
11
12
|
|
|
12
13
|
// There is currently an issue with node 20.12 and fetch mocking. A quick fix is to first call fetch so it's getter
|
|
13
14
|
// get properly instantiated, which allow it to be mocked properly.
|
|
@@ -1586,4 +1587,129 @@ describe('Provider', () => {
|
|
|
1586
1587
|
}
|
|
1587
1588
|
});
|
|
1588
1589
|
});
|
|
1590
|
+
|
|
1591
|
+
describe('request metrics', () => {
|
|
1592
|
+
it('records api call duration in requestMetrics via fetchWrapper', async context => {
|
|
1593
|
+
const metrics = RequestMetrics.startRequest();
|
|
1594
|
+
const response = new Response('{"data": "value"}', {
|
|
1595
|
+
status: 200,
|
|
1596
|
+
headers: new Headers({ 'Content-Type': 'application/json' }),
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
1600
|
+
|
|
1601
|
+
await provider.get('/endpoint', {
|
|
1602
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
1603
|
+
logger,
|
|
1604
|
+
requestMetrics: metrics,
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
const result = metrics.endRequest();
|
|
1608
|
+
assert.equal(result.apiCallCount, 1);
|
|
1609
|
+
assert.ok(result.totalApiDurationNs >= 0, 'Duration should be recorded');
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
it('records multiple api calls in the same requestMetrics', async context => {
|
|
1613
|
+
const metrics = RequestMetrics.startRequest();
|
|
1614
|
+
|
|
1615
|
+
context.mock.method(global, 'fetch', () =>
|
|
1616
|
+
Promise.resolve(
|
|
1617
|
+
new Response('{"data": "value"}', {
|
|
1618
|
+
status: 200,
|
|
1619
|
+
headers: new Headers({ 'Content-Type': 'application/json' }),
|
|
1620
|
+
}),
|
|
1621
|
+
),
|
|
1622
|
+
);
|
|
1623
|
+
|
|
1624
|
+
await provider.get('/endpoint1', {
|
|
1625
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
1626
|
+
logger,
|
|
1627
|
+
requestMetrics: metrics,
|
|
1628
|
+
});
|
|
1629
|
+
|
|
1630
|
+
await provider.post(
|
|
1631
|
+
'/endpoint2',
|
|
1632
|
+
{ key: 'value' },
|
|
1633
|
+
{
|
|
1634
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
1635
|
+
logger,
|
|
1636
|
+
requestMetrics: metrics,
|
|
1637
|
+
},
|
|
1638
|
+
);
|
|
1639
|
+
|
|
1640
|
+
const result = metrics.endRequest();
|
|
1641
|
+
assert.equal(result.apiCallCount, 2);
|
|
1642
|
+
assert.ok(result.totalApiDurationNs >= 0);
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
it('works without requestMetrics (backward compatible)', async context => {
|
|
1646
|
+
const response = new Response('{"data": "value"}', {
|
|
1647
|
+
status: 200,
|
|
1648
|
+
headers: new Headers({ 'Content-Type': 'application/json' }),
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
1652
|
+
|
|
1653
|
+
// No requestMetrics passed — should not throw
|
|
1654
|
+
const result = await provider.get('/endpoint', {
|
|
1655
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
1656
|
+
logger,
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
assert.equal(result.status, 200);
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
it('records api call duration from postForm', async () => {
|
|
1663
|
+
const metrics = RequestMetrics.startRequest();
|
|
1664
|
+
const httpsProvider = new Provider({
|
|
1665
|
+
prepareRequest: () => ({
|
|
1666
|
+
url: 'https://www.myApi.com',
|
|
1667
|
+
headers: {},
|
|
1668
|
+
}),
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
const scope = nock('https://www.myApi.com').post('/upload').reply(201, { success: true });
|
|
1672
|
+
|
|
1673
|
+
const FormData = (await import('form-data')).default;
|
|
1674
|
+
const form = new FormData();
|
|
1675
|
+
form.append('file', Buffer.from('test data'), 'test.txt');
|
|
1676
|
+
|
|
1677
|
+
await httpsProvider.postForm('/upload', form, {
|
|
1678
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
1679
|
+
logger,
|
|
1680
|
+
requestMetrics: metrics,
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
const postFormResult = metrics.endRequest();
|
|
1684
|
+
assert.equal(postFormResult.apiCallCount, 1);
|
|
1685
|
+
assert.ok(postFormResult.totalApiDurationNs > 0);
|
|
1686
|
+
scope.isDone();
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
it('records api call duration from postStream', async () => {
|
|
1690
|
+
const metrics = RequestMetrics.startRequest();
|
|
1691
|
+
const testData = 'binary stream data';
|
|
1692
|
+
const httpsProvider = new Provider({
|
|
1693
|
+
prepareRequest: () => ({
|
|
1694
|
+
url: 'https://www.myApi.com',
|
|
1695
|
+
headers: {},
|
|
1696
|
+
}),
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
const scope = nock('https://www.myApi.com').post('/upload', testData).reply(201, { success: true });
|
|
1700
|
+
|
|
1701
|
+
const stream = Readable.from(Buffer.from(testData));
|
|
1702
|
+
|
|
1703
|
+
await httpsProvider.postStream('/upload', stream, {
|
|
1704
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
1705
|
+
logger,
|
|
1706
|
+
requestMetrics: metrics,
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
const postStreamResult = metrics.endRequest();
|
|
1710
|
+
assert.equal(postStreamResult.apiCallCount, 1);
|
|
1711
|
+
assert.ok(postStreamResult.totalApiDurationNs > 0);
|
|
1712
|
+
scope.isDone();
|
|
1713
|
+
});
|
|
1714
|
+
});
|
|
1589
1715
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { RequestMetrics } from '../../src/resources/requestMetrics.js';
|
|
4
|
+
|
|
5
|
+
describe('RequestMetrics', () => {
|
|
6
|
+
describe('initial state', () => {
|
|
7
|
+
it('starts with zero api call count', () => {
|
|
8
|
+
const metrics = RequestMetrics.startRequest();
|
|
9
|
+
const result = metrics.endRequest();
|
|
10
|
+
assert.equal(result.apiCallCount, 0);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('starts with zero total api duration', () => {
|
|
14
|
+
const metrics = RequestMetrics.startRequest();
|
|
15
|
+
const result = metrics.endRequest();
|
|
16
|
+
assert.equal(result.totalApiDurationNs, 0);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('endRequest', () => {
|
|
21
|
+
it('returns elapsed duration since construction', () => {
|
|
22
|
+
const before = process.hrtime.bigint();
|
|
23
|
+
const metrics = RequestMetrics.startRequest();
|
|
24
|
+
const result = metrics.endRequest();
|
|
25
|
+
const after = process.hrtime.bigint();
|
|
26
|
+
assert.ok(result.durationNs >= 0n);
|
|
27
|
+
assert.ok(result.durationNs <= after - before);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('recordExternalApiCall', () => {
|
|
32
|
+
it('increments api call count', () => {
|
|
33
|
+
const metrics = RequestMetrics.startRequest();
|
|
34
|
+
const start1 = process.hrtime.bigint();
|
|
35
|
+
metrics.recordExternalApiCall(start1);
|
|
36
|
+
const start2 = process.hrtime.bigint();
|
|
37
|
+
metrics.recordExternalApiCall(start2);
|
|
38
|
+
const result = metrics.endRequest();
|
|
39
|
+
assert.equal(result.apiCallCount, 2);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('accumulates api duration', () => {
|
|
43
|
+
const metrics = RequestMetrics.startRequest();
|
|
44
|
+
const start = process.hrtime.bigint();
|
|
45
|
+
metrics.recordExternalApiCall(start);
|
|
46
|
+
const result = metrics.endRequest();
|
|
47
|
+
assert.ok(result.totalApiDurationNs >= 0);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|