events: add graph for event volume (#7639)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L 2023-12-06 19:06:07 +02:00 committed by GitHub
parent 1d003c1c0c
commit 944368c4f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 498 additions and 166 deletions

View File

@ -5,7 +5,7 @@ from json import loads
import django_filters import django_filters
from django.db.models.aggregates import Count from django.db.models.aggregates import Count
from django.db.models.fields.json import KeyTextTransform, KeyTransform from django.db.models.fields.json import KeyTextTransform, KeyTransform
from django.db.models.functions import ExtractDay from django.db.models.functions import ExtractDay, ExtractHour
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema from drf_spectacular.utils import OpenApiParameter, extend_schema
from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_objects_for_user
@ -149,7 +149,15 @@ class EventViewSet(ModelViewSet):
return Response(EventTopPerUserSerializer(instance=events, many=True).data) return Response(EventTopPerUserSerializer(instance=events, many=True).data)
@extend_schema( @extend_schema(
methods=["GET"], responses={200: CoordinateSerializer(many=True)},
)
@action(detail=False, methods=["GET"], pagination_class=None)
def volume(self, request: Request) -> Response:
"""Get event volume for specified filters and timeframe"""
queryset = self.filter_queryset(self.get_queryset())
return Response(queryset.get_events_per(timedelta(days=7), ExtractHour, 7 * 3))
@extend_schema(
responses={200: CoordinateSerializer(many=True)}, responses={200: CoordinateSerializer(many=True)},
filters=[], filters=[],
parameters=[ parameters=[

File diff suppressed because it is too large Load Diff

View File

@ -6276,6 +6276,86 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' description: ''
/events/events/volume/:
get:
operationId: events_events_volume_list
description: Get event volume for specified filters and timeframe
parameters:
- in: query
name: action
schema:
type: string
- in: query
name: client_ip
schema:
type: string
- in: query
name: context_authorized_app
schema:
type: string
description: Context Authorized application
- in: query
name: context_model_app
schema:
type: string
description: Context Model App
- in: query
name: context_model_name
schema:
type: string
description: Context Model Name
- in: query
name: context_model_pk
schema:
type: string
description: Context Model Primary Key
- name: ordering
required: false
in: query
description: Which field to use when ordering the results.
schema:
type: string
- name: search
required: false
in: query
description: A search term.
schema:
type: string
- in: query
name: tenant_name
schema:
type: string
description: Tenant name
- in: query
name: username
schema:
type: string
description: Username
tags:
- events
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Coordinate'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/events/notifications/: /events/notifications/:
get: get:
operationId: events_notifications_list operationId: events_notifications_list

View File

@ -1,3 +1,4 @@
import "@goauthentik/admin/events/EventVolumeChart";
import { EventGeo } from "@goauthentik/admin/events/utils"; import { EventGeo } from "@goauthentik/admin/events/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EventWithContext } from "@goauthentik/common/events"; import { EventWithContext } from "@goauthentik/common/events";
@ -10,7 +11,7 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { Event, EventsApi } from "@goauthentik/api"; import { Event, EventsApi } from "@goauthentik/api";
@ -35,6 +36,14 @@ export class EventListPage extends TablePage<Event> {
@property() @property()
order = "-created"; order = "-created";
static get styles(): CSSResult[] {
return super.styles.concat(css`
.pf-m-no-padding-bottom {
padding-bottom: 0;
}
`);
}
async apiEndpoint(page: number): Promise<PaginatedResponse<Event>> { async apiEndpoint(page: number): Promise<PaginatedResponse<Event>> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({ return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
ordering: this.order, ordering: this.order,
@ -55,6 +64,19 @@ export class EventListPage extends TablePage<Event> {
]; ];
} }
renderSectionBefore(): TemplateResult {
return html`
<div class="pf-c-page__main-section pf-m-no-padding-bottom">
<ak-events-volume-chart
.query=${{
page: this.page,
search: this.search,
}}
></ak-events-volume-chart>
</div>
`;
}
row(item: EventWithContext): TemplateResult[] { row(item: EventWithContext): TemplateResult[] {
return [ return [
html`<div>${actionToLabel(item.action)}</div> html`<div>${actionToLabel(item.action)}</div>

View File

@ -0,0 +1,63 @@
import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config";
import { AKChart } from "@goauthentik/app/elements/charts/Chart";
import { ChartData } from "chart.js";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import { Coordinate, EventsApi, EventsEventsListRequest } from "@goauthentik/api";
@customElement("ak-events-volume-chart")
export class EventVolumeChart extends AKChart<Coordinate[]> {
_query?: EventsEventsListRequest;
@property({ attribute: false })
set query(value: EventsEventsListRequest | undefined) {
this._query = value;
this.refreshHandler();
}
static get styles(): CSSResult[] {
return super.styles.concat(
PFCard,
css`
.pf-c-card__body {
height: 12rem;
}
`,
);
}
apiRequest(): Promise<Coordinate[]> {
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList(this._query);
}
getChartData(data: Coordinate[]): ChartData {
return {
datasets: [
{
label: msg("Events"),
backgroundColor: "rgba(189, 229, 184, .5)",
spanGaps: true,
data:
data.map((cord) => {
return {
x: cord.xCord || 0,
y: cord.yCord || 0,
};
}) || [],
},
],
};
}
render(): TemplateResult {
return html`<div class="pf-c-card">
<div class="pf-c-card__title">${msg("Event volume")}</div>
<div class="pf-c-card__body">${super.render()}</div>
</div>`;
}
}

View File

@ -6114,6 +6114,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6391,6 +6391,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6030,6 +6030,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -8023,6 +8023,9 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6238,6 +6238,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -7978,4 +7978,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
<target>Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ĝŕōũƥ ćŕēàţĩōń.</target> <target>Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ĝŕōũƥ ćŕēàţĩōń.</target>
</trans-unit> </trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit>
</body></file></xliff> </body></file></xliff>

View File

@ -6023,6 +6023,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -8025,6 +8025,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6071,6 +6071,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -7962,6 +7962,9 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sd7728d2b6e1d25e9"> <trans-unit id="sd7728d2b6e1d25e9">
<source>Property mappings used for group creation.</source> <source>Property mappings used for group creation.</source>
</trans-unit>
<trans-unit id="s7513372fe60f6387">
<source>Event volume</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>