Skip to content

Commit 16b0e73

Browse files
WIP: Fix wrongly converting action's headers to camelCase
1 parent cae6adb commit 16b0e73

File tree

1 file changed

+55
-3
lines changed

1 file changed

+55
-3
lines changed

src/libs/blueos/schemas.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,61 @@ const toKebabCase = (str: string): string => {
2020
* @param {z.ZodTypeAny} schema The Zod schema to convert
2121
* @returns {z.ZodTypeAny} The converted Zod schema
2222
*/
23-
const preprocessCamelCase = <T extends z.ZodTypeAny>(schema: T): z.ZodTypeAny => {
24-
return z.preprocess((val: Record<string, unknown>) => camelcaseKeys(val, { deep: true }), schema)
25-
}
23+
const preprocessCamelCase = <T extends z.ZodTypeAny>(schema: T): z.ZodTypeAny =>
24+
z.preprocess((val: any) => {
25+
if (!val || typeof val !== 'object') return val
26+
27+
// eslint-disable-next-line jsdoc/require-jsdoc
28+
function deepCloneJson<V>(v: V): V {
29+
return JSON.parse(JSON.stringify(v))
30+
}
31+
32+
// deep-clone to avoid mutating original input (JSON clone is fine for config objects)
33+
const clone = deepCloneJson(val)
34+
35+
// collect paths and values of all headers objects
36+
// eslint-disable-next-line jsdoc/require-jsdoc
37+
const headerEntries: Array<{ path: (string | number)[]; value: any }> = []
38+
39+
const extract = (obj: any, path: (string | number)[]) => {
40+
if (!obj || typeof obj !== 'object') return
41+
if (Array.isArray(obj)) {
42+
obj.forEach((it, i) => extract(it, path.concat(i)))
43+
return
44+
}
45+
for (const [k, v] of Object.entries(obj)) {
46+
if (k === 'headers' && v && typeof v === 'object' && !Array.isArray(v)) {
47+
headerEntries.push({ path: path.concat(k), value: v })
48+
delete obj[k]
49+
} else {
50+
extract(v, path.concat(k))
51+
}
52+
}
53+
}
54+
55+
extract(clone, [])
56+
57+
// camelcase everything else deeply
58+
const camelized = camelcaseKeys(clone, { deep: true })
59+
60+
// helper to set by path
61+
const setByPath = (root: any, path: (string | number)[], value: any) => {
62+
let cur = root
63+
for (let i = 0; i < path.length - 1; i++) {
64+
const p = path[i]
65+
if (cur[p] === undefined) cur[p] = typeof path[i + 1] === 'number' ? [] : {}
66+
cur = cur[p]
67+
}
68+
cur[path[path.length - 1]] = value
69+
}
70+
71+
// restore headers at same paths
72+
for (const { path, value } of headerEntries) {
73+
setByPath(camelized, path, value)
74+
}
75+
76+
return camelized
77+
}, schema)
2678

2779
// Zod schemas for external API responses
2880
const ServiceMetadataSchema = z

0 commit comments

Comments
 (0)