-
Notifications
You must be signed in to change notification settings - Fork 112
fix: handle clientContextParam collisions with builtin config keys #1788
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
base: main
Are you sure you want to change the base?
Conversation
4a0b1ea to
c0cc0c7
Compare
f3ea1fe to
13fd0f0
Compare
9117008 to
1fc37f8
Compare
packages/middleware-endpoint/src/adaptors/getEndpointFromInstructions.ts
Outdated
Show resolved
Hide resolved
packages/middleware-endpoint/src/adaptors/createConfigValueProvider.ts
Outdated
Show resolved
Hide resolved
...est/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java
Outdated
Show resolved
Hide resolved
ef3cda8 to
c5f0d86
Compare
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
packages/middleware-endpoint/src/adaptors/createConfigValueProvider.ts
Outdated
Show resolved
Hide resolved
| if (isClientContextParam) { | ||
| // For client context parameters, check clientContextParams first | ||
| const clientContextParams = config.clientContextParams as Record<string, unknown> | undefined; | ||
| const nestedValue: unknown = clientContextParams?.[configKey] ?? clientContextParams?.[canonicalEndpointParamKey]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any case where the nested object does not use the canonical parameter key?
If not, there's no reason to check the configKey entry for the nested object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so
packages/middleware-endpoint/src/adaptors/getEndpointFromInstructions.ts
Outdated
Show resolved
Hide resolved
| options: T & ClientInputEndpointParameters | ||
| ): T & ClientResolvedEndpointParameters => { | ||
| return Object.assign(options, { | ||
| apiKey: options.apiKey ?? "default-api-key", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main point of the fix, but this shouldn't appear here, since it will conflict with the same key potentially be added by the api key auth scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is generated because the parameter has a default value set defined in @endpointRuleSet. If a default value is not defined it doesn't generate this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that does not matter - a conflicting key cannot be generated here
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
...rc/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java
Outdated
Show resolved
Hide resolved
| @documentation("xyz interfaces") | ||
| @clientContextParams( | ||
| apiKey: { type: "string", documentation: "API key for authentication" } | ||
| customParam: { type: "string", documentation: "Custom parameter" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
going to comment again here since this is the source file and ruleset.ts is generated:
add some more client context params with varying permutations:
- conflicting / non-conflicting
- string / string[] / boolean
- has default + required / no default + optional
9f47441 to
1ed724d
Compare
1ed724d to
182326a
Compare
| endpoint: "https://localhost", | ||
| }); | ||
| apiKey: { apiKey: "test-api-key" }, | ||
| } as any); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there should be no as any here. This test is from a user perspective (integ test means calls user-facing public APIs only), so the types must align with the user intention.
| const config = { | ||
| apiKey: "direct-api-key", | ||
| clientContextParams: { | ||
| apiKey: "nested-api-key", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although conflicts don't matter at the unit level (and this code is not aware of what is considered a conflict),
change this test key to something else that doesn't conflict, like stage: string = "beta" | "prod". This implies that apiKey is a string at the base config level, but it isn't (when present).
packages/middleware-endpoint/src/adaptors/createConfigValueProvider.spec.ts
Show resolved
Hide resolved
| endpoint: "https://localhost/nowhere", | ||
| }); | ||
| apiKey: { apiKey: "test-api-key" }, | ||
| } as any); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove as any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I removed any it was throwing a type incompatibility error.
| type Provider, | ||
| ApiKeyIdentity, | ||
| ApiKeyIdentityProvider, | ||
| HttpApiKeyAuthLocation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these new imports look like types. Change the codegen part to use addTypeImport() instead, or if imported via symbol, ensure the symbol has the typeOnly property.
| customParam: "default-custom-value", | ||
| debugMode: false, | ||
| enableFeature: true, | ||
| } as const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized that this set of defaults cannot be the full set of clientContextParams that have default values.
For example, consider a non-conflicting clientContextParam stage with default of "prod". The user may instantiate like this:
new Client({
stage: "prod"
});but because the default value is written to the nested property, it will take precedence.
new Client({
stage: "beta" // <- set by user
clientContextParams: {
stage: "prod" // <- set by default, but higher priority
}
});Therefore, this set of defaults must only be the conflicting clientContextParam defaults. In such cases, the default value applied at the root config level will take care of setting the correct default value.
const resolvedConfig = {
stage: "prod", // set by resolveClientEndpointParameters as default
conflictingParam: "(any)", // conflicting param may have separate default
clientContextParams: {
stage: undefined // no default needs to be assigned here
conflictingParam: "conflictingParamDefault" // do set values for conflicting params
}
}Please also add an integration test for this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Will modify.
| * @internal | ||
| */ | ||
| export const commonParams = { | ||
| ApiKey: { type: "clientContextParams", name: "apiKey" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wasn't there an automatic conversion of the param name to camelCase in the endpoints generator? If it couldn't find an existing built-in name.
| */ | ||
| export interface EndpointParameters extends __EndpointParameters { | ||
| endpoint?: string | undefined; | ||
| endpoint: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not saying it's wrong necessarily, but why did endpoint become required in the change?
| @@ -1,5 +1,5 @@ | |||
| // smithy-typescript generated code | |||
| import type { HttpAuthScheme } from "@smithy/types"; | |||
| import { type HttpAuthScheme, ApiKeyIdentity, ApiKeyIdentityProvider } from "@smithy/types"; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change these to type imports
| type Provider, | ||
| ApiKeyIdentity, | ||
| ApiKeyIdentityProvider, | ||
| HttpApiKeyAuthLocation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use type imports
Issue #, if available:
#1779
codegen v3:aws/aws-sdk-js-v3#7549
Description of changes:
This fixes TypeScript client codegen conflict between known config keys and clientContextParams by adding the nested clientContextParams object to isolate conflicting parameters while maintaining backward compatibility and proper precedence.
--