@aws/nx-plugin 0.70.0 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,339 +1,5 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`cognito-auth generator (uxProvider=Cloudscape) > should update AppLayout > app-layout-with-auth 1`] = `
4
- "import { useAuth } from 'react-oidc-context';
5
- import * as React from 'react';
6
- import { createContext, useCallback, useEffect, useState } from 'react';
7
- import { NavItems } from './navitems';
8
- import Config from '../../config';
9
-
10
- import {
11
- BreadcrumbGroup,
12
- BreadcrumbGroupProps,
13
- SideNavigation,
14
- TopNavigation,
15
- } from '@cloudscape-design/components';
16
-
17
- import CloudscapeAppLayout, {
18
- AppLayoutProps,
19
- } from '@cloudscape-design/components/app-layout';
20
-
21
- import { matchByPath, useLocation, useNavigate } from '@tanstack/react-router';
22
- import { Outlet } from '@tanstack/react-router';
23
-
24
- const getBreadcrumbs = (
25
- pathName: string,
26
- search: string,
27
- defaultBreadcrumb: string,
28
- availableRoutes?: string[],
29
- ) => {
30
- const segments = [
31
- defaultBreadcrumb,
32
- ...pathName.split('/').filter((segment) => segment !== ''),
33
- ];
34
-
35
- return segments.map((segment, i) => {
36
- const href =
37
- i === 0
38
- ? '/'
39
- : \`/\${segments
40
- .slice(1, i + 1)
41
- .join('/')
42
- .replace('//', '/')}\`;
43
-
44
- const matched =
45
- !availableRoutes || availableRoutes.find((r) => matchByPath(r, href, {}));
46
-
47
- return {
48
- href: matched ? \`\${href}\${search}\` : '#',
49
- text: segment,
50
- };
51
- });
52
- };
53
-
54
- export interface AppLayoutContext {
55
- appLayoutProps: AppLayoutProps;
56
- setAppLayoutProps: (props: AppLayoutProps) => void;
57
- displayHelpPanel: (helpContent: React.ReactNode) => void;
58
- }
59
-
60
- /**
61
- * Context for updating/retrieving the AppLayout.
62
- */
63
- export const AppLayoutContext = createContext({
64
- appLayoutProps: {},
65
- // eslint-disable-next-line @typescript-eslint/no-empty-function
66
- setAppLayoutProps: (_: AppLayoutProps) => {},
67
- // eslint-disable-next-line @typescript-eslint/no-empty-function
68
- displayHelpPanel: (_: React.ReactNode) => {},
69
- });
70
-
71
- /**
72
- * Defines the App layout and contains logic for routing.
73
- */
74
- const AppLayout: React.FC = () => {
75
- const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
76
- const navigate = useNavigate();
77
- const appLayout = React.useRef<AppLayoutProps.Ref>(null);
78
- const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
79
- BreadcrumbGroupProps.Item[]
80
- >([{ text: '/', href: '/' }]);
81
- const [appLayoutProps, setAppLayoutProps] = useState<AppLayoutProps>({});
82
- const { pathname, search } = useLocation();
83
- const setAppLayoutPropsSafe = useCallback(
84
- (props: AppLayoutProps) => {
85
- JSON.stringify(appLayoutProps) !== JSON.stringify(props) &&
86
- setAppLayoutProps(props);
87
- },
88
- [appLayoutProps],
89
- );
90
- useEffect(() => {
91
- const breadcrumbs = getBreadcrumbs(
92
- pathname,
93
- Object.entries(search).reduce((p, [k, v]) => p + \`\${k}=\${v}\`, ''),
94
- '/',
95
- );
96
- setActiveBreadcrumbs(breadcrumbs);
97
- }, [pathname, search]);
98
- const onNavigate = useCallback(
99
- (
100
- e: CustomEvent<{
101
- href: string;
102
- external?: boolean;
103
- }>,
104
- ) => {
105
- if (!e.detail.external) {
106
- e.preventDefault();
107
- setAppLayoutPropsSafe({
108
- contentType: undefined,
109
- });
110
- navigate({ to: e.detail.href });
111
- }
112
- },
113
- [navigate, setAppLayoutPropsSafe],
114
- );
115
- return (
116
- <AppLayoutContext.Provider
117
- value={{
118
- appLayoutProps,
119
- setAppLayoutProps: setAppLayoutPropsSafe,
120
- displayHelpPanel: (helpContent: React.ReactNode) => {
121
- setAppLayoutPropsSafe({ tools: helpContent, toolsHide: false });
122
- appLayout.current?.openTools();
123
- },
124
- }}
125
- >
126
- <TopNavigation
127
- identity={{
128
- href: '/',
129
- title: Config.applicationName,
130
- logo: {
131
- src: Config.logo,
132
- },
133
- }}
134
- utilities={[
135
- {
136
- type: 'menu-dropdown',
137
- text: \`\${user?.profile?.['cognito:username']}\`,
138
- iconName: 'user-profile-active',
139
- onItemClick: (e) => {
140
- if (e.detail.id === 'signout') {
141
- removeUser();
142
- signoutRedirect({
143
- post_logout_redirect_uri: window.location.origin,
144
- extraQueryParams: {
145
- redirect_uri: window.location.origin,
146
- response_type: 'code',
147
- },
148
- });
149
- clearStaleState();
150
- }
151
- },
152
- items: [{ id: 'signout', text: 'Sign out' }],
153
- },
154
- ]}
155
- />
156
- <CloudscapeAppLayout
157
- ref={appLayout}
158
- breadcrumbs={
159
- <BreadcrumbGroup onFollow={onNavigate} items={activeBreadcrumbs} />
160
- }
161
- toolsHide
162
- navigation={
163
- <SideNavigation
164
- header={{ text: Config.applicationName, href: '/' }}
165
- activeHref={pathname}
166
- onFollow={onNavigate}
167
- items={NavItems}
168
- />
169
- }
170
- content={<Outlet />}
171
- {...appLayoutProps}
172
- />
173
- </AppLayoutContext.Provider>
174
- );
175
- };
176
-
177
- export default AppLayout;
178
- "
179
- `;
180
-
181
- exports[`cognito-auth generator (uxProvider=None) > should update AppLayout > app-layout-with-auth 1`] = `
182
- "import { useAuth } from 'react-oidc-context';
183
- import * as React from 'react';
184
-
185
- import { useEffect, useMemo, useState } from 'react';
186
-
187
- import Config from '../../config';
188
- import { Link, useLocation, useMatchRoute } from '@tanstack/react-router';
189
-
190
- const getBreadcrumbs = (
191
- matchRoute: ReturnType<typeof useMatchRoute>,
192
- pathName: string,
193
- search: string,
194
- defaultBreadcrumb: string,
195
- availableRoutes?: string[],
196
- ) => {
197
- const segments = [
198
- defaultBreadcrumb,
199
- ...pathName.split('/').filter((segment) => segment !== ''),
200
- ];
201
-
202
- return segments.map((segment, i) => {
203
- const href =
204
- i === 0
205
- ? '/'
206
- : \`/\${segments
207
- .slice(1, i + 1)
208
- .join('/')
209
- .replace('//', '/')}\`;
210
-
211
- const matched =
212
- !availableRoutes || availableRoutes.find((r) => matchRoute({ to: href }));
213
-
214
- return {
215
- href: matched ? \`\${href}\${search}\` : '#',
216
- text: segment,
217
- };
218
- });
219
- };
220
-
221
- /**
222
- * Defines the App layout and contains logic for routing.
223
- */
224
- const AppLayout: React.FC<React.PropsWithChildren> = ({ children }) => {
225
- const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
226
- const { user, removeUser, signoutRedirect, clearStaleState } = useAuth();
227
- const [activeBreadcrumbs, setActiveBreadcrumbs] = useState<
228
- {
229
- href: string;
230
- text: string;
231
- }[]
232
- >([{ text: '/', href: '/' }]);
233
- const matchRoute = useMatchRoute();
234
- const { pathname, search } = useLocation();
235
- const navItems = useMemo(() => [{ to: '/', label: 'Home' }], []);
236
- useEffect(() => {
237
- const breadcrumbs = getBreadcrumbs(
238
- matchRoute,
239
- pathname,
240
- Object.entries(search).reduce((p, [k, v]) => p + \`\${k}=\${v}\`, ''),
241
- 'Home',
242
- );
243
- setActiveBreadcrumbs(breadcrumbs);
244
- }, [matchRoute, pathname, search]);
245
- return (
246
- <div className="app-shell">
247
- <header className="app-header">
248
- <div className="app-header-inner">
249
- <div className="brand">
250
- <a href="/">
251
- <img
252
- src={Config.logo}
253
- alt={\`\${Config.applicationName} logo\`}
254
- className="brand-logo"
255
- />
256
- <span className="brand-name">{Config.applicationName}</span>
257
- </a>
258
- </div>
259
-
260
- <nav className="app-nav">
261
- {navItems.map((item) => (
262
- <Link
263
- key={item.to}
264
- to={item.to}
265
- className={pathname === item.to ? 'active' : undefined}
266
- >
267
- {item.label}
268
- </Link>
269
- ))}
270
- </nav>
271
- <div className="user-greeting">
272
- <span>Hi, {\`\${user?.profile?.['cognito:username']}\`}</span>
273
- <button
274
- type="button"
275
- className="signout-link"
276
- onClick={() => {
277
- removeUser();
278
- signoutRedirect({
279
- post_logout_redirect_uri: window.location.origin,
280
- extraQueryParams: {
281
- redirect_uri: window.location.origin,
282
- response_type: 'code',
283
- },
284
- });
285
- clearStaleState();
286
- }}
287
- >
288
- Sign out
289
- </button>
290
- </div>
291
- <div className="user-greeting">
292
- <span>Hi, {\`\${user?.profile?.['cognito:username']}\`}</span>
293
- <button
294
- type="button"
295
- className="signout-link"
296
- onClick={() => {
297
- removeUser();
298
- signoutRedirect({
299
- post_logout_redirect_uri: window.location.origin,
300
- extraQueryParams: {
301
- redirect_uri: window.location.origin,
302
- response_type: 'code',
303
- },
304
- });
305
- clearStaleState();
306
- }}
307
- >
308
- Sign out
309
- </button>
310
- </div>
311
- </div>
312
- </header>
313
- <main className="app-main">
314
- <nav className="breadcrumbs" aria-label="Breadcrumb">
315
- {activeBreadcrumbs.map((crumb, index) => (
316
- <span className="breadcrumb-segment" key={crumb.href || index}>
317
- {index > 0 && <span className="breadcrumb-separator">/</span>}
318
- {index === activeBreadcrumbs.length - 1 ? (
319
- <span className="breadcrumb-current">{crumb.text}</span>
320
- ) : (
321
- <Link to={crumb.href}>{crumb.text}</Link>
322
- )}
323
- </span>
324
- ))}
325
- </nav>
326
-
327
- <section className="card">{children}</section>
328
- </main>
329
- </div>
330
- );
331
- };
332
-
333
- export default AppLayout;
334
- "
335
- `;
336
-
337
3
  exports[`cognito-auth generator > should generate files > cognito-auth-component 1`] = `
338
4
  "import React, { PropsWithChildren, useEffect } from 'react';
339
5
  import { AuthProvider, AuthProviderProps, useAuth } from 'react-oidc-context';
@@ -635,261 +301,3 @@ export * from './checkov.js';
635
301
  export * from './runtime-config.js';
636
302
  "
637
303
  `;
