Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(topology): topology view #891

Merged
merged 26 commits into from
Mar 11, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5fd580e
feat(topology): topology view
Mar 8, 2023
8c65f95
fix(topology): silence match expression check notifications
Mar 9, 2023
a478a6b
fix(topology): labels should readonly
Mar 9, 2023
5073713
feat(topology): topology filters
Mar 9, 2023
b62fb96
fix(topology): enforce feature level
Mar 9, 2023
88b8662
fix(topology): also enable singles mode if realm only
Mar 9, 2023
9cc0013
fix(topology): restyle selector
Mar 9, 2023
b90c65e
chore(topology): sync display names
Mar 9, 2023
22e248c
chore(topology): remove extra hook deps
Mar 9, 2023
102e20c
chore(topology): fix typos
Mar 9, 2023
852366e
chore(topology): removing typos
Mar 9, 2023
2a3c7bb
fix(topology): mount menus to parent
Mar 9, 2023
245414d
feat(topology): flatten target filters
Mar 10, 2023
f62ff19
chore(topology): remove testing funcs
Mar 10, 2023
c960470
fix(topology): additional fixes for filters
Mar 10, 2023
59188b2
chore(topology): adjust comments
Mar 10, 2023
80c23f2
chore(topology): refactor hooks
Mar 10, 2023
51a4563
chore(topology): clean up eslint comments
Mar 10, 2023
7516661
chore(topology): center total column
Mar 10, 2023
c9441a1
feat(topology): listview expression search
Mar 10, 2023
c277358
feat(topology): enhance empty state view
Mar 10, 2023
dc6ee80
fix(topology): deep copy target object when checking matches
Mar 10, 2023
7f187ac
fix(topology): selectable area in list view
Mar 10, 2023
896ad08
chore(topology): refactor common match expre eval
Mar 10, 2023
aebdddc
fix(topology): fix filter typeahead
Mar 10, 2023
c804149
fix(topology): selectable text area should have styled cursor
Mar 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"CARD_TYPE": "Card type",
"CLEAR_FILTERS": "Clear all filters",
"CRITICAL": "CRITICAL",
"CREATE": "Create",
"CREATING": "Creating",
"CRYOSTAT_TRADEMARK": "Copyright The Cryostat Authors, The Universal Permissive License (UPL), Version 1.0",
"DATE": "Date",
"DESCRIPTION": "Description",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"@patternfly/react-icons": "^4.93.6",
"@patternfly/react-styles": "^4.92.6",
"@patternfly/react-table": "^4.112.39",
"@patternfly/react-topology": "4.91.27",
"@reduxjs/toolkit": "^1.9.3",
"@types/lodash": "^4.14.191",
"@types/react-router-dom": "^5.3.3",
Expand Down
26 changes: 17 additions & 9 deletions src/app/Archives/Archives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ import { useSubscriptions } from '@app/utils/useSubscriptions';
import { Card, CardBody, EmptyState, EmptyStateIcon, Tab, Tabs, TabTitleText, Title } from '@patternfly/react-core';
import { SearchIcon } from '@patternfly/react-icons';
import * as React from 'react';
import { StaticContext } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { of } from 'rxjs';
import { AllArchivedRecordingsTable } from './AllArchivedRecordingsTable';
import { AllTargetsArchivedRecordingsTable } from './AllTargetsArchivedRecordingsTable';

