Skip to content

Fix 190 #192

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

Merged
merged 13 commits into from
Nov 2, 2023
Merged
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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ Commands:
pull Fetch changes from Mergin Maps repository
push Upload local changes into Mergin Maps repository
remove Remove project from server.
rename Rename project in Mergin Maps repository.
reset Reset local changes in project.
share Fetch permissions to project
share-add Add permissions to [users] to project
share-remove Remove [users] permissions from project
Expand Down
52 changes: 52 additions & 0 deletions mergin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,5 +602,57 @@ def resolve_unfinished_pull(ctx):
_print_unhandled_exception()


@cli.command()
@click.argument("project_path")
@click.argument("new_project_name")
@click.pass_context
def rename(ctx, project_path: str, new_project_name: str):
"""Rename project in Mergin Maps repository."""
mc = ctx.obj["client"]
if mc is None:
return

if "/" not in project_path:
click.secho(f"Specify `project_path` as full name (<namespace>/<name>) instead.", fg="red")
return

if "/" in new_project_name:
old_workspace, old_project_name = project_path.split("/")
new_workspace, new_project_name = new_project_name.split("/")

if old_workspace != new_workspace:
click.secho(
"`new_project_name` should not contain namespace, project can only be rename within their namespace.\nTo move project to another workspace use web dashboard.",
fg="red",
)
return

try:
mc.rename_project(project_path, new_project_name)
click.echo("Project renamed")
except ClientError as e:
click.secho("Error: " + str(e), fg="red")
except Exception as e:
_print_unhandled_exception()


@cli.command()
@click.pass_context
def reset(ctx):
"""Reset local changes in project."""
directory = os.getcwd()
mc: MerginClient = ctx.obj["client"]
if mc is None:
return
try:
mc.reset_local_changes(directory)
except InvalidProject as e:
click.secho("Invalid project directory ({})".format(str(e)), fg="red")
except ClientError as e:
click.secho("Error: " + str(e), fg="red")
except Exception as e:
_print_unhandled_exception()


if __name__ == "__main__":
cli()
30 changes: 30 additions & 0 deletions mergin/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,36 @@ def has_writing_permissions(self, project_path):
info = self.project_info(project_path)
return info["permissions"]["upload"]

def rename_project(self, project_path: str, new_project_name: str) -> None:
"""
Rename project on server.

:param project_path: Project's full name (<namespace>/<name>)
:type project_path: String
:param new_project_name: Project's new name (<name>)
:type new_project_name: String
"""
# TODO: this version check should be replaced by checking against the list
# of endpoints that server publishes in /config (once implemented)
if not is_version_acceptable(self.server_version(), "2023.5.4"):
raise NotImplementedError("This needs server at version 2023.5.4 or later")

if "/" in new_project_name:
raise ClientError(
"Project's new name should be without workspace specification (<name>). Project can only be renamed within its current workspace."
)

project_info = self.project_info(project_path)
project_id = project_info["id"]
path = "/v2/projects/" + project_id
url = urllib.parse.urljoin(self.url, urllib.parse.quote(path))
json_headers = {"Content-Type": "application/json"}
data = {"name": new_project_name}
request = urllib.request.Request(
url, data=json.dumps(data).encode("utf-8"), headers=json_headers, method="PATCH"
)
self._do_request(request)

def reset_local_changes(self, directory: str, files_to_reset: typing.List[str] = None) -> None:
"""
Reset local changes to either all files or only listed files.
Expand Down
50 changes: 50 additions & 0 deletions mergin/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,56 @@ def test_project_metadata(mc):
assert mp.version() == "v0"


def test_project_rename(mc: MerginClient):
"""Check project can be renamed"""

test_project = "test_project_rename"
test_project_renamed = "test_project_renamed"
project = API_USER + "/" + test_project
project_renamed = API_USER + "/" + test_project_renamed

project_dir = os.path.join(TMP_DIR, test_project) # primary project dir

cleanup(mc, project, [project_dir])
cleanup(mc, project_renamed, [])

shutil.copytree(TEST_DATA_DIR, project_dir)
mc.create_project_and_push(project, project_dir)

# renamed project does not exist
with pytest.raises(ClientError, match="The requested URL was not found on the server"):
info = mc.project_info(project_renamed)

# rename
mc.rename_project(project, test_project_renamed)

# validate project info
project_info = mc.project_info(project_renamed)
assert project_info["version"] == "v1"
assert project_info["name"] == test_project_renamed
assert project_info["namespace"] == API_USER
with pytest.raises(ClientError, match="The requested URL was not found on the server"):
mc.project_info(project)

# recreate project
cleanup(mc, project, [project_dir])
shutil.copytree(TEST_DATA_DIR, project_dir)
mc.create_project_and_push(project, project_dir)

# rename to existing name - created previously
mc.project_info(project_renamed)
with pytest.raises(ClientError, match="Name already exist within workspace"):
mc.rename_project(project, test_project_renamed)

# cannot rename project that does not exist
with pytest.raises(ClientError, match="The requested URL was not found on the server."):
mc.rename_project(API_USER + "/" + "non_existing_project", "new_project")

# cannot rename with full project name
with pytest.raises(ClientError, match="Project's new name should be without workspace specification"):
mc.rename_project(project, "workspace" + "/" + test_project_renamed)


def test_download_files(mc: MerginClient):
"""Test downloading files at specified versions."""
test_project = "test_download_files"
Expand Down