zenml-nightly 0.75.0.dev20250312__py3-none-any.whl → 0.75.0.dev20250313__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 (160) hide show
  1. zenml/VERSION +1 -1
  2. zenml/__init__.py +2 -0
  3. zenml/analytics/context.py +7 -0
  4. zenml/artifacts/utils.py +0 -2
  5. zenml/cli/login.py +6 -0
  6. zenml/cli/model.py +7 -15
  7. zenml/cli/secret.py +47 -44
  8. zenml/cli/service_connectors.py +0 -1
  9. zenml/cli/stack.py +0 -1
  10. zenml/cli/tag.py +3 -5
  11. zenml/cli/utils.py +25 -23
  12. zenml/cli/workspace.py +79 -5
  13. zenml/client.py +615 -348
  14. zenml/config/global_config.py +16 -3
  15. zenml/config/pipeline_configurations.py +3 -2
  16. zenml/config/pipeline_run_configuration.py +2 -1
  17. zenml/config/secret_reference_mixin.py +1 -1
  18. zenml/constants.py +1 -3
  19. zenml/enums.py +0 -7
  20. zenml/event_hub/event_hub.py +3 -1
  21. zenml/exceptions.py +0 -24
  22. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +5 -3
  23. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +1 -4
  24. zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +1 -4
  25. zenml/integrations/mlflow/steps/mlflow_registry.py +1 -1
  26. zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -1
  27. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +3 -3
  28. zenml/model/model.py +8 -8
  29. zenml/models/__init__.py +18 -1
  30. zenml/models/v2/base/base.py +0 -5
  31. zenml/models/v2/base/filter.py +1 -1
  32. zenml/models/v2/base/scoped.py +104 -121
  33. zenml/models/v2/core/api_key.py +1 -1
  34. zenml/models/v2/core/artifact.py +31 -18
  35. zenml/models/v2/core/artifact_version.py +42 -25
  36. zenml/models/v2/core/component.py +22 -33
  37. zenml/models/v2/core/device.py +3 -2
  38. zenml/models/v2/core/event_source.py +2 -2
  39. zenml/models/v2/core/flavor.py +19 -47
  40. zenml/models/v2/core/logs.py +1 -2
  41. zenml/models/v2/core/model.py +7 -4
  42. zenml/models/v2/core/model_version.py +36 -27
  43. zenml/models/v2/core/pipeline.py +1 -1
  44. zenml/models/v2/core/pipeline_run.py +5 -13
  45. zenml/models/v2/core/run_template.py +1 -2
  46. zenml/models/v2/core/schedule.py +0 -9
  47. zenml/models/v2/core/secret.py +93 -127
  48. zenml/models/v2/core/server_settings.py +2 -2
  49. zenml/models/v2/core/service.py +43 -12
  50. zenml/models/v2/core/service_connector.py +14 -16
  51. zenml/models/v2/core/stack.py +24 -26
  52. zenml/models/v2/core/step_run.py +3 -15
  53. zenml/models/v2/core/tag.py +41 -15
  54. zenml/models/v2/core/user.py +19 -2
  55. zenml/models/v2/misc/statistics.py +45 -0
  56. zenml/models/v2/misc/tag.py +27 -0
  57. zenml/orchestrators/cache_utils.py +1 -1
  58. zenml/orchestrators/input_utils.py +1 -0
  59. zenml/orchestrators/step_launcher.py +0 -1
  60. zenml/orchestrators/step_run_utils.py +0 -2
  61. zenml/orchestrators/step_runner.py +10 -1
  62. zenml/pipelines/build_utils.py +0 -2
  63. zenml/pipelines/pipeline_decorator.py +3 -2
  64. zenml/pipelines/pipeline_definition.py +4 -5
  65. zenml/pipelines/run_utils.py +3 -3
  66. zenml/service_connectors/service_connector.py +0 -7
  67. zenml/service_connectors/service_connector_utils.py +0 -1
  68. zenml/stack/authentication_mixin.py +1 -1
  69. zenml/stack/flavor.py +3 -14
  70. zenml/stack/stack_component.py +1 -5
  71. zenml/steps/step_context.py +19 -0
  72. zenml/utils/string_utils.py +1 -1
  73. zenml/utils/tag_utils.py +642 -0
  74. zenml/zen_server/cloud_utils.py +21 -0
  75. zenml/zen_server/exceptions.py +0 -6
  76. zenml/zen_server/rbac/endpoint_utils.py +134 -46
  77. zenml/zen_server/rbac/models.py +65 -3
  78. zenml/zen_server/rbac/rbac_interface.py +9 -0
  79. zenml/zen_server/rbac/rbac_sql_zen_store.py +15 -7
  80. zenml/zen_server/rbac/utils.py +156 -29
  81. zenml/zen_server/rbac/zenml_cloud_rbac.py +43 -11
  82. zenml/zen_server/routers/actions_endpoints.py +3 -5
  83. zenml/zen_server/routers/artifact_endpoint.py +0 -5
  84. zenml/zen_server/routers/artifact_version_endpoints.py +15 -9
  85. zenml/zen_server/routers/auth_endpoints.py +22 -7
  86. zenml/zen_server/routers/code_repositories_endpoints.py +56 -3
  87. zenml/zen_server/routers/devices_endpoints.py +0 -4
  88. zenml/zen_server/routers/event_source_endpoints.py +0 -5
  89. zenml/zen_server/routers/flavors_endpoints.py +0 -5
  90. zenml/zen_server/routers/logs_endpoints.py +0 -1
  91. zenml/zen_server/routers/model_versions_endpoints.py +102 -23
  92. zenml/zen_server/routers/models_endpoints.py +51 -68
  93. zenml/zen_server/routers/pipeline_builds_endpoints.py +58 -4
  94. zenml/zen_server/routers/pipeline_deployments_endpoints.py +58 -4
  95. zenml/zen_server/routers/pipelines_endpoints.py +73 -4
  96. zenml/zen_server/routers/plugin_endpoints.py +0 -1
  97. zenml/zen_server/routers/run_metadata_endpoints.py +99 -0
  98. zenml/zen_server/routers/run_templates_endpoints.py +66 -3
  99. zenml/zen_server/routers/runs_endpoints.py +60 -8
  100. zenml/zen_server/routers/schedule_endpoints.py +69 -6
  101. zenml/zen_server/routers/secrets_endpoints.py +40 -4
  102. zenml/zen_server/routers/server_endpoints.py +53 -1
  103. zenml/zen_server/routers/service_accounts_endpoints.py +14 -15
  104. zenml/zen_server/routers/service_connectors_endpoints.py +96 -14
  105. zenml/zen_server/routers/service_endpoints.py +20 -7
  106. zenml/zen_server/routers/stack_components_endpoints.py +68 -7
  107. zenml/zen_server/routers/stacks_endpoints.py +98 -7
  108. zenml/zen_server/routers/steps_endpoints.py +17 -11
  109. zenml/zen_server/routers/tag_resource_endpoints.py +115 -0
  110. zenml/zen_server/routers/tags_endpoints.py +6 -17
  111. zenml/zen_server/routers/triggers_endpoints.py +5 -8
  112. zenml/zen_server/routers/users_endpoints.py +47 -12
  113. zenml/zen_server/routers/workspaces_endpoints.py +56 -1285
  114. zenml/zen_server/template_execution/utils.py +5 -4
  115. zenml/zen_server/utils.py +21 -0
  116. zenml/zen_server/zen_server_api.py +4 -0
  117. zenml/zen_stores/base_zen_store.py +29 -44
  118. zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +20 -10
  119. zenml/zen_stores/migrations/versions/1f9d1cd00b90_add_unique_name_constraints.py +231 -0
  120. zenml/zen_stores/migrations/versions/288f4fb6e112_make_tags_user_scoped.py +74 -0
  121. zenml/zen_stores/migrations/versions/2e695a26fe7a_add_user_default_workspace.py +45 -0
  122. zenml/zen_stores/migrations/versions/3b1776345020_remove_workspace_from_globals.py +81 -0
  123. zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +136 -0
  124. zenml/zen_stores/migrations/versions/9e7bf0970266_adding_exclusive_attribute_to_tags.py +47 -0
  125. zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +8 -4
  126. zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +12 -6
  127. zenml/zen_stores/migrations/versions/f1d723fd723b_add_secret_private_attr.py +61 -0
  128. zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py +35 -0
  129. zenml/zen_stores/rest_zen_store.py +172 -171
  130. zenml/zen_stores/schemas/action_schemas.py +8 -1
  131. zenml/zen_stores/schemas/api_key_schemas.py +8 -1
  132. zenml/zen_stores/schemas/artifact_schemas.py +28 -1
  133. zenml/zen_stores/schemas/code_repository_schemas.py +8 -1
  134. zenml/zen_stores/schemas/component_schemas.py +9 -14
  135. zenml/zen_stores/schemas/event_source_schemas.py +8 -1
  136. zenml/zen_stores/schemas/flavor_schemas.py +14 -20
  137. zenml/zen_stores/schemas/model_schemas.py +3 -0
  138. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +3 -1
  139. zenml/zen_stores/schemas/pipeline_run_schemas.py +0 -3
  140. zenml/zen_stores/schemas/run_template_schemas.py +8 -4
  141. zenml/zen_stores/schemas/schedule_schema.py +9 -14
  142. zenml/zen_stores/schemas/secret_schemas.py +15 -25
  143. zenml/zen_stores/schemas/service_connector_schemas.py +8 -17
  144. zenml/zen_stores/schemas/service_schemas.py +0 -1
  145. zenml/zen_stores/schemas/stack_schemas.py +12 -15
  146. zenml/zen_stores/schemas/step_run_schemas.py +7 -8
  147. zenml/zen_stores/schemas/tag_schemas.py +30 -2
  148. zenml/zen_stores/schemas/trigger_schemas.py +8 -1
  149. zenml/zen_stores/schemas/user_schemas.py +24 -2
  150. zenml/zen_stores/schemas/utils.py +16 -0
  151. zenml/zen_stores/schemas/workspace_schemas.py +7 -25
  152. zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +0 -3
  153. zenml/zen_stores/sql_zen_store.py +2905 -2280
  154. zenml/zen_stores/template_utils.py +1 -1
  155. zenml/zen_stores/zen_store_interface.py +82 -58
  156. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/METADATA +1 -1
  157. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/RECORD +160 -147
  158. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/LICENSE +0 -0
  159. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/WHEEL +0 -0
  160. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/entry_points.txt +0 -0
