Skip to content

Commit 3c51b60

Browse files
committed
feat: Trigger and task validate
1 parent 9014cdb commit 3c51b60

4 files changed

Lines changed: 219 additions & 4 deletions

File tree

apps/trigger/api/trigger.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,35 @@ def get_parameters():
3030
def get_request():
3131
return TriggerCreateRequest
3232

33+
@staticmethod
34+
def get_response():
35+
return TriggerResponse
36+
37+
38+
class TriggerOperateAPI(APIMixin):
39+
@staticmethod
40+
def get_parameters():
41+
return [
42+
OpenApiParameter(
43+
name="workspace_id",
44+
description="工作空间id",
45+
type=OpenApiTypes.STR,
46+
location='path',
47+
required=True,
48+
),
49+
OpenApiParameter(
50+
name="trigger_id",
51+
description="触发器id",
52+
type=OpenApiTypes.STR,
53+
location='path',
54+
required=True,
55+
),
56+
]
57+
58+
@staticmethod
59+
def get_request():
60+
return TriggerCreateRequest
61+
3362
@staticmethod
3463
def get_response():
3564
return TriggerResponse

apps/trigger/serializers/trigger.py

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
from django.utils.translation import gettext_lazy as _
1414
from rest_framework import serializers
1515

16+
from application.models import Application
1617
from common.db.search import page_search
1718
from common.exception.app_exception import AppApiException
19+
from tools.models import Tool
1820
from trigger.models import TriggerTypeChoices, Trigger, TriggerTaskTypeChoices, TriggerTask
1921

2022

@@ -33,6 +35,149 @@ class TriggerCreateRequest(serializers.Serializer):
3335
is_active = serializers.BooleanField(required=False, label=_('Is active'))
3436
trigger_task = TriggerTaskCreateRequest(many=True)
3537

