Skip to content

Commit 4e4e06b

Browse files
committed
Implement the fundamental feature for unmarshalling
Signed-off-by: moznion <moznion@mail.moznion.net>
1 parent 51fd6e1 commit 4e4e06b

8 files changed

+667
-13
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import { AttributeValue } from '@aws-sdk/client-dynamodb';
22

33
export function dynamodbRecord<T extends object>(item: T): Record<keyof T, AttributeValue>;
4+
export function fromDynamodbRecord<T extends object>(attrs: Record<string, AttributeValue>): T;

lib/dynamodb_item_field.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import ts from 'typescript';
2-
import { DynamodbPrimitiveTypes } from './dynamodb_primitive_types';
2+
import { DynamodbPrimitiveTypes, dynamodbPrimitiveValueToJSValueConvertingOp } from './dynamodb_primitive_types';
33
import { warn } from './logger';
44

55
export interface DynamodbItemField {
66
generateCode(argName: string): ts.ObjectLiteralElementLike | undefined;
7+
generateCodeForUnmarshal(argName: string): ts.ObjectLiteralElementLike | undefined;
78
}
89

910
export class PrimitiveField implements DynamodbItemField {
@@ -28,6 +29,16 @@ export class PrimitiveField implements DynamodbItemField {
2829
),
2930
);
3031
}
32+
33+
generateCodeForUnmarshal(argName: string): ts.ObjectLiteralElementLike | undefined {
34+
const valueConvertingOp = dynamodbPrimitiveValueToJSValueConvertingOp(this.fieldType);
35+
return ts.factory.createPropertyAssignment(
36+
this.fieldName,
37+
ts.factory.createIdentifier(
38+
`${valueConvertingOp.leftOp}${argName}['${this.fieldName}']?.${this.fieldType}${valueConvertingOp.rightOp}`,
39+
),
40+
);
41+
}
3142
}
3243

3344
export class ArrayField implements DynamodbItemField {
@@ -51,6 +62,16 @@ export class ArrayField implements DynamodbItemField {
5162
),
5263
);
5364
}
65+
66+
generateCodeForUnmarshal(argName: string): ts.ObjectLiteralElementLike | undefined {
67+
const valueConvertingOp = dynamodbPrimitiveValueToJSValueConvertingOp(this.valueType);
68+
return ts.factory.createPropertyAssignment(
69+
this.fieldName,
70+
ts.factory.createIdentifier(
71+
`${argName}['${this.fieldName}']?.L?.map(v => ${valueConvertingOp.leftOp}v.${this.valueType}${valueConvertingOp.rightOp})`,
72+
),
73+
);
74+
}
5475
}
5576

5677
export class SetField implements DynamodbItemField {
@@ -88,6 +109,16 @@ export class SetField implements DynamodbItemField {
88109
),
89110
);
90111
}
112+
113+
generateCodeForUnmarshal(argName: string): ts.ObjectLiteralElementLike | undefined {
114+
const valueConvertingOp = dynamodbPrimitiveValueToJSValueConvertingOp(this.valueType);
115+
return ts.factory.createPropertyAssignment(
116+
this.fieldName,
117+
ts.factory.createIdentifier(
118+
`${argName}['${this.fieldName}']?.${this.valueType}S?.map(v => ${valueConvertingOp.leftOp}v${valueConvertingOp.rightOp})`,
119+
),
120+
);
121+
}
91122
}
92123

93124
export class MapField implements DynamodbItemField {
@@ -154,6 +185,43 @@ export class MapField implements DynamodbItemField {
154185
),
155186
);
156187
}
188+
189+
generateCodeForUnmarshal(argName: string): ts.ObjectLiteralElementLike | undefined {
190+
const valueIdent = ts.factory.createIdentifier('m');
191+
const recordIdent = ts.factory.createIdentifier('r');
192+
193+
const valueConvertingOp = dynamodbPrimitiveValueToJSValueConvertingOp(this.valueType);
194+
195+
return ts.factory.createPropertyAssignment(
196+
this.fieldName,
197+
ts.factory.createImmediatelyInvokedFunctionExpression([
198+
ts.factory.createVariableStatement(
199+
[],
200+
[ts.factory.createVariableDeclaration(valueIdent), ts.factory.createVariableDeclaration(recordIdent)],
201+
),
202+
ts.factory.createBinaryExpression(
203+
valueIdent,
204+
ts.SyntaxKind.EqualsToken,
205+
ts.factory.createIdentifier(`new Map()`),
206+
),
207+
ts.factory.createBinaryExpression(
208+
recordIdent,
209+
ts.SyntaxKind.EqualsToken,
210+
ts.factory.createIdentifier(`${argName}['${this.fieldName}']?.M`),
211+
),
212+
ts.factory.createForInStatement(
213+
ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration('k')], ts.NodeFlags.Const),
214+
recordIdent,
215+
ts.factory.createBlock([
216+
ts.factory.createIdentifier(
217+
`${valueIdent.text}.set(k, ${valueConvertingOp.leftOp}${recordIdent.text}[k]?.${this.valueType}${valueConvertingOp.rightOp})`,
218+
),
219+
] as unknown as ts.Statement[]),
220+
),
221+
ts.factory.createReturnStatement(valueIdent),
222+
] as ts.Statement[]),
223+
);
224+
}
157225
}
158226

