Skip to content

Commit 8034b05

Browse files
committed
fix(util-dynamodb): fix signature overload resolution for marshall() fn
1 parent 4759f6a commit 8034b05

File tree

2 files changed

+171
-42
lines changed

2 files changed

+171
-42
lines changed
Lines changed: 135 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,26 @@
11
import { AttributeValue } from "@aws-sdk/client-dynamodb";
22

3-
import { convertToAttr } from "./convertToAttr";
43
import { marshall } from "./marshall";
5-
6-
jest.mock("./convertToAttr");
4+
import { NumberValue } from "./NumberValue";
75

86
describe("marshall", () => {
9-
const mockOutput = { S: "mockOutput" };
10-
(convertToAttr as jest.Mock).mockReturnValue({ M: mockOutput });
11-
12-
afterEach(() => {
13-
jest.clearAllMocks();
14-
});
15-
167
it("with object as an input", () => {
178
const input = { a: "A", b: "B" };
18-
expect(marshall(input)).toEqual(mockOutput);
19-
expect(convertToAttr).toHaveBeenCalledTimes(1);
20-
expect(convertToAttr).toHaveBeenCalledWith(input, undefined);
9+
expect(marshall(input)).toEqual({
10+
a: { S: "A" },
11+
b: { S: "B" },
12+
});
2113
});
2214

2315
["convertEmptyValues", "removeUndefinedValues"].forEach((option) => {
2416
describe(`options.${option}`, () => {
2517
[false, true].forEach((value) => {
2618
it(`passes ${value} to convertToAttr`, () => {
2719
const input = { a: "A", b: "B" };
28-
expect(marshall(input, { [option]: value })).toEqual(mockOutput);
29-
expect(convertToAttr).toHaveBeenCalledTimes(1);
30-
expect(convertToAttr).toHaveBeenCalledWith(input, { [option]: value });
20+
expect(marshall(input, { [option]: value })).toEqual({
21+
a: { S: "A" },
22+
b: { S: "B" },
23+
});
3124
});
3225
});
3326
});
@@ -37,9 +30,10 @@ describe("marshall", () => {
3730
type TestInputType = { a: string; b: string };
3831
const input: TestInputType = { a: "A", b: "B" };
3932

40-
expect(marshall(input)).toEqual(mockOutput);
41-
expect(convertToAttr).toHaveBeenCalledTimes(1);
42-
expect(convertToAttr).toHaveBeenCalledWith(input, undefined);
33+
expect(marshall(input)).toEqual({
34+
a: { S: "A" },
35+
b: { S: "B" },
36+
});
4337
});
4438

4539
it("with Interface as an input", () => {
@@ -49,29 +43,134 @@ describe("marshall", () => {
4943
}
5044
const input: TestInputInterface = { a: "A", b: "B" };
5145

52-
expect(marshall(input)).toEqual(mockOutput);
53-
expect(convertToAttr).toHaveBeenCalledTimes(1);
54-
expect(convertToAttr).toHaveBeenCalledWith(input, undefined);
46+
expect(marshall(input)).toEqual({
47+
a: { S: "A" },
48+
b: { S: "B" },
49+
});
5550
});
5651

5752
it("should resolve signatures correctly", () => {
58-
// eslint-disable @typescript-eslint/no-unused-vars
5953
const ss: AttributeValue.SSMember = marshall(new Set(["a"]));
54+
expect(ss).toEqual({
55+
SS: ["a"],
56+
} as AttributeValue.SSMember);
6057
const ns: AttributeValue.NSMember = marshall(new Set([0]));
61-
const bs: AttributeValue.BSMember = marshall(new Set([new Uint8Array()]));
58+
expect(ns).toEqual({
59+
NS: ["0"],
60+
} as AttributeValue.NSMember);
61+
const bs: AttributeValue.BSMember = marshall(new Set([new Uint8Array(4)]));
62+
expect(bs).toEqual({
63+
BS: [new Uint8Array(4)],
64+
} as AttributeValue.BSMember);
6265
const s: AttributeValue.SMember = marshall("a");
63-
const n: AttributeValue.NMember = marshall(0);
66+
expect(s).toEqual({
67+
S: "a",
68+
} as AttributeValue.SMember);
69+
const n1: AttributeValue.NMember = marshall(0);
70+
expect(n1).toEqual({ N: "0" } as AttributeValue.NMember);
71+
const n2: AttributeValue.NMember = marshall(BigInt(0));
72+
expect(n2).toEqual({ N: "0" } as AttributeValue.NMember);
73+
const n3: AttributeValue.NMember = marshall(NumberValue.from(0));
74+
expect(n3).toEqual({ N: "0" } as AttributeValue.NMember);
75+
const binary: AttributeValue.BMember = marshall(new Uint8Array(4));
76+
expect(binary).toEqual({
77+
B: new Uint8Array(4),
78+
} as AttributeValue.BMember);
6479
const nil: AttributeValue.NULLMember = marshall(null);
65-
const bool: AttributeValue.BOOLMember = marshall(false);
66-
const array: AttributeValue[] = marshall([]);
67-
const object: Record<string, AttributeValue> = marshall({
80+
expect(nil).toEqual({
81+
NULL: true,
82+
} as AttributeValue.NULLMember);
83+
const bool: AttributeValue.BOOLMember = marshall(false as boolean);
84+
expect(bool).toEqual({
85+
BOOL: false,
86+
} as AttributeValue.BOOLMember);
87+
const array: AttributeValue[] = marshall([1, 2, 3]);
88+
expect(array).toEqual([{ N: "1" }, { N: "2" }, { N: "3" }] as AttributeValue.NMember[]);
89+
const arrayLDefault: AttributeValue[] = marshall([1, 2, 3], {});
90+
expect(arrayLDefault).toEqual([{ N: "1" }, { N: "2" }, { N: "3" }] as AttributeValue.NMember[]);
91+
const arrayLFalse: AttributeValue[] = marshall([1, 2, 3], {
92+
convertTopLevelContainer: false,
93+
});
94+
expect(arrayLFalse).toEqual([{ N: "1" }, { N: "2" }, { N: "3" }] as AttributeValue.NMember[]);
95+
const arrayLTrue: AttributeValue.LMember = marshall([1, 2, 3], {
96+
convertTopLevelContainer: true,
97+
});
98+
expect(arrayLTrue).toEqual({
99+
L: [{ N: "1" }, { N: "2" }, { N: "3" }],
100+
} as AttributeValue.LMember);
101+
const arrayLBoolean: AttributeValue.LMember | AttributeValue[] = marshall([1, 2, 3], {
102+
convertTopLevelContainer: true as boolean,
103+
});
104+
expect(arrayLBoolean).toEqual({
105+
L: [{ N: "1" }, { N: "2" }, { N: "3" }],
106+
} as AttributeValue.LMember);
107+
const object1: Record<string, AttributeValue> = marshall({
68108
pk: "abc",
69109
sk: "xyz",
70110
});
71-
const unrecognizedClassInstance1: Record<string, AttributeValue> = marshall(new Map());
72-
const unrecognizedClassInstance2: Record<string, AttributeValue> = marshall(new Date());
73-
const unrecognizedNonClassInstanceValue: AttributeValue.$UnknownMember = marshall(BigInt(0));
74-
// eslint-enable @typescript-eslint/no-unused-vars
111+
expect(object1).toEqual({
112+
pk: { S: "abc" },
113+
sk: { S: "xyz" },
114+
} as Record<string, AttributeValue.SMember>);
115+
const object2: Record<string, AttributeValue> = marshall(
116+
{
117+
pk: "abc",
118+
sk: "xyz",
119+
},
120+
{}
121+
);
122+
expect(object2).toEqual({
123+
pk: { S: "abc" },
124+
sk: { S: "xyz" },
125+
} as Record<string, AttributeValue.SMember>);
126+
const object3: AttributeValue.MMember = marshall(
127+
{
128+
pk: "abc",
129+
sk: "xyz",
130+
},
131+
{ convertTopLevelContainer: true }
132+
);
133+
expect(object3).toEqual({
134+
M: {
135+
pk: { S: "abc" },
136+
sk: { S: "xyz" },
137+
},
138+
} as AttributeValue.MMember);
139+
const object4: Record<string, AttributeValue> | AttributeValue.MMember = marshall(
140+
{
141+
pk: "abc",
142+
sk: "xyz",
143+
},
144+
{ convertTopLevelContainer: true as boolean }
145+
);
146+
expect(object4).toEqual({
147+
M: {
148+
pk: { S: "abc" },
149+
sk: { S: "xyz" },
150+
},
151+
} as AttributeValue.MMember);
152+
const map: Record<string, AttributeValue> = marshall(new Map([["a", "a"]]));
153+
expect(map).toEqual({
154+
a: { S: "a" },
155+
} as Record<string, AttributeValue.SMember>);
156+
const unrecognizedClassInstance: Record<string, AttributeValue> = marshall(new Date(), {
157+
convertClassInstanceToMap: true,
158+
});
159+
expect(unrecognizedClassInstance).toEqual({} as Record<string, AttributeValue>);
160+
161+
const unrecognizedClassInstance2: Record<string, AttributeValue> = marshall(
162+
new (class {
163+
public a = "a";
164+
public b = "b";
165+
})(),
166+
{
167+
convertClassInstanceToMap: true,
168+
}
169+
);
170+
expect(unrecognizedClassInstance2).toEqual({
171+
a: { S: "a" },
172+
b: { S: "b" },
173+
} as Record<string, AttributeValue>);
75174
});
76175

77176
it("with class instance as an input", () => {
@@ -80,8 +179,9 @@ describe("marshall", () => {
80179
}
81180
const input = new TestInputClass("A", "B");
82181

83-
expect(marshall(input)).toEqual(mockOutput);
84-
expect(convertToAttr).toHaveBeenCalledTimes(1);
85-
expect(convertToAttr).toHaveBeenCalledWith(input, undefined);
182+
expect(marshall(input, { convertClassInstanceToMap: true })).toEqual({
183+
a: { S: "A" },
184+
b: { S: "B" },
185+
});
86186
});
87187
});

packages/util-dynamodb/src/marshall.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { AttributeValue } from "@aws-sdk/client-dynamodb";
22

33
import { convertToAttr } from "./convertToAttr";
44
import { NativeAttributeBinary, NativeAttributeValue } from "./models";
5+
import { NumberValue } from "./NumberValue";
56

67
/**
78
* An optional configuration object for `marshall`
@@ -36,17 +37,45 @@ export interface marshallOptions {
3637
* @param options - An optional configuration object for `marshall`
3738
*
3839
*/
40+
export function marshall(data: null, options?: marshallOptions): AttributeValue.NULLMember;
41+
export function marshall<L extends NativeAttributeValue[], O extends { convertTopLevelContainer: true }>(
42+
data: L,
43+
options: marshallOptions & O
44+
): AttributeValue.LMember;
45+
export function marshall<L extends NativeAttributeValue[], O extends { convertTopLevelContainer: false }>(
46+
data: L,
47+
options: marshallOptions & O
48+
): AttributeValue[];
49+
export function marshall<L extends NativeAttributeValue[], O extends { convertTopLevelContainer: boolean }>(
50+
data: L,
51+
options: marshallOptions & O
52+
): AttributeValue[] | AttributeValue.LMember;
53+
export function marshall<L extends NativeAttributeValue[]>(data: L, options?: marshallOptions): AttributeValue[];
54+
export function marshall(
55+
data: Set<bigint> | Set<number> | Set<NumberValue>,
56+
options?: marshallOptions
57+
): AttributeValue.NSMember;
3958
export function marshall(data: Set<string>, options?: marshallOptions): AttributeValue.SSMember;
40-
export function marshall(data: Set<number>, options?: marshallOptions): AttributeValue.NSMember;
4159
export function marshall(data: Set<NativeAttributeBinary>, options?: marshallOptions): AttributeValue.BSMember;
42-
export function marshall(data: string, options?: marshallOptions): AttributeValue.SMember;
43-
export function marshall(data: number, options?: marshallOptions): AttributeValue.NMember;
4460
export function marshall(data: NativeAttributeBinary, options?: marshallOptions): AttributeValue.BMember;
45-
export function marshall(data: null, options?: marshallOptions): AttributeValue.NULLMember;
4661
export function marshall(data: boolean, options?: marshallOptions): AttributeValue.BOOLMember;
47-
export function marshall<L extends NativeAttributeValue[]>(data: L, options?: marshallOptions): AttributeValue[];
48-
export function marshall<M extends Record<string, NativeAttributeValue>>(
49-
data: M,
62+
export function marshall(data: number | NumberValue | bigint, options?: marshallOptions): AttributeValue.NMember;
63+
export function marshall(data: string, options?: marshallOptions): AttributeValue.SMember;
64+
export function marshall(data: boolean, options?: marshallOptions): AttributeValue.BOOLMember;
65+
export function marshall<O extends { convertTopLevelContainer: true }>(
66+
data: Map<string, NativeAttributeValue> | Record<string, NativeAttributeValue>,
67+
options: marshallOptions & O
68+
): AttributeValue.MMember;
69+
export function marshall<O extends { convertTopLevelContainer: false }>(
70+
data: Map<string, NativeAttributeValue> | Record<string, NativeAttributeValue>,
71+
options: marshallOptions & O
72+
): Record<string, AttributeValue>;
73+
export function marshall<O extends { convertTopLevelContainer: boolean }>(
74+
data: Map<string, NativeAttributeValue> | Record<string, NativeAttributeValue>,
75+
options: marshallOptions & O
76+
): Record<string, AttributeValue> | AttributeValue.MMember;
77+
export function marshall(
78+
data: Map<string, NativeAttributeValue> | Record<string, NativeAttributeValue>,
5079
options?: marshallOptions
5180
): Record<string, AttributeValue>;
5281
export function marshall(data: unknown, options?: marshallOptions): AttributeValue.$UnknownMember;

0 commit comments

Comments
 (0)