@@ -66,7 +66,10 @@ from zenml.zen_server.auth import (
66
66
  from zenml.zen_server.exceptions import error_response
67
67
  from zenml.zen_server.rate_limit import rate_limit_requests
68
68
  from zenml.zen_server.rbac.models import Action, ResourceType
69
- from zenml.zen_server.rbac.utils import verify_permission
69
+ from zenml.zen_server.rbac.utils import (
70
+ verify_permission,
71
+ verify_permission_for_model,
72
+ )
70
73
  from zenml.zen_server.utils import (
71
74
  get_ip_location,
72
75
  handle_exceptions,
@@ -544,10 +547,6 @@ def api_token(
544
547
  response=None,
545
548
  ).access_token
546
549
 
547
- verify_permission(
548
- resource_type=ResourceType.PIPELINE_RUN, action=Action.CREATE
549
- )
550
-
551
550
  schedule_id = schedule_id or token.schedule_id
552
551
  pipeline_run_id = pipeline_run_id or token.pipeline_run_id
553
552
  step_run_id = step_run_id or token.step_run_id
@@ -583,15 +582,18 @@ def api_token(
583
582
  f"step run {token.step_run_id}."
584
583
  )
585
584
 
585
+ workspace_id: Optional[UUID] = None
586
+
586
587
  if schedule_id:
587
588
  # The schedule must exist
588
589
  try:
589
- schedule = zen_store().get_schedule(schedule_id, hydrate=False)
590
+ schedule = zen_store().get_schedule(schedule_id, hydrate=True)
590
591
  except KeyError:
591
592
  raise ValueError(
592
593
  f"Schedule {schedule_id} does not exist and API tokens cannot "
593
594
  "be generated for non-existent schedules for security reasons."
594
595
  )
596
+ workspace_id = schedule.workspace.id
595
597
 
596
598
  if not schedule.active:
597
599
  raise ValueError(
@@ -602,7 +604,7 @@ def api_token(
602
604
  if pipeline_run_id:
603
605
  # The pipeline run must exist and the run must not be concluded
604
606
  try:
605
- pipeline_run = zen_store().get_run(pipeline_run_id, hydrate=False)
607
+ pipeline_run = zen_store().get_run(pipeline_run_id, hydrate=True)
606
608
  except KeyError:
607
609
  raise ValueError(
608
610
  f"Pipeline run {pipeline_run_id} does not exist and API tokens "
@@ -610,6 +612,10 @@ def api_token(
610
612
  "security reasons."
611
613
  )
612
614
 
615
+ verify_permission_for_model(model=pipeline_run, action=Action.READ)
616
+
617
+ workspace_id = pipeline_run.workspace.id
618
+
613
619
  if pipeline_run.status.is_finished:
614
620
  raise ValueError(
615
621
  f"The execution of pipeline run {pipeline_run_id} has already "
@@ -627,6 +633,8 @@ def api_token(
627
633
  "be generated for non-existent step runs for security reasons."
628
634
  )
629
635
 
636
+ workspace_id = step_run.workspace.id
637
+
630
638
  if step_run.status.is_finished:
631
639
  raise ValueError(
632
640
  f"The execution of step run {step_run_id} has already "
@@ -634,6 +642,13 @@ def api_token(
634
642
  "for security reasons."
635
643
  )
636
644
 
645
+ assert workspace_id is not None
646
+ verify_permission(
647
+ resource_type=ResourceType.PIPELINE_RUN,
648
+ action=Action.CREATE,
649
+ workspace_id=workspace_id,
650
+ )
651
+
637
652
  return generate_access_token(
638
653
  user_id=token.user_id,
639
654
  # Keep the original API key and device token scopes
@@ -13,6 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Endpoint definitions for code repositories."""
15
15
 
16
+ from typing import Optional, Union
16
17
  from uuid import UUID
17
18
 
18
19
  from fastapi import APIRouter, Depends, Security
@@ -20,6 +21,7 @@ from fastapi import APIRouter, Depends, Security
20
21
  from zenml.constants import API, CODE_REPOSITORIES, VERSION_1
21
22
  from zenml.models import (
22
23
  CodeRepositoryFilter,
24
+ CodeRepositoryRequest,
23
25
  CodeRepositoryResponse,
24
26
  CodeRepositoryUpdate,
25
27
  Page,
@@ -27,12 +29,16 @@ from zenml.models import (
27
29
  from zenml.zen_server.auth import AuthContext, authorize
28
30
  from zenml.zen_server.exceptions import error_response
29
31
  from zenml.zen_server.rbac.endpoint_utils import (
32
+ verify_permissions_and_create_entity,
30
33
  verify_permissions_and_delete_entity,
31
34
  verify_permissions_and_get_entity,
32
35
  verify_permissions_and_list_entities,
33
36
  verify_permissions_and_update_entity,
34
37
  )
35
38
  from zenml.zen_server.rbac.models import ResourceType
39
+ from zenml.zen_server.routers.workspaces_endpoints import (
40
+ router as workspace_router,
41
+ )
36
42
  from zenml.zen_server.utils import (
37
43
  handle_exceptions,
38
44
  make_dependable,
@@ -46,16 +52,61 @@ router = APIRouter(
46
52
  )
47
53
 
48
54
 
55
+ @router.post(
56
+ "",
57
+ responses={401: error_response, 409: error_response, 422: error_response},
58
+ )
59
+ # TODO: the workspace scoped endpoint is only kept for dashboard compatibility
60
+ # and can be removed after the migration
61
+ @workspace_router.post(
62
+ "/{workspace_name_or_id}" + CODE_REPOSITORIES,
63
+ responses={401: error_response, 409: error_response, 422: error_response},
64
+ deprecated=True,
65
+ tags=["code_repositories"],
66
+ )
67
+ @handle_exceptions
68
+ def create_code_repository(
69
+ code_repository: CodeRepositoryRequest,
70
+ workspace_name_or_id: Optional[Union[str, UUID]] = None,
71
+ _: AuthContext = Security(authorize),
72
+ ) -> CodeRepositoryResponse:
73
+ """Creates a code repository.
74
+
75
+ Args:
76
+ code_repository: Code repository to create.
77
+ workspace_name_or_id: Optional name or ID of the workspace.
78
+
79
+ Returns:
80
+ The created code repository.
81
+ """
82
+ if workspace_name_or_id:
83
+ workspace = zen_store().get_workspace(workspace_name_or_id)
84
+ code_repository.workspace = workspace.id
85
+
86
+ return verify_permissions_and_create_entity(
87
+ request_model=code_repository,
88
+ create_method=zen_store().create_code_repository,
89
+ )
90
+
91
+
49
92
  @router.get(
50
93
  "",
51
- response_model=Page[CodeRepositoryResponse],
52
94
  responses={401: error_response, 404: error_response, 422: error_response},
53
95
  )
96
+ # TODO: the workspace scoped endpoint is only kept for dashboard compatibility
97
+ # and can be removed after the migration
98
+ @workspace_router.get(
99
+ "/{workspace_name_or_id}" + CODE_REPOSITORIES,
100
+ responses={401: error_response, 404: error_response, 422: error_response},
101
+ deprecated=True,
102
+ tags=["code_repositories"],
103
+ )
54
104
  @handle_exceptions
55
105
  def list_code_repositories(
56
106
  filter_model: CodeRepositoryFilter = Depends(
57
107
  make_dependable(CodeRepositoryFilter)
58
108
  ),
109
+ workspace_name_or_id: Optional[Union[str, UUID]] = None,
59
110
  hydrate: bool = False,
60
111
  _: AuthContext = Security(authorize),
61
112
  ) -> Page[CodeRepositoryResponse]:
@@ -64,12 +115,16 @@ def list_code_repositories(
64
115
  Args:
65
116
  filter_model: Filter model used for pagination, sorting,
66
117
  filtering.
118
+ workspace_name_or_id: Optional name or ID of the workspace.
67
119
  hydrate: Flag deciding whether to hydrate the output model(s)
68
120
  by including metadata fields in the response.
69
121
 
70
122
  Returns:
71
123
  Page of code repository objects.
72
124
  """
125
+ if workspace_name_or_id:
126
+ filter_model.workspace = workspace_name_or_id
127
+
73
128
  return verify_permissions_and_list_entities(
74
129
  filter_model=filter_model,
75
130
  resource_type=ResourceType.CODE_REPOSITORY,
@@ -80,7 +135,6 @@ def list_code_repositories(
80
135
 
81
136
  @router.get(
82
137
  "/{code_repository_id}",
83
- response_model=CodeRepositoryResponse,
84
138
  responses={401: error_response, 404: error_response, 422: error_response},
85
139
  )
86
140
  @handle_exceptions
@@ -108,7 +162,6 @@ def get_code_repository(
108
162
 
109
163
  @router.put(
110
164
  "/{code_repository_id}",
111
- response_model=CodeRepositoryResponse,
112
165
  responses={401: error_response, 404: error_response, 422: error_response},
113
166
  )
114
167
  @handle_exceptions
@@ -54,7 +54,6 @@ router = APIRouter(
54
54
 
55
55
  @router.get(
56
56
  "",
57
- response_model=Page[OAuthDeviceResponse],
58
57
  responses={401: error_response, 404: error_response, 422: error_response},
59
58
  )
60
59
  @handle_exceptions
@@ -85,7 +84,6 @@ def list_authorized_devices(
85
84
 
86
85
  @router.get(
87
86
  "/{device_id}",
88
- response_model=OAuthDeviceResponse,
89
87
  responses={401: error_response, 404: error_response, 422: error_response},
90
88
  )
91
89
  @handle_exceptions
@@ -136,7 +134,6 @@ def get_authorization_device(
136
134
 
137
135
  @router.put(
138
136
  "/{device_id}",
139
- response_model=OAuthDeviceResponse,
140
137
  responses={401: error_response, 404: error_response, 422: error_response},
141
138
  )
142
139
  @handle_exceptions
@@ -173,7 +170,6 @@ def update_authorized_device(
173
170
 
174
171
  @router.put(
175
172
  "/{device_id}" + DEVICE_VERIFY,
176
- response_model=OAuthDeviceResponse,
177
173
  responses={401: error_response, 404: error_response, 422: error_response},
178
174
  )
179
175
  @handle_exceptions
@@ -57,7 +57,6 @@ event_source_router = APIRouter(
57
57
 
58
58
  @event_source_router.get(
59
59
  "",
60
- response_model=Page[EventSourceResponse],
61
60
  responses={401: error_response, 404: error_response, 422: error_response},
62
61
  )
63
62
  @handle_exceptions
@@ -132,7 +131,6 @@ def list_event_sources(
132
131
 
133
132
  @event_source_router.get(
134
133
  "/{event_source_id}",
135
- response_model=EventSourceResponse,
136
134
  responses={401: error_response, 404: error_response, 422: error_response},
137
135
  )
138
136
  @handle_exceptions
@@ -185,7 +183,6 @@ def get_event_source(
185
183
 
186
184
  @event_source_router.post(
187
185
  "",
188
- response_model=EventSourceResponse,
189
186
  responses={401: error_response, 409: error_response, 422: error_response},
190
187
  )
191
188
  @handle_exceptions
@@ -222,14 +219,12 @@ def create_event_source(
222
219
 
223
220
  return verify_permissions_and_create_entity(
224
221
  request_model=event_source,
225
- resource_type=ResourceType.EVENT_SOURCE,
226
222
  create_method=event_source_handler.create_event_source,
227
223
  )
228
224
 
229
225
 
230
226
  @event_source_router.put(
231
227
  "/{event_source_id}",
232
- response_model=EventSourceResponse,
233
228
  responses={401: error_response, 404: error_response, 422: error_response},
234
229
  )
235
230
  @handle_exceptions
@@ -51,7 +51,6 @@ router = APIRouter(
51
51
 
52
52
  @router.get(
53
53
  "",
54
- response_model=Page[FlavorResponse],
55
54
  responses={401: error_response, 404: error_response, 422: error_response},
56
55
  )
57
56
  @handle_exceptions
@@ -81,7 +80,6 @@ def list_flavors(
81
80
 
82
81
  @router.get(
83
82
  "/{flavor_id}",
84
- response_model=FlavorResponse,
85
83
  responses={401: error_response, 404: error_response, 422: error_response},
86
84
  )
87
85
  @handle_exceptions
@@ -107,7 +105,6 @@ def get_flavor(
107
105
 
108
106
  @router.post(
109
107
  "",
110
- response_model=FlavorResponse,
111
108
  responses={401: error_response, 409: error_response, 422: error_response},
112
109
  )
113
110
  @handle_exceptions
@@ -125,14 +122,12 @@ def create_flavor(
125
122
  """
126
123
  return verify_permissions_and_create_entity(
127
124
  request_model=flavor,
128
- resource_type=ResourceType.FLAVOR,
129
125
  create_method=zen_store().create_flavor,
130
126
  )
131
127
 
132
128
 
133
129
  @router.put(
134
130
  "/{flavor_id}",
135
- response_model=FlavorResponse,
136
131
  responses={401: error_response, 409: error_response, 422: error_response},
137
132
  )
138
133
  @handle_exceptions
@@ -42,7 +42,6 @@ router = APIRouter(
42
42
 
43
43
  @router.get(
44
44
  "/{logs_id}",
45
- response_model=LogsResponse,
46
45
  responses={401: error_response, 404: error_response, 422: error_response},
47
46
  )
48
47
  @handle_exceptions
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Endpoint definitions for models."""
15
15
 
16
- from typing import Union
16
+ from typing import Optional, Union
17
17
  from uuid import UUID
18
18
 
19
19
  from fastapi import APIRouter, Depends, Security
@@ -24,6 +24,7 @@ from zenml.constants import (
24
24
  MODEL_VERSION_ARTIFACTS,
25
25
  MODEL_VERSION_PIPELINE_RUNS,
26
26
  MODEL_VERSIONS,
27
+ MODELS,
27
28
  RUNS,
28
29
  VERSION_1,
29
30
  )
@@ -35,22 +36,33 @@ from zenml.models import (
35
36
  ModelVersionPipelineRunFilter,
36
37
  ModelVersionPipelineRunRequest,
37
38
  ModelVersionPipelineRunResponse,
39
+ ModelVersionRequest,
38
40
  ModelVersionResponse,
39
41
  ModelVersionUpdate,
40
42
  )
41
43
  from zenml.models.v2.base.page import Page
42
44
  from zenml.zen_server.auth import AuthContext, authorize
43
45
  from zenml.zen_server.exceptions import error_response
44
- from zenml.zen_server.rbac.models import Action, ResourceType
46
+ from zenml.zen_server.rbac.endpoint_utils import (
47
+ verify_permissions_and_create_entity,
48
+ verify_permissions_and_delete_entity,
49
+ )
50
+ from zenml.zen_server.rbac.models import Action
45
51
  from zenml.zen_server.rbac.utils import (
46
52
  dehydrate_page,
47
53
  dehydrate_response_model,
48
- get_allowed_resource_ids,
49
54
  verify_permission_for_model,
50
55
  )
56
+ from zenml.zen_server.routers.models_endpoints import (
57
+ router as model_router,
58
+ )
59
+ from zenml.zen_server.routers.workspaces_endpoints import (
60
+ router as workspace_router,
61
+ )
51
62
  from zenml.zen_server.utils import (
52
63
  handle_exceptions,
53
64
  make_dependable,
65
+ set_filter_workspace_scope,
54
66
  zen_store,
55
67
  )
56
68
 
@@ -66,9 +78,54 @@ router = APIRouter(
66
78
  )
67
79
 
68
80
 
81
+ @router.post(
82
+ "",
83
+ responses={401: error_response, 409: error_response, 422: error_response},
84
+ )
85
+ # TODO: the workspace scoped endpoint is only kept for dashboard compatibility
86
+ # and can be removed after the migration
87
+ @workspace_router.post(
88
+ "/{workspace_name_or_id}" + MODELS + "/{model_id}" + MODEL_VERSIONS,
89
+ responses={401: error_response, 409: error_response, 422: error_response},
90
+ deprecated=True,
91
+ tags=["model_versions"],
92
+ )
93
+ @handle_exceptions
94
+ def create_model_version(
95
+ model_version: ModelVersionRequest,
96
+ model_id: Optional[UUID] = None,
97
+ workspace_name_or_id: Optional[Union[str, UUID]] = None,
98
+ _: AuthContext = Security(authorize),
99
+ ) -> ModelVersionResponse:
100
+ """Creates a model version.
101
+
102
+ Args:
103
+ model_version: Model version to create.
104
+ model_id: Optional ID of the model.
105
+ workspace_name_or_id: Optional name or ID of the workspace.
106
+
107
+ Returns:
108
+ The created model version.
109
+ """
110
+ if workspace_name_or_id:
111
+ workspace = zen_store().get_workspace(workspace_name_or_id)
112
+ model_version.workspace = workspace.id
113
+
114
+ if model_id:
115
+ model_version.model = model_id
116
+
117
+ return verify_permissions_and_create_entity(
118
+ request_model=model_version,
119
+ create_method=zen_store().create_model_version,
120
+ )
121
+
122
+
123
+ @model_router.get(
124
+ "/{model_name_or_id}" + MODEL_VERSIONS,
125
+ responses={401: error_response, 404: error_response, 422: error_response},
126
+ )
69
127
  @router.get(
70
128
  "",
71
- response_model=Page[ModelVersionResponse],
72
129
  responses={401: error_response, 404: error_response, 422: error_response},
73
130
  )
74
131
  @handle_exceptions
@@ -76,6 +133,7 @@ def list_model_versions(
76
133
  model_version_filter_model: ModelVersionFilter = Depends(
77
134
  make_dependable(ModelVersionFilter)
78
135
  ),
136
+ model_name_or_id: Optional[Union[str, UUID]] = None,
79
137
  hydrate: bool = False,
80
138
  auth_context: AuthContext = Security(authorize),
81
139
  ) -> Page[ModelVersionResponse]:
@@ -84,19 +142,38 @@ def list_model_versions(
84
142
  Args:
85
143
  model_version_filter_model: Filter model used for pagination, sorting,
86
144
  filtering.
145
+ model_name_or_id: Optional name or ID of the model.
87
146
  hydrate: Flag deciding whether to hydrate the output model(s)
88
147
  by including metadata fields in the response.
89
148
  auth_context: The authentication context.
90
149
 
91
150
  Returns:
92
151
  The model versions according to query filters.
152
+
153
+ Raises:
154
+ ValueError: If the model is missing from the filter.
93
155
  """
94
- allowed_model_ids = get_allowed_resource_ids(
95
- resource_type=ResourceType.MODEL
156
+ if model_name_or_id:
157
+ model_version_filter_model.model = model_name_or_id
158
+
159
+ if not model_version_filter_model.model:
160
+ raise ValueError("Model missing from the filter")
161
+
162
+ # A workspace scoped request must always be scoped to a specific
163
+ # workspace. This is required for the RBAC check to work.
164
+ set_filter_workspace_scope(model_version_filter_model)
165
+ assert isinstance(model_version_filter_model.workspace, UUID)
166
+
167
+ model = zen_store().get_model_by_name_or_id(
168
+ model_version_filter_model.model,
169
+ workspace=model_version_filter_model.workspace,
96
170
  )
171
+
172
+ # Check read permissions on the model
173
+ verify_permission_for_model(model, action=Action.READ)
174
+
97
175
  model_version_filter_model.configure_rbac(
98
- authenticated_user_id=auth_context.user.id,
99
- model_id=allowed_model_ids,
176
+ authenticated_user_id=auth_context.user.id
100
177
  )
101
178
 
102
179
  model_versions = zen_store().list_model_versions(
@@ -108,7 +185,6 @@ def list_model_versions(
108
185
 
109
186
  @router.get(
110
187
  "/{model_version_id}",
111
- response_model=ModelVersionResponse,
112
188
  responses={401: error_response, 404: error_response, 422: error_response},
113
189
  )
114
190
  @handle_exceptions
@@ -137,7 +213,6 @@ def get_model_version(
137
213
 
138
214
  @router.put(
139
215
  "/{model_version_id}",
140
- response_model=ModelVersionResponse,
141
216
  responses={401: error_response, 404: error_response, 422: error_response},
142
217
  )
143
218
  @handle_exceptions
@@ -184,9 +259,11 @@ def delete_model_version(
184
259
  Args:
185
260
  model_version_id: The name or ID of the model version to delete.
186
261
  """
187
- model_version = zen_store().get_model_version(model_version_id)
188
- verify_permission_for_model(model_version, action=Action.DELETE)
189
- zen_store().delete_model_version(model_version_id)
262
+ verify_permissions_and_delete_entity(
263
+ id=model_version_id,
264
+ get_method=zen_store().get_model_version,
265
+ delete_method=zen_store().delete_model_version,
266
+ )
190
267
 
191
268
 
192
269
  ##########################
@@ -220,17 +297,18 @@ def create_model_version_artifact_link(
220
297
  model_version = zen_store().get_model_version(
221
298
  model_version_artifact_link.model_version
222
299
  )
223
- verify_permission_for_model(model_version, action=Action.UPDATE)
224
300
 
225
- mv = zen_store().create_model_version_artifact_link(
226
- model_version_artifact_link
301
+ return verify_permissions_and_create_entity(
302
+ request_model=model_version_artifact_link,
303
+ create_method=zen_store().create_model_version_artifact_link,
304
+ # Check for UPDATE permissions on the model version instead of the
305
+ # model version artifact link
306
+ surrogate_models=[model_version],
227
307
  )
228
- return mv
229
308
 
230
309
 
231
310
  @model_version_artifacts_router.get(
232
311
  "",
233
- response_model=Page[ModelVersionArtifactResponse],
234
312
  responses={401: error_response, 404: error_response, 422: error_response},
235
313
  )
236
314
  @handle_exceptions
@@ -342,17 +420,18 @@ def create_model_version_pipeline_run_link(
342
420
  model_version = zen_store().get_model_version(
343
421
  model_version_pipeline_run_link.model_version, hydrate=False
344
422
  )
345
- verify_permission_for_model(model_version, action=Action.UPDATE)
346
423
 
347
- mv = zen_store().create_model_version_pipeline_run_link(
348
- model_version_pipeline_run_link
424
+ return verify_permissions_and_create_entity(
425
+ request_model=model_version_pipeline_run_link,
426
+ create_method=zen_store().create_model_version_pipeline_run_link,
427
+ # Check for UPDATE permissions on the model version instead of the
428
+ # model version pipeline run link
429
+ surrogate_models=[model_version],
349
430
  )
350
- return mv
351
431
 
352
432
 
353
433
  @model_version_pipeline_runs_router.get(
354
434
  "",
355
- response_model=Page[ModelVersionPipelineRunResponse],
356
435
  responses={401: error_response, 404: error_response, 422: error_response},
357
436
  )
358
437
  @handle_exceptions