Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow patching template version in resources #2674

Closed
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
2 changes: 1 addition & 1 deletion api_app/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.44"
__version__ = "0.4.45"
2 changes: 2 additions & 0 deletions api_app/api/routes/shared_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ async def patch_shared_service(shared_service_patch: ResourcePatch, response: Re
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
except ValidationError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
except ValueError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(v))


@shared_services_router.delete("/shared-services/{shared_service_id}", response_model=OperationInResponse, name=strings.API_DELETE_SHARED_SERVICE, dependencies=[Depends(get_current_admin_user)])
Expand Down
6 changes: 6 additions & 0 deletions api_app/api/routes/workspaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ async def patch_workspace(workspace_patch: ResourcePatch, response: Response, us
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
except ValidationError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
except ValueError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(v))


@workspaces_core_router.delete("/workspaces/{workspace_id}", response_model=OperationInResponse, name=strings.API_DELETE_WORKSPACE, dependencies=[Depends(get_current_admin_user)])
Expand Down Expand Up @@ -250,6 +252,8 @@ async def patch_workspace_service(workspace_service_patch: ResourcePatch, respon
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
except ValidationError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
except ValueError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(v))


@workspace_services_workspace_router.delete("/workspaces/{workspace_id}/workspace-services/{service_id}", response_model=OperationInResponse, name=strings.API_DELETE_WORKSPACE_SERVICE, dependencies=[Depends(get_current_workspace_owner_user)])
Expand Down Expand Up @@ -428,6 +432,8 @@ async def patch_user_resource(
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=strings.ETAG_CONFLICT)
except ValidationError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=v.message)
except ValueError as v:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(v))


# user resource actions
Expand Down
17 changes: 16 additions & 1 deletion api_app/db/repositories/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ def patch_resource(self, resource: Resource, resource_patch: ResourcePatch, reso
properties=resource_copy.properties,
resourceVersion=resource_copy.resourceVersion,
updatedWhen=resource_copy.updatedWhen,
user=resource_copy.user
user=resource_copy.user,
templateVersion=resource_copy.templateVersion
)
resource.history.append(history_item)

