summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn L. Villalovos <john@sodarock.com>2022-01-08 14:15:03 -0800
committerJohn L. Villalovos <john@sodarock.com>2022-01-08 14:15:03 -0800
commit79b1cc0b1be1e9986d9d3dae68db0bff981014fb (patch)
tree05a8cda1d7d038c247ab9e4208b775ed39bd243b
parentc9ed3ddc1253c828dc877dcd55000d818c297ee7 (diff)
downloadgitlab-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.py20
-rw-r--r--tests/functional/cli/conftest.py14
-rw-r--r--tests/functional/cli/test_cli.py11
-rw-r--r--tests/functional/cli/test_cli_variables.py35
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