Skip to content

Commit

Permalink
fix(lib-dynamodb): skip function properties in data objects for Dynam…
Browse files Browse the repository at this point in the history
…oDB (#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
  • Loading branch information
siddsriv authored Jan 31, 2024
1 parent ea7e6bf commit d054fe1
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 1 deletion.
9 changes: 9 additions & 0 deletions lib/lib-dynamodb/src/commands/marshallInput.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ describe("marshallInput and processObj", () => {
}
);
});

it("marshallInput should ignore function properties", () => {
const input = { Items: [() => {}, 1, "test"] };
const inputKeyNodes = { Items: null };
const output = { Items: { L: [{ N: "1" }, { S: "test" }] } };
expect(
marshallInput(input, inputKeyNodes, { convertTopLevelContainer: true, convertClassInstanceToMap: true })
).toEqual(output);
});
});

describe("marshallInput for commands", () => {
Expand Down
91 changes: 91 additions & 0 deletions lib/lib-dynamodb/src/commands/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,94 @@ describe("utils", () => {
});
});
});

describe("object with function property", () => {
const notAttrValue = { NotAttrValue: "NotAttrValue" };
const keyNodes = { Item: {} };
const nativeAttrObj = { Item: { id: 1, func: () => {} }, ...notAttrValue };
const attrObj = { Item: { id: { N: "1" } }, ...notAttrValue };
it("should remove functions", () => {
expect(
marshallInput(nativeAttrObj, keyNodes, { convertTopLevelContainer: true, convertClassInstanceToMap: true })
).toEqual(attrObj);
});

// List of functions
const listOfFunctions = { Item: { id: 1, funcs: [() => {}, () => {}] }, ...notAttrValue };
it("should remove functions from lists", () => {
expect(
marshallInput(listOfFunctions, keyNodes, { convertTopLevelContainer: true, convertClassInstanceToMap: true })
).toEqual({ Item: { id: { N: "1" }, funcs: { L: [] } }, ...notAttrValue });
});

// Nested list of functions
const nestedListOfFunctions = {
Item: {
id: 1,
funcs: [
[() => {}, () => {}],
[() => {}, () => {}],
],
},
...notAttrValue,
};
it("should remove functions from nested lists", () => {
expect(
marshallInput(nestedListOfFunctions, keyNodes, {
convertTopLevelContainer: true,
convertClassInstanceToMap: true,
})
).toEqual({ Item: { id: { N: "1" }, funcs: { L: [{ L: [] }, { L: [] }] } }, ...notAttrValue });
});

// Nested list of functions 3 levels down
const nestedListOfFunctions3Levels = {
Item: {
id: 1,
funcs: [
[
[() => {}, () => {}],
[() => {}, () => {}],
],
[
[() => {}, () => {}],
[() => {}, () => {}],
],
],
},
...notAttrValue,
};

it("should remove functions from a nested list of depth 3", () => {
expect(
marshallInput(nestedListOfFunctions3Levels, keyNodes, {
convertTopLevelContainer: true,
convertClassInstanceToMap: true,
})
).toEqual({
Item: {
id: { N: "1" },
funcs: {
L: [
{
L: [{ L: [] }, { L: [] }],
},
{
L: [{ L: [] }, { L: [] }],
},
],
},
},
...notAttrValue,
});
});
it("should throw when recursion depth has exceeded", () => {
const obj = {} as any;
obj.SELF = obj;
expect(() => marshallInput(obj, {}, { convertClassInstanceToMap: true })).toThrow(
new Error(
"Recursive copy depth exceeded 1000. Please set options.convertClassInstanceToMap to false and manually remove functions from your data object."
)
);
});
});
31 changes: 30 additions & 1 deletion lib/lib-dynamodb/src/commands/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,41 @@ const processAllKeysInObj = (obj: any, processFunc: Function, keyNodes: KeyNodes
}, {} as any);
};

function copyWithoutFunctions(o: any, depth = 0): any {
if (depth > 1000) {
throw new Error(
"Recursive copy depth exceeded 1000. Please set options.convertClassInstanceToMap to false and manually remove functions from your data object."
);
}
if (typeof o === "object" || typeof o === "function") {
if (Array.isArray(o)) {
return o.filter((item) => typeof item !== "function").map((item) => copyWithoutFunctions(item, depth + 1));
}
if (o === null) {
return null;
}
const copy = {} as any;
for (const [key, value] of Object.entries(o)) {
if (typeof value !== "function") {
copy[key] = copyWithoutFunctions(value, depth + 1);
}
}
return copy;
} else {
return o;
}
}

/**
* @internal
*/
export const marshallInput = (obj: any, keyNodes: KeyNodeChildren, options?: marshallOptions) => {
let _obj = obj;
if (options?.convertClassInstanceToMap) {
_obj = copyWithoutFunctions(obj);
}
const marshallFunc = (toMarshall: any) => marshall(toMarshall, options);
return processKeysInObj(obj, marshallFunc, keyNodes);
return processKeysInObj(_obj, marshallFunc, keyNodes);
};

/**
Expand Down

0 comments on commit d054fe1

Please sign in to comment.