38+
def is_valid(self, *, raise_exception=False):
39+
super().is_valid(raise_exception=True)
40+
trigger_type = self.data.get('trigger_type')
41+
trigger_setting = self.data.get('trigger_setting', {})
42+
43+
if trigger_type == TriggerTypeChoices.SCHEDULED:
44+
self._validate_scheduled_setting(trigger_setting)
45+
46+
elif trigger_type == TriggerTypeChoices.EVENT:
47+
self._validate_event_setting(trigger_setting)
48+
else:
49+
raise AppApiException(500, _('Error trigger type'))
50+
51+
return True
52+
53+
@staticmethod
54+
def _validate_required_field(setting, field_name, trigger_type):
55+
if field_name not in setting:
56+
raise serializers.ValidationError({
57+
'trigger_setting': f'{trigger_type} type requires {field_name} field'
58+
})
59+
60+
@staticmethod
61+
def _validate_non_empty_array(value, field_name):
62+
if not isinstance(value, list):
63+
raise serializers.ValidationError({
64+
'trigger_setting': f'{field_name} must be an array'
65+
})
66+
if len(value) == 0:
67+
raise serializers.ValidationError({
68+
'trigger_setting': f'{field_name} must not be empty'
69+
})
70+
71+
@staticmethod
72+
def _validate_number_range(values, field_name, min_val, max_val):
73+
for val in values:
74+
try:
75+
num = int(str(val))
76+
if num < min_val or num > max_val:
77+
raise ValueError
78+
except (ValueError, TypeError):
79+
raise serializers.ValidationError({
80+
'trigger_setting': f'{field_name} values must be between "{min_val}" and "{max_val}"'
81+
})
82+
83+
def _validate_time_array(self, time_list):
84+
self._validate_non_empty_array(time_list, 'time')
85+
86+
for time_str in time_list:
87+
self._validate_time_format(time_str)
88+
89+
@staticmethod
90+
def _validate_time_format(time_str):
91+
import re
92+
93+
pattern = r'^([01]\d|2[0-3]):([0-5]\d)$'
94+
if not re.match(pattern, str(time_str)):
95+
raise serializers.ValidationError({
96+
'trigger_setting': f'Invalid time format: {time_str}, must be HH:MM (e.g., 09:00)'
97+
})
98+
99+
def _validate_scheduled_setting(self, setting):
100+
schedule_type = setting.get('schedule_type')
101+
102+
valid_types = ['daily', 'weekly', 'monthly', 'interval']
103+
if schedule_type not in valid_types:
104+
raise serializers.ValidationError({'trigger_setting': f'schedule_type must be one of {valid_types}'})
105+
if schedule_type == 'daily':
106+
self._validate_daily(setting)
107+
elif schedule_type == 'weekly':
108+
self._validate_weekly(setting)
109+
elif schedule_type == 'monthly':
110+
self._validate_monthly(setting)
111+
elif schedule_type == 'interval':
112+
self._validate_interval(setting)
113+
114+
def _validate_daily(self, setting):
115+
self._validate_required_field(setting, 'time', 'daily')
116+
self._validate_time_array(setting['time'])
117+
118+
def _validate_weekly(self, setting):
119+
self._validate_required_field(setting, 'weekdays', 'weekly')
120+
self._validate_required_field(setting, 'time', 'weekly')
121+
weekdays = setting['weekdays']
122+
self._validate_non_empty_array(weekdays, 'weekdays')
123+
self._validate_number_range(weekdays, 'weekdays', 1, 7)
124+
self._validate_time_array(setting['time'])
125+
126+
def _validate_monthly(self, setting):
127+
self._validate_required_field(setting, 'days', 'monthly')
128+
self._validate_required_field(setting, 'time', 'monthly')
129+
days = setting['days']
130+
self._validate_non_empty_array(days, 'days')
131+
self._validate_number_range(days, 'days', 1, 31)
132+
self._validate_time_array(setting['time'])
133+
134+
def _validate_interval(self, setting):
135+
self._validate_required_field(setting, 'interval_value', 'interval')
136+
self._validate_required_field(setting, 'interval_unit', 'interval')
137+
interval_value = setting['interval_value']
138+
interval_unit = setting['interval_unit']
139+
try:
140+
value_int = int(interval_value)
141+
if value_int < 1:
142+
raise ValueError
143+
except (ValueError, TypeError):
144+
raise serializers.ValidationError({
145+
'trigger_setting': 'interval_value must be an integer greater than or equal to 1'
146+
})
147+
valid_units = ['minutes', 'hours']
148+
if interval_unit not in valid_units:
149+
raise serializers.ValidationError({
150+
'trigger_setting': f'interval_unit must be one of {valid_units}'
151+
})
152+
153+
@staticmethod
154+
def _validate_event_setting(setting):
155+
body = setting.get('body')
156+
if body is not None and not isinstance(body, list):
157+
raise serializers.ValidationError({
158+
'trigger_setting': 'body must be an array'
159+
})
160+
161+
class TriggerModelSerializer(serializers.ModelSerializer):
162+
class Meta:
163+
model = Trigger
164+
fields = "__all__"
165+
166+
class TriggerTaskModelSerializer(serializers.ModelSerializer):
167+
class Meta:
168+
model = TriggerTask
169+
fields = "__all__"
170+
171+
class ApplicationTriggerTaskSerializer(serializers.ModelSerializer):
172+
class Meta:
173+
model = Application
174+
fields = ['id', 'name', 'work_flow', 'icon', 'type']
175+
176+
class ToolTriggerTaskSerializer(serializers.ModelSerializer):
177+
class Meta:
178+
model = Tool
179+
fields = ['id', 'name', 'input_field_list', 'icon']
180+
36181

37182
class TriggerResponse(serializers.ModelSerializer):
38183
class Meta:
@@ -75,7 +220,6 @@ def insert(self, instance, with_valid=True):
75220
else:
76221
raise AppApiException(500, _('Trigger task can not be empty'))
77222

78-
79223
return TriggerResponse(trigger_model).data
80224

81225
@staticmethod
@@ -89,7 +233,6 @@ def to_trigger_task_model(trigger_id: str, source_type: str, source_id: str):
89233
)
90234

91235

