Skip to content

Commit

Permalink
Add support for smithy-modeled endpoint resolution. (#441)
Browse files Browse the repository at this point in the history
  • Loading branch information
isaiahvita committed Jul 31, 2023
1 parent 7f8b3b9 commit fc41a89
Show file tree
Hide file tree
Showing 35 changed files with 3,304 additions and 75 deletions.
8 changes: 0 additions & 8 deletions .changelog/1a8aa15f7f0649e3ba084e5ab48a6345.json

This file was deleted.

8 changes: 0 additions & 8 deletions .changelog/935d5acf9da941a2b1b8b326494e5c33.json

This file was deleted.

9 changes: 9 additions & 0 deletions .changelog/f0db0775153a40d38e770b582eaef5b5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "f0db0775-153a-40d3-8e77-0b582eaef5b5",
"type": "feature",
"collapse": true,
"description": "Adds support for smithy-modeled endpoint resolution.",
"modules": [
"."
]
}
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main, ep20 ]
branches: [ main ]

jobs:
unit-tests:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,19 @@ final class CodegenVisitor extends ShapeVisitor.Default<Void> {
var modelTransformer = ModelTransformer.create();

/*
smithy 1.23.0 added support for mixins. This transform flattens and applies the mixins
and remove them from the model
*/
* smithy 1.23.0 added support for mixins. This transform flattens and applies
* the mixins
* and remove them from the model
*/
resolvedModel = modelTransformer.flattenAndRemoveMixins(resolvedModel);

// Add unique operation input/output shapes
resolvedModel = AddOperationShapes.execute(resolvedModel, settings.getService());

/*
smithy 1.12.0 added support for binding common errors to the service shape
this transform copies these common errors to the operations
*/
* smithy 1.12.0 added support for binding common errors to the service shape
* this transform copies these common errors to the operations
*/
resolvedModel = modelTransformer.copyServiceErrorsToOperations(resolvedModel,
settings.getService(resolvedModel));

Expand Down Expand Up @@ -154,8 +155,7 @@ private static ProtocolGenerator resolveProtocolGenerator(
Collection<GoIntegration> integrations,
Model model,
ServiceShape service,
GoSettings settings
) {
GoSettings settings) {
// Collect all the supported protocol generators.
Map<ShapeId, ProtocolGenerator> generators = new HashMap<>();
for (GoIntegration integration : integrations) {
Expand Down Expand Up @@ -222,7 +222,7 @@ void execute() {
.delegator(writers);

LOGGER.info("Generating serde for protocol " + protocolGenerator.getProtocol()
+ " on " + service.getId());
+ " on " + service.getId());
writers.useFileWriter("serializers.go", settings.getModuleName(), writer -> {
ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build();
protocolGenerator.generateRequestSerializers(context);
Expand All @@ -242,8 +242,18 @@ void execute() {
});
}

writers.useFileWriter("endpoints.go", settings.getModuleName(), writer -> {
ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build();
protocolGenerator.generateEndpointResolution(context);
});

writers.useFileWriter("endpoints_test.go", settings.getModuleName(), writer -> {
ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build();
protocolGenerator.generateEndpointResolutionTests(context);
});

LOGGER.info("Generating protocol " + protocolGenerator.getProtocol()
+ " unit tests for " + service.getId());
+ " unit tests for " + service.getId());
writers.useFileWriter("protocol_test.go", settings.getModuleName(), writer -> {
protocolGenerator.generateProtocolTests(contextBuilder.writer(writer).build());
});
Expand All @@ -256,9 +266,9 @@ void execute() {
writers.flushWriters();

GoModuleInfo goModuleInfo = new GoModuleInfo.Builder()
.goDirective(settings.getGoDirective())
.dependencies(dependencies)
.build();
.goDirective(settings.getGoDirective())
.dependencies(dependencies)
.build();

GoModGenerator.writeGoMod(settings, fileManifest, goModuleInfo);

Expand Down Expand Up @@ -320,7 +330,8 @@ public Void serviceShape(ServiceShape shape) {
new ServiceGenerator(settings, model, symbolProvider, serviceWriter, shape, integrations,
runtimePlugins, applicationProtocol).run();

// Generate each operation for the service. We do this here instead of via the operation visitor method to
// Generate each operation for the service. We do this here instead of via the
// operation visitor method to
// limit it to the operations bound to the service.
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class);
Set<OperationShape> containedOperations = new TreeSet<>(topDownIndex.getContainedOperations(service));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -100,6 +101,26 @@ private void init() {
// TODO figure out better way to annotate where the failure occurs, check templates and args
// TODO to try to find programming bugs.

/**
* Joins multiple writables together within a single writable without newlines between writables in the list.
*
* @param writables list of writables to join
* @param separator separator between writables
* @return new writable
*/
public static Writable joinWritables(List<Writable> writables, String separator) {
return (GoWriter w) -> {
for (int i = 0; i < writables.size(); i++) {
var writable = writables.get(i);
var sep = separator;
if (i == writables.size() - 1) {
sep = "";
}
w.writeInline("$W" + sep, writable);
}
};
}

/**
* Returns a Writable for the string and args to be composed inline to another writer's contents.
*
Expand All @@ -115,6 +136,18 @@ public static Writable goTemplate(String contents, Map<String, Object>... args)
};
}

public static Writable goDocTemplate(String contents) {
return goDocTemplate(contents, new HashMap<>());
}

@SafeVarargs
public static Writable goDocTemplate(String contents, Map<String, Object>... args) {
validateTemplateArgsNotNull(args);
return (GoWriter w) -> {
w.writeGoDocTemplate(contents, args);
};
}

/**
* Returns a Writable that can later be invoked to write the contents as template
* as a code block instead of single content of text.
Expand Down Expand Up @@ -289,6 +322,11 @@ public final void writeGoTemplate(String contents, Map<String, Object>... args)
});
}

@SafeVarargs
public final void writeGoDocTemplate(String contents, Map<String, Object>... args) {
writeRenderedDocs(goTemplate(contents, args));
}

/**
* Writes the contents as template as a code block instead of single content fo text.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,18 +206,21 @@ private void generateConfig() {
});
});

getAllConfigFields().stream().filter(ConfigField::getWithHelper)
getAllConfigFields().stream().filter(ConfigField::getWithHelper).filter(ConfigField::isDeprecated)
.forEach(configField -> {
writer.writeDocs(configField.getDeprecated().get());
writeWithHelperFunction(writer, configField);
});

getAllConfigFields().stream().filter(ConfigField::getWithHelper).filter(
Predicate.not(ConfigField::isDeprecated))
.forEach(configField -> {
writer.writeDocs(
String.format("With%s returns a functional option for setting the Client's %s option.",
String.format(
"With%s returns a functional option for setting the Client's %s option.",
configField.getName(), configField.getName()));
writer.openBlock("func With$L(v $P) func(*Options) {", "}", configField.getName(),
configField.getType(),
() -> {
writer.openBlock("return func(o *Options) {", "}", () -> {
writer.write("o.$L = v", configField.getName());
});
}).write("");
writeWithHelperFunction(writer, configField);

});

generateApplicationProtocolTypes();
Expand All @@ -233,6 +236,16 @@ private void generateConfig() {
});
}

private void writeWithHelperFunction(GoWriter writer, ConfigField configField) {
writer.openBlock("func With$L(v $P) func(*Options) {", "}", configField.getName(),
configField.getType(),
() -> {
writer.openBlock("return func(o *Options) {", "}", () -> {
writer.write("o.$L = v", configField.getName());
});
}).write("");
}

private List<ConfigField> getAllConfigFields() {
List<ConfigField> configFields = new ArrayList<>();
for (RuntimeClientPlugin runtimeClientPlugin : runtimePlugins) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ public final class SmithyGoDependency {
public static final GoDependency XML = stdlib("encoding/xml");
public static final GoDependency SYNC = stdlib("sync");
public static final GoDependency PATH = stdlib("path");
public static final GoDependency LOG = stdlib("log");

public static final GoDependency SMITHY = smithy(null, "smithy");
public static final GoDependency SMITHY_TRANSPORT = smithy("transport", "smithytransport");
public static final GoDependency SMITHY_HTTP_TRANSPORT = smithy("transport/http", "smithyhttp");
public static final GoDependency SMITHY_MIDDLEWARE = smithy("middleware");
public static final GoDependency SMITHY_TIME = smithy("time", "smithytime");
Expand All @@ -61,6 +63,8 @@ public final class SmithyGoDependency {
public static final GoDependency SMITHY_DOCUMENT_JSON = smithy("document/json", "smithydocumentjson");
public static final GoDependency SMITHY_SYNC = smithy("sync", "smithysync");
public static final GoDependency SMITHY_AUTH_BEARER = smithy("auth/bearer");
public static final GoDependency SMITHY_ENDPOINTS = smithy("endpoints", "smithyendpoints");
public static final GoDependency SMITHY_ENDPOINT_RULESFN = smithy("endpoints/private/rulesfn");

public static final GoDependency GO_CMP = goCmp("cmp");
public static final GoDependency GO_CMP_OPTIONS = goCmp("cmp/cmpopts");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,18 @@ public static boolean isUniverseType(Symbol symbol) {
return symbol.getProperty(SymbolUtils.GO_UNIVERSE_TYPE, Boolean.class)
.orElse(false);
}

public static Symbol.Builder getPackageSymbol(
String importPath, String symbolName, String namespaceAlias, boolean pointable
) {
Symbol.Builder builder;
if (pointable) {
builder = SymbolUtils.createPointableSymbolBuilder(symbolName);
} else {
builder = SymbolUtils.createValueSymbolBuilder(symbolName);
}

// TODO this doesn't seem right
return builder.namespace(importPath, "/").putProperty(SymbolUtils.NAMESPACE_ALIAS, namespaceAlias);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.smithy.go.codegen.endpoints;

import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters;

/**
* Used by integrations to provide BuiltIn value resolution
* functionality during endpoint resolution.
*
*/
public interface EndpointBuiltInHandler {

/**
* Used by integrations to set a member on the endpoints resolution
* middleware object.
*
* @param writer Settings used to generate.
*/
default void renderEndpointBuiltInField(GoWriter writer) {
// pass
}

/**
* Used by integrations to set invoke BuiltIn resolution during Endpoint
* resolution.
*
* @param writer Settings used to generate.
*/
default void renderEndpointBuiltInInvocation(GoWriter writer) {
// pass
}


/**
* Used by integrations to set initialize BuiltIn values on the Endpoint
* resolution object.
*
* @param writer Settings used to generate.
*/
default void renderEndpointBuiltInInitialization(GoWriter writer, Parameters parameters) {
// pass
}
}
Loading

0 comments on commit fc41a89

Please sign in to comment.