Disable integration backsync conditionally (#4084)
# What this PR does Disable integration backsync conditionally if token has not been generated ## Which issue(s) this PR closes Closes https://github.com/grafana/oncall-private/issues/2599 <!-- *Note*: if you have more than one GitHub issue that this PR closes, be sure to preface each issue link with a [closing keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue). This ensures that the issue(s) are auto-closed once the PR has been merged. --> ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
This commit is contained in:
parent
04604caa62
commit
5bcb438012
6 changed files with 223 additions and 8 deletions
|
|
@ -183,4 +183,15 @@ export class AlertReceiveChannelHelper {
|
|||
data,
|
||||
});
|
||||
}
|
||||
|
||||
static async checkIfTokenExists(integrationId: string) {
|
||||
try {
|
||||
await onCallApi({ skipErrorHandling: true }).GET('/alert_receive_channels/{id}/api_token/', {
|
||||
params: { path: { id: integrationId } },
|
||||
});
|
||||
return true;
|
||||
} catch (_e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,19 @@ export class AlertReceiveChannelConnectedChannelsStore {
|
|||
}
|
||||
|
||||
@AutoLoadingState(ActionKey.FETCH_INTEGRATIONS_AVAILABLE_FOR_CONNECTION)
|
||||
async fetchItemsAvailableForConnection({ search, page }: { search?: string; page: number }) {
|
||||
async fetchItemsAvailableForConnection({
|
||||
search,
|
||||
page,
|
||||
currentIntegrationId,
|
||||
}: {
|
||||
search?: string;
|
||||
page: number;
|
||||
currentIntegrationId: string;
|
||||
}) {
|
||||
await this.rootStore.alertReceiveChannelStore.fetchPaginatedItems({
|
||||
filters: {
|
||||
search,
|
||||
id_ne: this.itemsAsList.map(({ alert_receive_channel: { id } }) => id),
|
||||
id_ne: [...this.itemsAsList.map(({ alert_receive_channel: { id } }) => id), currentIntegrationId],
|
||||
},
|
||||
perpage: 10,
|
||||
page,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,24 @@ export interface paths {
|
|||
patch: operations['alert_receive_channels_partial_update'];
|
||||
trace?: never;
|
||||
};
|
||||
'/alert_receive_channels/{id}/api_token/': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description Internal API endpoints for alert receive channels (integrations). */
|
||||
get: operations['alert_receive_channels_api_token_retrieve'];
|
||||
put?: never;
|
||||
/** @description Internal API endpoints for alert receive channels (integrations). */
|
||||
post: operations['alert_receive_channels_api_token_create'];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/alert_receive_channels/{id}/change_team/': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -245,6 +263,23 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/alert_receive_channels/{id}/status_options/': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description Internal API endpoints for alert receive channels (integrations). */
|
||||
get: operations['alert_receive_channels_status_options_list'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/alert_receive_channels/{id}/stop_maintenance/': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -366,6 +401,23 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/alert_receive_channels/test_connection/': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** @description Internal API endpoints for alert receive channels (integrations). */
|
||||
post: operations['alert_receive_channels_test_connection_create'];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/alert_receive_channels/validate_name/': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -1448,6 +1500,10 @@ export interface components {
|
|||
readonly alertmanager_v2_migrated_at: string | null;
|
||||
additional_settings?: components['schemas']['AdditionalSettingsField'] | null;
|
||||
};
|
||||
AlertReceiveChannelBacksyncStatusOptions: {
|
||||
value: string;
|
||||
display_name: string;
|
||||
};
|
||||
AlertReceiveChannelConnectContactPoint: {
|
||||
datasource_uid: string;
|
||||
contact_point_name: string;
|
||||
|
|
@ -1746,6 +1802,9 @@ export interface components {
|
|||
readonly status: boolean;
|
||||
readonly instruction: string;
|
||||
};
|
||||
IntegrationTokenPostResponse: {
|
||||
token: string;
|
||||
};
|
||||
Key: {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -2440,6 +2499,49 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_api_token_retrieve: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description A string identifying this alert receive channel. */
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description No response body */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_api_token_create: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description A string identifying this alert receive channel. */
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['IntegrationTokenPostResponse'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_change_team_update: {
|
||||
parameters: {
|
||||
query: {
|
||||
|
|
@ -2661,7 +2763,7 @@ export interface operations {
|
|||
};
|
||||
responses: {
|
||||
/** @description No response body */
|
||||
200: {
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
|
|
@ -2799,6 +2901,28 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_status_options_list: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description A string identifying this alert receive channel. */
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['AlertReceiveChannelBacksyncStatusOptions'][];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_stop_maintenance_create: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -3004,6 +3128,30 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_test_connection_create: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['AlertReceiveChannel'];
|
||||
'application/x-www-form-urlencoded': components['schemas']['AlertReceiveChannel'];
|
||||
'multipart/form-data': components['schemas']['AlertReceiveChannel'];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description No response body */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
alert_receive_channels_validate_name_retrieve: {
|
||||
parameters: {
|
||||
query: {
|
||||
|
|
|
|||
21
grafana-plugin/src/pages/integration/Integration.hooks.ts
Normal file
21
grafana-plugin/src/pages/integration/Integration.hooks.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_receive_channel.helpers';
|
||||
|
||||
import { useCurrentIntegration } from './OutgoingTab/OutgoingTab.hooks';
|
||||
|
||||
export const useIntegrationTokenCheck = () => {
|
||||
const [tokenExists, setTokenExists] = useState(true);
|
||||
|
||||
const { id } = useCurrentIntegration();
|
||||
|
||||
useEffect(() => {
|
||||
const checkToken = async () => {
|
||||
const tokenExists = await AlertReceiveChannelHelper.checkIfTokenExists(id);
|
||||
setTokenExists(tokenExists);
|
||||
};
|
||||
checkToken();
|
||||
}, [id]);
|
||||
|
||||
return tokenExists;
|
||||
};
|
||||
|
|
@ -38,6 +38,7 @@ export const ConnectIntegrationModal = observer(({ onDismiss }: { onDismiss: ()
|
|||
await alertReceiveChannelConnectedChannelsStore.fetchItemsAvailableForConnection({
|
||||
page,
|
||||
search,
|
||||
currentIntegrationId: currentIntegration.id,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { HorizontalGroup, Tooltip, Icon, useStyles2, IconButton, Switch, Checkbox, ConfirmModal } from '@grafana/ui';
|
||||
import {
|
||||
HorizontalGroup,
|
||||
Tooltip,
|
||||
Icon,
|
||||
useStyles2,
|
||||
IconButton,
|
||||
Switch,
|
||||
Checkbox,
|
||||
ConfirmModal,
|
||||
useTheme2,
|
||||
} from '@grafana/ui';
|
||||
import { observer } from 'mobx-react';
|
||||
import Emoji from 'react-emoji-render';
|
||||
|
||||
|
|
@ -9,6 +19,7 @@ import { IntegrationLogoWithTitle } from 'components/IntegrationLogo/Integration
|
|||
import { Text } from 'components/Text/Text';
|
||||
import { AlertReceiveChannelHelper } from 'models/alert_receive_channel/alert_receive_channel.helpers';
|
||||
import { ApiSchemas } from 'network/oncall-api/api.types';
|
||||
import { useIntegrationTokenCheck } from 'pages/integration/Integration.hooks';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { PLUGIN_ROOT } from 'utils/consts';
|
||||
import { useConfirmModal } from 'utils/hooks';
|
||||
|
|
@ -33,6 +44,8 @@ interface ConnectedIntegrationsTableProps {
|
|||
const ConnectedIntegrationsTable: FC<ConnectedIntegrationsTableProps> = observer(
|
||||
({ selectable, allowDelete, onChange, onBacksyncChange, tableProps, defaultBacksyncedIds = [], allowBacksync }) => {
|
||||
const { alertReceiveChannelStore } = useStore();
|
||||
const { colors } = useTheme2();
|
||||
const tokenExists = useIntegrationTokenCheck();
|
||||
|
||||
const columns = [
|
||||
...(selectable
|
||||
|
|
@ -70,15 +83,22 @@ const ConnectedIntegrationsTable: FC<ConnectedIntegrationsTableProps> = observer
|
|||
title: (
|
||||
<HorizontalGroup>
|
||||
<Text type="secondary">Backsync</Text>
|
||||
<Tooltip content={<>Switch on to start sending data from other integrations</>}>
|
||||
<Icon name={'info-circle'} />
|
||||
</Tooltip>
|
||||
{tokenExists ? (
|
||||
<Tooltip content={<>Switch on to start sending data from other integrations</>}>
|
||||
{<Icon name={'info-circle'} />}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip content={<>Token must be generated to enable backsync</>}>
|
||||
{<Icon name={'info-circle'} color={colors.error.shade} />}
|
||||
</Tooltip>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
),
|
||||
render: (connectedIntegration: ConnectedIntegration) => (
|
||||
<BacksyncSwitcher
|
||||
defaultChecked={defaultBacksyncedIds.includes(connectedIntegration.id)}
|
||||
onChange={(checked: boolean) => onBacksyncChange(connectedIntegration.id, checked)}
|
||||
disabled={!tokenExists}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
|
@ -98,15 +118,21 @@ const ConnectedIntegrationsTable: FC<ConnectedIntegrationsTableProps> = observer
|
|||
const BacksyncSwitcher = ({
|
||||
onChange,
|
||||
defaultChecked,
|
||||
disabled,
|
||||
}: {
|
||||
onChange: (checked: boolean) => void;
|
||||
defaultChecked?: boolean;
|
||||
disabled?: boolean;
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<div className={styles.backsyncColumn}>
|
||||
<Switch defaultChecked={defaultChecked} onChange={({ currentTarget }) => onChange(currentTarget.checked)} />
|
||||
<Switch
|
||||
disabled={disabled}
|
||||
defaultChecked={defaultChecked}
|
||||
onChange={({ currentTarget }) => onChange(currentTarget.checked)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue