Skip to content

Commit 13fd0f0

Browse files
committed
fix: add fallback resolution
1 parent ebb79d0 commit 13fd0f0

File tree

5 files changed

+95
-40
lines changed

5 files changed

+95
-40
lines changed

packages/middleware-endpoint/src/adaptors/createConfigValueProvider.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,31 @@ describe(createConfigValueProvider.name, () => {
5858
expect(await createConfigValueProvider("v1", "endpoint", config)()).toEqual(sampleUrl);
5959
expect(await createConfigValueProvider("v2", "endpoint", config)()).toEqual(sampleUrl);
6060
});
61+
62+
it("should prioritize clientContextParams over direct properties", async () => {
63+
const config = {
64+
apiKey: "direct-api-key",
65+
clientContextParams: {
66+
apiKey: "nested-api-key",
67+
},
68+
};
69+
expect(await createConfigValueProvider("apiKey", "apiKey", config)()).toEqual("nested-api-key");
70+
});
71+
72+
it("should fall back to direct property when clientContextParams is not provided", async () => {
73+
const config = {
74+
customParam: "direct-value",
75+
};
76+
expect(await createConfigValueProvider("customParam", "customParam", config)()).toEqual("direct-value");
77+
});
78+
79+
it("should fall back to direct property when clientContextParams exists but param is not in it", async () => {
80+
const config = {
81+
customParam: "direct-value",
82+
clientContextParams: {
83+
otherParam: "other-value",
84+
},
85+
};
86+
expect(await createConfigValueProvider("customParam", "customParam", config)()).toEqual("direct-value");
87+
});
6188
});

