Skip to content

Commit 04a81db

Browse files
fix(docs): Fix JSON arrays getting converted to objects in snippet payloads (#4714)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]>
1 parent 2c48908 commit 04a81db

File tree

3 files changed

+190
-2
lines changed

3 files changed

+190
-2
lines changed

packages/fdr-sdk/src/api-definition/snippets/backfill.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ async function backfillSnippetsForExample(
142142
// process request body similar to getHarRequest
143143
let bodyValue = undefined;
144144
if (example.requestBody != null && example.requestBody.type === "json" && example.requestBody.value) {
145-
if (typeof example.requestBody.value === "object") {
145+
if (typeof example.requestBody.value === "object" && !Array.isArray(example.requestBody.value)) {
146146
const filteredValue = Object.fromEntries(
147147
Object.entries(example.requestBody.value).filter(([_, valueObj]) => {
148148
// keep arrays and primitive values
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import type { EndpointDefinition, ExampleEndpointCall } from "../latest";
2+
import { EndpointId } from "../latest";
3+
import { getHarRequest } from "./get-har-request";
4+
5+
describe("getHarRequest", () => {
6+
it("preserves array JSON request body", () => {
7+
const endpoint: EndpointDefinition = {
8+
id: EndpointId("test-endpoint"),
9+
displayName: "Test Endpoint",
10+
operationId: undefined,
11+
description: undefined,
12+
availability: undefined,
13+
method: "POST",
14+
path: [{ type: "literal", value: "/test" }],
15+
defaultEnvironment: undefined,
16+
environments: undefined,
17+
pathParameters: undefined,
18+
queryParameters: undefined,
19+
requestHeaders: undefined,
20+
responseHeaders: undefined,
21+
requests: undefined,
22+
responses: undefined,
23+
errors: undefined,
24+
auth: undefined,
25+
examples: undefined,
26+
snippetTemplates: undefined,
27+
protocol: undefined,
28+
namespace: undefined
29+
};
30+
31+
const example: ExampleEndpointCall = {
32+
name: undefined,
33+
description: "",
34+
path: "/test",
35+
pathParameters: undefined,
36+
queryParameters: undefined,
37+
headers: undefined,
38+
requestBody: {
39+
type: "json",
40+
value: [
41+
{
42+
id: "5df263b7db5a7e6ea03fae9b",
43+
name: "How to reset your password",
44+
content:
45+
'# How to reset your password\n\n1. Go to the login page\n2. Click on the "Forgot password" link\n3. Follow the instructions',
46+
knowledge_source_id: "5df263b7db5a7e6ea03fae9b"
47+
}
48+
]
49+
},
50+
responseStatusCode: 200,
51+
responseBody: undefined,
52+
snippets: undefined
53+
};
54+
55+
const harRequest = getHarRequest(endpoint, example, {}, example.requestBody);
56+
57+
expect(harRequest.postData?.text).toBeDefined();
58+
const parsedBody = JSON.parse(harRequest.postData!.text!);
59+
60+
expect(Array.isArray(parsedBody)).toBe(true);
61+
expect(parsedBody).toHaveLength(1);
62+
expect(parsedBody[0]).toEqual({
63+
id: "5df263b7db5a7e6ea03fae9b",
64+
name: "How to reset your password",
65+
content:
66+
'# How to reset your password\n\n1. Go to the login page\n2. Click on the "Forgot password" link\n3. Follow the instructions',
67+
knowledge_source_id: "5df263b7db5a7e6ea03fae9b"
68+
});
69+
70+
expect(harRequest.postData!.text!.trim()).toMatch(/^\[/);
71+
expect(harRequest.postData!.text!.trim()).toMatch(/\]$/);
72+
});
73+
74+
it("preserves object JSON request body", () => {
75+
const endpoint: EndpointDefinition = {
76+
id: EndpointId("test-endpoint"),
77+
displayName: "Test Endpoint",
78+
operationId: undefined,
79+
description: undefined,
80+
availability: undefined,
81+
method: "POST",
82+
path: [{ type: "literal", value: "/test" }],
83+
defaultEnvironment: undefined,
84+
environments: undefined,
85+
pathParameters: undefined,
86+
queryParameters: undefined,
87+
requestHeaders: undefined,
88+
responseHeaders: undefined,
89+
requests: undefined,
90+
responses: undefined,
91+
errors: undefined,
92+
auth: undefined,
93+
examples: undefined,
94+
snippetTemplates: undefined,
95+
protocol: undefined,
96+
namespace: undefined
97+
};
98+
99+
const example: ExampleEndpointCall = {
100+
name: undefined,
101+
description: "",
102+
path: "/test",
103+
pathParameters: undefined,
104+
queryParameters: undefined,
105+
headers: undefined,
106+
requestBody: {
107+
type: "json",
108+
value: {
109+
name: "John Doe",
110+
111+
}
112+
},
113+
responseStatusCode: 200,
114+
responseBody: undefined,
115+
snippets: undefined
116+
};
117+
118+
const harRequest = getHarRequest(endpoint, example, {}, example.requestBody);
119+
120+
expect(harRequest.postData?.text).toBeDefined();
121+
const parsedBody = JSON.parse(harRequest.postData!.text!);
122+
expect(Array.isArray(parsedBody)).toBe(false);
123+
expect(parsedBody).toEqual({
124+
name: "John Doe",
125+
126+
});
127+
});
128+
129+
it("filters out empty object properties from object request body", () => {
130+
const endpoint: EndpointDefinition = {
131+
id: EndpointId("test-endpoint"),
132+
displayName: "Test Endpoint",
133+
operationId: undefined,
134+
description: undefined,
135+
availability: undefined,
136+
method: "POST",
137+
path: [{ type: "literal", value: "/test" }],
138+
defaultEnvironment: undefined,
139+
environments: undefined,
140+
pathParameters: undefined,
141+
queryParameters: undefined,
142+
requestHeaders: undefined,
143+
responseHeaders: undefined,
144+
requests: undefined,
145+
responses: undefined,
146+
errors: undefined,
147+
auth: undefined,
148+
examples: undefined,
149+
snippetTemplates: undefined,
150+
protocol: undefined,
151+
namespace: undefined
152+
};
153+
154+
const example: ExampleEndpointCall = {
155+
name: undefined,
156+
description: "",
157+
path: "/test",
158+
pathParameters: undefined,
159+
queryParameters: undefined,
160+
headers: undefined,
161+
requestBody: {
162+
type: "json",
163+
value: {
164+
name: "John Doe",
165+
166+
emptyObject: {},
167+
validArray: [1, 2, 3],
168+
validObject: { key: "value" }
169+
}
170+
},
171+
responseStatusCode: 200,
172+
responseBody: undefined,
173+
snippets: undefined
174+
};
175+
176+
const harRequest = getHarRequest(endpoint, example, {}, example.requestBody);
177+
178+
expect(harRequest.postData?.text).toBeDefined();
179+
const parsedBody = JSON.parse(harRequest.postData!.text!);
180+
expect(parsedBody).toEqual({
181+
name: "John Doe",
182+
183+
validArray: [1, 2, 3],
184+
validObject: { key: "value" }
185+
});
186+
expect(parsedBody).not.toHaveProperty("emptyObject");
187+
});
188+
});

packages/fdr-sdk/src/api-definition/snippets/get-har-request.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function getHarRequest(
5151
};
5252

5353
// filter out request parameters that have no value and are not an array
54-
if (requestBody.value && typeof requestBody.value === "object") {
54+
if (requestBody.value && typeof requestBody.value === "object" && !Array.isArray(requestBody.value)) {
5555
requestBody.value = Object.fromEntries(
5656
Object.entries(requestBody.value).filter(([_, valueObj]) => {
5757
// Keep arrays and primitive values

0 commit comments

Comments
 (0)