diff options
author | John L. Villalovos <john@sodarock.com> | 2022-01-08 14:15:03 -0800 |
---|---|---|
committer | John L. Villalovos <john@sodarock.com> | 2022-01-08 14:15:03 -0800 |
commit | 79b1cc0b1be1e9986d9d3dae68db0bff981014fb (patch) | |
tree | 05a8cda1d7d038c247ab9e4208b775ed39bd243b | |
parent | c9ed3ddc1253c828dc877dcd55000d818c297ee7 (diff) | |
download | gitlab-jlvillal/parent_attrs.tar.gz |
fix: cli: url-encode path components of the URLjlvillal/parent_attrs
In the CLI we need to make sure the components put into the path
portion of the URL are url-encoded. Otherwise they will be interpreted
as part of the path. For example can specify the project ID as a path,
but in the URL it must be url-encoded or it doesn't work.
Also stop adding the components of the path as query parameters in the
URL.
Closes: #783
Closes: #1498
-rw-r--r-- | gitlab/v4/cli.py | 20 | ||||
-rw-r--r-- | tests/functional/cli/conftest.py | 14 | ||||
-rw-r--r-- | tests/functional/cli/test_cli.py | 11 | ||||
-rw-r--r-- | tests/functional/cli/test_cli_variables.py | 35 |
4 files changed, 67 insertions, 13 deletions
diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py index 675f93a..5b276ae 100644 --- a/gitlab/v4/cli.py +++ b/gitlab/v4/cli.py @@ -39,6 +39,7 @@ class GitlabCLI(object): self.action = action.lower() self.gl = gl self.args = args + self.parent_args: Dict[str, Any] = {} self.mgr_cls: Union[ Type[gitlab.mixins.CreateMixin], Type[gitlab.mixins.DeleteMixin], @@ -53,7 +54,10 @@ class GitlabCLI(object): # the class _path attribute, and replace the value with the result. if TYPE_CHECKING: assert self.mgr_cls._path is not None - self.mgr_cls._path = self.mgr_cls._path.format(**self.args) + + self._process_from_parent_attrs() + + self.mgr_cls._path = self.mgr_cls._path.format(**self.parent_args) self.mgr = self.mgr_cls(gl) if self.mgr_cls._types: @@ -63,6 +67,18 @@ class GitlabCLI(object): obj.set_from_cli(self.args[attr_name]) self.args[attr_name] = obj.get() + def _process_from_parent_attrs(self) -> None: + """Items in the path need to be url-encoded. There is a 1:1 mapping from + mgr_cls._from_parent_attrs <--> mgr_cls._path. Those values must be url-encoded + as they may contain a slash '/'.""" + for key in self.mgr_cls._from_parent_attrs: + if key not in self.args: + continue + + self.parent_args[key] = gitlab.utils.clean_str_id(self.args[key]) + # If we don't delete it then it will be added to the URL as a query-string + del self.args[key] + def __call__(self) -> Any: # Check for a method that matches object + action method = f"do_{self.what}_{self.action}" @@ -85,7 +101,7 @@ class GitlabCLI(object): data = {} if self.mgr._from_parent_attrs: for k in self.mgr._from_parent_attrs: - data[k] = self.args[k] + data[k] = self.parent_args[k] if not issubclass(self.cls, gitlab.mixins.GetWithoutIdMixin): if TYPE_CHECKING: assert isinstance(self.cls._id_attr, str) diff --git a/tests/functional/cli/conftest.py b/tests/functional/cli/conftest.py index ba94dcb..4311339 100644 --- a/tests/functional/cli/conftest.py +++ b/tests/functional/cli/conftest.py @@ -1,4 +1,7 @@ import pytest +import responses + +from gitlab.const import DEFAULT_URL @pytest.fixture @@ -19,3 +22,14 @@ def gitlab_cli(script_runner, gitlab_config): return script_runner.run(*command) return _gitlab_cli + + +@pytest.fixture +def resp_get_project(): + return { + "method": responses.GET, + "url": f"{DEFAULT_URL}/api/v4/projects/1", + "json": {"name": "name", "path": "test-path", "id": 1}, + "content_type": "application/json", + "status": 200, + } diff --git a/tests/functional/cli/test_cli.py b/tests/functional/cli/test_cli.py index 97ecacb..a889066 100644 --- a/tests/functional/cli/test_cli.py +++ b/tests/functional/cli/test_cli.py @@ -17,17 +17,6 @@ CI_JOB_TOKEN = "ci-job-token" CI_SERVER_URL = "https://gitlab.example.com" -@pytest.fixture -def resp_get_project(): - return { - "method": responses.GET, - "url": f"{DEFAULT_URL}/api/v4/projects/1", - "json": {"name": "name", "path": "test-path", "id": 1}, - "content_type": "application/json", - "status": 200, - } - - def test_main_entrypoint(script_runner, gitlab_config): ret = script_runner.run("python", "-m", "gitlab", "--config-file", gitlab_config) assert ret.returncode == 2 diff --git a/tests/functional/cli/test_cli_variables.py b/tests/functional/cli/test_cli_variables.py index 9b1b16d..5195f16 100644 --- a/tests/functional/cli/test_cli_variables.py +++ b/tests/functional/cli/test_cli_variables.py @@ -1,3 +1,12 @@ +import copy + +import pytest +import responses + +from gitlab import config +from gitlab.const import DEFAULT_URL + + def test_list_instance_variables(gitlab_cli, gl): cmd = ["variable", "list"] ret = gitlab_cli(cmd) @@ -17,3 +26,29 @@ def test_list_project_variables(gitlab_cli, project): ret = gitlab_cli(cmd) assert ret.success + + +def test_list_project_variables_with_path(gitlab_cli, project): + cmd = ["project-variable", "list", "--project-id", project.path_with_namespace] + ret = gitlab_cli(cmd) + + assert ret.success + + +@pytest.mark.script_launch_mode("inprocess") +@responses.activate +def test_list_project_variables_with_path_url_check( + monkeypatch, script_runner, resp_get_project +): + monkeypatch.setattr(config, "_DEFAULT_FILES", []) + resp_get_project_variables = copy.deepcopy(resp_get_project) + resp_get_project_variables.update( + url=f"{DEFAULT_URL}/api/v4/projects/project%2Fwith%2Fa%2Fnamespace/variables" + ) + resp_get_project_variables.update(json=[]) + + responses.add(**resp_get_project_variables) + ret = script_runner.run( + "gitlab", "project-variable", "list", "--project-id", "project/with/a/namespace" + ) + assert ret.success |