Skip to content

Commit 6512268

Browse files
AllanZhengYPsrchase
authored andcommitted
update runtime config customization to be fully overridable (smithy-lang#153)
* update runtime config customization to be fully overridable Previously, some default runtime config values are hard-coded into templates. This change makes each one of the properties(e.g. requestHandler) to be overridable by customizations from SDK side. If no customization exists, the generator can still generate SDK with proper dependencies. This change is added to support the customization of using WebSocket requestHandler. This change also brings in the capability that customization applied later can override the runtime config values added in earlier customization. This is necessary sometimes. For example, if customization A adds runtime config: {x: X, y: Y}. x and y are deeply coupled that they need to be in the same customization. If there's a single client needs runtime config {x: X, y: Y-update}, we should be able to make a new customization overriding the original customization. * Add ordering to customizations; runtimeConfig needs writing a whole line
1 parent 3be8028 commit 6512268

File tree

9 files changed

+301
-94
lines changed

9 files changed

+301
-94
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenVisitor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Collection;
20+
import java.util.Comparator;
2021
import java.util.HashMap;
2122
import java.util.LinkedHashMap;
2223
import java.util.List;
@@ -95,6 +96,8 @@ class CodegenVisitor extends ShapeVisitor.Default<Void> {
9596
runtimePlugins.add(runtimePlugin);
9697
});
9798
});
99+
// Sort the integrations in specified order.
100+
integrations.sort(Comparator.comparingInt(TypeScriptIntegration::getOrder));
98101

99102
// Decorate the symbol provider using integrations.
100103
SymbolProvider resolvedProvider = TypeScriptCodegenPlugin.createSymbolProvider(model);

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeConfigGenerator.java

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@
1616
package software.amazon.smithy.typescript.codegen;
1717

1818
import java.util.List;
19+
import java.util.Map;
20+
import java.util.TreeMap;
21+
import java.util.function.Consumer;
22+
23+
import software.amazon.smithy.build.SmithyBuildException;
1924
import software.amazon.smithy.codegen.core.SymbolProvider;
2025
import software.amazon.smithy.model.Model;
2126
import software.amazon.smithy.model.shapes.ServiceShape;
2227
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
28+
import software.amazon.smithy.utils.MapUtils;
2329

