@budibase/server 3.23.10 → 3.23.11
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/builder/assets/{easymde-92d28041.js → easymde-ad68176c.js} +1 -1
- package/builder/assets/{index-25de6765.css → index-68c7076a.css} +1 -1
- package/builder/assets/{index-200c1478.js → index-d2c74196.js} +3 -3
- package/builder/index.html +2 -2
- package/dist/automation.js +1 -1
- package/dist/automation.js.map +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/query.js +1 -1
- package/dist/query.js.map +2 -2
- package/package.json +2 -2
- package/src/integrations/rest.ts +31 -49
- package/src/integrations/tests/rest.spec.ts +44 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "3.23.
|
|
4
|
+
"version": "3.23.11",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -219,5 +219,5 @@
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
},
|
|
222
|
-
"gitHead": "
|
|
222
|
+
"gitHead": "c085b108795137b085f13ae0b0387bcf8454ee52"
|
|
223
223
|
}
|
package/src/integrations/rest.ts
CHANGED
|
@@ -481,59 +481,41 @@ export class RestIntegration implements IntegrationBase {
|
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
)
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
string | readonly string[]
|
|
492
|
-
> => {
|
|
493
|
-
if (!input.headers) {
|
|
494
|
-
const headerObject: Record<string, string> = {}
|
|
495
|
-
input.headers = headerObject
|
|
496
|
-
return headerObject
|
|
497
|
-
}
|
|
498
|
-
if (Array.isArray(input.headers)) {
|
|
499
|
-
const headerObject = input.headers.reduce<Record<string, string>>(
|
|
500
|
-
(acc, [name, value]) => {
|
|
501
|
-
acc[name] = value
|
|
502
|
-
return acc
|
|
503
|
-
},
|
|
504
|
-
{}
|
|
505
|
-
)
|
|
506
|
-
input.headers = headerObject
|
|
507
|
-
return headerObject
|
|
508
|
-
}
|
|
509
|
-
if (input.headers instanceof Headers) {
|
|
510
|
-
const headerObject: Record<string, string> = {}
|
|
511
|
-
input.headers.forEach((value, key) => {
|
|
512
|
-
headerObject[key] = value
|
|
513
|
-
})
|
|
514
|
-
input.headers = headerObject
|
|
515
|
-
return headerObject
|
|
516
|
-
}
|
|
517
|
-
return input.headers
|
|
484
|
+
const ensureHeaderObject = (): Record<
|
|
485
|
+
string,
|
|
486
|
+
string | readonly string[]
|
|
487
|
+
> => {
|
|
488
|
+
if (!input.headers) {
|
|
489
|
+
const headerObject: Record<string, string> = {}
|
|
490
|
+
return headerObject
|
|
518
491
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
492
|
+
if (Array.isArray(input.headers)) {
|
|
493
|
+
const headerObject = input.headers.reduce<Record<string, string>>(
|
|
494
|
+
(acc, [name, value]) => {
|
|
495
|
+
acc[name] = value
|
|
496
|
+
return acc
|
|
497
|
+
},
|
|
498
|
+
{}
|
|
524
499
|
)
|
|
525
|
-
|
|
526
|
-
continue
|
|
527
|
-
}
|
|
528
|
-
if (typeof headerValue === "undefined" || headerValue === null) {
|
|
529
|
-
continue
|
|
530
|
-
}
|
|
531
|
-
const headerString = Array.isArray(headerValue)
|
|
532
|
-
? headerValue.join(", ")
|
|
533
|
-
: String(headerValue)
|
|
534
|
-
headers[headerName] = headerString
|
|
500
|
+
return headerObject
|
|
535
501
|
}
|
|
502
|
+
if (input.headers instanceof Headers) {
|
|
503
|
+
return Object.fromEntries(input.headers)
|
|
504
|
+
}
|
|
505
|
+
return input.headers
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const headers = ensureHeaderObject()
|
|
509
|
+
|
|
510
|
+
// Delete Content-Type to allow fetch to auto-generate the correct header/boundary.
|
|
511
|
+
const existingContentTypeKey = Object.keys(headers).find(
|
|
512
|
+
key => key.toLowerCase() === "content-type"
|
|
513
|
+
)
|
|
514
|
+
if (existingContentTypeKey) {
|
|
515
|
+
delete headers[existingContentTypeKey]
|
|
536
516
|
}
|
|
517
|
+
|
|
518
|
+
input.headers = headers
|
|
537
519
|
input.body = form
|
|
538
520
|
break
|
|
539
521
|
}
|
|
@@ -28,9 +28,10 @@ import {
|
|
|
28
28
|
import { createServer } from "http"
|
|
29
29
|
import { AddressInfo } from "net"
|
|
30
30
|
import * as undici from "undici"
|
|
31
|
-
import
|
|
31
|
+
import {
|
|
32
32
|
RequestInit as UndiciRequestInit,
|
|
33
33
|
Response as UndiciResponse,
|
|
34
|
+
FormData as UndiciFormData,
|
|
34
35
|
} from "undici"
|
|
35
36
|
import TestConfiguration from "../../../src/tests/utilities/TestConfiguration"
|
|
36
37
|
import sdk from "../../sdk"
|
|
@@ -299,6 +300,48 @@ describe("REST Integration", () => {
|
|
|
299
300
|
expectFormDataToMatch(body, { a: "1", b: "2" })
|
|
300
301
|
})
|
|
301
302
|
|
|
303
|
+
it("should correctly clean conflicting Content-Type header for form data", async () => {
|
|
304
|
+
const input = { payload: "data", count: 42 }
|
|
305
|
+
|
|
306
|
+
queueJsonResponse(
|
|
307
|
+
(url, options) => {
|
|
308
|
+
expect(url).toEqual("https://example.com/api/submit")
|
|
309
|
+
expect(options?.method).toEqual("POST")
|
|
310
|
+
expect(options?.body).toBeInstanceOf(UndiciFormData)
|
|
311
|
+
|
|
312
|
+
const headers = options?.headers as Record<string, any>
|
|
313
|
+
const contentTypeHeader =
|
|
314
|
+
headers["Content-Type"] || headers["content-type"]
|
|
315
|
+
// The original Content-Type inserted in the test data below should be stripped so that
|
|
316
|
+
// undici can automatically insert the correct multipart/form-data header with boundary
|
|
317
|
+
expect(contentTypeHeader).toBeUndefined()
|
|
318
|
+
},
|
|
319
|
+
{ success: true }
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
const { data } = await integration.create({
|
|
323
|
+
path: "api/submit",
|
|
324
|
+
bodyType: BodyType.FORM_DATA,
|
|
325
|
+
requestBody: input,
|
|
326
|
+
// Injects a conflicting header that the production code MUST delete
|
|
327
|
+
headers: {
|
|
328
|
+
"Content-Type": "application/json",
|
|
329
|
+
"X-Custom-Header": "KeepMe",
|
|
330
|
+
},
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// Assert that the non-Content-Type header was kept
|
|
334
|
+
const lastFetchOptions = fetchMock.mock.calls[0][1]
|
|
335
|
+
expect((lastFetchOptions!.headers! as any)["X-Custom-Header"]).toEqual(
|
|
336
|
+
"KeepMe"
|
|
337
|
+
)
|
|
338
|
+
expect(
|
|
339
|
+
(lastFetchOptions!.headers! as any)["Content-Type"]
|
|
340
|
+
).toBeUndefined()
|
|
341
|
+
|
|
342
|
+
expect(data).toEqual({ success: true })
|
|
343
|
+
})
|
|
344
|
+
|
|
302
345
|
it("should allow encoded form data", () => {
|
|
303
346
|
const { URLSearchParams } = require("url")
|
|
304
347
|
const output = integration.addBody("encoded", input, {})
|