@zapier/zapier-sdk 0.13.2 → 0.13.4
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/CHANGELOG.md +12 -0
- package/dist/api/debug.d.ts.map +1 -1
- package/dist/api/debug.js +36 -1
- package/dist/api/schemas.d.ts +174 -174
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/index.cjs +475 -9
- package/dist/index.d.mts +381 -157
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.mjs +446 -13
- package/dist/plugins/api/index.d.ts +1 -3
- package/dist/plugins/api/index.d.ts.map +1 -1
- package/dist/plugins/eventEmission/builders.d.ts +13 -0
- package/dist/plugins/eventEmission/builders.d.ts.map +1 -0
- package/dist/plugins/eventEmission/builders.js +78 -0
- package/dist/plugins/eventEmission/index.d.ts +34 -0
- package/dist/plugins/eventEmission/index.d.ts.map +1 -0
- package/dist/plugins/eventEmission/index.js +216 -0
- package/dist/plugins/eventEmission/index.test.d.ts +5 -0
- package/dist/plugins/eventEmission/index.test.d.ts.map +1 -0
- package/dist/plugins/eventEmission/index.test.js +143 -0
- package/dist/plugins/eventEmission/transport.d.ts +37 -0
- package/dist/plugins/eventEmission/transport.d.ts.map +1 -0
- package/dist/plugins/eventEmission/transport.js +96 -0
- package/dist/plugins/eventEmission/transport.test.d.ts +5 -0
- package/dist/plugins/eventEmission/transport.test.d.ts.map +1 -0
- package/dist/plugins/eventEmission/transport.test.js +153 -0
- package/dist/plugins/eventEmission/types.d.ts +53 -0
- package/dist/plugins/eventEmission/types.d.ts.map +1 -0
- package/dist/plugins/eventEmission/types.js +1 -0
- package/dist/plugins/eventEmission/utils.d.ts +45 -0
- package/dist/plugins/eventEmission/utils.d.ts.map +1 -0
- package/dist/plugins/eventEmission/utils.js +114 -0
- package/dist/plugins/fetch/schemas.d.ts +4 -4
- package/dist/plugins/getAction/index.d.ts.map +1 -1
- package/dist/plugins/getAction/index.js +3 -2
- package/dist/plugins/getAction/schemas.d.ts +2 -2
- package/dist/plugins/listActions/schemas.d.ts +2 -2
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +4 -4
- package/dist/plugins/listInputFields/index.d.ts +2 -1
- package/dist/plugins/listInputFields/index.d.ts.map +1 -1
- package/dist/plugins/listInputFields/index.js +7 -2
- package/dist/plugins/listInputFields/index.test.js +36 -5
- package/dist/plugins/listInputFields/schemas.d.ts +2 -2
- package/dist/plugins/request/schemas.d.ts +4 -4
- package/dist/plugins/runAction/index.d.ts.map +1 -1
- package/dist/plugins/runAction/index.js +6 -1
- package/dist/plugins/runAction/schemas.d.ts +2 -2
- package/dist/resolvers/actionType.d.ts.map +1 -1
- package/dist/resolvers/actionType.js +2 -3
- package/dist/resolvers/authenticationId.d.ts.map +1 -1
- package/dist/schemas/Action.d.ts +2 -2
- package/dist/schemas/App.d.ts +30 -30
- package/dist/sdk.d.ts +2 -2
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +4 -1
- package/dist/types/sdk.d.ts +5 -1
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/types/telemetry-events.d.ts +76 -0
- package/dist/types/telemetry-events.d.ts.map +1 -0
- package/dist/types/telemetry-events.js +8 -0
- package/package.json +1 -1
- package/src/api/debug.ts +44 -1
- package/src/constants.ts +6 -0
- package/src/index.ts +24 -0
- package/src/plugins/api/index.ts +1 -5
- package/src/plugins/eventEmission/builders.ts +115 -0
- package/src/plugins/eventEmission/index.test.ts +169 -0
- package/src/plugins/eventEmission/index.ts +294 -0
- package/src/plugins/eventEmission/transport.test.ts +214 -0
- package/src/plugins/eventEmission/transport.ts +135 -0
- package/src/plugins/eventEmission/types.ts +58 -0
- package/src/plugins/eventEmission/utils.ts +121 -0
- package/src/plugins/getAction/index.ts +5 -2
- package/src/plugins/listInputFields/index.test.ts +37 -5
- package/src/plugins/listInputFields/index.ts +10 -3
- package/src/plugins/runAction/index.ts +9 -0
- package/src/resolvers/actionType.ts +4 -3
- package/src/resolvers/authenticationId.ts +2 -1
- package/src/sdk.ts +5 -1
- package/src/types/sdk.ts +7 -1
- package/src/types/telemetry-events.ts +85 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Event Transport Layer
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
HttpTransport,
|
|
8
|
+
ConsoleTransport,
|
|
9
|
+
NoopTransport,
|
|
10
|
+
createTransport,
|
|
11
|
+
} from "./transport";
|
|
12
|
+
import type { TelemetryEvent } from "../../types/telemetry-events";
|
|
13
|
+
|
|
14
|
+
// Mock fetch globally
|
|
15
|
+
const mockFetch = vi.fn();
|
|
16
|
+
global.fetch = mockFetch;
|
|
17
|
+
|
|
18
|
+
// Mock console.log
|
|
19
|
+
const mockConsoleLog = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
20
|
+
|
|
21
|
+
describe("Transport Layer", () => {
|
|
22
|
+
const sampleEvent: TelemetryEvent = {
|
|
23
|
+
event_id: "test-123",
|
|
24
|
+
timestamp_ms: Date.now(),
|
|
25
|
+
release_id: "test-release",
|
|
26
|
+
error_message: "Test error",
|
|
27
|
+
is_user_facing: false,
|
|
28
|
+
} as any;
|
|
29
|
+
|
|
30
|
+
const sampleSubject = "platform.sdk.ErrorOccurredEvent";
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
vi.clearAllMocks();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("HttpTransport", () => {
|
|
37
|
+
it("should emit events via HTTP successfully", async () => {
|
|
38
|
+
mockFetch.mockResolvedValueOnce({
|
|
39
|
+
ok: true,
|
|
40
|
+
status: 200,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const transport = new HttpTransport({
|
|
44
|
+
endpoint: "https://telemetry.example.com/events",
|
|
45
|
+
headers: { "x-api-key": "test-key" },
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await transport.emit(sampleSubject, sampleEvent);
|
|
49
|
+
|
|
50
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
51
|
+
"https://telemetry.example.com/events",
|
|
52
|
+
{
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"x-api-key": "test-key",
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
subject: sampleSubject,
|
|
60
|
+
properties: sampleEvent,
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should retry on HTTP failures", async () => {
|
|
67
|
+
// Fail twice, then succeed
|
|
68
|
+
mockFetch
|
|
69
|
+
.mockResolvedValueOnce({ ok: false, status: 500 })
|
|
70
|
+
.mockResolvedValueOnce({ ok: false, status: 500 })
|
|
71
|
+
.mockResolvedValueOnce({ ok: true, status: 200 });
|
|
72
|
+
|
|
73
|
+
const transport = new HttpTransport({
|
|
74
|
+
endpoint: "https://telemetry.example.com/events",
|
|
75
|
+
retryAttempts: 3,
|
|
76
|
+
retryDelayMs: 10, // Short delay for testing
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await transport.emit(sampleSubject, sampleEvent);
|
|
80
|
+
|
|
81
|
+
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should handle network errors silently", async () => {
|
|
85
|
+
mockFetch.mockRejectedValue(new Error("Network error"));
|
|
86
|
+
|
|
87
|
+
const transport = new HttpTransport({
|
|
88
|
+
endpoint: "https://telemetry.example.com/events",
|
|
89
|
+
retryAttempts: 1,
|
|
90
|
+
retryDelayMs: 1,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Should not throw despite network error
|
|
94
|
+
await expect(
|
|
95
|
+
transport.emit(sampleSubject, sampleEvent),
|
|
96
|
+
).resolves.toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should stop retrying after max attempts", async () => {
|
|
100
|
+
mockFetch.mockResolvedValue({ ok: false, status: 500 });
|
|
101
|
+
|
|
102
|
+
const transport = new HttpTransport({
|
|
103
|
+
endpoint: "https://telemetry.example.com/events",
|
|
104
|
+
retryAttempts: 2,
|
|
105
|
+
retryDelayMs: 1,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await transport.emit(sampleSubject, sampleEvent);
|
|
109
|
+
|
|
110
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("ConsoleTransport", () => {
|
|
115
|
+
it("should log events to console", async () => {
|
|
116
|
+
const transport = new ConsoleTransport();
|
|
117
|
+
|
|
118
|
+
await transport.emit(sampleSubject, sampleEvent);
|
|
119
|
+
|
|
120
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(
|
|
121
|
+
"[SDK Telemetry]",
|
|
122
|
+
JSON.stringify(
|
|
123
|
+
{ subject: sampleSubject, properties: sampleEvent },
|
|
124
|
+
null,
|
|
125
|
+
2,
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should handle console errors silently", async () => {
|
|
131
|
+
mockConsoleLog.mockImplementation(() => {
|
|
132
|
+
throw new Error("Console error");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const transport = new ConsoleTransport();
|
|
136
|
+
|
|
137
|
+
// Should not throw despite console error
|
|
138
|
+
await expect(
|
|
139
|
+
transport.emit(sampleSubject, sampleEvent),
|
|
140
|
+
).resolves.toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("NoopTransport", () => {
|
|
145
|
+
it("should do nothing when emitting events", async () => {
|
|
146
|
+
const transport = new NoopTransport();
|
|
147
|
+
|
|
148
|
+
await transport.emit(sampleSubject, sampleEvent);
|
|
149
|
+
|
|
150
|
+
// Verify no side effects
|
|
151
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
152
|
+
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("createTransport", () => {
|
|
157
|
+
it("should create HTTP transport with valid config", () => {
|
|
158
|
+
const transport = createTransport({
|
|
159
|
+
type: "http",
|
|
160
|
+
endpoint: "https://example.com",
|
|
161
|
+
headers: { "x-key": "value" },
|
|
162
|
+
retryAttempts: 5,
|
|
163
|
+
retryDelayMs: 2000,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(transport).toBeInstanceOf(HttpTransport);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should create console transport", () => {
|
|
170
|
+
const transport = createTransport({
|
|
171
|
+
type: "console",
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(transport).toBeInstanceOf(ConsoleTransport);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should create noop transport by default", () => {
|
|
178
|
+
const transport = createTransport({
|
|
179
|
+
type: "noop",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(transport).toBeInstanceOf(NoopTransport);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("should fallback to noop transport on invalid HTTP config", () => {
|
|
186
|
+
const transport = createTransport({
|
|
187
|
+
type: "http",
|
|
188
|
+
// Missing endpoint
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
expect(transport).toBeInstanceOf(NoopTransport);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should fallback to noop transport on unknown type", () => {
|
|
195
|
+
const transport = createTransport({
|
|
196
|
+
type: "unknown" as any,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(transport).toBeInstanceOf(NoopTransport);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should handle transport creation errors gracefully", () => {
|
|
203
|
+
// Since createTransport already handles errors internally,
|
|
204
|
+
// this test verifies that invalid configs don't throw
|
|
205
|
+
expect(() => {
|
|
206
|
+
const transport = createTransport({
|
|
207
|
+
type: "http" as const,
|
|
208
|
+
// Missing required endpoint - should fallback to noop
|
|
209
|
+
});
|
|
210
|
+
expect(transport).toBeInstanceOf(NoopTransport);
|
|
211
|
+
}).not.toThrow();
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport abstraction layer for event emission
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable transport mechanisms for emitting telemetry events.
|
|
5
|
+
* All transports implement silent failure to prevent SDK disruption.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Constants for transport configuration
|
|
9
|
+
const DEFAULT_RETRY_ATTEMPTS = 2;
|
|
10
|
+
const DEFAULT_RETRY_DELAY_MS = 300;
|
|
11
|
+
|
|
12
|
+
// Transport interface - all implementations must be async and silent
|
|
13
|
+
export interface EventTransport {
|
|
14
|
+
emit<T extends any>(subject: string, event: T): Promise<void>;
|
|
15
|
+
close?(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Transport configuration
|
|
19
|
+
export interface TransportConfig {
|
|
20
|
+
type: "http" | "console" | "noop";
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
retryAttempts?: number;
|
|
24
|
+
retryDelayMs?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// HTTP Transport - sends events to remote endpoint
|
|
28
|
+
export class HttpTransport implements EventTransport {
|
|
29
|
+
constructor(
|
|
30
|
+
private config: {
|
|
31
|
+
endpoint: string;
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
retryAttempts?: number;
|
|
34
|
+
retryDelayMs?: number;
|
|
35
|
+
},
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
async emit<T extends any>(subject: string, event: T): Promise<void> {
|
|
39
|
+
try {
|
|
40
|
+
await this.emitWithRetry(
|
|
41
|
+
subject,
|
|
42
|
+
event,
|
|
43
|
+
this.config.retryAttempts || DEFAULT_RETRY_ATTEMPTS,
|
|
44
|
+
);
|
|
45
|
+
} catch {
|
|
46
|
+
// Silent failure - never throw
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async emitWithRetry<T extends any>(
|
|
51
|
+
subject: string,
|
|
52
|
+
event: T,
|
|
53
|
+
attemptsLeft: number,
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
try {
|
|
56
|
+
const payload = {
|
|
57
|
+
subject,
|
|
58
|
+
properties: event,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const response = await fetch(this.config.endpoint, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
...this.config.headers,
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(payload),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!response.ok && attemptsLeft > 1) {
|
|
71
|
+
await this.delay(this.config.retryDelayMs || DEFAULT_RETRY_DELAY_MS);
|
|
72
|
+
return this.emitWithRetry(subject, event, attemptsLeft - 1);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (attemptsLeft > 1) {
|
|
76
|
+
await this.delay(this.config.retryDelayMs || DEFAULT_RETRY_DELAY_MS);
|
|
77
|
+
return this.emitWithRetry(subject, event, attemptsLeft - 1);
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async delay(ms: number): Promise<void> {
|
|
84
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Console Transport - logs events to console (for development)
|
|
89
|
+
export class ConsoleTransport implements EventTransport {
|
|
90
|
+
async emit<T extends any>(subject: string, event: T): Promise<void> {
|
|
91
|
+
try {
|
|
92
|
+
console.log(
|
|
93
|
+
"[SDK Telemetry]",
|
|
94
|
+
JSON.stringify({ subject, properties: event }, null, 2),
|
|
95
|
+
);
|
|
96
|
+
} catch {
|
|
97
|
+
// Silent failure - never throw
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// No-op Transport - discards all events (for testing/disabled state)
|
|
103
|
+
export class NoopTransport implements EventTransport {
|
|
104
|
+
async emit<T extends any>(_subject: string, _event: T): Promise<void> {
|
|
105
|
+
// Intentionally do nothing
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Transport factory
|
|
110
|
+
export function createTransport(config: TransportConfig): EventTransport {
|
|
111
|
+
try {
|
|
112
|
+
switch (config.type) {
|
|
113
|
+
case "http":
|
|
114
|
+
if (!config.endpoint) {
|
|
115
|
+
throw new Error("HTTP transport requires endpoint");
|
|
116
|
+
}
|
|
117
|
+
return new HttpTransport({
|
|
118
|
+
endpoint: config.endpoint,
|
|
119
|
+
headers: config.headers,
|
|
120
|
+
retryAttempts: config.retryAttempts,
|
|
121
|
+
retryDelayMs: config.retryDelayMs,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
case "console":
|
|
125
|
+
return new ConsoleTransport();
|
|
126
|
+
|
|
127
|
+
case "noop":
|
|
128
|
+
default:
|
|
129
|
+
return new NoopTransport();
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// If transport creation fails, return noop to maintain silent operation
|
|
133
|
+
return new NoopTransport();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Common context that can be injected into events
|
|
2
|
+
export interface EventContext {
|
|
3
|
+
customuser_id?: number | null;
|
|
4
|
+
account_id?: number | null;
|
|
5
|
+
identity_id?: number | null;
|
|
6
|
+
visitor_id?: string | null;
|
|
7
|
+
correlation_id?: string | null;
|
|
8
|
+
selected_api?: string | null;
|
|
9
|
+
app_id?: number | null;
|
|
10
|
+
app_version_id?: number | null;
|
|
11
|
+
zap_id?: number | null;
|
|
12
|
+
node_id?: number | null;
|
|
13
|
+
environment?: string | null;
|
|
14
|
+
sdk_version?: string | null;
|
|
15
|
+
operation_type?: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ErrorEventData {
|
|
19
|
+
error_message: string;
|
|
20
|
+
is_user_facing: boolean;
|
|
21
|
+
error_type?: string | null;
|
|
22
|
+
error_status_code?: number | null;
|
|
23
|
+
error_stack_trace?: string | null;
|
|
24
|
+
error_source_method?: string | null;
|
|
25
|
+
error_source_file?: string | null;
|
|
26
|
+
error_line_number?: number | null;
|
|
27
|
+
operation_type?: string | null;
|
|
28
|
+
operation_key?: string | null;
|
|
29
|
+
error_severity?: string | null;
|
|
30
|
+
is_recoverable?: boolean | null;
|
|
31
|
+
error_metadata?: Record<string, string | null> | null;
|
|
32
|
+
parent_error_id?: string | null;
|
|
33
|
+
error_occurred_timestamp_ms?: number | null;
|
|
34
|
+
error_code?: string | null;
|
|
35
|
+
recovery_attempted?: boolean | null;
|
|
36
|
+
recovery_action?: string | null;
|
|
37
|
+
recovery_successful?: boolean | null;
|
|
38
|
+
execution_time_before_error_ms?: number | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Enhanced Error Event Builder with runtime context
|
|
42
|
+
export interface EnhancedErrorEventData extends ErrorEventData {
|
|
43
|
+
execution_start_time?: number | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ApplicationLifecycleEventData {
|
|
47
|
+
lifecycle_event_type: "startup" | "exit" | "signal_termination";
|
|
48
|
+
exit_code?: number | null;
|
|
49
|
+
signal_name?: string | null;
|
|
50
|
+
uptime_ms?: number | null;
|
|
51
|
+
is_graceful_shutdown?: boolean | null;
|
|
52
|
+
shutdown_duration_ms?: number | null;
|
|
53
|
+
// Allow overriding auto-gathered system info if needed
|
|
54
|
+
memory_usage_bytes?: number | null;
|
|
55
|
+
peak_memory_usage_bytes?: number | null;
|
|
56
|
+
cpu_time_ms?: number | null;
|
|
57
|
+
active_requests_count?: number | null;
|
|
58
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple utility functions for event emission
|
|
3
|
+
* These are pure functions that can be used to populate common event fields
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as os from "os";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate a unique event ID
|
|
10
|
+
*/
|
|
11
|
+
export function generateEventId(): string {
|
|
12
|
+
return crypto.randomUUID();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get current timestamp in milliseconds since epoch
|
|
17
|
+
*/
|
|
18
|
+
export function getCurrentTimestamp(): number {
|
|
19
|
+
return Date.now();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get release ID (git SHA) - in production this would come from build process
|
|
24
|
+
*/
|
|
25
|
+
export function getReleaseId(): string {
|
|
26
|
+
return process?.env?.SDK_RELEASE_ID || "development";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get operating system information
|
|
31
|
+
*/
|
|
32
|
+
export function getOsInfo(): {
|
|
33
|
+
platform: string | null;
|
|
34
|
+
release: string | null;
|
|
35
|
+
architecture: string | null;
|
|
36
|
+
} {
|
|
37
|
+
try {
|
|
38
|
+
return {
|
|
39
|
+
platform: os.platform() || null,
|
|
40
|
+
release: os.release() || null,
|
|
41
|
+
architecture: os.arch() || null,
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
// Browser environment - os module not available
|
|
45
|
+
return {
|
|
46
|
+
platform: null,
|
|
47
|
+
release: null,
|
|
48
|
+
architecture: null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get platform versions (Node.js, npm, etc.)
|
|
55
|
+
*/
|
|
56
|
+
export function getPlatformVersions(): Record<string, string | null> {
|
|
57
|
+
const versions: Record<string, string | null> = {};
|
|
58
|
+
if (typeof process?.versions === "object") {
|
|
59
|
+
for (const [key, value] of Object.entries(process.versions)) {
|
|
60
|
+
versions[key] = value || null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return versions;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if running in CI environment
|
|
68
|
+
*/
|
|
69
|
+
export function isCi(): boolean {
|
|
70
|
+
return !!(
|
|
71
|
+
process?.env?.CI ||
|
|
72
|
+
process?.env?.CONTINUOUS_INTEGRATION ||
|
|
73
|
+
process?.env?.GITHUB_ACTIONS ||
|
|
74
|
+
process?.env?.JENKINS_URL ||
|
|
75
|
+
process?.env?.GITLAB_CI ||
|
|
76
|
+
process?.env?.CIRCLECI ||
|
|
77
|
+
process?.env?.TRAVIS ||
|
|
78
|
+
process?.env?.BUILDKITE ||
|
|
79
|
+
process?.env?.DRONE ||
|
|
80
|
+
process?.env?.BITBUCKET_PIPELINES_UUID
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get CI platform name if running in CI
|
|
86
|
+
*/
|
|
87
|
+
export function getCiPlatform(): string | null {
|
|
88
|
+
if (process?.env?.GITHUB_ACTIONS) return "github-actions";
|
|
89
|
+
if (process?.env?.JENKINS_URL) return "jenkins";
|
|
90
|
+
if (process?.env?.GITLAB_CI) return "gitlab-ci";
|
|
91
|
+
if (process?.env?.CIRCLECI) return "circleci";
|
|
92
|
+
if (process?.env?.TRAVIS) return "travis";
|
|
93
|
+
if (process?.env?.BUILDKITE) return "buildkite";
|
|
94
|
+
if (process?.env?.DRONE) return "drone";
|
|
95
|
+
if (process?.env?.BITBUCKET_PIPELINES_UUID) return "bitbucket-pipelines";
|
|
96
|
+
if (process?.env?.CI || process?.env?.CONTINUOUS_INTEGRATION)
|
|
97
|
+
return "unknown-ci";
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get memory usage in bytes
|
|
103
|
+
*/
|
|
104
|
+
export function getMemoryUsage(): number | null {
|
|
105
|
+
if (process?.memoryUsage) {
|
|
106
|
+
const usage = process.memoryUsage();
|
|
107
|
+
return usage.rss || null; // Resident Set Size
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get CPU time in milliseconds
|
|
114
|
+
*/
|
|
115
|
+
export function getCpuTime(): number | null {
|
|
116
|
+
if (process?.cpuUsage) {
|
|
117
|
+
const usage = process.cpuUsage();
|
|
118
|
+
return Math.round((usage.user + usage.system) / 1000); // Convert to milliseconds
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
@@ -36,9 +36,12 @@ export const getActionPlugin: Plugin<
|
|
|
36
36
|
// Use the listActions function from the SDK to search for the specific action
|
|
37
37
|
const actionsResult = await sdk.listActions({ appKey });
|
|
38
38
|
|
|
39
|
-
// Search through all actions to find the matching one
|
|
39
|
+
// Search through all actions to find the matching one (by key or ID)
|
|
40
40
|
for (const action of actionsResult.data) {
|
|
41
|
-
if (
|
|
41
|
+
if (
|
|
42
|
+
(action.key === actionKey || action.id === actionKey) &&
|
|
43
|
+
action.action_type === actionType
|
|
44
|
+
) {
|
|
42
45
|
return { data: action };
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
ZapierApiError,
|
|
6
6
|
} from "../../types/errors";
|
|
7
7
|
import { listInputFieldsPlugin } from "./index";
|
|
8
|
+
import { getActionPlugin } from "../getAction";
|
|
9
|
+
import { getAppPlugin } from "../getApp";
|
|
10
|
+
import { listActionsPlugin } from "../listActions";
|
|
11
|
+
import { listAppsPlugin } from "../listApps";
|
|
8
12
|
import { createSdk } from "../../sdk";
|
|
9
13
|
import type { ApiClient } from "../../api";
|
|
10
14
|
import type { ListInputFieldsOptions } from "./schemas";
|
|
@@ -45,6 +49,28 @@ const mockNeedsResponse: NeedsResponse = {
|
|
|
45
49
|
needs: mockNeeds,
|
|
46
50
|
};
|
|
47
51
|
|
|
52
|
+
const mockActionsResponse = {
|
|
53
|
+
results: [
|
|
54
|
+
{
|
|
55
|
+
slug: "slack",
|
|
56
|
+
selected_api: "slack",
|
|
57
|
+
actions: [
|
|
58
|
+
{
|
|
59
|
+
key: "send_message",
|
|
60
|
+
name: "Send Message",
|
|
61
|
+
description: "Send a message to a channel",
|
|
62
|
+
type_of: "write",
|
|
63
|
+
type: "write",
|
|
64
|
+
id: "core:12345",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
meta: {
|
|
70
|
+
next_cursor: null,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
48
74
|
describe("listInputFields plugin", () => {
|
|
49
75
|
let mockApiClient: ApiClient;
|
|
50
76
|
let mockGetVersionedImplementationId: any;
|
|
@@ -52,6 +78,7 @@ describe("listInputFields plugin", () => {
|
|
|
52
78
|
beforeEach(() => {
|
|
53
79
|
vi.clearAllMocks();
|
|
54
80
|
mockApiClient = {
|
|
81
|
+
get: vi.fn().mockResolvedValue(mockActionsResponse),
|
|
55
82
|
post: vi.fn().mockResolvedValue(mockNeedsResponse),
|
|
56
83
|
} as Partial<ApiClient> as ApiClient;
|
|
57
84
|
|
|
@@ -69,7 +96,12 @@ describe("listInputFields plugin", () => {
|
|
|
69
96
|
meta: {},
|
|
70
97
|
getVersionedImplementationId: mockGetVersionedImplementationId,
|
|
71
98
|
},
|
|
72
|
-
)
|
|
99
|
+
)
|
|
100
|
+
.addPlugin(listAppsPlugin as any)
|
|
101
|
+
.addPlugin(listActionsPlugin as any)
|
|
102
|
+
.addPlugin(getAppPlugin as any)
|
|
103
|
+
.addPlugin(getActionPlugin as any)
|
|
104
|
+
.addPlugin(listInputFieldsPlugin as any);
|
|
73
105
|
}
|
|
74
106
|
|
|
75
107
|
describe("schema validation", () => {
|
|
@@ -319,7 +351,7 @@ describe("listInputFields plugin", () => {
|
|
|
319
351
|
it("should throw ZapierApiError when API response indicates failure", async () => {
|
|
320
352
|
mockApiClient.post = vi.fn().mockResolvedValue({
|
|
321
353
|
success: false,
|
|
322
|
-
errors: ["
|
|
354
|
+
errors: ["Authentication failed", "Invalid credentials"],
|
|
323
355
|
});
|
|
324
356
|
|
|
325
357
|
const sdk = createTestSdk();
|
|
@@ -327,7 +359,7 @@ describe("listInputFields plugin", () => {
|
|
|
327
359
|
sdk.listInputFields({
|
|
328
360
|
appKey: "slack",
|
|
329
361
|
actionType: "write",
|
|
330
|
-
actionKey: "
|
|
362
|
+
actionKey: "send_message", // Use valid action so we get to the POST call
|
|
331
363
|
}),
|
|
332
364
|
).rejects.toThrow(ZapierApiError);
|
|
333
365
|
|
|
@@ -335,10 +367,10 @@ describe("listInputFields plugin", () => {
|
|
|
335
367
|
sdk.listInputFields({
|
|
336
368
|
appKey: "slack",
|
|
337
369
|
actionType: "write",
|
|
338
|
-
actionKey: "
|
|
370
|
+
actionKey: "send_message", // Use valid action so we get to the POST call
|
|
339
371
|
}),
|
|
340
372
|
).rejects.toThrow(
|
|
341
|
-
"Failed to get action fields:
|
|
373
|
+
"Failed to get action fields: Authentication failed, Invalid credentials",
|
|
342
374
|
);
|
|
343
375
|
});
|
|
344
376
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { ZapierConfigurationError, ZapierApiError } from "../../types/errors";
|
|
16
16
|
import { createPaginatedFunction } from "../../utils/function-utils";
|
|
17
17
|
import type { GetAppPluginProvides } from "../getApp";
|
|
18
|
+
import type { GetActionPluginProvides } from "../getAction";
|
|
18
19
|
import type { GetVersionedImplementationId } from "../manifest/schemas";
|
|
19
20
|
import {
|
|
20
21
|
appKeyResolver,
|
|
@@ -196,13 +197,13 @@ export interface ListInputFieldsPluginProvides {
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
export const listInputFieldsPlugin: Plugin<
|
|
199
|
-
GetSdkType<GetAppPluginProvides>, // requires getApp in SDK
|
|
200
|
+
GetSdkType<GetAppPluginProvides & GetActionPluginProvides>, // requires getApp and getAction in SDK
|
|
200
201
|
{
|
|
201
202
|
api: ApiClient;
|
|
202
203
|
getVersionedImplementationId: GetVersionedImplementationId;
|
|
203
204
|
}, // requires api and getVersionedImplementationId in context
|
|
204
205
|
ListInputFieldsPluginProvides
|
|
205
|
-
> = ({ context }) => {
|
|
206
|
+
> = ({ sdk, context }) => {
|
|
206
207
|
const listInputFields = createPaginatedFunction(
|
|
207
208
|
async function listInputFieldsPage(
|
|
208
209
|
options: ListInputFieldsOptions & { cursor?: string } & {
|
|
@@ -226,10 +227,16 @@ export const listInputFieldsPlugin: Plugin<
|
|
|
226
227
|
);
|
|
227
228
|
}
|
|
228
229
|
|
|
230
|
+
const { data: action } = await sdk.getAction({
|
|
231
|
+
appKey,
|
|
232
|
+
actionType,
|
|
233
|
+
actionKey,
|
|
234
|
+
});
|
|
235
|
+
|
|
229
236
|
// Build needs request
|
|
230
237
|
const needsRequest: NeedsRequest = {
|
|
231
238
|
selected_api: selectedApi,
|
|
232
|
-
action:
|
|
239
|
+
action: action.key,
|
|
233
240
|
type_of: actionType,
|
|
234
241
|
params: inputs || {},
|
|
235
242
|
};
|