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 ;
24
25
import java .util .function .Consumer ;
25
26
import java .util .stream .Collectors ;
26
27
import software .amazon .smithy .build .SmithyBuildException ;
28
+ import software .amazon .smithy .codegen .core .CodegenException ;
27
29
import software .amazon .smithy .codegen .core .SymbolProvider ;
28
30
import software .amazon .smithy .model .Model ;
29
31
import software .amazon .smithy .model .knowledge .ServiceIndex ;
30
32
import software .amazon .smithy .model .shapes .ServiceShape ;
31
33
import software .amazon .smithy .model .shapes .ShapeId ;
32
- import software .amazon .smithy .model .traits .Trait ;
33
34
import software .amazon .smithy .typescript .codegen .auth .AuthUtils ;
34
35
import software .amazon .smithy .typescript .codegen .auth .http .HttpAuthScheme ;
35
36
import software .amazon .smithy .typescript .codegen .auth .http .HttpAuthSchemeProviderGenerator ;
@@ -208,50 +209,57 @@ private void generateHttpAuthSchemeConfig(
208
209
TypeScriptWriter writer ,
209
210
LanguageTarget target
210
211
) {
212
+ // feat(experimentalIdentityAndAuth): write the default imported HttpAuthSchemeProvider
213
+ if (target .equals (LanguageTarget .SHARED )) {
214
+ configs .put ("httpAuthSchemeProvider" , w -> {
215
+ String providerName = "default" + service .toShapeId ().getName () + "HttpAuthSchemeProvider" ;
216
+ w .addRelativeImport (providerName , null , Paths .get ("." , CodegenUtils .SOURCE_FOLDER ,
217
+ HttpAuthSchemeProviderGenerator .HTTP_AUTH_FOLDER ,
218
+ HttpAuthSchemeProviderGenerator .HTTP_AUTH_SCHEME_RESOLVER_MODULE ));
219
+ w .write (providerName );
220
+ });
221
+ }
222
+
211
223
// feat(experimentalIdentityAndAuth): gather HttpAuthSchemes to generate
212
- SupportedHttpAuthSchemesIndex index = new SupportedHttpAuthSchemesIndex (integrations );
224
+ SupportedHttpAuthSchemesIndex authIndex = new SupportedHttpAuthSchemesIndex (integrations );
213
225
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 ) {
226
+ Map <ShapeId , HttpAuthScheme > targetAuthSchemes = getTargetAuthSchemes (target , authIndex , serviceIndex );
227
+
228
+ // Generate only if the "inherited" target is different than the current target
229
+ Map <ShapeId , HttpAuthScheme > inheritedAuthSchemes = Collections .emptyMap ();
230
+ LanguageTarget inherited = LanguageTarget .SHARED ;
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 = getTargetAuthSchemes (LanguageTarget .SHARED , authIndex , serviceIndex );
237
+ // REACT_NATIVE inherits from BROWSER
238
+ } else if (target .equals (LanguageTarget .REACT_NATIVE )) {
239
+ inheritedAuthSchemes = getTargetAuthSchemes (LanguageTarget .BROWSER , authIndex , serviceIndex );
240
+ inherited = LanguageTarget .BROWSER ;
241
+ } else {
242
+ throw new CodegenException ("Unhandled Language Target: " + target );
243
+ }
244
+
245
+ // If target and inherited auth schemes are equal, then don't generate target auth schemes.
246
+ if (areAuthSchemesTargetEqual (targetAuthSchemes , target , inheritedAuthSchemes , inherited )) {
227
247
return ;
228
248
}
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
- });
249
+
237
250
// feat(experimentalIdentityAndAuth): write the default IdentityProviderConfiguration with configured
238
251
// HttpAuthSchemes
239
252
configs .put ("httpAuthSchemes" , w -> {
240
- 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 ();
253
+ w .openBlock ("[" , "]" , () -> {
254
+ Iterator <Entry <ShapeId , HttpAuthScheme >> iter = targetAuthSchemes .entrySet ().iterator ();
245
255
while (iter .hasNext ()) {
246
256
Entry <ShapeId , HttpAuthScheme > entry = iter .next ();
247
- // Default IdentityProvider or HttpSigner, otherwise write {@code never} to force a TypeScript
248
- // compilation failure.
249
257
Consumer <TypeScriptWriter > defaultIdentityProvider = entry .getValue ()
250
258
.getDefaultIdentityProviders ()
251
- .getOrDefault (target , AuthUtils . DEFAULT_NEVER_WRITER );
259
+ .get (target );
252
260
Consumer <TypeScriptWriter > defaultSigner = entry .getValue ()
253
261
.getDefaultSigners ()
254
- .getOrDefault (target , AuthUtils . DEFAULT_NEVER_WRITER );
262
+ .get (target );
255
263
w .write ("""
256
264
{
257
265
schemeId: $S,
@@ -269,6 +277,74 @@ private void generateHttpAuthSchemeConfig(
269
277
});
270
278
}
271
279
280
+ /**
281
+ * Get auth schemes for a {@link LanguageTarget}.
282
+ *
283
+ * Criteria for an auth scheme is:
284
+ * - Auth scheme must be registered
285
+ * - Identity Provider must be provided for target
286
+ * - Signer must be provided for target
287
+ *
288
+ * @param target target platform
289
+ * @param authIndex supported auth scheme index
290
+ * @param serviceIndex service index
291
+ * @return returns effective auth schemes for a particular target.
292
+ */
293
+ private Map <ShapeId , HttpAuthScheme > getTargetAuthSchemes (
294
+ LanguageTarget target ,
295
+ SupportedHttpAuthSchemesIndex authIndex ,
296
+ ServiceIndex serviceIndex
297
+ ) {
298
+ return AuthUtils .getAllEffectiveNoAuthAwareAuthSchemes (this .service , serviceIndex , authIndex )
299
+ .entrySet ()
300
+ .stream ()
301
+ .filter (e -> e .getValue () != null
302
+ && e .getValue ().getDefaultIdentityProviders ().get (target ) != null
303
+ && e .getValue ().getDefaultSigners ().get (target ) != null )
304
+ .collect (Collectors .toMap (Entry ::getKey , Entry ::getValue ));
305
+ }
306
+
307
+ /**
308
+ * Checks if auth schemes are equal based on {@link LanguageTarget}.
309
+ *
310
+ * Criteria for equivalent auth schemes are:
311
+ *
312
+ * - Must have the same auth schemes registered
313
+ * - Registered auth scheme targets must have equivalent identity providers
314
+ * - Registered auth scheme targets must have equivalent signers
315
+ *
316
+ * @param targetAuthSchemes target auth schemes
317
+ * @param target target platform
318
+ * @param inheritedAuthSchemes inherited auth schemes
319
+ * @param inherited inherited platform
320
+ * @return if auth schemes are equal.
321
+ */
322
+ private boolean areAuthSchemesTargetEqual (
323
+ Map <ShapeId , HttpAuthScheme > targetAuthSchemes ,
324
+ LanguageTarget target ,
325
+ Map <ShapeId , HttpAuthScheme > inheritedAuthSchemes ,
326
+ LanguageTarget inherited
327
+ ) {
328
+ if (targetAuthSchemes .size () != inheritedAuthSchemes .size ()) {
329
+ return false ;
330
+ }
331
+ for (var targetEntry : targetAuthSchemes .entrySet ()) {
332
+ var inheritedAuthScheme = inheritedAuthSchemes .get (targetEntry .getKey ());
333
+ if (inheritedAuthScheme == null ) {
334
+ return false ;
335
+ }
336
+ if (targetEntry .getValue ().getDefaultIdentityProviders ().get (target )
337
+ != inheritedAuthScheme .getDefaultIdentityProviders ().get (inherited )) {
338
+ return false ;
339
+ }
340
+ if (targetEntry .getValue ().getDefaultSigners ().get (target )
341
+ != inheritedAuthScheme .getDefaultSigners ().get (inherited )) {
342
+ return false ;
343
+ }
344
+ }
345
+ return true ;
346
+ }
347
+
272
348
private Map <String , Consumer <TypeScriptWriter >> getDefaultRuntimeConfigs (LanguageTarget target ) {
273
349
switch (target ) {
274
350
case NODE :
0 commit comments