@trayio/cdk-runtime 5.27.0 → 5.28.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/connector/polling/PollingServiceClient.d.ts.map +1 -1
- package/dist/connector/polling/PollingServiceClient.integration.test.d.ts +2 -0
- package/dist/connector/polling/PollingServiceClient.integration.test.d.ts.map +1 -0
- package/dist/connector/polling/PollingServiceClient.integration.test.js +130 -0
- package/dist/connector/polling/PollingServiceClient.js +7 -3
- package/dist/connector/polling/PollingServiceClient.unit.test.js +6 -5
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PollingServiceClient.d.ts","sourceRoot":"","sources":["../../../src/connector/polling/PollingServiceClient.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAGN,eAAe,EAEf,MAAM,kDAAkD,CAAC;AAuB1D,MAAM,MAAM,6BAA6B,GAAG;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;CACxB,CAAC;
|
|
1
|
+
{"version":3,"file":"PollingServiceClient.d.ts","sourceRoot":"","sources":["../../../src/connector/polling/PollingServiceClient.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAGN,eAAe,EAEf,MAAM,kDAAkD,CAAC;AAuB1D,MAAM,MAAM,6BAA6B,GAAG;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;CACxB,CAAC;AAyBF;;;;GAIG;AACH,qBAAa,oBAAoB;IACpB,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAEpC,QAAQ,CACb,OAAO,EAAE,6BAA6B,EACtC,cAAc,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAmCV,UAAU,CACf,OAAO,EAAE,+BAA+B,EACxC,cAAc,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;YAaF,QAAQ;CAqCtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PollingServiceClient.integration.test.d.ts","sourceRoot":"","sources":["../../../src/connector/polling/PollingServiceClient.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const http = __importStar(require("http"));
|
|
27
|
+
const AxiosHttpClient_1 = require("@trayio/axios/http/AxiosHttpClient");
|
|
28
|
+
const PollingServiceClient_1 = require("./PollingServiceClient");
|
|
29
|
+
const ENV_VAR = 'TRAY_POLLING_SERVICE_URL';
|
|
30
|
+
/**
|
|
31
|
+
* Emulates the production path that broke deregistration: a hop that only
|
|
32
|
+
* honours a request body on DELETE when Content-Length declares it, so a
|
|
33
|
+
* chunked DELETE body arrives at the polling service as empty. The 400
|
|
34
|
+
* response mirrors the polling service's Tapir decode failure verbatim.
|
|
35
|
+
*/
|
|
36
|
+
const startPollingServiceStub = async (observed) => {
|
|
37
|
+
const server = http.createServer((req, res) => {
|
|
38
|
+
const chunks = [];
|
|
39
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
40
|
+
req.on('end', () => {
|
|
41
|
+
const received = Buffer.concat(chunks).toString('utf8');
|
|
42
|
+
const bodyHonoured = req.method !== 'DELETE' || req.headers['content-length'] !== undefined;
|
|
43
|
+
const body = bodyHonoured ? received : '';
|
|
44
|
+
observed.push({ method: req.method, headers: req.headers, body });
|
|
45
|
+
try {
|
|
46
|
+
JSON.parse(body);
|
|
47
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
48
|
+
res.end('{}');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
52
|
+
res.end(JSON.stringify({
|
|
53
|
+
message: 'Invalid value for: body (exhausted input)',
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
await new Promise((resolve) => {
|
|
59
|
+
server.listen(0, '127.0.0.1', () => resolve());
|
|
60
|
+
});
|
|
61
|
+
return server;
|
|
62
|
+
};
|
|
63
|
+
describe('PollingServiceClient against a DELETE-body-hostile HTTP hop', () => {
|
|
64
|
+
let server;
|
|
65
|
+
let observed;
|
|
66
|
+
let originalEnv;
|
|
67
|
+
beforeAll(async () => {
|
|
68
|
+
observed = [];
|
|
69
|
+
server = await startPollingServiceStub(observed);
|
|
70
|
+
originalEnv = process.env[ENV_VAR];
|
|
71
|
+
const { port } = server.address();
|
|
72
|
+
process.env[ENV_VAR] = `http://127.0.0.1:${port}`;
|
|
73
|
+
});
|
|
74
|
+
afterAll(async () => {
|
|
75
|
+
if (originalEnv === undefined) {
|
|
76
|
+
delete process.env[ENV_VAR];
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
process.env[ENV_VAR] = originalEnv;
|
|
80
|
+
}
|
|
81
|
+
await new Promise((resolve, reject) => {
|
|
82
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
observed.length = 0;
|
|
87
|
+
});
|
|
88
|
+
it('deregister body survives: sent with Content-Length, not chunked, and decodes server-side', async () => {
|
|
89
|
+
const client = new PollingServiceClient_1.PollingServiceClient(new AxiosHttpClient_1.AxiosHttpClient());
|
|
90
|
+
await client.deregister({
|
|
91
|
+
connectorName: 'sample-polling-trigger',
|
|
92
|
+
connectorVersion: '1.0',
|
|
93
|
+
registeredHandlerName: 'new_rows_destroy',
|
|
94
|
+
publicUrl: 'https://routing-token.staging.trayapp.io',
|
|
95
|
+
preserveCursor: true,
|
|
96
|
+
}, 'exec-token');
|
|
97
|
+
expect(observed).toHaveLength(1);
|
|
98
|
+
const request = observed[0];
|
|
99
|
+
expect(request.method).toBe('DELETE');
|
|
100
|
+
expect(request.headers['content-length']).toBeDefined();
|
|
101
|
+
expect(request.headers['transfer-encoding']).toBeUndefined();
|
|
102
|
+
expect(JSON.parse(request.body)).toEqual({
|
|
103
|
+
connectorName: 'sample-polling-trigger',
|
|
104
|
+
connectorVersion: '1.0',
|
|
105
|
+
operationName: 'new_rows',
|
|
106
|
+
publicUrl: 'https://routing-token.staging.trayapp.io',
|
|
107
|
+
preserveCursor: true,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it('register keeps working through the same hop', async () => {
|
|
111
|
+
const client = new PollingServiceClient_1.PollingServiceClient(new AxiosHttpClient_1.AxiosHttpClient());
|
|
112
|
+
await client.register({
|
|
113
|
+
connectorName: 'sample-polling-trigger',
|
|
114
|
+
connectorVersion: '1.0',
|
|
115
|
+
registeredHandlerName: 'new_rows',
|
|
116
|
+
authenticationId: null,
|
|
117
|
+
organizationId: 'org-uuid',
|
|
118
|
+
workspaceId: 'ws-uuid',
|
|
119
|
+
projectId: null,
|
|
120
|
+
publicUrl: 'https://routing-token.staging.trayapp.io',
|
|
121
|
+
operationInput: { pageSize: 2 },
|
|
122
|
+
}, 'exec-token');
|
|
123
|
+
expect(observed).toHaveLength(1);
|
|
124
|
+
expect(observed[0].method).toBe('POST');
|
|
125
|
+
expect(JSON.parse(observed[0].body)).toMatchObject({
|
|
126
|
+
operationName: 'new_rows',
|
|
127
|
+
operationInput: { pageSize: 2 },
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -24,8 +24,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.PollingServiceClient = void 0;
|
|
27
|
-
const stream_1 = require("stream");
|
|
28
27
|
const E = __importStar(require("fp-ts/Either"));
|
|
28
|
+
const BufferExtensions_1 = require("@trayio/commons/buffer/BufferExtensions");
|
|
29
29
|
const Http_1 = require("@trayio/commons/http/Http");
|
|
30
30
|
const PollingTypes_1 = require("@trayio/cdk-dsl/connector/operation/PollingTypes");
|
|
31
31
|
const POLLING_SERVICE_URL_ENV = 'TRAY_POLLING_SERVICE_URL';
|
|
@@ -61,7 +61,6 @@ const resolveEndpoint = () => {
|
|
|
61
61
|
}
|
|
62
62
|
return `${baseUrl.replace(/\/$/, '')}${TRIGGERS_REGISTER_PATH}`;
|
|
63
63
|
};
|
|
64
|
-
const bodyToReadable = (payload) => stream_1.Readable.from(JSON.stringify(payload));
|
|
65
64
|
/**
|
|
66
65
|
* Internal HTTP client used by the runtime when invoking polling trigger
|
|
67
66
|
* create / destroy operations. Not exported from the public `@trayio/cdk-runtime`
|
|
@@ -116,12 +115,17 @@ class PollingServiceClient {
|
|
|
116
115
|
throw new Error('polling service call requires an execution token (ctx.executionToken)');
|
|
117
116
|
}
|
|
118
117
|
const url = resolveEndpoint();
|
|
118
|
+
// A buffered body with an explicit Content-Length: streamed bodies go out
|
|
119
|
+
// with Transfer-Encoding: chunked, and chunked DELETE bodies get dropped
|
|
120
|
+
// in transit, so the polling service receives an empty body and responds 400.
|
|
121
|
+
const bodyBuffer = Buffer.from(JSON.stringify(body), 'utf8');
|
|
119
122
|
const request = Http_1.HttpRequest.create({
|
|
120
123
|
headers: {
|
|
121
124
|
[Http_1.HttpHeader.ContentType]: 'application/json',
|
|
125
|
+
[Http_1.HttpHeader.ContentLength]: String(bodyBuffer.byteLength),
|
|
122
126
|
[EXECUTION_TOKEN_HEADER]: executionToken,
|
|
123
127
|
},
|
|
124
|
-
body:
|
|
128
|
+
body: BufferExtensions_1.BufferExtensions.bufferToArrayBuffer(bodyBuffer),
|
|
125
129
|
});
|
|
126
130
|
const responseE = await this.httpClient.execute(method, url, request)();
|
|
127
131
|
if (E.isLeft(responseE)) {
|
|
@@ -33,16 +33,13 @@ const ENDPOINT_PATH = '/triggers/register';
|
|
|
33
33
|
const ENV_VAR = 'TRAY_POLLING_SERVICE_URL';
|
|
34
34
|
const createMockHttpClient = (capture) => ({
|
|
35
35
|
execute: jest.fn().mockImplementation((method, url, request) => TE.tryCatch(async () => {
|
|
36
|
-
const
|
|
37
|
-
for await (const chunk of request.body) {
|
|
38
|
-
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
|
|
39
|
-
}
|
|
40
|
-
const raw = Buffer.concat(chunks).toString('utf8');
|
|
36
|
+
const raw = Buffer.from(request.body).toString('utf8');
|
|
41
37
|
capture.push({
|
|
42
38
|
method,
|
|
43
39
|
url,
|
|
44
40
|
headers: request.headers,
|
|
45
41
|
body: raw.length > 0 ? JSON.parse(raw) : undefined,
|
|
42
|
+
bodyByteLength: Buffer.byteLength(raw, 'utf8'),
|
|
46
43
|
});
|
|
47
44
|
return {
|
|
48
45
|
statusCode: 200,
|
|
@@ -107,6 +104,7 @@ describe('PollingServiceClient', () => {
|
|
|
107
104
|
expect(call.url).toBe(`${BASE_URL}${ENDPOINT_PATH}`);
|
|
108
105
|
expect(call.headers).toMatchObject({
|
|
109
106
|
'Content-Type': 'application/json',
|
|
107
|
+
'Content-Length': String(call.bodyByteLength),
|
|
110
108
|
'X-Tray-Execution-Token': 'exec-jwt-token',
|
|
111
109
|
});
|
|
112
110
|
expect(call.body).toEqual({
|
|
@@ -239,8 +237,11 @@ describe('PollingServiceClient', () => {
|
|
|
239
237
|
expect(call.method).toBe(Http_1.HttpMethod.Delete);
|
|
240
238
|
expect(call.url).toBe(`${BASE_URL}${ENDPOINT_PATH}`);
|
|
241
239
|
expect(call.headers).toMatchObject({
|
|
240
|
+
'Content-Type': 'application/json',
|
|
241
|
+
'Content-Length': String(call.bodyByteLength),
|
|
242
242
|
'X-Tray-Execution-Token': 'exec-jwt-token',
|
|
243
243
|
});
|
|
244
|
+
expect(call.bodyByteLength).toBeGreaterThan(0);
|
|
244
245
|
expect(call.body).toEqual({
|
|
245
246
|
connectorName: 'salesforce',
|
|
246
247
|
connectorVersion: '7.2.0',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trayio/cdk-runtime",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.28.0",
|
|
4
4
|
"description": "A Runtime that executes connector operations defined using the CDK DSL",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./*": "./dist/*.js"
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"node": ">=18.x"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@trayio/axios": "5.
|
|
18
|
-
"@trayio/cdk-dsl": "5.
|
|
19
|
-
"@trayio/express": "5.
|
|
20
|
-
"@trayio/winston": "5.
|
|
17
|
+
"@trayio/axios": "5.28.0",
|
|
18
|
+
"@trayio/cdk-dsl": "5.28.0",
|
|
19
|
+
"@trayio/express": "5.28.0",
|
|
20
|
+
"@trayio/winston": "5.28.0",
|
|
21
21
|
"mime": "3.0.0",
|
|
22
22
|
"uuid": "9.0.0"
|
|
23
23
|
},
|