Repository: smithy-lang/smithy
Pull request #2027 (2023-11-03 04:21:49)

Add relativeDocumentation to aws.iam#iamResource and aws.iam#defineConditionKeys traits
*Description of changes:*
Add relativeDocumentation to aws.iam#iamResource and aws.iam#defineConditionKeys traits

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/docs/source-2.0/aws/aws-iam.rst b/docs/source-2.0/aws/aws-iam.rst
index 5ba48d570..d1556ead3 100644
--- a/docs/source-2.0/aws/aws-iam.rst
+++ b/docs/source-2.0/aws/aws-iam.rst
@@ -139,6 +139,10 @@ Each condition key structure supports the following members:
     * - externalDocumentation
       - ``string``
       - A valid URL that defines more information about the condition key.
+    * - relativeDocumentation
+      - ``string``
+      - A relative URL path that defines more information about the condition key
+        within a set of IAM-related documentation.
 
 .. code-block:: smithy
 
@@ -410,6 +414,10 @@ members:
     * - name
       - ``string``
       - The name of the resource in AWS IAM.
+    * - relativeDocumentation
+      - ``string``
+      - A relative URL path that defines more information about the resource
+        within a set of IAM-related documentation.
 
 The following example defines a simple resource with a name in AWS IAM that
 deviates from the :ref:`shape name of the shape ID <shape-id>` of the resource.
diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeyDefinition.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeyDefinition.java
index e0a90c4ac..da0b38d61 100644
--- a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeyDefinition.java
+++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/ConditionKeyDefinition.java
@@ -28,15 +28,18 @@ public final class ConditionKeyDefinition implements ToNode, ToSmithyBuilder<Con
     private static final String TYPE = "type";
     private static final String DOCUMENTATION = "documentation";
     private static final String EXTERNAL_DOCUMENTATION = "externalDocumentation";
+    private static final String RELATIVE_DOCUMENTATION = "relativeDocumentation";
 
     private final String type;
     private final String documentation;
     private final String externalDocumentation;
+    private final String relativeDocumentation;
 
     private ConditionKeyDefinition(Builder builder) {
         type = SmithyBuilder.requiredState(TYPE, builder.type);
         documentation = builder.documentation;
         externalDocumentation = builder.externalDocumentation;
+        relativeDocumentation = builder.relativeDocumentation;
     }
 
     public static Builder builder() {
@@ -51,6 +54,8 @@ public final class ConditionKeyDefinition implements ToNode, ToSmithyBuilder<Con
                 .ifPresent(builder::documentation);
         objectNode.getStringMember(EXTERNAL_DOCUMENTATION).map(StringNode::getValue)
                 .ifPresent(builder::externalDocumentation);
+        objectNode.getStringMember(RELATIVE_DOCUMENTATION).map(StringNode::getValue)
+                .ifPresent(builder::relativeDocumentation);
 
         return builder.build();
     }
@@ -76,11 +81,22 @@ public final class ConditionKeyDefinition implements ToNode, ToSmithyBuilder<Con
         return Optional.ofNullable(externalDocumentation);
     }
 
+    /**
+     * Get the relative URL path that defines more information about the condition key
+     * within a set of IAM-related documentation.
+     *
+     * @return A relative URL to the documentation page.
+     */
+    public Optional<String> getRelativeDocumentation() {
+        return Optional.ofNullable(relativeDocumentation);
+    }
+
     @Override
     public SmithyBuilder<ConditionKeyDefinition> toBuilder() {
         return builder()
                 .documentation(documentation)
                 .externalDocumentation(externalDocumentation)
+                .relativeDocumentation(relativeDocumentation)
                 .type(type);
     }
 
@@ -90,6 +106,7 @@ public final class ConditionKeyDefinition implements ToNode, ToSmithyBuilder<Con
                 .withMember(TYPE, Node.from(type))
                 .withOptionalMember(DOCUMENTATION, getDocumentation().map(Node::from))
                 .withOptionalMember(EXTERNAL_DOCUMENTATION, getExternalDocumentation().map(Node::from))
+                .withOptionalMember(RELATIVE_DOCUMENTATION, getRelativeDocumentation().map(Node::from))
                 .build();
     }
 
@@ -104,18 +121,20 @@ public final class ConditionKeyDefinition implements ToNode, ToSmithyBuilder<Con
         ConditionKeyDefinition that = (ConditionKeyDefinition) o;
         return Objects.equals(type, that.type)
                && Objects.equals(documentation, that.documentation)