packages/middleware-endpoint/src/adaptors/createConfigValueProvider.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ export const createConfigValueProvider = <Config extends Record<string, unknown>
1818
config: Config
1919
) => {
2020
const configProvider = async () => {
21-
const configValue: unknown = config[configKey] ?? config[canonicalEndpointParamKey];
21+
// Check clientContextParams first for client context parameters
22+
const clientContextParams = config.clientContextParams as Record<string, unknown> | undefined;
23+
const nestedValue: unknown = clientContextParams?.[configKey] ?? clientContextParams?.[canonicalEndpointParamKey];
24+
25+
// Fall back to direct config properties
26+
const configValue: unknown = nestedValue ?? config[configKey] ?? config[canonicalEndpointParamKey];
27+
2228
if (typeof configValue === "function") {
2329
return configValue();
2430
}

packages/middleware-endpoint/src/adaptors/getEndpointFromInstructions.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ export const resolveParams = async <
7878
instructionsSupplier: EndpointParameterInstructionsSupplier,
7979
clientConfig: Partial<EndpointResolvedConfig<T>> & Config
8080
) => {
81+
// Initialize clientContextParams to empty object if undefined
82+
// when accessing nested properties during parameter resolution
83+
const config = clientConfig as typeof clientConfig & { clientContextParams?: Record<string, unknown> };
84+
if (config.clientContextParams === undefined) {
85+
config.clientContextParams = {};
86+
}
87+
8188
const endpointParams: EndpointParameters = {};
8289
const instructions: EndpointParameterInstructions = instructionsSupplier?.getEndpointParameterInstructions?.() || {};
8390

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -111,29 +111,29 @@ private void generateEndpointParameters() {
111111
this.delegator.useFileWriter(
112112
Paths.get(CodegenUtils.SOURCE_FOLDER, ENDPOINT_FOLDER, ENDPOINT_PARAMETERS_FILE).toString(),
113113
writer -> {
114-
writer.addTypeImport("EndpointParameters", "__EndpointParameters", TypeScriptDependency.SMITHY_TYPES);
115-
writer.addTypeImport("Provider", null, TypeScriptDependency.SMITHY_TYPES);
114+
writer.addImport("EndpointParameters", "__EndpointParameters", TypeScriptDependency.SMITHY_TYPES);
115+
writer.addImport("Provider", null, TypeScriptDependency.SMITHY_TYPES);
116+
Map<String, String> clientContextParams =
117+
ruleSetParameterFinder.getClientContextParams();
118+
Map<String, String> builtInParams = ruleSetParameterFinder.getBuiltInParams();
119+
builtInParams.keySet().removeIf(OmitEndpointParams::isOmitted);
120+
Set<String> knownConfigKeys = Set.of(
121+
"apiKey", "retryStrategy", "requestHandler");
122+
// Generate clientContextParams with all params excluding built-ins
123+
Map<String, String> customerContextParams = new HashMap<>();
124+
for (Map.Entry<String, String> entry : clientContextParams.entrySet()) {
125+
if (!builtInParams.containsKey(entry.getKey())) {
126+
customerContextParams.put(entry.getKey(), entry.getValue());
127+
}
128+
}
116129

117130
writer.writeDocs("@public");
118131
writer.openBlock(
119132
"export interface ClientInputEndpointParameters {",
120133
"}",
121134
() -> {
122-
Map<String, String> clientContextParams =
123-
ruleSetParameterFinder.getClientContextParams();
124-
Map<String, String> builtInParams = ruleSetParameterFinder.getBuiltInParams();
125-
builtInParams.keySet().removeIf(OmitEndpointParams::isOmitted);
126-
Set<String> knownConfigKeys = Set.of(
127-
"apiKey", "retryStrategy", "requestHandler");
128-
// Generate clientContextParams with all params excluding built-ins
129-
Map<String, String> customerContextParams = new HashMap<>();
130-
for (Map.Entry<String, String> entry : clientContextParams.entrySet()) {
131-
if (!builtInParams.containsKey(entry.getKey())) {
132-
customerContextParams.put(entry.getKey(), entry.getValue());
133-
}
134-
}
135135
if (!customerContextParams.isEmpty()) {
136-
writer.write("clientContextParams: {");
136+
writer.write("clientContextParams?: {");
137137
writer.indent();
138138
ObjectNode ruleSet = endpointRuleSetTrait.getRuleSet().expectObjectNode();
139139
ruleSet.getObjectMember("parameters").ifPresent(parameters -> {
@@ -160,44 +160,30 @@ private void generateEndpointParameters() {
160160
);
161161

162162
writer.write("");
163-
writer.openBlock(
164-
"""
165-
export type ClientResolvedEndpointParameters = Omit<ClientInputEndpointParameters, "endpoint"> & {
166-
""",
163+
// Build Omit type - omit endpoint, and clientContextParams if exists
164+
String omitFields = customerContextParams.isEmpty()
165+
? "\"endpoint\""
166+
: "\"endpoint\" | \"clientContextParams\"";
167+
writer.openBlock(
168+
"export type ClientResolvedEndpointParameters = "
169+
+ "Omit<ClientInputEndpointParameters, " + omitFields + "> & {",
167170
"};",
168171
() -> {
169172
writer.write("defaultSigningName: string;");
170-
// Add clientContextParams with same structure as input
171-
Map<String, String> clientContextParams = ruleSetParameterFinder.getClientContextParams();
172-
Map<String, String> customerContextParams = new HashMap<>();
173-
Map<String, String> builtInParams = ruleSetParameterFinder.getBuiltInParams();
174-
for (Map.Entry<String, String> entry : clientContextParams.entrySet()) {
175-
if (!builtInParams.containsKey(entry.getKey())) {
176-
customerContextParams.put(entry.getKey(), entry.getValue());
177-
}
178-
}
179173
if (!customerContextParams.isEmpty()) {
180174
writer.write("clientContextParams: {");
181175
writer.indent();
182176
ObjectNode ruleSet = endpointRuleSetTrait.getRuleSet().expectObjectNode();
183177
ruleSet.getObjectMember("parameters").ifPresent(parameters -> {
184178
parameters.accept(new RuleSetParametersVisitor(
185-
writer, customerContextParams, false));
179+
writer, customerContextParams, true));
186180
});
187181
writer.dedent();
188182
writer.write("};");
189183
}
190184
}
191185
);
192186
// Generate clientContextParamDefaults only if there are customer context params
193-
Map<String, String> clientContextParams = ruleSetParameterFinder.getClientContextParams();
194-
Map<String, String> builtInParams = ruleSetParameterFinder.getBuiltInParams();
195-
Map<String, String> customerContextParams = new HashMap<>();
196-
for (Map.Entry<String, String> entry : clientContextParams.entrySet()) {
197-
if (!builtInParams.containsKey(entry.getKey())) {
198-
customerContextParams.put(entry.getKey(), entry.getValue());
199-
}
200-
}
201187
if (!customerContextParams.isEmpty()) {
202188
// Check if any parameters have default values
203189
boolean hasDefaults = false;
@@ -253,6 +239,34 @@ private void generateEndpointParameters() {
253239
"defaultSigningName: \"$L\",",
254240
settings.getDefaultSigningName()
255241
);
242+
// Only generate clientContextParams if there are customer context params
243+
if (!customerContextParams.isEmpty()) {
244+
// Initialize clientContextParams if undefined to satisfy type requirements
245+
// Check if we have defaults to merge
246+
boolean hasDefaultsForResolve = false;
247+
if (ruleSet.getObjectMember("parameters").isPresent()) {
248+
ObjectNode parameters = ruleSet.getObjectMember("parameters")
249+
.get().expectObjectNode();
250+
for (Map.Entry<String, String> entry : customerContextParams.entrySet()) {
251+
String paramName = entry.getKey();
252+
ObjectNode paramNode = parameters.getObjectMember(paramName).orElse(null);
253+
if (paramNode != null && paramNode.containsMember("default")) {
254+
hasDefaultsForResolve = true;
255+
break;
256+
}
257+
}
258+
}
259+
if (hasDefaultsForResolve) {
260+
writer.write(
261+
"clientContextParams: Object.assign(clientContextParamDefaults, "
262+
+ "options.clientContextParams ?? {}),"
263+
);
264+
} else {
265+
writer.write(
266+
"clientContextParams: options.clientContextParams ?? {},"
267+
);
268+
}
269+
}
256270
});
257271
}
258272
);

smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,13 @@ public void containsExtraContextParameter() {
5656
return Object.assign(options, {
5757
stage: options.stage ?? "production",
5858
defaultSigningName: "",
59+
clientContextParams: Object.assign(clientContextParamDefaults, options.clientContextParams ?? {}),
5960
});
6061
"""));
6162
assertThat(endpointParameters, containsString(
6263
"""
6364
export interface ClientInputEndpointParameters {
64-
clientContextParams: {
65+
clientContextParams?: {
6566
region?: string | undefined | Provider<string | undefined>;
6667
stage?: string | undefined | Provider<string | undefined>;
6768
};

0 commit comments

Comments
 (0)