638
-
639
- exports[`cognito-auth generator > terraform iacProvider > should generate terraform files for cognito auth and snapshot them > terraform-cognito-auth-files 1`] = `
640
- {
641
- "add-callback-url.tf": "terraform {
642
- required_providers {
643
- aws = {
644
- source = "hashicorp/aws"
645
- version = "~> 6.0"
646
- }
647
- }
648
- }
649
-
650
- # Variables
651
- variable "callback_url" {
652
- description = "Callback URL to add (e.g., https://d123456789.cloudfront.net)"
653
- type = string
654
- }
655
-
656
- # Read runtime config to get user pool client details
657
- module "runtime_config_reader" {
658
- source = "../../runtime-config/read"
659
- }
660
-
661
- # Extract cognito details from runtime config
662
- locals {
663
- cognito_props = try(module.runtime_config_reader.config.cognitoProps, null)
664
-
665
- user_pool_id = local.cognito_props != null ? local.cognito_props.userPoolId : null
666
- user_pool_client_id = local.cognito_props != null ? local.cognito_props.userPoolWebClientId : null
667
- }
668
-
669
- # Validation: Ensure cognito props exist in runtime config
670
- resource "terraform_data" "validate_cognito_props" {
671
- lifecycle {
672
- precondition {
673
- condition = local.cognito_props != null
674
- error_message = "ERROR: cognitoProps not found in runtime config. Ensure user-identity module has been deployed first and has added cognitoProps to the runtime configuration."
675
- }
676
-
677
- precondition {
678
- condition = local.user_pool_id != null && local.user_pool_id != ""
679
- error_message = "ERROR: cognitoProps.userPoolId is missing or empty in runtime config. Check that user-identity module completed successfully."
680
- }
681
-
682
- precondition {
683
- condition = local.user_pool_client_id != null && local.user_pool_client_id != ""
684
- error_message = "ERROR: cognitoProps.userPoolWebClientId is missing or empty in runtime config. Check that user-identity module completed successfully."
685
- }
686
- }
687
- }
688
-
689
-
690
- # Update the user pool client with additional callback URL
691
- resource "null_resource" "add_callback_url" {
692
- triggers = {
693
- callback_url = var.callback_url
694
- user_pool_id = local.user_pool_id
695
- user_pool_client_id = local.user_pool_client_id
696
- }
697
-
698
- provisioner "local-exec" {
699
- command = <<-EOT
700
- uv run --with boto3 python -c "
701
- import boto3
702
- import sys
703
-
704
- # Configuration
705
- user_pool_id = '\${local.user_pool_id}'
706
- client_id = '\${local.user_pool_client_id}'
707
- new_callback_url = '\${var.callback_url}'
708
-
709
- # Initialize Cognito client
710
- cognito = boto3.client('cognito-idp')
711
-
712
- try:
713
- # Get current user pool client configuration
714
- response = cognito.describe_user_pool_client(
715
- UserPoolId=user_pool_id,
716
- ClientId=client_id
717
- )
718
-
719
- client_config = response['UserPoolClient']
720
- current_callback_urls = client_config.get('CallbackURLs', [])
721
- current_logout_urls = client_config.get('LogoutURLs', [])
722
-
723
- # Check if URL already exists
724
- if new_callback_url in current_callback_urls:
725
- print(f'Callback URL {new_callback_url} already exists')
726
- else:
727
- # Add new URL to both callback and logout URLs
728
- updated_callback_urls = current_callback_urls + [new_callback_url]
729
- updated_logout_urls = current_logout_urls + [new_callback_url]
730
-
731
- # Update the user pool client
732
- # Only include valid update parameters (exclude read-only fields and ones we're setting)
733
- valid_update_params = [
734
- 'ClientName', 'RefreshTokenValidity', 'AccessTokenValidity', 'IdTokenValidity',
735
- 'TokenValidityUnits', 'ReadAttributes', 'WriteAttributes', 'ExplicitAuthFlows',
736
- 'SupportedIdentityProviders', 'DefaultRedirectURI', 'AllowedOAuthFlows',
737
- 'AllowedOAuthScopes', 'AllowedOAuthFlowsUserPoolClient', 'AnalyticsConfiguration',
738
- 'PreventUserExistenceErrors', 'EnableTokenRevocation',
739
- 'EnablePropagateAdditionalUserContextData', 'AuthSessionValidity', 'RefreshTokenRotation'
740
- ]
741
-
742
- update_config = {k: v for k, v in client_config.items() if k in valid_update_params}
743
-
744
- cognito.update_user_pool_client(
745
- UserPoolId=user_pool_id,
746
- ClientId=client_id,
747
- CallbackURLs=updated_callback_urls,
748
- LogoutURLs=updated_logout_urls,
749
- **update_config
750
- )
751
-
752
- print(f'Successfully added callback URL: {new_callback_url}')
753
-
754
- except Exception as e:
755
- print(f'Error updating callback URLs: {e}')
756
- sys.exit(1)
757
- "
758
- EOT
759
- }
760
-
761
- depends_on = [terraform_data.validate_cognito_props]
762
- }
763
-
764
- ",
765
- "user-identity.tf": "module "identity" {
766
- source = "./identity"
767
-
768
- user_pool_domain_prefix = "test"
769
- allow_signup = true
770
- }
771
-
772
- # Outputs
773
- output "region" {
774
- description = "AWS region"
775
- value = module.identity.region
776
- }
777
-
778
- output "user_pool_id" {
779
- description = "ID of the Cognito User Pool"
780
- value = module.identity.user_pool_id
781
- }
782
-
783
- output "user_pool_arn" {
784
- description = "ARN of the Cognito User Pool"
785
- value = module.identity.user_pool_arn
786
- }
787
-
788
- output "user_pool_client_id" {
789
- description = "ID of the Cognito User Pool Client"
790
- value = module.identity.user_pool_client_id
791
- }
792
-
793
- output "identity_pool_id" {
794
- description = "ID of the Cognito Identity Pool"
795
- value = module.identity.identity_pool_id
796
- }
797
-
798
- output "authenticated_role_name" {
799
- description = "Name of the authenticated IAM role"
800
- value = module.identity.authenticated_role_name
801
- }
802
-
803
- output "authenticated_role_arn" {
804
- description = "ARN of the authenticated IAM role"
805
- value = module.identity.authenticated_role_arn
806
- }
807
-
808
- output "user_pool_domain" {
809
- description = "Domain of the Cognito User Pool"
810
- value = module.identity.user_pool_domain
811
- }
812
- ",
813
- }
814
- `;
815
-
816
- exports[`cognito-auth generator > terraform iacProvider > should place add-callback-url module directly after cloudfront distribution resource > complex-static-website-with-callback-url 1`] = `
817
- "
818
- # Some initial resources
819
- resource "aws_s3_bucket" "website" {
820
- bucket = "test-bucket"
821
- }
822
-
823
- resource "aws_cloudfront_distribution" "website" {
824
- origin {
825
- domain_name = aws_s3_bucket.website.bucket_regional_domain_name
826
- origin_id = "S3-\${aws_s3_bucket.website.id}"
827
- }
828
-
829
- enabled = true
830
- is_ipv6_enabled = true
831
- default_root_object = "index.html"
832
-
833
- default_cache_behavior {
834
- allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
835
- cached_methods = ["GET", "HEAD"]
836
- target_origin_id = "S3-\${aws_s3_bucket.website.id}"
837
- compress = true
838
- viewer_protocol_policy = "redirect-to-https"
839
- }
840
-
841
- restrictions {
842
- geo_restriction {
843
- restriction_type = "none"
844
- }
845
- }
846
-
847
- viewer_certificate {
848
- cloudfront_default_certificate = true
849
- }
850
- }
851
-
852
- # Add CloudFront domain to user pool client callback URLs
853
- module "add_callback_url" {
854
- source = "../user-identity/add-callback-url"
855
-
856
- callback_url = "https://\${aws_cloudfront_distribution.website.domain_name}"
857
-
858
- depends_on = [aws_cloudfront_distribution.website]
859
- }
860
-
861
- # S3 Bucket Policy for CloudFront OAC
862
- resource "aws_s3_bucket_policy" "website_cloudfront_policy" {
863
- bucket = aws_s3_bucket.website.id
864
- policy = jsonencode({
865
- Version = "2012-10-17"
866
- Statement = [
867
- {
868
- Effect = "Allow"
869
- Principal = {
870
- Service = "cloudfront.amazonaws.com"
871
- }
872
- Action = "s3:GetObject"
873
- Resource = "\${aws_s3_bucket.website.arn}/*"
874
- }
875
- ]
876
- })
877
- }
878
-
879
- # Some outputs
880
- output "website_bucket_name" {
881
- value = aws_s3_bucket.website.bucket
882
- }
883
- "
884
- `;
885
-
886
- exports[`cognito-auth generator > terraform iacProvider > should update static-website.tf with add-callback-url module > static-website-with-callback-url 1`] = `
887
- "
888
- module "static_website" {
889
- source = "../../../core/static-website"
890
-
891
- website_name = "test-project"
892
- build_path = "dist/packages/test-project"
893
- }
894
- "
895
- `;