@trayio/cdk-runtime 5.26.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.
@@ -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;AA4BF;;;;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;CAgCtB"}
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=PollingServiceClient.integration.test.d.ts.map
@@ -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: bodyToReadable(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 chunks = [];
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.26.0",
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.26.0",
18
- "@trayio/cdk-dsl": "5.26.0",
19
- "@trayio/express": "5.26.0",
20
- "@trayio/winston": "5.26.0",
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
  },