159227
export class KeyValuePairMapField implements DynamodbItemField {
@@ -220,4 +288,41 @@ export class KeyValuePairMapField implements DynamodbItemField {
220288
),
221289
);
222290
}
291+
292+
generateCodeForUnmarshal(argName: string): ts.ObjectLiteralElementLike | undefined {
293+
const valueIdent = ts.factory.createIdentifier('m');
294+
const recordIdent = ts.factory.createIdentifier('r');
295+
296+
const valueConvertingOp = dynamodbPrimitiveValueToJSValueConvertingOp(this.valueType);
297+
298+
return ts.factory.createPropertyAssignment(
299+
this.fieldName,
300+
ts.factory.createImmediatelyInvokedFunctionExpression([
301+
ts.factory.createVariableStatement(
302+
[],
303+
[ts.factory.createVariableDeclaration(valueIdent), ts.factory.createVariableDeclaration(recordIdent)],
304+
),
305+
ts.factory.createBinaryExpression(valueIdent, ts.SyntaxKind.EqualsToken, ts.factory.createIdentifier('{}')),
306+
ts.factory.createBinaryExpression(
307+
recordIdent,
308+
ts.SyntaxKind.EqualsToken,
309+
ts.factory.createIdentifier(`${argName}['${this.fieldName}']?.M`),
310+
),
311+
ts.factory.createForInStatement(
312+
ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration('k')], ts.NodeFlags.Const),
313+
recordIdent,
314+
ts.factory.createBlock([
315+
ts.factory.createBinaryExpression(
316+
ts.factory.createIdentifier(`${valueIdent.text}[k]`),
317+
ts.SyntaxKind.EqualsToken,
318+
ts.factory.createIdentifier(
319+
`${valueConvertingOp.leftOp}${recordIdent.text}[k]?.${this.valueType}${valueConvertingOp.rightOp}`,
320+
),
321+
),
322+
] as unknown as ts.Statement[]),
323+
),
324+
ts.factory.createReturnStatement(valueIdent),
325+
] as ts.Statement[]),
326+
);
327+
}
223328
}

lib/dynamodb_primitive_types.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,45 @@ export function dynamodbPrimitiveTypeFromName(typeName: string | undefined): Dyn
5353
return undefined;
5454
}
5555
}
56+
57+
export interface DynamodbPrimitiveValueToJSValueConvertingOp {
58+
leftOp: string;
59+
rightOp: string;
60+
}
61+
62+
export function dynamodbPrimitiveValueToJSValueConvertingOp(
63+
primitiveType: DynamodbPrimitiveTypes,
64+
): DynamodbPrimitiveValueToJSValueConvertingOp {
65+
switch (primitiveType) {
66+
case DynamodbPrimitiveTypes.String:
67+
return {
68+
leftOp: '',
69+
rightOp: '',
70+
};
71+
case DynamodbPrimitiveTypes.Number:
72+
return {
73+
leftOp: '(() => { const n = Number(',
74+
rightOp: '); return isNaN(n) ? undefined : n; })()',
75+
};
76+
case DynamodbPrimitiveTypes.Boolean:
77+
return {
78+
leftOp: '',
79+
rightOp: '',
80+
};
81+
case DynamodbPrimitiveTypes.Null:
82+
return {
83+
leftOp: '(',
84+
rightOp: ' === true ? null : undefined)',
85+
};
86+
case DynamodbPrimitiveTypes.Binary:
87+
return {
88+
leftOp: '',
89+
rightOp: '',
90+
};
91+
default:
92+
return {
93+
leftOp: '',
94+
rightOp: '',
95+
};
96+
}
97+
}

lib/dynamodb_record_transformer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import {
1515
import { warn } from './logger';
1616

1717
export class DynamodbRecordTransformer {
18-
public static readonly dynamodbRecordFuncName = 'dynamodbRecord';
19-
private static readonly shouldLenientTypeCheck = !!process.env['TS_DYNAMODB_ATTR_TRANSFORMER_LENIENT_TYPE_CHECK'];
18+
public static readonly dynamodbRecordFuncName = 'dynamodbRecord'; // TODO
19+
private static readonly shouldLenientTypeCheck = !!process.env['TS_DYNAMODB_ATTR_TRANSFORMER_LENIENT_TYPE_CHECK']; // TODO
2020

2121
public static visitNode(node: ts.CallExpression, typeChecker: ts.TypeChecker): ts.Node | undefined {
2222
if (node.typeArguments === undefined || node.typeArguments.length !== 1 || !node.typeArguments[0]) {

0 commit comments

Comments
 (0)