Skip to content

Commit

Permalink
Add state provider status popup (#2217)
Browse files Browse the repository at this point in the history
  • Loading branch information
usenko-timur authored Apr 10, 2024
1 parent e0cce3e commit e8455a1
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Unreleased
Added
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- Add the state provider status action on the failover controller modal.

- Add the "Migrations" page.

- Add "rebalancer_mode" and "rebalancer" options on web UI.
Expand Down
8 changes: 7 additions & 1 deletion webui/src/models/cluster/failover/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,32 @@ import type {
} from 'src/generated/graphql-typing-ts';
import { app } from 'src/models';

import type { Failover } from './types';
import type { Failover, StateProviderStatus } from './types';

const { some } = app.utils;

// events
export const failoverModalOpenEvent = app.domain.createEvent('failover modal open event');
export const failoverModalCloseEvent = app.domain.createEvent('failover modal close event');
export const stateProviderStatusGetEvent = app.domain.createEvent('state provider status popover open event');

export const changeFailoverEvent = app.domain.createEvent<ChangeFailoverMutationVariables>('change failover event');

// stores
export const $failoverModalVisible = app.domain.createStore(false);
export const $failoverModalError = app.domain.createStore<string>('');
export const $failover = app.domain.createStore<Failover>(null);
export const $stateProviderStatus = app.domain.createStore<StateProviderStatus[]>([]);

// effects
export const getFailoverFx = app.domain.createEffect<void, GetFailoverParamsQuery>('get failover');
export const changeFailoverFx = app.domain.createEffect<ChangeFailoverMutationVariables, ChangeFailoverMutation>(
'change failover'
);
export const getStateProviderStatusFx = app.domain.createEffect<
void,
{ cluster: { failover_state_provider_status: StateProviderStatus[] } }
>('get state provider status');

// computed
export const $failoverModal = combine({
Expand Down
23 changes: 22 additions & 1 deletion webui/src/models/cluster/failover/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ import { forward } from 'effector';

import graphql from 'src/api/graphql';
import { app } from 'src/models';
import { changeFailoverMutation, getFailoverParams } from 'src/store/request/queries.graphql';
import { changeFailoverMutation, getFailoverParams, getStateProviderStatus } from 'src/store/request/queries.graphql';

import { clusterPageCloseEvent } from '../page';
import { refreshServerListAndClusterEvent } from '../server-list';
import {
$failover,
$failoverModalError,
$failoverModalVisible,
$stateProviderStatus,
changeFailoverEvent,
changeFailoverFx,
failoverModalCloseEvent,
failoverModalOpenEvent,
getFailoverFx,
getStateProviderStatusFx,
stateProviderStatusGetEvent,
} from '.';

const { notifyEvent, notifyErrorEvent } = app;
Expand All @@ -25,6 +28,11 @@ forward({
to: getFailoverFx,
});

forward({
from: stateProviderStatusGetEvent,
to: getStateProviderStatusFx,
});

forward({
from: changeFailoverEvent,
to: changeFailoverFx,
Expand All @@ -48,6 +56,11 @@ forward({
to: notifyErrorEvent,
});

forward({
from: getStateProviderStatusFx.failData,
to: notifyErrorEvent,
});

forward({
from: getFailoverFx.fail,
to: failoverModalCloseEvent,
Expand All @@ -68,7 +81,15 @@ $failoverModalError

$failoverModalVisible.on(failoverModalOpenEvent, trueL).reset(failoverModalCloseEvent).reset(clusterPageCloseEvent);

$stateProviderStatus
.on(getStateProviderStatusFx.doneData, (_, payload) => {
return payload.cluster.failover_state_provider_status;
})
.reset(getStateProviderStatusFx, failoverModalCloseEvent, clusterPageCloseEvent);

// effects
getFailoverFx.use(() => graphql.fetch(getFailoverParams));

changeFailoverFx.use((params) => graphql.fetch(changeFailoverMutation, params));

getStateProviderStatusFx.use(() => graphql.fetch(getStateProviderStatus));
3 changes: 2 additions & 1 deletion webui/src/models/cluster/failover/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { GetFailoverParamsQuery } from 'src/generated/graphql-typing-ts';
import type { GetFailoverParamsQuery, StateProviderStatus } from 'src/generated/graphql-typing-ts';
import type { Maybe } from 'src/models';

export type Failover = Maybe<GetFailoverParamsQuery>;
export type { StateProviderStatus };
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
toFailoverMode,
toFailoverStateProvider,
} from './FailoverModalForm.types';
import { FailoverModalFormStateProviderStatusAction } from './FailoverModalFormStateProviderStatusAction';

import { styles } from './FailoverModalForm.styles';

Expand Down Expand Up @@ -193,7 +194,9 @@ const FailoverModalFormForm = ({
handleChange,
values,
errors,
}: FailoverFormFormikProps) => {
mode,
failover,
}: FailoverFormFormikProps & FailoverFormProps) => {
const { loading, pending, error } = useStore($failoverModal);

const handleTabChange = useCallback(
Expand Down Expand Up @@ -390,6 +393,16 @@ const FailoverModalFormForm = ({
value={values.state_provider}
//@ts-ignore
onChange={handleStateProviderChange}
topRightControls={
toFailoverMode(failover?.cluster?.failover_params?.mode ?? mode) === 'stateful'
? [
<FailoverModalFormStateProviderStatusAction
key="status"
stateProvider={toFailoverStateProvider(failover?.cluster?.failover_params?.state_provider)}
/>,
]
: undefined
}
/>
{values.state_provider === 'tarantool' && (
<div className={styles.inputs}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import React, { useCallback } from 'react';
import { useEvent } from 'effector-react';
// @ts-ignore
import { Modal } from '@tarantool.io/ui-kit';

Expand All @@ -10,7 +11,8 @@ import FailoverModalFormForm, { FailoverFormProps } from './FailoverModalForm.fo
const { failoverModalCloseEvent } = cluster.failover;

const FailoverModalForm = ({ mode, failover }: FailoverFormProps) => {
const handleClose = useCallback(() => failoverModalCloseEvent(), []);
const event = useEvent(failoverModalCloseEvent);
const handleClose = useCallback(() => event(), [event]);

return (
<Modal visible className="meta-test__FailoverModal" title="Failover control" onClose={handleClose}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import React, { useCallback } from 'react';
import { css } from '@emotion/css';
import { useEvent, useStore } from 'effector-react';
// @ts-ignore
import { Button, IconFailed, IconSuccess, Text, UriLabel, withPopover } from '@tarantool.io/ui-kit';

import { cluster } from 'src/models';

import { FailoverStateProvider } from './FailoverModalForm.types';

export const styles = {
root: css`
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 4px;
`,
header: css`
display: block;
margin-bottom: 8px;
`,
};

const PopoverRoot = withPopover(Button);
const { $stateProviderStatus, stateProviderStatusGetEvent, getStateProviderStatusFx } = cluster.failover;

export const FailoverModalFormStateProviderStatusAction = ({
stateProvider,
}: {
stateProvider: FailoverStateProvider;
}) => {
const stateProviderStatusPending = useStore(getStateProviderStatusFx.pending);
const stateProviderStatus = useStore($stateProviderStatus);
const event = useEvent(stateProviderStatusGetEvent);
const handleClick = useCallback(() => {
event();
}, [event]);

return (
<PopoverRoot
size="xs"
type="button"
onClick={handleClick}
loading={stateProviderStatusPending}
disabled={stateProviderStatusPending}
popoverContent={
stateProviderStatusPending || !stateProviderStatus || stateProviderStatus.length === 0 ? undefined : (
<div className={styles.root}>
<Text className={styles.header}>
{stateProvider === 'etcd2' ? 'etcd' : stateProvider} state provider status:
</Text>
{stateProviderStatus.map((value, index) => (
<UriLabel
key={index}
uri={value.uri}
title={value.status ? 'status: true' : 'status: false'}
icon={value.status ? IconSuccess : IconFailed}
/>
))}
</div>
)
}
>
status
</PopoverRoot>
);
};
11 changes: 11 additions & 0 deletions webui/src/store/request/queries.graphql.js
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,17 @@ export const getFailoverParams = gql`
}
`;

export const getStateProviderStatus = gql`
query getStateProviderStatus {
cluster {
failover_state_provider_status {
uri
status
}
}
}
`;

export const validateFilesQuery = gql`
query validateConfig($sections: [ConfigSectionInput!]) {
cluster {
Expand Down

0 comments on commit e8455a1

Please sign in to comment.