2430
/**
2531
* Generates runtime configuration files, files that are used to
@@ -34,6 +40,170 @@ final class RuntimeConfigGenerator {
3440
private final SymbolProvider symbolProvider;
3541
private final TypeScriptDelegator delegator;
3642
private final List<TypeScriptIntegration> integrations;
43+
private final Map<String, Consumer<TypeScriptWriter>> nodeRuntimeConfigDefaults = MapUtils.of(
44+
"requestHandler", writer -> {
45+
writer.addDependency(TypeScriptDependency.AWS_SDK_NODE_HTTP_HANDLER);
46+
writer.addImport("NodeHttpHandler", "NodeHttpHandler",
47+
TypeScriptDependency.AWS_SDK_NODE_HTTP_HANDLER.packageName);
48+
writer.write("requestHandler: new NodeHttpHandler(),");
49+
},
50+
"sha256", writer -> {
51+
writer.addDependency(TypeScriptDependency.AWS_SDK_HASH_NODE);
52+
writer.addImport("Hash", "Hash",
53+
TypeScriptDependency.AWS_SDK_HASH_NODE.packageName);
54+
writer.write("sha256: Hash.bind(null, \"sha256\"),");
55+
},
56+
"urlParser", writer -> {
57+
writer.addDependency(TypeScriptDependency.AWS_SDK_URL_PARSER_NODE);
58+
writer.addImport("parseUrl", "parseUrl",
59+
TypeScriptDependency.AWS_SDK_URL_PARSER_NODE.packageName);
60+
writer.write("urlParser: parseUrl,");
61+
},
62+
"bodyLengthChecker", writer -> {
63+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_BODY_LENGTH_NODE);
64+
writer.addImport("calculateBodyLength", "calculateBodyLength",
65+
TypeScriptDependency.AWS_SDK_UTIL_BODY_LENGTH_NODE.packageName);
66+
writer.write("bodyLengthChecker: calculateBodyLength,");
67+
},
68+
"streamCollector", writer -> {
69+
writer.addDependency(TypeScriptDependency.AWS_SDK_STREAM_COLLECTOR_NODE);
70+
writer.addImport("streamCollector", "streamCollector",
71+
TypeScriptDependency.AWS_SDK_STREAM_COLLECTOR_NODE.packageName);
72+
writer.write("streamCollector,");
73+
},
74+
"base64Decoder", writer -> {
75+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_BASE64_NODE);
76+
writer.addImport("fromBase64", "fromBase64",
77+
TypeScriptDependency.AWS_SDK_UTIL_BASE64_NODE.packageName);
78+
writer.write("base64Decoder: fromBase64,");
79+
},
80+
"base64Encoder", writer -> {
81+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_BASE64_NODE);
82+
writer.addImport("toBase64", "toBase64",
83+
TypeScriptDependency.AWS_SDK_UTIL_BASE64_NODE.packageName);
84+
writer.write("base64Encoder: toBase64,");
85+
},
86+
"utf8Decoder", writer -> {
87+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_UTF8_NODE);
88+
writer.addImport("fromUtf8", "fromUtf8",
89+
TypeScriptDependency.AWS_SDK_UTIL_UTF8_NODE.packageName);
90+
writer.write("utf8Decoder: fromUtf8,");
91+
},
92+
"utf8Encoder", writer -> {
93+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_UTF8_NODE);
94+
writer.addImport("toUtf8", "toUtf8",
95+
TypeScriptDependency.AWS_SDK_UTIL_UTF8_NODE.packageName);
96+
writer.write("utf8Encoder: toUtf8,");
97+
},
98+
"defaultUserAgent", writer -> {
99+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_USER_AGENT_NODE);
100+
writer.addImport("defaultUserAgent", "defaultUserAgent",
101+
TypeScriptDependency.AWS_SDK_UTIL_USER_AGENT_NODE.packageName);
102+
writer.addImport("name", "name", "./package.json");
103+
writer.addImport("version", "version", "./package.json");
104+
writer.write("defaultUserAgent: defaultUserAgent(name, version),");
105+
}
106+
);
107+
private final Map<String, Consumer<TypeScriptWriter>> browserRuntimeConfigDefaults = MapUtils.of(
108+
"requestHandler", writer -> {
109+
writer.addDependency(TypeScriptDependency.AWS_SDK_FETCH_HTTP_HANDLER);
110+
writer.addImport("FetchHttpHandler", "FetchHttpHandler",
111+
TypeScriptDependency.AWS_SDK_FETCH_HTTP_HANDLER.packageName);
112+
writer.write("requestHandler: new FetchHttpHandler(),");
113+
},
114+
"sha256", writer -> {
115+
writer.addDependency(TypeScriptDependency.AWS_CRYPTO_SHA256_BROWSER);
116+
writer.addImport("Sha256", "Sha256",
117+
TypeScriptDependency.AWS_CRYPTO_SHA256_BROWSER.packageName);
118+
writer.write("sha256: Sha256,");
119+
},
120+
"urlParser", writer -> {
121+
writer.addDependency(TypeScriptDependency.AWS_SDK_URL_PARSER_BROWSER);
122+
writer.addImport("parseUrl", "parseUrl",
123+
TypeScriptDependency.AWS_SDK_URL_PARSER_BROWSER.packageName);
124+
writer.write("urlParser: parseUrl,");
125+
},
126+
"bodyLengthChecker", writer -> {
127+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_BODY_LENGTH_BROWSER);
128+
writer.addImport("calculateBodyLength", "calculateBodyLength",
129+
TypeScriptDependency.AWS_SDK_UTIL_BODY_LENGTH_BROWSER.packageName);
130+
writer.write("bodyLengthChecker: calculateBodyLength,");
131+
},
132+
"streamCollector", writer -> {
133+
writer.addDependency(TypeScriptDependency.AWS_SDK_STREAM_COLLECTOR_BROWSER);
134+
writer.addImport("streamCollector", "streamCollector",
135+
TypeScriptDependency.AWS_SDK_STREAM_COLLECTOR_BROWSER.packageName);
136+
writer.write("streamCollector,");
137+
},
138+
"base64Decoder", writer -> {
139+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_BASE64_BROWSER);
140+
writer.addImport("fromBase64", "fromBase64",
141+
TypeScriptDependency.AWS_SDK_UTIL_BASE64_BROWSER.packageName);
142+
writer.write("base64Decoder: fromBase64,");
143+
},
144+
"base64Encoder", writer -> {
145+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_BASE64_BROWSER);
146+
writer.addImport("toBase64", "toBase64",
147+
TypeScriptDependency.AWS_SDK_UTIL_BASE64_BROWSER.packageName);
148+
writer.write("base64Encoder: toBase64,");
149+
},
150+
"utf8Decoder", writer -> {
151+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_UTF8_BROWSER);
152+
writer.addImport("fromUtf8", "fromUtf8",
153+
TypeScriptDependency.AWS_SDK_UTIL_UTF8_BROWSER.packageName);
154+
writer.write("utf8Decoder: fromUtf8,");
155+
},
156+
"utf8Encoder", writer -> {
157+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_UTF8_BROWSER);
158+
writer.addImport("toUtf8", "toUtf8",
159+
TypeScriptDependency.AWS_SDK_UTIL_UTF8_BROWSER.packageName);
160+
writer.write("utf8Encoder: toUtf8,");
161+
},
162+
"defaultUserAgent", writer -> {
163+
writer.addDependency(TypeScriptDependency.AWS_SDK_UTIL_USER_AGENT_BROWSER);
164+
writer.addImport("defaultUserAgent", "defaultUserAgent",
165+
TypeScriptDependency.AWS_SDK_UTIL_USER_AGENT_BROWSER.packageName);
166+
writer.addImport("name", "name", "./package.json");
167+
writer.addImport("version", "version", "./package.json");
168+
writer.write("defaultUserAgent: defaultUserAgent(name, version),");
169+
}
170+
);
171+
private final Map<String, Consumer<TypeScriptWriter>> reactNativeRuntimeConfigDefaults = MapUtils.of(
172+
"requestHandler", writer -> {
173+
writer.addDependency(TypeScriptDependency.AWS_SDK_FETCH_HTTP_HANDLER);
174+
writer.addImport("FetchHttpHandler", "FetchHttpHandler",
175+
TypeScriptDependency.AWS_SDK_FETCH_HTTP_HANDLER.packageName);
176+
writer.write("requestHandler: new FetchHttpHandler({ bufferBody: true }),");
177+
},
178+
"sha256", writer -> {
179+
writer.addDependency(TypeScriptDependency.AWS_CRYPTO_SHA256_JS);
180+
writer.addImport("Sha256", "Sha256",
181+
TypeScriptDependency.AWS_CRYPTO_SHA256_JS.packageName);
182+
writer.write("sha256: Sha256,");
183+
},
184+
"urlParser", writer -> {
185+
writer.addDependency(TypeScriptDependency.AWS_SDK_URL_PARSER_BROWSER);
186+
writer.addImport("parseUrl", "parseUrl",
187+
TypeScriptDependency.AWS_SDK_URL_PARSER_BROWSER.packageName);
188+
writer.write("urlParser: parseUrl,");
189+
},
190+
"streamCollector", writer -> {
191+
writer.addDependency(TypeScriptDependency.AWS_SDK_STREAM_COLLECTOR_RN);
192+
writer.addImport("streamCollector", "streamCollector",
193+
TypeScriptDependency.AWS_SDK_STREAM_COLLECTOR_RN.packageName);
194+
writer.write("streamCollector,");
195+
},
196+
"defaultUserAgent", writer -> {
197+
writer.addImport("name", "name", "./package.json");
198+
writer.addImport("version", "version", "./package.json");
199+
writer.write("defaultUserAgent: `aws-sdk-js-v3-react-native-$${name}/$${version}`,");
200+
}
201+
);
202+
private final Map<String, Consumer<TypeScriptWriter>> sharedRuntimeConfigDefaults = MapUtils.of(
203+
"disableHostPrefix", writer -> {
204+
writer.write("disableHostPrefix: false,");
205+
}
206+
);
37207

38208
RuntimeConfigGenerator(
39209
TypeScriptSettings settings,
@@ -62,12 +232,32 @@ void generate(LanguageTarget target) {
62232
// Inject customizations into the ~template.
63233
writer.onSection("customizations", original -> {
64234
writer.indent();
235+
// Start with defaults, use a TreeMap for keeping entries sorted.
236+
Map<String, Consumer<TypeScriptWriter>> configs =
237+
new TreeMap<>(getDefaultRuntimeConfigs(target));
238+
// Add any integration supplied runtime config writers.
65239
for (TypeScriptIntegration integration : integrations) {
66-
integration.addRuntimeConfigValues(settings, model, symbolProvider, writer, target);
240+
configs.putAll(integration.getRuntimeConfigWriters(settings, model, symbolProvider, target));
67241
}
242+
configs.values().forEach(value -> value.accept(writer));
68243
writer.dedent();
69244
});
70245
writer.write(contents, "");
71246
});
72247
}
248+
249+
private Map<String, Consumer<TypeScriptWriter>> getDefaultRuntimeConfigs(LanguageTarget target) {
250+
switch (target) {
251+
case NODE:
252+
return nodeRuntimeConfigDefaults;
253+
case BROWSER:
254+
return browserRuntimeConfigDefaults;
255+
case REACT_NATIVE:
256+
return reactNativeRuntimeConfigDefaults;
257+
case SHARED:
258+
return sharedRuntimeConfigDefaults;
259+
default:
260+
throw new SmithyBuildException("Unknown target: " + target);
261+
}
262+
}
73263
}

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/AddEventStreamDependency.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
package software.amazon.smithy.typescript.codegen.integration;
1717

18+
import java.util.Collections;
1819
import java.util.List;
20+
import java.util.Map;
1921
import java.util.Set;
22+
import java.util.function.Consumer;
2023

2124
import software.amazon.smithy.codegen.core.SymbolProvider;
2225
import software.amazon.smithy.model.Model;
@@ -29,6 +32,7 @@
2932
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
3033
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
3134
import software.amazon.smithy.utils.ListUtils;
35+
import software.amazon.smithy.utils.MapUtils;
3236

3337
/**
3438
* Adds event streams if needed.
@@ -65,42 +69,43 @@ public void addConfigInterfaceFields(
6569
}
6670

6771
@Override
68-
public void addRuntimeConfigValues(
72+
public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
6973
TypeScriptSettings settings,
7074
Model model,
7175
SymbolProvider symbolProvider,
72-
TypeScriptWriter writer,
7376
LanguageTarget target
7477
) {
7578
if (!hasEventStream(model, settings.getService(model))) {
76-
return;
79+
return Collections.emptyMap();
7780
}
78-
7981
switch (target) {
8082
case NODE:
81-
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_NODE);
82-
writer.addImport("eventStreamSerdeProvider", "eventStreamSerdeProvider",
83-
TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_NODE.packageName);
84-
writer.write("eventStreamSerdeProvider");
85-
break;
83+
return MapUtils.of("eventStreamSerdeProvider", writer -> {
84+
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_NODE);
85+
writer.addImport("eventStreamSerdeProvider", "eventStreamSerdeProvider",
86+
TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_NODE.packageName);
87+
writer.write("eventStreamSerdeProvider,");
88+
});
8689
case BROWSER:
87-
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_BROWSER);
88-
writer.addImport("eventStreamSerdeProvider", "eventStreamSerdeProvider",
89-
TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_BROWSER.packageName);
90-
writer.write("eventStreamSerdeProvider");
91-
break;
90+
return MapUtils.of("eventStreamSerdeProvider", writer -> {
91+
writer.addDependency(TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_BROWSER);
92+
writer.addImport("eventStreamSerdeProvider", "eventStreamSerdeProvider",
93+
TypeScriptDependency.AWS_SDK_EVENTSTREAM_SERDE_BROWSER.packageName);
94+
writer.write("eventStreamSerdeProvider,");
95+
});
9296
case REACT_NATIVE:
9397
// TODO: add ReactNative eventstream support
94-
writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY);
95-
writer.addImport("invalidFunction", "invalidFunction",
96-
TypeScriptDependency.INVALID_DEPENDENCY.packageName);
97-
writer.openBlock("eventStreamSerdeProvider: () => ({", "})", () -> {
98-
writer.write("serialize: invalidFunction(\"event stream is not supported in ReactNative.\"),");
99-
writer.write("deserialize: invalidFunction(\"event stream is not supported in ReactNative.\")");
98+
return MapUtils.of("eventStreamSerdeProvider", writer -> {
99+
writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY);
100+
writer.addImport("invalidFunction", "invalidFunction",
101+
TypeScriptDependency.INVALID_DEPENDENCY.packageName);
102+
writer.openBlock("eventStreamSerdeProvider: () => ({", "}),", () -> {
103+
writer.write("serialize: invalidFunction(\"event stream is not supported in ReactNative.\"),");
104+
writer.write("deserialize: invalidFunction(\"event stream is not supported in ReactNative.\")");
105+
});
100106
});
101-
break;
102107
default:
103-
// do nothing
108+
return Collections.emptyMap();
104109
}
105110
}
106111

0 commit comments

Comments
 (0)