Skip to content

Commit 84a53e2

Browse files
committed
fixup! feat: support @httpApiKeyAuth trait
1 parent e357787 commit 84a53e2

File tree

7 files changed

+142
-111
lines changed

7 files changed

+142
-111
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/AddHttpApiKeyAuthPlugin.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import software.amazon.smithy.codegen.core.Symbol;
2525
import software.amazon.smithy.codegen.core.SymbolProvider;
2626
import software.amazon.smithy.model.Model;
27+
import software.amazon.smithy.model.knowledge.ServiceIndex;
2728
import software.amazon.smithy.model.knowledge.TopDownIndex;
2829
import software.amazon.smithy.model.shapes.OperationShape;
2930
import software.amazon.smithy.model.shapes.ServiceShape;
@@ -71,8 +72,12 @@ public List<RuntimeClientPlugin> getClientPlugins() {
7172
.namespace("./" + CodegenUtils.SOURCE_FOLDER + "/middleware/HttpApiKeyAuth", "/")
7273
.name("resolveHttpApiKeyAuthConfig")
7374
.build())
74-
.servicePredicate((m, s) -> s.hasTrait(HttpApiKeyAuthTrait.class)
75-
&& !areAllOptionalAuthOperations(m, s))
75+
.servicePredicate((m, s) -> ServiceIndex.of(m).getEffectiveAuthSchemes(s)
76+
.values().stream()
77+
.filter(c -> c instanceof HttpApiKeyAuthTrait)
78+
.findFirst()
79+
.isPresent()
80+
&& !areAllOptionalAuthOperations(m, s))
7681
.build(),
7782

7883
// Add the middleware to operations that use HTTP API key authorization.
@@ -89,8 +94,12 @@ public List<RuntimeClientPlugin> getClientPlugins() {
8994
s.getTrait(HttpApiKeyAuthTrait.class).get().getScheme().ifPresent(scheme ->
9095
put("scheme", scheme));
9196
}})
92-
.operationPredicate((m, s, o) -> s.hasTrait(HttpApiKeyAuthTrait.class)
93-
&& !o.hasTrait(OptionalAuthTrait.class))
97+
.operationPredicate((m, s, o) -> ServiceIndex.of(m).getEffectiveAuthSchemes(s)
98+
.values().stream()
99+
.filter(c -> c instanceof HttpApiKeyAuthTrait)
100+
.findFirst()
101+
.isPresent()
102+
&& !o.hasTrait(OptionalAuthTrait.class))
94103
.build()
95104
);
96105
}
@@ -137,8 +146,11 @@ public void writeAdditionalFiles(
137146
private static boolean areAllOptionalAuthOperations(Model model, ServiceShape service) {
138147
TopDownIndex topDownIndex = TopDownIndex.of(model);
139148
Set<OperationShape> operations = topDownIndex.getContainedOperations(service);
149+
ServiceIndex index = ServiceIndex.of(model);
150+
140151
for (OperationShape operation : operations) {
141-
if (!operation.hasTrait(OptionalAuthTrait.class)) {
152+
if (index.getEffectiveAuthSchemes(service, operation).isEmpty()
153+
|| !operation.hasTrait(OptionalAuthTrait.class)) {
142154
return false;
143155
}
144156
}

smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/integration/AddHttpApiKeyAuthPluginTest.java

Lines changed: 28 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -30,70 +30,25 @@
3030

3131
public class AddHttpApiKeyAuthPluginTest {
3232
@Test
33-
public void httpApiKeyAuthClient() {
34-
MockManifest manifest = new MockManifest();
35-
PluginContext context = PluginContext.builder()
36-
.pluginClassLoader(getClass().getClassLoader())
37-
.model(Model.assembler()
38-
.addImport(getClass().getResource("http-api-key-auth-trait.smithy"))
39-
.discoverModels()
40-
.assemble()
41-
.unwrap())
42-
.fileManifest(manifest)
43-
.settings(Node.objectNodeBuilder()
44-
.withMember("service", Node.from("smithy.example#Example"))
45-
.withMember("package", Node.from("example"))
46-
.withMember("packageVersion", Node.from("1.0.0"))
47-
.build())
48-
.build();
49-
50-
new TypeScriptCodegenPlugin().execute(context);
51-
52-
// Ensure that the client imports the config properly and calls the resolve function.
53-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/ExampleClient.ts").get(),
54-
containsString("from \"./middleware/HttpApiKeyAuth\""));
55-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/ExampleClient.ts").get(),
56-
containsString("= resolveHttpApiKeyAuthConfig("));
57-
58-
// Ensure that the GetFoo operation imports the middleware and uses it with all the options.
59-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetFooCommand.ts").get(),
60-
containsString("from \"../middleware/HttpApiKeyAuth\""));
61-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetFooCommand.ts").get(),
62-
containsString("this.middlewareStack.use(getHttpApiKeyAuthPlugin(configuration, { scheme: 'ApiKey', "
63-
+ "in: 'header', name: 'Authorization'}));"));
64-
65-
// Ensure that the GetBar operation does not import the middleware or use it.
66-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetBarCommand.ts").get(),
67-
not(containsString("from \"../middleware/HttpApiKeyAuth\"")));
68-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetBarCommand.ts").get(),
69-
not(containsString("this.middlewareStack.use(getHttpApiKeyAuthPlugin")));
33+
public void httpApiKeyAuthClientOnService() {
34+
testInjects("http-api-key-auth-trait.smithy", ", { scheme: 'ApiKey', in: 'header', name: 'Authorization'}");
35+
}
7036

