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 ;
45
46
*/
46
47
@ SmithyInternalApi
47
48
final class RuntimeConfigGenerator {
48
-
49
49
private final TypeScriptSettings settings ;
50
50
private final Model model ;
51
51
private final ServiceShape service ;
@@ -208,50 +208,57 @@ 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 > targetAuthSchemes = getTargetAuthSchemes (target , authIndex , serviceIndex );
226
+
227
+ // Generate only if the "inherited" target is different than the current target
228
+ Map <ShapeId , HttpAuthScheme > inheritedAuthSchemes = Collections .emptyMap ();
229
+ LanguageTarget inherited = LanguageTarget .SHARED ;
230
+ // Always generated the SHARED target
231
+ if (target .equals (LanguageTarget .SHARED )) {
232
+ // no-op
233
+ // NODE and BROWSER inherit from SHARED
234
+ } else if (target .equals (LanguageTarget .NODE ) || target .equals (LanguageTarget .BROWSER )) {
235
+ inheritedAuthSchemes = getTargetAuthSchemes (LanguageTarget .SHARED , authIndex , serviceIndex );
236
+ // REACT_NATIVE inherits from BROWSER
237
+ } else if (target .equals (LanguageTarget .REACT_NATIVE )) {
238
+ inheritedAuthSchemes = getTargetAuthSchemes (LanguageTarget .BROWSER , authIndex , serviceIndex );
239
+ inherited = LanguageTarget .BROWSER ;
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 (areAuthSchemesTargetEqual (targetAuthSchemes , target , inheritedAuthSchemes , inherited )) {
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
- });
248
+
237
249
// feat(experimentalIdentityAndAuth): write the default IdentityProviderConfiguration with configured
238
250
// HttpAuthSchemes
239
251
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 ();
252
+ w .openBlock ("[" , "]" , () -> {
253
+ Iterator <Entry <ShapeId , HttpAuthScheme >> iter = targetAuthSchemes .entrySet ().iterator ();
245
254
while (iter .hasNext ()) {
246
255
Entry <ShapeId , HttpAuthScheme > entry = iter .next ();
247
- // Default IdentityProvider or HttpSigner, otherwise write {@code never} to force a TypeScript
248
- // compilation failure.
249
256
Consumer <TypeScriptWriter > defaultIdentityProvider = entry .getValue ()
250
257
.getDefaultIdentityProviders ()
251
- .getOrDefault (target , AuthUtils . DEFAULT_NEVER_WRITER );
258
+ .get (target );
252
259
Consumer <TypeScriptWriter > defaultSigner = entry .getValue ()
253
260
.getDefaultSigners ()
254
- .getOrDefault (target , AuthUtils . DEFAULT_NEVER_WRITER );
261
+ .get (target );
255
262
w .write ("""
256
263
{
257
264
schemeId: $S,
@@ -269,6 +276,74 @@ private void generateHttpAuthSchemeConfig(
269
276
});
270
277
}
271
278
279
+ /**
280
+ * Get auth schemes for a {@link LanguageTarget}.
281
+ *
282
+ * Criteria for an auth scheme is:
283
+ * - Auth scheme must be registered
284
+ * - Identity Provider must be provided for target
285
+ * - Signer must be provided for target
286
+ *
287
+ * @param target target platform
288
+ * @param authIndex supported auth scheme index
289
+ * @param serviceIndex service index
290
+ * @return returns effective auth schemes for a particular target.
291
+ */
292
+ private Map <ShapeId , HttpAuthScheme > getTargetAuthSchemes (
293
+ LanguageTarget target ,
294
+ SupportedHttpAuthSchemesIndex authIndex ,
295
+ ServiceIndex serviceIndex
296
+ ) {
297
+ return AuthUtils .getAllEffectiveNoAuthAwareAuthSchemes (this .service , serviceIndex , authIndex )
298
+ .entrySet ()
299
+ .stream ()
300
+ .filter (e -> e .getValue () != null
301
+ && e .getValue ().getDefaultIdentityProviders ().get (target ) != null
302
+ && e .getValue ().getDefaultSigners ().get (target ) != null )
303
+ .collect (Collectors .toMap (Entry ::getKey , Entry ::getValue ));
304
+ }
305
+
306
+ /**
307
+ * Checks if auth schemes are equal based on {@link LanguageTarget}.
308
+ *
309
+ * Criteria for equivalent auth schemes are:
310
+ *
311
+ * - Must have the same auth schemes registered
312
+ * - Registered auth scheme targets must have equivalent identity providers
313
+ * - Registered auth scheme targets must have equivalent signers
314
+ *
315
+ * @param targetAuthSchemes target auth schemes
316
+ * @param target target platform
317
+ * @param inheritedAuthSchemes inherited auth schemes
318
+ * @param inherited inherited platform
319
+ * @return if auth schemes are equal.
320
+ */
321
+ private boolean areAuthSchemesTargetEqual (
322
+ Map <ShapeId , HttpAuthScheme > targetAuthSchemes ,
323
+ LanguageTarget target ,
324
+ Map <ShapeId , HttpAuthScheme > inheritedAuthSchemes ,
325
+ LanguageTarget inherited
326
+ ) {
327
+ if (targetAuthSchemes .size () != inheritedAuthSchemes .size ()) {
328
+ return false ;
329
+ }
330
+ for (var targetEntry : targetAuthSchemes .entrySet ()) {
331
+ var inheritedAuthScheme = inheritedAuthSchemes .get (targetEntry .getKey ());
332
+ if (inheritedAuthScheme == null ) {
333
+ return false ;
334
+ }
335
+ if (targetEntry .getValue ().getDefaultIdentityProviders ().get (target )
336
+ != inheritedAuthScheme .getDefaultIdentityProviders ().get (inherited )) {
337
+ return false ;
338
+ }
339
+ if (targetEntry .getValue ().getDefaultSigners ().get (target )
340
+ != inheritedAuthScheme .getDefaultSigners ().get (inherited )) {
341
+ return false ;
342
+ }
343
+ }
344
+ return true ;
345
+ }
346
+
272
347
private Map <String , Consumer <TypeScriptWriter >> getDefaultRuntimeConfigs (LanguageTarget target ) {
273
348
switch (target ) {
274
349
case NODE :
0 commit comments