/*
This specific target is used as the "source" for the Uploads version of the ArchivedRecordingsTable.
The connectUrl is the 'uploads' because for actions performed on uploaded archived recordings,
Expand All @@ -60,12 +61,19 @@ export const uploadAsTarget: Target = {
alias: '',
};

export interface ArchivesProps {}
export type SupportedTab = 'all-archives' | 'all-targets' | 'uploads';

export interface ArchivesProps {
tab?: SupportedTab;
}

export const Archives: React.FC<ArchivesProps> = (_) => {
export const Archives: React.FC<RouteComponentProps<Record<string, never>, StaticContext, ArchivesProps>> = ({
location,
..._props
}) => {
const context = React.useContext(ServiceContext);
const addSubscription = useSubscriptions();
const [activeTab, setActiveTab] = React.useState(0);
const [activeTab, setActiveTab] = React.useState(location?.state?.tab || 'all-archives');
const [archiveEnabled, setArchiveEnabled] = React.useState(false);

React.useEffect(() => {
Expand All @@ -76,14 +84,14 @@ export const Archives: React.FC<ArchivesProps> = (_) => {

const cardBody = React.useMemo(() => {
return archiveEnabled ? (
<Tabs id="archives" activeKey={activeTab} onSelect={(evt, idx) => setActiveTab(Number(idx))}>
<Tab id="all-targets" eventKey={0} title={<TabTitleText>All Targets</TabTitleText>}>
<Tabs id="archives" activeKey={activeTab} onSelect={(evt, key) => setActiveTab(`${key}` as SupportedTab)}>
<Tab id="all-targets" eventKey={'all-archives'} title={<TabTitleText>All Targets</TabTitleText>}>
<AllTargetsArchivedRecordingsTable />
</Tab>
<Tab id="all-archives" eventKey={1} title={<TabTitleText>All Archives</TabTitleText>}>
<Tab id="all-archives" eventKey={'all-targets'} title={<TabTitleText>All Archives</TabTitleText>}>
<AllArchivedRecordingsTable />
</Tab>
<Tab id="uploads" eventKey={2} title={<TabTitleText>Uploads</TabTitleText>}>
<Tab id="uploads" eventKey={'uploads'} title={<TabTitleText>Uploads</TabTitleText>}>
<ArchivedRecordingsTable target={uploadTargetAsObs} isUploadsTable={true} isNestedTable={false} />
</Tab>
</Tabs>
Expand All @@ -106,4 +114,4 @@ export const Archives: React.FC<ArchivesProps> = (_) => {
);
};

export default Archives;
export default withRouter(Archives);
30 changes: 16 additions & 14 deletions src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisConfigForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
TemplateType,
} from '@app/Shared/Services/Api.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { NO_TARGET } from '@app/Shared/Services/Target.service';
import { NO_TARGET, Target } from '@app/Shared/Services/Target.service';
import { SelectTemplateSelectorForm } from '@app/TemplateSelector/SelectTemplateSelectorForm';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import {
Expand All @@ -64,12 +64,17 @@ import {
} from '@patternfly/react-core';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { concatMap, filter, first } from 'rxjs';
import { concatMap, filter, first, Observable } from 'rxjs';

interface AutomatedAnalysisConfigFormProps {
useTitle?: boolean;
targetObs?: Observable<Target>;
}

export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormProps> = ({ useTitle = false }) => {
export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormProps> = ({
useTitle = false,
targetObs,
}) => {
const context = React.useContext(ServiceContext);
const addSubscription = useSubscriptions();
const { t } = useTranslation();
Expand Down Expand Up @@ -103,8 +108,7 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
const refreshTemplates = React.useCallback(() => {
setIsLoading(true);
addSubscription(
context.target
.target()
(targetObs ? targetObs : context.target.target())
.pipe(
filter((target) => target !== NO_TARGET),
first(),
Expand All @@ -125,7 +129,7 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
},
})
);
}, [addSubscription, context.target, context.api, setErrorMessage, setTemplates, setIsLoading]);
}, [addSubscription, context.target, context.api, setErrorMessage, setTemplates, setIsLoading, targetObs]);

React.useEffect(() => {
addSubscription(
Expand All @@ -139,12 +143,12 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr

React.useEffect(() => {
addSubscription(
context.target.target().subscribe(() => {
(targetObs ? targetObs : context.target.target()).subscribe(() => {
refreshTemplates();
setIsLoading(false);
})
);
}, [addSubscription, context.target, refreshTemplates, setIsLoading]);
}, [addSubscription, targetObs, refreshTemplates, setIsLoading, context.target]);

const getEventString = React.useCallback((templateName: string, templateType: string) => {
let str = '';
Expand Down Expand Up @@ -340,13 +344,11 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
if (isLoading) {
return <LoadingView />;
}
return (
return useTitle ? (
<Form>
{useTitle ? (
<FormSection title={t('AutomatedAnalysisConfigForm.FORM_TITLE')}>{formContent}</FormSection>
) : (
formContent
)}
<FormSection title={t('AutomatedAnalysisConfigForm.FORM_TITLE')}>{formContent}</FormSection>
</Form>
) : (
formContent
);
};
2 changes: 1 addition & 1 deletion src/app/Dashboard/Charts/jfr/JFRMetricsChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
*/