71-
// Make sure that the middleware file was written and exports the plugin symbol.
72-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/middleware/HttpApiKeyAuth/index.ts").get(),
73-
containsString("export const getHttpApiKeyAuthPlugin"));
37+
@Test
38+
public void httpApiKeyAuthClientOnOperation() {
39+
testInjects("http-api-key-auth-trait-on-operation.smithy",
40+
", { scheme: 'ApiKey', in: 'header', name: 'Authorization'}");
7441
}
7542

7643
// This should be identical to the httpApiKeyAuthClient test except for the parameters provided
7744
// to the middleware.
7845
@Test
7946
public void httpApiKeyAuthClientNoScheme() {
80-
MockManifest manifest = new MockManifest();
81-
PluginContext context = PluginContext.builder()
82-
.pluginClassLoader(getClass().getClassLoader())
83-
.model(Model.assembler()
84-
.addImport(getClass().getResource("http-api-key-auth-trait-no-scheme.smithy"))
85-
.discoverModels()
86-
.assemble()
87-
.unwrap())
88-
.fileManifest(manifest)
89-
.settings(Node.objectNodeBuilder()
90-
.withMember("service", Node.from("smithy.example#Example"))
91-
.withMember("package", Node.from("example"))
92-
.withMember("packageVersion", Node.from("1.0.0"))
93-
.build())
94-
.build();
47+
testInjects("http-api-key-auth-trait-no-scheme.smithy", ", { in: 'header', name: 'Authorization'}");
48+
}
9549

96-
new TypeScriptCodegenPlugin().execute(context);
50+
private void testInjects(String filename, String extra) {
51+
MockManifest manifest = generate(filename);
9752

9853
// Ensure that the client imports the config properly and calls the resolve function.
9954
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/ExampleClient.ts").get(),
@@ -105,8 +60,7 @@ public void httpApiKeyAuthClientNoScheme() {
10560
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetFooCommand.ts").get(),
10661
containsString("from \"../middleware/HttpApiKeyAuth\""));
10762
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetFooCommand.ts").get(),
108-
containsString("this.middlewareStack.use(getHttpApiKeyAuthPlugin(configuration, { "
109-
+ "in: 'header', name: 'Authorization'}));"));
63+
containsString("this.middlewareStack.use(getHttpApiKeyAuthPlugin(configuration" + extra + "));"));
11064

