Skip to content

Commit 90f7013

Browse files
fix(format-item): resolve wrap number issues (dynamodb-toolbox#590)
Co-authored-by: Adam McCarthy <5650580+amccarthy1@users.noreply.github.com>
1 parent a59efdf commit 90f7013

File tree

3 files changed

+121
-81
lines changed

3 files changed

+121
-81
lines changed

src/__tests__/entity.parse.unit.test.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { DocumentClientWithWrappedNumbers } from './bootstrap.test'
22

33
import Table from '../classes/Table'
44
import Entity from '../classes/Entity'
5-
import { toDynamoBigInt } from '../lib/utils'
65
import { unmarshall } from '@aws-sdk/util-dynamodb'
76

87
const TestTable = new Table({
@@ -206,7 +205,8 @@ describe('parse', () => {
206205

207206
it('parses wrapped numbers', () => {
208207
const wrap = (value: number) =>
209-
unmarshall({ valueToUnmarshall: {N: value.toString()} }, { wrapNumbers: true }).valueToUnmarshall.value
208+
unmarshall({ valueToUnmarshall: { N: value.toString() } }, { wrapNumbers: true })
209+
.valueToUnmarshall.value
210210

211211
const item = TestEntity.parse({
212212
pk: 'test@test.com',
@@ -226,35 +226,39 @@ describe('parse', () => {
226226
const item = TestEntity.parse({
227227
pk: 'test@test.com',
228228
sk: 'bigint',
229-
test_bigint: toDynamoBigInt(BigInt('90071992547409911234')),
229+
test_bigint: { value: '99999999999999999999999999999999999999' },
230230
test_bigint_coerce: '12345'
231231
})
232-
expect(item).toEqual({
233-
email: 'test@test.com',
234-
test_type: 'bigint',
235-
test_bigint: BigInt('90071992547409911234'),
236-
test_bigint_coerce: BigInt('12345')
237-
})
232+
expect(item).toMatchInlineSnapshot(`
233+
{
234+
"email": "test@test.com",
235+
"test_bigint": 99999999999999999999999999999999999999n,
236+
"test_bigint_coerce": 12345n,
237+
"test_type": "bigint",
238+
}
239+
`)
238240
})
239241

240242
it('parses bigint sets', () => {
241243
const item = TestEntity.parse({
242244
pk: 'test@test.com',
243245
sk: 'bigint',
244246
test_bigint_set_type: new Set([
245-
toDynamoBigInt(BigInt('90071992547409911234')),
246-
toDynamoBigInt(BigInt('-90071992547409911234')),
247+
{ value: '90071992547409911234' },
248+
{ value: '-90071992547409911234' },
247249
1234
248-
]),
249-
})
250-
expect(item).toEqual({
251-
email: 'test@test.com',
252-
test_type: 'bigint',
253-
test_bigint_set_type: [
254-
BigInt('90071992547409911234'),
255-
BigInt('-90071992547409911234'),
256-
BigInt(1234),
257-
]
250+
])
258251
})
252+
expect(item).toMatchInlineSnapshot(`
253+
{
254+
"email": "test@test.com",
255+
"test_bigint_set_type": [
256+
90071992547409911234n,
257+
-90071992547409911234n,
258+
1234n,
259+
],
260+
"test_type": "bigint",
261+
}
262+
`)
259263
})
260264
})

src/__tests__/formatItem.unit.test.ts

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ DefaultTable.addEntity(
2323
set: { type: 'set', setType: 'string', alias: 'set_alias' },
2424
set_alias2: { type: 'set', setType: 'string', map: 'set2' },
2525
number: 'number',
26+
numberSet: { type: 'set', setType: 'number' },
27+
bigint: 'bigint',
28+
bigintSet: { type: 'set', setType: 'bigint' },
2629
list: { type: 'list', alias: 'list_alias' },
2730
list_alias2: { type: 'list', map: 'list2' },
2831
test: 'map',
@@ -38,63 +41,95 @@ DefaultTable.addEntity(
3841
} as const)
3942
)
4043

41-
// console.log(DefaultTable.User);
42-
4344
describe('formatItem', () => {
4445
it('formats item with no alias', () => {
45-
const result = formatItem()(
46-
DefaultTable.User.schema.attributes,
47-
DefaultTable.User.linked,
48-
{ pk: 'test' }
49-
)
46+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
47+
pk: 'test'
48+
})
5049
expect(result).toEqual({ pk: 'test' })
5150
})
5251

5352
it('formats item with alias', () => {
54-
const result = formatItem()(
55-
DefaultTable.User.schema.attributes,
56-
DefaultTable.User.linked,
57-
{ list: ['test'] }
58-
)
53+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
54+
list: ['test']
55+
})
5956
expect(result).toEqual({ list_alias: ['test'] })
6057
})
6158

6259
it('formats item with mapping', () => {
63-
const result = formatItem()(
64-
DefaultTable.User.schema.attributes,
65-
DefaultTable.User.linked,
66-
{ list2: ['test'] }
67-
)
60+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
61+
list2: ['test']
62+
})
6863
expect(result).toEqual({ list_alias2: ['test'] })
6964
})
7065