-               && Objects.equals(externalDocumentation, that.externalDocumentation);
+               && Objects.equals(externalDocumentation, that.externalDocumentation)
+               && Objects.equals(relativeDocumentation, that.relativeDocumentation);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(type, documentation, externalDocumentation);
+        return Objects.hash(type, documentation, externalDocumentation, relativeDocumentation);
     }
 
     public static final class Builder implements SmithyBuilder<ConditionKeyDefinition> {
         private String type;
         private String documentation;
         private String externalDocumentation;
+        private String relativeDocumentation;
 
         @Override
         public ConditionKeyDefinition build() {
@@ -136,5 +155,10 @@ public final class ConditionKeyDefinition implements ToNode, ToSmithyBuilder<Con
             this.externalDocumentation = externalDocumentation;
             return this;
         }
+
+        public Builder relativeDocumentation(String relativeDocumentation) {
+            this.relativeDocumentation = relativeDocumentation;
+            return this;
+        }
     }
 }
diff --git a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java
index baed36536..2e3bed2fb 100644
--- a/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java
+++ b/smithy-aws-iam-traits/src/main/java/software/amazon/smithy/aws/iam/traits/IamResourceTrait.java
@@ -33,10 +33,12 @@ public final class IamResourceTrait extends AbstractTrait
     public static final ShapeId ID = ShapeId.from("aws.iam#iamResource");
 
     public final String name;
+    public final String relativeDocumentation;
 
     private IamResourceTrait(Builder builder) {
         super(ID, builder.getSourceLocation());
         name = builder.name;
+        relativeDocumentation = builder.relativeDocumentation;
     }
 
     /**
@@ -48,6 +50,16 @@ public final class IamResourceTrait extends AbstractTrait
         return Optional.of(name);
     }
 
+    /**
+     * Get the relative URL path that defines more information about the resource
+     * within a set of IAM-related documentation.
+     *
+     * @return A relative URL to the documentation page.
+     */
+    public Optional<String> getRelativeDocumentation() {
+        return Optional.ofNullable(relativeDocumentation);
+    }
+
     public static Builder builder() {
         return new Builder();
     }
@@ -80,6 +92,7 @@ public final class IamResourceTrait extends AbstractTrait
 
     public static final class Builder extends AbstractTraitBuilder<IamResourceTrait, Builder> {
         private String name;
+        private String relativeDocumentation;
 
         private Builder() {}
 
@@ -92,5 +105,10 @@ public final class IamResourceTrait extends AbstractTrait
             this.name = name;
             return this;
         }
+
+        public Builder relativeDocumentation(String relativeDocumentation) {
+            this.relativeDocumentation = relativeDocumentation;
+            return this;
+        }
     }
 }
diff --git a/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy b/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy
index 3b5f585d8..ae2ca3b5e 100644
--- a/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy
+++ b/smithy-aws-iam-traits/src/main/resources/META-INF/smithy/aws.iam.smithy
@@ -39,6 +39,10 @@ structure disableConditionKeyInference {}
 structure iamResource {
     /// The name of the resource in AWS IAM.
     name: String
+
+    ///  A relative URL path that defines more information about the resource
+    ///  within a set of IAM-related documentation.
+    relativeDocumentation: String
 }
 
 /// Other actions that the invoker must be authorized to perform when executing the targeted operation.