11165
// Ensure that the GetBar operation does not import the middleware or use it.
11266
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetBarCommand.ts").get(),
@@ -119,13 +73,13 @@ public void httpApiKeyAuthClientNoScheme() {
11973
containsString("export const getHttpApiKeyAuthPlugin"));
12074
}
12175

122-
@Test
123-
public void notAnHttpApiKeyAuthClient() {
76+
private MockManifest generate(String filename)
77+
{
12478
MockManifest manifest = new MockManifest();
12579
PluginContext context = PluginContext.builder()
12680
.pluginClassLoader(getClass().getClassLoader())
12781
.model(Model.assembler()
128-
.addImport(getClass().getResource("endpoint-trait.smithy"))
82+
.addImport(getClass().getResource(filename))
12983
.discoverModels()
13084
.assemble()
13185
.unwrap())
@@ -139,43 +93,25 @@ public void notAnHttpApiKeyAuthClient() {
13993

14094
new TypeScriptCodegenPlugin().execute(context);
14195

142-
// Make sure that the config and middleware were not added to the client.
143-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/ExampleClient.ts").get(),
144-
not(containsString("= resolveHttpApiKeyAuthConfig(")));
145-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/ExampleClient.ts").get(),
146-
not(containsString("from \"./middleware/HttpApiKeyAuth\"")));
147-
148-
// Make sure that the import and middleware were not used in the operation.
149-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetFooCommand.ts").get(),
150-
not(containsString("from \"../middleware/HttpApiKeyAuth\"")));
151-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/commands/GetFooCommand.ts").get(),
152-
not(containsString("this.middlewareStack.use(getHttpApiKeyAuthPlugin(configuration")));
96+
return manifest;
97+
}
15398

154-
// Make sure that the middleware file was not written.
155-
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/middleware/HttpApiKeyAuth/index.ts")
156-
.isPresent(),
157-
is(false));
99+
@Test
100+
public void notAnHttpApiKeyAuthClient() {
101+
testDoesNotInject("endpoint-trait.smithy");
158102
}
159103

160104
@Test
161105
public void httpApiKeyAuthClientWithAllOptionalAuthOperations() {
162-
MockManifest manifest = new MockManifest();
163-
PluginContext context = PluginContext.builder()
164-
.pluginClassLoader(getClass().getClassLoader())
165-
.model(Model.assembler()
166-
.addImport(getClass().getResource("http-api-key-auth-trait-unused.smithy"))
167-
.discoverModels()
168-
.assemble()
169-
.unwrap())
170-
.fileManifest(manifest)
171-
.settings(Node.objectNodeBuilder()
172-
.withMember("service", Node.from("smithy.example#Example"))
173-
.withMember("package", Node.from("example"))
174-
.withMember("packageVersion", Node.from("1.0.0"))
175-
.build())
176-
.build();
106+
testDoesNotInject("http-api-key-auth-trait-all-optional.smithy");
107+
}
108+
@Test
109+
public void httpApiKeyAuthClientWithNoAuthTraits() {
110+
testDoesNotInject("http-api-key-auth-trait-unused.smithy");
111+
}
177112

