@zapier/zapier-sdk 0.15.3 → 0.15.8

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/api/auth.d.ts +10 -0
  3. package/dist/api/auth.d.ts.map +1 -1
  4. package/dist/api/auth.js +45 -0
  5. package/dist/api/auth.test.d.ts +2 -0
  6. package/dist/api/auth.test.d.ts.map +1 -0
  7. package/dist/api/auth.test.js +220 -0
  8. package/dist/api/client.d.ts.map +1 -1
  9. package/dist/api/client.js +18 -32
  10. package/dist/api/client.methods.test.d.ts +2 -0
  11. package/dist/api/client.methods.test.d.ts.map +1 -0
  12. package/dist/api/client.methods.test.js +158 -0
  13. package/dist/api/client.test.js +27 -11
  14. package/dist/api/router.d.ts +16 -0
  15. package/dist/api/router.d.ts.map +1 -0
  16. package/dist/api/router.js +37 -0
  17. package/dist/api/router.test.d.ts +2 -0
  18. package/dist/api/router.test.d.ts.map +1 -0
  19. package/dist/api/router.test.js +109 -0
  20. package/dist/api/schemas.d.ts +38 -38
  21. package/dist/auth.d.ts +15 -0
  22. package/dist/auth.d.ts.map +1 -1
  23. package/dist/auth.js +25 -0
  24. package/dist/index.cjs +350 -87
  25. package/dist/index.d.mts +430 -269
  26. package/dist/index.mjs +350 -88
  27. package/dist/plugins/eventEmission/index.d.ts +1 -1
  28. package/dist/plugins/eventEmission/index.d.ts.map +1 -1
  29. package/dist/plugins/eventEmission/index.js +94 -22
  30. package/dist/plugins/eventEmission/index.test.js +340 -2
  31. package/dist/plugins/getAuthentication/index.d.ts +2 -5
  32. package/dist/plugins/getAuthentication/index.d.ts.map +1 -1
  33. package/dist/plugins/getAuthentication/index.js +3 -24
  34. package/dist/plugins/getAuthentication/index.test.js +32 -144
  35. package/dist/plugins/getAuthentication/schemas.d.ts +4 -13
  36. package/dist/plugins/getAuthentication/schemas.d.ts.map +1 -1
  37. package/dist/plugins/getAuthentication/schemas.js +1 -11
  38. package/dist/schemas/Action.d.ts +1 -1
  39. package/dist/schemas/Auth.d.ts +6 -6
  40. package/dist/sdk.d.ts +1 -1
  41. package/dist/temporary-internal-core/handlers/getAuthentication.d.ts +94 -0
  42. package/dist/temporary-internal-core/handlers/getAuthentication.d.ts.map +1 -0
  43. package/dist/temporary-internal-core/handlers/getAuthentication.js +68 -0
  44. package/dist/temporary-internal-core/handlers/getAuthentication.test.d.ts +2 -0
  45. package/dist/temporary-internal-core/handlers/getAuthentication.test.d.ts.map +1 -0
  46. package/dist/temporary-internal-core/handlers/getAuthentication.test.js +248 -0
  47. package/dist/temporary-internal-core/handlers/listApps.js +1 -1
  48. package/dist/temporary-internal-core/index.d.ts +2 -0
  49. package/dist/temporary-internal-core/index.d.ts.map +1 -1
  50. package/dist/temporary-internal-core/index.js +2 -0
  51. package/dist/temporary-internal-core/schemas/authentications/index.d.ts +454 -0
  52. package/dist/temporary-internal-core/schemas/authentications/index.d.ts.map +1 -0
  53. package/dist/temporary-internal-core/schemas/authentications/index.js +96 -0
  54. package/dist/temporary-internal-core/schemas/errors/index.d.ts +139 -0
  55. package/dist/temporary-internal-core/schemas/errors/index.d.ts.map +1 -0
  56. package/dist/temporary-internal-core/schemas/errors/index.js +129 -0
  57. package/dist/temporary-internal-core/utils/app-locators.d.ts +0 -20
  58. package/dist/temporary-internal-core/utils/app-locators.d.ts.map +1 -1
  59. package/dist/temporary-internal-core/utils/app-locators.js +1 -45
  60. package/dist/temporary-internal-core/utils/string-utils.d.ts +28 -0
  61. package/dist/temporary-internal-core/utils/string-utils.d.ts.map +1 -0
  62. package/dist/temporary-internal-core/utils/string-utils.js +52 -0
  63. package/dist/temporary-internal-core/utils/transformations.d.ts +14 -0
  64. package/dist/temporary-internal-core/utils/transformations.d.ts.map +1 -1
  65. package/dist/temporary-internal-core/utils/transformations.js +37 -1
  66. package/package.json +1 -1
