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

Tweaks for integrations #2869

Merged
merged 29 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
45e7f2c
first changes to fix integrations navigation
teodosii Aug 22, 2023
a8df7b6
tweaks & optimizations
teodosii Aug 23, 2023
6c3ead7
Merge branch 'dev' into rares/2843
teodosii Aug 23, 2023
1ab9996
fix
teodosii Aug 23, 2023
5ec0607
fix for reapplying the filters
teodosii Aug 23, 2023
0445b65
changelog
teodosii Aug 23, 2023
76773a1
Merge branch 'dev' into rares/2843
teodosii Aug 23, 2023
b8494fe
Merge branch 'dev' into rares/2843
teodosii Aug 24, 2023
5ac0ab9
Merge branch 'dev' into rares/2843
teodosii Aug 28, 2023
6f1b6cd
fixed table filters update
teodosii Aug 28, 2023
d4b0199
param typing
teodosii Aug 28, 2023
1ce7014
Merge branch 'dev' into rares/2843
teodosii Aug 28, 2023
2299146
review
teodosii Aug 28, 2023
988687a
fix passed page param in table + build fix
teodosii Aug 28, 2023
40d4fc6
fixed Integrations page losing its filters, fixed initial page load f…
teodosii Aug 29, 2023
1a9278c
removed maintenance page, updated changelog
teodosii Aug 29, 2023
8f40236
maintenance breadcrumb removal
teodosii Aug 29, 2023
619fa4a
fixed mobx not updating the routes tree component
teodosii Aug 29, 2023
cae77f2
Merge branch 'dev' into rares/2843
teodosii Aug 29, 2023
055a4f6
changelog
teodosii Aug 29, 2023
f99987b
build fix
teodosii Aug 29, 2023
58f0a23
fixed routes not moving up/down
teodosii Aug 29, 2023
2ae899b
few other tweaks
teodosii Aug 30, 2023
12cf219
Merge branch 'dev' into rares/2843
teodosii Aug 30, 2023
e3c095c
fixed didInvalidateFn for async calls
teodosii Aug 30, 2023
8bc5afd
lint
teodosii Aug 30, 2023
52cbbe0
@action on mobx action
teodosii Aug 30, 2023
9d2244d
corrected changelog
teodosii Aug 30, 2023
b8f2b72
removed maintenance from plugin.json
teodosii Aug 30, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
updated to reflect the latest state by @joeyorlando ([#2886](https://github.com/grafana/oncall/pull/2886))
- Fix issue where Grafana integration would fail to parse alerting config for routes without receivers @mderynck
([#2894](https://github.com/grafana/oncall/pull/2894))
- Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869))

## v1.3.27 (2023-08-25)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const CursorPagination: FC<CursorPaginationProps> = (props) => {
setDisabled(false);
}, [prev, next]);