178-
new TypeScriptCodegenPlugin().execute(context);
113+
private void testDoesNotInject(String filename) {
114+
MockManifest manifest = generate(filename);
179115

180116
// Make sure that the config and middleware were not added to the client.
181117
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/ExampleClient.ts").get(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace smithy.example
2+
3+
// This test input is used to validate that the generated client properly injects
4+
// the HTTP API key authentication middleware with the correct options.
5+
//
6+
// In this case the operations all have the `@optionalAuth` trait, so the middleware
7+
// should not be injected at all.
8+
9+
@httpApiKeyAuth(in: "header", name: "Authorization", scheme: "ApiKey")
10+
service Example {
11+
version: "2019-10-15",
12+
operations: [GetFoo, GetBar]
13+
}
14+
15+
@optionalAuth
16+
operation GetFoo {
17+
input: GetFooInput,
18+
output: GetFooOutput,
19+
errors: [GetFooError]
20+
}
21+
22+
structure GetFooInput {}
23+
structure GetFooOutput {}
24+
25+
@error("client")
26+
structure GetFooError {}
27+
28+
@optionalAuth
29+
operation GetBar {
30+
input: GetBarInput,
31+
output: GetBarOutput,
32+
errors: [GetBarError]
33+
}
34+
35+
structure GetBarInput {}
36+
structure GetBarOutput {}
37+
38+
@error("client")
39+
structure GetBarError {}

smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/integration/http-api-key-auth-trait-no-scheme.smithy

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
namespace smithy.example
22

3+
// This test input is used to validate that the generated client properly injects
4+
// the HTTP API key authentication middleware with the correct options.
5+
//
6+
// In this case the options should not include the `scheme` attribute.
7+
38
@httpApiKeyAuth(in: "header", name: "Authorization")
9+
@auth([httpApiKeyAuth])
410
service Example {
511
version: "2019-10-15",
612
operations: [GetFoo, GetBar]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace smithy.example
2+
3+
// This test input is used to validate that the generated client properly injects
4+
// the HTTP API key authentication middleware with the correct options.
5+
//
6+
// In this scenario the `@auth` trait is on the operation rather than the service.
7+
8+
@httpApiKeyAuth(in: "header", name: "Authorization", scheme: "ApiKey")
9+
service Example {
10+
version: "2019-10-15",
11+
operations: [GetFoo, GetBar]
12+
}
13+
14+
@auth([httpApiKeyAuth])
15+
operation GetFoo {
16+
input: GetFooInput,
17+
output: GetFooOutput,
18+
errors: [GetFooError]
19+
}
20+
21+
structure GetFooInput {}
22+
structure GetFooOutput {}
23+
24+
@error("client")
25+
structure GetFooError {}
26+
27+
@optionalAuth
28+
operation GetBar {
29+
input: GetBarInput,
30+
output: GetBarOutput,
31+
errors: [GetBarError]
32+
}
33+
34+
structure GetBarInput {}
35+
structure GetBarOutput {}
36+
37+
@error("client")
38+
structure GetBarError {}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
namespace smithy.example
22

3+
// This test input is used to validate that the generated client properly injects
4+
// the HTTP API key authentication middleware with the correct options.
5+
//
6+
// In this scenario the `@httpApiKeyAuth` trait is specified on the service, but
7+
// since there is no `@auth` trait on the service or on the operations, the HTTP
8+
// API key auth is unused, so the middleware should not be injected at all.
9+
310
@httpApiKeyAuth(in: "header", name: "Authorization", scheme: "ApiKey")
411
service Example {
512
version: "2019-10-15",
613
operations: [GetFoo, GetBar]
714
}
815

9-
@optionalAuth
1016
operation GetFoo {
1117
input: GetFooInput,
1218
output: GetFooOutput,
@@ -18,16 +24,3 @@ structure GetFooOutput {}
1824

1925
@error("client")
2026
structure GetFooError {}
21-
22-
@optionalAuth
23-
operation GetBar {
24-
input: GetBarInput,
25-
output: GetBarOutput,
26-
errors: [GetBarError]
27-
}
28-
29-
structure GetBarInput {}
30-
structure GetBarOutput {}
31-
32-
@error("client")
33-
structure GetBarError {}

smithy-typescript-codegen/src/test/resources/software/amazon/smithy/typescript/codegen/integration/http-api-key-auth-trait.smithy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
namespace smithy.example
22

3+
// This test input is used to validate that the generated client properly injects
4+
// the HTTP API key authentication middleware with the correct options.
5+
//
6+
// In this scenario the `@auth` trait is on the service and therefore applies to
7+
// all operations except operations with the `@optionalAuth` trait.
8+
39
@httpApiKeyAuth(in: "header", name: "Authorization", scheme: "ApiKey")
10+
@auth([httpApiKeyAuth])
411
service Example {
512
version: "2019-10-15",
613
operations: [GetFoo, GetBar]

0 commit comments

Comments
 (0)