@blokjs/api-call 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blokjs/api-call",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "files": ["dist"],
5
5
  "description": "Node module for making API calls",
6
6
  "type": "module",
@@ -26,8 +26,8 @@
26
26
  "vitest": "^4.0.18"
27
27
  },
28
28
  "dependencies": {
29
- "@blokjs/runner": "^2.0.1",
30
- "@blokjs/shared": "^2.0.1",
29
+ "@blokjs/runner": "^2.1.0",
30
+ "@blokjs/shared": "^2.1.0",
31
31
  "lodash": "^4.17.21",
32
32
  "zod": "^3.24.2"
33
33
  },
@@ -1,7 +0,0 @@
1
- /**
2
- * API Call Node Tests - Updated for Function-First Implementation
3
- *
4
- * Tests migrated from class-based to function-first pattern.
5
- * All existing behavior is preserved.
6
- */
7
- export {};
@@ -1,110 +0,0 @@
1
- /**
2
- * API Call Node Tests - Updated for Function-First Implementation
3
- *
4
- * Tests migrated from class-based to function-first pattern.
5
- * All existing behavior is preserved.
6
- */
7
- import { describe, expect, it, vi } from "vitest";
8
- import ApiCallNode from "../index.js";
9
- import { runApiCall } from "../util.js";
10
- // Mock the util function
11
- vi.mock("../util", () => ({
12
- runApiCall: vi.fn(),
13
- }));
14
- describe("ApiCall Node - Function-First", () => {
15
- const mockContext = {
16
- id: "test-id",
17
- workflow_name: "test-workflow",
18
- workflow_path: "/test",
19
- request: {
20
- method: "POST",
21
- body: { default: "data" },
22
- headers: {},
23
- params: {},
24
- query: {},
25
- },
26
- response: {
27
- data: {},
28
- success: true,
29
- error: null,
30
- },
31
- error: {
32
- message: [],
33
- },
34
- vars: {},
35
- config: {
36
- "api-call": {}, // Node configuration
37
- },
38
- logger: {
39
- log: vi.fn(),
40
- info: vi.fn(),
41
- error: vi.fn(),
42
- warn: vi.fn(),
43
- debug: vi.fn(),
44
- },
45
- env: {},
46
- eventLogger: null,
47
- _PRIVATE_: null,
48
- };
49
- const validInputs = {
50
- method: "GET",
51
- url: "https://api.example.com",
52
- headers: { Authorization: "Bearer token" },
53
- responseType: "json",
54
- body: { key: "value" },
55
- };
56
- it("should successfully make an API call and return response", async () => {
57
- const mockResult = { success: true, data: { message: "API Response" } };
58
- // Mock the API call
59
- vi.mocked(runApiCall).mockResolvedValue(mockResult);
60
- // Execute the node using handle()
61
- const result = (await ApiCallNode.handle(mockContext, validInputs));
62
- // Check the result structure
63
- expect(result.success).toBe(true);
64
- expect(result.data).toEqual(mockResult);
65
- expect(result.error).toBeNull();
66
- });
67
- it("should use ctx.response.data as the body if inputs.body is empty", async () => {
68
- mockContext.response.data = { fallback: "data" };
69
- const inputsWithoutBody = { ...validInputs, body: {} };
70
- const mockResult = { success: true, data: { fallback: "data" } };
71
- vi.mocked(runApiCall).mockResolvedValue(mockResult);
72
- const result = (await ApiCallNode.handle(mockContext, inputsWithoutBody));
73
- expect(result.success).toBe(true);
74
- expect(result.data).toEqual(mockResult);
75
- expect(result.error).toBeNull();
76
- });
77
- it("should return an error if the API call fails", async () => {
78
- const mockError = new Error("API request failed");
79
- vi.mocked(runApiCall).mockRejectedValue(mockError);
80
- const result = (await ApiCallNode.handle(mockContext, validInputs));
81
- expect(result.success).toBe(false);
82
- expect(result.error).toBeDefined();
83
- expect(result.error.message).toBe("API request failed");
84
- expect(result.error.context.code).toBe(500); // Runtime error = 500
85
- });
86
- it("should validate input with Zod and reject invalid URLs", async () => {
87
- const invalidInputs = {
88
- ...validInputs,
89
- url: "not-a-valid-url",
90
- };
91
- const result = (await ApiCallNode.handle(mockContext, invalidInputs));
92
- expect(result.success).toBe(false);
93
- expect(result.error).toBeDefined();
94
- expect(result.error.context.code).toBe(400); // Validation error = 400
95
- });
96
- it("should use default values for optional fields", async () => {
97
- const minimalInputs = {
98
- url: "https://api.example.com",
99
- };
100
- const mockResult = { success: true };
101
- vi.mocked(runApiCall).mockResolvedValue(mockResult);
102
- const result = (await ApiCallNode.handle(mockContext, minimalInputs));
103
- expect(result.success).toBe(true);
104
- // Verify runApiCall was called with defaults
105
- expect(vi.mocked(runApiCall)).toHaveBeenCalledWith("https://api.example.com", "GET", // default
106
- {}, // default headers
107
- mockContext.response.data, // default body from context
108
- "json");
109
- });
110
- });
@@ -1,5 +0,0 @@
1
- /**
2
- * Tests for runApiCall's HTTP-error handling — it must NOT discard the upstream
3
- * status, body, or Retry-After header on a >=400 response.
4
- */
5
- export {};
@@ -1,80 +0,0 @@
1
- /**
2
- * Tests for runApiCall's HTTP-error handling — it must NOT discard the upstream
3
- * status, body, or Retry-After header on a >=400 response.
4
- */
5
- import { GlobalError } from "@blokjs/shared";
6
- import { afterEach, describe, expect, it, vi } from "vitest";
7
- import { runApiCall } from "../util.js";
8
- function mockFetchResponse(opts) {
9
- const headers = new Headers(opts.headers ?? {});
10
- return {
11
- status: opts.status,
12
- statusText: opts.statusText ?? "",
13
- ok: opts.status < 400,
14
- headers,
15
- json: async () => opts.json,
16
- text: async () => opts.body ?? "",
17
- };
18
- }
19
- describe("runApiCall — HTTP error handling", () => {
20
- afterEach(() => {
21
- vi.restoreAllMocks();
22
- });
23
- it("throws a GlobalError carrying the upstream status code on >=400 (not a generic 500)", async () => {
24
- vi.stubGlobal("fetch", vi.fn(async () => mockFetchResponse({
25
- status: 429,
26
- statusText: "Too Many Requests",
27
- headers: { "content-type": "application/json", "retry-after": "30" },
28
- json: { error: "rate_limited" },
29
- })));
30
- const err = await runApiCall("https://api.example.com/x", "GET", {}, {}, "json").catch((e) => e);
31
- expect(err).toBeInstanceOf(GlobalError);
32
- const ge = err;
33
- expect(ge.context.code).toBe(429);
34
- expect(ge.context.name).toBe("ApiCallError");
35
- expect(ge.context.json).toMatchObject({
36
- status: 429,
37
- statusText: "Too Many Requests",
38
- retryAfter: "30",
39
- retryAfterSeconds: 30,
40
- body: { error: "rate_limited" },
41
- });
42
- });
43
- it("captures a text body and an HTTP-date Retry-After", async () => {
44
- const future = new Date(Date.now() + 60_000).toUTCString();
45
- vi.stubGlobal("fetch", vi.fn(async () => mockFetchResponse({
46
- status: 503,
47
- statusText: "Service Unavailable",
48
- headers: { "content-type": "text/plain", "retry-after": future },
49
- body: "down for maintenance",
50
- })));
51
- const err = (await runApiCall("https://api.example.com/y", "GET", {}, {}, "json").catch((e) => e));
52
- expect(err.context.code).toBe(503);
53
- const json = err.context.json;
54
- expect(json.body).toBe("down for maintenance");
55
- expect(typeof json.retryAfterSeconds).toBe("number");
56
- expect(json.retryAfterSeconds).toBeGreaterThan(50);
57
- });
58
- it("omits retry fields when no Retry-After header is present", async () => {
59
- vi.stubGlobal("fetch", vi.fn(async () => mockFetchResponse({
60
- status: 404,
61
- statusText: "Not Found",
62
- headers: { "content-type": "application/json" },
63
- json: { message: "missing" },
64
- })));
65
- const err = (await runApiCall("https://api.example.com/z", "GET", {}, {}, "json").catch((e) => e));
66
- expect(err.context.code).toBe(404);
67
- const json = err.context.json;
68
- expect(json).not.toHaveProperty("retryAfter");
69
- expect(json.body).toMatchObject({ message: "missing" });
70
- });
71
- it("returns the parsed JSON body on a 2xx response", async () => {
72
- vi.stubGlobal("fetch", vi.fn(async () => mockFetchResponse({
73
- status: 200,
74
- headers: { "content-type": "application/json" },
75
- json: { ok: true },
76
- })));
77
- const result = await runApiCall("https://api.example.com/ok", "GET", {}, {}, "json");
78
- expect(result).toEqual({ ok: true });
79
- });
80
- });