diff --git a/.changeset/little-doors-live.md b/.changeset/little-doors-live.md new file mode 100644 index 00000000000..2ed57ca6d63 --- /dev/null +++ b/.changeset/little-doors-live.md @@ -0,0 +1,5 @@ +--- +"@smithy/experimental-identity-and-auth": patch +--- + +Add `@httpApiKeyAuth` integration tests. diff --git a/packages/experimental-identity-and-auth/src/integration/httpApiKeyAuth.integ.spec.ts b/packages/experimental-identity-and-auth/src/integration/httpApiKeyAuth.integ.spec.ts new file mode 100644 index 00000000000..9ab8efa9805 --- /dev/null +++ b/packages/experimental-identity-and-auth/src/integration/httpApiKeyAuth.integ.spec.ts @@ -0,0 +1,215 @@ +import { + HttpApiKeyAuthServiceClient, + OnlyHttpApiKeyAuthCommand, + OnlyHttpApiKeyAuthOptionalCommand, + SameAsServiceCommand, +} from "@smithy/identity-and-auth-http-api-key-auth-service"; +import { requireRequestsFrom } from "@smithy/util-test"; + +describe("@httpApiKeyAuth integration tests", () => { + // TODO(experimentalIdentityAndAuth): should match `HttpApiKeyAuthService` `@httpApiKeyAuth` trait + const MOCK_API_KEY_NAME = "Authorization"; + const MOCK_API_KEY_SCHEME = "ApiKey"; + const MOCK_API_KEY = "APIKEY_123"; + + // Arbitrary mock endpoint (`requireRequestsFrom()` intercepts network requests) + const MOCK_ENDPOINT = "https://foo.bar"; + + describe("Operation requires `@httpApiKeyAuth`", () => { + it("Request is thrown when `apiKey` is not configured", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlyHttpApiKeyAuthCommand({}))).rejects.toThrow( + "HttpAuthScheme `smithy.api#httpApiKeyAuth` did not have an IdentityProvider configured." + ); + }); + + it("Request is thrown when `apiKey` is configured incorrectly", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: {} as any, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlyHttpApiKeyAuthCommand({}))).rejects.toThrow( + "request could not be signed with `apiKey` since the `apiKey` is not defined" + ); + }); + + it("Request is thrown given configured `apiKey` identity provider throws", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: async () => { + throw new Error("IdentityProvider throws this error"); + }, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlyHttpApiKeyAuthCommand({}))).rejects.toThrow( + "IdentityProvider throws this error" + ); + }); + + it("Request is signed given configured `apiKey` identity provider", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: async () => ({ + apiKey: MOCK_API_KEY, + }), + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: `${MOCK_API_KEY_SCHEME} ${MOCK_API_KEY}`, + }, + }); + await client.send(new OnlyHttpApiKeyAuthCommand({})); + }); + + it("Request is signed given configured `apiKey` identity", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: { + apiKey: MOCK_API_KEY, + }, + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: `${MOCK_API_KEY_SCHEME} ${MOCK_API_KEY}`, + }, + }); + await client.send(new OnlyHttpApiKeyAuthCommand({})); + }); + }); + + describe("Operation has `@httpApiKeyAuth` and `@optionalAuth`", () => { + it("Request is NOT thrown and NOT signed when `apiKey` is not configured", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: (value) => expect(value).toBeUndefined(), + }, + }); + await client.send(new OnlyHttpApiKeyAuthOptionalCommand({})); + }); + + it("Request is thrown when `apiKey` is configured incorrectly", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: {} as any, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlyHttpApiKeyAuthOptionalCommand({}))).rejects.toThrow( + "request could not be signed with `apiKey` since the `apiKey` is not defined" + ); + }); + + it("Request is thrown given configured `apiKey` identity provider throws", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: async () => { + throw new Error("IdentityProvider throws this error"); + }, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new OnlyHttpApiKeyAuthOptionalCommand({}))).rejects.toThrow( + "IdentityProvider throws this error" + ); + }); + + it("Request is signed given configured `apiKey` identity provider", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: async () => ({ + apiKey: MOCK_API_KEY, + }), + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: `${MOCK_API_KEY_SCHEME} ${MOCK_API_KEY}`, + }, + }); + await client.send(new OnlyHttpApiKeyAuthOptionalCommand({})); + }); + + it("Request is signed given configured `apiKey` identity", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: { + apiKey: MOCK_API_KEY, + }, + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: `${MOCK_API_KEY_SCHEME} ${MOCK_API_KEY}`, + }, + }); + await client.send(new OnlyHttpApiKeyAuthOptionalCommand({})); + }); + }); + + describe("Service has `@httpApiKeyAuth`", () => { + it("Request is thrown when `apiKey` is not configured", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new SameAsServiceCommand({}))).rejects.toThrow( + "HttpAuthScheme `smithy.api#httpApiKeyAuth` did not have an IdentityProvider configured." + ); + }); + + it("Request is thrown when `apiKey` is configured incorrectly", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: {} as any, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new SameAsServiceCommand({}))).rejects.toThrow( + "request could not be signed with `apiKey` since the `apiKey` is not defined" + ); + }); + + it("Request is thrown given configured `apiKey` identity provider throws", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: async () => { + throw new Error("IdentityProvider throws this error"); + }, + }); + requireRequestsFrom(client).toMatch({}); + await expect(client.send(new SameAsServiceCommand({}))).rejects.toThrow("IdentityProvider throws this error"); + }); + + it("Request is signed given configured `apiKey` identity provider", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: async () => ({ + apiKey: MOCK_API_KEY, + }), + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: `${MOCK_API_KEY_SCHEME} ${MOCK_API_KEY}`, + }, + }); + await client.send(new SameAsServiceCommand({})); + }); + + it("Request is signed given configured `apiKey` identity", async () => { + const client = new HttpApiKeyAuthServiceClient({ + endpoint: MOCK_ENDPOINT, + apiKey: { + apiKey: MOCK_API_KEY, + }, + }); + requireRequestsFrom(client).toMatch({ + headers: { + [MOCK_API_KEY_NAME]: `${MOCK_API_KEY_SCHEME} ${MOCK_API_KEY}`, + }, + }); + await client.send(new SameAsServiceCommand({})); + }); + }); +}); diff --git a/scripts/build-generated-test-packages.js b/scripts/build-generated-test-packages.js index 82eb4000eb1..0bee4f96d65 100644 --- a/scripts/build-generated-test-packages.js +++ b/scripts/build-generated-test-packages.js @@ -36,6 +36,13 @@ const weatherSsdkDir = path.join( "typescript-ssdk-codegen" ) +// TODO(experimentalIdentityAndAuth): add `@httpApiKeyAuth` client for integration tests +const httpApiKeyAuthClientDir = path.join( + codegenTestDir, + "identity-and-auth-http-api-key-auth", + "typescript-codegen" +); + const nodeModulesDir = path.join(root, "node_modules"); const buildAndCopyToNodeModules = async (packageName, codegenDir, nodeModulesDir) => { @@ -61,6 +68,8 @@ const buildAndCopyToNodeModules = async (packageName, codegenDir, nodeModulesDir await buildAndCopyToNodeModules("weather-ssdk", weatherSsdkDir, nodeModulesDir); // TODO(experimentalIdentityAndAuth): build generic client for integration tests await buildAndCopyToNodeModules("@smithy/weather-experimental-identity-and-auth", weatherExperimentalIdentityAndAuthClientDir, nodeModulesDir); + // TODO(experimentalIdentityAndAuth): add `@httpApiKeyAuth` client for integration tests + await buildAndCopyToNodeModules("@smithy/identity-and-auth-http-api-key-auth-service", httpApiKeyAuthClientDir, nodeModulesDir); } catch (e) { console.log(e); process.exit(1); diff --git a/smithy-typescript-codegen-test/model/identity-and-auth/httpApiKeyAuth/HttpApiKeyAuthService.smithy b/smithy-typescript-codegen-test/model/identity-and-auth/httpApiKeyAuth/HttpApiKeyAuthService.smithy new file mode 100644 index 00000000000..6ff848e449f --- /dev/null +++ b/smithy-typescript-codegen-test/model/identity-and-auth/httpApiKeyAuth/HttpApiKeyAuthService.smithy @@ -0,0 +1,27 @@ +$version: "2.0" + +namespace identity.auth.httpApiKeyAuth + +use common#fakeProtocol + +@fakeProtocol +@httpApiKeyAuth(scheme: "ApiKey", name: "Authorization", in: "header") +service HttpApiKeyAuthService { + operations: [ + OnlyHttpApiKeyAuth + OnlyHttpApiKeyAuthOptional + SameAsService + ] +} + +@http(method: "GET", uri: "/OnlyHttpApiKeyAuth") +@auth([httpApiKeyAuth]) +operation OnlyHttpApiKeyAuth {} + +@http(method: "GET", uri: "/OnlyHttpApiKeyAuthOptional") +@auth([httpApiKeyAuth]) +@optionalAuth +operation OnlyHttpApiKeyAuthOptional {} + +@http(method: "GET", uri: "/SameAsService") +operation SameAsService {} diff --git a/smithy-typescript-codegen-test/smithy-build.json b/smithy-typescript-codegen-test/smithy-build.json index fd306649b4a..021ea3ff6c8 100644 --- a/smithy-typescript-codegen-test/smithy-build.json +++ b/smithy-typescript-codegen-test/smithy-build.json @@ -74,6 +74,29 @@ } } } + }, + "identity-and-auth-http-api-key-auth": { + "transforms": [ + { + "name": "includeServices", + "args": { + "services": ["identity.auth.httpApiKeyAuth#HttpApiKeyAuthService"] + } + } + ], + "plugins": { + "typescript-codegen": { + "service": "identity.auth.httpApiKeyAuth#HttpApiKeyAuthService", + "targetNamespace": "HttpApiKeyAuthService", + "package": "@smithy/identity-and-auth-http-api-key-auth-service", + "packageVersion": "0.0.1", + "packageJson": { + "license": "Apache-2.0", + "private": true + }, + "experimentalIdentityAndAuth": true + } + } } }, "plugins": {