2323from kleinkram .api .deser import FileObject
2424from kleinkram .api .deser import MissionObject
2525from kleinkram .api .deser import ProjectObject
26+ from kleinkram .api .deser import TemplateObject
27+ from kleinkram .api .deser import TriggerObject
2628from kleinkram .api .deser import _parse_action_template
29+ from kleinkram .api .deser import _parse_action_trigger
2730from kleinkram .api .deser import _parse_execution
2831from kleinkram .api .deser import _parse_file
2932from kleinkram .api .deser import _parse_mission
3336from kleinkram .api .query import FileQuery
3437from kleinkram .api .query import MissionQuery
3538from kleinkram .api .query import ProjectQuery
39+ from kleinkram .api .query import TriggerQuery
3640from kleinkram .api .query import file_query_is_unique
3741from kleinkram .api .query import mission_query_is_unique
3842from kleinkram .api .query import project_query_is_unique
5256from kleinkram .errors import TemplateNotFound
5357from kleinkram .errors import TemplateValidationError
5458from kleinkram .models import ActionTemplate
59+ from kleinkram .models import ActionTrigger
5560from kleinkram .models import Execution
5661from kleinkram .models import File
5762from kleinkram .models import Mission
5863from kleinkram .models import Project
64+ from kleinkram .models import TriggerConfig
65+ from kleinkram .models import TriggerType
5966from kleinkram .utils import is_valid_uuid4
6067from kleinkram .utils import parse_uuid_like
6168from kleinkram .utils import split_args
@@ -236,7 +243,7 @@ def get_template_revisions(
236243) -> Generator [ActionTemplate , None , None ]:
237244 try :
238245 response_stream = paginated_request (client , f"/templates/{ template_id } /revisions" )
239- yield from map (lambda p : _parse_action_template (p ), response_stream )
246+ yield from map (lambda p : _parse_action_template (TemplateObject ( p ) ), response_stream )
240247 except ValueError as e :
241248 raise kleinkram .errors .TemplateNotFound (f"Template not found: { template_id } " ) from e
242249 except httpx .HTTPStatusError :
@@ -260,7 +267,30 @@ def get_templates(
260267 client : AuthenticatedClient ,
261268) -> Generator [ActionTemplate , None , None ]:
262269 response_stream = paginated_request (client , "/templates" )
263- yield from map (lambda p : _parse_action_template (p ), response_stream )
270+ yield from map (lambda p : _parse_action_template (TemplateObject (p )), response_stream )
271+
272+
273+ LIST_ACTIONTRIGGERS_ENDPOINT = "/triggers"
274+
275+
276+ def get_triggers (client : AuthenticatedClient , query : Optional [TriggerQuery ] = None ) -> List [ActionTrigger ]:
277+ params = {"missionUuid" : str (query .mission_uuid )} if query and query .mission_uuid else None
278+ # the backend does not support pagination for triggers currently, so we do a single request
279+ resp = client .get (LIST_ACTIONTRIGGERS_ENDPOINT , params = params )
280+ resp .raise_for_status ()
281+ payload = resp .json ()
282+ return list (map (lambda p : _parse_action_trigger (TriggerObject (p )), payload ))
283+
284+
285+ def get_trigger (
286+ client : AuthenticatedClient ,
287+ trigger_uuid : UUID ,
288+ ) -> ActionTrigger :
289+ resp = client .patch (UPDATE_TRIGGER .format (trigger_uuid ), json = {})
290+ if resp .status_code == 404 :
291+ raise kleinkram .errors .TriggerNotFound (f"Trigger not found: { trigger_uuid } " )
292+ resp .raise_for_status ()
293+ return _parse_action_trigger (TriggerObject (resp .json ()))
264294
265295
266296def get_project (client : AuthenticatedClient , query : ProjectQuery , exact_match : bool = False ) -> Project :
@@ -299,6 +329,29 @@ def get_file(client: AuthenticatedClient, query: FileQuery) -> File:
299329 raise kleinkram .errors .FileNotFound (f"File not found: { query } " )
300330
301331
332+ def _create_trigger (
333+ client : AuthenticatedClient ,
334+ name : str ,
335+ description : str ,
336+ template_uuid : UUID ,
337+ mission_uuid : UUID ,
338+ type_ : TriggerType ,
339+ config : TriggerConfig ,
340+ ) -> UUID :
341+ payload = {
342+ "name" : name ,
343+ "description" : description ,
344+ "templateUuid" : str (template_uuid ),
345+ "missionUuid" : str (mission_uuid ),
346+ "type" : type_ .value ,
347+ "config" : config .__dict__ ,
348+ }
349+ resp = client .post ("/triggers" , json = payload )
350+ resp .raise_for_status ()
351+
352+ return UUID (resp .json ()["uuid" ], version = 4 )
353+
354+
302355def _launch_execution (client : AuthenticatedClient , mission_uuid : UUID , template_uuid : UUID ) -> UUID :
303356 """
304357 Submits a new action to the API and returns the action UUID.
@@ -460,6 +513,42 @@ def _update_project(
460513 resp .raise_for_status ()
461514
462515
516+ UPDATE_TRIGGER = "/triggers/{}"
517+
518+
519+ def _update_trigger (
520+ client : AuthenticatedClient ,
521+ trigger_uuid : UUID ,
522+ * ,
523+ name : Optional [str ] = None ,
524+ description : Optional [str ] = None ,
525+ template_uuid : Optional [UUID ] = None ,
526+ mission_uuid : Optional [UUID ] = None ,
527+ type_ : Optional [TriggerType ] = None ,
528+ config : Optional [TriggerConfig ] = None ,
529+ ) -> None :
530+
531+ if all (v is None for v in [name , description , template_uuid , mission_uuid , type_ , config ]):
532+ raise ValueError ("at least one field must be updated" )
533+
534+ body = {}
535+ if name is not None :
536+ body ["name" ] = name
537+ if description is not None :
538+ body ["description" ] = description
539+ if template_uuid is not None :
540+ body ["templateUuid" ] = str (template_uuid )
541+ if mission_uuid is not None :
542+ body ["missionUuid" ] = str (mission_uuid )
543+ if type_ is not None :
544+ body ["type" ] = type_ .value
545+ if config is not None :
546+ body ["config" ] = config .__dict__
547+
548+ resp = client .patch (f"{ UPDATE_TRIGGER .format (trigger_uuid )} " , json = body )
549+ resp .raise_for_status ()
550+
551+
463552def _get_api_version () -> Tuple [int , int , int ]:
464553 config = get_config ()
465554 client = httpx .Client ()
@@ -537,3 +626,13 @@ def _delete_execution(client: AuthenticatedClient, execution_id: UUID) -> None:
537626 if resp .status_code == 404 :
538627 raise kleinkram .errors .ExecutionNotFound (f"Execution not found: { execution_id } " )
539628 resp .raise_for_status ()
629+
630+
631+ DELETE_TRIGGER_ONE = "/triggers/{}"
632+
633+
634+ def _delete_trigger (client : AuthenticatedClient , trigger_uuid : UUID ) -> None :
635+ resp = client .delete (DELETE_TRIGGER_ONE .format (trigger_uuid ))
636+ if resp .status_code == 404 :
637+ raise kleinkram .errors .TriggerNotFound (f"Trigger not found: { trigger_uuid } " )
638+ resp .raise_for_status ()
0 commit comments