From 3b85199dd3cd85fb0b8d2c70a09af8063ccf586f Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 5 Dec 2024 10:09:36 -0500 Subject: [PATCH 1/6] fix(rules): allow selection of event templates when no targets are available --- src/app/Rules/CreateRule.tsx | 2 +- src/app/Shared/Services/Api.service.tsx | 6 +++++- src/test/Rules/CreateRule.test.tsx | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/app/Rules/CreateRule.tsx b/src/app/Rules/CreateRule.tsx index e032e0376..ba05bf742 100644 --- a/src/app/Rules/CreateRule.tsx +++ b/src/app/Rules/CreateRule.tsx @@ -285,7 +285,7 @@ export const CreateRuleForm: React.FC = (_props) => { : []; }), ), - of([]), + context.api.getEventTemplates().pipe(catchError((_) => of([]))), ), ), ) diff --git a/src/app/Shared/Services/Api.service.tsx b/src/app/Shared/Services/Api.service.tsx index 691451ec1..bdbeca5ab 100644 --- a/src/app/Shared/Services/Api.service.tsx +++ b/src/app/Shared/Services/Api.service.tsx @@ -592,7 +592,7 @@ export class ApiService { const body = new window.FormData(); body.append('template', file); - return this.sendLegacyRequest('v4', 'templates', 'Template Upload Failed', { + return this.sendLegacyRequest('v4', 'event_templates', 'Template Upload Failed', { body: body, method: 'POST', headers: {}, @@ -1361,6 +1361,10 @@ export class ApiService { return this.doGet(`targets/${target.id}/recordings`, 'v4', undefined, suppressNotifications, skipStatusCheck); } + getEventTemplates(suppressNotifications = false, skipStatusCheck = false): Observable { + return this.doGet('event_templates', 'v4', undefined, suppressNotifications, skipStatusCheck); + } + getTargetEventTemplates( target: TargetStub, suppressNotifications = false, diff --git a/src/test/Rules/CreateRule.test.tsx b/src/test/Rules/CreateRule.test.tsx index b9e4de28a..e7f7145df 100644 --- a/src/test/Rules/CreateRule.test.tsx +++ b/src/test/Rules/CreateRule.test.tsx @@ -43,6 +43,12 @@ const mockEventTemplate: EventTemplate = { provider: 'some provider', description: 'some description', }; +const mockCustomEventTemplate: EventTemplate = { + name: 'CustomTemplate', + type: 'CUSTOM', + provider: 'some provider', + description: 'some description', +}; const mockRule: Rule = { name: 'mockRule', @@ -64,6 +70,7 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockNavigate, })); +jest.spyOn(defaultServices.api, 'getEventTemplates').mockReturnValue(of([mockCustomEventTemplate])); jest.spyOn(defaultServices.api, 'getTargetEventTemplates').mockReturnValue(of([mockEventTemplate])); jest.spyOn(defaultServices.targets, 'targets').mockReturnValue(of([mockTarget])); From 2b03d40379b774f47817103dae1f7dce8616f039 Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 5 Dec 2024 10:23:29 -0500 Subject: [PATCH 2/6] feat(eventtemplates): enable custom event template UI to display when no target is selected --- src/app/Events/EventTemplates.tsx | 5 ++- src/app/Events/Events.tsx | 64 +++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/app/Events/EventTemplates.tsx b/src/app/Events/EventTemplates.tsx index 88cb453fb..21e81f1ef 100644 --- a/src/app/Events/EventTemplates.tsx +++ b/src/app/Events/EventTemplates.tsx @@ -165,9 +165,10 @@ export const EventTemplates: React.FC = () => { context.target .target() .pipe( - filter((target) => !!target), first(), - concatMap((target: Target) => context.api.getTargetEventTemplates(target)), + concatMap((target: Target) => + target ? context.api.getTargetEventTemplates(target) : context.api.getEventTemplates(), + ), ) .subscribe({ next: handleTemplates, diff --git a/src/app/Events/Events.tsx b/src/app/Events/Events.tsx index 4156e7220..4061bb598 100644 --- a/src/app/Events/Events.tsx +++ b/src/app/Events/Events.tsx @@ -25,24 +25,48 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { concatMap, filter } from 'rxjs'; import { EventTemplates } from './EventTemplates'; import { EventTypes } from './EventTypes'; +import { BreadcrumbPage } from '@app/BreadcrumbPage/BreadcrumbPage'; +import { TargetContextSelector } from '@app/TargetView/TargetContextSelector'; export interface EventsProps {} export const Events: React.FC = ({ ...props }) => { + const context = React.useContext(ServiceContext); + const addSubscription = useSubscriptions(); + const [targetSelected, setTargetSelected] = React.useState(false); + + React.useEffect(() => { + addSubscription(context.target.target().subscribe((t) => setTargetSelected(!!t))); + }, [context, context.target, setTargetSelected]); + return ( - - - - - - - - - - - - <> - + <> + + + {targetSelected ? ( + <> + + + + + + + + + + + + ) : ( + <> + + + + + + + )} + + ); }; @@ -51,7 +75,11 @@ enum EventTab { EVENT_TYPE = 'event-type', } -export const EventTabs: React.FC = () => { +export interface EventTabsProps { + targetSelected: boolean; +} + +export const EventTabs: React.FC = (props: EventTabsProps) => { const { search, pathname } = useLocation(); const navigate = useNavigate(); @@ -70,9 +98,11 @@ export const EventTabs: React.FC = () => { - - - + {props?.targetSelected && ( + + + + )} ); }; From 2d1b617822573f3c1caab5c45f5cf8e41608fe5f Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 5 Dec 2024 10:34:16 -0500 Subject: [PATCH 3/6] feat(agentprobes): enable agent probe templates UI to display when no target is selected --- src/app/Events/Events.tsx | 66 +++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/app/Events/Events.tsx b/src/app/Events/Events.tsx index 4061bb598..04f6b37b3 100644 --- a/src/app/Events/Events.tsx +++ b/src/app/Events/Events.tsx @@ -43,28 +43,16 @@ export const Events: React.FC = ({ ...props }) => { <> - {targetSelected ? ( - <> - - - - - - - - - - - - ) : ( - <> - - - - - - - )} + + + + + + + + + + ); @@ -75,11 +63,11 @@ enum EventTab { EVENT_TYPE = 'event-type', } -export interface EventTabsProps { +export interface TabsProps { targetSelected: boolean; } -export const EventTabs: React.FC = (props: EventTabsProps) => { +export const EventTabs: React.FC = ({ targetSelected }) => { const { search, pathname } = useLocation(); const navigate = useNavigate(); @@ -98,7 +86,7 @@ export const EventTabs: React.FC = (props: EventTabsProps) => { - {props?.targetSelected && ( + {targetSelected && ( @@ -112,7 +100,7 @@ enum AgentTab { AGENT_PROBE = 'agent-probe', } -export const AgentTabs: React.FC = () => { +export const AgentTabs: React.FC = ({ targetSelected }) => { const context = React.useContext(ServiceContext); const addSubscription = useSubscriptions(); @@ -148,18 +136,20 @@ export const AgentTabs: React.FC = () => { - - ) - } - > - - + {targetSelected && ( + + ) + } + > + + + )} ); }; From 2699a2dfa54022700d40176166e323b0134d8dfa Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 5 Dec 2024 10:37:29 -0500 Subject: [PATCH 4/6] show tabs, but disabled --- src/app/Events/Events.tsx | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/app/Events/Events.tsx b/src/app/Events/Events.tsx index 04f6b37b3..d7a060316 100644 --- a/src/app/Events/Events.tsx +++ b/src/app/Events/Events.tsx @@ -86,11 +86,9 @@ export const EventTabs: React.FC = ({ targetSelected }) => { - {targetSelected && ( - - - - )} + + + ); }; @@ -136,20 +134,18 @@ export const AgentTabs: React.FC = ({ targetSelected }) => { - {targetSelected && ( - - ) - } - > - - - )} + + ) + } + > + + ); }; From 81bba84aced766c3ef68424f2c7cf98c72cb7c7f Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 5 Dec 2024 10:40:33 -0500 Subject: [PATCH 5/6] refactor --- src/app/Events/Events.tsx | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/app/Events/Events.tsx b/src/app/Events/Events.tsx index d7a060316..e22ebde91 100644 --- a/src/app/Events/Events.tsx +++ b/src/app/Events/Events.tsx @@ -16,7 +16,6 @@ import { AgentLiveProbes } from '@app/Agent/AgentLiveProbes'; import { AgentProbeTemplates } from '@app/Agent/AgentProbeTemplates'; import { ServiceContext } from '@app/Shared/Services/Services'; -import { TargetView } from '@app/TargetView/TargetView'; import { useSubscriptions } from '@app/utils/hooks/useSubscriptions'; import { getActiveTab, switchTab } from '@app/utils/utils'; import { Card, CardBody, Tab, Tabs, Tooltip } from '@patternfly/react-core'; @@ -31,26 +30,18 @@ import { TargetContextSelector } from '@app/TargetView/TargetContextSelector'; export interface EventsProps {} export const Events: React.FC = ({ ...props }) => { - const context = React.useContext(ServiceContext); - const addSubscription = useSubscriptions(); - const [targetSelected, setTargetSelected] = React.useState(false); - - React.useEffect(() => { - addSubscription(context.target.target().subscribe((t) => setTargetSelected(!!t))); - }, [context, context.target, setTargetSelected]); - return ( <> - + - + - + @@ -63,14 +54,19 @@ enum EventTab { EVENT_TYPE = 'event-type', } -export interface TabsProps { - targetSelected: boolean; -} +export const EventTabs: React.FC = () => { + const context = React.useContext(ServiceContext); + const addSubscription = useSubscriptions(); -export const EventTabs: React.FC = ({ targetSelected }) => { const { search, pathname } = useLocation(); const navigate = useNavigate(); + const [targetSelected, setTargetSelected] = React.useState(false); + + React.useEffect(() => { + addSubscription(context.target.target().subscribe((t) => setTargetSelected(!!t))); + }, [addSubscription, context, context.target]); + const activeTab = React.useMemo(() => { return getActiveTab(search, 'eventTab', Object.values(EventTab), EventTab.EVENT_TEMPLATE); }, [search]); @@ -98,13 +94,19 @@ enum AgentTab { AGENT_PROBE = 'agent-probe', } -export const AgentTabs: React.FC = ({ targetSelected }) => { +export const AgentTabs: React.FC = () => { const context = React.useContext(ServiceContext); const addSubscription = useSubscriptions(); const { search, pathname } = useLocation(); const navigate = useNavigate(); + const [targetSelected, setTargetSelected] = React.useState(false); + + React.useEffect(() => { + addSubscription(context.target.target().subscribe((t) => setTargetSelected(!!t))); + }, [addSubscription, context, context.target]); + const activeTab = React.useMemo(() => { return getActiveTab(search, 'agentTab', Object.values(AgentTab), AgentTab.AGENT_TEMPLATE); }, [search]); From a62ae83e00ebe4fe37a437e792ee752de3c2746b Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 5 Dec 2024 16:33:06 -0500 Subject: [PATCH 6/6] lint --- src/app/Events/EventTemplates.tsx | 2 +- src/app/Events/Events.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/Events/EventTemplates.tsx b/src/app/Events/EventTemplates.tsx index 21e81f1ef..9b1d60a3c 100644 --- a/src/app/Events/EventTemplates.tsx +++ b/src/app/Events/EventTemplates.tsx @@ -62,7 +62,7 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { forkJoin, Observable, of } from 'rxjs'; -import { catchError, concatMap, defaultIfEmpty, filter, first, tap } from 'rxjs/operators'; +import { catchError, concatMap, defaultIfEmpty, first, tap } from 'rxjs/operators'; const tableColumns: TableColumn[] = [ { diff --git a/src/app/Events/Events.tsx b/src/app/Events/Events.tsx index e22ebde91..ce67be295 100644 --- a/src/app/Events/Events.tsx +++ b/src/app/Events/Events.tsx @@ -15,7 +15,9 @@ */ import { AgentLiveProbes } from '@app/Agent/AgentLiveProbes'; import { AgentProbeTemplates } from '@app/Agent/AgentProbeTemplates'; +import { BreadcrumbPage } from '@app/BreadcrumbPage/BreadcrumbPage'; import { ServiceContext } from '@app/Shared/Services/Services'; +import { TargetContextSelector } from '@app/TargetView/TargetContextSelector'; import { useSubscriptions } from '@app/utils/hooks/useSubscriptions'; import { getActiveTab, switchTab } from '@app/utils/utils'; import { Card, CardBody, Tab, Tabs, Tooltip } from '@patternfly/react-core'; @@ -24,8 +26,6 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { concatMap, filter } from 'rxjs'; import { EventTemplates } from './EventTemplates'; import { EventTypes } from './EventTypes'; -import { BreadcrumbPage } from '@app/BreadcrumbPage/BreadcrumbPage'; -import { TargetContextSelector } from '@app/TargetView/TargetContextSelector'; export interface EventsProps {}