Expand All @@ -116,6 +117,10 @@ def patch_resource(self, resource: Resource, resource_patch: ResourcePatch, reso
if resource_patch.isEnabled is not None:
resource.isEnabled = resource_patch.isEnabled

if resource_patch.templateVersion is not None:
self.validate_template_version_patch(resource, resource_patch, resource_template_repo, resource_template)
resource.templateVersion = resource_patch.templateVersion

if resource_patch.properties is not None and len(resource_patch.properties) > 0:
self.validate_patch(resource_patch, resource_template_repo, resource_template)

Expand All @@ -125,6 +130,16 @@ def patch_resource(self, resource: Resource, resource_patch: ResourcePatch, reso
self.update_item_with_etag(resource, etag)
return resource, resource_template

def validate_template_version_patch(self, resource: Resource, resource_patch: ResourcePatch, resource_template_repo: ResourceTemplateRepository, resource_template: ResourceTemplate):
parent_resource_id = None
if resource.resourceType == ResourceType.UserResource:
parent_resource_id = resource.parentWorkspaceServiceId

try:
resource_template_repo.get_template_by_name_and_version(resource_template.name, resource_patch.templateVersion, resource_template.resourceType, parent_resource_id)
except EntityDoesNotExist:
raise ValueError(f"Template '{resource_template.name}' not found for resource type '{resource_template.resourceType}' with target template version '{resource_patch.templateVersion}'")

def validate_patch(self, resource_patch: ResourcePatch, resource_template_repo: ResourceTemplateRepository, resource_template: ResourceTemplate):
# get the enriched (combined) template
enriched_template = resource_template_repo.enrich_template(resource_template, is_update=True)
Expand Down
1 change: 1 addition & 0 deletions api_app/models/domain/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ResourceHistoryItem(AzureTREModel):
resourceVersion: int
updatedWhen: float
user: dict = {}
templateVersion: Optional[str]


class Resource(AzureTREModel):
Expand Down
2 changes: 2 additions & 0 deletions api_app/models/schemas/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
class ResourcePatch(BaseModel):
isEnabled: Optional[bool]
properties: Optional[dict]
templateVersion: Optional[str]

class Config:
schema_extra = {
"example": {
"isEnabled": False,
"templateVersion": "1.0.1",
"properties": {
"display_name": "the display name",
"description": "a description",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ async def test_patch_shared_service_patches_shared_service(self, _, update_item_

modified_shared_service = sample_shared_service()
modified_shared_service.isEnabled = False
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user())]
modified_shared_service.history = [ResourceHistoryItem(properties=copy.deepcopy(modified_shared_service.properties), isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_shared_service.templateVersion)]
modified_shared_service.resourceVersion = 1
modified_shared_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
modified_shared_service.user = create_admin_user()
Expand Down
12 changes: 6 additions & 6 deletions api_app/tests_ma/test_api/test_routes/test_workspaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ async def test_patch_workspaces_patches_workspace(self, _, __, update_item_mock,

modified_workspace = sample_workspace()
modified_workspace.isEnabled = False
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user())]
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
modified_workspace.resourceVersion = 1
modified_workspace.user = create_admin_user()
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
Expand All @@ -438,7 +438,7 @@ async def test_patch_workspace_returns_409_if_bad_etag(self, _, __, update_item_
etag = "some-etag-value"
modified_workspace = sample_workspace()
modified_workspace.isEnabled = False
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user())]
modified_workspace.history = [ResourceHistoryItem(properties={'client_id': '12345'}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_admin_user(), templateVersion=modified_workspace.templateVersion)]
modified_workspace.resourceVersion = 1
modified_workspace.user = create_admin_user()
modified_workspace.updatedWhen = FAKE_UPDATE_TIMESTAMP
Expand Down Expand Up @@ -676,7 +676,7 @@ async def test_patch_user_resources_patches_user_resource(self, _, update_item_m

modified_user_resource = sample_user_resource_object()
modified_user_resource.isEnabled = False
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user())]
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
modified_user_resource.resourceVersion = 1
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
modified_user_resource.user = create_workspace_owner_user()
Expand All @@ -700,7 +700,7 @@ async def test_patch_user_resource_validates_against_template(self, _, __, ___,

modified_resource = sample_user_resource_object()
modified_resource.isEnabled = False
modified_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user())]
modified_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_resource.templateVersion)]
modified_resource.resourceVersion = 1
modified_resource.properties["vm_size"] = "large"
modified_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
Expand Down Expand Up @@ -862,7 +862,7 @@ async def test_patch_workspace_service_patches_workspace_service(self, _, update

modified_workspace_service = sample_workspace_service()
modified_workspace_service.isEnabled = False
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user())]
modified_workspace_service.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_owner_user(), templateVersion=modified_workspace_service.templateVersion)]
modified_workspace_service.resourceVersion = 1
modified_workspace_service.user = create_workspace_researcher_user()
modified_workspace_service.updatedWhen = FAKE_UPDATE_TIMESTAMP
Expand Down Expand Up @@ -1032,7 +1032,7 @@ async def test_patch_user_resources_patches_user_resource(self, _, update_item_m

modified_user_resource = sample_user_resource_object()
modified_user_resource.isEnabled = False
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user())]
modified_user_resource.history = [ResourceHistoryItem(properties={}, isEnabled=True, resourceVersion=0, updatedWhen=FAKE_CREATE_TIMESTAMP, user=create_workspace_researcher_user(), templateVersion=modified_user_resource.templateVersion)]
modified_user_resource.resourceVersion = 1
modified_user_resource.updatedWhen = FAKE_UPDATE_TIMESTAMP
modified_user_resource.user = create_workspace_researcher_user()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ def test_patch_resource_preserves_property_history(_, __, resource_repo):
resourceVersion=0,
updatedWhen=FAKE_CREATE_TIMESTAMP,
properties={'display_name': 'initial display name', 'description': 'initial description', 'computed_prop': 'computed_val'},
user=user)]
user=user,
templateVersion=resource.templateVersion)]
expected_resource.properties['display_name'] = 'updated name'
expected_resource.resourceVersion = 1
expected_resource.user = user
Expand All @@ -341,7 +342,8 @@ def test_patch_resource_preserves_property_history(_, __, resource_repo):
resourceVersion=1,
updatedWhen=FAKE_UPDATE_TIMESTAMP,
properties={'display_name': 'updated name', 'description': 'initial description', 'computed_prop': 'computed_val'},
user=user
user=user,
templateVersion=resource.templateVersion
)
)

Expand Down