Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions src/aap_eda/api/filters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .decision_environment import DecisionEnvironmentFilter
from .eda_credential import EdaCredentialFilter
from .event_stream import EventStreamFilter
from .management_job import ManagementJobFilter
from .organization import OrganizationFilter
from .project import ProjectFilter
from .rulebook import RulebookFilter
Expand Down Expand Up @@ -52,4 +53,6 @@
"OrganizationTeamFilter",
# EventStream
"EventStreamFilter",
# Management jobs
"ManagementJobFilter",
)
38 changes: 38 additions & 0 deletions src/aap_eda/api/filters/management_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2026 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import django_filters

from aap_eda.core import models


class ManagementJobFilter(django_filters.FilterSet):
name = django_filters.CharFilter(
field_name="name",
lookup_expr="icontains",
label="Filter by management job name.",
)
job_type = django_filters.CharFilter(
field_name="job_type",
lookup_expr="exact",
label="Filter by job type.",
)
is_enabled = django_filters.BooleanFilter(
field_name="is_enabled",
label="Filter by enabled status.",
)

class Meta:
model = models.ManagementJob
fields = ["name", "job_type", "is_enabled"]
11 changes: 11 additions & 0 deletions src/aap_eda/api/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@
EdaCredentialUpdateSerializer,
)
from .event_stream import EventStreamInSerializer, EventStreamOutSerializer
from .management_job import (
ManagementJobExecutionDetailSerializer,
ManagementJobExecutionSerializer,
ManagementJobReadSerializer,
ManagementJobUpdateSerializer,
)
from .organization import (
OrganizationCreateSerializer,
OrganizationRefSerializer,
Expand Down Expand Up @@ -155,4 +161,9 @@
# event streams
"EventStreamInSerializer",
"EventStreamOutSerializer",
# management jobs
"ManagementJobReadSerializer",
"ManagementJobUpdateSerializer",
"ManagementJobExecutionSerializer",
"ManagementJobExecutionDetailSerializer",
)
77 changes: 77 additions & 0 deletions src/aap_eda/api/serializers/management_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright 2026 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from rest_framework import serializers

from aap_eda.core import models

__all__ = (
"ManagementJobReadSerializer",
"ManagementJobUpdateSerializer",
"ManagementJobExecutionSerializer",
"ManagementJobExecutionDetailSerializer",
)


class ManagementJobReadSerializer(serializers.ModelSerializer):
class Meta:
model = models.ManagementJob
read_only_fields = [
"id",
"name",
"description",
"job_type",
"organization",
"created_at",
"modified_at",
]
fields = [
"is_enabled",
"parameters",
*read_only_fields,
]


class ManagementJobUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = models.ManagementJob
fields = ["is_enabled", "parameters"]


class ManagementJobExecutionSerializer(serializers.ModelSerializer):
class Meta:
model = models.ManagementJobExecution
read_only_fields = [
"id",
"status",
"started_at",
"finished_at",
"created_at",
]
fields = read_only_fields


class ManagementJobExecutionDetailSerializer(serializers.ModelSerializer):
class Meta:
model = models.ManagementJobExecution
read_only_fields = [
"id",
"status",
"started_at",
"finished_at",
"output",
"errors",
"created_at",
]
fields = read_only_fields
5 changes: 5 additions & 0 deletions src/aap_eda/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
router.register("organizations", views.OrganizationViewSet)
router.register("teams", views.TeamViewSet)
router.register("event-streams", views.EventStreamViewSet)
router.register(
"management-jobs",
views.ManagementJobViewSet,
basename="management-job",
)
router.register(
"external_event_stream",
views.ExternalEventStreamViewSet,
Expand Down
3 changes: 3 additions & 0 deletions src/aap_eda/api/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .decision_environment import DecisionEnvironmentViewSet
from .eda_credential import EdaCredentialViewSet
from .event_stream import EventStreamViewSet
from .management_job import ManagementJobViewSet
from .external_event_stream import ExternalEventStreamViewSet
from .organization import OrganizationViewSet
from .project import ProjectViewSet
Expand Down Expand Up @@ -63,4 +64,6 @@
"EventStreamViewSet",
# External event stream
"ExternalEventStreamViewSet",
# Management jobs
"ManagementJobViewSet",
)
111 changes: 111 additions & 0 deletions src/aap_eda/api/views/management_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright 2026 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from django_filters import rest_framework as defaultfilters
from drf_spectacular.utils import extend_schema
from rest_framework import mixins, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from aap_eda.api import filters, serializers
from aap_eda.api.views.mixins import PartialUpdateOnlyModelMixin
from aap_eda.core import models
from aap_eda.core.enums import ExecutionStatus


class ManagementJobViewSet(
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
PartialUpdateOnlyModelMixin,
viewsets.GenericViewSet,
):
queryset = models.ManagementJob.objects.order_by("-created_at")
filter_backends = (defaultfilters.DjangoFilterBackend,)
filterset_class = filters.ManagementJobFilter

def get_serializer_class(self):
if self.action in ("list", "retrieve"):
return serializers.ManagementJobReadSerializer
if self.action == "partial_update":
return serializers.ManagementJobUpdateSerializer
return serializers.ManagementJobReadSerializer

def get_response_serializer_class(self):
return serializers.ManagementJobReadSerializer

@extend_schema(
description="Launch an on-demand execution of a management job.",
request=None,
responses={
status.HTTP_201_CREATED: serializers.ManagementJobExecutionDetailSerializer, # noqa: E501
},
)
@action(methods=["post"], detail=True)
def launch(self, request, pk=None):
management_job = self.get_object()
execution = models.ManagementJobExecution.objects.create(
management_job=management_job,
status=ExecutionStatus.PENDING,
organization=management_job.organization,
)
serializer = serializers.ManagementJobExecutionDetailSerializer(
execution
)
return Response(serializer.data, status=status.HTTP_201_CREATED)

@extend_schema(
description="List execution history for a management job.",
responses={
status.HTTP_200_OK: serializers.ManagementJobExecutionSerializer(
many=True
),
},
)
@action(methods=["get"], detail=True)
def executions(self, request, pk=None):
management_job = self.get_object()
queryset = models.ManagementJobExecution.objects.filter(
management_job=management_job,
).order_by("-created_at")
result = self.paginate_queryset(queryset)
serializer = serializers.ManagementJobExecutionSerializer(
result, many=True
)
return self.get_paginated_response(serializer.data)

@extend_schema(
description="Get execution details for a management job.",
responses={
status.HTTP_200_OK: serializers.ManagementJobExecutionDetailSerializer, # noqa: E501
},
)
@action(
methods=["get"],
detail=True,
url_path="executions/(?P<exec_id>[^/.]+)",
url_name="execution-detail",
)
def execution_detail(self, request, pk=None, exec_id=None):
management_job = self.get_object()
try:
execution = models.ManagementJobExecution.objects.get(
pk=exec_id,
management_job=management_job,
)
except models.ManagementJobExecution.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = serializers.ManagementJobExecutionDetailSerializer(
execution
)
return Response(serializer.data)
12 changes: 12 additions & 0 deletions src/aap_eda/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,15 @@ class AnalyticsCredentialType(DjangoStrEnum):
AnalyticsCredentialType.BASIC,
AnalyticsCredentialType.OAUTH,
]


class ManagementJobType(DjangoStrEnum):
CLEANUP_AUDIT_LOGS = "cleanup_audit_logs"
CLEANUP_STALE_ACTIVATIONS = "cleanup_stale_activations"


class ExecutionStatus(DjangoStrEnum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
Loading
Loading