Skip to content

Commit

Permalink
Restore repository update in the tool shed 2.0.
Browse files Browse the repository at this point in the history
I guess somehow this critical API endpoint had no tests.
  • Loading branch information
jmchilton committed Aug 6, 2024
1 parent e0b6532 commit 6a84be2
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 10 deletions.
12 changes: 8 additions & 4 deletions lib/tool_shed/util/repository_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,6 @@ def change_repository_name_in_hgrc_file(hgrc_file: str, new_name: str) -> None:

def update_repository(trans: "ProvidesUserContext", id: str, **kwds) -> Tuple[Optional["Repository"], Optional[str]]:
"""Update an existing ToolShed repository"""
app = trans.app
message = None
flush_needed = False
sa_session = app.model.session
repository = sa_session.get(app.model.Repository, app.security.decode_id(id))
if repository is None:
return None, "Unknown repository ID"
Expand All @@ -443,6 +439,14 @@ def update_repository(trans: "ProvidesUserContext", id: str, **kwds) -> Tuple[Op
message = "You are not the owner of this repository, so you cannot administer it."
return None, message


def update_validated_repository(trans: "ProvidesUserContext", repository: "Repository", **kwds) -> Tuple[Optional["Repository"], Optional[str]]:
"""Update an existing ToolShed repository metadata once permissions have been checked."""
app = trans.app
sa_session = app.model.session
message = None
flush_needed = False

# Allowlist properties that can be changed via this method
for key in ("type", "description", "long_description", "remote_repository_url", "homepage_url"):
# If that key is available, not None and different than what's in the model
Expand Down
36 changes: 30 additions & 6 deletions lib/tool_shed/webapp/api2/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
RepositoryUpdateRequest,
ResetMetadataOnRepositoryRequest,
ResetMetadataOnRepositoryResponse,
UpdateRepositoryRequest,
ValidRepostiroyUpdateMessage,
)
from . import (
Expand Down Expand Up @@ -293,6 +294,27 @@ def show(
repository = get_repository_in_tool_shed(self.app, encoded_repository_id)
return to_detailed_model(self.app, repository)

@router.put(
"/api/repositories/{encoded_repository_id}",
operation_id="repositories__update_repository",
)
def update_repository(
self,
encoded_repository_id: str = RepositoryIdPathParam,
request: UpdateRepositoryRequest = Body(...),
) -> DetailedRepository:
repository = get_repository_in_tool_shed(self.app, encoded_repository_id)
_ensure_can_manage(trans, repository)

# may want to set some of these to null, so we're using the exclude_unset feature
# to just serialize the ones we want to use to a dictionary.
update_dictionary = request.model_dump(exclude_unset=True)
repo_result, message = update_validated_repository(trans, repository, **update_dictionary)
if repo_result is None:
raise ActionInputError(message)

return to_detailed_model(self.app, repository)

@router.get(
"/api/repositories/{encoded_repository_id}/permissions",
operation_id="repositories__permissions",
Expand Down Expand Up @@ -323,8 +345,7 @@ def show_allow_push(
encoded_repository_id: str = RepositoryIdPathParam,
) -> List[str]:
repository = get_repository_in_tool_shed(self.app, encoded_repository_id)
if not can_manage_repo(trans, repository):
raise InsufficientPermissionsException("You do not have permission to update this repository.")
_ensure_can_manage(trans, repository)
return trans.app.security_agent.usernames_that_can_push(repository)

@router.post(
Expand Down Expand Up @@ -390,8 +411,7 @@ def set_deprecated(
encoded_repository_id: str = RepositoryIdPathParam,
):
repository = get_repository_in_tool_shed(self.app, encoded_repository_id)
if not can_manage_repo(trans, repository):
raise InsufficientPermissionsException("You do not have permission to update this repository.")
_ensure_can_manage(trans, repository)
repository.deprecated = True
trans.sa_session.add(repository)
with transaction(trans.sa_session):
Expand All @@ -409,8 +429,7 @@ def unset_deprecated(
encoded_repository_id: str = RepositoryIdPathParam,
):
repository = get_repository_in_tool_shed(self.app, encoded_repository_id)
if not can_manage_repo(trans, repository):
raise InsufficientPermissionsException("You do not have permission to update this repository.")
_ensure_can_manage(trans, repository)
repository.deprecated = False
trans.sa_session.add(repository)
with transaction(trans.sa_session):
Expand Down Expand Up @@ -505,3 +524,8 @@ def get_readmes(
) -> dict:
repository = get_repository_in_tool_shed(self.app, encoded_repository_id)
return readmes(self.app, repository, changeset_revision)


def _ensure_can_manage(trans: SessionRequestContext, repository: Repository) -> None:
if not can_manage_repo(trans, repository):
raise InsufficientPermissionsException("You do not have permission to update this repository.")
19 changes: 19 additions & 0 deletions lib/tool_shed_client/schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,25 @@ class CreateRepositoryRequest(BaseModel):
model_config = ConfigDict(populate_by_name=True)


class UpdateRepositoryRequest(BaseModel):
name: Optional[str] = None
synopsis: Optional[str] = None
type_: Optional[RepositoryType] = Field(
None,
alias="type",
title="Type",
)
description: Optional[str] = None
remote_repository_url: Optional[str] = None
homepage_url: Optional[str] = None
category_ids: Optional[Union[List[str], str]] = Field(
...,
alias="category_ids[]",
title="Category IDs",
) = None
model_config = ConfigDict(populate_by_name=True)


class RepositoryUpdateRequest(BaseModel):
commit_message: Optional[str] = None

Expand Down

0 comments on commit 6a84be2

Please sign in to comment.