Skip to content

Support generating non-AWS client #2222

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

Merged
merged 7 commits into from
Apr 15, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isSigV4Service;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;

Expand Down Expand Up @@ -44,6 +45,7 @@
/**
* Configure clients with AWS auth configurations and plugin.
*/
// TODO: Think about AWS Auth supported for only some operations and not all, when not AWS service, with say @auth([])
Copy link
Member

Choose a reason for hiding this comment

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

If the service has an auth trait you need to add the mechanics needed to support that regardless of what the default is (where @auth([]) is no auth by default). Since the auth trait can only reference auth traits on the service, you don't have to worry about a single operation introducing its own auth. I'm pretty sure the current setup already handles operations with disabled auth.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I put this TODO to think more deeply about different cases when I handle supporting SigV4 for non-AWS clients.

public final class AddAwsAuthPlugin implements TypeScriptIntegration {

@Override
Expand All @@ -54,6 +56,9 @@ public void addConfigInterfaceFields(
TypeScriptWriter writer
) {
ServiceShape service = settings.getService(model);
if (!isSigV4Service(service)) {
return;
}
if (!areAllOptionalAuthOperations(model, service)) {
writer.addImport("Credentials", "__Credentials", TypeScriptDependency.AWS_SDK_TYPES.packageName);
writer.writeDocs("Default credentials provider; Not available in browser runtime.")
Expand All @@ -66,13 +71,13 @@ public List<RuntimeClientPlugin> getClientPlugins() {
return ListUtils.of(
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_CONFIG)
.servicePredicate((m, s) -> !areAllOptionalAuthOperations(m, s))
.servicePredicate((m, s) -> isSigV4Service(s) && !areAllOptionalAuthOperations(m, s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_MIDDLEWARE)
// See operationUsesAwsAuth() below for AwsAuth Middleware customizations.
.servicePredicate(
(m, s) -> !testServiceId(s, "STS") && !hasOptionalAuthOperation(m, s)
(m, s) -> !testServiceId(s, "STS") && isSigV4Service(s) && !hasOptionalAuthOperation(m, s)
).build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_SIGNING.dependency, "AwsAuth", HAS_MIDDLEWARE)
Expand All @@ -89,7 +94,7 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
LanguageTarget target
) {
ServiceShape service = settings.getService(model);
if (areAllOptionalAuthOperations(model, service)) {
if (!isSigV4Service(service) || areAllOptionalAuthOperations(model, service)) {
return Collections.emptyMap();
}
switch (target) {
Expand Down Expand Up @@ -130,8 +135,8 @@ private static boolean operationUsesAwsAuth(Model model, ServiceShape service, O
}

// optionalAuth trait doesn't require authentication.
if (hasOptionalAuthOperation(model, service)) {
return !operation.getTrait(OptionalAuthTrait.class).isPresent();
if (isSigV4Service(service) && hasOptionalAuthOperation(model, service)) {
return !operation.hasTrait(OptionalAuthTrait.class);
}
return false;
}
Expand All @@ -140,7 +145,7 @@ private static boolean hasOptionalAuthOperation(Model model, ServiceShape servic
TopDownIndex topDownIndex = TopDownIndex.of(model);
Set<OperationShape> operations = topDownIndex.getContainedOperations(service);
for (OperationShape operation : operations) {
if (operation.getTrait(OptionalAuthTrait.class).isPresent()) {
if (operation.hasTrait(OptionalAuthTrait.class)) {
return true;
}
}
Expand All @@ -151,7 +156,7 @@ private static boolean areAllOptionalAuthOperations(Model model, ServiceShape se
TopDownIndex topDownIndex = TopDownIndex.of(model);
Set<OperationShape> operations = topDownIndex.getContainedOperations(service);
for (OperationShape operation : operations) {
if (!operation.getTrait(OptionalAuthTrait.class).isPresent()) {
if (!operation.hasTrait(OptionalAuthTrait.class)) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;
import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isSigV4Service;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -31,6 +34,9 @@
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
import software.amazon.smithy.utils.MapUtils;

// TODO: This javadoc is specific to needs of AWS client. However it has elements that would be needed by non-AWS
// clients too, like logger, region for SigV4. We should refactor these into different Integration or rename this
// class to be generic.
/**
* AWS clients need to know the service name for collecting metrics, the
* region name used to resolve endpoints, the max attempt to retry a request
Expand Down Expand Up @@ -82,10 +88,14 @@ public void addConfigInterfaceFields(
writer.addImport("Provider", "__Provider", TypeScriptDependency.AWS_SDK_TYPES.packageName);
writer.addImport("Logger", "__Logger", TypeScriptDependency.AWS_SDK_TYPES.packageName);

writer.writeDocs("Unique service identifier.\n@internal")
.write("serviceId?: string;\n");
writer.writeDocs("The AWS region to which this client will send requests")
.write("region?: string | __Provider<string>;\n");
if (isAwsService(settings, model)) {
writer.writeDocs("Unique service identifier.\n@internal")
.write("serviceId?: string;\n");
}
if (isSigV4Service(settings, model)) {
writer.writeDocs("The AWS region to which this client will send requests or use as signingRegion")
.write("region?: string | __Provider<string>;\n");
}
writer.writeDocs("Value for how many times a request will be made at most in case of retry.")
.write("maxAttempts?: number | __Provider<number>;\n");
writer.writeDocs("Optional logger for logging debug/info/warn/error.")
Expand Down Expand Up @@ -114,11 +124,17 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
+ "trait was found on " + service.getId());
}
}
runtimeConfigs.putAll(getDefaultConfig(target));
runtimeConfigs.putAll(getDefaultConfig(target, settings, model));
return runtimeConfigs;
}

private Map<String, Consumer<TypeScriptWriter>> getDefaultConfig(LanguageTarget target) {
private Map<String, Consumer<TypeScriptWriter>> getDefaultConfig(
LanguageTarget target,
TypeScriptSettings settings,
Model model
) {
Map<String, Consumer<TypeScriptWriter>> defaultConfigs = new HashMap();
boolean isSigV4Service = isSigV4Service(settings, model);
switch (target) {
case SHARED:
return MapUtils.of(
Expand All @@ -128,40 +144,46 @@ private Map<String, Consumer<TypeScriptWriter>> getDefaultConfig(LanguageTarget
}
);
case BROWSER:
return MapUtils.of(
"region", writer -> {
writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY);
writer.addImport("invalidProvider", "invalidProvider",
TypeScriptDependency.INVALID_DEPENDENCY.packageName);
writer.write("region: invalidProvider(\"Region is missing\"),");
},
"maxAttempts", writer -> {
writer.addDependency(TypeScriptDependency.MIDDLEWARE_RETRY);
writer.addImport("DEFAULT_MAX_ATTEMPTS", "DEFAULT_MAX_ATTEMPTS",
TypeScriptDependency.MIDDLEWARE_RETRY.packageName);
writer.write("maxAttempts: DEFAULT_MAX_ATTEMPTS,");
}
);
if (isSigV4Service) {
defaultConfigs.put("region", writer -> {
writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY);
writer.addImport("invalidProvider", "invalidProvider",
TypeScriptDependency.INVALID_DEPENDENCY.packageName);
writer.write("region: invalidProvider(\"Region is missing\"),");
});
}
defaultConfigs.put("maxAttempts", writer -> {
writer.addDependency(TypeScriptDependency.MIDDLEWARE_RETRY);
writer.addImport("DEFAULT_MAX_ATTEMPTS", "DEFAULT_MAX_ATTEMPTS",
TypeScriptDependency.MIDDLEWARE_RETRY.packageName);
writer.write("maxAttempts: DEFAULT_MAX_ATTEMPTS,");
});
return defaultConfigs;
case NODE:
return MapUtils.of(
"region", writer -> {
writer.addDependency(AwsDependency.NODE_CONFIG_PROVIDER);
writer.addImport("loadConfig", "loadNodeConfig",
AwsDependency.NODE_CONFIG_PROVIDER.packageName);
writer.addDependency(TypeScriptDependency.CONFIG_RESOLVER);
writer.addImport("NODE_REGION_CONFIG_OPTIONS", "NODE_REGION_CONFIG_OPTIONS",
TypeScriptDependency.CONFIG_RESOLVER.packageName);
writer.addImport("NODE_REGION_CONFIG_FILE_OPTIONS", "NODE_REGION_CONFIG_FILE_OPTIONS",
TypeScriptDependency.CONFIG_RESOLVER.packageName);
writer.write(
if (isSigV4Service) {
// TODO: For non-AWS service, figure out how the region should be configured.
defaultConfigs.put("region", writer -> {
writer.addDependency(AwsDependency.NODE_CONFIG_PROVIDER);
writer.addImport("loadConfig", "loadNodeConfig",
AwsDependency.NODE_CONFIG_PROVIDER.packageName);
writer.addDependency(TypeScriptDependency.CONFIG_RESOLVER);
writer.addImport("NODE_REGION_CONFIG_OPTIONS", "NODE_REGION_CONFIG_OPTIONS",
TypeScriptDependency.CONFIG_RESOLVER.packageName);
writer.addImport("NODE_REGION_CONFIG_FILE_OPTIONS", "NODE_REGION_CONFIG_FILE_OPTIONS",
TypeScriptDependency.CONFIG_RESOLVER.packageName);
writer.write(
"region: loadNodeConfig(NODE_REGION_CONFIG_OPTIONS, NODE_REGION_CONFIG_FILE_OPTIONS),");
},
"maxAttempts", writer -> {
writer.addImport("NODE_MAX_ATTEMPT_CONFIG_OPTIONS", "NODE_MAX_ATTEMPT_CONFIG_OPTIONS",
TypeScriptDependency.MIDDLEWARE_RETRY.packageName);
writer.write("maxAttempts: loadNodeConfig(NODE_MAX_ATTEMPT_CONFIG_OPTIONS),");
}
);
});
}
defaultConfigs.put("maxAttempts", writer -> {
writer.addDependency(AwsDependency.NODE_CONFIG_PROVIDER);
writer.addImport("loadConfig", "loadNodeConfig",
AwsDependency.NODE_CONFIG_PROVIDER.packageName);
writer.addImport("NODE_MAX_ATTEMPT_CONFIG_OPTIONS", "NODE_MAX_ATTEMPT_CONFIG_OPTIONS",
TypeScriptDependency.MIDDLEWARE_RETRY.packageName);
writer.write("maxAttempts: loadNodeConfig(NODE_MAX_ATTEMPT_CONFIG_OPTIONS),");
});
return defaultConfigs;
default:
return Collections.emptyMap();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_CONFIG;
import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE;

Expand Down Expand Up @@ -44,6 +45,7 @@ public List<RuntimeClientPlugin> getClientPlugins() {
return ListUtils.of(
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Region", HAS_CONFIG)
.servicePredicate((m, s) -> isAwsService(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(TypeScriptDependency.CONFIG_RESOLVER.dependency, "Endpoints", HAS_CONFIG)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;

import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand All @@ -33,12 +35,15 @@
/**
* Add client plubins and configs to support injecting user agent.
*/
// TODO: Looks to add this back for non-AWS service clients, by fixing the dependency on ClientSharedValues.serviceId
Copy link
Member

Choose a reason for hiding this comment

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

should be fairly easy to just write out the service shape name / id. This is what Go does

public class AddUserAgentDependency implements TypeScriptIntegration {
@Override
public List<RuntimeClientPlugin> getClientPlugins() {
return ListUtils.of(
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.MIDDLEWARE_USER_AGENT.dependency, "UserAgent").build());
.withConventions(AwsDependency.MIDDLEWARE_USER_AGENT.dependency, "UserAgent")
.servicePredicate((m, s) -> isAwsService(s))
.build());
}

@Override
Expand All @@ -48,6 +53,9 @@ public void addConfigInterfaceFields(
SymbolProvider symbolProvider,
TypeScriptWriter writer
) {
if (!isAwsService(settings, model)) {
return;
}
writer.addImport("Provider", "Provider", TypeScriptDependency.AWS_SDK_TYPES.packageName);
writer.addImport("UserAgent", "__UserAgent", TypeScriptDependency.AWS_SDK_TYPES.packageName);
writer.writeDocs("The provider populating default tracking information to be sent with `user-agent`, "
Expand All @@ -62,6 +70,9 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
SymbolProvider symbolProvider,
LanguageTarget target
) {
if (!isAwsService(settings, model)) {
return Collections.emptyMap();
}
switch (target) {
case NODE:
return MapUtils.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;

import java.util.Collections;
import java.util.Map;
import java.util.function.BiConsumer;
Expand All @@ -39,7 +41,7 @@ public void writeAdditionalFiles(
SymbolProvider symbolProvider,
BiConsumer<String, Consumer<TypeScriptWriter>> writerFactory
) {
if (!settings.generateClient()) {
if (!settings.generateClient() || !isAwsService(settings, model)) {
return;
}

Expand All @@ -55,7 +57,7 @@ public void addConfigInterfaceFields(
SymbolProvider symbolProvider,
TypeScriptWriter writer
) {
if (!settings.generateClient()) {
if (!settings.generateClient() || !isAwsService(settings, model)) {
return;
}

Expand All @@ -71,7 +73,7 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
SymbolProvider symbolProvider,
LanguageTarget target
) {
if (!settings.generateClient()) {
if (!settings.generateClient() || !isAwsService(settings, model)) {
return Collections.emptyMap();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

package software.amazon.smithy.aws.typescript.codegen;

import static software.amazon.smithy.aws.typescript.codegen.AwsTraitsUtils.isAwsService;

import java.util.Arrays;
import java.util.Calendar;
import java.util.function.BiConsumer;
Expand Down Expand Up @@ -56,7 +58,8 @@ public void writeAdditionalFiles(
writer.write(resource);
});

if (!settings.generateClient()) {
// TODO: May need to generate a different/modified README.md for these cases
if (!settings.generateClient() || !isAwsService(settings, model)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public SymbolProvider decorateSymbolProvider(
return symbol;
}

// TODO: Should this WARNING be avoided somehow if client is not for an AWS service?
// If the SDK service ID trait is present, use that, otherwise fall back to
// the default naming strategy for the service.
return shape.getTrait(ServiceTrait.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copy link
Member

Choose a reason for hiding this comment

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

There's already an AwsProtocolUtils where this would fit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think AwsProtocolUtils is for protocol specific utilities and this isn't about protocols, so I kept it separate.

* Copyright 2021 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.aws.typescript.codegen;

import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;

/**
* Utility methods related to AWS traits.
*/
final class AwsTraitsUtils {

private AwsTraitsUtils() {}

static boolean isAwsService(TypeScriptSettings settings, Model model) {
return isAwsService(settings.getService(model));
}

static boolean isAwsService(ServiceShape serviceShape) {
return serviceShape.hasTrait(ServiceTrait.class);
}

static boolean isSigV4Service(TypeScriptSettings settings, Model model) {
return isSigV4Service(settings.getService(model));
}

static boolean isSigV4Service(ServiceShape serviceShape) {
return serviceShape.hasTrait(SigV4Trait.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use aws.protocols#restJson1

@aws.api#service(sdkId: "Not Same")
@restJson1
@aws.auth#sigv4(name: "notsame")
service OriginalName {
version: "2019-10-15",
operations: [GetFoo]
Expand Down
Loading