zenml-nightly 0.58.2.dev20240623__py3-none-any.whl → 0.58.2.dev20240626__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. zenml/VERSION +1 -1
  2. zenml/actions/base_action.py +177 -174
  3. zenml/actions/pipeline_run/pipeline_run_action.py +28 -23
  4. zenml/artifact_stores/base_artifact_store.py +7 -1
  5. zenml/artifacts/utils.py +13 -10
  6. zenml/cli/service_connectors.py +1 -0
  7. zenml/client.py +234 -58
  8. zenml/config/compiler.py +10 -9
  9. zenml/config/docker_settings.py +25 -9
  10. zenml/constants.py +1 -1
  11. zenml/event_hub/base_event_hub.py +5 -5
  12. zenml/event_hub/event_hub.py +15 -6
  13. zenml/event_sources/base_event.py +0 -11
  14. zenml/event_sources/base_event_source.py +7 -0
  15. zenml/event_sources/webhooks/base_webhook_event_source.py +1 -4
  16. zenml/exceptions.py +4 -0
  17. zenml/hooks/hook_validators.py +2 -3
  18. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +3 -3
  19. zenml/integrations/mlflow/__init__.py +1 -1
  20. zenml/integrations/s3/artifact_stores/s3_artifact_store.py +76 -3
  21. zenml/logging/step_logging.py +54 -51
  22. zenml/models/__init__.py +17 -0
  23. zenml/models/v2/core/action.py +276 -0
  24. zenml/models/v2/core/trigger.py +182 -141
  25. zenml/new/pipelines/pipeline.py +13 -3
  26. zenml/new/pipelines/pipeline_decorator.py +1 -2
  27. zenml/new/pipelines/run_utils.py +1 -12
  28. zenml/new/steps/step_decorator.py +2 -3
  29. zenml/pipelines/base_pipeline.py +0 -2
  30. zenml/pipelines/pipeline_decorator.py +1 -2
  31. zenml/stack/stack_component.py +4 -0
  32. zenml/steps/base_step.py +1 -2
  33. zenml/steps/step_decorator.py +1 -2
  34. zenml/types.py +10 -1
  35. zenml/utils/pipeline_docker_image_builder.py +20 -5
  36. zenml/zen_server/rbac/models.py +1 -0
  37. zenml/zen_server/rbac/utils.py +22 -1
  38. zenml/zen_server/routers/actions_endpoints.py +324 -0
  39. zenml/zen_server/routers/triggers_endpoints.py +30 -158
  40. zenml/zen_server/zen_server_api.py +2 -0
  41. zenml/zen_stores/migrations/versions/25155145c545_separate_actions_and_triggers.py +228 -0
  42. zenml/zen_stores/rest_zen_store.py +103 -4
  43. zenml/zen_stores/schemas/__init__.py +2 -0
  44. zenml/zen_stores/schemas/action_schemas.py +192 -0
  45. zenml/zen_stores/schemas/trigger_schemas.py +43 -50
  46. zenml/zen_stores/schemas/user_schemas.py +10 -2
  47. zenml/zen_stores/schemas/workspace_schemas.py +5 -0
  48. zenml/zen_stores/sql_zen_store.py +240 -30
  49. zenml/zen_stores/zen_store_interface.py +85 -0
  50. {zenml_nightly-0.58.2.dev20240623.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/METADATA +2 -2
  51. {zenml_nightly-0.58.2.dev20240623.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/RECORD +54 -50
  52. {zenml_nightly-0.58.2.dev20240623.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/LICENSE +0 -0
  53. {zenml_nightly-0.58.2.dev20240623.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/WHEEL +0 -0
  54. {zenml_nightly-0.58.2.dev20240623.dist-info → zenml_nightly-0.58.2.dev20240626.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,6 @@ from uuid import UUID
18
18
  from fastapi import APIRouter, Depends, Security
19
19
 
20
20
  from zenml import TriggerRequest
21
- from zenml.actions.base_action import BaseActionHandler
22
21
  from zenml.constants import API, TRIGGER_EXECUTIONS, TRIGGERS, VERSION_1
23
22
  from zenml.enums import PluginType
24
23
  from zenml.event_sources.base_event_source import BaseEventSourceHandler
@@ -81,54 +80,11 @@ def list_triggers(
81
80
  Returns:
82
81
  All triggers.
83
82
  """
84
-
85
- def list_triggers_fn(
86
- filter_model: TriggerFilter,
87
- ) -> Page[TriggerResponse]:
88
- """List triggers through their associated plugins.
89
-
90
- Args:
91
- filter_model: Filter model used for pagination, sorting,
92
- filtering.
93
-
94
- Returns:
95
- All triggers.
96
-
97
- Raises:
98
- ValueError: If the plugin for a trigger action is not a valid action
99
- plugin.
100
- """
101
- triggers = zen_store().list_triggers(
102
- trigger_filter_model=filter_model, hydrate=hydrate
103
- )
104
-
105
- # Process the triggers through their associated plugins
106
- for idx, trigger in enumerate(triggers.items):
107
- action_handler = plugin_flavor_registry().get_plugin(
108
- name=trigger.action_flavor,
109
- _type=PluginType.ACTION,
110
- subtype=trigger.action_subtype,
111
- )
112
-
113
- # Validate that the flavor and plugin_type correspond to an action
114
- # handler implementation
115
- if not isinstance(action_handler, BaseActionHandler):
116
- raise ValueError(
117
- f"Action handler plugin {trigger.action_subtype} "
118
- f"for flavor {trigger.action_flavor} is not a valid action "
119
- "handler plugin."
120
- )
121
-
122
- triggers.items[idx] = action_handler.get_trigger(
123
- trigger, hydrate=hydrate
124
- )
125
-
126
- return triggers
127
-
128
83
  return verify_permissions_and_list_entities(
129
84
  filter_model=trigger_filter_model,
130
85
  resource_type=ResourceType.TRIGGER,
131
- list_method=list_triggers_fn,
86
+ list_method=zen_store().list_triggers,
87
+ hydrate=hydrate,
132
88
  )
133
89
 
134
90
 
@@ -152,31 +108,9 @@ def get_trigger(
152
108
 
153
109
  Returns:
154
110
  The requested trigger.
155
-
156
- Raises:
157
- ValueError: If the action flavor/subtype combination is not actually a webhook event source
158
111
  """
159
112
  trigger = zen_store().get_trigger(trigger_id=trigger_id, hydrate=hydrate)
160
-
161
113
  verify_permission_for_model(trigger, action=Action.READ)
162
-
163
- action_handler = plugin_flavor_registry().get_plugin(
164
- name=trigger.action_flavor,
165
- _type=PluginType.ACTION,
166
- subtype=trigger.action_subtype,
167
- )
168
-
169
- # Validate that the flavor and plugin_type correspond to an action
170
- # handler implementation
171
- if not isinstance(action_handler, BaseActionHandler):
172
- raise ValueError(
173
- f"Action handler plugin {trigger.action_subtype} "
174
- f"for flavor {trigger.action_flavor} is not a valid action "
175
- "handler plugin."
176
- )
177
-
178
- trigger = action_handler.get_trigger(trigger, hydrate=hydrate)
179
-
180
114
  return dehydrate_response_model(trigger)
181
115
 
182
116
 
@@ -201,55 +135,35 @@ def create_trigger(
201
135
  Raises:
202
136
  ValueError: If the action flavor/subtype combination is not actually a webhook event source
203
137
  """
204
- if trigger.service_account_id:
205
- service_account = zen_store().get_service_account(
206
- service_account_name_or_id=trigger.service_account_id
138
+ if trigger.event_source_id and trigger.event_filter:
139
+ event_source = zen_store().get_event_source(
140
+ event_source_id=trigger.event_source_id
207
141
  )
208
- verify_permission_for_model(service_account, action=Action.READ)
209
-
210
- event_source = zen_store().get_event_source(
211
- event_source_id=trigger.event_source_id
212
- )
213
142
 
214
- event_source_handler = plugin_flavor_registry().get_plugin(
215
- name=event_source.flavor,
216
- _type=PluginType.EVENT_SOURCE,
217
- subtype=event_source.plugin_subtype,
218
- )
219
-
220
- # Validate that the flavor and plugin_type correspond to an event source
221
- # implementation
222
- if not isinstance(event_source_handler, BaseEventSourceHandler):
223
- raise ValueError(
224
- f"Event source plugin {event_source.plugin_subtype} "
225
- f"for flavor {event_source.flavor} is not a valid event source "
226
- "handler implementation."
143
+ event_source_handler = plugin_flavor_registry().get_plugin(
144
+ name=event_source.flavor,
145
+ _type=PluginType.EVENT_SOURCE,
146
+ subtype=event_source.plugin_subtype,
227
147
  )
228
148
 
229
- # Validate the trigger event filter
230
- event_source_handler.validate_event_filter_configuration(
231
- trigger.event_filter
232
- )
233
-
234
- action_handler = plugin_flavor_registry().get_plugin(
235
- name=trigger.action_flavor,
236
- _type=PluginType.ACTION,
237
- subtype=trigger.action_subtype,
238
- )
149
+ # Validate that the flavor and plugin_type correspond to an event source
150
+ # implementation
151
+ if not isinstance(event_source_handler, BaseEventSourceHandler):
152
+ raise ValueError(
153
+ f"Event source plugin {event_source.plugin_subtype} "
154
+ f"for flavor {event_source.flavor} is not a valid event source "
155
+ "handler implementation."
156
+ )
239
157
 
240
- # Validate that the flavor and plugin_type correspond to an action
241
- # handler implementation
242
- if not isinstance(action_handler, BaseActionHandler):
243
- raise ValueError(
244
- f"Action handler plugin {trigger.action_subtype} "
245
- f"for flavor {trigger.action_flavor} is not a valid action "
246
- "handler plugin."
158
+ # Validate the trigger event filter
159
+ event_source_handler.validate_event_filter_configuration(
160
+ trigger.event_filter
247
161
  )
248
162
 
249
163
  return verify_permissions_and_create_entity(
250
164
  request_model=trigger,
251
165
  resource_type=ResourceType.TRIGGER,
252
- create_method=action_handler.create_trigger,
166
+ create_method=zen_store().create_trigger,
253
167
  )
254
168
 
255
169
 
@@ -278,13 +192,12 @@ def update_trigger(
278
192
  """
279
193
  trigger = zen_store().get_trigger(trigger_id=trigger_id)
280
194
 
281
- if trigger_update.service_account_id:
282
- service_account = zen_store().get_service_account(
283
- service_account_name_or_id=trigger_update.service_account_id
284
- )
285
- verify_permission_for_model(service_account, action=Action.READ)
286
-
287
195
  if trigger_update.event_filter:
196
+ if not trigger.event_source:
197
+ raise ValueError(
198
+ "Trying to set event filter for trigger without event source."
199
+ )
200
+
288
201
  event_source = zen_store().get_event_source(
289
202
  event_source_id=trigger.event_source.id
290
203
  )
@@ -306,29 +219,13 @@ def update_trigger(
306
219
 
307
220
  # Validate the trigger event filter
308
221
  event_source_handler.validate_event_filter_configuration(
309
- trigger.event_filter
222
+ trigger_update.event_filter
310
223
  )
311
224
 
312
225
  verify_permission_for_model(trigger, action=Action.UPDATE)
313
226
 
314
- action_handler = plugin_flavor_registry().get_plugin(
315
- name=trigger.action_flavor,
316
- _type=PluginType.ACTION,
317
- subtype=trigger.action_subtype,
318
- )
319
-
320
- # Validate that the flavor and plugin_type correspond to an action
321
- # handler implementation
322
- if not isinstance(action_handler, BaseActionHandler):
323
- raise ValueError(
324
- f"Action handler plugin {trigger.action_subtype} "
325
- f"for flavor {trigger.action_flavor} is not a valid action "
326
- "handler plugin."
327
- )
328
-
329
- updated_trigger = action_handler.update_trigger(
330
- trigger=trigger,
331
- trigger_update=trigger_update,
227
+ updated_trigger = zen_store().update_trigger(
228
+ trigger_id=trigger_id, trigger_update=trigger_update
332
229
  )
333
230
 
334
231
  return dehydrate_response_model(updated_trigger)
@@ -341,41 +238,16 @@ def update_trigger(
341
238
  @handle_exceptions
342
239
  def delete_trigger(
343
240
  trigger_id: UUID,
344
- force: bool = False,
345
241
  _: AuthContext = Security(authorize),
346
242
  ) -> None:
347
243
  """Deletes a trigger.
348
244
 
349
245
  Args:
350
246
  trigger_id: Name of the trigger.
351
- force: Flag deciding whether to force delete the trigger.
352
-
353
- Raises:
354
- ValueError: If the action flavor/subtype combination is not actually a webhook event source
355
247
  """
356
248
  trigger = zen_store().get_trigger(trigger_id=trigger_id)
357
-
358
249
  verify_permission_for_model(trigger, action=Action.DELETE)
359
-
360
- action_handler = plugin_flavor_registry().get_plugin(
361
- name=trigger.action_flavor,
362
- _type=PluginType.ACTION,
363
- subtype=trigger.action_subtype,
364
- )
365
-
366
- # Validate that the flavor and plugin_type correspond to an action
367
- # handler implementation
368
- if not isinstance(action_handler, BaseActionHandler):
369
- raise ValueError(
370
- f"Action handler plugin {trigger.action_subtype} "
371
- f"for flavor {trigger.action_flavor} is not a valid action "
372
- "handler plugin."
373
- )
374
-
375
- action_handler.delete_trigger(
376
- trigger=trigger,
377
- force=force,
378
- )
250
+ zen_store().delete_trigger(trigger_id=trigger_id)
379
251
 
380
252
 
381
253
  executions_router = APIRouter(
@@ -40,6 +40,7 @@ from zenml.constants import API, HEALTH
40
40
  from zenml.enums import AuthScheme, SourceContextTypes
41
41
  from zenml.zen_server.exceptions import error_detail
42
42
  from zenml.zen_server.routers import (
43
+ actions_endpoints,
43
44
  artifact_endpoint,
44
45
  artifact_version_endpoints,
45
46
  auth_endpoints,
@@ -262,6 +263,7 @@ async def dashboard(request: Request) -> Any:
262
263
  return templates.TemplateResponse("index.html", {"request": request})
263
264
 
264
265
 
266
+ app.include_router(actions_endpoints.router)
265
267
  app.include_router(artifact_endpoint.artifact_router)
266
268
  app.include_router(artifact_version_endpoints.artifact_version_router)
267
269
  app.include_router(auth_endpoints.router)
@@ -0,0 +1,228 @@
1
+ """Separate actions and triggers [25155145c545].
2
+
3
+ Revision ID: 25155145c545
4
+ Revises: 0.58.2
5
+ Create Date: 2024-05-16 11:29:53.341275
6
+
7
+ """
8
+
9
+ from datetime import datetime
10
+ from uuid import uuid4
11
+
12
+ import sqlalchemy as sa
13
+ import sqlmodel
14
+ from alembic import op
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision = "25155145c545"
18
+ down_revision = "0.58.2"
19
+ branch_labels = None
20
+ depends_on = None
21
+
22
+
23
+ def migrate_actions() -> None:
24
+ """Migrate actions from the trigger table."""
25
+ conn = op.get_bind()
26
+ meta = sa.MetaData()
27
+ meta.reflect(only=("trigger", "action"), bind=op.get_bind())
28
+ trigger_table = sa.Table("trigger", meta)
29
+ action_table = sa.Table("action", meta)
30
+
31
+ triggers = conn.execute(
32
+ sa.select(
33
+ trigger_table.c.id,
34
+ trigger_table.c.name,
35
+ trigger_table.c.user_id,
36
+ trigger_table.c.workspace_id,
37
+ trigger_table.c.service_account_id,
38
+ trigger_table.c.auth_window,
39
+ trigger_table.c.action,
40
+ trigger_table.c.action_subtype,
41
+ trigger_table.c.action_flavor,
42
+ )
43
+ ).fetchall()
44
+
45
+ now = datetime.utcnow()
46
+
47
+ actions_to_insert = []
48
+ trigger_updates = {}
49
+ for trigger in triggers:
50
+ action_id = str(uuid4()).replace("-", "")
51
+
52
+ actions_to_insert.append(
53
+ {
54
+ "id": action_id,
55
+ "workspace_id": trigger.workspace_id,
56
+ "user_id": trigger.user_id,
57
+ "created": now,
58
+ "updated": now,
59
+ "name": f"{trigger.name}_action",
60
+ "description": f"Automatically migrated action for trigger {trigger.name}",
61
+ "service_account_id": trigger.service_account_id,
62
+ "auth_window": trigger.auth_window,
63
+ "configuration": trigger.action,
64
+ "flavor": trigger.action_flavor,
65
+ "plugin_subtype": trigger.action_subtype,
66
+ }
67
+ )
68
+
69
+ trigger_updates[trigger.id] = action_id
70
+
71
+ op.bulk_insert(action_table, actions_to_insert)
72
+
73
+ for trigger_id, action_id in trigger_updates.items():
74
+ query = (
75
+ sa.update(trigger_table)
76
+ .where(trigger_table.c.id.is_(trigger_id))
77
+ .values(action_id=action_id)
78
+ )
79
+ conn.execute(query)
80
+
81
+
82
+ def upgrade() -> None:
83
+ """Upgrade database schema and/or data, creating a new revision."""
84
+ # ### commands auto generated by Alembic - please adjust! ###
85
+ op.create_table(
86
+ "action",
87
+ sa.Column(
88
+ "workspace_id", sqlmodel.sql.sqltypes.GUID(), nullable=False
89
+ ),
90
+ sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
91
+ sa.Column(
92
+ "service_account_id", sqlmodel.sql.sqltypes.GUID(), nullable=False
93
+ ),
94
+ sa.Column("description", sa.TEXT(), nullable=True),
95
+ sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
96
+ sa.Column("created", sa.DateTime(), nullable=False),
97
+ sa.Column("updated", sa.DateTime(), nullable=False),
98
+ sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
99
+ sa.Column("auth_window", sa.Integer(), nullable=False),
100
+ sa.Column(
101
+ "flavor", sqlmodel.sql.sqltypes.AutoString(), nullable=False
102
+ ),
103
+ sa.Column(
104
+ "plugin_subtype",
105
+ sqlmodel.sql.sqltypes.AutoString(),
106
+ nullable=False,
107
+ ),
108
+ sa.Column("configuration", sa.LargeBinary(), nullable=False),
109
+ sa.ForeignKeyConstraint(
110
+ ["service_account_id"],
111
+ ["user.id"],
112
+ name="fk_action_service_account_id_user",
113
+ ondelete="CASCADE",
114
+ ),
115
+ sa.ForeignKeyConstraint(
116
+ ["user_id"],
117
+ ["user.id"],
118
+ name="fk_action_user_id_user",
119
+ ondelete="SET NULL",
120
+ ),
121
+ sa.ForeignKeyConstraint(
122
+ ["workspace_id"],
123
+ ["workspace.id"],
124
+ name="fk_action_workspace_id_workspace",
125
+ ondelete="CASCADE",
126
+ ),
127
+ sa.PrimaryKeyConstraint("id"),
128
+ )
129
+
130
+ # Add the action_id column as nullable until we migrate the actions into
131
+ # a separate table
132
+ with op.batch_alter_table("trigger", schema=None) as batch_op:
133
+ batch_op.add_column(
134
+ sa.Column("action_id", sqlmodel.sql.sqltypes.GUID(), nullable=True)
135
+ )
136
+
137
+ migrate_actions()
138
+
139
+ with op.batch_alter_table("trigger", schema=None) as batch_op:
140
+ # Make the action_id column non-nullable now that we've inserted the
141
+ # actions and the value is set for each row
142
+ batch_op.alter_column(
143
+ "action_id",
144
+ existing_type=sqlmodel.sql.sqltypes.GUID(),
145
+ existing_nullable=True,
146
+ nullable=False,
147
+ )
148
+
149
+ batch_op.add_column(
150
+ sa.Column("schedule", sa.LargeBinary(), nullable=True)
151
+ )
152
+ batch_op.alter_column(
153
+ "event_source_id", existing_type=sa.CHAR(length=32), nullable=True
154
+ )
155
+ batch_op.drop_constraint(
156
+ "fk_trigger_event_source_id_event_source", type_="foreignkey"
157
+ )
158
+ batch_op.drop_constraint(
159
+ "fk_trigger_service_account_id_user", type_="foreignkey"
160
+ )
161
+ batch_op.create_foreign_key(
162
+ "fk_trigger_event_source_id_event_source",
163
+ "event_source",
164
+ ["event_source_id"],
165
+ ["id"],
166
+ ondelete="SET NULL",
167
+ )
168
+ batch_op.create_foreign_key(
169
+ "fk_trigger_action_id_action",
170
+ "action",
171
+ ["action_id"],
172
+ ["id"],
173
+ ondelete="CASCADE",
174
+ )
175
+ batch_op.drop_column("action_subtype")
176
+ batch_op.drop_column("action_flavor")
177
+ batch_op.drop_column("action")
178
+ batch_op.drop_column("auth_window")
179
+ batch_op.drop_column("service_account_id")
180
+
181
+ # ### end Alembic commands ###
182
+
183
+
184
+ def downgrade() -> None:
185
+ """Downgrade database schema and/or data back to the previous revision."""
186
+ # ### commands auto generated by Alembic - please adjust! ###
187
+ with op.batch_alter_table("trigger", schema=None) as batch_op:
188
+ batch_op.add_column(
189
+ sa.Column("service_account_id", sa.CHAR(length=32), nullable=False)
190
+ )
191
+ batch_op.add_column(
192
+ sa.Column("auth_window", sa.INTEGER(), nullable=False)
193
+ )
194
+ batch_op.add_column(sa.Column("action", sa.BLOB(), nullable=False))
195
+ batch_op.add_column(
196
+ sa.Column("action_flavor", sa.VARCHAR(), nullable=False)
197
+ )
198
+ batch_op.add_column(
199
+ sa.Column("action_subtype", sa.VARCHAR(), nullable=False)
200
+ )
201
+ batch_op.drop_constraint(
202
+ "fk_trigger_action_id_action", type_="foreignkey"
203
+ )
204
+ batch_op.drop_constraint(
205
+ "fk_trigger_event_source_id_event_source", type_="foreignkey"
206
+ )
207
+ batch_op.create_foreign_key(
208
+ "fk_trigger_service_account_id_user",
209
+ "user",
210
+ ["service_account_id"],
211
+ ["id"],
212
+ ondelete="CASCADE",
213
+ )
214
+ batch_op.create_foreign_key(
215
+ "fk_trigger_event_source_id_event_source",
216
+ "event_source",
217
+ ["event_source_id"],
218
+ ["id"],
219
+ ondelete="CASCADE",
220
+ )
221
+ batch_op.alter_column(
222
+ "event_source_id", existing_type=sa.CHAR(length=32), nullable=False
223
+ )
224
+ batch_op.drop_column("schedule")
225
+ batch_op.drop_column("action_id")
226
+
227
+ op.drop_table("action")
228
+ # ### end Alembic commands ###
@@ -47,6 +47,7 @@ from zenml.config.global_config import GlobalConfiguration
47
47
  from zenml.config.pipeline_run_configuration import PipelineRunConfiguration
48
48
  from zenml.config.store_config import StoreConfiguration
49
49
  from zenml.constants import (
50
+ ACTIONS,
50
51
  API,
51
52
  API_KEY_ROTATE,
52
53
  API_KEYS,
@@ -108,6 +109,10 @@ from zenml.exceptions import AuthorizationException, MethodNotAllowedError
108
109
  from zenml.io import fileio
109
110
  from zenml.logger import get_logger
110
111
  from zenml.models import (
112
+ ActionFilter,
113
+ ActionRequest,
114
+ ActionResponse,
115
+ ActionUpdate,
111
116
  APIKeyFilter,
112
117
  APIKeyRequest,
113
118
  APIKeyResponse,
@@ -480,6 +485,100 @@ class RestZenStore(BaseZenStore):
480
485
  response_body = self.put(SERVER_SETTINGS, body=settings_update)
481
486
  return ServerSettingsResponse.model_validate(response_body)
482
487
 
488
+ # -------------------- Actions --------------------
489
+
490
+ def create_action(self, action: ActionRequest) -> ActionResponse:
491
+ """Create an action.
492
+
493
+ Args:
494
+ action: The action to create.
495
+
496
+ Returns:
497
+ The created action.
498
+ """
499
+ return self._create_resource(
500
+ resource=action,
501
+ route=ACTIONS,
502
+ response_model=ActionResponse,
503
+ )
504
+
505
+ def get_action(
506
+ self,
507
+ action_id: UUID,
508
+ hydrate: bool = True,
509
+ ) -> ActionResponse:
510
+ """Get an action by ID.
511
+
512
+ Args:
513
+ action_id: The ID of the action to get.
514
+ hydrate: Flag deciding whether to hydrate the output model(s)
515
+ by including metadata fields in the response.
516
+
517
+ Returns:
518
+ The action.
519
+ """
520
+ return self._get_resource(
521
+ resource_id=action_id,
522
+ route=ACTIONS,
523
+ response_model=ActionResponse,
524
+ params={"hydrate": hydrate},
525
+ )
526
+
527
+ def list_actions(
528
+ self,
529
+ action_filter_model: ActionFilter,
530
+ hydrate: bool = False,
531
+ ) -> Page[ActionResponse]:
532
+ """List all actions matching the given filter criteria.
533
+
534
+ Args:
535
+ action_filter_model: All filter parameters including pagination
536
+ params.
537
+ hydrate: Flag deciding whether to hydrate the output model(s)
538
+ by including metadata fields in the response.
539
+
540
+ Returns:
541
+ A list of all actions matching the filter criteria.
542
+ """
543
+ return self._list_paginated_resources(
544
+ route=ACTIONS,
545
+ response_model=ActionResponse,
546
+ filter_model=action_filter_model,
547
+ params={"hydrate": hydrate},
548
+ )
549
+
550
+ def update_action(
551
+ self,
552
+ action_id: UUID,
553
+ action_update: ActionUpdate,
554
+ ) -> ActionResponse:
555
+ """Update an existing action.
556
+
557
+ Args:
558
+ action_id: The ID of the action to update.
559
+ action_update: The update to be applied to the action.
560
+
561
+ Returns:
562
+ The updated action.
563
+ """
564
+ return self._update_resource(
565
+ resource_id=action_id,
566
+ resource_update=action_update,
567
+ route=ACTIONS,
568
+ response_model=ActionResponse,
569
+ )
570
+
571
+ def delete_action(self, action_id: UUID) -> None:
572
+ """Delete an action.
573
+
574
+ Args:
575
+ action_id: The ID of the action to delete.
576
+ """
577
+ self._delete_resource(
578
+ resource_id=action_id,
579
+ route=ACTIONS,
580
+ )
581
+
483
582
  # ----------------------------- API Keys -----------------------------
484
583
 
485
584
  def create_api_key(
@@ -1434,14 +1533,14 @@ class RestZenStore(BaseZenStore):
1434
1533
  run_configuration = run_configuration or PipelineRunConfiguration()
1435
1534
  try:
1436
1535
  response_body = self.post(
1437
- f"{PIPELINE_BUILDS}/{build_id}/run", body=run_configuration
1536
+ f"{PIPELINE_BUILDS}/{build_id}/runs", body=run_configuration
1438
1537
  )
1439
1538
  except MethodNotAllowedError as e:
1440
1539
  raise RuntimeError(
1441
1540
  "Running a build is not supported for this server."
1442
1541
  ) from e
1443
1542
 
1444
- return PipelineRunResponse.parse_obj(response_body)
1543
+ return PipelineRunResponse.model_validate(response_body)
1445
1544
 
1446
1545
  # -------------------------- Pipeline Deployments --------------------------
1447
1546
 
@@ -1538,7 +1637,7 @@ class RestZenStore(BaseZenStore):
1538
1637
 
1539
1638
  try:
1540
1639
  response_body = self.post(
1541
- f"{PIPELINE_DEPLOYMENTS}/{deployment_id}/run",
1640
+ f"{PIPELINE_DEPLOYMENTS}/{deployment_id}/runs",
1542
1641
  body=run_configuration,
1543
1642
  )
1544
1643
  except MethodNotAllowedError as e:
@@ -1546,7 +1645,7 @@ class RestZenStore(BaseZenStore):
1546
1645
  "Running a deployment is not supported for this server."
1547
1646
  ) from e
1548
1647
 
1549
- return PipelineRunResponse.parse_obj(response_body)
1648
+ return PipelineRunResponse.model_validate(response_body)
1550
1649
 
1551
1650
  # -------------------- Event Sources --------------------
1552
1651
 
@@ -13,6 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """SQL Model Implementations."""
15
15
 
16
+ from zenml.zen_stores.schemas.action_schemas import ActionSchema
16
17
  from zenml.zen_stores.schemas.api_key_schemas import APIKeySchema
17
18
  from zenml.zen_stores.schemas.artifact_schemas import (
18
19
  ArtifactSchema,
@@ -71,6 +72,7 @@ from zenml.zen_stores.schemas.model_schemas import (
71
72
  from zenml.zen_stores.schemas.server_settings_schemas import ServerSettingsSchema
72
73
 
73
74
  __all__ = [
75
+ "ActionSchema",
74
76
  "APIKeySchema",
75
77
  "ArtifactSchema",
76
78
  "ArtifactVersionSchema",