7166
it('formats item with set (alias)', () => {
72-
const result = formatItem()(
73-
DefaultTable.User.schema.attributes,
74-
DefaultTable.User.linked,
75-
{ set: new Set([1, 2, 3]) }
76-
)
67+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
68+
set: new Set([1, 2, 3])
69+
})
7770
expect(result).toEqual({ set_alias: [1, 2, 3] })
7871
})
7972

8073
it('formats item with set (map)', () => {
81-
const result = formatItem()(
82-
DefaultTable.User.schema.attributes,
83-
DefaultTable.User.linked,
84-
{ set2: new Set([1, 2, 3]) }
85-
)
74+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
75+
set2: new Set([1, 2, 3])
76+
})
8677
expect(result).toEqual({ set_alias2: [1, 2, 3] })
8778
})
8879

8980
it('formats item with linked fields', () => {
90-
const result = formatItem()(
91-
DefaultTable.User.schema.attributes,
92-
DefaultTable.User.linked,
93-
{ sk: 'test1#test2' }
94-
)
81+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
82+
sk: 'test1#test2'
83+
})
9584
expect(result).toEqual({ sk: 'test1#test2', linked1: 'test1', linked2: 'test2' })
9685
})
9786

87+
it('formats item with wrapped numbers', () => {
88+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
89+
number: { value: '1' },
90+
numberSet: new Set([
91+
{
92+
value: '1'
93+
},
94+
{
95+
value: '2'
96+
},
97+
{
98+
value: '3'
99+
}
100+
]),
101+
bigint: { value: '1' },
102+
bigintSet: new Set([
103+
{
104+
value: '11234567899999999'
105+
},
106+
{
107+
value: '11234567899999999'
108+
},
109+
{
110+
value: '11234567899999999'
111+
}
112+
])
113+
})
114+
115+
expect(result).toMatchInlineSnapshot(`
116+
{
117+
"bigint": 1n,
118+
"bigintSet": [
119+
11234567899999999n,
120+
11234567899999999n,
121+
11234567899999999n,
122+
],
123+
"number": 1,
124+
"numberSet": [
125+
1,
126+
2,
127+
3,
128+
],
129+
}
130+
`)
131+
})
132+
98133
it('specifies attributes to include', () => {
99134
const result = formatItem()(
100135
DefaultTable.User.schema.attributes,
@@ -126,11 +161,9 @@ describe('formatItem', () => {
126161
})
127162

128163
it('formats item with linked aliased composite field', () => {
129-
const result = formatItem()(
130-
DefaultTable.User.schema.attributes,
131-
DefaultTable.User.linked,
132-
{ composite1: 'test1#test2' }
133-
)
164+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
165+
composite1: 'test1#test2'
166+
})
134167
expect(result).toEqual({
135168
composite1_alias: 'test1#test2',
136169
linked3: 'test1',
@@ -139,11 +172,9 @@ describe('formatItem', () => {
139172
})
140173

141174
it('formats item with linked mapped composite field', () => {
142-
const result = formatItem()(
143-
DefaultTable.User.schema.attributes,
144-
DefaultTable.User.linked,
145-
{ composite2: 'test1#test2' }
146-
)
175+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
176+
composite2: 'test1#test2'
177+
})
147178
expect(result).toEqual({
148179
composite2_alias: 'test1#test2',
149180
linked5: 'test1',
@@ -152,20 +183,16 @@ describe('formatItem', () => {
152183
})
153184

154185
it('passes through attribute not specified in entity', () => {
155-
const result = formatItem()(
156-
DefaultTable.User.schema.attributes,
157-
DefaultTable.User.linked,
158-
{ unspecified: 'value' }
159-
)
186+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
187+
unspecified: 'value'
188+
})
160189
expect(result).toEqual({ unspecified: 'value' })
161190
})
162191

163192
it('passes through null attribute', () => {
164-
const result = formatItem()(
165-
DefaultTable.User.schema.attributes,
166-
DefaultTable.User.linked,
167-
{ number: null }
168-
)
193+
const result = formatItem()(DefaultTable.User.schema.attributes, DefaultTable.User.linked, {
194+
number: null
195+
})
169196
expect(result).toEqual({ number: null })
170197
})
171198
})

src/lib/formatItem.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,43 @@
44
* @license MIT
55
*/
66

7-
import { PureAttributeDefinition } from '../classes/Entity'
7+
import type { NativeAttributeValue, NativeScalarAttributeValue, NumberValue } from '@aws-sdk/util-dynamodb'
8+
import type { PureAttributeDefinition } from '../classes/Entity'
89
import validateTypes from './validateTypes'
9-
import { Linked } from './parseEntity'
10+
import type { Linked } from './parseEntity'
1011

1112
// Convert from DocumentClient values, which may be wrapped sets or numbers,
1213
// into normal TS values.
13-
const convertDynamoValues = (value: unknown, attr?: PureAttributeDefinition): unknown => {
14+
const convertDynamoValues = (value: NativeAttributeValue, attr?: PureAttributeDefinition): unknown => {
1415
if (value === null) {
1516
return value
1617
}
1718

1819
// Unwrap bigint/number sets to regular numbers/bigints
1920
if (attr && attr.type === 'set') {
2021
if (attr.setType === 'bigint') {
21-
value = Array.from(value as Set<bigint>).map((n: any) => BigInt(n))
22+
value = Array.from(value as Set<bigint>).map((n: any) => BigInt(unwrapAttributeValue(n)))
2223
} else if (attr.setType === 'number') {
23-
value = Array.from(value as Set<number>).map((n: any) => Number(n))
24+
value = Array.from(value as Set<number>).map((n: any) => Number(unwrapAttributeValue(n)))
2425
} else {
2526
value = Array.from(value as Set<unknown>)
2627
}
2728
}
2829

29-
// Convert wrapped number values to bigints
3030
if (attr && attr.type === 'bigint') {
31-
value = BigInt(value as number)
31+
value = BigInt(unwrapAttributeValue(value))
3232
}
33+
3334
if (attr && attr.type === 'number') {
34-
value = Number(value as number)
35+
value = Number(unwrapAttributeValue(value))
36+
}
37+
38+
return value
39+
}
40+
41+
const unwrapAttributeValue = (value: NativeAttributeValue): boolean | number | bigint | string => {
42+
if( value?.value !== undefined ) {
43+
return value.value
3544
}
3645

3746
return value

0 commit comments

Comments
 (0)