diff --git a/.github/workflows/deployment-test.yml b/.github/workflows/deployment-test.yml index 19d9e761..3c8dc91d 100644 --- a/.github/workflows/deployment-test.yml +++ b/.github/workflows/deployment-test.yml @@ -104,7 +104,7 @@ jobs: test: needs: [deploy] - timeout-minutes: 15 + timeout-minutes: 30 runs-on: ubuntu-latest steps: diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3df55c88..24733694 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -10,7 +10,7 @@ on: jobs: playwright: - timeout-minutes: 15 + timeout-minutes: 30 runs-on: ubuntu-latest steps: diff --git a/apps/client/index.html b/apps/client/index.html index cceaf540..977e87aa 100644 --- a/apps/client/index.html +++ b/apps/client/index.html @@ -8,6 +8,7 @@ + diff --git a/apps/client/package.json b/apps/client/package.json index 26053975..e415d318 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -27,17 +27,17 @@ "@aws-sdk/client-iot-events": "3.515.0", "@aws-sdk/client-iotsitewise": "3.515.0", "@aws-sdk/credential-providers": "3.515.0", - "@cloudscape-design/collection-hooks": "^1.0.36", + "@cloudscape-design/collection-hooks": "^1.0.37", "@cloudscape-design/component-toolkit": "^1.0.0-beta.38", "@cloudscape-design/components": "^3.0.518", "@cloudscape-design/design-tokens": "3.0.34", "@cloudscape-design/global-styles": "^1.0.23", - "@iot-app-kit/core": "10.0.0", - "@iot-app-kit/dashboard": "10.0.0", - "@tanstack/react-query": "^5.22.2", + "@iot-app-kit/core": "10.1.0", + "@iot-app-kit/dashboard": "10.1.0", + "@tanstack/react-query": "^5.24.1", "aws-amplify": "^5.3.11", "axios": "^1.6.7", - "cytoscape": "^3.27.0", + "cytoscape": "^3.28.1", "jotai": "^2.6.4", "nanoid": "3.1.31", "react": "^18.2.0", @@ -49,7 +49,7 @@ "react-use": "^17.4.2", "tiny-invariant": "^1.3.1", "uuid": "^9.0.1", - "vite": "^5.0.12", + "vite": "^5.1.5", "web-vitals": "^3.5.0" }, "devDependencies": { @@ -59,12 +59,12 @@ "@hookform/devtools": "^4.3.1", "@originjs/vite-plugin-federation": "^1.3.3", "@tanstack/eslint-plugin-query": "^5.20.1", - "@tanstack/react-query-devtools": "^5.24.0", + "@tanstack/react-query-devtools": "^5.24.1", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.1.0", "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^14.5.2", - "@types/react": "^18.2.57", + "@types/react": "^18.2.61", "@types/react-dom": "^18.2.19", "@types/uuid": "^9.0.8", "@vitejs/plugin-react": "^4.2.1", @@ -72,7 +72,7 @@ "@vitest/ui": "^1.1.1", "aws-sdk-client-mock": "^3.0.0", "aws-sdk-client-mock-jest": "^3.0.0", - "babel-plugin-formatjs": "^10.5.12", + "babel-plugin-formatjs": "^10.5.13", "customize-cra": "^1.0.0", "eslint-config-custom": "*", "eslint-plugin-formatjs": "^4.11.1", diff --git a/apps/client/src/auth/auth-service.interface.ts b/apps/client/src/auth/auth-service.interface.ts index c7fcceb0..b75dd0be 100644 --- a/apps/client/src/auth/auth-service.interface.ts +++ b/apps/client/src/auth/auth-service.interface.ts @@ -31,4 +31,9 @@ export interface AuthService { * @param callback the callback function to call when application is signed-in */ onSignedIn(callback: () => unknown): void; + + // TODO: Refactor interface to only include shared methods + getEdgeEndpoint?(): string; + + setEdgeEndpoint?(endpoint: string): void; } diff --git a/apps/client/src/auth/auth-service.ts b/apps/client/src/auth/auth-service.ts index 8e1d221f..a9cd3066 100644 --- a/apps/client/src/auth/auth-service.ts +++ b/apps/client/src/auth/auth-service.ts @@ -34,6 +34,19 @@ class ClientAuthService { onSignedIn(callback: () => unknown) { return this.authService.onSignedIn(callback); } + + setEdgeEndpoint(endpoint: string) { + if (this.authService.setEdgeEndpoint) { + this.authService.setEdgeEndpoint(endpoint); + } + } + + getEdgeEndpoint() { + if (this.authService.getEdgeEndpoint) { + return this.authService.getEdgeEndpoint(); + } + return ''; + } } export const authService = new ClientAuthService(); diff --git a/apps/client/src/auth/edge-auth-service.ts b/apps/client/src/auth/edge-auth-service.ts index 5c61a9b9..e70a5b14 100644 --- a/apps/client/src/auth/edge-auth-service.ts +++ b/apps/client/src/auth/edge-auth-service.ts @@ -7,6 +7,15 @@ export class EdgeAuthService implements AuthService { accessKeyId: '', secretAccessKey: '', }; + private edgeEndpoint = '0.0.0.0'; + + setEdgeEndpoint(endpoint: string) { + this.edgeEndpoint = endpoint; + } + + getEdgeEndpoint() { + return this.edgeEndpoint; + } setAwsCredentials(credentials: AwsCredentialIdentity) { this.credentials = credentials; diff --git a/apps/client/src/constants/format.ts b/apps/client/src/constants/format.ts index 44c19079..a21b5e6d 100644 --- a/apps/client/src/constants/format.ts +++ b/apps/client/src/constants/format.ts @@ -4,3 +4,4 @@ export const ROOT_INDEX_PAGE_FORMAT: Format = 'default'; export const DASHBOARDS_INDEX_PAGE_FORMAT: Format = 'table'; export const CREATE_DASHBOARD_PAGE_FORMAT: Format = 'form'; export const DASHBOARD_PAGE_FORMAT: Format = 'default'; +export const EDGE_LOGIN_PAGE_FORMAT: Format = 'form'; diff --git a/apps/client/src/constants/index.ts b/apps/client/src/constants/index.ts index eed633bf..d09c2b6c 100644 --- a/apps/client/src/constants/index.ts +++ b/apps/client/src/constants/index.ts @@ -11,6 +11,7 @@ export const DASHBOARD_PATH = ':dashboardId'; export const ROOT_HREF = '/'; export const DASHBOARDS_HREF = '/dashboards'; export const CREATE_DASHBOARD_HREF = '/dashboards/create'; +export const EDGE_LOGIN_HREF = '/edge-login'; export const DEFAULT_CONTENT_DENSITY: ContentDensity = 'comfortable'; export const CONTENT_DENSITY_KEY = 'content-density'; diff --git a/apps/client/src/helpers/meta-tags.ts b/apps/client/src/helpers/meta-tags.ts index 05768978..dc67f8f0 100644 --- a/apps/client/src/helpers/meta-tags.ts +++ b/apps/client/src/helpers/meta-tags.ts @@ -10,6 +10,7 @@ export const extractedMetaTags = ( applicationName: '', authenticationFlowType: '', cognitoEndpoint: '', + edgeEndpoint: '', identityPoolId: '', region: '', userPoolId: '', diff --git a/apps/client/src/helpers/strings/is-string-with-value.ts b/apps/client/src/helpers/strings/is-string-with-value.ts new file mode 100644 index 00000000..e534f19d --- /dev/null +++ b/apps/client/src/helpers/strings/is-string-with-value.ts @@ -0,0 +1,25 @@ +const isString = (value: unknown): value is string => { + return typeof value === 'string'; +}; + +export const isStringWithValue = (value: unknown): value is string => { + return isString(value) && value !== ''; +}; + +if (import.meta.vitest) { + it('returns true for string with value', () => { + expect(isStringWithValue('abc')).toBe(true); + }); + + it('returns false for string with no value', () => { + expect(isStringWithValue('')).toBe(false); + }); + + it('returns false for non-string value', () => { + expect(isStringWithValue({})).toBe(false); + }); + + it('returns false for undefined value', () => { + expect(isStringWithValue(undefined)).toBe(false); + }); +} diff --git a/apps/client/src/hooks/dashboard/use-viewport.ts b/apps/client/src/hooks/dashboard/use-viewport.ts index 03a10ef9..63f20e3c 100644 --- a/apps/client/src/hooks/dashboard/use-viewport.ts +++ b/apps/client/src/hooks/dashboard/use-viewport.ts @@ -1,7 +1,7 @@ import { useLocalStorage } from 'react-use'; import type { Viewport } from '@iot-app-kit/core'; -const DEFAULT_VIEWPORT: Viewport = { duration: '5 minutes' }; +const DEFAULT_VIEWPORT: Viewport = { duration: '5 minute' }; export const useViewport = (dashboardId: string) => { return useLocalStorage( `dashboard-${dashboardId}-viewport`, diff --git a/apps/client/src/index.tsx b/apps/client/src/index.tsx index 35bd11fa..f39e3096 100644 --- a/apps/client/src/index.tsx +++ b/apps/client/src/index.tsx @@ -13,14 +13,13 @@ import { queryClient } from './data/query-client'; import { setServiceUrl } from './services'; import metricHandler from './metrics/metric-handler'; import { extractedMetaTags } from './helpers/meta-tags'; +import { isStringWithValue } from './helpers/strings/is-string-with-value'; import { registerServiceWorker } from './register-service-worker'; import { authService } from './auth/auth-service'; import { initializeAuthDependents } from './initialize-auth-dependents'; import { registerLogger } from './register-loggers'; import { registerMetricsRecorder } from './register-metrics-recorder'; -import { EdgeLoginPage } from './routes/edge-login/edge-login-page'; - import '@aws-amplify/ui-react/styles.css'; import '@cloudscape-design/global-styles/index.css'; @@ -36,6 +35,7 @@ const { authenticationFlowType, cognitoEndpoint, domainName, + edgeEndpoint, identityPoolId, logMode, metricsMode, @@ -44,7 +44,11 @@ const { userPoolWebClientId, } = metadata; -if (domainName && domainName !== '') { +if (isStringWithValue(edgeEndpoint)) { + authService.setEdgeEndpoint(edgeEndpoint); +} + +if (isStringWithValue(domainName)) { Amplify.configure({ Auth: { authenticationFlowType, @@ -95,10 +99,8 @@ if (authMode === 'edge') { - - - - + + , diff --git a/apps/client/src/layout/components/top-navigation/top-navigation.tsx b/apps/client/src/layout/components/top-navigation/top-navigation.tsx index 5771bbe3..8d31a782 100644 --- a/apps/client/src/layout/components/top-navigation/top-navigation.tsx +++ b/apps/client/src/layout/components/top-navigation/top-navigation.tsx @@ -4,11 +4,12 @@ import { useState } from 'react'; import { useIntl } from 'react-intl'; import { SettingsModal } from './components/settings-modal'; -import { ROOT_HREF } from '~/constants'; +import { EDGE_LOGIN_HREF, ROOT_HREF } from '~/constants'; import { preventFullPageLoad } from '~/helpers/events'; import { useApplication } from '~/hooks/application/use-application'; import { getAuthMode } from '~/helpers/authMode'; +import { authService } from '~/auth/auth-service'; function EdgeNavigation() { const [isSettingsModalVisible, setIsSettingsModalVisible] = useState(false); @@ -59,7 +60,11 @@ function EdgeNavigation() { iconName: 'user-profile', onItemClick: (event) => { if (event.detail.id === 'signout') { - // TODO: call signout for edge mode + authService.setAwsCredentials({ + accessKeyId: '', + secretAccessKey: '', + }); + navigate(EDGE_LOGIN_HREF); } }, items: [ diff --git a/apps/client/src/routes/dashboards/dashboard/dashboard-configuration.ts b/apps/client/src/routes/dashboards/dashboard/dashboard-configuration.ts new file mode 100644 index 00000000..57824d54 --- /dev/null +++ b/apps/client/src/routes/dashboards/dashboard/dashboard-configuration.ts @@ -0,0 +1,43 @@ +import { type DashboardClientConfiguration } from '@iot-app-kit/dashboard'; +import { IoTSiteWiseClient } from '@aws-sdk/client-iotsitewise'; +import { IoTEventsClient } from '@aws-sdk/client-iot-events'; +import { IoTTwinMakerClient } from '@aws-sdk/client-iottwinmaker'; +import { getAuthMode } from '~/helpers/authMode'; + +import { authService } from '~/auth/auth-service'; + +export function getDashboardClientConfiguration(): DashboardClientConfiguration { + if (getAuthMode() === 'edge') { + return getEdgeClientConfig(); + } + + return getCloudClientConfig(); +} + +function getCloudClientConfig(): DashboardClientConfiguration { + return { + awsCredentials: () => authService.getAwsCredentials(), + awsRegion: authService.awsRegion, + }; +} + +function getEdgeClientConfig(): DashboardClientConfiguration { + const clientConfig = { + endpoint: authService.getEdgeEndpoint(), + credentials: () => authService.getAwsCredentials(), + region: authService.awsRegion, + disableHostPrefix: true, + }; + + const iotSiteWiseClient = new IoTSiteWiseClient(clientConfig); + const iotEventsClient = new IoTEventsClient(clientConfig); + const iotTwinMakerClient = new IoTTwinMakerClient(clientConfig); + + const clients = { + iotSiteWiseClient, + iotEventsClient, + iotTwinMakerClient, + }; + + return clients; +} diff --git a/apps/client/src/routes/dashboards/dashboard/dashboard-page.tsx b/apps/client/src/routes/dashboards/dashboard/dashboard-page.tsx index 62489d90..6600c746 100644 --- a/apps/client/src/routes/dashboards/dashboard/dashboard-page.tsx +++ b/apps/client/src/routes/dashboards/dashboard/dashboard-page.tsx @@ -18,9 +18,10 @@ import { useEmitNotification } from '~/hooks/notifications/use-emit-notification import { useDisplaySettings } from '~/hooks/dashboard/use-displaySettings'; import { getDashboardEditMode, setDashboardEditMode } from '~/store/viewMode'; import { GenericErrorNotification } from '~/structures/notifications/generic-error-notification'; +import { getDashboardClientConfiguration } from './dashboard-configuration'; +import { getAuthMode } from '~/helpers/authMode'; import './styles.css'; -import { authService } from '~/auth/auth-service'; export function DashboardPage() { const params = useParams<{ dashboardId: string }>(); @@ -65,14 +66,10 @@ export function DashboardPage() { return ; } - const awsRegion = authService.awsRegion; - return ( authService.getAwsCredentials(), - awsRegion, - }} + edgeMode={getAuthMode() === 'edge' ? 'enabled' : 'disabled'} + clientConfiguration={getDashboardClientConfiguration()} dashboardConfiguration={{ ...dashboardDefinition, displaySettings, diff --git a/apps/client/src/routes/edge-login/components/edge-endpoint-field.tsx b/apps/client/src/routes/edge-login/components/edge-endpoint-field.tsx deleted file mode 100644 index 6b038f84..00000000 --- a/apps/client/src/routes/edge-login/components/edge-endpoint-field.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import FormField from '@cloudscape-design/components/form-field'; -import Input from '@cloudscape-design/components/input'; -import { Controller } from 'react-hook-form'; - -import type { Control } from 'react-hook-form'; -import type { EdgeLoginFormValues } from '../hooks/use-edge-login-form'; -import { isJust } from '~/helpers/predicates/is-just'; - -interface EdgeEndpointFieldProps { - control: Control; -} - -export function EdgeEndpointField(props: EdgeEndpointFieldProps) { - return ( - ( - - field.onChange(event.detail.value)} - value={field.value} - placeholder="Enter hostname or IP address" - /> - - )} - /> - ); -} diff --git a/apps/client/src/routes/edge-login/edge-login-page.spec.tsx b/apps/client/src/routes/edge-login/edge-login-page.spec.tsx index a0b15014..f17015f8 100644 --- a/apps/client/src/routes/edge-login/edge-login-page.spec.tsx +++ b/apps/client/src/routes/edge-login/edge-login-page.spec.tsx @@ -3,24 +3,27 @@ import { render, screen } from '~/helpers/tests/testing-library'; import userEvent from '@testing-library/user-event'; import { EdgeLoginPage } from './edge-login-page'; +import { edgeLogin } from '~/services'; -const getHostField = () => - screen.getByPlaceholderText('Enter hostname or IP address'); const getUsernameField = () => screen.getByPlaceholderText('Enter username'); const getPasswordField = () => screen.getByPlaceholderText('Enter password'); const getSigninButton = () => screen.getByRole('button', { name: 'Sign in' }); -const hostName = '1.2.3.4.5'; const username = 'username'; const password = 'password'; vi.mock('~/services', () => ({ edgeLogin: vi.fn().mockReturnValue({ - data: { - accessKey: '', - secretKeyId: '', - }, + accessKey: '', + secretAccessKey: '', + }), +})); + +const navigateMock = vi.fn(); +vi.mock('~/hooks/application/use-application', () => ({ + useApplication: () => ({ + navigate: navigateMock, }), })); @@ -43,9 +46,6 @@ describe('', () => { const signInText = screen.getByText('Sign in to edge gateway'); expect(signInText).toBeInTheDocument(); - await user.type(getHostField(), hostName); - expect(getHostField()).toHaveValue(hostName); - await user.type(getUsernameField(), username); expect(getUsernameField()).toHaveValue(username); @@ -54,9 +54,45 @@ describe('', () => { await user.click(getSigninButton()); - // Sign in successful, so children are rendered instead of sign in page - expect(signInText).not.toBeInTheDocument(); + // Sign in successful, so should navigate to app + expect(navigateMock).toBeCalledWith('/dashboards'); }); - // TODO: error tests + it('does not sign in if there was an api error', async () => { + vi.resetAllMocks(); + const errorMessage = 'Incorrect username or password'; + const errorResponse = { + response: { + body: { + message: errorMessage, + }, + status: 401, + }, + isAxiosError: true, + toJSON: () => { + return {}; + }, + name: '', + message: '', + }; + + vi.mocked(edgeLogin).mockRejectedValue(errorResponse); + + const user = userEvent.setup(); + render(); + + const signInText = screen.getByText('Sign in to edge gateway'); + expect(signInText).toBeInTheDocument(); + + await user.type(getUsernameField(), username); + expect(getUsernameField()).toHaveValue(username); + + await user.type(getPasswordField(), password); + expect(getPasswordField()).toHaveValue(password); + + await user.click(getSigninButton()); + + // Expect login failure + expect(signInText).toBeInTheDocument(); + }); }); diff --git a/apps/client/src/routes/edge-login/edge-login-page.tsx b/apps/client/src/routes/edge-login/edge-login-page.tsx index 144c612d..39b9074e 100644 --- a/apps/client/src/routes/edge-login/edge-login-page.tsx +++ b/apps/client/src/routes/edge-login/edge-login-page.tsx @@ -9,79 +9,83 @@ import ColumnLayout from '@cloudscape-design/components/column-layout'; import { colorBackgroundHomeHeader } from '@cloudscape-design/design-tokens'; import { DevTool } from '@hookform/devtools'; import { authService } from '../../auth/auth-service'; -import { PropsWithChildren } from 'react'; import { useEdgeLoginForm } from './hooks/use-edge-login-form'; -import { EdgeEndpointField } from './components/edge-endpoint-field'; import { EdgeUsernameField } from './components/edge-username-field'; import { EdgeMechanismField } from './components/edge-mechanism-field'; import { EdgePasswordField } from './components/edge-password-field'; import { edgeLogin } from '~/services'; +import { ApiError } from '~/services/generated/core/ApiError'; +import { useApplication } from '~/hooks/application/use-application'; -export function EdgeLoginPage({ children }: PropsWithChildren) { - const [isLoggedIn, setIsLoggedIn] = useState(false); +// Adding type since ApiError types body as any +interface ApiErrorBody { + message: string; +} + +export function EdgeLoginPage() { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const { control, handleSubmit } = useEdgeLoginForm(); + const { navigate } = useApplication(); const isProdEnv: boolean = process.env.NODE_ENV === 'production'; - if (isLoggedIn) { - return children; - } else { - return ( - -
- -
{ - event.preventDefault(); + return ( + +
+ + { + event.preventDefault(); - void handleSubmit(async (formData) => { - try { - setIsLoading(true); - const data = await edgeLogin(formData); - authService.setAwsCredentials(data); - setIsLoading(false); - setIsLoggedIn(true); - } catch (error) { - // TODO: invalid credential error handling - setError('Error getting credentials'); - setIsLoading(false); + void handleSubmit(async (formData) => { + try { + setIsLoading(true); + const data = await edgeLogin(formData); + authService.setAwsCredentials(data); + setIsLoading(false); + navigate('/dashboards'); + } catch (error) { + if (error instanceof ApiError) { + const errorBody = error.body as ApiErrorBody; + setError(errorBody.message); + } else { + setError('Error logging in.'); } - })(); - }} - > - - - Sign in - - + setIsLoading(false); } - errorText={error} - > - Sign in to edge gateway} + })(); + }} + > + - - - - - - - - - - {!isProdEnv && } - -
- - ); - } + + Sign in + + + } + errorText={error} + > + Sign in to edge gateway} + > + + + + + + + + + {!isProdEnv && } + +
+ + ); } diff --git a/apps/client/src/routes/edge-login/edge-login-route.tsx b/apps/client/src/routes/edge-login/edge-login-route.tsx new file mode 100644 index 00000000..552ac70c --- /dev/null +++ b/apps/client/src/routes/edge-login/edge-login-route.tsx @@ -0,0 +1,15 @@ +import { type RouteObject } from 'react-router-dom'; +import { EDGE_LOGIN_PAGE_FORMAT } from '~/constants/format'; +import { EdgeLoginPage } from './edge-login-page'; + +export const edgeLoginRoute = { + path: 'edge-login', + element: , + handle: { + crumb: () => ({ + text: 'Edge login', + href: '/edge-login', + }), + format: EDGE_LOGIN_PAGE_FORMAT, + }, +} satisfies RouteObject; diff --git a/apps/client/src/routes/edge-login/hooks/use-edge-login-form.ts b/apps/client/src/routes/edge-login/hooks/use-edge-login-form.ts index 87c8cb42..4c8c1feb 100644 --- a/apps/client/src/routes/edge-login/hooks/use-edge-login-form.ts +++ b/apps/client/src/routes/edge-login/hooks/use-edge-login-form.ts @@ -3,14 +3,12 @@ import { useForm } from 'react-hook-form'; export type EdgeAuthMechanisms = 'linux' | 'ldap'; export interface EdgeLoginFormValues { - edgeEndpoint: string; username: string; password: string; authMechanism: EdgeAuthMechanisms; } export const DEFAULT_VALUES: EdgeLoginFormValues = { - edgeEndpoint: '', username: '', password: '', authMechanism: 'linux', diff --git a/apps/client/src/routes/root-index/index.ts b/apps/client/src/routes/root-index/index.ts index 97908525..b4e392d7 100644 --- a/apps/client/src/routes/root-index/index.ts +++ b/apps/client/src/routes/root-index/index.ts @@ -1 +1 @@ -export { rootIndexRoute } from './root-index-route'; +export { rootIndexRoute, rootIndexEdgeRoute } from './root-index-route'; diff --git a/apps/client/src/routes/root-index/root-index-route.tsx b/apps/client/src/routes/root-index/root-index-route.tsx index 10f062ec..4c53f253 100644 --- a/apps/client/src/routes/root-index/root-index-route.tsx +++ b/apps/client/src/routes/root-index/root-index-route.tsx @@ -1,5 +1,8 @@ import { Navigate, type RouteObject } from 'react-router-dom'; -import { ROOT_INDEX_PAGE_FORMAT } from '~/constants/format'; +import { + ROOT_INDEX_PAGE_FORMAT, + EDGE_LOGIN_PAGE_FORMAT, +} from '~/constants/format'; export const rootIndexRoute = { index: true, @@ -8,3 +11,11 @@ export const rootIndexRoute = { format: ROOT_INDEX_PAGE_FORMAT, }, } satisfies RouteObject; + +export const rootIndexEdgeRoute = { + index: true, + element: , + handle: { + format: EDGE_LOGIN_PAGE_FORMAT, + }, +} satisfies RouteObject; diff --git a/apps/client/src/routes/root/root-route.tsx b/apps/client/src/routes/root/root-route.tsx index 9e5c6896..078e0e23 100644 --- a/apps/client/src/routes/root/root-route.tsx +++ b/apps/client/src/routes/root/root-route.tsx @@ -1,12 +1,25 @@ import { RootPage } from './root-page'; -import { rootIndexRoute } from '../root-index/index'; +import { rootIndexRoute, rootIndexEdgeRoute } from '../root-index/index'; import { rootDashboardsRoute } from '../dashboards/root-dashboards'; +import { edgeLoginRoute } from '../edge-login/edge-login-route'; import { ROOT_PATH, ROOT_HREF } from '~/constants'; -import { intl } from '~/services'; +import { getAuthMode } from '~/helpers/authMode'; import type { RouteObject } from 'react-router-dom'; import { RootErrorPage } from './root-error-page'; import { Layout } from '~/layout/layout'; +import { intl } from '~/services'; + +let children: RouteObject[] = [rootIndexRoute, rootDashboardsRoute]; + +if (getAuthMode() === 'edge') { + children = [ + rootIndexEdgeRoute, + edgeLoginRoute, + rootIndexRoute, + rootDashboardsRoute, + ]; +} export const rootRoute = { path: ROOT_PATH, @@ -30,5 +43,5 @@ export const rootRoute = { href: ROOT_HREF, }), }, - children: [rootIndexRoute, rootDashboardsRoute], + children, } satisfies RouteObject; diff --git a/apps/client/src/services/generated/models/EdgeLoginBody.ts b/apps/client/src/services/generated/models/EdgeLoginBody.ts index bb634827..f646ec9d 100644 --- a/apps/client/src/services/generated/models/EdgeLoginBody.ts +++ b/apps/client/src/services/generated/models/EdgeLoginBody.ts @@ -1,5 +1,4 @@ export type EdgeLoginBody = { - edgeEndpoint: string; username: string; password: string; authMechanism: string; diff --git a/apps/client/src/types/metadata.ts b/apps/client/src/types/metadata.ts index 74a6836b..0b183ad6 100644 --- a/apps/client/src/types/metadata.ts +++ b/apps/client/src/types/metadata.ts @@ -1,16 +1,17 @@ export interface Metadata { + applicationName: string; + authenticationFlowType: string; + authMode: string; awsAccessKeyId: string; awsSecretAccessKey: string; awsSessionToken: string; - applicationName: string; - authenticationFlowType: string; cognitoEndpoint: string; + domainName?: string; + edgeEndpoint?: string; identityPoolId: string; + logMode: string; + metricsMode: string; region: string; userPoolId: string; userPoolWebClientId: string; - logMode: string; - metricsMode: string; - authMode: string; - domainName?: string; } diff --git a/apps/core/.example.edge.env b/apps/core/.example.edge.env new file mode 100644 index 00000000..86b2d301 --- /dev/null +++ b/apps/core/.example.edge.env @@ -0,0 +1,20 @@ +EDGE_ENDPOINT=xxxx + +APPLICATION_NAME=IotApp +AUTH_MODE=edge +AWS_ACCESS_KEY_ID=fakeMyKeyId +AWS_REGION=edge +AWS_SECRET_ACCESS_KEY=fakeSecretAccessKey +AWS_SESSION_TOKEN= +COGNITO_IDENTITY_POOL_ID=fakeIdentiyPoolId +COGNITO_USE_LOCAL_VERIFIER=true +COGNITO_USER_POOL_ID=us-west-2_h23TJjQR9 +COGNITO_USER_POOL_CLIENT_ID=9cehli62qxmki9mg5adjmucuq +COGNITO_DOMAIN_NAME=https://test.auth.us-west-2.amazoncognito.com +DATABASE_ENDPOINT=http://127.0.0.1:8000 +DATABASE_LAUNCH_LOCAL=true +DATABASE_PORT=8000 +DATABASE_TABLE_NAME=ApiResourceTable +NODE_ENV=development +# WebPack Server, Local Cognito and DDB endpoints, and AWS endpoints +SERVICE_ENDPOINTS='ws://localhost:3001 http://localhost:9229 http://localhost:8000 https://*.amazonaws.com' diff --git a/apps/core/package.json b/apps/core/package.json index 075cfc33..78654076 100644 --- a/apps/core/package.json +++ b/apps/core/package.json @@ -27,7 +27,7 @@ "@fastify/csrf-protection": "^6.4.1", "@fastify/helmet": "^11.1.1", "@fastify/static": "^7.0.1", - "@fastify/view": "^8.2.0", + "@fastify/view": "^9.0.0", "@nestjs/axios": "^1.0.1", "@nestjs/common": "^9.4.3", "@nestjs/config": "^3.1.1", diff --git a/apps/core/src/app.module.ts b/apps/core/src/app.module.ts index 1d209972..fe0e6dcf 100644 --- a/apps/core/src/app.module.ts +++ b/apps/core/src/app.module.ts @@ -3,23 +3,24 @@ import { ConfigModule } from '@nestjs/config'; import { APP_GUARD } from '@nestjs/core'; import { ThrottlerModule } from '@nestjs/throttler'; +import { authConfig } from './config/auth.config'; import { databaseConfig } from './config/database.config'; +import { edgeConfig } from './config/edge.config'; +import { globalConfig } from './config/global.config'; +import { jwtConfig } from './config/jwt.config'; import { DashboardsModule } from './dashboards/dashboards.module'; import { HealthModule } from './health/health.module'; import { DynamoDbLocalSetupService } from './lifecycle-hooks/dynamodb-local-setup'; import { CognitoJwtAuthGuard } from './auth/cognito-jwt-auth.guard'; -import { authConfig } from './config/auth.config'; import { MvcModule } from './mvc/mvc.module'; -import { jwtConfig } from './config/jwt.config'; import { MigrationModule } from './migration/migration.module'; import { EdgeLoginModule } from './edge-login/edge-login.module'; import { LoggerModule } from './logging/logger.module'; -import { globalConfig } from './config/global.config'; @Module({ imports: [ ConfigModule.forRoot({ - load: [authConfig, databaseConfig, globalConfig, jwtConfig], + load: [authConfig, databaseConfig, edgeConfig, globalConfig, jwtConfig], isGlobal: true, }), DashboardsModule, diff --git a/apps/core/src/bootstrap/security.bootstrap.ts b/apps/core/src/bootstrap/security.bootstrap.ts index e35d8090..270da46d 100644 --- a/apps/core/src/bootstrap/security.bootstrap.ts +++ b/apps/core/src/bootstrap/security.bootstrap.ts @@ -16,14 +16,16 @@ import invariant from 'tiny-invariant'; * @see {@link https://docs.nestjs.com/security/csrf | CSRF Protection} */ export const bootstrapSecurity = async (app: NestFastifyApplication) => { - const { SERVICE_ENDPOINTS } = process.env; + const { EDGE_ENDPOINT, SERVICE_ENDPOINTS } = process.env; + + const edge_endpoint = isDefined(EDGE_ENDPOINT) ? [EDGE_ENDPOINT] : []; // Split the space separated service endpoints invariant( isDefined(SERVICE_ENDPOINTS), envVarRequiredMsg('SERVICE_ENDPOINTS'), ); - const serviceEndpoints = SERVICE_ENDPOINTS.split(' '); + const serviceEndpoints = [...SERVICE_ENDPOINTS.split(' '), ...edge_endpoint]; // Upgrade insecure requests for all non DEV environment const upgradeInsecureRequests = isDevEnv() ? null : []; diff --git a/apps/core/src/config/edge.config.ts b/apps/core/src/config/edge.config.ts new file mode 100644 index 00000000..0ce6530c --- /dev/null +++ b/apps/core/src/config/edge.config.ts @@ -0,0 +1,11 @@ +import { registerAs } from '@nestjs/config'; + +export const configFactory = () => { + const { EDGE_ENDPOINT: edgeEndpoint } = process.env; + + return { + edgeEndpoint, + }; +}; + +export const edgeConfig = registerAs('edge', configFactory); diff --git a/apps/core/src/edge-login/edge-login.e2e.spec.ts b/apps/core/src/edge-login/edge-login.e2e.spec.ts index dcb314a7..e031b0fc 100644 --- a/apps/core/src/edge-login/edge-login.e2e.spec.ts +++ b/apps/core/src/edge-login/edge-login.e2e.spec.ts @@ -5,10 +5,11 @@ import { } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; import { AppModule } from '../app.module'; -import { configureTestProcessEnv } from '../testing/aws-configuration'; +import { configureEdgeTestProcessEnv } from '../testing/aws-configuration'; import { EdgeLoginService } from './edge-login.service'; import { HttpModule, HttpService } from '@nestjs/axios'; import { EdgeCredentials } from './entities/edge-credentials.entity'; +import { AxiosError } from '@nestjs/terminus/dist/errors/axios.error'; describe('EdgeLoginModule', () => { let app: NestFastifyApplication; @@ -34,7 +35,7 @@ describe('EdgeLoginModule', () => { }; beforeEach(async () => { - configureTestProcessEnv(process.env); + configureEdgeTestProcessEnv(process.env); const moduleRef = await Test.createTestingModule({ imports: [AppModule, HttpModule], @@ -71,7 +72,6 @@ describe('EdgeLoginModule', () => { describe('POST /api/migration HTTP/1.1', () => { test('correctly proxies the edge credential request', async () => { const requestBody = { - edgeEndpoint: '1.2.3.4.5', username: 'testUser', password: 'testPassword', authMechanism: 'linux', @@ -102,7 +102,6 @@ describe('EdgeLoginModule', () => { .mockRejectedValueOnce(errorResponse); const requestBody = { - edgeEndpoint: '1.2.3.4.5', username: 'testUser', password: 'testPassword', authMechanism: 'linux', @@ -118,5 +117,50 @@ describe('EdgeLoginModule', () => { expect(httpServiceSpy).toHaveBeenCalled(); expect(response.statusCode).toBe(500); }); + + test('returns incorrect username/password if the axios call results in 401', async () => { + const errorResponse: AxiosError = { + response: { + body: { + message: 'Incorrect username or password', + }, + status: 401, + }, + isAxiosError: true, + toJSON: () => { + return {}; + }, + name: '', + message: '', + }; + + httpServiceSpy.mockReset(); + httpServiceSpy = jest + .spyOn(httpService.axiosRef, 'post') + .mockRejectedValueOnce(errorResponse); + + const requestBody = { + username: 'testUser', + password: 'testPassword', + authMechanism: 'linux', + }; + + const { body } = await app.inject({ + method: 'POST', + url: '/api/edge-login', + payload: requestBody, + }); + + const expectedResponse = { + statusCode: 401, + message: 'Incorrect username or password', + error: 'Unauthorized', + }; + + expect(edgeLoginSpy).toHaveBeenCalled(); + expect(httpServiceSpy).toHaveBeenCalled(); + + expect(body).toBe(JSON.stringify(expectedResponse)); + }); }); }); diff --git a/apps/core/src/edge-login/edge-login.module.ts b/apps/core/src/edge-login/edge-login.module.ts index 1718aae0..45904c9d 100644 --- a/apps/core/src/edge-login/edge-login.module.ts +++ b/apps/core/src/edge-login/edge-login.module.ts @@ -1,11 +1,13 @@ +import { HttpModule } from '@nestjs/axios'; import { Module, ModuleMetadata } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { EdgeLoginController } from './edge-login.controller'; import { EdgeLoginService } from './edge-login.service'; -import { HttpModule } from '@nestjs/axios'; +import { edgeConfig } from '../config/edge.config'; export const edgeLoginModuleMetadata: ModuleMetadata = { - imports: [HttpModule], + imports: [ConfigModule.forFeature(edgeConfig), HttpModule], controllers: [EdgeLoginController], providers: [EdgeLoginService], }; diff --git a/apps/core/src/edge-login/edge-login.service.ts b/apps/core/src/edge-login/edge-login.service.ts index 1b9c12fa..643edb98 100644 --- a/apps/core/src/edge-login/edge-login.service.ts +++ b/apps/core/src/edge-login/edge-login.service.ts @@ -1,13 +1,26 @@ import { HttpService } from '@nestjs/axios'; -import { Injectable } from '@nestjs/common'; +import { + Inject, + Injectable, + RequestTimeoutException, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigType } from '@nestjs/config'; +import { isAxiosError } from '@nestjs/terminus/dist/utils'; import { Agent } from 'https'; + import { EdgeCredentials } from './entities/edge-credentials.entity'; import { EdgeLoginBody } from './entities/edge-login-body.entity'; +import { edgeConfig } from '../config/edge.config'; import { Result, err, ok } from '../types'; +import { isStringWithValue } from '../types/strings/is-string-with-value'; @Injectable() export class EdgeLoginService { - constructor(private readonly httpService: HttpService) {} + constructor( + @Inject(edgeConfig.KEY) private edge: ConfigType, + private readonly httpService: HttpService, + ) {} public async login( body: EdgeLoginBody, @@ -15,8 +28,13 @@ export class EdgeLoginService { const httpsAgent = new Agent({ rejectUnauthorized: false }); try { + const { edgeEndpoint } = this.edge; + if (!isStringWithValue(edgeEndpoint)) { + throw new Error(); + } + const result = await this.httpService.axiosRef.post( - `https://${body.edgeEndpoint}/authenticate`, + `${edgeEndpoint}/authenticate`, { username: body.username, password: body.password, @@ -37,6 +55,20 @@ export class EdgeLoginService { sessionExpiryTime, }); } catch (error) { + if (isAxiosError(error)) { + // If response field, server responded with status code thats not 2xx + if (error.response) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (error.response.status && error.response.status === 401) { + return err( + new UnauthorizedException('Incorrect username or password'), + ); + } + } else if (error.request) { + // If request field, no response from server + return err(new RequestTimeoutException('Request timed out')); + } + } return error instanceof Error ? err(error) : err(new Error('Error getting edge credentials')); diff --git a/apps/core/src/edge-login/entities/edge-login-body.entity.ts b/apps/core/src/edge-login/entities/edge-login-body.entity.ts index efea199e..48a3218a 100644 --- a/apps/core/src/edge-login/entities/edge-login-body.entity.ts +++ b/apps/core/src/edge-login/entities/edge-login-body.entity.ts @@ -1,12 +1,6 @@ import { IsString } from 'class-validator'; export class EdgeLoginBody { - /** - * @example "192.168.0.1" - */ - @IsString() - public readonly edgeEndpoint: string; - /** * @example "user" */ diff --git a/apps/core/src/migration/service/convert-monitor-to-app-definition.ts b/apps/core/src/migration/service/convert-monitor-to-app-definition.ts index 4efd9066..4b3d331a 100644 --- a/apps/core/src/migration/service/convert-monitor-to-app-definition.ts +++ b/apps/core/src/migration/service/convert-monitor-to-app-definition.ts @@ -162,7 +162,17 @@ const lineChartProperties = { style: 'solid', }, legend: { + width: '30%', + visibleContent: { + unit: true, + latestValue: true, + minValue: false, + asset: true, + maxValue: false, + }, visible: true, + position: 'right', + height: '30%', }, }; @@ -410,6 +420,10 @@ const convertKpiAndGridWidget = ( secondaryFont: {}, title: monitorWidget.title, ...queryConfig, + showTimestamp: true, + showAggregationAndResolution: true, + resolution: '0', + showUnit: true, }, }; appWidgets.push(newAppWidget); diff --git a/apps/core/src/mvc/mvc.controller.ts b/apps/core/src/mvc/mvc.controller.ts index 0f8254df..1bafed66 100644 --- a/apps/core/src/mvc/mvc.controller.ts +++ b/apps/core/src/mvc/mvc.controller.ts @@ -2,6 +2,7 @@ import { Get, Controller, Render, Inject, Header } from '@nestjs/common'; import { ConfigType } from '@nestjs/config'; import { Public } from '../auth/public.decorator'; import { authConfig } from '../config/auth.config'; +import { edgeConfig } from '../config/edge.config'; import { globalConfig } from '../config/global.config'; // Responds with the index page for all of the client routes. @@ -11,6 +12,7 @@ const CLIENT_ROUTES = ['', 'dashboards', 'dashboards/*']; export class MvcController { constructor( @Inject(authConfig.KEY) private auth: ConfigType, + @Inject(edgeConfig.KEY) private edge: ConfigType, @Inject(globalConfig.KEY) private global: ConfigType, ) {} @@ -21,35 +23,38 @@ export class MvcController { root() { const { authenticationFlowType, + authMode, clientAwsAccessKeyId, clientAwsSecretAccessKey, clientAwsSessionToken, cognitoEndpoint, + domainName, identityPoolId, region, - userPoolWebClientId, userPoolId, - domainName, - authMode, + userPoolWebClientId, } = this.auth; + const { edgeEndpoint } = this.edge; + const { applicationName, logMode, metricsMode } = this.global; return { applicationName, authenticationFlowType, + authMode, clientAwsAccessKeyId, clientAwsSecretAccessKey, clientAwsSessionToken, cognitoEndpoint, + domainName, + edgeEndpoint, identityPoolId, + logMode, + metricsMode, region, userPoolId, userPoolWebClientId, - logMode, - metricsMode, - domainName, - authMode, }; } } diff --git a/apps/core/src/mvc/mvc.module.ts b/apps/core/src/mvc/mvc.module.ts index da8f88b4..6fea7352 100644 --- a/apps/core/src/mvc/mvc.module.ts +++ b/apps/core/src/mvc/mvc.module.ts @@ -1,8 +1,17 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; + import { MvcController } from './mvc.controller'; +import { authConfig } from '../config/auth.config'; +import { edgeConfig } from '../config/edge.config'; +import { globalConfig } from '../config/global.config'; @Module({ - imports: [], + imports: [ + ConfigModule.forFeature(authConfig), + ConfigModule.forFeature(edgeConfig), + ConfigModule.forFeature(globalConfig), + ], controllers: [MvcController], providers: [], }) diff --git a/apps/core/src/testing/aws-configuration.ts b/apps/core/src/testing/aws-configuration.ts index d32af705..f5cf02fe 100644 --- a/apps/core/src/testing/aws-configuration.ts +++ b/apps/core/src/testing/aws-configuration.ts @@ -13,11 +13,17 @@ export const databasePort = '8001'; export const databaseTableName = 'dashboard-api-e2e-test'; export const configureTestProcessEnv = (env: NodeJS.ProcessEnv) => { + env.AWS_ACCESS_KEY_ID = accessKeyId; + env.AWS_SECRET_ACCESS_KEY = secretAccessKey; + env.AWS_REGION = region; env.DATABASE_PORT = databasePort; env.DATABASE_ENDPOINT = databaseEndpoint; env.DATABASE_TABLE_NAME = databaseTableName; env.DATABASE_LAUNCH_LOCAL = databaseLaunchLocal; - env.AWS_ACCESS_KEY_ID = accessKeyId; - env.AWS_SECRET_ACCESS_KEY = secretAccessKey; - env.AWS_REGION = region; +}; + +export const configureEdgeTestProcessEnv = (env: NodeJS.ProcessEnv) => { + configureTestProcessEnv(env); + env.AUTH_MODE = 'edge'; + env.EDGE_ENDPOINT = 'https://1.2.3.4'; }; diff --git a/apps/core/src/types/strings/is-string-with-value.ts b/apps/core/src/types/strings/is-string-with-value.ts new file mode 100644 index 00000000..0398855e --- /dev/null +++ b/apps/core/src/types/strings/is-string-with-value.ts @@ -0,0 +1,7 @@ +const isString = (value: unknown): value is string => { + return typeof value === 'string'; +}; + +export const isStringWithValue = (value: unknown): value is string => { + return isString(value) && value !== ''; +}; diff --git a/cdk/lib/core/core-service.ts b/cdk/lib/core/core-service.ts index 644539d2..f1d41ac7 100644 --- a/cdk/lib/core/core-service.ts +++ b/cdk/lib/core/core-service.ts @@ -23,6 +23,7 @@ export interface CoreServiceProps { readonly userPoolId: string; readonly authMode: string; readonly domainName?: string; + readonly edgeEndpoint?: string; } export class CoreService extends Construct { @@ -40,6 +41,7 @@ export class CoreService extends Construct { userPoolId, authMode, domainName, + edgeEndpoint, } = props; const serviceSourceRolePrincipal = new ServicePrincipal( @@ -141,6 +143,10 @@ export class CoreService extends Construct { name: 'AUTH_MODE', value: authMode, }, + { + name: 'EDGE_ENDPOINT', + value: edgeEndpoint ? edgeEndpoint : '', + }, ], }, }, diff --git a/cdk/lib/iot-application-stack.ts b/cdk/lib/iot-application-stack.ts index 8fc85cf6..5fc5718e 100644 --- a/cdk/lib/iot-application-stack.ts +++ b/cdk/lib/iot-application-stack.ts @@ -21,6 +21,7 @@ export class IotApplicationStack extends Stack { super(scope, id, props); const authMode = this.node.tryGetContext('authMode') as string; + const edgeEndpoint = this.node.tryGetContext('edgeEndpoint') as string; const { logGroup: { logGroupArn }, @@ -98,6 +99,7 @@ export class IotApplicationStack extends Stack { userPoolClientId: userPoolClientId, userPoolId: userPoolId, authMode: authMode ? authMode : AuthModeOptions.COGNITO, + edgeEndpoint, }, }); diff --git a/cdk/package.json b/cdk/package.json index 7d954d6b..b4dc2edb 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -20,7 +20,7 @@ "lint:fix": "eslint \"{bin,lib}/**/*.ts\" --fix" }, "dependencies": { - "aws-cdk-lib": "2.130.0", + "aws-cdk-lib": "2.131.0", "constructs": "^10.2.70", "source-map-support": "^0.5.21" }, diff --git a/deployCdk.js b/deployCdk.js index 479a6b3b..8c896919 100755 --- a/deployCdk.js +++ b/deployCdk.js @@ -5,6 +5,7 @@ const { execSync } = require('child_process'); const cognito = 'AWS Cognito'; const sso = 'AWS IAM Identity Center (formerly AWS SSO)'; +const edge = 'Edge'; const askQuestions = () => { const questions = [ @@ -13,7 +14,7 @@ const askQuestions = () => { name: 'AUTH', message: 'Welcome to IoT Application. Please select an authentication mode.', default: cognito, - choices: [cognito, sso], + choices: [cognito, sso, edge], } ]; @@ -30,6 +31,16 @@ const run = async () => { } else if (AUTH === sso) { execSync("yarn workspace cdk deploy:no-review:sso", { stdio: 'inherit' }); console.log('Please follow README instructions to complete SSO setup after deployment: https://github.com/awslabs/iot-application/tree/main/deploymentguide'); + } else if (AUTH === edge) { + const edgeQuestion = [ + { + type: 'input', + name: 'ENDPOINT', + message: 'What is the ip/hostname of your edge gateway? (example: htttps://1.2.3.4.5)', + } + ]; + const { ENDPOINT } = await inquirer.prompt(edgeQuestion); + execSync(`yarn workspace cdk cdk deploy -c authMode=edge -c edgeEndpoint="${ENDPOINT}" --all --require-approval never`, { stdio: 'inherit' }); } else { execSync("yarn workspace cdk deploy:no-review:cognito", {stdio: 'inherit' }); } diff --git a/package.json b/package.json index 7b4283b4..98716e92 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "clean": "npx turbo run clean && rimraf .turbo && rimraf --glob *.tsbuildinfo", "clean:full": "npx turbo run clean:full && rimraf .turbo && rimraf --glob *.tsbuildinfo && rimraf node_modules", "dev": "turbo run dev", - "deploy": "yarn install && yarn clean && node deployCdk.js", + "deploy": "yarn install && yarn clean && yarn workspace cdk bootstrap && node deployCdk.js", "gen:types": "openapi -i ${npm_package_config_docs} -o ${npm_package_config_genpath} ${npm_package_config_genconfig} && yarn format:gen:types", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "format:gen:types": "prettier --write \"${npm_package_config_genpath}/**/*.ts\"", diff --git a/turbo.json b/turbo.json index bd5875d6..68b625c0 100644 --- a/turbo.json +++ b/turbo.json @@ -31,6 +31,7 @@ "DATABASE_TABLE_NAME", "DATABASE_PORT", "DATABASE_LAUNCH_LOCAL", + "EDGE_ENDPOINT", "PUBLIC_URL", "NODE_ENV", "SERVICE_ENDPOINTS", diff --git a/yarn.lock b/yarn.lock index f17f2111..8548e833 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1048,48 +1048,6 @@ "@aws-sdk/util-utf8-node" "3.6.1" tslib "^2.0.0" -"@aws-sdk/client-iot-events@3.354.0": - version "3.354.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-iot-events/-/client-iot-events-3.354.0.tgz#dc5932dc9315dd0b320ea3cc1e59b83ccba2d9b7" - integrity sha512-rD+a6qWB6wWOO97lNZdcYizR55Skmn8Tvs1WMEWw2xZBg7H0RcUJ3+ErxMPHnd41R/9fwyoNOjXPlC5eBACQ4g== - dependencies: - "@aws-crypto/sha256-browser" "3.0.0" - "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/client-sts" "3.354.0" - "@aws-sdk/config-resolver" "3.354.0" - "@aws-sdk/credential-provider-node" "3.354.0" - "@aws-sdk/fetch-http-handler" "3.353.0" - "@aws-sdk/hash-node" "3.347.0" - "@aws-sdk/invalid-dependency" "3.347.0" - "@aws-sdk/middleware-content-length" "3.347.0" - "@aws-sdk/middleware-endpoint" "3.347.0" - "@aws-sdk/middleware-host-header" "3.347.0" - "@aws-sdk/middleware-logger" "3.347.0" - "@aws-sdk/middleware-recursion-detection" "3.347.0" - "@aws-sdk/middleware-retry" "3.354.0" - "@aws-sdk/middleware-serde" "3.347.0" - "@aws-sdk/middleware-signing" "3.354.0" - "@aws-sdk/middleware-stack" "3.347.0" - "@aws-sdk/middleware-user-agent" "3.352.0" - "@aws-sdk/node-config-provider" "3.354.0" - "@aws-sdk/node-http-handler" "3.350.0" - "@aws-sdk/smithy-client" "3.347.0" - "@aws-sdk/types" "3.347.0" - "@aws-sdk/url-parser" "3.347.0" - "@aws-sdk/util-base64" "3.310.0" - "@aws-sdk/util-body-length-browser" "3.310.0" - "@aws-sdk/util-body-length-node" "3.310.0" - "@aws-sdk/util-defaults-mode-browser" "3.353.0" - "@aws-sdk/util-defaults-mode-node" "3.354.0" - "@aws-sdk/util-endpoints" "3.352.0" - "@aws-sdk/util-retry" "3.347.0" - "@aws-sdk/util-user-agent-browser" "3.347.0" - "@aws-sdk/util-user-agent-node" "3.354.0" - "@aws-sdk/util-utf8" "3.310.0" - "@smithy/protocol-http" "^1.0.1" - "@smithy/types" "^1.0.0" - tslib "^2.5.0" - "@aws-sdk/client-iot-events@3.515.0": version "3.515.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-iot-events/-/client-iot-events-3.515.0.tgz#1242849f5948a81fb82b1391c29450fd25cd31ff" @@ -6863,15 +6821,15 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cloudscape-design/collection-hooks@1.0.36", "@cloudscape-design/collection-hooks@^1.0.36": +"@cloudscape-design/collection-hooks@1.0.36": version "1.0.36" resolved "https://registry.yarnpkg.com/@cloudscape-design/collection-hooks/-/collection-hooks-1.0.36.tgz#689ce98918274d74bb15d03d21139192779fd638" integrity sha512-IInI1NLKnv4ihIglkI1se33TxiUjQ/0shZ3t8n02VLv0fhhT4p0ObISoxHScNfWk9eJ1+EYL1fTcc8yJQF/7/Q== -"@cloudscape-design/collection-hooks@^1.0.0": - version "1.0.25" - resolved "https://registry.yarnpkg.com/@cloudscape-design/collection-hooks/-/collection-hooks-1.0.25.tgz#6444158d1a6dbc2094ea068d7787525cab6d41d4" - integrity sha512-2p6sa7eYko1VqMwjEWG3H/Ka5DlsPhwEBAqmY+pNQ9IunSLv/i3JFTSun5W1OUUXCbeqTSndagjsZG0gIlXvFw== +"@cloudscape-design/collection-hooks@^1.0.0", "@cloudscape-design/collection-hooks@^1.0.37": + version "1.0.37" + resolved "https://registry.yarnpkg.com/@cloudscape-design/collection-hooks/-/collection-hooks-1.0.37.tgz#c91d3197275520d4fc09a1b4242300662df385ef" + integrity sha512-uXGfKwhF3cqLFRYDOhA9DGWcnwwZfL6N81wFdMG5qN0WsFgIUBvDCP7XWfSnMS77Y1TSdUXGWsW5Fnoi9XJpnQ== "@cloudscape-design/component-toolkit@^1.0.0-beta": version "1.0.0-beta.27" @@ -7604,10 +7562,10 @@ fastq "^1.17.0" glob "^10.3.4" -"@fastify/view@^8.2.0": - version "8.2.0" - resolved "https://registry.yarnpkg.com/@fastify/view/-/view-8.2.0.tgz#80ef9569198ca3a0f3207fa25b2dd6d0c12fc4c7" - integrity sha512-hBSiBofCnJNlPHEMZWpO1SL84eqOaqujJ1hR3jntFyZZCkweH5jMs12DKYyGesjVll7SJFRRxPUBB8kmUmneRQ== +"@fastify/view@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@fastify/view/-/view-9.0.0.tgz#563386b06eb26fb9a17db872c46f8b77af23c2cb" + integrity sha512-4KSM75Ad7Q9adQWJK17XAJlmwNE9HhIe8TdSTnvFzDvt0aOwmS4iY6k+Xd4NWuEk0JbteS6kzo0eZOr0ZZgPVg== dependencies: fastify-plugin "^4.0.0" hashlru "^2.3.0" @@ -7703,13 +7661,13 @@ "@formatjs/icu-skeleton-parser" "1.7.0" tslib "^2.4.0" -"@formatjs/icu-messageformat-parser@2.7.5": - version "2.7.5" - resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.5.tgz#6c12c08544eafef874df13b30729daf7b4dbd089" - integrity sha512-zCB53HdGDibh6/2ISEN3TGsFQruQ6gGKMFV94qHNyVrs0tNO6ncKhV0vq0n3Ydz8ipIQ2GaYAvfCoimNOVvKqA== +"@formatjs/icu-messageformat-parser@2.7.6": + version "2.7.6" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz#3d69806de056d2919d53dad895a5ff4851e4e9ff" + integrity sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA== dependencies: "@formatjs/ecma402-abstract" "1.18.2" - "@formatjs/icu-skeleton-parser" "1.7.2" + "@formatjs/icu-skeleton-parser" "1.8.0" tslib "^2.4.0" "@formatjs/icu-skeleton-parser@1.6.0": @@ -7736,10 +7694,10 @@ "@formatjs/ecma402-abstract" "1.18.0" tslib "^2.4.0" -"@formatjs/icu-skeleton-parser@1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.7.2.tgz#ffbdd535c33249635ad0e54a34813194287a1567" - integrity sha512-nlIXVv280bjGW3ail5Np1+xgGKBnMhwQQIivgbk9xX0af8ESQO+y2VW9TOY7mCrs3WH786uVpZlLimXAlXH7SA== +"@formatjs/icu-skeleton-parser@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz#5f3d3a620c687d6f8c180d80d1241e8f213acf79" + integrity sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA== dependencies: "@formatjs/ecma402-abstract" "1.18.2" tslib "^2.4.0" @@ -7834,12 +7792,12 @@ intl-messageformat "10.5.8" tslib "^2.4.0" -"@formatjs/ts-transformer@3.13.11", "@formatjs/ts-transformer@^3.13.9": - version "3.13.11" - resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-3.13.11.tgz#c3603ff59c4b3e54535a26b8e204f6ccdacd5a91" - integrity sha512-qOAGGUQC7GSMMaAFy2MAlC8REM58lUNi8W+VlRb57cDUpge46x2hP33Mzu5N1xmm6OCqDQTRXws5jEOcRI3C8Q== +"@formatjs/ts-transformer@3.13.12", "@formatjs/ts-transformer@^3.13.9": + version "3.13.12" + resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-3.13.12.tgz#5984b7b92492580bfd09deb09a5b57589b757c5f" + integrity sha512-uf1+DgbsCrzHAg7uIf0QlzpIkHYxRSRig5iJa9FaoUNIDZzNEE2oW/uLLLq7I9Z2FLIPhbmgq8hbW40FoQv+Fg== dependencies: - "@formatjs/icu-messageformat-parser" "2.7.5" + "@formatjs/icu-messageformat-parser" "2.7.6" "@types/json-stable-stringify" "^1.0.32" "@types/node" "14 || 16 || 17" chalk "^4.0.0" @@ -7934,34 +7892,32 @@ dependencies: "@iot-app-kit/charts-core" "^2.1.2" -"@iot-app-kit/components@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/components/-/components-10.0.0.tgz#bb9800684fa8f983640f26e68994ca79ae554b97" - integrity sha512-icP1RqjS/+dzXPo/nzWqInLKH8+TombkCjzRczJ5VNOWilrMrvhXspW+yCRpVNl3q3gti9KpCbXjs6BI93Wu+g== +"@iot-app-kit/components@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/components/-/components-10.1.0.tgz#d1b74eb001f57b457c41988577d2330879518cca" + integrity sha512-2V0XScZ7rrai+gUoTW0LPGz3XGD23+QBx4Rl/tNLaeATpISEvh2MPZmMdkSKSEOLhmKcxq+03wvkTTwqIHbtgA== dependencies: "@awsui/collection-hooks" "^1.0.51" "@awsui/components-react" "^3.0.0" "@awsui/design-tokens" "^3.0.41" - "@iot-app-kit/core" "10.0.0" - "@iot-app-kit/related-table" "10.0.0" + "@iot-app-kit/core" "10.1.0" + "@iot-app-kit/related-table" "10.1.0" "@stencil/core" "^2.7.0" "@synchro-charts/core" "7.2.0" styled-components "^5.3.11" -"@iot-app-kit/core-util@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/core-util/-/core-util-10.0.0.tgz#2ae41f48520d13c470dc3f80151ddca95bcc2eb8" - integrity sha512-Ny+ge/aLE3daaVkwSJ2EephxmTenKkmYkXy7CRd0vezkO9wuqeQiJvgbzdVO7dfujDwdnVNir7tiCny1H/1Byw== +"@iot-app-kit/core-util@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/core-util/-/core-util-10.1.0.tgz#a3f9eb0fdc6e61580f609c2624fbe8b4cd94a6a4" + integrity sha512-3PygngkcDLret+LDOAPcRNnO+PIyXp6C5u7ekZ/poVWCjFD0AzGJi9fwNpFBEI3dE+EhvbI0c4xolY9tzxCbyA== dependencies: - "@aws-sdk/client-iot-events" "3.354.0" - "@aws-sdk/client-iotsitewise" "3.456.0" - "@iot-app-kit/core" "10.0.0" + "@iot-app-kit/core" "10.1.0" lodash.difference "4.5.0" -"@iot-app-kit/core@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/core/-/core-10.0.0.tgz#7aaad5e0ff7335e62e9fc4b8b3a9faffee0801d8" - integrity sha512-Cizxn0AtaEssThJ4CWpWwvXiLZ5NBOKUy1RvpTfjo0BFYDyM0yT7Dmqh1fvtBTe87+8F7mFSg+EoDbbglfNeHA== +"@iot-app-kit/core@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/core/-/core-10.1.0.tgz#8393305538846ae87bcc70e08a0d390a87452959" + integrity sha512-uTuwSMrh/3soUD6sH82fJok2SvmNhUVWMe6KYHe3q6S0KauidnXnJpYckWempegkzoLbI7T4SzK1W7IJ7uaWWQ== dependencies: d3-array "^3.2.4" intervals-fn "^3.0.3" @@ -7970,17 +7926,17 @@ rxjs "^7.8.1" uuid "^9.0.0" -"@iot-app-kit/dashboard@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/dashboard/-/dashboard-10.0.0.tgz#f528662c0054746306070af3323969763abd773d" - integrity sha512-SLx/qLz/x1a/qLB15jBddZtd4b5oanWPKMiGxLgWJgvckqw0Ca/ENnxzPu25e8xO/MrThH63zjVJ7z/kUi4V8A== +"@iot-app-kit/dashboard@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/dashboard/-/dashboard-10.1.0.tgz#ae0e07634f516b457608eab8911daa08bff60d5a" + integrity sha512-R6LLgju1z1eJYkR6eZ1lwe4SeS00aSN2ydjHYxZDafqksFL8xrqGwZPrlFU6h8I7HXE/3292EtAEGFMKKqCUMg== dependencies: "@iot-app-kit/charts-core" "2.1.2" - "@iot-app-kit/components" "10.0.0" - "@iot-app-kit/core" "10.0.0" - "@iot-app-kit/core-util" "10.0.0" - "@iot-app-kit/react-components" "10.0.0" - "@iot-app-kit/source-iotsitewise" "10.0.0" + "@iot-app-kit/components" "10.1.0" + "@iot-app-kit/core" "10.1.0" + "@iot-app-kit/core-util" "10.1.0" + "@iot-app-kit/react-components" "10.1.0" + "@iot-app-kit/source-iotsitewise" "10.1.0" "@popperjs/core" "^2.11.8" "@tanstack/react-query" "^4.29.15" aws-sdk-client-mock "^3.0.0" @@ -8001,20 +7957,20 @@ turbowatch "^2.29.4" uuid "^9.0.0" -"@iot-app-kit/react-components@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/react-components/-/react-components-10.0.0.tgz#3bbdf5f99743adb6d48dcf2d698be882b46fc681" - integrity sha512-iRB/RCnkp3SbzqdwSd/IQOUebC9JRNcZpO9Wmaf8lUGjNyZ5xRhpmo7vxEISXvWD1sPJpAvK+oDVUyX5ryxzpA== +"@iot-app-kit/react-components@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/react-components/-/react-components-10.1.0.tgz#60a2c1819222c06d91f193626506d185234d9df9" + integrity sha512-gvbe0dTkVUzAGfyWcWHW/RieccTWDps+temOBCJLgFyY+C/T8a7QOf2WzItf6gdcfhgAPxpo84pb++zQwCImPg== dependencies: "@cloudscape-design/collection-hooks" "1.0.36" "@cloudscape-design/components" "3.0.518" "@cloudscape-design/design-tokens" "3.0.34" "@iot-app-kit/charts" "2.1.2" "@iot-app-kit/charts-core" "2.1.2" - "@iot-app-kit/components" "10.0.0" - "@iot-app-kit/core" "10.0.0" - "@iot-app-kit/core-util" "10.0.0" - "@iot-app-kit/source-iottwinmaker" "10.0.0" + "@iot-app-kit/components" "10.1.0" + "@iot-app-kit/core" "10.1.0" + "@iot-app-kit/core-util" "10.1.0" + "@iot-app-kit/source-iottwinmaker" "10.1.0" color "^4.2.3" copy-to-clipboard "^3.3.3" d3-array "^3.2.3" @@ -8045,22 +8001,20 @@ video.js "8.3.0" zustand "^4.3.9" -"@iot-app-kit/related-table@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/related-table/-/related-table-10.0.0.tgz#114066a3a276cf9529c5bb2db8747bb1f06a39fd" - integrity sha512-VZXshigJhKG6p9zJWPwe+r+vfglMd2mO4N5ozGrQJVSTC4Yyb1rfVpV+2fyzA+ivUvIwPjw/MRModuNr9XZjdw== +"@iot-app-kit/related-table@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/related-table/-/related-table-10.1.0.tgz#655d704582936921c0010eafb66dae38c4d97e74" + integrity sha512-si+esNlKYbdw+yAPJqpTbqUEdsUmBM8h+nWVycvs0x2n8qW7oR/nKH0NDNHaDfWGHXbXERjTxmnxqk/1cEI3yw== dependencies: uuid "^9.0.0" -"@iot-app-kit/source-iotsitewise@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/source-iotsitewise/-/source-iotsitewise-10.0.0.tgz#4a4010a5109cd1fed791b65d0a2afbb488dc066c" - integrity sha512-RNLPFY9e0aHfq7CirFEetWd+dSIkoeuU2aKjeeCLIkBiT+T7LQmvRUFrQW92aNykgsqILhEtB2gEtlmNls1DLA== +"@iot-app-kit/source-iotsitewise@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/source-iotsitewise/-/source-iotsitewise-10.1.0.tgz#613b2589fdf91801391b198fd1638f1da0343972" + integrity sha512-QdUJLL2Fi4K4UWbHcH26mgA21nZNOOf3OrtRK+KlVpswQ5inlvVibVLRUKuUpWtFjK5Vz8FlwS0xgtEhpTs8Ag== dependencies: - "@aws-sdk/client-iot-events" "3.354.0" - "@aws-sdk/client-iotsitewise" "3.456.0" - "@iot-app-kit/core" "10.0.0" - "@iot-app-kit/core-util" "10.0.0" + "@iot-app-kit/core" "10.1.0" + "@iot-app-kit/core-util" "10.1.0" "@synchro-charts/core" "7.2.0" dataloader "^2.2.2" lodash.isequal "^4.5.0" @@ -8068,10 +8022,10 @@ lodash.uniqwith "^4.5.0" rxjs "^7.8.1" -"@iot-app-kit/source-iottwinmaker@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@iot-app-kit/source-iottwinmaker/-/source-iottwinmaker-10.0.0.tgz#bad79af524dd4a43ccc9add399a42aa9e3a38e7e" - integrity sha512-WBMI1b7kH+M9H8joLT781e9ZZS9Wy4H8k4FVMJxDCPGvGSZJuC0n9AXp1uqXqJO1FSsWk7ejNFx6di+9qgS8rA== +"@iot-app-kit/source-iottwinmaker@10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@iot-app-kit/source-iottwinmaker/-/source-iottwinmaker-10.1.0.tgz#56ba5b35103f81d20c6ab9a107a426158ba31449" + integrity sha512-QOLef7FN3r8yAMINtwDwLPelRNg8grJB/pUu5m3pRh13MfcDuy83Gj8AV/6bwriPTxSRxAcBU5Z/5InZjJDebA== dependencies: "@aws-sdk/client-iotsitewise" "3.456.0" "@aws-sdk/client-iottwinmaker" "3.335.0" @@ -8080,7 +8034,7 @@ "@aws-sdk/client-s3" "3.335.0" "@aws-sdk/client-secrets-manager" "3.353.0" "@aws-sdk/url-parser" "3.374.0" - "@iot-app-kit/core" "10.0.0" + "@iot-app-kit/core" "10.1.0" "@tanstack/query-core" "^4.29.15" lodash "^4.17.21" rxjs "^7.8.1" @@ -10102,20 +10056,20 @@ resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.36.1.tgz#79f8c1a539d47c83104210be2388813a7af2e524" integrity sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA== -"@tanstack/query-core@5.22.2": - version "5.22.2" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.22.2.tgz#af67d41b0b4a3e846c2325f32540f39ca0d4788d" - integrity sha512-z3PwKFUFACMUqe1eyesCIKg3Jv1mysSrYfrEW5ww5DCDUD4zlpTKBvUDaEjsfZzL3ULrFLDM9yVUxI/fega1Qg== +"@tanstack/query-core@5.24.1": + version "5.24.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.24.1.tgz#d40928dec22b47df97fb2648e8c499772e8d7eb2" + integrity sha512-DZ6Nx9p7BhjkG50ayJ+MKPgff+lMeol7QYXkvuU5jr2ryW/4ok5eanaS9W5eooA4xN0A/GPHdLGOZGzArgf5Cg== "@tanstack/query-devtools@5.24.0": version "5.24.0" resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.24.0.tgz#b9b7828d42d5034415b1973ff4a154e880b17d59" integrity sha512-pThim455t69zrZaQKa7IRkEIK8UBTS+gHVAdNfhO72Xh4rWpMc63ovRje5/n6iw63+d6QiJzVadsJVdPoodSeQ== -"@tanstack/react-query-devtools@^5.24.0": - version "5.24.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.24.0.tgz#a3d0cfdc9ef72977e3c905230d4cd805dc377920" - integrity sha512-ae3TSg9hKSicLP6TZMj4hglLsLBaS8O3ex/mgHZNQ8yKCDLnVoLO1a9EqYWPzw2k+uaK5uMCKBAVbLVBLx93oA== +"@tanstack/react-query-devtools@^5.24.1": + version "5.24.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.24.1.tgz#e3a8ea71115fb899119126e1507fc340ee9d9496" + integrity sha512-qa4SEugN+EF8JJXcpsM9Lu05HfUv5cvHvLuB0uw/81eJZyNHFdtHFBi5RLCgpBrOyVMDfH8UQ3VBMqXzFKV68A== dependencies: "@tanstack/query-devtools" "5.24.0" @@ -10127,12 +10081,12 @@ "@tanstack/query-core" "4.36.1" use-sync-external-store "^1.2.0" -"@tanstack/react-query@^5.22.2": - version "5.22.2" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.22.2.tgz#e5fce278fbdd026fc1d561a4505142b9f93549d7" - integrity sha512-TaxJDRzJ8/NWRT4lY2jguKCrNI6MRN+67dELzPjNUlvqzTxGANlMp68l7aC7hG8Bd1uHNxHl7ihv7MT50i/43A== +"@tanstack/react-query@^5.24.1": + version "5.24.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.24.1.tgz#bcb913febe0d813cec1fda7783298d07aa998b20" + integrity sha512-4+09JEdO4d6+Gc8Y/g2M/MuxDK5IY0QV8+2wL2304wPKJgJ54cBbULd3nciJ5uvh/as8rrxx6s0mtIwpRuGd1g== dependencies: - "@tanstack/query-core" "5.22.2" + "@tanstack/query-core" "5.24.1" "@testing-library/dom@^9.0.0": version "9.2.0" @@ -10820,10 +10774,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@^18.2.57": - version "18.2.57" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.57.tgz#147b516d8bdb2900219acbfc6f939bdeecca7691" - integrity sha512-ZvQsktJgSYrQiMirAN60y4O/LRevIV8hUzSOSNB6gfR3/o3wCBFQx3sPwIYtuDMeiVgsSS3UzCV26tEzgnfvQw== +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@^18.2.61": + version "18.2.61" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.61.tgz#5607308495037436779939ec0348a5816c08799d" + integrity sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -11917,10 +11871,10 @@ aws-amplify@^5.3.11: "@aws-amplify/storage" "5.9.5" tslib "^2.0.0" -aws-cdk-lib@2.130.0: - version "2.130.0" - resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.130.0.tgz#ce1b8cd1bcf9ca040d600c2570dfbac9b2df3cfb" - integrity sha512-yK7ibePipdjlI4AFM94fwwtsCkmpWJ0JFZTMPahahC/3Pxe/BA/nnI/4Namvl5QPxW5QlU0xQYU7cywioq3RQg== +aws-cdk-lib@2.131.0: + version "2.131.0" + resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.131.0.tgz#6e336e9a3e77b07052d28c017ea020f5c9948341" + integrity sha512-9XLgiTgY+q0S3K93VPeJO0chIN8BZwZ3aSrILvF868Dz+0NTNrD2m5M0xGK5Rw0uoJS+N+DvGaz/2hLAiVqcBw== dependencies: "@aws-cdk/asset-awscli-v1" "^2.2.202" "@aws-cdk/asset-kubectl-v20" "^2.1.2" @@ -12061,18 +12015,18 @@ babel-loader@^8.2.3: make-dir "^3.1.0" schema-utils "^2.6.5" -babel-plugin-formatjs@^10.5.12: - version "10.5.12" - resolved "https://registry.yarnpkg.com/babel-plugin-formatjs/-/babel-plugin-formatjs-10.5.12.tgz#d7af7e1d224bc5fc1581371d3a9b55da38fdacfa" - integrity sha512-pOkJoD5nURdes/6NpL5JeEoF8Nz18yAUmbjhc0jyWtK26QDPUDLsXk2kas5jbu5TFMFI+GFUQ9cSVCewbuvC7g== +babel-plugin-formatjs@^10.5.13: + version "10.5.13" + resolved "https://registry.yarnpkg.com/babel-plugin-formatjs/-/babel-plugin-formatjs-10.5.13.tgz#2856143aae4b7fb0376765e96053d79baadea2f1" + integrity sha512-0dtMhoa6q0P5lUHBphLd8/y+CRlh5IG3Rq+Wk64kOEDwUVZF8xq1qMSjC3iw1wH4tmP2qTzBL+Bz1xEjyBd/ew== dependencies: "@babel/core" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "7" "@babel/traverse" "7" "@babel/types" "^7.12.11" - "@formatjs/icu-messageformat-parser" "2.7.5" - "@formatjs/ts-transformer" "3.13.11" + "@formatjs/icu-messageformat-parser" "2.7.6" + "@formatjs/ts-transformer" "3.13.12" "@types/babel__core" "^7.1.7" "@types/babel__helper-plugin-utils" "^7.10.0" "@types/babel__traverse" "^7.1.7" @@ -13389,10 +13343,10 @@ cwd@0.10.0: find-pkg "^0.1.2" fs-exists-sync "^0.1.0" -cytoscape@^3.27.0: - version "3.27.0" - resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.27.0.tgz#5141cd694570807c91075b609181bce102e0bb88" - integrity sha512-pPZJilfX9BxESwujODz5pydeGi+FBrXq1rcaB1mfhFXXFJ9GjE6CNndAk+8jPzoXGD+16LtSS4xlYEIUiW4Abg== +cytoscape@^3.28.1: + version "3.28.1" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.28.1.tgz#f32c3e009bdf32d47845a16a4cd2be2bbc01baf7" + integrity sha512-xyItz4O/4zp9/239wCcH8ZcFuuZooEeF8KHRmzjDfGdXsj3OG9MFSMA0pJE0uX3uCN/ygof6hHf4L7lst+JaDg== dependencies: heap "^0.2.6" lodash "^4.17.21" @@ -16086,12 +16040,7 @@ ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0, ignore@^5.2.4: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" - integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== - -ignore@^5.3.1: +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -19893,10 +19842,10 @@ postcss@^7.0.35: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.23, postcss@^8.4.27, postcss@^8.4.32, postcss@^8.4.4: - version "8.4.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" - integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== +postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.23, postcss@^8.4.27, postcss@^8.4.35, postcss@^8.4.4: + version "8.4.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7" + integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== dependencies: nanoid "^3.3.7" picocolors "^1.0.0" @@ -21235,14 +21184,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.6.0: +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== @@ -23083,13 +23025,13 @@ vite-plugin-dynamic-import@^1.5.0: optionalDependencies: fsevents "~2.3.2" -"vite@^3.1.0 || ^4.0.0 || ^5.0.0-0", vite@^5.0.12: - version "5.0.12" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" - integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== +"vite@^3.1.0 || ^4.0.0 || ^5.0.0-0", vite@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.1.5.tgz#bdbc2b15e8000d9cc5172f059201178f9c9de5fb" + integrity sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q== dependencies: esbuild "^0.19.3" - postcss "^8.4.32" + postcss "^8.4.35" rollup "^4.2.0" optionalDependencies: fsevents "~2.3.3"