33
33
import software .amazon .smithy .model .shapes .OperationShape ;
34
34
import software .amazon .smithy .model .shapes .ServiceShape ;
35
35
import software .amazon .smithy .model .shapes .Shape ;
36
+ import software .amazon .smithy .model .shapes .ShapeId ;
36
37
import software .amazon .smithy .model .shapes .StructureShape ;
37
38
import software .amazon .smithy .model .traits .ErrorTrait ;
38
39
import software .amazon .smithy .model .traits .ExamplesTrait ;
39
40
import software .amazon .smithy .model .traits .HttpChecksumRequiredTrait ;
41
+ import software .amazon .smithy .model .traits .HttpErrorTrait ;
40
42
import software .amazon .smithy .model .traits .HttpTrait ;
41
43
import software .amazon .smithy .model .traits .TimestampFormatTrait ;
42
44
import software .amazon .smithy .model .traits .Trait ;
@@ -302,6 +304,7 @@ private Map<String, Node> createErrorExamplesForMembersWithHttpTraits(
302
304
* This method is used for converting the Smithy examples to OpenAPI examples for non-payload HTTP message body.
303
305
*/
304
306
private Map <String , Node > createBodyExamples (
307
+ Context <T > context ,
305
308
Shape operationOrError ,
306
309
List <HttpBinding > bindings ,
307
310
MessageType type ,
@@ -312,7 +315,7 @@ private Map<String, Node> createBodyExamples(
312
315
}
313
316
314
317
if (type == MessageType .ERROR ) {
315
- return createErrorBodyExamples (operationOrError , bindings , operation );
318
+ return createErrorBodyExamples (context , operationOrError , bindings , operation );
316
319
} else {
317
320
Map <String , Node > examples = new TreeMap <>();
318
321
// unique numbering for unique example names in OpenAPI.
@@ -342,6 +345,7 @@ private Map<String, Node> createBodyExamples(
342
345
}
343
346
344
347
private Map <String , Node > createErrorBodyExamples (
348
+ Context <T > context ,
345
349
Shape error ,
346
350
List <HttpBinding > bindings ,
347
351
OperationShape operation
@@ -350,14 +354,15 @@ private Map<String, Node> createErrorBodyExamples(
350
354
// unique numbering for unique example names in OpenAPI.
351
355
int uniqueNum = 1 ;
352
356
Optional <ExamplesTrait > examplesTrait = operation .getTrait (ExamplesTrait .class );
357
+
358
+ Set <ShapeId > errorShapeIds = getErrorShapeIdsForMatching (context , error , operation );
359
+
353
360
for (ExamplesTrait .Example example : examplesTrait .map (ExamplesTrait ::getExamples )
354
361
.orElse (Collections .emptyList ())) {
355
362
String name = operation .getId ().getName () + "_example" + uniqueNum ++;
356
- // this has to be checked because an operation can have more than one error linked to it.
357
363
if (example .getError ().isPresent ()
358
- && example .getError ().get ().getShapeId () == error .toShapeId ()) {
359
- // get members included in bindings
360
- ObjectNode values = getMembersWithHttpBindingTrait (bindings , example .getError ().get ().getContent ());
364
+ && errorShapeIds .contains (example .getError ().get ().getShapeId ())) {
365
+ ObjectNode values = example .getError ().get ().getContent ();
361
366
examples .put (name ,
362
367
ExampleObject .builder ()
363
368
.summary (example .getTitle ())
@@ -370,6 +375,39 @@ private Map<String, Node> createErrorBodyExamples(
370
375
return examples ;
371
376
}
372
377
378
+ private Set <ShapeId > getErrorShapeIdsForMatching (Context <T > context , Shape error , OperationShape operation ) {
379
+ Set <ShapeId > errorShapeIds = new LinkedHashSet <>();
380
+ String errorName = error .getId ().getName ();
381
+
382
+ if (errorName .matches (".*\\ d+Error$" )) {
383
+ if (error instanceof StructureShape && context .getModel () != null ) {
384
+ String statusCode = extractStatusCodeFromSyntheticErrorName (errorName );
385
+ context .getModel ().shapes (StructureShape .class )
386
+ .filter (shape -> shape .hasTrait (ErrorTrait .class ))
387
+ .filter (shape -> getHttpStatusCode (shape ).equals (statusCode ))
388
+ .forEach (shape -> errorShapeIds .add (shape .getId ()));
389
+ }
390
+ } else {
391
+ errorShapeIds .add (error .toShapeId ());
392
+ }
393
+
394
+ return errorShapeIds ;
395
+ }
396
+
397
+ private String extractStatusCodeFromSyntheticErrorName (String errorName ) {
398
+ if (errorName .matches (".*\\ d+Error$" )) {
399
+ return errorName .replaceAll (".*?(\\ d+)Error$" , "$1" );
400
+ }
401
+ return "400" ;
402
+ }
403
+
404
+ private String getHttpStatusCode (StructureShape errorShape ) {
405
+ return errorShape .getTrait (HttpErrorTrait .class )
406
+ .map (trait -> String .valueOf (trait .getCode ()))
407
+ .orElse (errorShape .hasTrait (ErrorTrait .class ) && "client" .equals (
408
+ errorShape .expectTrait (ErrorTrait .class ).getValue ()) ? "400" : "500" );
409
+ }
410
+
373
411
/*
374
412
* Returns a modified copy of [inputOrOutput] only containing members bound to a HttpBinding trait in [bindings].
375
413
*/
@@ -598,7 +636,7 @@ private Optional<RequestBodyObject> createRequestDocument(
598
636
String pointer = context .putSynthesizedSchema (synthesizedName , schema );
599
637
MediaTypeObject mediaTypeObject = MediaTypeObject .builder ()
600
638
.schema (Schema .builder ().ref (pointer ).build ())
601
- .examples (createBodyExamples (operation , bindings , MessageType .REQUEST , null ))
639
+ .examples (createBodyExamples (context , operation , bindings , MessageType .REQUEST , null ))
602
640
.build ();
603
641
604
642
// If any of the top level bindings are required, then the body itself must be required.
@@ -840,7 +878,7 @@ private void createResponseDocumentIfNeeded(
840
878
String pointer = context .putSynthesizedSchema (synthesizedName , schema );
841
879
MediaTypeObject mediaTypeObject = MediaTypeObject .builder ()
842
880
.schema (Schema .builder ().ref (pointer ).build ())
843
- .examples (createBodyExamples (operationOrError , bindings , messageType , operation ))
881
+ .examples (createBodyExamples (context , operationOrError , bindings , messageType , operation ))
844
882
.build ();
845
883
846
884
responseBuilder .putContent (mediaType , mediaTypeObject );
0 commit comments