@@ -63,14 +67,18 @@ list supportedPrincipalTypes {
 /// A defined condition key to appear within a service in addition to inferred and global condition keys.
 @private
 structure ConditionKeyDefinition {
+    @required
+    type: ConditionKeyType
+
     /// Defines documentation about the condition key.
     documentation: String
 
     /// A valid URL that defines more information about the condition key.
     externalDocumentation: String
 
-    @required
-    type: ConditionKeyType
+    ///  A relative URL path that defines more information about the condition key
+    ///  within a set of IAM-related documentation.
+    relativeDocumentation: String
 }
 
 /// The IAM policy type of the value that will supplied for this context key
diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/PluginContext.java b/smithy-build/src/main/java/software/amazon/smithy/build/PluginContext.java
index a416f7c7c..f2408bd58 100644
--- a/smithy-build/src/main/java/software/amazon/smithy/build/PluginContext.java
+++ b/smithy-build/src/main/java/software/amazon/smithy/build/PluginContext.java
@@ -254,16 +254,17 @@ public final class PluginContext implements ToSmithyBuilder<PluginContext> {
 
     @Override
     public Builder toBuilder() {
-        return builder()
-                .projection(projectionName, projection)
+        Builder builder =  builder()
                 .model(model)
-                .originalModel(originalModel)
                 .events(events)
                 .settings(settings)
                 .fileManifest(fileManifest)
                 .pluginClassLoader(pluginClassLoader)
                 .sources(sources)
                 .artifactName(artifactName);
+        getProjection().ifPresent(config -> builder.projection(projectionName, config));
+        getOriginalModel().ifPresent(builder::originalModel);
+        return builder;
     }
 
     /**


Test patch:
diff --git a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java
index 9b1d6cfb6..e47054780 100644
--- a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java
+++ b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/ConditionKeysIndexTest.java
@@ -53,6 +53,9 @@ public class ConditionKeysIndexTest {
         assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#GetResource2")), is(empty()));
 
         // Defined context keys are assembled from the names and mapped with the definitions.
+        assertEquals(index.getDefinedConditionKeys(service).get("foo:baz").getDocumentation().get(), "Foo baz");
+        assertEquals(index.getDefinedConditionKeys(service).get("foo:baz").getRelativeDocumentation().get(),
+            "condition-keys.html");
         assertThat(index.getDefinedConditionKeys(service).get("myservice:Resource1Id1").getDocumentation(),
                 not(Optional.empty()));
         assertEquals(index.getDefinedConditionKeys(service).get("myservice:ResourceTwoId2").getDocumentation().get(),
diff --git a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java
index 96556b185..eb7bc1837 100644
--- a/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java
+++ b/smithy-aws-iam-traits/src/test/java/software/amazon/smithy/aws/iam/traits/IamResourceTraitTest.java
@@ -36,5 +36,6 @@ public class IamResourceTraitTest {
 
         assertTrue(superResource.hasTrait(IamResourceTrait.class));
         assertEquals(superResource.expectTrait(IamResourceTrait.class).getName().get(), "super");
+        assertEquals(superResource.expectTrait(IamResourceTrait.class).getRelativeDocumentation().get(), "API-Super.html");
     }
 }
diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy
index 76bfb1e2d..55fc02b9a 100644
--- a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy
+++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/iam-resource.smithy
@@ -10,7 +10,10 @@ service MyService {
     resources: [SuperResource]
 }
 
-@aws.iam#iamResource(name: "super")
+@aws.iam#iamResource(
+    name: "super",
+    relativeDocumentation: "API-Super.html"
+)
 @arn(template: "super/{id1}")
 resource SuperResource {
     identifiers: {
diff --git a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy
index 62afdc379..a7c0999c0 100644
--- a/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy
+++ b/smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/successful-condition-keys.smithy
@@ -2,7 +2,13 @@ $version: "1.0"
 namespace smithy.example
 
 @aws.api#service(sdkId: "My")
-@aws.iam#defineConditionKeys("foo:baz": {type: "String", documentation: "Foo baz"})
+@aws.iam#defineConditionKeys(
+  "foo:baz": {
+    type: "String",
+    documentation: "Foo baz",
+    relativeDocumentation: "condition-keys.html"
+  }
+)
 service MyService {
   version: "2019-02-20",
   operations: [Operation1],
diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/PluginContextTest.java b/smithy-build/src/test/java/software/amazon/smithy/build/PluginContextTest.java
index aa26b499e..f70e7724b 100644
--- a/smithy-build/src/test/java/software/amazon/smithy/build/PluginContextTest.java
+++ b/smithy-build/src/test/java/software/amazon/smithy/build/PluginContextTest.java
@@ -82,4 +82,16 @@ public class PluginContextTest {
         assertThat(context.getSources(), equalTo(context2.getSources()));
         assertThat(context.getEvents(), equalTo(context2.getEvents()));
     }
+
+    @Test
+    public void roundTripsDefaults() {
+        PluginContext before = PluginContext.builder()
+                .model(Model.builder().build())
+                .fileManifest(new MockManifest())
+                .build();
+        PluginContext after = before.toBuilder().build();
+        assertThat(before.getModel(), equalTo(after.getModel()));
+        assertThat(before.getFileManifest(), equalTo(after.getFileManifest()));
+        assertThat(before.getProjectionName(), equalTo(after.getProjectionName()));
+    }
 }