92-
93236
class TriggerOperateSerializer(serializers.Serializer):
94237
trigger_id = serializers.UUIDField(required=True, label=_('trigger id'))
95238
user_id = serializers.UUIDField(required=True, label=_("User ID"))
@@ -104,6 +247,30 @@ def is_valid(self, *, raise_exception=False):
104247
if not query_set.exists():
105248
raise AppApiException(500, _('Trigger id does not exist'))
106249

250+
def edit(self):
251+
pass
252+
253+
def one(self, with_valid=True):
254+
if with_valid:
255+
self.is_valid()
256+
trigger_id = self.data.get('trigger_id')
257+
trigger = QuerySet(Trigger).filter(id=trigger_id).first()
258+
259+
trigger_tasks = QuerySet(TriggerTask).filter(trigger_id=trigger_id)
260+
application_ids = [str(task.source_id) for task in trigger_tasks if task.source_type == TriggerTaskTypeChoices.APPLICATION]
261+
tool_ids = [str(task.source_id) for task in trigger_tasks if task.source_type == TriggerTaskTypeChoices.TOOL]
262+
263+
264+
application_task_list = [ApplicationTriggerTaskSerializer(application).data for application in
265+
QuerySet(Application).filter(id__in=application_ids)]
266+
267+
tool_task_list = [ToolTriggerTaskSerializer(tool).data for tool in QuerySet(Tool).filter(id__in=tool_ids)]
268+
269+
return {
270+
**TriggerModelSerializer(trigger).data,
271+
'application_task_list':application_task_list,
272+
'tool_task_list':tool_task_list,
273+
}
107274

108275
class TriggerQuerySerializer(serializers.Serializer):
109276
name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('Trigger name'))

apps/trigger/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
urlpatterns = [
1616
path('workspace/<str:workspace_id>/trigger', views.TriggerView.as_view(), name='trigger'),
17+
path('workspace/<str:workspace_id>/trigger/<str:trigger_id>', views.TriggerView.Operate.as_view(), name='trigger'),
1718
path('workspace/<str:workspace_id>/trigger/<int:current_page>/<int:page_size>', views.TriggerView.Page.as_view(),
1819
name='trigger_page'),
1920
path('workspace/<str:workspace_id>/task', views.TriggerTaskView.as_view(), name='task'),

apps/trigger/views/trigger.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
from application.api.application_api import ApplicationCreateAPI
1515
from common import result
1616
from common.auth import TokenAuth
17-
from trigger.serializers.trigger import TriggerSerializer, TriggerQuerySerializer
17+
from trigger.serializers.trigger import TriggerSerializer, TriggerQuerySerializer, TriggerOperateSerializer
1818
from common.auth.authentication import has_permissions, get_is_permissions
1919
from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants
2020
from common.log.log import log
2121
from tools.api.tool import GetInternalToolAPI
22-
from trigger.api.trigger import TriggerCreateAPI
22+
from trigger.api.trigger import TriggerCreateAPI, TriggerOperateAPI
2323
from trigger.serializers.trigger import TriggerSerializer
2424

2525

@@ -55,6 +55,24 @@ def get(self, request: Request, workspace_id: str):
5555
'name': request.query_params.get('name'),
5656
'type': request.query_params.get('type')}).list())
5757

58+
class Operate(APIView):
59+
authentication_classes = [TokenAuth]
60+
61+
@extend_schema(
62+
methods=['GET'],
63+
description=_('Get trigger details'),
64+
summary=_('Get trigger details'),
65+
operation_id=_('Get trigger details'), # type: ignore
66+
parameters=TriggerOperateAPI.get_parameters(),
67+
responses=result.DefaultResultSerializer,
68+
tags=[_('Trigger')] # type: ignore
69+
)
70+
def get(self, request: Request, workspace_id: str, trigger_id: str):
71+
return result.success(TriggerOperateSerializer(
72+
data={'trigger_id': trigger_id, 'workspace_id': workspace_id,'user_id': request.user.id}
73+
).one())
74+
75+
5876
class Page(APIView):
5977
authentication_classes = [TokenAuth]
6078

0 commit comments

Comments
 (0)