@zapier/zapier-sdk 0.33.0 → 0.33.2
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/index.cjs +2 -1
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/plugins/registry/index.d.ts.map +1 -1
- package/dist/plugins/registry/index.js +1 -0
- package/dist/types/sdk.d.ts +8 -0
- package/dist/types/sdk.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/api/auth.test.d.ts +0 -2
- package/dist/api/auth.test.d.ts.map +0 -1
- package/dist/api/auth.test.js +0 -220
- package/dist/api/client.test.d.ts +0 -2
- package/dist/api/client.test.d.ts.map +0 -1
- package/dist/api/client.test.js +0 -611
- package/dist/api/debug.test.d.ts +0 -2
- package/dist/api/debug.test.d.ts.map +0 -1
- package/dist/api/debug.test.js +0 -59
- package/dist/api/polling.test.d.ts +0 -2
- package/dist/api/polling.test.d.ts.map +0 -1
- package/dist/api/polling.test.js +0 -360
- package/dist/auth.test.d.ts +0 -2
- package/dist/auth.test.d.ts.map +0 -1
- package/dist/auth.test.js +0 -480
- package/dist/plugins/eventEmission/builders.test.d.ts +0 -2
- package/dist/plugins/eventEmission/builders.test.d.ts.map +0 -1
- package/dist/plugins/eventEmission/builders.test.js +0 -138
- package/dist/plugins/eventEmission/index.test.d.ts +0 -5
- package/dist/plugins/eventEmission/index.test.d.ts.map +0 -1
- package/dist/plugins/eventEmission/index.test.js +0 -712
- package/dist/plugins/eventEmission/transport.test.d.ts +0 -5
- package/dist/plugins/eventEmission/transport.test.d.ts.map +0 -1
- package/dist/plugins/eventEmission/transport.test.js +0 -164
- package/dist/plugins/fetch/index.test.d.ts +0 -2
- package/dist/plugins/fetch/index.test.d.ts.map +0 -1
- package/dist/plugins/fetch/index.test.js +0 -428
- package/dist/plugins/findFirstConnection/index.test.d.ts +0 -2
- package/dist/plugins/findFirstConnection/index.test.d.ts.map +0 -1
- package/dist/plugins/findFirstConnection/index.test.js +0 -177
- package/dist/plugins/findUniqueConnection/index.test.d.ts +0 -2
- package/dist/plugins/findUniqueConnection/index.test.d.ts.map +0 -1
- package/dist/plugins/findUniqueConnection/index.test.js +0 -159
- package/dist/plugins/getAction/index.test.d.ts +0 -2
- package/dist/plugins/getAction/index.test.d.ts.map +0 -1
- package/dist/plugins/getAction/index.test.js +0 -211
- package/dist/plugins/getApp/index.test.d.ts +0 -2
- package/dist/plugins/getApp/index.test.d.ts.map +0 -1
- package/dist/plugins/getApp/index.test.js +0 -157
- package/dist/plugins/getConnection/index.test.d.ts +0 -2
- package/dist/plugins/getConnection/index.test.d.ts.map +0 -1
- package/dist/plugins/getConnection/index.test.js +0 -124
- package/dist/plugins/getInputFieldsSchema/index.test.d.ts +0 -2
- package/dist/plugins/getInputFieldsSchema/index.test.d.ts.map +0 -1
- package/dist/plugins/getInputFieldsSchema/index.test.js +0 -291
- package/dist/plugins/listActions/index.test.d.ts +0 -2
- package/dist/plugins/listActions/index.test.d.ts.map +0 -1
- package/dist/plugins/listActions/index.test.js +0 -454
- package/dist/plugins/listApps/index.test.d.ts +0 -2
- package/dist/plugins/listApps/index.test.d.ts.map +0 -1
- package/dist/plugins/listApps/index.test.js +0 -124
- package/dist/plugins/listConnections/index.test.d.ts +0 -2
- package/dist/plugins/listConnections/index.test.d.ts.map +0 -1
- package/dist/plugins/listConnections/index.test.js +0 -920
- package/dist/plugins/listInputFieldChoices/index.test.d.ts +0 -2
- package/dist/plugins/listInputFieldChoices/index.test.d.ts.map +0 -1
- package/dist/plugins/listInputFieldChoices/index.test.js +0 -717
- package/dist/plugins/listInputFields/index.test.d.ts +0 -2
- package/dist/plugins/listInputFields/index.test.d.ts.map +0 -1
- package/dist/plugins/listInputFields/index.test.js +0 -359
- package/dist/plugins/manifest/index.test.d.ts +0 -2
- package/dist/plugins/manifest/index.test.d.ts.map +0 -1
- package/dist/plugins/manifest/index.test.js +0 -1179
- package/dist/plugins/request/index.test.d.ts +0 -2
- package/dist/plugins/request/index.test.d.ts.map +0 -1
- package/dist/plugins/request/index.test.js +0 -458
- package/dist/plugins/runAction/index.test.d.ts +0 -2
- package/dist/plugins/runAction/index.test.d.ts.map +0 -1
- package/dist/plugins/runAction/index.test.js +0 -350
- package/dist/resolvers/connectionId.test.d.ts +0 -2
- package/dist/resolvers/connectionId.test.d.ts.map +0 -1
- package/dist/resolvers/connectionId.test.js +0 -61
- package/dist/sdk.test.d.ts +0 -2
- package/dist/sdk.test.d.ts.map +0 -1
- package/dist/sdk.test.js +0 -260
- package/dist/types/domain.test.d.ts +0 -2
- package/dist/types/domain.test.d.ts.map +0 -1
- package/dist/types/domain.test.js +0 -39
- package/dist/utils/array-utils.test.d.ts +0 -2
- package/dist/utils/array-utils.test.d.ts.map +0 -1
- package/dist/utils/array-utils.test.js +0 -107
- package/dist/utils/batch-utils.test.d.ts +0 -2
- package/dist/utils/batch-utils.test.d.ts.map +0 -1
- package/dist/utils/batch-utils.test.js +0 -476
- package/dist/utils/domain-utils.test.d.ts +0 -2
- package/dist/utils/domain-utils.test.d.ts.map +0 -1
- package/dist/utils/domain-utils.test.js +0 -346
- package/dist/utils/file-utils.test.d.ts +0 -2
- package/dist/utils/file-utils.test.d.ts.map +0 -1
- package/dist/utils/file-utils.test.js +0 -51
- package/dist/utils/function-utils.test.d.ts +0 -2
- package/dist/utils/function-utils.test.d.ts.map +0 -1
- package/dist/utils/function-utils.test.js +0 -188
- package/dist/utils/id-utils.test.d.ts +0 -2
- package/dist/utils/id-utils.test.d.ts.map +0 -1
- package/dist/utils/id-utils.test.js +0 -22
- package/dist/utils/pagination-utils.test.d.ts +0 -17
- package/dist/utils/pagination-utils.test.d.ts.map +0 -1
- package/dist/utils/pagination-utils.test.js +0 -461
- package/dist/utils/retry-utils.test.d.ts +0 -2
- package/dist/utils/retry-utils.test.d.ts.map +0 -1
- package/dist/utils/retry-utils.test.js +0 -90
- package/dist/utils/string-utils.test.d.ts +0 -2
- package/dist/utils/string-utils.test.d.ts.map +0 -1
- package/dist/utils/string-utils.test.js +0 -59
- package/dist/utils/telemetry-context.test.d.ts +0 -2
- package/dist/utils/telemetry-context.test.d.ts.map +0 -1
- package/dist/utils/telemetry-context.test.js +0 -154
- package/dist/utils/telemetry-utils.test.d.ts +0 -2
- package/dist/utils/telemetry-utils.test.d.ts.map +0 -1
- package/dist/utils/telemetry-utils.test.js +0 -155
- package/dist/utils/url-utils.test.d.ts +0 -2
- package/dist/utils/url-utils.test.d.ts.map +0 -1
- package/dist/utils/url-utils.test.js +0 -103
- package/dist/utils/validation.test.d.ts +0 -2
- package/dist/utils/validation.test.d.ts.map +0 -1
- package/dist/utils/validation.test.js +0 -44
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport.test.d.ts","sourceRoot":"","sources":["../../../src/plugins/eventEmission/transport.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Event Transport Layer
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
|
-
import { createHttpTransport, createConsoleTransport, createNoopTransport, createTransport, } from "./transport";
|
|
6
|
-
// Mock fetch globally
|
|
7
|
-
const mockFetch = vi.fn();
|
|
8
|
-
global.fetch = mockFetch;
|
|
9
|
-
// Mock console.log
|
|
10
|
-
const mockConsoleLog = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
11
|
-
describe("Transport Layer", () => {
|
|
12
|
-
const sampleEvent = {
|
|
13
|
-
event_id: "test-123",
|
|
14
|
-
timestamp_ms: Date.now(),
|
|
15
|
-
release_id: "test-release",
|
|
16
|
-
error_message: "Test error",
|
|
17
|
-
is_user_facing: false,
|
|
18
|
-
};
|
|
19
|
-
const sampleSubject = "platform.sdk.ErrorOccurredEvent";
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
vi.clearAllMocks();
|
|
22
|
-
});
|
|
23
|
-
describe("createHttpTransport", () => {
|
|
24
|
-
it("should emit events via HTTP successfully", async () => {
|
|
25
|
-
mockFetch.mockResolvedValueOnce({
|
|
26
|
-
ok: true,
|
|
27
|
-
status: 200,
|
|
28
|
-
});
|
|
29
|
-
const transport = createHttpTransport({
|
|
30
|
-
endpoint: "https://telemetry.example.com/events",
|
|
31
|
-
headers: { "x-api-key": "test-key" },
|
|
32
|
-
});
|
|
33
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
34
|
-
expect(mockFetch).toHaveBeenCalledWith("https://telemetry.example.com/events", {
|
|
35
|
-
method: "POST",
|
|
36
|
-
headers: {
|
|
37
|
-
"Content-Type": "application/json",
|
|
38
|
-
"x-api-key": "test-key",
|
|
39
|
-
},
|
|
40
|
-
body: JSON.stringify({
|
|
41
|
-
subject: sampleSubject,
|
|
42
|
-
properties: sampleEvent,
|
|
43
|
-
}),
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
it("should retry on HTTP failures", async () => {
|
|
47
|
-
// Fail twice, then succeed
|
|
48
|
-
mockFetch
|
|
49
|
-
.mockResolvedValueOnce({ ok: false, status: 500 })
|
|
50
|
-
.mockResolvedValueOnce({ ok: false, status: 500 })
|
|
51
|
-
.mockResolvedValueOnce({ ok: true, status: 200 });
|
|
52
|
-
const transport = createHttpTransport({
|
|
53
|
-
endpoint: "https://telemetry.example.com/events",
|
|
54
|
-
retryAttempts: 3,
|
|
55
|
-
retryDelayMs: 10, // Short delay for testing
|
|
56
|
-
});
|
|
57
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
58
|
-
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
59
|
-
});
|
|
60
|
-
it("should handle network errors silently", async () => {
|
|
61
|
-
mockFetch.mockRejectedValue(new Error("Network error"));
|
|
62
|
-
const transport = createHttpTransport({
|
|
63
|
-
endpoint: "https://telemetry.example.com/events",
|
|
64
|
-
retryAttempts: 1,
|
|
65
|
-
retryDelayMs: 1,
|
|
66
|
-
});
|
|
67
|
-
// Should not throw despite network error
|
|
68
|
-
await expect(transport.emit(sampleSubject, sampleEvent)).resolves.toBeUndefined();
|
|
69
|
-
});
|
|
70
|
-
it("should stop retrying after max attempts", async () => {
|
|
71
|
-
mockFetch.mockResolvedValue({ ok: false, status: 500 });
|
|
72
|
-
const transport = createHttpTransport({
|
|
73
|
-
endpoint: "https://telemetry.example.com/events",
|
|
74
|
-
retryAttempts: 2,
|
|
75
|
-
retryDelayMs: 1,
|
|
76
|
-
});
|
|
77
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
78
|
-
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
describe("createConsoleTransport", () => {
|
|
82
|
-
it("should log events to console", async () => {
|
|
83
|
-
const transport = createConsoleTransport();
|
|
84
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
85
|
-
expect(mockConsoleLog).toHaveBeenCalledWith("[SDK Telemetry]", JSON.stringify({ subject: sampleSubject, properties: sampleEvent }, null, 2));
|
|
86
|
-
});
|
|
87
|
-
it("should handle console errors silently", async () => {
|
|
88
|
-
mockConsoleLog.mockImplementation(() => {
|
|
89
|
-
throw new Error("Console error");
|
|
90
|
-
});
|
|
91
|
-
const transport = createConsoleTransport();
|
|
92
|
-
// Should not throw despite console error
|
|
93
|
-
await expect(transport.emit(sampleSubject, sampleEvent)).resolves.toBeUndefined();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
describe("createNoopTransport", () => {
|
|
97
|
-
it("should do nothing when emitting events", async () => {
|
|
98
|
-
const transport = createNoopTransport();
|
|
99
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
100
|
-
// Verify no side effects
|
|
101
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
102
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
describe("createTransport", () => {
|
|
106
|
-
it("should create HTTP transport with valid config", async () => {
|
|
107
|
-
mockFetch.mockResolvedValueOnce({ ok: true, status: 200 });
|
|
108
|
-
const transport = createTransport({
|
|
109
|
-
type: "http",
|
|
110
|
-
endpoint: "https://example.com",
|
|
111
|
-
headers: { "x-key": "value" },
|
|
112
|
-
retryAttempts: 5,
|
|
113
|
-
retryDelayMs: 2000,
|
|
114
|
-
});
|
|
115
|
-
expect(transport.emit).toBeTypeOf("function");
|
|
116
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
117
|
-
expect(mockFetch).toHaveBeenCalledWith("https://example.com", expect.any(Object));
|
|
118
|
-
});
|
|
119
|
-
it("should create console transport", async () => {
|
|
120
|
-
const transport = createTransport({
|
|
121
|
-
type: "console",
|
|
122
|
-
});
|
|
123
|
-
expect(transport.emit).toBeTypeOf("function");
|
|
124
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
125
|
-
expect(mockConsoleLog).toHaveBeenCalled();
|
|
126
|
-
});
|
|
127
|
-
it("should create noop transport by default", async () => {
|
|
128
|
-
const transport = createTransport({
|
|
129
|
-
type: "noop",
|
|
130
|
-
});
|
|
131
|
-
expect(transport.emit).toBeTypeOf("function");
|
|
132
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
133
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
134
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
135
|
-
});
|
|
136
|
-
it("should fallback to noop transport on invalid HTTP config", async () => {
|
|
137
|
-
const transport = createTransport({
|
|
138
|
-
type: "http",
|
|
139
|
-
// Missing endpoint
|
|
140
|
-
});
|
|
141
|
-
expect(transport.emit).toBeTypeOf("function");
|
|
142
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
143
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
144
|
-
});
|
|
145
|
-
it("should fallback to noop transport on unknown type", async () => {
|
|
146
|
-
const transport = createTransport({
|
|
147
|
-
type: "unknown",
|
|
148
|
-
});
|
|
149
|
-
expect(transport.emit).toBeTypeOf("function");
|
|
150
|
-
await transport.emit(sampleSubject, sampleEvent);
|
|
151
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
152
|
-
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
153
|
-
});
|
|
154
|
-
it("should handle transport creation errors gracefully", async () => {
|
|
155
|
-
expect(() => {
|
|
156
|
-
const transport = createTransport({
|
|
157
|
-
type: "http",
|
|
158
|
-
// Missing required endpoint - should fallback to noop
|
|
159
|
-
});
|
|
160
|
-
expect(transport.emit).toBeTypeOf("function");
|
|
161
|
-
}).not.toThrow();
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/plugins/fetch/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { fetchPlugin } from "./index";
|
|
3
|
-
import { createSdk } from "../../sdk";
|
|
4
|
-
import { eventEmissionPlugin } from "../eventEmission";
|
|
5
|
-
import { ZapierRelayError } from "../../types/errors";
|
|
6
|
-
describe("fetch plugin", () => {
|
|
7
|
-
let mockApiClient;
|
|
8
|
-
let mockFetch;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
vi.clearAllMocks();
|
|
11
|
-
mockFetch = vi.fn().mockResolvedValue(new Response('{"success": true}', {
|
|
12
|
-
status: 200,
|
|
13
|
-
headers: { "Content-Type": "application/json" },
|
|
14
|
-
}));
|
|
15
|
-
mockApiClient = {
|
|
16
|
-
fetch: mockFetch,
|
|
17
|
-
};
|
|
18
|
-
});
|
|
19
|
-
const apiPlugin = () => ({
|
|
20
|
-
context: {
|
|
21
|
-
api: mockApiClient,
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
function createTestSdk() {
|
|
25
|
-
return createSdk()
|
|
26
|
-
.addPlugin(() => ({
|
|
27
|
-
context: {
|
|
28
|
-
options: {},
|
|
29
|
-
},
|
|
30
|
-
}))
|
|
31
|
-
.addPlugin(apiPlugin)
|
|
32
|
-
.addPlugin(eventEmissionPlugin)
|
|
33
|
-
.addPlugin(fetchPlugin);
|
|
34
|
-
}
|
|
35
|
-
describe("URL transformation", () => {
|
|
36
|
-
it("should transform full URLs to relay path format", async () => {
|
|
37
|
-
const sdk = createTestSdk();
|
|
38
|
-
await sdk.fetch("https://api.github.com/user");
|
|
39
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.github.com/user", expect.any(Object));
|
|
40
|
-
});
|
|
41
|
-
it("should preserve query parameters and fragments", async () => {
|
|
42
|
-
const sdk = createTestSdk();
|
|
43
|
-
await sdk.fetch("https://api.example.com/search?q=test&limit=10#section");
|
|
44
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/search?q=test&limit=10#section", expect.any(Object));
|
|
45
|
-
});
|
|
46
|
-
it("should handle URLs with ports", async () => {
|
|
47
|
-
const sdk = createTestSdk();
|
|
48
|
-
await sdk.fetch("https://api.example.com:8443/data");
|
|
49
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com:8443/data", expect.any(Object));
|
|
50
|
-
});
|
|
51
|
-
it("should handle URL objects", async () => {
|
|
52
|
-
const sdk = createTestSdk();
|
|
53
|
-
await sdk.fetch(new URL("https://api.example.com/data"));
|
|
54
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", expect.any(Object));
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
describe("headers handling", () => {
|
|
58
|
-
it("should pass through regular headers", async () => {
|
|
59
|
-
const sdk = createTestSdk();
|
|
60
|
-
const headers = {
|
|
61
|
-
"Content-Type": "application/json",
|
|
62
|
-
"X-Custom-Header": "test-value",
|
|
63
|
-
};
|
|
64
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
65
|
-
method: "POST",
|
|
66
|
-
headers,
|
|
67
|
-
});
|
|
68
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
69
|
-
method: "POST",
|
|
70
|
-
body: undefined,
|
|
71
|
-
headers: {
|
|
72
|
-
"Content-Type": "application/json",
|
|
73
|
-
"X-Custom-Header": "test-value",
|
|
74
|
-
},
|
|
75
|
-
authRequired: true,
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
it("should add relay-specific headers when provided", async () => {
|
|
79
|
-
const sdk = createTestSdk();
|
|
80
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
81
|
-
method: "POST",
|
|
82
|
-
authenticationId: 123,
|
|
83
|
-
callbackUrl: "https://webhook.example.com/callback",
|
|
84
|
-
});
|
|
85
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
86
|
-
method: "POST",
|
|
87
|
-
body: undefined,
|
|
88
|
-
headers: {
|
|
89
|
-
"X-Relay-Authentication-Id": "123",
|
|
90
|
-
"X-Relay-Callback-Url": "https://webhook.example.com/callback",
|
|
91
|
-
},
|
|
92
|
-
authRequired: true,
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
it("should handle Headers object", async () => {
|
|
96
|
-
const sdk = createTestSdk();
|
|
97
|
-
const headers = new Headers();
|
|
98
|
-
headers.set("Content-Type", "application/json");
|
|
99
|
-
headers.set("Authorization", "Bearer token");
|
|
100
|
-
await sdk.fetch("https://api.example.com/data", { headers });
|
|
101
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
102
|
-
method: "GET",
|
|
103
|
-
body: undefined,
|
|
104
|
-
headers: {
|
|
105
|
-
"content-type": "application/json",
|
|
106
|
-
authorization: "Bearer token",
|
|
107
|
-
},
|
|
108
|
-
authRequired: true,
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
it("should handle headers as array of tuples", async () => {
|
|
112
|
-
const sdk = createTestSdk();
|
|
113
|
-
const headers = [
|
|
114
|
-
["Content-Type", "application/json"],
|
|
115
|
-
["X-Api-Key", "secret"],
|
|
116
|
-
];
|
|
117
|
-
await sdk.fetch("https://api.example.com/data", { headers });
|
|
118
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
119
|
-
method: "GET",
|
|
120
|
-
body: undefined,
|
|
121
|
-
headers: {
|
|
122
|
-
"Content-Type": "application/json",
|
|
123
|
-
"X-Api-Key": "secret",
|
|
124
|
-
},
|
|
125
|
-
authRequired: true,
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
describe("HTTP methods", () => {
|
|
130
|
-
const methods = [
|
|
131
|
-
"GET",
|
|
132
|
-
"POST",
|
|
133
|
-
"PUT",
|
|
134
|
-
"DELETE",
|
|
135
|
-
"PATCH",
|
|
136
|
-
"HEAD",
|
|
137
|
-
"OPTIONS",
|
|
138
|
-
];
|
|
139
|
-
methods.forEach((method) => {
|
|
140
|
-
it(`should support ${method} method`, async () => {
|
|
141
|
-
const sdk = createTestSdk();
|
|
142
|
-
await sdk.fetch("https://api.example.com/data", { method });
|
|
143
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
144
|
-
method,
|
|
145
|
-
body: undefined,
|
|
146
|
-
headers: {},
|
|
147
|
-
authRequired: true,
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
it("should default to GET method", async () => {
|
|
152
|
-
const sdk = createTestSdk();
|
|
153
|
-
await sdk.fetch("https://api.example.com/data");
|
|
154
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
155
|
-
method: "GET",
|
|
156
|
-
body: undefined,
|
|
157
|
-
headers: {},
|
|
158
|
-
authRequired: true,
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
describe("request body", () => {
|
|
163
|
-
it("should pass through request body", async () => {
|
|
164
|
-
const sdk = createTestSdk();
|
|
165
|
-
const body = '{"name": "test", "value": 42}';
|
|
166
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
167
|
-
method: "POST",
|
|
168
|
-
body,
|
|
169
|
-
});
|
|
170
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
171
|
-
method: "POST",
|
|
172
|
-
body,
|
|
173
|
-
headers: {
|
|
174
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
175
|
-
},
|
|
176
|
-
authRequired: true,
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
it("should auto-set Content-Type for JSON object bodies", async () => {
|
|
180
|
-
const sdk = createTestSdk();
|
|
181
|
-
const body = '{"key": "value"}';
|
|
182
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
183
|
-
method: "POST",
|
|
184
|
-
body,
|
|
185
|
-
});
|
|
186
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
187
|
-
method: "POST",
|
|
188
|
-
body,
|
|
189
|
-
headers: {
|
|
190
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
191
|
-
},
|
|
192
|
-
authRequired: true,
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
it("should auto-set Content-Type for JSON array bodies", async () => {
|
|
196
|
-
const sdk = createTestSdk();
|
|
197
|
-
const body = '[{"id": 1}, {"id": 2}]';
|
|
198
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
199
|
-
method: "POST",
|
|
200
|
-
body,
|
|
201
|
-
});
|
|
202
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
203
|
-
method: "POST",
|
|
204
|
-
body,
|
|
205
|
-
headers: {
|
|
206
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
207
|
-
},
|
|
208
|
-
authRequired: true,
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
it("should not override explicit Content-Type header", async () => {
|
|
212
|
-
const sdk = createTestSdk();
|
|
213
|
-
const body = '{"key": "value"}';
|
|
214
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
215
|
-
method: "POST",
|
|
216
|
-
body,
|
|
217
|
-
headers: {
|
|
218
|
-
"Content-Type": "text/plain",
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
222
|
-
method: "POST",
|
|
223
|
-
body,
|
|
224
|
-
headers: {
|
|
225
|
-
"Content-Type": "text/plain",
|
|
226
|
-
},
|
|
227
|
-
authRequired: true,
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
it("should not set Content-Type for non-JSON bodies", async () => {
|
|
231
|
-
const sdk = createTestSdk();
|
|
232
|
-
const body = "plain text body";
|
|
233
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
234
|
-
method: "POST",
|
|
235
|
-
body,
|
|
236
|
-
});
|
|
237
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
238
|
-
method: "POST",
|
|
239
|
-
body,
|
|
240
|
-
headers: {},
|
|
241
|
-
authRequired: true,
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
it("should not set Content-Type for FormData bodies", async () => {
|
|
245
|
-
const sdk = createTestSdk();
|
|
246
|
-
const body = new FormData();
|
|
247
|
-
body.append("file", "contents");
|
|
248
|
-
await sdk.fetch("https://api.example.com/upload", {
|
|
249
|
-
method: "POST",
|
|
250
|
-
body,
|
|
251
|
-
});
|
|
252
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/upload", {
|
|
253
|
-
method: "POST",
|
|
254
|
-
body,
|
|
255
|
-
headers: {},
|
|
256
|
-
authRequired: true,
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
it("should set Content-Type for URLSearchParams bodies", async () => {
|
|
260
|
-
const sdk = createTestSdk();
|
|
261
|
-
const body = new URLSearchParams({ key: "value", foo: "bar" });
|
|
262
|
-
await sdk.fetch("https://api.example.com/form", {
|
|
263
|
-
method: "POST",
|
|
264
|
-
body,
|
|
265
|
-
});
|
|
266
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/form", {
|
|
267
|
-
method: "POST",
|
|
268
|
-
body,
|
|
269
|
-
headers: {
|
|
270
|
-
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
|
271
|
-
},
|
|
272
|
-
authRequired: true,
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
it("should not set Content-Type for invalid JSON-like strings", async () => {
|
|
276
|
-
const sdk = createTestSdk();
|
|
277
|
-
const body = "{ this is not json";
|
|
278
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
279
|
-
method: "POST",
|
|
280
|
-
body,
|
|
281
|
-
});
|
|
282
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
283
|
-
method: "POST",
|
|
284
|
-
body,
|
|
285
|
-
headers: {},
|
|
286
|
-
authRequired: true,
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
it("should auto-set Content-Type for plain object bodies", async () => {
|
|
290
|
-
const sdk = createTestSdk();
|
|
291
|
-
const body = { channel: "C0123456789", text: "Hello, world!" };
|
|
292
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
293
|
-
method: "POST",
|
|
294
|
-
body: body,
|
|
295
|
-
});
|
|
296
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
297
|
-
method: "POST",
|
|
298
|
-
body,
|
|
299
|
-
headers: {
|
|
300
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
301
|
-
},
|
|
302
|
-
authRequired: true,
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
it("should auto-set Content-Type for plain array bodies", async () => {
|
|
306
|
-
const sdk = createTestSdk();
|
|
307
|
-
const body = [{ id: 1 }, { id: 2 }];
|
|
308
|
-
await sdk.fetch("https://api.example.com/data", {
|
|
309
|
-
method: "POST",
|
|
310
|
-
body: body,
|
|
311
|
-
});
|
|
312
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/data", {
|
|
313
|
-
method: "POST",
|
|
314
|
-
body,
|
|
315
|
-
headers: {
|
|
316
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
317
|
-
},
|
|
318
|
-
authRequired: true,
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
it("should not set Content-Type for Blob bodies", async () => {
|
|
322
|
-
const sdk = createTestSdk();
|
|
323
|
-
const body = new Blob(["binary data"], {
|
|
324
|
-
type: "application/octet-stream",
|
|
325
|
-
});
|
|
326
|
-
await sdk.fetch("https://api.example.com/upload", {
|
|
327
|
-
method: "POST",
|
|
328
|
-
body,
|
|
329
|
-
});
|
|
330
|
-
expect(mockFetch).toHaveBeenCalledWith("/relay/api.example.com/upload", {
|
|
331
|
-
method: "POST",
|
|
332
|
-
body,
|
|
333
|
-
headers: {},
|
|
334
|
-
authRequired: true,
|
|
335
|
-
});
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
describe("authentication", () => {
|
|
339
|
-
it("should set authRequired to true in API call options", async () => {
|
|
340
|
-
const sdk = createTestSdk();
|
|
341
|
-
await sdk.fetch("https://api.example.com/data");
|
|
342
|
-
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
343
|
-
authRequired: true,
|
|
344
|
-
}));
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
describe("response handling", () => {
|
|
348
|
-
it("should return the response from api.fetch", async () => {
|
|
349
|
-
const mockResponse = new Response('{"result": "success"}', {
|
|
350
|
-
status: 200,
|
|
351
|
-
statusText: "OK",
|
|
352
|
-
headers: { "Content-Type": "application/json" },
|
|
353
|
-
});
|
|
354
|
-
mockFetch.mockResolvedValue(mockResponse);
|
|
355
|
-
const sdk = createTestSdk();
|
|
356
|
-
const response = await sdk.fetch("https://api.example.com/data");
|
|
357
|
-
expect(response).toBe(mockResponse);
|
|
358
|
-
expect(response.status).toBe(200);
|
|
359
|
-
});
|
|
360
|
-
it("should handle API errors", async () => {
|
|
361
|
-
const error = new Error("Network error");
|
|
362
|
-
mockFetch.mockRejectedValue(error);
|
|
363
|
-
const sdk = createTestSdk();
|
|
364
|
-
await expect(sdk.fetch("https://api.example.com/data")).rejects.toThrow("Network error");
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
describe("relay errors", () => {
|
|
368
|
-
it("should throw ZapierRelayError with the x-relay-error header message", async () => {
|
|
369
|
-
const friendlyMessage = "Ashby does not support direct HTTP requests. Use zapier.apps.{appKey} or zapier.runAction() to use Actions built for this app.";
|
|
370
|
-
mockFetch.mockResolvedValue(new Response("", {
|
|
371
|
-
status: 404,
|
|
372
|
-
headers: { "x-relay-error": friendlyMessage },
|
|
373
|
-
}));
|
|
374
|
-
const sdk = createTestSdk();
|
|
375
|
-
const error = await sdk
|
|
376
|
-
.fetch("https://api.example.com/data")
|
|
377
|
-
.catch((e) => e);
|
|
378
|
-
expect(error).toBeInstanceOf(ZapierRelayError);
|
|
379
|
-
expect(error.message).toBe(friendlyMessage);
|
|
380
|
-
expect(error.statusCode).toBe(404);
|
|
381
|
-
});
|
|
382
|
-
it("should throw ZapierRelayError for non-matching x-relay-error", async () => {
|
|
383
|
-
mockFetch.mockResolvedValue(new Response("", {
|
|
384
|
-
status: 404,
|
|
385
|
-
headers: { "x-relay-error": "Some other relay error" },
|
|
386
|
-
}));
|
|
387
|
-
const sdk = createTestSdk();
|
|
388
|
-
const error = await sdk
|
|
389
|
-
.fetch("https://api.example.com/data")
|
|
390
|
-
.catch((e) => e);
|
|
391
|
-
expect(error).toBeInstanceOf(ZapierRelayError);
|
|
392
|
-
expect(error.message).toBe("Some other relay error");
|
|
393
|
-
});
|
|
394
|
-
it("should throw ZapierRelayError regardless of status code", async () => {
|
|
395
|
-
mockFetch.mockResolvedValue(new Response("", {
|
|
396
|
-
status: 502,
|
|
397
|
-
headers: { "x-relay-error": "upstream timeout" },
|
|
398
|
-
}));
|
|
399
|
-
const sdk = createTestSdk();
|
|
400
|
-
const error = await sdk
|
|
401
|
-
.fetch("https://api.example.com/data")
|
|
402
|
-
.catch((e) => e);
|
|
403
|
-
expect(error).toBeInstanceOf(ZapierRelayError);
|
|
404
|
-
expect(error.statusCode).toBe(502);
|
|
405
|
-
});
|
|
406
|
-
});
|
|
407
|
-
describe("context and metadata", () => {
|
|
408
|
-
it("should provide context with meta information", () => {
|
|
409
|
-
const sdk = createTestSdk();
|
|
410
|
-
const context = sdk.getContext();
|
|
411
|
-
expect(context.meta.fetch).toBeDefined();
|
|
412
|
-
expect(context.meta.fetch.inputParameters).toBeDefined();
|
|
413
|
-
expect(context.meta.fetch.inputParameters).toHaveLength(2);
|
|
414
|
-
});
|
|
415
|
-
it("should include cli and mcp in packages", () => {
|
|
416
|
-
const sdk = createTestSdk();
|
|
417
|
-
const context = sdk.getContext();
|
|
418
|
-
expect(context.meta.fetch.packages).toContain("cli");
|
|
419
|
-
expect(context.meta.fetch.packages).toContain("mcp");
|
|
420
|
-
expect(context.meta.fetch.packages).toContain("sdk");
|
|
421
|
-
});
|
|
422
|
-
it("should be in the http category", () => {
|
|
423
|
-
const sdk = createTestSdk();
|
|
424
|
-
const context = sdk.getContext();
|
|
425
|
-
expect(context.meta.fetch.categories).toContain("http");
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/plugins/findFirstConnection/index.test.ts"],"names":[],"mappings":""}
|