Skip to content

Commit d054fe1

Browse files
authoredJan 31, 2024
fix(lib-dynamodb): skip function properties in data objects for DynamoDB (#5697)
* fix(lib-dynamodb): check for function types in processKeys * test(lib-dynamodb): adding cases for skipping function properties when processing keys * fix(lib-dynamodb): skip function in processKeys for array case * test(lib-dynamodb): adding additional test cases for function properties * fix(lib-dynamodb): additional checks for function type in processObj * fix(lib-dynamodb): add a function to skip function properties processing * test(lib-dynamodb): correct attribute objects for cases
1 parent ea7e6bf commit d054fe1

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed
 

‎lib/lib-dynamodb/src/commands/marshallInput.spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ describe("marshallInput and processObj", () => {
1010
}
1111
);
1212
});
13+
14+
it("marshallInput should ignore function properties", () => {
15+
const input = { Items: [() => {}, 1, "test"] };
16+
const inputKeyNodes = { Items: null };
17+
const output = { Items: { L: [{ N: "1" }, { S: "test" }] } };
18+
expect(
19+
marshallInput(input, inputKeyNodes, { convertTopLevelContainer: true, convertClassInstanceToMap: true })
20+
).toEqual(output);
21+
});
1322
});
1423

1524
describe("marshallInput for commands", () => {

‎lib/lib-dynamodb/src/commands/utils.spec.ts

+91
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,94 @@ describe("utils", () => {
146146
});
147147
});
148148
});
149+
150+
describe("object with function property", () => {
151+
const notAttrValue = { NotAttrValue: "NotAttrValue" };
152+
const keyNodes = { Item: {} };
153+
const nativeAttrObj = { Item: { id: 1, func: () => {} }, ...notAttrValue };
154+
const attrObj = { Item: { id: { N: "1" } }, ...notAttrValue };
155+
it("should remove functions", () => {
156+
expect(
157+
marshallInput(nativeAttrObj, keyNodes, { convertTopLevelContainer: true, convertClassInstanceToMap: true })
158+
).toEqual(attrObj);
159+
});
160+
161+
// List of functions
162+
const listOfFunctions = { Item: { id: 1, funcs: [() => {}, () => {}] }, ...notAttrValue };
163+
it("should remove functions from lists", () => {
164+
expect(
165+
marshallInput(listOfFunctions, keyNodes, { convertTopLevelContainer: true, convertClassInstanceToMap: true })
166+
).toEqual({ Item: { id: { N: "1" }, funcs: { L: [] } }, ...notAttrValue });
167+
});
168+
169+
// Nested list of functions
170+
const nestedListOfFunctions = {
171+
Item: {
172+
id: 1,
173+
funcs: [
174+
[() => {}, () => {}],
175+
[() => {}, () => {}],
176+
],
177+
},
178+
...notAttrValue,
179+
};
180+
it("should remove functions from nested lists", () => {
181+
expect(
182+
marshallInput(nestedListOfFunctions, keyNodes, {
183+
convertTopLevelContainer: true,
184+
convertClassInstanceToMap: true,
185+
})
186+
).toEqual({ Item: { id: { N: "1" }, funcs: { L: [{ L: [] }, { L: [] }] } }, ...notAttrValue });
187+
});
188+
189+
// Nested list of functions 3 levels down
190+
const nestedListOfFunctions3Levels = {
191+
Item: {
192+
id: 1,
193+
funcs: [
194+
[
195+
[() => {}, () => {}],
196+
[() => {}, () => {}],
197+
],
198+
[
199+
[() => {}, () => {}],
200+
[() => {}, () => {}],
201+
],
202+
],
203+
},
204+
...notAttrValue,
205+
};
206+
207+
it("should remove functions from a nested list of depth 3", () => {
208+
expect(
209+
marshallInput(nestedListOfFunctions3Levels, keyNodes, {
210+
convertTopLevelContainer: true,
211+
convertClassInstanceToMap: true,
212+
})
213+
).toEqual({
214+
Item: {
215+
id: { N: "1" },
216+
funcs: {
217+
L: [
218+
{
219+
L: [{ L: [] }, { L: [] }],
220+
},
221+
{
222+
L: [{ L: [] }, { L: [] }],
223+
},
224+
],
225+
},
226+
},
227+
...notAttrValue,
228+
});
229+
});
230+
it("should throw when recursion depth has exceeded", () => {
231+
const obj = {} as any;
232+
obj.SELF = obj;
233+
expect(() => marshallInput(obj, {}, { convertClassInstanceToMap: true })).toThrow(
234+
new Error(
235+
"Recursive copy depth exceeded 1000. Please set options.convertClassInstanceToMap to false and manually remove functions from your data object."
236+
)
237+
);
238+
});
239+
});

‎lib/lib-dynamodb/src/commands/utils.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,41 @@ const processAllKeysInObj = (obj: any, processFunc: Function, keyNodes: KeyNodes
9090
}, {} as any);
9191
};
9292

93+
function copyWithoutFunctions(o: any, depth = 0): any {
94+
if (depth > 1000) {
95+
throw new Error(
96+
"Recursive copy depth exceeded 1000. Please set options.convertClassInstanceToMap to false and manually remove functions from your data object."
97+
);
98+
}
99+
if (typeof o === "object" || typeof o === "function") {
100+
if (Array.isArray(o)) {
101+
return o.filter((item) => typeof item !== "function").map((item) => copyWithoutFunctions(item, depth + 1));
102+
}
103+
if (o === null) {
104+
return null;
105+
}
106+
const copy = {} as any;
107+
for (const [key, value] of Object.entries(o)) {
108+
if (typeof value !== "function") {
109+
copy[key] = copyWithoutFunctions(value, depth + 1);
110+
}
111+
}
112+
return copy;
113+
} else {
114+
return o;
115+
}
116+
}
117+
93118
/**
94119
* @internal
95120
*/
96121
export const marshallInput = (obj: any, keyNodes: KeyNodeChildren, options?: marshallOptions) => {
122+
let _obj = obj;
123+
if (options?.convertClassInstanceToMap) {
124+
_obj = copyWithoutFunctions(obj);
125+
}
97126
const marshallFunc = (toMarshall: any) => marshall(toMarshall, options);
98-
return processKeysInObj(obj, marshallFunc, keyNodes);
127+
return processKeysInObj(_obj, marshallFunc, keyNodes);
99128
};
100129

101130
/**

0 commit comments

Comments
 (0)
Please sign in to comment.