const onChangeItemsPerPageCallback = useCallback((option) => {
const onChangeItemsPerPageCallback = useCallback((option: SelectableValue) => {
setDisabled(true);
onChangeItemsPerPage(option.value);
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
.integrationTree__group {
position: relative;
margin-bottom: 12px;

&--hidden {
display: none;
}
}

.integrationTree__icon {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import React, { useEffect, useState } from 'react';
import { Icon, IconButton, IconName } from '@grafana/ui';
import cn from 'classnames/bind';
import { isArray, isUndefined } from 'lodash-es';
import { observer } from 'mobx-react';

import styles from './IntegrationCollapsibleTreeView.module.scss';

const cx = cn.bind(styles);

export interface IntegrationCollapsibleItem {
isHidden?: boolean;
customIcon?: IconName;
canHoverIcon: boolean;
collapsedView: (toggle?: () => void) => React.ReactNode; // needs toggle param for toggling on click
Expand All @@ -22,7 +24,7 @@ interface IntegrationCollapsibleTreeViewProps {
configElements: Array<IntegrationCollapsibleItem | IntegrationCollapsibleItem[]>;
}

const IntegrationCollapsibleTreeView: React.FC<IntegrationCollapsibleTreeViewProps> = (props) => {
const IntegrationCollapsibleTreeView: React.FC<IntegrationCollapsibleTreeViewProps> = observer((props) => {
const { configElements } = props;

const [expandedList, setExpandedList] = useState(getStartingExpandedState());
Expand Down Expand Up @@ -97,7 +99,7 @@ const IntegrationCollapsibleTreeView: React.FC<IntegrationCollapsibleTreeViewPro
})
);
}
};
});

const IntegrationCollapsibleTreeItem: React.FC<{
item: IntegrationCollapsibleItem;
Expand All @@ -107,7 +109,7 @@ const IntegrationCollapsibleTreeItem: React.FC<{
const iconOnClickFn = !item.isCollapsible ? undefined : onClick;

return (
<div className={cx('integrationTree__group')}>
<div className={cx('integrationTree__group', { 'integrationTree__group--hidden': item.isHidden })}>
<div className={cx('integrationTree__icon')}>
{item.canHoverIcon ? (
<IconButton name={getIconName()} onClick={iconOnClickFn} size="lg" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ const IntegrationContactPoint: React.FC<{
}> = observer(({ id }) => {
const { alertReceiveChannelStore } = useStore();
const contactPoints = alertReceiveChannelStore.connectedContactPoints[id];
const warnings = contactPoints.filter((cp) => !cp.notificationConnected);

const warnings = contactPoints?.filter((cp) => !cp.notificationConnected);
const [
{
isLoading,
Expand Down Expand Up @@ -88,6 +87,7 @@ const IntegrationContactPoint: React.FC<{
<Drawer scrollableContent title="Connected Contact Points" onClose={closeDrawer} closeOnMaskClick={false}>
<div className={cx('contactpoints__drawer')}>
<GTable
emptyText={'No contact points'}
className={cx('contactpoints__table')}
rowKey="id"
data={contactPoints}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';

import { ConfirmModal, HorizontalGroup, Icon, IconName } from '@grafana/ui';
import cn from 'classnames/bind';
Expand All @@ -15,7 +15,6 @@ import { ChannelFilter } from 'models/channel_filter';
import CommonIntegrationHelper from 'pages/integration/CommonIntegration.helper';
import IntegrationHelper from 'pages/integration/Integration.helper';
import { useStore } from 'state/useStore';
import { openNotification } from 'utils';

const cx = cn.bind(styles);

Expand All @@ -26,24 +25,39 @@ interface CollapsedIntegrationRouteDisplayProps {
toggle: () => void;
openEditTemplateModal: (templateName: string | string[], channelFilterId?: ChannelFilter['id']) => void;
onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void;
onRouteDelete: (routeId: string) => void;
onItemMove: () => void;
}

const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDisplayProps> = observer(
({ channelFilterId, alertReceiveChannelId, routeIndex, toggle, openEditTemplateModal, onEditRegexpTemplate }) => {
({
channelFilterId,
alertReceiveChannelId,
routeIndex,
toggle,
openEditTemplateModal,
onEditRegexpTemplate,
onRouteDelete,
onItemMove,
}) => {
const store = useStore();
const { escalationChainStore, alertReceiveChannelStore } = store;
const [routeIdForDeletion, setRouteIdForDeletion] = useState<ChannelFilter['id']>(undefined);

const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];

const routeWording = useMemo(() => {
return CommonIntegrationHelper.getRouteConditionWording(
alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId],
routeIndex
);
}, [routeIndex, alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]]);

if (!channelFilter) {
return null;
}

const escalationChain = escalationChainStore.items[channelFilter.escalation_chain];
const routeWording = CommonIntegrationHelper.getRouteConditionWording(
alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId],
routeIndex
);
const chatOpsAvailableChannels = IntegrationHelper.getChatOpsChannels(channelFilter, store).filter(
(channel) => channel
);
Expand All @@ -59,10 +73,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
<div className={cx('heading-container__item', 'heading-container__item--large')}>
<TooltipBadge
borderType="success"
text={CommonIntegrationHelper.getRouteConditionWording(
alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId],
routeIndex
)}
text={routeWording}
tooltipTitle={CommonIntegrationHelper.getRouteConditionTooltipWording(
alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId],
routeIndex
Expand Down Expand Up @@ -93,6 +104,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
alertReceiveChannelId={alertReceiveChannelId}
channelFilterId={channelFilterId}
routeIndex={routeIndex}
onItemMove={onItemMove}
setRouteIdForDeletion={() => setRouteIdForDeletion(channelFilterId)}
openRouteTemplateEditor={() => handleEditRoutingTemplate(channelFilter, channelFilterId)}
/>
Expand Down Expand Up @@ -179,8 +191,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla

async function onRouteDeleteConfirm() {
setRouteIdForDeletion(undefined);
await alertReceiveChannelStore.deleteChannelFilter(routeIdForDeletion);
openNotification('Route has been deleted');
onRouteDelete(routeIdForDeletion);
}
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ interface ExpandedIntegrationRouteDisplayProps {
templates: AlertTemplatesDTO[];
openEditTemplateModal: (templateName: string | string[], channelFilterId?: ChannelFilter['id']) => void;
onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void;
onRouteDelete: (routeId: string) => void;
onItemMove: () => void;
}

interface ExpandedIntegrationRouteDisplayState {
Expand All @@ -59,7 +61,16 @@ interface ExpandedIntegrationRouteDisplayState {
}

const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayProps> = observer(
({ alertReceiveChannelId, channelFilterId, templates, routeIndex, openEditTemplateModal, onEditRegexpTemplate }) => {
({
alertReceiveChannelId,
channelFilterId,
templates,
routeIndex,
openEditTemplateModal,
onEditRegexpTemplate,
onRouteDelete,
onItemMove,
}) => {
const store = useStore();
const {
telegramChannelStore,
Expand Down Expand Up @@ -130,6 +141,7 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP
alertReceiveChannelId={alertReceiveChannelId}
channelFilterId={channelFilterId}
routeIndex={routeIndex}
onItemMove={onItemMove}
setRouteIdForDeletion={() => setState({ routeIdForDeletion: channelFilterId })}
openRouteTemplateEditor={() => handleEditRoutingTemplate(channelFilter, channelFilterId)}
/>
Expand Down Expand Up @@ -278,8 +290,7 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP

async function onRouteDeleteConfirm() {
setState({ routeIdForDeletion: undefined });
await alertReceiveChannelStore.deleteChannelFilter(routeIdForDeletion);
openNotification('Route has been deleted');
onRouteDelete(routeIdForDeletion);
}

function onEscalationChainChange({ id }) {
Expand Down Expand Up @@ -319,6 +330,7 @@ interface RouteButtonsDisplayProps {
routeIndex: number;
setRouteIdForDeletion(): void;
openRouteTemplateEditor(): void;
onItemMove();
}

export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
Expand All @@ -327,6 +339,7 @@ export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
routeIndex,
setRouteIdForDeletion,
openRouteTemplateEditor,
onItemMove,
}) => {
const { alertReceiveChannelStore } = useStore();
const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];
Expand Down Expand Up @@ -404,11 +417,13 @@ export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
function onRouteMoveDown(e: React.SyntheticEvent) {
e.stopPropagation();
alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex + 1);
onItemMove();
}

function onRouteMoveUp(e: React.SyntheticEvent) {
e.stopPropagation();
alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex - 1);
onItemMove();
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ class RemoteFilters extends Component<RemoteFiltersProps, RemoteFiltersState> {
}

LocationHelper.update({ ...values }, 'partial');

onChange(values, isOnMount);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export class AlertReceiveChannelStore extends BaseStore {
return results;
}

async updatePaginatedItems(query: any = '', page = 1) {
async updatePaginatedItems(query: any = '', page = 1, updateCounters = false) {
const filters = typeof query === 'string' ? { search: query } : query;
const { count, results } = await makeRequest(this.path, { params: { ...filters, page } });

Expand All @@ -153,7 +153,9 @@ export class AlertReceiveChannelStore extends BaseStore {
results: results.map((item: AlertReceiveChannel) => item.id),
};

this.updateCounters();
if (updateCounters) {
this.updateCounters();
}

return results;
}
Expand Down Expand Up @@ -297,7 +299,7 @@ export class AlertReceiveChannelStore extends BaseStore {
method: 'DELETE',
});

this.updateChannelFilters(channelFilter.alert_receive_channel, true);
return this.updateChannelFilters(channelFilter.alert_receive_channel, true);
}

@action
Expand Down Expand Up @@ -499,6 +501,21 @@ export class AlertReceiveChannelStore extends BaseStore {
this.counters = counters;
}

async updateCountersForIntegration(id: AlertReceiveChannel['id']): Promise<any> {
const counters = await makeRequest(`${this.path}${id}/counters`, {
method: 'GET',
});

this.counters = {
...this.counters,
[id]: {
...counters[id],
},
};

return counters;
}

startMaintenanceMode = (id: AlertReceiveChannel['id'], mode: MaintenanceMode, duration: number): Promise<void> =>
makeRequest<null>(`${this.path}${id}/start_maintenance/`, {
method: 'POST',
Expand Down
5 changes: 5 additions & 0 deletions grafana-plugin/src/models/filters/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export class FiltersStore extends BaseStore {
@observable.shallow
public values: { [page: string]: FiltersValues } = {};

@observable
public isLoading = true;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

easier to track now if the filters are loading or not

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isLoading===true is the same as store.filters.options[page]===undefined

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so don't see the reason of having isLoading flag


private _globalValues: FiltersValues = {};

constructor(rootStore: RootStore) {
Expand Down Expand Up @@ -52,6 +55,8 @@ export class FiltersStore extends BaseStore {
[page]: result,
};

this.isLoading = false;

return result;
}

Expand Down
56 changes: 28 additions & 28 deletions grafana-plugin/src/models/user/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,35 +111,35 @@ export class UserStore extends BaseStore {
}

@action
async updateItems(f: any = { searchTerm: '' }, page = 1) {
return new Promise<void>(async (resolve) => {
const filters = typeof f === 'string' ? { searchTerm: f } : f; // for GSelect compatibility
const { searchTerm: search } = filters;
const { count, results } = await makeRequest(this.path, {
params: { search, page },
});

this.items = {
...this.items,
...results.reduce(
(acc: { [key: number]: User }, item: User) => ({
...acc,
[item.pk]: {
...item,
timezone: getTimezone(item),
},
}),
{}
),
};

this.searchResult = {
count,
results: results.map((item: User) => item.pk),
};

resolve();
async updateItems(f: any = { searchTerm: '' }, page = 1): Promise<any> {
const filters = typeof f === 'string' ? { searchTerm: f } : f; // for GSelect compatibility
const { searchTerm: search } = filters;
const response = await makeRequest(this.path, {
params: { search, page },
});

const { count, results } = response;

this.items = {
...this.items,
...results.reduce(
(acc: { [key: number]: User }, item: User) => ({
...acc,
[item.pk]: {
...item,
timezone: getTimezone(item),
},
}),
{}
),
};

this.searchResult = {
count,
results: results.map((item: User) => item.pk),
};

return response;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part is not touched, I only removed the wrapped new Promise (...) and instead returned the response which is a Promise too.

}

getSearchResult() {
Expand Down
Loading