From 4dc3a7f96594cccb0fe7a68e84b14d747078a776 Mon Sep 17 00:00:00 2001 From: Peter Makowski Date: Tue, 11 Jun 2024 15:24:09 +0200 Subject: [PATCH] feat: show msm connection info Signed-off-by: Peter Makowski --- .../components/StatusBar/StatusBar.test.tsx | 14 +++++++ .../base/components/StatusBar/StatusBar.tsx | 30 ++++++++++++-- src/app/store/msm/index.ts | 1 + src/app/store/msm/selectors.ts | 17 ++++++++ src/app/store/msm/slice.ts | 41 +++++++++++++++++++ src/app/store/msm/types/base.ts | 11 +++++ src/app/store/msm/types/enum.ts | 3 ++ src/app/store/root/types.ts | 13 ++++-- src/app/store/utils/slice.ts | 3 ++ src/root-reducer.ts | 2 + src/testing/factories/index.ts | 2 + src/testing/factories/msm.ts | 17 ++++++++ src/testing/factories/state.ts | 13 ++++++ 13 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 src/app/store/msm/index.ts create mode 100644 src/app/store/msm/selectors.ts create mode 100644 src/app/store/msm/slice.ts create mode 100644 src/app/store/msm/types/base.ts create mode 100644 src/app/store/msm/types/enum.ts create mode 100644 src/testing/factories/msm.ts diff --git a/src/app/base/components/StatusBar/StatusBar.test.tsx b/src/app/base/components/StatusBar/StatusBar.test.tsx index 0a12194c1ef..f37464de1f8 100644 --- a/src/app/base/components/StatusBar/StatusBar.test.tsx +++ b/src/app/base/components/StatusBar/StatusBar.test.tsx @@ -248,3 +248,17 @@ it("hides the feedback link in development environment", () => { screen.queryByRole("button", { name: "Give feedback" }) ).not.toBeInTheDocument(); }); + +it("displays the status message when connected to MAAS Site Manager", () => { + state.msm = factory.msmState({ + status: factory.msmStatus({ + running: "connected", + }), + }); + + renderWithMockStore(, { state }); + + expect( + screen.getByText("Connected to MAAS Site Manager") + ).toBeInTheDocument(); +}); diff --git a/src/app/base/components/StatusBar/StatusBar.tsx b/src/app/base/components/StatusBar/StatusBar.tsx index 33e5184b9bb..82d054c5275 100644 --- a/src/app/base/components/StatusBar/StatusBar.tsx +++ b/src/app/base/components/StatusBar/StatusBar.tsx @@ -1,7 +1,9 @@ -import type { ReactNode } from "react"; +import { useEffect, type ReactNode } from "react"; -import { Button, Link } from "@canonical/react-components"; -import { useSelector } from "react-redux"; +import { Button, Icon, Link } from "@canonical/react-components"; +import { useDispatch, useSelector } from "react-redux"; + +import TooltipButton from "../TooltipButton"; import { useUsabilla } from "@/app/base/hooks"; import configSelectors from "@/app/store/config/selectors"; @@ -18,6 +20,8 @@ import { isDeployedWithHardwareSync, isMachineDetails, } from "@/app/store/machine/utils"; +import { msmActions } from "@/app/store/msm"; +import msmSelectors from "@/app/store/msm/selectors"; import type { UtcDatetime } from "@/app/store/types/model"; import { NodeStatus } from "@/app/store/types/node"; import { formatUtcDatetime, getTimeDistanceString } from "@/app/utils/time"; @@ -57,6 +61,12 @@ export const StatusBar = (): JSX.Element | null => { const version = useSelector(versionSelectors.get); const maasName = useSelector(configSelectors.maasName); const allowUsabilla = useUsabilla(); + const msmRunning = useSelector(msmSelectors.running); + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(msmActions.fetch()); + }, [dispatch]); if (!(maasName && version)) { return null; @@ -101,6 +111,20 @@ export const StatusBar = (): JSX.Element | null => { :  {version} +
+ + {msmRunning === "connected" ? ( + + + Connected to MAAS Site Manager + + ) : null} + +
  • state.msm.status; +const running = createSelector(status, (status) => status?.running); +const loading = (state: RootState) => state.msm.loading; +const errors = (state: RootState) => state.msm.errors; + +const msmSelectors = { + status, + running, + loading, + errors, +}; + +export default msmSelectors; diff --git a/src/app/store/msm/slice.ts b/src/app/store/msm/slice.ts new file mode 100644 index 00000000000..79f1d64e5d5 --- /dev/null +++ b/src/app/store/msm/slice.ts @@ -0,0 +1,41 @@ +import type { PayloadAction } from "@reduxjs/toolkit"; +import { createSlice } from "@reduxjs/toolkit"; + +import type { MsmState, MsmStatus } from "./types/base"; + +import { genericInitialState } from "@/app/store/utils/slice"; + +const initialState: MsmState = { + ...genericInitialState, + status: null, +}; + +const msmSlice = createSlice({ + name: "msm", + initialState, + reducers: { + fetch: { + prepare: () => ({ + meta: { + model: "msm", + method: "status", + }, + payload: null, + }), + reducer: () => {}, + }, + fetchSuccess(state, action: PayloadAction) { + state.status = action.payload; + state.loading = false; + state.errors = null; + }, + fetchError(state, action: PayloadAction) { + state.errors = action.payload; + state.loading = false; + }, + }, +}); + +export const { actions: msmActions } = msmSlice; + +export default msmSlice.reducer; diff --git a/src/app/store/msm/types/base.ts b/src/app/store/msm/types/base.ts new file mode 100644 index 00000000000..cd78c35136f --- /dev/null +++ b/src/app/store/msm/types/base.ts @@ -0,0 +1,11 @@ +export interface MsmStatus { + smUrl: string | null; + running: "not_connected" | "pending" | "connected"; + startTime: string | null; +} + +export interface MsmState { + status: MsmStatus | null; + loading: boolean; + errors: string | null; +} diff --git a/src/app/store/msm/types/enum.ts b/src/app/store/msm/types/enum.ts new file mode 100644 index 00000000000..7ec78101ef9 --- /dev/null +++ b/src/app/store/msm/types/enum.ts @@ -0,0 +1,3 @@ +export enum MsmMeta { + MODEL = "msm", +} diff --git a/src/app/store/root/types.ts b/src/app/store/root/types.ts index ad2ed8f87e2..83e1fa6ebf9 100644 --- a/src/app/store/root/types.ts +++ b/src/app/store/root/types.ts @@ -1,9 +1,5 @@ import type { RouterState } from "redux-first-history"; -import type { ReservedIpState } from "../reservedip/types"; -import type { ReservedIpMeta } from "../reservedip/types/enum"; -import type { VMClusterMeta, VMClusterState } from "../vmcluster/types"; - import type { BootResourceState, BootResourceMeta, @@ -33,6 +29,8 @@ import type { } from "@/app/store/licensekeys/types"; import type { MachineState, MachineMeta } from "@/app/store/machine/types"; import type { MessageState, MessageMeta } from "@/app/store/message/types"; +import type { MsmState } from "@/app/store/msm/types/base"; +import type { MsmMeta } from "@/app/store/msm/types/enum"; import type { NodeDeviceState, NodeDeviceMeta, @@ -50,6 +48,8 @@ import type { PackageRepositoryMeta, } from "@/app/store/packagerepository/types"; import type { PodState, PodMeta } from "@/app/store/pod/types"; +import type { ReservedIpState } from "@/app/store/reservedip/types"; +import type { ReservedIpMeta } from "@/app/store/reservedip/types/enum"; import type { ResourcePoolState, ResourcePoolMeta, @@ -73,6 +73,10 @@ import type { TagState, TagMeta } from "@/app/store/tag/types"; import type { TokenState, TokenMeta } from "@/app/store/token/types"; import type { UserState, UserMeta } from "@/app/store/user/types"; import type { VLANState, VLANMeta } from "@/app/store/vlan/types"; +import type { + VMClusterMeta, + VMClusterState, +} from "@/app/store/vmcluster/types"; import type { ZoneState, ZoneMeta } from "@/app/store/zone/types"; export type RootState = { @@ -90,6 +94,7 @@ export type RootState = { [LicenseKeysMeta.MODEL]: LicenseKeysState; [MachineMeta.MODEL]: MachineState; [MessageMeta.MODEL]: MessageState; + [MsmMeta.MODEL]: MsmState; [NodeDeviceMeta.MODEL]: NodeDeviceState; [NodeScriptResultMeta.MODEL]: NodeScriptResultState; [NotificationMeta.MODEL]: NotificationState; diff --git a/src/app/store/utils/slice.ts b/src/app/store/utils/slice.ts index 52672cccef4..64dfc8bc60b 100644 --- a/src/app/store/utils/slice.ts +++ b/src/app/store/utils/slice.ts @@ -5,6 +5,8 @@ import type { SliceCaseReducers, } from "@reduxjs/toolkit"; +import type { MsmMeta } from "../msm/types/enum"; + import type { KeysOfUnion } from "@/app/base/types"; import type { BootResourceMeta } from "@/app/store/bootresource/types"; import type { ConfigMeta } from "@/app/store/config/types"; @@ -50,6 +52,7 @@ export type CommonStates = Omit< | ConfigMeta.MODEL | GeneralMeta.MODEL | MessageMeta.MODEL + | MsmMeta.MODEL | NodeScriptResultMeta.MODEL | StatusMeta.MODEL | ZoneMeta.MODEL diff --git a/src/root-reducer.ts b/src/root-reducer.ts index c0b6715c16d..fdb1083ff6a 100644 --- a/src/root-reducer.ts +++ b/src/root-reducer.ts @@ -18,6 +18,7 @@ import iprange from "@/app/store/iprange"; import licensekeys from "@/app/store/licensekeys"; import machine from "@/app/store/machine"; import message from "@/app/store/message"; +import msm from "@/app/store/msm"; import nodedevice from "@/app/store/nodedevice"; import nodescriptresult from "@/app/store/nodescriptresult"; import notification from "@/app/store/notification"; @@ -61,6 +62,7 @@ const createAppReducer = (routerReducer: Reducer) => licensekeys, machine, message, + msm, nodedevice, nodescriptresult, notification, diff --git a/src/testing/factories/index.ts b/src/testing/factories/index.ts index 06e856b4aca..4af43aa6d1f 100644 --- a/src/testing/factories/index.ts +++ b/src/testing/factories/index.ts @@ -44,6 +44,8 @@ export { machineStatus, machineStatuses, messageState, + msmState, + msmStatus, nodeDeviceState, nodeScriptResultState, notificationState, diff --git a/src/testing/factories/msm.ts b/src/testing/factories/msm.ts new file mode 100644 index 00000000000..76656814452 --- /dev/null +++ b/src/testing/factories/msm.ts @@ -0,0 +1,17 @@ +import { define } from "cooky-cutter"; + +import { timestamp } from "./general"; + +import type { MsmState, MsmStatus } from "@/app/store/msm/types/base"; + +const msmStatus = define({ + smUrl: "https://example.com", + running: "not_connected", + startTime: timestamp("Wed, 08 Jul. 2022 05:35:45"), +}); + +export const msm = define({ + status: msmStatus(), + loading: false, + errors: null, +}); diff --git a/src/testing/factories/state.ts b/src/testing/factories/state.ts index d8410e726ad..089fc250e78 100644 --- a/src/testing/factories/state.ts +++ b/src/testing/factories/state.ts @@ -70,6 +70,7 @@ import type { } from "@/app/store/machine/types"; import { FilterGroupKey, FilterGroupType } from "@/app/store/machine/types"; import type { MessageState } from "@/app/store/message/types"; +import type { MsmState, MsmStatus } from "@/app/store/msm/types/base"; import type { NodeDeviceState } from "@/app/store/nodedevice/types"; import type { NodeScriptResultState } from "@/app/store/nodescriptresult/types"; import type { NotificationState } from "@/app/store/notification/types"; @@ -424,6 +425,17 @@ export const messageState = define({ items: () => [], }); +export const msmStatus = define({ + running: "not_connected", + smUrl: "http://example.com", + startTime: "2021-01-01", +}); +export const msmState = define({ + status: msmStatus(), + loading: false, + errors: null, +}); + export const architecturesState = define({ ...defaultGeneralState, }); @@ -681,6 +693,7 @@ export const rootState = define({ licensekeys: licenseKeysState, machine: machineState, message: messageState, + msm: msmState, nodedevice: nodeDeviceState, notification: notificationState, nodescriptresult: nodeScriptResultState,