Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transform to mark required idempotency tokens client optional #2466

Merged
merged 10 commits into from
Nov 15, 2024
25 changes: 25 additions & 0 deletions docs/source-2.0/guides/smithy-build-json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,31 @@ applied.
use an :ref:`enum shape <enum>` instead to avoid needing to use this
transform.

.. _makeIdempotencyTokensClientOptional:

makeIdempotencyTokensClientOptional
-----------------------------------

Makes required Idempotency token members :ref:`@clientOptional <clientOptional-trait>`.

Idempotency tokens that are required should fail validation, but shouldn't be required to create a type.
This allows a default value to get injected when missing.

.. code-block:: json

{
"version": "1.0",
"projections": {
"exampleProjection": {
"transforms": [
{
"name": "makeIdempotencyTokensClientOptional"
}
]
}
}
}

.. _changeTypes:

changeTypes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.build.transforms;

import software.amazon.smithy.build.ProjectionTransformer;
import software.amazon.smithy.build.TransformContext;
import software.amazon.smithy.model.Model;

/**
* {@code makeIdempotencyTokensClientOptional} makes {@code @idempotencyToken} fields {@code @clientOptional}.
*/
public final class MakeIdempotencyTokensClientOptional implements ProjectionTransformer {

@Override
public String getName() {
return "makeIdempotencyTokensClientOptional";
}

@Override
public Model transform(TransformContext context) {
Model model = context.getModel();
return context.getTransformer().makeIdempotencyTokensClientOptional(model);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ software.amazon.smithy.build.transforms.IncludeShapesByTag
software.amazon.smithy.build.transforms.IncludeTags
software.amazon.smithy.build.transforms.IncludeTraits
software.amazon.smithy.build.transforms.IncludeTraitsByTag
software.amazon.smithy.build.transforms.MakeIdempotencyTokensClientOptional
software.amazon.smithy.build.transforms.RemoveDeprecatedShapes
software.amazon.smithy.build.transforms.RemoveTraitDefinitions
software.amazon.smithy.build.transforms.RemoveUnusedShapes
software.amazon.smithy.build.transforms.RenameShapes
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,18 @@ public void removeShapesDeprecatedBeforeVersion(String relativeVersion) {
});
}

/**
* Makes {@code idempotencyToken} fields {@code clientOptional}.
*
* @see ModelTransformer#makeIdempotencyTokensClientOptional(Model)
*/
public void makeIdempotencyTokensClientOptional() {
transforms.add((model, transformer) -> {
LOGGER.finest("Making `@idempotencyToken` fields `@clientOptional`");
return transformer.makeIdempotencyTokensClientOptional(model);
});
}

/**
* Changes each compatible string shape with the enum trait to an enum shape.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.model.transform;

import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.ClientOptionalTrait;
import software.amazon.smithy.model.traits.IdempotencyTokenTrait;
import software.amazon.smithy.model.traits.RequiredTrait;

/**
* Makes {@code idempotencyToken} members {@code clientOptional}, so they can be injected if missing.
*/
final class MakeIdempotencyTokenClientOptional {
private MakeIdempotencyTokenClientOptional() {}

public static Model transform(Model model) {
return ModelTransformer.create().mapShapes(model, shape -> {
if (shape.isMemberShape()
&& shape.hasTrait(RequiredTrait.class)
&& shape.hasTrait(IdempotencyTokenTrait.class)
&& !shape.hasTrait(ClientOptionalTrait.class)) {
return Shape.shapeToBuilder(shape).addTrait(new ClientOptionalTrait()).build();
}
return shape;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -720,4 +720,17 @@ public Model filterDeprecatedRelativeDate(Model model, String relativeDate) {
public Model filterDeprecatedRelativeVersion(Model model, String relativeVersion) {
return new FilterDeprecatedRelativeVersion(relativeVersion).transform(this, model);
}

/**
* Makes any {@code @idempotencyToken fields {@code @clientOptional} so that missing tokens can be injected.
*
* <p>Idempotency tokens that are required should fail validation, but shouldn't be required to create a type,
* allowing for a default value to be injected when missing.
*
* @param model Model to transform.
* @return Returns the transformed model.
*/
public Model makeIdempotencyTokensClientOptional(Model model) {
return MakeIdempotencyTokenClientOptional.transform(model);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.model.transform;

import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.ClientOptionalTrait;
import software.amazon.smithy.model.traits.IdempotencyTokenTrait;
import software.amazon.smithy.model.traits.RequiredTrait;

public class MakeIdempotencyTokenClientOptionalTest {
private static final ShapeId operationInput = ShapeId.from("smithy.example#IdempotencyTokenRequiredInput");

@Test
void compareTransform() {
Model before = Model.assembler()
.addImport(FlattenPaginationInfoTest.class.getResource("idempotency-token.smithy"))
.assemble()
.unwrap();
Model result = ModelTransformer.create().makeIdempotencyTokensClientOptional(before);

Shape input = result.expectShape(operationInput);
Shape member = result.expectShape(input.getMember("token").get().getId());

assertTrue(member.hasTrait(ClientOptionalTrait.class));
assertTrue(member.hasTrait(RequiredTrait.class));
assertTrue(member.hasTrait(IdempotencyTokenTrait.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
$version: "2.0"

namespace smithy.example

operation IdempotencyTokenRequired {
input := {
@idempotencyToken
@required
token: String
}
}
Loading