Skip to content

Commit

Permalink
chore: release accountId-endpoints config and codegen changes (#6313)
Browse files Browse the repository at this point in the history
This reverts commit 08aa23f.
  • Loading branch information
siddsriv authored Jul 26, 2024
1 parent d5011a5 commit de8d4a6
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

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.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.logging.Logger;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
import software.amazon.smithy.typescript.codegen.LanguageTarget;
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.endpointsV2.RuleSetParameterFinder;
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* Generates accountIdEndpointMode configuration field for service clients
* that have the AccountIdEndpointMode built-in param in the ruleset.
*/
@SmithyInternalApi
public final class AddAccountIdEndpointModeRuntimeConfig implements TypeScriptIntegration {

private static final Logger LOGGER = Logger.getLogger(AddAccountIdEndpointModeRuntimeConfig.class.getName());

@Override
public void addConfigInterfaceFields(
TypeScriptSettings settings,
Model model,
SymbolProvider symbolProvider,
TypeScriptWriter writer
) {
if (isAwsService(settings, model)) {
ServiceShape service = settings.getService(model);
Optional<EndpointRuleSetTrait> endpointRuleSetTrait = service.getTrait(EndpointRuleSetTrait.class);
if (endpointRuleSetTrait.isPresent()) {
RuleSetParameterFinder ruleSetParameterFinder = new RuleSetParameterFinder(service);
if (ruleSetParameterFinder.getBuiltInParams().containsKey("AccountIdEndpointMode")) {
writer.addDependency(AwsDependency.AWS_SDK_CORE);
// TODO: change to addImportSubmodule when available; smithy-ts, #pull-1280
writer.addImport("AccountIdEndpointMode", "AccountIdEndpointMode",
"@aws-sdk/core/account-id-endpoint");
writer.writeDocs("Defines if the AWS AccountId will be used for endpoint routing.");
writer.write("accountIdEndpointMode?: AccountIdEndpointMode | "
+ "__Provider<AccountIdEndpointMode>;\n");
}
}
}
}

@Override
public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
TypeScriptSettings settings,
Model model,
SymbolProvider symbolProvider,
LanguageTarget target
) {
ServiceShape service = settings.getService(model);
Map<String, Consumer<TypeScriptWriter>> runtimeConfigs = new HashMap<>();
if (isAwsService(settings, model) || isSigV4Service(settings, model)) {
Optional<EndpointRuleSetTrait> endpointRuleSetTrait = service.getTrait(EndpointRuleSetTrait.class);
if (endpointRuleSetTrait.isPresent()) {
RuleSetParameterFinder ruleSetParameterFinder = new RuleSetParameterFinder(service);
if (ruleSetParameterFinder.getBuiltInParams().containsKey("AccountIdEndpointMode")) {
switch (target) {
case BROWSER:
runtimeConfigs.put("accountIdEndpointMode", writer -> {
writer.addDependency(AwsDependency.AWS_SDK_CORE);
// TODO: change to addImportSubmodule when available
writer.addImport("DEFAULT_ACCOUNT_ID_ENDPOINT_MODE", "DEFAULT_ACCOUNT_ID_ENDPOINT_MODE",
"@aws-sdk/core/account-id-endpoint");
writer.write("(() => Promise.resolve(DEFAULT_ACCOUNT_ID_ENDPOINT_MODE))");
});
break;
case NODE:
runtimeConfigs.put("accountIdEndpointMode", writer -> {
writer.addDependency(TypeScriptDependency.NODE_CONFIG_PROVIDER);
writer.addImport("loadConfig", "loadNodeConfig",
TypeScriptDependency.NODE_CONFIG_PROVIDER);
writer.addDependency(AwsDependency.AWS_SDK_CORE);
// TODO: change to addImportSubmodule when available
writer.addImport("NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS",
"NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS",
"@aws-sdk/core/account-id-endpoint");
writer.write(
"loadNodeConfig(NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS)");
});
break;
default:
LOGGER.warning("AccountIdEndpointMode config not supported for target: " + target);
break;
}
}
}
}
return runtimeConfigs;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

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

import software.amazon.smithy.typescript.codegen.TypeScriptCodegenContext;
import software.amazon.smithy.typescript.codegen.endpointsV2.OmitEndpointParams;
import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration;
import software.amazon.smithy.utils.SetUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
public class AddOmittedEndpointParams implements TypeScriptIntegration {

@Override
public void customize(TypeScriptCodegenContext codegenContext) {
setParamForOmission();
}

private void setParamForOmission() {
OmitEndpointParams.addOmittedParams(SetUtils.of("AccountId"));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
software.amazon.smithy.aws.typescript.codegen.AddEndpointsV2ParameterNameMap
software.amazon.smithy.aws.typescript.codegen.AddAwsRuntimeConfig
software.amazon.smithy.aws.typescript.codegen.AddAccountIdEndpointModeRuntimeConfig
software.amazon.smithy.aws.typescript.codegen.AddBuiltinPlugins
software.amazon.smithy.aws.typescript.codegen.AddAwsAuthPlugin
software.amazon.smithy.aws.typescript.codegen.AddTokenAuthPlugin
Expand Down
6 changes: 6 additions & 0 deletions packages/core/account-id-endpoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

/**
* Do not edit:
* This is a compatibility redirect for contexts that do not understand package.json exports field.
*/
module.exports = require("./dist-cjs/submodules/account-id-endpoint/index.js");
14 changes: 12 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
"require": "./dist-cjs/submodules/httpAuthSchemes/index.js",
"types": "./dist-types/submodules/httpAuthSchemes/index.d.ts"
},
"./account-id-endpoint": {
"module": "./dist-es/submodules/account-id-endpoint/index.js",
"node": "./dist-cjs/submodules/account-id-endpoint/index.js",
"import": "./dist-es/submodules/account-id-endpoint/index.js",
"require": "./dist-cjs/submodules/account-id-endpoint/index.js",
"types": "./dist-types/submodules/account-id-endpoint/index.d.ts"
},
"./protocols": {
"module": "./dist-es/submodules/protocols/index.js",
"node": "./dist-cjs/submodules/protocols/index.js",
Expand All @@ -58,7 +65,8 @@
"dist-*/**",
"./client.js",
"./httpAuthSchemes.js",
"./protocols.js"
"./protocols.js",
"./account-id-endpoint.js"
],
"sideEffects": false,
"author": {
Expand All @@ -73,7 +81,9 @@
"@smithy/smithy-client": "^3.1.10",
"@smithy/types": "^3.3.0",
"fast-xml-parser": "4.2.5",
"tslib": "^2.6.2"
"tslib": "^2.6.2",
"@smithy/node-config-provider": "^3.1.4",
"@smithy/util-middleware": "^3.0.3"
},
"devDependencies": {
"@tsconfig/recommended": "1.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Provider } from "@smithy/types";
import { normalizeProvider } from "@smithy/util-middleware";

import {
AccountIdEndpointMode,
DEFAULT_ACCOUNT_ID_ENDPOINT_MODE,
validateAccountIdEndpointMode,
} from "./AccountIdEndpointModeConstants";

/**
* @public
*/
export interface AccountIdEndpointModeInputConfig {
/**
* The account ID endpoint mode to use.
*/
accountIdEndpointMode?: AccountIdEndpointMode | Provider<AccountIdEndpointMode>;
}

/**
* @internal
*/
interface PreviouslyResolved {}

/**
* @internal
*/
export interface AccountIdEndpointModeResolvedConfig {
/**
* Resolved value for input config {config.accountIdEndpointMode}
*/
accountIdEndpointMode: Provider<AccountIdEndpointMode>;
}

/**
* @internal
*/
export const resolveAccountIdEndpointModeConfig = <T>(
input: T & AccountIdEndpointModeInputConfig & PreviouslyResolved
): T & AccountIdEndpointModeResolvedConfig => {
return {
...input,
accountIdEndpointMode: async () => {
const accountIdEndpointModeProvider = normalizeProvider(
input.accountIdEndpointMode ?? DEFAULT_ACCOUNT_ID_ENDPOINT_MODE
);
const accIdMode = await accountIdEndpointModeProvider();
if (!validateAccountIdEndpointMode(accIdMode)) {
throw new Error(
`Invalid value for accountIdEndpointMode: ${accIdMode}. Valid values are: "required", "preferred", "disabled".`
);
}
return accIdMode;
},
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { validateAccountIdEndpointMode } from "./AccountIdEndpointModeConstants";

describe("validateAccountIdEndpointMode", () => {
it('should return true for "disabled"', () => {
const result = validateAccountIdEndpointMode("disabled");
expect(result).toBe(true);
});

it('should return true for "preferred"', () => {
const result = validateAccountIdEndpointMode("preferred");
expect(result).toBe(true);
});

it('should return true for "required"', () => {
const result = validateAccountIdEndpointMode("required");
expect(result).toBe(true);
});

it("should return false for an invalid value", () => {
const result = validateAccountIdEndpointMode("invalidValue");
expect(result).toBe(false);
});

it("should return false for an empty string", () => {
const result = validateAccountIdEndpointMode("");
expect(result).toBe(false);
});

it("should return false for a number", () => {
const result = validateAccountIdEndpointMode(123);
expect(result).toBe(false);
});

it("should return false for null", () => {
const result = validateAccountIdEndpointMode(null);
expect(result).toBe(false);
});

it("should return false for undefined", () => {
const result = validateAccountIdEndpointMode(undefined);
expect(result).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type AccountIdEndpointMode = "disabled" | "preferred" | "required";

export const DEFAULT_ACCOUNT_ID_ENDPOINT_MODE = "preferred";

export const ACCOUNT_ID_ENDPOINT_MODE_VALUES: AccountIdEndpointMode[] = ["disabled", "preferred", "required"];

/**
* @internal
*/
export function validateAccountIdEndpointMode(value: any): value is AccountIdEndpointMode {
return ACCOUNT_ID_ENDPOINT_MODE_VALUES.includes(value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { DEFAULT_ACCOUNT_ID_ENDPOINT_MODE } from "./AccountIdEndpointModeConstants";
import {
CONFIG_ACCOUNT_ID_ENDPOINT_MODE,
ENV_ACCOUNT_ID_ENDPOINT_MODE,
NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS,
} from "./NodeAccountIdEndpointModeConfigOptions";

describe("NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS", () => {
const originalEnv = process.env;

beforeEach(() => {
jest.resetModules();
process.env = { ...originalEnv };
});

afterEach(() => {
process.env = originalEnv;
});

describe("environmentVariableSelector", () => {
it("should return the value set in environment variables", () => {
const testValue = "preferred";
process.env[ENV_ACCOUNT_ID_ENDPOINT_MODE] = testValue;
const selector = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.environmentVariableSelector;
expect(selector(process.env)).toEqual(testValue);
});

it("should throw an error if the environment variable is set to an invalid value", () => {
process.env[ENV_ACCOUNT_ID_ENDPOINT_MODE] = "InvalidValue";
const selector = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.environmentVariableSelector;
expect(() => selector(process.env)).toThrow("Invalid AccountIdEndpointMode value");
});

it("should not throw an error if the environment variable is not set", () => {
delete process.env[ENV_ACCOUNT_ID_ENDPOINT_MODE];
const selector = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.environmentVariableSelector;
expect(() => selector(process.env)).not.toThrow();
});
});

describe("configFileSelector", () => {
it("should return the value set in the configuration file", () => {
const testValue = "required";
const profile = { [CONFIG_ACCOUNT_ID_ENDPOINT_MODE]: testValue };
const selector = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.configFileSelector;
expect(selector(profile)).toEqual(testValue);
});

it("should throw an error if the configuration file contains an invalid value", () => {
const profile = { [CONFIG_ACCOUNT_ID_ENDPOINT_MODE]: "InvalidValue" };
const selector = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.configFileSelector;
expect(() => selector(profile)).toThrow("Invalid AccountIdEndpointMode value");
});

it("should not throw an error if the configuration file does not contain the setting", () => {
const profile = {};
const selector = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.configFileSelector;
expect(() => selector(profile)).not.toThrow();
});
});

describe("default", () => {
it("should return the default value", () => {
const defaultValue = NODE_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_OPTIONS.default;
expect(defaultValue).toEqual(DEFAULT_ACCOUNT_ID_ENDPOINT_MODE);
});
});
});
Loading

0 comments on commit de8d4a6

Please sign in to comment.