16
16
package software .amazon .smithy .typescript .codegen ;
17
17
18
18
import java .nio .file .Paths ;
19
+ import java .util .Collections ;
19
20
import java .util .Iterator ;
20
21
import java .util .List ;
21
22
import java .util .Map ;
22
- import java .util .Map . Entry ;
23
+ import java .util .Objects ;
23
24
import java .util .TreeMap ;
24
25
import java .util .function .Consumer ;
25
- import java .util .stream .Collectors ;
26
26
import software .amazon .smithy .build .SmithyBuildException ;
27
+ import software .amazon .smithy .codegen .core .CodegenException ;
27
28
import software .amazon .smithy .codegen .core .SymbolProvider ;
28
29
import software .amazon .smithy .model .Model ;
29
30
import software .amazon .smithy .model .knowledge .ServiceIndex ;
30
31
import software .amazon .smithy .model .shapes .ServiceShape ;
31
32
import software .amazon .smithy .model .shapes .ShapeId ;
32
- import software .amazon .smithy .model .traits .Trait ;
33
33
import software .amazon .smithy .typescript .codegen .auth .AuthUtils ;
34
34
import software .amazon .smithy .typescript .codegen .auth .http .HttpAuthScheme ;
35
35
import software .amazon .smithy .typescript .codegen .auth .http .HttpAuthSchemeProviderGenerator ;
@@ -208,59 +208,76 @@ private void generateHttpAuthSchemeConfig(
208
208
TypeScriptWriter writer ,
209
209
LanguageTarget target
210
210
) {
211
+ // feat(experimentalIdentityAndAuth): write the default imported HttpAuthSchemeProvider
212
+ if (target .equals (LanguageTarget .SHARED )) {
213
+ configs .put ("httpAuthSchemeProvider" , w -> {
214
+ String providerName = "default" + service .toShapeId ().getName () + "HttpAuthSchemeProvider" ;
215
+ w .addRelativeImport (providerName , null , Paths .get ("." , CodegenUtils .SOURCE_FOLDER ,
216
+ HttpAuthSchemeProviderGenerator .HTTP_AUTH_FOLDER ,
217
+ HttpAuthSchemeProviderGenerator .HTTP_AUTH_SCHEME_RESOLVER_MODULE ));
218
+ w .write (providerName );
219
+ });
220
+ }
221
+
211
222
// feat(experimentalIdentityAndAuth): gather HttpAuthSchemes to generate
212
- SupportedHttpAuthSchemesIndex index = new SupportedHttpAuthSchemesIndex (integrations );
223
+ SupportedHttpAuthSchemesIndex authIndex = new SupportedHttpAuthSchemesIndex (integrations );
213
224
ServiceIndex serviceIndex = ServiceIndex .of (model );
214
- Map <ShapeId , Trait > authSchemeTraits = serviceIndex .getAuthSchemes (service );
215
- Map <ShapeId , HttpAuthScheme > authSchemes = authSchemeTraits .entrySet ().stream ()
216
- .filter (entry -> index .getHttpAuthScheme (entry .getKey ()) != null )
217
- .collect (Collectors .toMap (
218
- entry -> entry .getKey (),
219
- entry -> index .getHttpAuthScheme (entry .getKey ())));
220
- // feat(experimentalIdentityAndAuth): can be removed after changing to NO_AUTH_AWARE schemes
221
- // The default set of HttpAuthSchemes is @smithy.api#noAuth on the shared runtime config
222
- authSchemes .put (AuthUtils .NO_AUTH_ID , index .getHttpAuthScheme (AuthUtils .NO_AUTH_ID ));
223
- boolean shouldGenerateHttpAuthSchemes = index .getSupportedHttpAuthSchemes ().values ().stream ().anyMatch (value ->
224
- // If either an default identity or signer is configured for the target, generate auth schemes
225
- value .getDefaultIdentityProviders ().containsKey (target ) || value .getDefaultSigners ().containsKey (target ));
226
- if (!shouldGenerateHttpAuthSchemes ) {
225
+ Map <ShapeId , HttpAuthScheme > allEffectiveHttpAuthSchemes =
226
+ AuthUtils .getAllEffectiveNoAuthAwareAuthSchemes (service , serviceIndex , authIndex );
227
+ List <HttpAuthSchemeTarget > targetAuthSchemes = getHttpAuthSchemeTargets (target , allEffectiveHttpAuthSchemes );
228
+
229
+ // Generate only if the "inherited" target is different than the current target
230
+ List <HttpAuthSchemeTarget > inheritedAuthSchemes = Collections .emptyList ();
231
+ // Always generated the SHARED target
232
+ if (target .equals (LanguageTarget .SHARED )) {
233
+ // no-op
234
+ // NODE and BROWSER inherit from SHARED
235
+ } else if (target .equals (LanguageTarget .NODE ) || target .equals (LanguageTarget .BROWSER )) {
236
+ inheritedAuthSchemes = getHttpAuthSchemeTargets (LanguageTarget .SHARED , allEffectiveHttpAuthSchemes );
237
+ // REACT_NATIVE inherits from BROWSER
238
+ } else if (target .equals (LanguageTarget .REACT_NATIVE )) {
239
+ inheritedAuthSchemes = getHttpAuthSchemeTargets (LanguageTarget .BROWSER , allEffectiveHttpAuthSchemes );
240
+ } else {
241
+ throw new CodegenException ("Unhandled Language Target: " + target );
242
+ }
243
+
244
+ // If target and inherited auth schemes are equal, then don't generate target auth schemes.
245
+ if (targetAuthSchemes .equals (inheritedAuthSchemes )) {
227
246
return ;
228
247
}
229
- // feat(experimentalIdentityAndAuth): write the default imported HttpAuthSchemeProvider
230
- configs .put ("httpAuthSchemeProvider" , w -> {
231
- String providerName = "default" + service .toShapeId ().getName () + "HttpAuthSchemeProvider" ;
232
- w .addRelativeImport (providerName , null , Paths .get ("." , CodegenUtils .SOURCE_FOLDER ,
233
- HttpAuthSchemeProviderGenerator .HTTP_AUTH_FOLDER ,
234
- HttpAuthSchemeProviderGenerator .HTTP_AUTH_SCHEME_RESOLVER_MODULE ));
235
- w .write (providerName );
236
- });
237
- // feat(experimentalIdentityAndAuth): write the default IdentityProviderConfiguration with configured
238
- // HttpAuthSchemes
248
+
249
+ // feat(experimentalIdentityAndAuth): write the default httpAuthSchemes
239
250
configs .put ("httpAuthSchemes" , w -> {
240
251
w .addDependency (TypeScriptDependency .EXPERIMENTAL_IDENTITY_AND_AUTH );
241
- w .addImport ("DefaultIdentityProviderConfiguration" , null ,
242
- TypeScriptDependency .EXPERIMENTAL_IDENTITY_AND_AUTH );
243
- w .openBlock ("new DefaultIdentityProviderConfiguration([" , "])" , () -> {
244
- Iterator <Entry <ShapeId , HttpAuthScheme >> iter = authSchemes .entrySet ().iterator ();
252
+ w .addImport ("IdentityProviderConfig" , null , TypeScriptDependency .EXPERIMENTAL_IDENTITY_AND_AUTH );
253
+ w .openBlock ("[" , "]" , () -> {
254
+ Iterator <HttpAuthSchemeTarget > iter = targetAuthSchemes .iterator ();
245
255
while (iter .hasNext ()) {
246
- Entry <ShapeId , HttpAuthScheme > entry = iter .next ();
247
- // Default IdentityProvider or HttpSigner, otherwise write {@code never} to force a TypeScript
248
- // compilation failure.
249
- Consumer <TypeScriptWriter > defaultIdentityProvider = entry .getValue ()
250
- .getDefaultIdentityProviders ()
251
- .getOrDefault (target , AuthUtils .DEFAULT_NEVER_WRITER );
252
- Consumer <TypeScriptWriter > defaultSigner = entry .getValue ()
253
- .getDefaultSigners ()
254
- .getOrDefault (target , AuthUtils .DEFAULT_NEVER_WRITER );
255
- w .write ("""
256
- {
257
- schemeId: $S,
258
- identityProvider: $C,
259
- signer: $C,
260
- }""" ,
261
- entry .getKey (),
262
- defaultIdentityProvider ,
263
- defaultSigner );
256
+ HttpAuthSchemeTarget entry = iter .next ();
257
+ if (entry .identityProvider == null ) {
258
+ w .write ("""
259
+ {
260
+ schemeId: $S,
261
+ identityProvider: (config: IdentityProviderConfig) =>
262
+ config.getIdentityProvider($S),
263
+ signer: $C,
264
+ }""" ,
265
+ entry .httpAuthScheme .getSchemeId (),
266
+ entry .httpAuthScheme .getSchemeId (),
267
+ entry .signer );
268
+ } else {
269
+ w .write ("""
270
+ {
271
+ schemeId: $S,
272
+ identityProvider: (config: IdentityProviderConfig) =>
273
+ config.getIdentityProvider($S) || ($C),
274
+ signer: $C,
275
+ }""" ,
276
+ entry .httpAuthScheme .getSchemeId (),
277
+ entry .httpAuthScheme .getSchemeId (),
278
+ entry .identityProvider ,
279
+ entry .signer );
280
+ }
264
281
if (iter .hasNext ()) {
265
282
w .writeInline (", " );
266
283
}
@@ -269,6 +286,104 @@ private void generateHttpAuthSchemeConfig(
269
286
});
270
287
}
271
288
289
+ private static class HttpAuthSchemeTarget {
290
+ public HttpAuthScheme httpAuthScheme ;
291
+ public Consumer <TypeScriptWriter > identityProvider ;
292
+ public Consumer <TypeScriptWriter > signer ;
293
+
294
+ HttpAuthSchemeTarget (
295
+ HttpAuthScheme httpAuthScheme ,
296
+ Consumer <TypeScriptWriter > identityProvider ,
297
+ Consumer <TypeScriptWriter > signer
298
+ ) {
299
+ this .httpAuthScheme = httpAuthScheme ;
300
+ this .identityProvider = identityProvider ;
301
+ this .signer = signer ;
302
+ }
303
+
304
+ @ Override
305
+ public boolean equals (Object other ) {
306
+ if (!(other instanceof HttpAuthSchemeTarget )) {
307
+ return false ;
308
+ }
309
+ HttpAuthSchemeTarget o = (HttpAuthSchemeTarget ) other ;
310
+ return Objects .equals (httpAuthScheme , o .httpAuthScheme )
311
+ && Objects .equals (identityProvider , o .identityProvider )
312
+ && Objects .equals (signer , o .signer );
313
+ }
314
+
315
+ @ Override
316
+ public int hashCode () {
317
+ return super .hashCode ();
318
+ }
319
+ }
320
+
321
+ private List <HttpAuthSchemeTarget > getHttpAuthSchemeTargets (
322
+ LanguageTarget target ,
323
+ Map <ShapeId , HttpAuthScheme > httpAuthSchemes
324
+ ) {
325
+ return getPartialHttpAuthSchemeTargets (target , httpAuthSchemes )
326
+ .values ()
327
+ .stream ()
328
+ .filter (httpAuthSchemeTarget -> httpAuthSchemeTarget .signer != null )
329
+ .toList ();
330
+ }
331
+
332
+ private Map <ShapeId , HttpAuthSchemeTarget > getPartialHttpAuthSchemeTargets (
333
+ LanguageTarget target ,
334
+ Map <ShapeId , HttpAuthScheme > httpAuthSchemes
335
+ ) {
336
+ LanguageTarget inherited ;
337
+ if (target .equals (LanguageTarget .SHARED )) {
338
+ // SHARED doesn't inherit any target, so inherited is null
339
+ inherited = null ;
340
+ } else if (target .equals (LanguageTarget .NODE ) || target .equals (LanguageTarget .BROWSER )) {
341
+ inherited = LanguageTarget .SHARED ;
342
+ } else if (target .equals (LanguageTarget .REACT_NATIVE )) {
343
+ inherited = LanguageTarget .BROWSER ;
344
+ } else {
345
+ throw new CodegenException ("Unsupported Language Target: " + target );
346
+ }
347
+
348
+ Map <ShapeId , HttpAuthSchemeTarget > httpAuthSchemeTargets = inherited == null
349
+ // SHARED inherits no HttpAuthSchemeTargets
350
+ ? new TreeMap <>()
351
+ // Otherwise, get inherited HttpAuthSchemeTargets
352
+ : getPartialHttpAuthSchemeTargets (inherited , httpAuthSchemes );
353
+
354
+ for (HttpAuthScheme httpAuthScheme : httpAuthSchemes .values ()) {
355
+ // If HttpAuthScheme is not registered, skip code generation
356
+ if (httpAuthScheme == null ) {
357
+ continue ;
358
+ }
359
+
360
+ // Get identity provider and signer for the current target
361
+ Consumer <TypeScriptWriter > identityProvider =
362
+ httpAuthScheme .getDefaultIdentityProviders ().get (target );
363
+ Consumer <TypeScriptWriter > signer =
364
+ httpAuthScheme .getDefaultSigners ().get (target );
365
+
366
+ HttpAuthSchemeTarget existingEntry =
367
+ httpAuthSchemeTargets .get (httpAuthScheme .getSchemeId ());
368
+
369
+ // If HttpAuthScheme is not added yet, add the entry
370
+ if (existingEntry == null ) {
371
+ httpAuthSchemeTargets .put (httpAuthScheme .getSchemeId (),
372
+ new HttpAuthSchemeTarget (httpAuthScheme , identityProvider , signer ));
373
+ continue ;
374
+ }
375
+
376
+ // Mutate existing entry for identity provider and signer if available
377
+ if (identityProvider != null ) {
378
+ existingEntry .identityProvider = identityProvider ;
379
+ }
380
+ if (signer != null ) {
381
+ existingEntry .signer = signer ;
382
+ }
383
+ }
384
+ return httpAuthSchemeTargets ;
385
+ }
386
+
272
387
private Map <String , Consumer <TypeScriptWriter >> getDefaultRuntimeConfigs (LanguageTarget target ) {
273
388
switch (target ) {
274
389
case NODE :
0 commit comments