Skip to content

Commit f3ea1fe

Browse files
committed
fix: add fallback resolution
1 parent c0cc0c7 commit f3ea1fe

File tree

5 files changed

+94
-38
lines changed

5 files changed

+94
-38
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: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,27 @@ private void generateEndpointParameters() {
113113
writer -> {
114114
writer.addImport("EndpointParameters", "__EndpointParameters", TypeScriptDependency.SMITHY_TYPES);
115115
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,31 @@ 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-
}
173+
// Add clientContextParams with same types as input
179174
if (!customerContextParams.isEmpty()) {
180175
writer.write("clientContextParams: {");
181176
writer.indent();
182177
ObjectNode ruleSet = endpointRuleSetTrait.getRuleSet().expectObjectNode();
183178
ruleSet.getObjectMember("parameters").ifPresent(parameters -> {
184179
parameters.accept(new RuleSetParametersVisitor(
185-
writer, customerContextParams, false));
180+
writer, customerContextParams, true));
186181
});
187182
writer.dedent();
188183
writer.write("};");
189184
}
190185
}
191186
);
192187
// 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-
}
201188
if (!customerContextParams.isEmpty()) {
202189
// Check if any parameters have default values
203190
boolean hasDefaults = false;
@@ -253,6 +240,34 @@ private void generateEndpointParameters() {
253240
"defaultSigningName: \"$L\",",
254241
settings.getDefaultSigningName()
255242
);
243+
// Only generate clientContextParams if there are customer context params
244+
if (!customerContextParams.isEmpty()) {
245+
// Initialize clientContextParams if undefined to satisfy type requirements
246+
// Check if we have defaults to merge
247+
boolean hasDefaultsForResolve = false;
248+
if (ruleSet.getObjectMember("parameters").isPresent()) {
249+
ObjectNode parameters = ruleSet.getObjectMember("parameters")
250+
.get().expectObjectNode();
251+
for (Map.Entry<String, String> entry : customerContextParams.entrySet()) {
252+
String paramName = entry.getKey();
253+
ObjectNode paramNode = parameters.getObjectMember(paramName).orElse(null);
254+
if (paramNode != null && paramNode.containsMember("default")) {
255+
hasDefaultsForResolve = true;
256+
break;
257+
}
258+
}
259+
}
260+
if (hasDefaultsForResolve) {
261+
writer.write(
262+
"clientContextParams: Object.assign(clientContextParamDefaults, "
263+
+ "options.clientContextParams ?? {}),"
264+
);
265+
} else {
266+
writer.write(
267+
"clientContextParams: options.clientContextParams ?? {},"
268+
);
269+
}
270+
}
256271
});
257272
}
258273
);

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)