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

chore(codegen): move s3 transform to Java code #5963

Merged
merged 2 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion clients/client-s3/src/models/models_0.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// smithy-typescript generated code
import { ExceptionOptionType as __ExceptionOptionType, SENSITIVE_STRING } from "@smithy/smithy-client";

import { StreamingBlobTypes } from "@smithy/types";

import { S3ServiceException as __BaseException } from "./S3ServiceException";
Expand Down
2 changes: 0 additions & 2 deletions clients/client-s3/src/models/models_1.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// smithy-typescript generated code
import { ExceptionOptionType as __ExceptionOptionType, SENSITIVE_STRING } from "@smithy/smithy-client";

import { StreamingBlobTypes } from "@smithy/types";

import {
Expand Down Expand Up @@ -28,7 +27,6 @@ import {
StorageClass,
Tag,
} from "./models_0";

import { S3ServiceException as __BaseException } from "./S3ServiceException";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.TimestampShape;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.HttpHeaderTrait;
import software.amazon.smithy.typescript.codegen.LanguageTarget;
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
Expand Down Expand Up @@ -106,6 +111,91 @@ public Model preprocessModel(Model model, TypeScriptSettings settings) {
});
}
LOGGER.info("Patching " + inputShapes.size() + " input shapes with CRT notification");

boolean expiresShapeIsPresent = model.getShape(ShapeId.from("com.amazonaws.s3#Expires")).isPresent();
if (expiresShapeIsPresent) {
// ExpiresString customization part 1:
// enforce that "com.amazonaws.s3#Expires" retains type=timestamp.
// add a shape "com.amazonaws.s3#ExpiresString" of type=string.
Shape expiresShape = model.getShape(ShapeId.from("com.amazonaws.s3#Expires")).get();
TimestampShape expiresTimestampShape = TimestampShape.builder()
.id(expiresShape.getId())
.build();
StringShape expiresStringShape = StringShape.builder()
.id("com.amazonaws.s3#ExpiresString")
.build();
modelBuilder
.removeShape(expiresShape.getId())
.addShapes(expiresTimestampShape, expiresStringShape);

// ExpiresString customization part 2:
// for any output shape member targeting Expires, add a member ExpiresString targeting ExpiresString.
// and mark Expires deprecated in favor of ExpiresString.
// move Expires documentation trait to ExpiresString.
// set the httpHeader trait of ExpiresString to be ExpiresString.
// SDK middleware will take care of copying expires header to expiresstring header prior to deserialization.
for (OperationShape operationShape : topDownIndex.getContainedOperations(serviceShape)) {
if (operationShape.getOutput().isEmpty()) {
continue;
}
StructureShape structureShape = model.expectShape(
operationShape.getOutputShape(), StructureShape.class
);

Set<Map.Entry<String, MemberShape>> memberEntries = structureShape
.getAllMembers()
.entrySet();
StructureShape.Builder structureShapeBuilder = structureShape.toBuilder();

boolean isTargetingExpires = structureShape
.getAllMembers()
.values()
.stream()
.anyMatch(memberShape -> memberShape.getTarget().equals(expiresShape.getId()));

if (isTargetingExpires) {
for (Map.Entry<String, MemberShape> entry : memberEntries) {
String memberName = entry.getKey();
MemberShape memberShape = entry.getValue();

if (memberShape.getTarget().equals(expiresShape.getId())) {
structureShapeBuilder
.removeMember(memberName)
.addMember(
memberName,
expiresTimestampShape.getId(),
(m) -> {
m
.addTrait(new DocumentationTrait("Deprecated in favor of ExpiresString."))
.addTrait(memberShape.getTrait(HttpHeaderTrait.class).get())
.addTrait(DeprecatedTrait.builder().build());
}
)
.addMember(
"ExpiresString",
expiresStringShape.getId(),
(m) -> {
m
.addTrait(memberShape.getTrait(DocumentationTrait.class).get())
.addTrait(new HttpHeaderTrait("ExpiresString"));
}
);
} else {
// This is done to preserve the member order
// and insert ExpiresString adjacent to Expires.
structureShapeBuilder
.removeMember(memberName)
.addMember(memberName, memberShape.getTarget(), (m) -> {
m.addTraits(memberShape.getAllTraits().values());
});
}
}
modelBuilder
.addShape(structureShapeBuilder.build());
}
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code should initially have been in Java anyway. However, at the time I couldn't figure out how to mutate the model, and wrote it in JS.

It turns out the .addX() methods on the various builders generally do a replace on a map collection, and it was easier to figure out this time.

return modelBuilder.addShapes(inputShapes).build();
}

Expand Down
47 changes: 0 additions & 47 deletions scripts/generate-clients/s3-hack.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,53 +23,6 @@ module.exports = function () {
namespace: "com.amazonaws.s3",
});

const expiresShape = s3ModelObject.shapes["com.amazonaws.s3#Expires"];
if (expiresShape) {
// enforce that Expires retains type timestamp.
expiresShape.type = "timestamp";

// add the ExpiresString string shape.
const newShapes = {};
for (const [shapeId, shape] of Object.entries(s3ModelObject.shapes)) {
newShapes[shapeId] = shape;
if (shapeId === "com.amazonaws.s3#Expires") {
newShapes["com.amazonaws.s3#ExpiresString"] = {
type: "string",
};
}
}
s3ModelObject.shapes = newShapes;

// add ExpiresString alongside output shapes containing Expires.
for (const [shapeId, shape] of Object.entries(s3ModelObject.shapes)) {
if (shape?.traits?.["smithy.api#output"]) {
const newMembers = {};
for (const [memberName, member] of Object.entries(shape.members)) {
newMembers[memberName] = member;
if (member.target === "com.amazonaws.s3#Expires") {
const existingDoc = member.traits["smithy.api#documentation"];
if (!member.traits) {
member.traits = {};
}

newMembers.ExpiresString = {
target: "com.amazonaws.s3#ExpiresString",
traits: {
...member.traits,
"smithy.api#httpHeader": "ExpiresString",
"smithy.api#documentation": existingDoc,
},
};

member.traits["smithy.api#deprecated"] = {};
member.traits["smithy.api#documentation"] = "Deprecated in favor of ExpiresString.";
}
}
shape.members = newMembers;
}
}
}

fs.writeFileSync(s3ModelLocation, JSON.stringify(s3ModelObject, null, 2));

return () => {
Expand Down
Loading