88import java .nio .file .Paths ;
99import java .util .ArrayList ;
1010import java .util .HashSet ;
11+ import java .util .LinkedHashMap ;
1112import java .util .List ;
1213import java .util .Map ;
1314import java .util .Optional ;
15+ import java .util .Set ;
1416import java .util .TreeMap ;
1517import java .util .stream .Collectors ;
1618import software .amazon .smithy .codegen .core .CodegenException ;
3335
3436@ SmithyInternalApi
3537final class DocumentClientCommandGenerator implements Runnable {
36-
3738 static final String COMMAND_PROPERTIES_SECTION = "command_properties" ;
3839 static final String COMMAND_BODY_EXTRA_SECTION = "command_body_extra" ;
3940 static final String COMMAND_CONSTRUCTOR_SECTION = "command_constructor" ;
@@ -55,6 +56,10 @@ final class DocumentClientCommandGenerator implements Runnable {
5556 private final List <MemberShape > outputMembersWithAttr ;
5657 private final String clientCommandClassName ;
5758 private final String clientCommandLocalName ;
59+ private final String helperTypePrefix ;
60+ private final Map <String , String > structureHelperTypeNames = new LinkedHashMap <>();
61+ private final List <StructureShape > structureHelperTypes = new ArrayList <>();
62+ private final Set <String > reservedHelperTypeNames = new HashSet <>();
5863 /**
5964 * Map of package name to external:local name entries.
6065 */
@@ -90,6 +95,9 @@ final class DocumentClientCommandGenerator implements Runnable {
9095
9196 clientCommandClassName = symbol .getName ();
9297 clientCommandLocalName = "__" + clientCommandClassName ;
98+ helperTypePrefix = DocumentClientUtils .getModifiedName (symbol .getName ().replaceAll ("Command$" , "" ));
99+ reservedHelperTypeNames .add (inputTypeName );
100+ reservedHelperTypeNames .add (outputTypeName );
93101 }
94102
95103 @ Override
@@ -320,151 +328,50 @@ private void writeStructureKeyNode(StructureShape structureTarget) {
320328 });
321329 }
322330
323- /**
324- * Detects operations requiring custom TransactWrite item type generation by inspecting
325- * the input shape structure (TransactItems list of structures with ConditionCheck/Put/Delete/Update).
326- */
327- private boolean requiresTransactWriteItemTypes () {
328- Optional <StructureShape > inputOpt = operationIndex .getInput (operation );
329- if (inputOpt .isEmpty ()) {
330- return false ;
331- }
332- StructureShape inputShape = inputOpt .get ();
333- if (!inputShape .getMember ("TransactItems" ).isPresent ()) {
334- return false ;
335- }
336- Shape transactItemsTarget = model .expectShape (
337- inputShape .getMember ("TransactItems" ).get ().getTarget ()
338- );
339- if (!transactItemsTarget .isListShape ()) {
340- return false ;
341- }
342- Shape itemShape = model .expectShape (
343- ((CollectionShape ) transactItemsTarget ).getMember ().getTarget ()
344- );
345- if (!itemShape .isStructureShape ()) {
346- return false ;
331+ private void generateInputAndOutputTypes () {
332+ collectHelperTypes (inputMembersWithAttr );
333+ collectHelperTypes (outputMembersWithAttr );
334+
335+ for (StructureShape structureShape : structureHelperTypes ) {
336+ writer .write ("" );
337+ writeNamedStructureOmitType (structureShape );
347338 }
348- StructureShape itemStructure = (StructureShape ) itemShape ;
349- return itemStructure .getMember ("ConditionCheck" ).isPresent ()
350- && itemStructure .getMember ("Put" ).isPresent ()
351- && itemStructure .getMember ("Delete" ).isPresent ()
352- && itemStructure .getMember ("Update" ).isPresent ();
353- }
354339
355- private void generateInputAndOutputTypes () {
356340 writer .write ("" );
357- if (requiresTransactWriteItemTypes ()) {
358- generateTransactWriteItemTypes ();
359- writeTransactWriteInputType ();
360- } else {
361- writeType (inputTypeName , originalInputTypeName , operationIndex .getInput (operation ), inputMembersWithAttr );
362- }
341+ writeType (inputTypeName , originalInputTypeName , operationIndex .getInput (operation ), inputMembersWithAttr );
363342 writer .write ("" );
364343 writeType (outputTypeName , originalOutputTypeName , operationIndex .getOutput (operation ), outputMembersWithAttr );
365344 writer .write ("" );
366345 }
367346
368- private void generateTransactWriteItemTypes () {
369- String clientPkg = AwsDependency .CLIENT_DYNAMODB_PEER .getPackageName ();
370- String utilPkg = AwsDependency .UTIL_DYNAMODB .getPackageName ();
371-
372- registerTypeImport ("ConditionCheck" , "ConditionCheck" , clientPkg );
373- registerTypeImport ("Put" , "Put" , clientPkg );
374- registerTypeImport ("Delete" , "Delete" , clientPkg );
375- registerTypeImport ("Update" , "Update" , clientPkg );
376- registerTypeImport ("TransactWriteItem" , "ClientTransactWriteItem" , clientPkg );
377- registerTypeImport ("NativeAttributeValue" , "NativeAttributeValue" , utilPkg );
347+ private void collectHelperTypes (List <MemberShape > membersWithAttr ) {
348+ for (MemberShape member : membersWithAttr ) {
349+ collectHelperTypes (member , new HashSet <>());
350+ }
351+ }
378352
379- writer .writeDocs (
380- "Document client variant of ConditionCheck for TransactWrite.\n "
381- + "Uses native JavaScript types (NativeAttributeValue) instead of AttributeValue.\n @public"
382- );
383- writer .openBlock (
384- "export type TransactWriteConditionCheck = Omit<ConditionCheck, \" Key\" | \" ExpressionAttributeValues\" > & {" ,
385- "};" ,
386- () -> {
387- writer .write ("Key: Record<string, NativeAttributeValue> | undefined;" );
388- writer .write ("ExpressionAttributeValues?: Record<string, NativeAttributeValue> | undefined;" );
353+ private void collectHelperTypes (MemberShape member , Set <String > parents ) {
354+ Shape memberTarget = model .expectShape (member .getTarget ());
355+ if (memberTarget .isStructureShape ()) {
356+ StructureShape structureTarget = (StructureShape ) memberTarget ;
357+ String structureId = structureTarget .getId ().toString ();
358+ if (!parents .add (structureId )) {
359+ return ;
389360 }
390- );
391- writer .write ("" );
392361
393- writer .writeDocs (
394- "Document client variant of Put for TransactWrite.\n "
395- + "Uses native JavaScript types (NativeAttributeValue) instead of AttributeValue.\n @public"
396- );
397- writer .openBlock (
398- "export type TransactWritePut = Omit<Put, \" Item\" | \" ExpressionAttributeValues\" > & {" ,
399- "};" ,
400- () -> {
401- writer .write ("Item: Record<string, NativeAttributeValue> | undefined;" );
402- writer .write ("ExpressionAttributeValues?: Record<string, NativeAttributeValue> | undefined;" );
362+ List <MemberShape > membersWithAttr = getStructureMembersWithAttr (Optional .of (structureTarget ));
363+ for (MemberShape memberWithAttr : membersWithAttr ) {
364+ collectHelperTypes (memberWithAttr , parents );
403365 }
404- );
405- writer .write ("" );
406-
407- writer .writeDocs (
408- "Document client variant of Delete for TransactWrite.\n "
409- + "Uses native JavaScript types (NativeAttributeValue) instead of AttributeValue.\n @public"
410- );
411- writer .openBlock (
412- "export type TransactWriteDelete = Omit<Delete, \" Key\" | \" ExpressionAttributeValues\" > & {" ,
413- "};" ,
414- () -> {
415- writer .write ("Key: Record<string, NativeAttributeValue> | undefined;" );
416- writer .write ("ExpressionAttributeValues?: Record<string, NativeAttributeValue> | undefined;" );
366+ if (!membersWithAttr .isEmpty ()) {
367+ getStructureHelperTypeName (structureTarget );
417368 }
418- );
419- writer .write ("" );
420-
421- writer .writeDocs (
422- "Document client variant of Update for TransactWrite.\n "
423- + "Uses native JavaScript types (NativeAttributeValue) instead of AttributeValue.\n @public"
424- );
425- writer .openBlock (
426- "export type TransactWriteUpdate = Omit<Update, \" Key\" | \" ExpressionAttributeValues\" > & {" ,
427- "};" ,
428- () -> {
429- writer .write ("Key: Record<string, NativeAttributeValue> | undefined;" );
430- writer .write ("ExpressionAttributeValues?: Record<string, NativeAttributeValue> | undefined;" );
431- }
432- );
433- writer .write ("" );
434-
435- writer .writeDocs (
436- "Document client variant of TransactWriteItem.\n "
437- + "Uses native JavaScript types (NativeAttributeValue) instead of AttributeValue.\n "
438- + "Each item must have exactly one of ConditionCheck, Put, Delete, or Update.\n @public"
439- );
440- writer .openBlock (
441- "export type TransactWriteItem = Omit<ClientTransactWriteItem, "
442- + "\" ConditionCheck\" | \" Put\" | \" Delete\" | \" Update\" > & {" ,
443- "};" ,
444- () -> {
445- writer .write ("ConditionCheck?: TransactWriteConditionCheck;" );
446- writer .write ("Put?: TransactWritePut;" );
447- writer .write ("Delete?: TransactWriteDelete;" );
448- writer .write ("Update?: TransactWriteUpdate;" );
449- }
450- );
451- writer .write ("" );
452- }
453-
454- private void writeTransactWriteInputType () {
455- registerTypeImport (
456- originalInputTypeName ,
457- "__" + originalInputTypeName ,
458- AwsDependency .CLIENT_DYNAMODB_PEER .getPackageName ()
459- );
460- writer .writeDocs ("@public" );
461- writer .openBlock (
462- "export type $L = Omit<__$L, \" TransactItems\" > & {" ,
463- "};" ,
464- inputTypeName ,
465- originalInputTypeName ,
466- () -> writer .write ("TransactItems: TransactWriteItem[] | undefined;" )
467- );
369+ parents .remove (structureId );
370+ } else if (memberTarget .isMapShape ()) {
371+ collectHelperTypes (((MapShape ) memberTarget ).getValue (), parents );
372+ } else if (memberTarget instanceof CollectionShape ) {
373+ collectHelperTypes (((CollectionShape ) memberTarget ).getMember (), parents );
374+ }
468375 }
469376
470377 private List <MemberShape > getStructureMembersWithAttr (Optional <StructureShape > optionalShape ) {
@@ -518,20 +425,18 @@ private void writeType(
518425 }
519426 }
520427
521- private void writeStructureOmitType (StructureShape structureTarget ) {
428+ private void writeNamedStructureOmitType (StructureShape structureTarget ) {
522429 List <MemberShape > membersWithAttr = getStructureMembersWithAttr (Optional .of (structureTarget ));
523430 String memberUnionToOmit = membersWithAttr .stream ()
524431 .map (memberWithAttr -> "'" + symbolProvider .toMemberName (memberWithAttr ) + "'" )
525432 .collect (Collectors .joining (" | " ));
526- String typeNameToOmit = symbolProvider .toSymbol (structureTarget ).getName ();
527- registerTypeImport (
528- typeNameToOmit ,
529- typeNameToOmit ,
530- AwsDependency .CLIENT_DYNAMODB_PEER .getPackageName ()
531- );
433+ String typeNameToOmit = getStructureBaseTypeName (structureTarget );
434+
435+ writer .writeDocs ("@public" );
532436 writer .openBlock (
533- "Omit<$L, $L> & {" ,
534- "}" ,
437+ "export type $L = Omit<$L, $L> & {" ,
438+ "};" ,
439+ getStructureHelperTypeName (structureTarget ),
535440 typeNameToOmit ,
536441 memberUnionToOmit ,
537442 () -> {
@@ -558,7 +463,7 @@ private void writeStructureMemberOmitType(MemberShape member) {
558463 private void writeMemberOmitType (MemberShape member , boolean allowUndefined ) {
559464 Shape memberTarget = model .expectShape (member .getTarget ());
560465 if (memberTarget .isStructureShape ()) {
561- writeStructureOmitType (( StructureShape ) memberTarget );
466+ writer . write ( getStructureHelperTypeName (( StructureShape ) memberTarget ) );
562467 } else if (memberTarget .isUnionShape ()) {
563468 if (symbolProvider .toSymbol (memberTarget ).getName ().equals ("AttributeValue" )) {
564469 writeNativeAttributeValue ();
@@ -588,6 +493,40 @@ private void writeMemberOmitType(MemberShape member, boolean allowUndefined) {
588493 }
589494 }
590495
496+ private String getStructureHelperTypeName (StructureShape structureTarget ) {
497+ String structureId = structureTarget .getId ().toString ();
498+ if (structureHelperTypeNames .containsKey (structureId )) {
499+ return structureHelperTypeNames .get (structureId );
500+ }
501+
502+ String shapeName = symbolProvider .toSymbol (structureTarget ).getName ();
503+ String preferredName = shapeName .startsWith (helperTypePrefix )
504+ ? shapeName
505+ : helperTypePrefix + shapeName ;
506+ String helperTypeName = preferredName ;
507+ int collisionSuffix = 2 ;
508+ while (!reservedHelperTypeNames .add (helperTypeName )) {
509+ helperTypeName = preferredName + collisionSuffix ++;
510+ }
511+
512+ structureHelperTypeNames .put (structureId , helperTypeName );
513+ structureHelperTypes .add (structureTarget );
514+ return helperTypeName ;
515+ }
516+
517+ private String getStructureBaseTypeName (StructureShape structureTarget ) {
518+ String externalName = symbolProvider .toSymbol (structureTarget ).getName ();
519+ String helperTypeName = getStructureHelperTypeName (structureTarget );
520+ String localName = externalName .equals (helperTypeName ) ? "Client" + externalName : externalName ;
521+
522+ registerTypeImport (
523+ externalName ,
524+ localName ,
525+ AwsDependency .CLIENT_DYNAMODB_PEER .getPackageName ()
526+ );
527+ return localName ;
528+ }
529+
591530 private void writeNativeAttributeValue () {
592531 String nativeAttributeValue = "NativeAttributeValue" ;
593532 registerTypeImport (
0 commit comments