34
34
import software .amazon .smithy .model .shapes .BooleanShape ;
35
35
import software .amazon .smithy .model .shapes .CollectionShape ;
36
36
import software .amazon .smithy .model .shapes .DocumentShape ;
37
+ import software .amazon .smithy .model .shapes .DoubleShape ;
38
+ import software .amazon .smithy .model .shapes .FloatShape ;
37
39
import software .amazon .smithy .model .shapes .MemberShape ;
38
40
import software .amazon .smithy .model .shapes .NumberShape ;
39
41
import software .amazon .smithy .model .shapes .OperationShape ;
42
44
import software .amazon .smithy .model .shapes .StringShape ;
43
45
import software .amazon .smithy .model .shapes .StructureShape ;
44
46
import software .amazon .smithy .model .shapes .TimestampShape ;
47
+ import software .amazon .smithy .model .shapes .UnionShape ;
45
48
import software .amazon .smithy .model .traits .HttpTrait ;
46
49
import software .amazon .smithy .model .traits .TimestampFormatTrait .Format ;
47
50
import software .amazon .smithy .typescript .codegen .ApplicationProtocol ;
@@ -280,7 +283,7 @@ private void writeHeaders(
280
283
Shape target = index .getShape (binding .getMember ().getTarget ()).get ();
281
284
String headerValue = getInputValue (context , binding .getLocation (), "input." + memberName ,
282
285
binding .getMember (), target );
283
- writer .write ("headers['$L' ] = $L;" , binding .getLocationName (), headerValue );
286
+ writer .write ("headers[$S ] = $L;" , binding .getLocationName (), headerValue );
284
287
});
285
288
}
286
289
@@ -294,7 +297,7 @@ private void writeHeaders(
294
297
String headerValue = getInputValue (context , binding .getLocation (),
295
298
"input." + memberName + "[suffix]" , binding .getMember (), target );
296
299
// Append the suffix to the defined prefix and serialize the value in to that key.
297
- writer .write ("headers['$L' + suffix] = $L;" , binding .getLocationName (), headerValue );
300
+ writer .write ("headers[$S + suffix] = $L;" , binding .getLocationName (), headerValue );
298
301
});
299
302
});
300
303
}
@@ -318,14 +321,18 @@ private List<HttpBinding> writeRequestBody(
318
321
return documentBindings ;
319
322
}
320
323
if (!payloadBindings .isEmpty ()) {
321
- // TODO Validate payload structures are handled correctly.
322
324
SymbolProvider symbolProvider = context .getSymbolProvider ();
323
325
// There can only be one payload binding.
324
326
HttpBinding binding = payloadBindings .get (0 );
325
327
String memberName = symbolProvider .toMemberName (binding .getMember ());
326
- Shape target = context .getModel ().getShapeIndex ().getShape (binding .getMember ().getTarget ()).get ();
327
- writer .write ("let body: any = $L;" , getInputValue (
328
- context , Location .PAYLOAD , "input." + memberName , binding .getMember (), target ));
328
+
329
+ // Write the default `body` property.
330
+ writer .write ("let body: any = {};" );
331
+ writer .openBlock ("if (input.$L !== undefined) {" , "}" , memberName , () -> {
332
+ Shape target = context .getModel ().getShapeIndex ().getShape (binding .getMember ().getTarget ()).get ();
333
+ writer .write ("body = $L;" , getInputValue (
334
+ context , Location .PAYLOAD , "input." + memberName , binding .getMember (), target ));
335
+ });
329
336
return payloadBindings ;
330
337
}
331
338
@@ -363,6 +370,8 @@ private String getInputValue(
363
370
return getBlobInputParam (bindingType , dataSource );
364
371
} else if (target instanceof CollectionShape ) {
365
372
return getCollectionInputParam (bindingType , dataSource );
373
+ } else if (target instanceof StructureShape || target instanceof UnionShape ) {
374
+ return getNamedMembersInputParam (context , bindingType , dataSource , target );
366
375
}
367
376
368
377
throw new CodegenException (String .format (
@@ -418,6 +427,35 @@ private String getCollectionInputParam(
418
427
}
419
428
}
420
429
430
+ /**
431
+ * Given context and a source of data, generate an input value provider for the
432
+ * shape. This redirects to a serialization function for payloads,
433
+ * and fails otherwise.
434
+ *
435
+ * @param context The generation context.
436
+ * @param bindingType How this value is bound to the operation input.
437
+ * @param dataSource The in-code location of the data to provide an input of
438
+ * ({@code input.foo}, {@code entry}, etc.)
439
+ * @param target The shape of the value being provided.
440
+ * @return Returns a value or expression of the input shape.
441
+ */
442
+ private String getNamedMembersInputParam (
443
+ GenerationContext context ,
444
+ Location bindingType ,
445
+ String dataSource ,
446
+ Shape target
447
+ ) {
448
+ switch (bindingType ) {
449
+ case PAYLOAD :
450
+ // Redirect to a serialization function.
451
+ Symbol symbol = context .getSymbolProvider ().toSymbol (target );
452
+ return ProtocolGenerator .getSerFunctionName (symbol , context .getProtocolName ())
453
+ + "(" + dataSource + ", context)" ;
454
+ default :
455
+ throw new CodegenException ("Unexpected named member shape binding location `" + bindingType + "`" );
456
+ }
457
+ }
458
+
421
459
/**
422
460
* Writes the code needed to serialize the input document of a request.
423
461
*
@@ -517,10 +555,10 @@ private void readHeaders(
517
555
ShapeIndex index = context .getModel ().getShapeIndex ();
518
556
for (HttpBinding binding : bindingIndex .getRequestBindings (operation , Location .HEADER )) {
519
557
String memberName = symbolProvider .toMemberName (binding .getMember ());
520
- writer .openBlock ("if (output.headers[$L ] !== undefined) {" , "}" , binding .getLocationName (), () -> {
558
+ writer .openBlock ("if (output.headers[$S ] !== undefined) {" , "}" , binding .getLocationName (), () -> {
521
559
Shape target = index .getShape (binding .getMember ().getTarget ()).get ();
522
560
String headerValue = getOutputValue (context , binding .getLocation (),
523
- "output.headers[" + binding .getLocationName () + "]" , binding .getMember (), target );
561
+ "output.headers[' " + binding .getLocationName () + "' ]" , binding .getMember (), target );
524
562
writer .write ("contents.$L = $L;" , memberName , headerValue );
525
563
});
526
564
}
@@ -567,11 +605,10 @@ private List<HttpBinding> readResponseBody(
567
605
return documentBindings ;
568
606
}
569
607
if (!payloadBindings .isEmpty ()) {
570
- // TODO Validate payload structures are handled correctly.
571
608
// There can only be one payload binding.
572
609
HttpBinding binding = payloadBindings .get (0 );
573
610
Shape target = context .getModel ().getShapeIndex ().getShape (binding .getMember ().getTarget ()).get ();
574
- writer .write ("output .$L = $L;" , binding .getMemberName (), getOutputValue (context ,
611
+ writer .write ("contents .$L = $L;" , binding .getMemberName (), getOutputValue (context ,
575
612
Location .PAYLOAD , "data" , binding .getMember (), target ));
576
613
return payloadBindings ;
577
614
}
@@ -599,7 +636,11 @@ private String getOutputValue(
599
636
MemberShape member ,
600
637
Shape target
601
638
) {
602
- if (isNativeSimpleType (target )) {
639
+ if (target instanceof NumberShape ) {
640
+ return getNumberOutputParam (bindingType , dataSource , target );
641
+ } else if (target instanceof BooleanShape ) {
642
+ return getBooleanOutputParam (bindingType , dataSource );
643
+ } else if (target instanceof StringShape || target instanceof DocumentShape ) {
603
644
return dataSource ;
604
645
} else if (target instanceof TimestampShape ) {
605
646
HttpBindingIndex httpIndex = context .getModel ().getKnowledge (HttpBindingIndex .class );
@@ -609,13 +650,34 @@ private String getOutputValue(
609
650
return getBlobOutputParam (bindingType , dataSource );
610
651
} else if (target instanceof CollectionShape ) {
611
652
return getCollectionOutputParam (bindingType , dataSource );
653
+ } else if (target instanceof StructureShape || target instanceof UnionShape ) {
654
+ return getNamedMembersOutputParam (context , bindingType , dataSource , target );
612
655
}
613
656
614
657
throw new CodegenException (String .format (
615
658
"Unsupported %s binding of %s to %s in %s using the %s protocol" ,
616
659
bindingType , member .getMemberName (), target .getType (), member .getContainer (), getName ()));
617
660
}
618
661
662
+ /**
663
+ * Given context and a source of data, generate an output value provider for the
664
+ * boolean. By default, this checks strict equality to 'true'in headers and passes
665
+ * through for documents.
666
+ *
667
+ * @param bindingType How this value is bound to the operation output.
668
+ * @param dataSource The in-code location of the data to provide an output of
669
+ * ({@code output.foo}, {@code entry}, etc.)
670
+ * @return Returns a value or expression of the output boolean.
671
+ */
672
+ private String getBooleanOutputParam (Location bindingType , String dataSource ) {
673
+ switch (bindingType ) {
674
+ case HEADER :
675
+ return dataSource + " === 'true'" ;
676
+ default :
677
+ throw new CodegenException ("Unexpected blob binding location `" + bindingType + "`" );
678
+ }
679
+ }
680
+
619
681
/**
620
682
* Given context and a source of data, generate an output value provider for the
621
683
* blob. By default, this base64 decodes content in headers and passes through
@@ -660,6 +722,58 @@ private String getCollectionOutputParam(
660
722
}
661
723
}
662
724
725
+ /**
726
+ * Given context and a source of data, generate an output value provider for the
727
+ * shape. This redirects to a deserialization function for documents and payloads,
728
+ * and fails otherwise.
729
+ *
730
+ * @param context The generation context.
731
+ * @param bindingType How this value is bound to the operation output.
732
+ * @param dataSource The in-code location of the data to provide an output of
733
+ * ({@code output.foo}, {@code entry}, etc.)
734
+ * @param target The shape of the value being provided.
735
+ * @return Returns a value or expression of the output shape.
736
+ */
737
+ private String getNamedMembersOutputParam (
738
+ GenerationContext context ,
739
+ Location bindingType ,
740
+ String dataSource ,
741
+ Shape target
742
+ ) {
743
+ switch (bindingType ) {
744
+ case PAYLOAD :
745
+ // Redirect to a deserialization function.
746
+ Symbol symbol = context .getSymbolProvider ().toSymbol (target );
747
+ return ProtocolGenerator .getDeserFunctionName (symbol , context .getProtocolName ())
748
+ + "(" + dataSource + ", context)" ;
749
+ default :
750
+ throw new CodegenException ("Unexpected named member shape binding location `" + bindingType + "`" );
751
+ }
752
+ }
753
+
754
+ /**
755
+ * Given context and a source of data, generate an output value provider for the
756
+ * number. By default, invokes parseInt on byte/short/integer/long types in headers,
757
+ * invokes parseFloat on float/double types in headers, and fails otherwise.
758
+ *
759
+ * @param bindingType How this value is bound to the operation output.
760
+ * @param dataSource The in-code location of the data to provide an output of
761
+ * ({@code output.foo}, {@code entry}, etc.)
762
+ * @param target The shape of the value being provided.
763
+ * @return Returns a value or expression of the output number.
764
+ */
765
+ private String getNumberOutputParam (Location bindingType , String dataSource , Shape target ) {
766
+ switch (bindingType ) {
767
+ case HEADER :
768
+ if (target instanceof FloatShape || target instanceof DoubleShape ) {
769
+ return "parseFloat(" + dataSource + ", 10)" ;
770
+ }
771
+ return "parseInt(" + dataSource + ", 10)" ;
772
+ default :
773
+ throw new CodegenException ("Unexpected number binding location `" + bindingType + "`" );
774
+ }
775
+ }
776
+
663
777
/**
664
778
* Writes the code that loads an {@code errorCode} String with the content used
665
779
* to dispatch errors to specific serializers.
0 commit comments