events: add graph for event volume (#7639)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
1d003c1c0c
commit
944368c4f2
|
@ -5,7 +5,7 @@ from json import loads
|
|||
import django_filters
|
||||
from django.db.models.aggregates import Count
|
||||
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.utils import OpenApiParameter, extend_schema
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
|
@ -149,7 +149,15 @@ class EventViewSet(ModelViewSet):
|
|||
return Response(EventTopPerUserSerializer(instance=events, many=True).data)
|
||||
|
||||
@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)},
|
||||
filters=[],
|
||||
parameters=[
|
||||
|
|
File diff suppressed because it is too large
Load diff
80
schema.yml
80
schema.yml
|
@ -6276,6 +6276,86 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
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/:
|
||||
get:
|
||||
operationId: events_notifications_list
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import "@goauthentik/admin/events/EventVolumeChart";
|
||||
import { EventGeo } from "@goauthentik/admin/events/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
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 { 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 { Event, EventsApi } from "@goauthentik/api";
|
||||
|
@ -35,6 +36,14 @@ export class EventListPage extends TablePage<Event> {
|
|||
@property()
|
||||
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>> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
|
||||
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[] {
|
||||
return [
|
||||
html`<div>${actionToLabel(item.action)}</div>
|
||||
|
|
63
web/src/admin/events/EventVolumeChart.ts
Normal file
63
web/src/admin/events/EventVolumeChart.ts
Normal 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>`;
|
||||
}
|
||||
}
|
|
@ -6114,6 +6114,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6391,6 +6391,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6030,6 +6030,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -8023,6 +8023,9 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6238,6 +6238,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</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>
|
||||
<target>Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ĝŕōũƥ ćŕēàţĩōń.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
|
|
@ -6023,6 +6023,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -8025,6 +8025,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6071,6 +6071,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -7962,6 +7962,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="sd7728d2b6e1d25e9">
|
||||
<source>Property mappings used for group creation.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7513372fe60f6387">
|
||||
<source>Event volume</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
Reference in a new issue