import { CreateRecordingProps } from '@app/CreateRecording/CreateRecording';
import { DashboardCardDescriptor, DashboardCardProps, DashboardCardSizes } from '@app/Dashboard/Dashboard';
import { LoadingView } from '@app/LoadingView/LoadingView';
import { ServiceContext } from '@app/Shared/Services/Services';
import { FeatureLevel } from '@app/Shared/Services/Settings.service';
Expand All @@ -60,7 +61,6 @@ import * as React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { interval } from 'rxjs';
import { DashboardCardDescriptor, DashboardCardProps, DashboardCardSizes } from '@app/Dashboard/Dashboard';
import { DashboardCard } from '../../DashboardCard';
import { ChartContext } from './../ChartContext';
import { ControllerState, RECORDING_NAME } from './JFRMetricsChartController';
Expand Down
2 changes: 1 addition & 1 deletion src/app/Dashboard/Charts/mbean/MBeanMetricsChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
* SOFTWARE.
*/

import { DashboardCardDescriptor, DashboardCardProps, DashboardCardSizes } from '@app/Dashboard/Dashboard';
import { ServiceContext } from '@app/Shared/Services/Services';
import { FeatureLevel } from '@app/Shared/Services/Settings.service';
import useDayjs from '@app/utils/useDayjs';
Expand All @@ -56,7 +57,6 @@ import _ from 'lodash';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { interval } from 'rxjs';
import { DashboardCardDescriptor, DashboardCardProps, DashboardCardSizes } from '@app/Dashboard/Dashboard';
import { DashboardCard } from '../../DashboardCard';
import { ChartContext } from './../ChartContext';
import { MBeanMetrics } from './MBeanMetricsChartController';
Expand Down
2 changes: 1 addition & 1 deletion src/app/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export const Dashboard: React.FC<DashboardProps> = (_) => {
);

