Add filters to outgoing webhooks 2 (#1598)

# What this PR does

## Which issue(s) this PR fixes

## Checklist

- [ ] Tests updated
- [ ] Documentation added
- [ ] `CHANGELOG.md` updated
This commit is contained in:
Ildar Iskhakov 2023-03-23 09:52:59 +08:00 committed by GitHub
parent 1cd906ceab
commit cfcfa0336e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 5 deletions

View file

@ -1,22 +1,32 @@
from django.apps import apps
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django_filters import rest_framework as filters
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from apps.api.permissions import RBACPermission
from apps.api.serializers.webhook import WebhookSerializer
from apps.auth_token.auth import PluginAuthentication
from apps.webhooks.models import Webhook
from common.api_helpers.filters import ByTeamModelFieldFilterMixin, ModelFieldFilterMixin, TeamModelMultipleChoiceFilter
from common.api_helpers.mixins import PublicPrimaryKeyMixin, TeamFilteringMixin
class WebhooksFilter(ByTeamModelFieldFilterMixin, ModelFieldFilterMixin, filters.FilterSet):
team = TeamModelMultipleChoiceFilter()
class WebhooksView(TeamFilteringMixin, PublicPrimaryKeyMixin, ModelViewSet):
authentication_classes = (PluginAuthentication,)
permission_classes = (IsAuthenticated, RBACPermission)
rbac_permissions = {
"metadata": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
"filters": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
"list": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
"retrieve": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
"create": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_WRITE],
@ -28,6 +38,10 @@ class WebhooksView(TeamFilteringMixin, PublicPrimaryKeyMixin, ModelViewSet):
model = Webhook
serializer_class = WebhookSerializer
filter_backends = [SearchFilter, filters.DjangoFilterBackend]
search_fields = ["public_primary_key", "name"]
filterset_class = WebhooksFilter
def get_queryset(self, ignore_filtering_by_available_teams=False):
queryset = Webhook.objects.filter(
organization=self.request.auth.organization,
@ -48,9 +62,8 @@ class WebhooksView(TeamFilteringMixin, PublicPrimaryKeyMixin, ModelViewSet):
# use this method to get the object from the whole organization instead of the current team
pk = self.kwargs["pk"]
organization = self.request.auth.organization
try:
obj = organization.webhooks.get(public_primary_key=pk)
obj = organization.webhooks.filter(*self.available_teams_lookup_args).get(public_primary_key=pk)
except ObjectDoesNotExist:
raise NotFound
@ -83,3 +96,22 @@ class WebhooksView(TeamFilteringMixin, PublicPrimaryKeyMixin, ModelViewSet):
)[0]
if self.request.auth.organization.pk not in enabled_webhooks_2_orgs.json_value["org_ids"]:
raise PermissionDenied("Webhooks 2 not enabled for organization. Permission denied.")
@action(methods=["get"], detail=False)
def filters(self, request):
filter_name = request.query_params.get("search", None)
api_root = "/api/internal/v1/"
filter_options = [
{
"name": "team",
"type": "team_select",
"href": api_root + "teams/",
"global": True,
},
]
if filter_name is not None:
filter_options = list(filter(lambda f: filter_name in f["name"], filter_options))
return Response(filter_options)

View file

@ -8,6 +8,20 @@ export const form: { name: string; fields: FormItem[] } = {
type: FormItemType.Input,
validation: { required: true },
},
{
name: 'team',
label: 'Assign to team',
description:
'Assigning to the teams allows you to filter Outgoing Webhooks and configure their visibility. Go to OnCall -> Settings -> Team and Access Settings for more details',
type: FormItemType.GSelect,
extra: {
modelName: 'grafanaTeamStore',
displayField: 'name',
valueField: 'id',
showSearch: true,
allowClear: true,
},
},
{
name: 'trigger_type',
label: 'Trigger type',

View file

@ -1,6 +1,7 @@
import { action, observable } from 'mobx';
import BaseStore from 'models/base_store';
import { OutgoingWebhook } from 'models/outgoing_webhook/outgoing_webhook.types';
import { makeRequest } from 'network';
import { RootStore } from 'state';
@ -52,9 +53,11 @@ export class OutgoingWebhook2Store extends BaseStore {
}
@action
async updateItems(query = '') {
async updateItems(query: any = '') {
const params = typeof query === 'string' ? { search: query } : query;
const results = await makeRequest(`${this.path}`, {
params: { search: query },
params,
});
this.items = {
@ -68,9 +71,11 @@ export class OutgoingWebhook2Store extends BaseStore {
),
};
const key = typeof query === 'string' ? query : '';
this.searchResult = {
...this.searchResult,
[query]: results.map((item: OutgoingWebhook2) => item.id),
[key]: results.map((item: OutgoingWebhook) => item.id),
};
}

View file

@ -17,8 +17,12 @@ import Text from 'components/Text/Text';
import WithConfirm from 'components/WithConfirm/WithConfirm';
import OutgoingWebhook2Form from 'containers/OutgoingWebhook2Form/OutgoingWebhook2Form';
import OutgoingWebhook2Status from 'containers/OutgoingWebhook2Status/OutgoingWebhook2Status';
import RemoteFilters from 'containers/RemoteFilters/RemoteFilters';
import TeamName from 'containers/TeamName/TeamName';
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
import { ActionDTO } from 'models/action';
import { FiltersValues } from 'models/filters/filters.types';
import { OutgoingWebhook } from 'models/outgoing_webhook/outgoing_webhook.types';
import { OutgoingWebhook2 } from 'models/outgoing_webhook_2/outgoing_webhook_2.types';
import { makeRequest } from 'network';
import { PageProps, WithStoreProps } from 'state/types';
@ -122,6 +126,11 @@ class OutgoingWebhooks2 extends React.Component<OutgoingWebhooks2Props, Outgoing
title: 'Last run',
dataIndex: 'last_run',
},
{
width: '15%',
title: 'Team',
render: (item: OutgoingWebhook) => this.renderTeam(item, store.grafanaTeamStore.items),
},
{
width: '20%',
key: 'action',
@ -139,6 +148,7 @@ class OutgoingWebhooks2 extends React.Component<OutgoingWebhooks2Props, Outgoing
{() => (
<>
<div className={cx('root')}>
{this.renderOutgoingWebhooksFilters()}
<GTable
emptyText={webhooks ? 'No outgoing webhooks found' : 'Loading...'}
title={() => (
@ -191,6 +201,36 @@ class OutgoingWebhooks2 extends React.Component<OutgoingWebhooks2Props, Outgoing
);
}
renderOutgoingWebhooksFilters() {
const { query, store } = this.props;
return (
<div className={cx('filters')}>
<RemoteFilters
query={query}
page="webhooks"
grafanaTeamStore={store.grafanaTeamStore}
onChange={this.handleFiltersChange}
/>
</div>
);
}
handleFiltersChange = (filters: FiltersValues, isOnMount) => {
const { store } = this.props;
const { outgoingWebhook2Store } = store;
outgoingWebhook2Store.updateItems(filters).then(() => {
if (isOnMount) {
this.parseQueryParams();
}
});
};
renderTeam(record: OutgoingWebhook, teams: any) {
return <TeamName team={teams[record.team]} />;
}
renderActionButtons = (record: ActionDTO) => {
return (
<HorizontalGroup justify="flex-end">