Skip to content

Commit 9f2c66f

Browse files
authored
Fix trait codegen for timestamp member with timestampformat trait (#2744)
1 parent a92a157 commit 9f2c66f

9 files changed

Lines changed: 127 additions & 47 deletions

File tree

smithy-trait-codegen/src/it/java/software/amazon/smithy/traitcodegen/test/CreatesTraitTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.example.traits.structures.BasicAnnotationTrait;
4242
import com.example.traits.structures.NestedA;
4343
import com.example.traits.structures.NestedB;
44+
import com.example.traits.structures.StructMemberWithTimestampFormatTrait;
4445
import com.example.traits.structures.StructWithIdrefMemberTrait;
4546
import com.example.traits.structures.StructWithListOfMapTrait;
4647
import com.example.traits.structures.StructWithUniqueItemsListTrait;
@@ -53,6 +54,8 @@
5354
import com.example.traits.uniqueitems.SetMember;
5455
import com.example.traits.uniqueitems.StringSetTrait;
5556
import com.example.traits.uniqueitems.StructureSetTrait;
57+
import java.time.Instant;
58+
import java.time.format.DateTimeFormatter;
5659
import java.util.stream.Stream;
5760
import org.junit.jupiter.params.ParameterizedTest;
5861
import org.junit.jupiter.params.provider.Arguments;
@@ -210,6 +213,14 @@ static Stream<Arguments> createTraitTests() {
210213
.idRefMemberB(ShapeId.from("test.smithy.traitcodegen#b"))
211214
.build()
212215
.toNode()),
216+
Arguments.of(StructMemberWithTimestampFormatTrait.ID,
217+
StructMemberWithTimestampFormatTrait.builder()
218+
.memberDateTime(Instant.parse("1985-04-12T23:20:50.52Z"))
219+
.memberHttpDate(Instant.from(
220+
DateTimeFormatter.RFC_1123_DATE_TIME.parse("Tue, 29 Apr 2014 18:30:38 GMT")))
221+
.memberEpochSeconds(Instant.ofEpochSecond((long) 1515531081.123))
222+
.build()
223+
.toNode()),
213224
// Timestamps
214225
Arguments.of(TimestampTrait.ID, Node.from("1985-04-12T23:20:50.52Z")),
215226
Arguments.of(DateTimeTimestampTrait.ID, Node.from("1985-04-12T23:20:50.52Z")),

smithy-trait-codegen/src/it/java/software/amazon/smithy/traitcodegen/test/LoadsFromModelTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.example.traits.structures.BasicAnnotationTrait;
4949
import com.example.traits.structures.NestedA;
5050
import com.example.traits.structures.NestedB;
51+
import com.example.traits.structures.StructMemberWithTimestampFormatTrait;
5152
import com.example.traits.structures.StructWithIdrefMemberTrait;
5253
import com.example.traits.structures.StructWithListOfMapTrait;
5354
import com.example.traits.structures.StructWithUniqueItemsListTrait;
@@ -332,6 +333,16 @@ static Stream<Arguments> loadsModelTests() {
332333
Optional.of(ShapeId.from("test.smithy.traitcodegen#a")),
333334
"getIdRefMemberB",
334335
Optional.of(ShapeId.from("test.smithy.traitcodegen#b")))),
336+
Arguments.of("structures/struct-member-with-timestamp-format-trait.smithy",
337+
StructMemberWithTimestampFormatTrait.class,
338+
MapUtils.of(
339+
"getMemberDateTime",
340+
Optional.of(Instant.parse("1985-04-12T23:20:50.52Z")),
341+
"getMemberHttpDate",
342+
Optional.of(Instant.from(
343+
DateTimeFormatter.RFC_1123_DATE_TIME.parse("Tue, 29 Apr 2014 18:30:38 GMT"))),
344+
"getMemberEpochSeconds",
345+
Optional.of(Instant.ofEpochSecond((long) 1515531081.123)))),
335346
// Timestamps
336347
Arguments.of("timestamps/struct-with-nested-timestamps.smithy",
337348
StructWithNestedTimestampsTrait.class,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
$version: "2.0"
2+
3+
namespace test.smithy.traitcodegen
4+
5+
use test.smithy.traitcodegen.structures#StructMemberWithTimestampFormat
6+
7+
@StructMemberWithTimestampFormat(
8+
memberDateTime: "1985-04-12T23:20:50.52Z"
9+
memberHttpDate: "Tue, 29 Apr 2014 18:30:38 GMT"
10+
memberEpochSeconds: 1515531081.123
11+
)
12+
structure myStruct {}

smithy-trait-codegen/src/main/java/software/amazon/smithy/traitcodegen/generators/FromNodeGenerator.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,8 @@ private MemberGenerator(MemberShape member) {
247247

248248
@Override
249249
public Void memberShape(MemberShape shape) {
250-
if (shape.hasTrait(IdRefTrait.ID)) {
251-
writer.writeInline(memberPrefix + "Member($1S, n -> $3C, builder::$2L)",
252-
fieldName,
253-
memberName,
254-
(Runnable) () -> shape
255-
.accept(new FromNodeMapperVisitor(writer, model, "n", 1, symbolProvider)));
250+
if (shape.hasTrait(IdRefTrait.ID) || shape.hasTrait(TimestampFormatTrait.ID)) {
251+
writeObjectNodeMember(shape);
256252
return null;
257253
}
258254
return model.expectShape(shape.getTarget()).accept(this);
@@ -413,19 +409,13 @@ public Void enumShape(EnumShape shape) {
413409

414410
@Override
415411
public Void structureShape(StructureShape shape) {
416-
writer.writeInline(memberPrefix + "Member($1S, n -> $3C, builder::$2L)",
417-
fieldName,
418-
memberName,
419-
(Runnable) () -> shape.accept(new FromNodeMapperVisitor(writer, model, "n", symbolProvider)));
412+
writeObjectNodeMember(shape);
420413
return null;
421414
}
422415

423416
@Override
424417
public Void timestampShape(TimestampShape shape) {
425-
writer.writeInline(memberPrefix + "Member($1S, n -> $3C, builder::$2L)",
426-
fieldName,
427-
memberName,
428-
(Runnable) () -> shape.accept(new FromNodeMapperVisitor(writer, model, "n", symbolProvider)));
418+
writeObjectNodeMember(shape);
429419
return null;
430420
}
431421

@@ -438,5 +428,12 @@ public Void unionShape(UnionShape shape) {
438428
public Void blobShape(BlobShape shape) {
439429
throw new UnsupportedOperationException("Shape not supported " + shape);
440430
}
431+
432+
private void writeObjectNodeMember(Shape shape) {
433+
writer.writeInline(memberPrefix + "Member($1S, n -> $3C, builder::$2L)",
434+
fieldName,
435+
memberName,
436+
(Runnable) () -> shape.accept(new FromNodeMapperVisitor(writer, model, "n", symbolProvider)));
437+
}
441438
}
442439
}

smithy-trait-codegen/src/main/java/software/amazon/smithy/traitcodegen/generators/FromNodeMapperVisitor.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -269,24 +269,10 @@ public Void structureShape(StructureShape shape) {
269269
@Override
270270
public Void timestampShape(TimestampShape shape) {
271271
if (shape.hasTrait(TimestampFormatTrait.ID)) {
272-
switch (shape.expectTrait(TimestampFormatTrait.class).getFormat()) {
273-
case EPOCH_SECONDS:
274-
writer.writeInline("$2T.ofEpochSecond($1L.expectNumberNode().getValue().longValue())",
275-
varName,
276-
Instant.class);
277-
return null;
278-
case HTTP_DATE:
279-
writer.writeInline("$2T.from($3T.RFC_1123_DATE_TIME.parse($1L.expectStringNode().getValue()))",
280-
varName,
281-
Instant.class,
282-
DateTimeFormatter.class);
283-
return null;
284-
default:
285-
// Fall through on default
286-
break;
287-
}
272+
writeForTimestampFormat(shape);
273+
} else {
274+
writer.writeInline("$2T.parse($1L.expectStringNode().getValue())", varName, Instant.class);
288275
}
289-
writer.writeInline("$2T.parse($1L.expectStringNode().getValue())", varName, Instant.class);
290276
return null;
291277
}
292278

@@ -304,6 +290,8 @@ public Void blobShape(BlobShape shape) {
304290
public Void memberShape(MemberShape shape) {
305291
if (shape.hasTrait(IdRefTrait.ID)) {
306292
writer.write("$T.fromNode($L)", ShapeId.class, varName);
293+
} else if (shape.hasTrait(TimestampFormatTrait.ID)) {
294+
writeForTimestampFormat(shape);
307295
} else {
308296
model.expectShape(shape.getTarget()).accept(this);
309297
}
@@ -321,4 +309,23 @@ public Void intEnumShape(IntEnumShape shape) {
321309
writer.write("$L.expectNumberNode().getValue().intValue()", varName);
322310
return null;
323311
}
312+
313+
private void writeForTimestampFormat(Shape shape) {
314+
switch (shape.expectTrait(TimestampFormatTrait.class).getFormat()) {
315+
case EPOCH_SECONDS:
316+
writer.writeInline("$2T.ofEpochSecond($1L.expectNumberNode().getValue().longValue())",
317+
varName,
318+
Instant.class);
319+
break;
320+
case HTTP_DATE:
321+
writer.writeInline("$2T.from($3T.RFC_1123_DATE_TIME.parse($1L.expectStringNode().getValue()))",
322+
varName,
323+
Instant.class,
324+
DateTimeFormatter.class);
325+
break;
326+
default:
327+
// Fall through on default
328+
writer.writeInline("$2T.parse($1L.expectStringNode().getValue())", varName, Instant.class);
329+
}
330+
}
324331
}

smithy-trait-codegen/src/main/java/software/amazon/smithy/traitcodegen/generators/ToNodeGenerator.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ public Void mapShape(MapShape shape) {
352352
public Void memberShape(MemberShape shape) {
353353
if (shape.hasTrait(IdRefTrait.ID)) {
354354
toStringMapper();
355+
} else if (shape.hasTrait(TimestampFormatTrait.ID)) {
356+
writeForTimestampFormat(shape);
355357
} else {
356358
model.expectShape(shape.getTarget()).accept(this);
357359
}
@@ -391,22 +393,10 @@ public Void structureShape(StructureShape shape) {
391393
@Override
392394
public Void timestampShape(TimestampShape shape) {
393395
if (shape.hasTrait(TimestampFormatTrait.ID)) {
394-
switch (shape.expectTrait(TimestampFormatTrait.class).getFormat()) {
395-
case EPOCH_SECONDS:
396-
writer.write("$T.from($L.getEpochSecond())", Node.class, varName);
397-
return null;
398-
case HTTP_DATE:
399-
writer.write("$T.from($T.RFC_1123_DATE_TIME.format($L))",
400-
Node.class,
401-
DateTimeFormatter.class,
402-
varName);
403-
return null;
404-
default:
405-
// Fall through on default
406-
break;
407-
}
396+
writeForTimestampFormat(shape);
397+
} else {
398+
toStringMapper();
408399
}
409-
toStringMapper();
410400
return null;
411401
}
412402

@@ -417,5 +407,24 @@ private void fromNodeMapper() {
417407
private void toStringMapper() {
418408
writer.write("$T.from($L.toString())", Node.class, varName);
419409
}
410+
411+
private void writeForTimestampFormat(Shape shape) {
412+
switch (shape.expectTrait(TimestampFormatTrait.class).getFormat()) {
413+
case EPOCH_SECONDS:
414+
writer.write("$T.from($L.getEpochSecond())", Node.class, varName);
415+
break;
416+
case HTTP_DATE:
417+
writer.write("$T.from($T.RFC_1123_DATE_TIME.format($T.ofInstant($L, $T.UTC)))",
418+
Node.class,
419+
DateTimeFormatter.class,
420+
ZonedDateTime.class,
421+
varName,
422+
ZoneOffset.class);
423+
break;
424+
default:
425+
// Fall through on default
426+
toStringMapper();
427+
}
428+
}
420429
}
421430
}

smithy-trait-codegen/src/test/java/software/amazon/smithy/traitcodegen/TraitCodegenPluginTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import software.amazon.smithy.model.node.ObjectNode;
2727

2828
public class TraitCodegenPluginTest {
29-
private static final int EXPECTED_NUMBER_OF_FILES = 68;
29+
private static final int EXPECTED_NUMBER_OF_FILES = 69;
3030

3131
private MockManifest manifest;
3232
private Model model;

smithy-trait-codegen/src/test/resources/META-INF/smithy/manifest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ structures/structure-trait.smithy
4343
structures/struct-with-listofmap-trait.smithy
4444
structures/struct-with-uniqueitems-list-trait.smithy
4545
structures/struct-with-idref-member-trait.smithy
46+
structures/struct-member-with-timestamp-format-trait.smithy
4647
timestamps/date-time-format-timestamp-trait.smithy
4748
timestamps/epoch-seconds-format-timestamp-trait.smithy
4849
timestamps/http-date-format-timestamp-trait.smithy
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
$version: "2.0"
2+
3+
namespace test.smithy.traitcodegen.structures
4+
5+
@trait
6+
structure StructMemberWithTimestampFormat {
7+
@timestampFormat("date-time")
8+
memberDateTime: Timestamp
9+
10+
@timestampFormat("http-date")
11+
memberHttpDate: Timestamp
12+
13+
memberEpochSeconds: MemberEpochSeconds
14+
15+
memberList: TimeStampList
16+
17+
memberMap: StringTimeStampMap
18+
}
19+
20+
@timestampFormat("epoch-seconds")
21+
timestamp MemberEpochSeconds
22+
23+
list TimeStampList {
24+
@timestampFormat("http-date")
25+
member: Timestamp
26+
}
27+
28+
map StringTimeStampMap {
29+
key: String
30+
@timestampFormat("http-date")
31+
value:Timestamp
32+
}

0 commit comments

Comments
 (0)