@@ -6,14 +6,18 @@
6
6
  */
7
7
  import { createTransport } from "./transport";
8
8
  import { generateEventId, getCurrentTimestamp, getReleaseId } from "./utils";
9
+ import { extractUserIdsFromJwt } from "../../api/auth";
10
+ import { resolveAuthToken } from "../../auth";
9
11
  import { buildApplicationLifecycleEvent, buildErrorEventWithContext, } from "./builders";
10
12
  import { getTrackingBaseUrl } from "../../utils/url-utils";
13
+ // Maximum time to wait for telemetry emission before allowing process to exit
14
+ const TELEMETRY_EMIT_TIMEOUT_MS = 300;
11
15
  const APPLICATION_LIFECYCLE_EVENT_SUBJECT = "platform.sdk.ApplicationLifecycleEvent";
12
16
  const ERROR_OCCURRED_EVENT_SUBJECT = "platform.sdk.ErrorOccurredEvent";
13
17
  // Track transport success/failure so we only log failure once.
14
18
  const transportStates = new WeakMap();
15
19
  // Silent emission wrapper with smart first-failure logging
16
- async function silentEmit(transport, subject, event) {
20
+ async function silentEmit(transport, subject, event, userContextPromise) {
17
21
  try {
18
22
  // Get or initialize state for this transport
19
23
  let state = transportStates.get(transport);
@@ -21,9 +25,21 @@ async function silentEmit(transport, subject, event) {
21
25
  state = { hasWorked: false, hasLoggedFailure: false };
22
26
  transportStates.set(transport, state);
23
27
  }
28
+ // Resolve user context and merge into event
29
+ let enrichedEvent = event;
30
+ if (userContextPromise) {
31
+ try {
32
+ const userContext = await userContextPromise;
33
+ // Use Object.assign to safely merge user context into event
34
+ enrichedEvent = Object.assign({}, event, userContext);
35
+ }
36
+ catch {
37
+ // If user context promise fails, continue with original event
38
+ }
39
+ }
24
40
  // Fire and forget - don't await the transport
25
41
  transport
26
- .emit(subject, event)
42
+ .emit(subject, enrichedEvent)
27
43
  .then(() => {
28
44
  // Mark as working if any emit succeeds
29
45
  state.hasWorked = true;
@@ -76,6 +92,27 @@ export const eventEmissionPlugin = ({ context }) => {
76
92
  : // Otherwise, use option transport or default
77
93
  (context.options.eventEmission?.transport ?? defaultTransport),
78
94
  };
95
+ // Create getUserContext promise for dynamic user context injection
96
+ const getUserContext = (async () => {
97
+ try {
98
+ const token = await resolveAuthToken({
99
+ token: context.options.token,
100
+ getToken: context.options.getToken,
101
+ baseUrl: context.options.baseUrl,
102
+ authBaseUrl: context.options.authBaseUrl,
103
+ authClientId: context.options.authClientId,
104
+ onEvent: context.options.onEvent,
105
+ fetch: context.options.fetch,
106
+ });
107
+ if (token) {
108
+ return extractUserIdsFromJwt(token);
109
+ }
110
+ }
111
+ catch {
112
+ // Fall back to null context on any error
113
+ }
114
+ return { customuser_id: null, account_id: null };
115
+ })();
79
116
  const startupTime = Date.now();
80
117
  let shutdownStartTime = null;
81
118
  // If disabled, return noop implementations
@@ -86,7 +123,7 @@ export const eventEmissionPlugin = ({ context }) => {
86
123
  transport: createTransport({ type: "noop" }),
87
124
  config,
88
125
  emit: () => { },
89
- createBaseEvent: () => ({
126
+ createBaseEvent: async () => ({
90
127
  event_id: generateEventId(),
91
128
  timestamp_ms: getCurrentTimestamp(),
92
129
  release_id: getReleaseId(),
@@ -109,23 +146,34 @@ export const eventEmissionPlugin = ({ context }) => {
109
146
  transport = createTransport({ type: "noop" });
110
147
  }
111
148
  // Helper to create base event
112
- const createBaseEventHelper = () => ({
113
- event_id: generateEventId(),
114
- timestamp_ms: getCurrentTimestamp(),
115
- release_id: getReleaseId(),
116
- customuser_id: null,
117
- account_id: null,
118
- identity_id: null,
119
- visitor_id: null,
120
- correlation_id: null,
121
- });
149
+ const createBaseEventHelper = async () => {
150
+ const baseEvent = {
151
+ event_id: generateEventId(),
152
+ timestamp_ms: getCurrentTimestamp(),
153
+ release_id: getReleaseId(),
154
+ customuser_id: null,
155
+ account_id: null,
156
+ identity_id: null,
157
+ visitor_id: null,
158
+ correlation_id: null,
159
+ };
160
+ // Enrich with user context if available
161
+ try {
162
+ const userContext = await getUserContext;
163
+ return { ...baseEvent, ...userContext };
164
+ }
165
+ catch {
166
+ // Return base event if user context fails
167
+ return baseEvent;
168
+ }
169
+ };
122
170
  // Register lifecycle event handlers if enabled
123
171
  if (config.enabled) {
124
172
  // Emit startup event
125
173
  const startupEvent = buildApplicationLifecycleEvent({
126
174
  lifecycle_event_type: "startup",
127
175
  });
128
- silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, startupEvent);
176
+ silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, startupEvent, getUserContext);
129
177
  // Register process event handlers (Node.js only)
130
178
  if (typeof process?.on === "function") {
131
179
  // Handle normal process exit
@@ -141,11 +189,11 @@ export const eventEmissionPlugin = ({ context }) => {
141
189
  is_graceful_shutdown: code === 0,
142
190
  shutdown_duration_ms: shutdownDuration,
143
191
  });
144
- silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, exitEvent);
192
+ silentEmit(transport, APPLICATION_LIFECYCLE_EVENT_SUBJECT, exitEvent, getUserContext);
145
193
  });
146
194
  // Handle uncaught exceptions
147
195
  process.on("uncaughtException", async (error) => {
148
- const errorEvent = buildErrorEventWithContext({
196
+ let errorEvent = buildErrorEventWithContext({
149
197
  error_message: error.message || "Unknown error",
150
198
  error_type: "UncaughtException",
151
199
  error_stack_trace: error.stack || null,
@@ -154,11 +202,19 @@ export const eventEmissionPlugin = ({ context }) => {
154
202
  is_recoverable: false,
155
203
  execution_start_time: startupTime,
156
204
  });
205
+ // Enrich with user context if available
206
+ try {
207
+ const userContext = await getUserContext;
208
+ errorEvent = { ...errorEvent, ...userContext };
209
+ }
210
+ catch {
211
+ // Continue with original event if user context fails
212
+ }
157
213
  // Wait up to 300ms for telemetry to send before allowing process to exit
158
214
  try {
159
215
  await Promise.race([
160
216
  transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
161
- new Promise((resolve) => setTimeout(resolve, 300)),
217
+ new Promise((resolve) => setTimeout(resolve, TELEMETRY_EMIT_TIMEOUT_MS)),
162
218
  ]);
163
219
  }
164
220
  catch {
@@ -174,7 +230,7 @@ export const eventEmissionPlugin = ({ context }) => {
174
230
  ? reason
175
231
  : "Unhandled promise rejection";
176
232
  const errorStack = reason instanceof Error ? reason.stack : null;
177
- const errorEvent = buildErrorEventWithContext({
233
+ let errorEvent = buildErrorEventWithContext({
178
234
  error_message: errorMessage,
179
235
  error_type: "UnhandledRejection",
180
236
  error_stack_trace: errorStack,
@@ -186,11 +242,19 @@ export const eventEmissionPlugin = ({ context }) => {
186
242
  promise: String(promise),
187
243
  },
188
244
  });
245
+ // Enrich with user context if available
246
+ try {
247
+ const userContext = await getUserContext;
248
+ errorEvent = { ...errorEvent, ...userContext };
249
+ }
250
+ catch {
251
+ // Continue with original event if user context fails
252
+ }
189
253
  // Wait up to 300ms for telemetry to send
190
254
  try {
191
255
  await Promise.race([
192
256
  transport.emit(ERROR_OCCURRED_EVENT_SUBJECT, errorEvent),
193
- new Promise((resolve) => setTimeout(resolve, 300)),
257
+ new Promise((resolve) => setTimeout(resolve, TELEMETRY_EMIT_TIMEOUT_MS)),
194
258
  ]);
195
259
  }
196
260
  catch {
@@ -201,17 +265,25 @@ export const eventEmissionPlugin = ({ context }) => {
201
265
  const handleSignal = async (signal) => {
202
266
  shutdownStartTime = Date.now();
203
267
  const uptime = Date.now() - startupTime;
204
- const signalEvent = buildApplicationLifecycleEvent({
268
+ let signalEvent = buildApplicationLifecycleEvent({
205
269
  lifecycle_event_type: "signal_termination",
206
270
  signal_name: signal,
207
271
  uptime_ms: uptime,
208
272
  is_graceful_shutdown: true,
209
273
  });
274
+ // Enrich with user context if available
275
+ try {
276
+ const userContext = await getUserContext;
277
+ signalEvent = { ...signalEvent, ...userContext };
278
+ }
279
+ catch {
280
+ // Continue with original event if user context fails
281
+ }
210
282
  // Wait up to 300ms for telemetry to send
211
283
  try {
212
284
  await Promise.race([
213
285
  transport.emit(APPLICATION_LIFECYCLE_EVENT_SUBJECT, signalEvent),
214
- new Promise((resolve) => setTimeout(resolve, 300)),
286
+ new Promise((resolve) => setTimeout(resolve, TELEMETRY_EMIT_TIMEOUT_MS)),
215
287
  ]);
216
288
  }
217
289
  catch {
@@ -231,7 +303,7 @@ export const eventEmissionPlugin = ({ context }) => {
231
303
  transport,
232
304
  config,
233
305
  emit: (subject, event) => {
234
- silentEmit(transport, subject, event);
306
+ silentEmit(transport, subject, event, getUserContext);
235
307
  },
236
308
  createBaseEvent: createBaseEventHelper,
237
309
  },
@@ -12,9 +12,16 @@ const mockTransport = {
12
12
  vi.mock("./transport", () => ({
13
13
  createTransport: vi.fn(() => mockTransport),
14
14
  }));
15
+ // Mock CLI login package - default to returning null context
16
+ const mockGetToken = vi.fn().mockResolvedValue(undefined);
17
+ vi.mock("@zapier/zapier-sdk-cli-login", () => ({
18
+ getToken: mockGetToken,
19
+ }));
15
20
  describe("eventEmissionPlugin", () => {
16
21
  beforeEach(() => {
17
22
  vi.clearAllMocks();
23
+ // Reset to default behavior - no token available
24
+ mockGetToken.mockResolvedValue(undefined);
18
25
  });
19
26
  it("should create plugin with default configuration", () => {
20
27
  const plugin = eventEmissionPlugin({
@@ -64,8 +71,13 @@ describe("eventEmissionPlugin", () => {
64
71
  const testSubject = "test.event.TestEvent";
65
72
  plugin.context.eventEmission.emit(testSubject, testEvent);
66
73
  // Give async emission time to complete
67
- await new Promise((resolve) => setTimeout(resolve, 0));
68
- expect(mockTransport.emit).toHaveBeenCalledWith(testSubject, testEvent);
74
+ await new Promise((resolve) => setTimeout(resolve, 50));
75
+ // The event will be enriched with user context (null values)
76
+ expect(mockTransport.emit).toHaveBeenCalledWith(testSubject, {
77
+ ...testEvent,
78
+ customuser_id: null,
79
+ account_id: null,
80
+ });
69
81
  });
70
82
  it("should handle transport creation failures silently", () => {
71
83
  // Mock createTransport to throw an error
@@ -220,6 +232,8 @@ describe("eventEmissionPlugin", () => {
220
232
  mockConsoleWarn.mockRestore();
221
233
  });
222
234
  it("should merge options with defaults", () => {
235
+ // Override env var to ensure proper transport is used
236
+ vi.stubEnv("ZAPIER_SDK_TELEMETRY_TRANSPORT", undefined);
223
237
  const plugin = eventEmissionPlugin({
224
238
  sdk: {},
225
239
  context: {
@@ -239,5 +253,329 @@ describe("eventEmissionPlugin", () => {
239
253
  type: "http",
240
254
  endpoint: "https://example.com",
241
255
  });
256
+ vi.unstubAllEnvs();
257
+ });
258
+ it("should extract user IDs from JWT token and include in events", async () => {
259
+ // Create a test JWT token with user data
260
+ // JWT format: header.payload.signature
261
+ const header = { alg: "HS256", typ: "JWT" };
262
+ const payload = {
263
+ "zap:acc": "12345",
264
+ sub: "67890",
265
+ sub_type: "customuser",
266
+ "zap:uname": "test@example.com",
267
+ };
268
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
269
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
270
+ const testJwt = `${encodedHeader}.${encodedPayload}.fake-signature`;
271
+ // Mock getToken to return the JWT
272
+ mockGetToken.mockResolvedValue(testJwt);
273
+ const plugin = eventEmissionPlugin({
274
+ sdk: {},
275
+ context: {
276
+ meta: {},
277
+ options: {
278
+ eventEmission: {
279
+ enabled: true,
280
+ transport: { type: "console" },
281
+ },
282
+ },
283
+ },
284
+ });
285
+ // Test that createBaseEvent includes the extracted user IDs
286
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
287
+ expect(baseEvent.customuser_id).toBe(67890);
288
+ expect(baseEvent.account_id).toBe(12345);
289
+ });
290
+ it("should handle service tokens with nested JWT", async () => {
291
+ // Create a nested JWT for service token testing
292
+ const nestedHeader = { alg: "HS256", typ: "JWT" };
293
+ const nestedPayload = {
294
+ "zap:acc": "99999",
295
+ sub: "88888",
296
+ sub_type: "customuser",
297
+ };
298
+ const nestedEncodedHeader = Buffer.from(JSON.stringify(nestedHeader)).toString("base64url");
299
+ const nestedEncodedPayload = Buffer.from(JSON.stringify(nestedPayload)).toString("base64url");
300
+ const nestedJwt = `${nestedEncodedHeader}.${nestedEncodedPayload}.nested-signature`;
301
+ // Create the service token that wraps the nested JWT
302
+ const serviceHeader = { alg: "HS256", typ: "JWT" };
303
+ const servicePayload = {
304
+ "zap:acc": "11111",
305
+ sub: "22222",
306
+ sub_type: "service",
307
+ njwt: nestedJwt,
308
+ };
309
+ const serviceEncodedHeader = Buffer.from(JSON.stringify(serviceHeader)).toString("base64url");
310
+ const serviceEncodedPayload = Buffer.from(JSON.stringify(servicePayload)).toString("base64url");
311
+ const serviceJwt = `${serviceEncodedHeader}.${serviceEncodedPayload}.service-signature`;
312
+ // Mock getToken to return the service JWT
313
+ mockGetToken.mockResolvedValue(serviceJwt);
314
+ const plugin = eventEmissionPlugin({
315
+ sdk: {},
316
+ context: {
317
+ meta: {},
318
+ options: {
319
+ eventEmission: {
320
+ enabled: true,
321
+ transport: { type: "console" },
322
+ },
323
+ },
324
+ },
325
+ });
326
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
327
+ // Should extract from nested JWT, not the service token
328
+ expect(baseEvent.customuser_id).toBe(88888);
329
+ expect(baseEvent.account_id).toBe(99999);
330
+ });
331
+ it("should handle invalid JWT tokens gracefully", async () => {
332
+ // Mock getToken to return an invalid JWT
333
+ mockGetToken.mockResolvedValue("not-a-valid-jwt-token");
334
+ const plugin = eventEmissionPlugin({
335
+ sdk: {},
336
+ context: {
337
+ meta: {},
338
+ options: {
339
+ eventEmission: {
340
+ enabled: true,
341
+ transport: { type: "console" },
342
+ },
343
+ },
344
+ },
345
+ });
346
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
347
+ // Should default to null when JWT is invalid
348
+ expect(baseEvent.customuser_id).toBe(null);
349
+ expect(baseEvent.account_id).toBe(null);
350
+ });
351
+ it("should handle missing token gracefully", async () => {
352
+ // mockGetToken defaults to returning undefined (no token)
353
+ const plugin = eventEmissionPlugin({
354
+ sdk: {},
355
+ context: {
356
+ meta: {},
357
+ options: {
358
+ eventEmission: {
359
+ enabled: true,
360
+ transport: { type: "console" },
361
+ },
362
+ },
363
+ },
364
+ });
365
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
366
+ // Should default to null when no token is provided
367
+ expect(baseEvent.customuser_id).toBe(null);
368
+ expect(baseEvent.account_id).toBe(null);
369
+ });
370
+ it("should extract user IDs when getToken returns valid JWT", async () => {
371
+ // Create a test JWT token
372
+ const header = { alg: "HS256", typ: "JWT" };
373
+ const payload = {
374
+ "zap:acc": "98765",
375
+ sub: "54321",
376
+ sub_type: "customuser",
377
+ };
378
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
379
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
380
+ const testJwt = `${encodedHeader}.${encodedPayload}.test-signature`;
381
+ // Mock getToken to return the JWT
382
+ mockGetToken.mockResolvedValue(testJwt);
383
+ const plugin = eventEmissionPlugin({
384
+ sdk: {},
385
+ context: {
386
+ meta: {},
387
+ options: {
388
+ eventEmission: {
389
+ enabled: true,
390
+ transport: { type: "console" },
391
+ },
392
+ },
393
+ },
394
+ });
395
+ // Test that createBaseEvent includes the extracted user IDs
396
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
397
+ expect(baseEvent.customuser_id).toBe(54321);
398
+ expect(baseEvent.account_id).toBe(98765);
399
+ });
400
+ it("should handle getToken failures gracefully", async () => {
401
+ // Mock getToken to reject/throw
402
+ mockGetToken.mockRejectedValue(new Error("Token fetch failed"));
403
+ const plugin = eventEmissionPlugin({
404
+ sdk: {},
405
+ context: {
406
+ meta: {},
407
+ options: {
408
+ eventEmission: {
409
+ enabled: true,
410
+ transport: { type: "console" },
411
+ },
412
+ },
413
+ },
414
+ });
415
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
416
+ // Should gracefully fall back to null context
417
+ expect(baseEvent.customuser_id).toBe(null);
418
+ expect(baseEvent.account_id).toBe(null);
419
+ });
420
+ it("should extract user IDs from static token in SDK options", async () => {
421
+ // Create a test JWT token
422
+ const header = { alg: "HS256", typ: "JWT" };
423
+ const payload = {
424
+ "zap:acc": "11111",
425
+ sub: "22222",
426
+ sub_type: "customuser",
427
+ };
428
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
429
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
430
+ const testJwt = `${encodedHeader}.${encodedPayload}.static-signature`;
431
+ const plugin = eventEmissionPlugin({
432
+ sdk: {},
433
+ context: {
434
+ meta: {},
435
+ options: {
436
+ token: testJwt,
437
+ eventEmission: {
438
+ enabled: true,
439
+ transport: { type: "console" },
440
+ },
441
+ },
442
+ },
443
+ });
444
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
445
+ // Should extract from static token in options
446
+ expect(baseEvent.customuser_id).toBe(22222);
447
+ expect(baseEvent.account_id).toBe(11111);
448
+ // CLI login package should not be called when token is in options
449
+ expect(mockGetToken).not.toHaveBeenCalled();
450
+ });
451
+ it("should extract user IDs from getToken function in SDK options", async () => {
452
+ // Create a test JWT token
453
+ const header = { alg: "HS256", typ: "JWT" };
454
+ const payload = {
455
+ "zap:acc": "33333",
456
+ sub: "44444",
457
+ sub_type: "customuser",
458
+ };
459
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
460
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
461
+ const testJwt = `${encodedHeader}.${encodedPayload}.custom-signature`;
462
+ const customGetToken = vi.fn().mockResolvedValue(testJwt);
463
+ const plugin = eventEmissionPlugin({
464
+ sdk: {},
465
+ context: {
466
+ meta: {},
467
+ options: {
468
+ getToken: customGetToken,
469
+ eventEmission: {
470
+ enabled: true,
471
+ transport: { type: "console" },
472
+ },
473
+ },
474
+ },
475
+ });
476
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
477
+ // Should extract from custom getToken function
478
+ expect(baseEvent.customuser_id).toBe(44444);
479
+ expect(baseEvent.account_id).toBe(33333);
480
+ expect(customGetToken).toHaveBeenCalled();
481
+ // CLI login package should not be called when getToken is in options
482
+ expect(mockGetToken).not.toHaveBeenCalled();
483
+ });
484
+ it("should prioritize static token over getToken function", async () => {
485
+ // Create test JWT tokens
486
+ const staticHeader = { alg: "HS256", typ: "JWT" };
487
+ const staticPayload = {
488
+ "zap:acc": "55555",
489
+ sub: "66666",
490
+ sub_type: "customuser",
491
+ };
492
+ const staticEncodedHeader = Buffer.from(JSON.stringify(staticHeader)).toString("base64url");
493
+ const staticEncodedPayload = Buffer.from(JSON.stringify(staticPayload)).toString("base64url");
494
+ const staticJwt = `${staticEncodedHeader}.${staticEncodedPayload}.static-sig`;
495
+ const customGetToken = vi.fn().mockResolvedValue("should-not-be-used");
496
+ const plugin = eventEmissionPlugin({
497
+ sdk: {},
498
+ context: {
499
+ meta: {},
500
+ options: {
501
+ token: staticJwt,
502
+ getToken: customGetToken,
503
+ eventEmission: {
504
+ enabled: true,
505
+ transport: { type: "console" },
506
+ },
507
+ },
508
+ },
509
+ });
510
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
511
+ // Should use static token, not getToken
512
+ expect(baseEvent.customuser_id).toBe(66666);
513
+ expect(baseEvent.account_id).toBe(55555);
514
+ expect(customGetToken).not.toHaveBeenCalled();
515
+ expect(mockGetToken).not.toHaveBeenCalled();
516
+ });
517
+ it("should fall back to CLI login when SDK options have no token", async () => {
518
+ // Create a test JWT token for CLI login
519
+ const header = { alg: "HS256", typ: "JWT" };
520
+ const payload = {
521
+ "zap:acc": "77777",
522
+ sub: "88888",
523
+ sub_type: "customuser",
524
+ };
525
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
526
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
527
+ const testJwt = `${encodedHeader}.${encodedPayload}.cli-signature`;
528
+ mockGetToken.mockResolvedValue(testJwt);
529
+ const plugin = eventEmissionPlugin({
530
+ sdk: {},
531
+ context: {
532
+ meta: {},
533
+ options: {
534
+ // No token or getToken in options
535
+ eventEmission: {
536
+ enabled: true,
537
+ transport: { type: "console" },
538
+ },
539
+ },
540
+ },
541
+ });
542
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
543
+ // Should fall back to CLI login package
544
+ expect(baseEvent.customuser_id).toBe(88888);
545
+ expect(baseEvent.account_id).toBe(77777);
546
+ expect(mockGetToken).toHaveBeenCalled();
547
+ });
548
+ it("should handle custom getToken returning undefined", async () => {
549
+ const customGetToken = vi.fn().mockResolvedValue(undefined);
550
+ // Also mock CLI login to return a token
551
+ const header = { alg: "HS256", typ: "JWT" };
552
+ const payload = {
553
+ "zap:acc": "99999",
554
+ sub: "10101",
555
+ sub_type: "customuser",
556
+ };
557
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
558
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
559
+ const testJwt = `${encodedHeader}.${encodedPayload}.fallback-signature`;
560
+ mockGetToken.mockResolvedValue(testJwt);
561
+ const plugin = eventEmissionPlugin({
562
+ sdk: {},
563
+ context: {
564
+ meta: {},
565
+ options: {
566
+ getToken: customGetToken,
567
+ eventEmission: {
568
+ enabled: true,
569
+ transport: { type: "console" },
570
+ },
571
+ },
572
+ },
573
+ });
574
+ const baseEvent = await plugin.context.eventEmission.createBaseEvent();
575
+ // Should fall back to CLI login when custom getToken returns undefined
576
+ expect(baseEvent.customuser_id).toBe(10101);
577
+ expect(baseEvent.account_id).toBe(99999);
578
+ expect(customGetToken).toHaveBeenCalled();
579
+ expect(mockGetToken).toHaveBeenCalled();
242
580
  });
243
581
  });
@@ -1,11 +1,8 @@
1
1
  import type { Plugin } from "../../types/plugin";
2
2
  import type { ApiClient } from "../../api";
3
- import type { AuthenticationItem } from "../../types/domain";
4
- import { GetAuthenticationSchema, type GetAuthenticationOptions } from "./schemas";
3
+ import { GetAuthenticationSchema, type GetAuthenticationOptions, type GetAuthenticationResponse } from "./schemas";
5
4
  export interface GetAuthenticationPluginProvides {
6
- getAuthentication: (options: GetAuthenticationOptions) => Promise<{
7
- data: AuthenticationItem;
8
- }>;
5
+ getAuthentication: (options: GetAuthenticationOptions) => Promise<GetAuthenticationResponse>;
9
6
  context: {
10
7
  meta: {
11
8
  getAuthentication: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/getAuthentication/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EACL,uBAAuB,EACvB,KAAK,wBAAwB,EAC9B,MAAM,WAAW,CAAC;AAUnB,MAAM,WAAW,+BAA+B;IAC9C,iBAAiB,EAAE,CACjB,OAAO,EAAE,wBAAwB,KAC9B,OAAO,CAAC;QAAE,IAAI,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC;IAC3C,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,iBAAiB,EAAE;gBACjB,WAAW,EAAE,OAAO,uBAAuB,CAAC;aAC7C,CAAC;SACH,CAAC;KACH,CAAC;CACH;AAED,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,EAAE,EAAE,sBAAsB;AAC1B;IAAE,GAAG,EAAE,SAAS,CAAA;CAAE,EAAE,0BAA0B;AAC9C,+BAA+B,CA6DhC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/getAuthentication/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EACL,uBAAuB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,WAAW,CAAC;AAKnB,MAAM,WAAW,+BAA+B;IAC9C,iBAAiB,EAAE,CACjB,OAAO,EAAE,wBAAwB,KAC9B,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACxC,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,iBAAiB,EAAE;gBACjB,WAAW,EAAE,OAAO,uBAAuB,CAAC;aAC7C,CAAC;SACH,CAAC;KACH,CAAC;CACH;AAED,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,EAAE,EAAE,sBAAsB;AAC1B;IAAE,GAAG,EAAE,SAAS,CAAA;CAAE,EAAE,0BAA0B;AAC9C,+BAA+B,CA+BhC,CAAC"}
@@ -1,34 +1,13 @@
1
1
  import { GetAuthenticationSchema, } from "./schemas";
2
- import { ZapierAuthenticationError, ZapierResourceNotFoundError, } from "../../types/errors";
3
2
  import { createFunction } from "../../utils/function-utils";
4
- import { normalizeAuthenticationItem } from "../../utils/domain-utils";
5
3
  import { authenticationIdGenericResolver } from "../../resolvers";
6
4
  import { AuthenticationItemSchema } from "../../schemas/Auth";
7
5
  export const getAuthenticationPlugin = ({ context }) => {
8
6
  const getAuthentication = createFunction(async function getAuthentication(options) {
9
7
  const { api } = context;
10
- const { authenticationId } = options;
11
- const data = await api.get(`/zapier/api/v4/authentications/${authenticationId}/`, {
12
- customErrorHandler: ({ status }) => {
13
- if (status === 401) {
14
- return new ZapierAuthenticationError(`Authentication failed. Your token may not have permission to access authentications or may be expired. (HTTP ${status})`, { statusCode: status });
15
- }
16
- if (status === 403) {
17
- return new ZapierAuthenticationError(`Access forbidden. Your token may not have the required scopes to get authentication ${authenticationId}. (HTTP ${status})`, { statusCode: status });
18
- }
19
- if (status === 404) {
20
- return new ZapierResourceNotFoundError(`Authentication ${authenticationId} not found. It may not exist or you may not have access to it. (HTTP ${status})`, {
21
- resourceType: "Authentication",
22
- resourceId: String(authenticationId),
23
- });
24
- }
25
- return undefined;
26
- },
27
- authRequired: true,
28
- });
29
- return {
30
- data: normalizeAuthenticationItem(data),
31
- };
8
+ // Call the SDK API endpoint, which will be routed to the handler via handlerOverride in pathConfig.
9
+ // When the handler is migrated to SDK API, this same API call will go over the network.
10
+ return await api.get(`/api/v0/authentications/${options.authenticationId}`);
32
11
  }, GetAuthenticationSchema);
33
12
  return {
34
13
  getAuthentication,