return (
<TargetView pageTitle={t('Dashboard.PAGE_TITLE')} compactSelect={false}>
<TargetView pageTitle={t('Dashboard.PAGE_TITLE')}>
<ChartContext.Provider value={chartContext}>
<Grid id={'dashboard-grid'} hasGutter>
{cardConfigs
Expand Down
40 changes: 29 additions & 11 deletions src/app/Events/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,29 @@ import { TargetView } from '@app/TargetView/TargetView';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import { Card, CardBody, Stack, StackItem, Tab, Tabs, Tooltip } from '@patternfly/react-core';
import * as React from 'react';
import { StaticContext } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { concatMap, filter } from 'rxjs';
import { EventTemplates } from './EventTemplates';
import { EventTypes } from './EventTypes';

export interface EventsProps {}
export type SupportedEventTab = 'templates' | 'types';

export const Events: React.FC<EventsProps> = (_) => {
export type SupportedAgentTab = 'templates' | 'probes';

export interface EventsProps {
eventTab?: SupportedEventTab;
agentTab?: SupportedAgentTab;
}

export const Events: React.FC<RouteComponentProps<Record<string, never>, StaticContext, EventsProps>> = ({
location,
...props
}) => {
const context = React.useContext(ServiceContext);
const addSubscription = useSubscriptions();
const [eventActiveTab, setEventActiveTab] = React.useState(0);
const [probeActiveTab, setProbeActiveTab] = React.useState(0);
const [eventActiveTab, setEventActiveTab] = React.useState(location?.state?.eventTab || 'templates');
const [probeActiveTab, setProbeActiveTab] = React.useState(location?.state?.agentTab || 'templates');
const [agentDetected, setAgentDetected] = React.useState(false);

React.useEffect(() => {
Expand All @@ -68,9 +80,15 @@ export const Events: React.FC<EventsProps> = (_) => {
);
}, [addSubscription, context.target, context.api, setAgentDetected]);

const handleEventTabSelect = React.useCallback((evt, idx) => setEventActiveTab(idx), [setEventActiveTab]);
const handleEventTabSelect = React.useCallback(
(evt, key: string | number) => setEventActiveTab(`${key}` as SupportedEventTab),
[setEventActiveTab]
);

const handleProbeTabSelect = React.useCallback((evt, idx) => setProbeActiveTab(idx), [setProbeActiveTab]);
const handleProbeTabSelect = React.useCallback(
(evt, key: string | number) => setProbeActiveTab(`${key}` as SupportedAgentTab),
[setProbeActiveTab]
);

return (
<>
Expand All @@ -80,10 +98,10 @@ export const Events: React.FC<EventsProps> = (_) => {
<Card>
<CardBody>
<Tabs activeKey={eventActiveTab} onSelect={handleEventTabSelect}>
<Tab eventKey={0} title="Event Templates">
<Tab eventKey={'templates'} title="Event Templates">
<EventTemplates />
</Tab>
<Tab eventKey={1} title="Event Types">
<Tab eventKey={'types'} title="Event Types">
<EventTypes />
</Tab>
</Tabs>
Expand All @@ -94,11 +112,11 @@ export const Events: React.FC<EventsProps> = (_) => {
<Card>
<CardBody>
<Tabs activeKey={probeActiveTab} onSelect={handleProbeTabSelect}>
<Tab eventKey={0} title="Probe Templates">
<Tab eventKey={'templates'} title="Probe Templates">
<AgentProbeTemplates agentDetected={agentDetected} />
</Tab>
<Tab
eventKey={1}
eventKey={'probes'}
title="Live Configuration"
isAriaDisabled={!agentDetected}
tooltip={
Expand All @@ -119,4 +137,4 @@ export const Events: React.FC<EventsProps> = (_) => {
);
};

export default Events;
export default withRouter(Events);
26 changes: 19 additions & 7 deletions src/app/Recordings/Recordings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,44 @@ import { TargetView } from '@app/TargetView/TargetView';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import { Card, CardBody, CardTitle, Tab, Tabs, TabTitleText } from '@patternfly/react-core';
import * as React from 'react';
import { StaticContext } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { ActiveRecordingsTable } from './ActiveRecordingsTable';
import { ArchivedRecordingsTable } from './ArchivedRecordingsTable';

export interface RecordingsProps {}
export type SupportedTab = 'active' | 'archived';

export const Recordings: React.FunctionComponent<RecordingsProps> = (_) => {
export interface RecordingsProps {
tab?: SupportedTab;
}

export const Recordings: React.FC<RouteComponentProps<Record<string, never>, StaticContext, RecordingsProps>> = ({
location,
..._props
}) => {
const context = React.useContext(ServiceContext);
const [activeTab, setActiveTab] = React.useState(0);
const [activeTab, setActiveTab] = React.useState(location?.state?.tab || 'active');
const [archiveEnabled, setArchiveEnabled] = React.useState(false);
const addSubscription = useSubscriptions();

React.useEffect(() => {
addSubscription(context.api.isArchiveEnabled().subscribe(setArchiveEnabled));
}, [context.api, addSubscription, setArchiveEnabled]);

const onTabSelect = React.useCallback((_, idx) => setActiveTab(Number(idx)), [setActiveTab]);
const onTabSelect = React.useCallback(
(_, key: string | number) => setActiveTab(`${key}` as SupportedTab),
[setActiveTab]
);

const targetAsObs = React.useMemo(() => context.target.target(), [context.target]);

const cardBody = React.useMemo(() => {
return archiveEnabled ? (
<Tabs id="recordings" activeKey={activeTab} onSelect={onTabSelect} unmountOnExit>
<Tab id="active-recordings" eventKey={0} title={<TabTitleText>Active Recordings</TabTitleText>}>
<Tab id="active-recordings" eventKey={'active'} title={<TabTitleText>Active Recordings</TabTitleText>}>
<ActiveRecordingsTable archiveEnabled={true} />
</Tab>
<Tab id="archived-recordings" eventKey={1} title={<TabTitleText>Archived Recordings</TabTitleText>}>
<Tab id="archived-recordings" eventKey={'archived'} title={<TabTitleText>Archived Recordings</TabTitleText>}>
<ArchivedRecordingsTable target={targetAsObs} isUploadsTable={false} isNestedTable={false} />
</Tab>
</Tabs>
Expand All @@ -86,4 +98,4 @@ export const Recordings: React.FunctionComponent<RecordingsProps> = (_) => {
);
};

export default Recordings;
export default withRouter(Recordings);
Loading