Repository: civisanalytics/civis-python

Issue:
CLI: GET /aliases/{alias} endpoint is inaccessible
The CLI emitted a confusing `UserWarning` of `UserWarning: conflicting command name: get` when I used the terminal command of `civis files download <file-id> <filename>`. This is the line that triggered the said `UserWarning`:

https://github.com/civisanalytics/civis-python/blob/v1.10.0/civis/cli/__main__.py#L287

My CLI file download operation succeeded and got me the file I needed, with no errors or other warnings. We should look into what triggers the `UserWarning` and suppress/remove/fix it as appropriate, though this ticket is probably low-priority.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1fbfc4f..4404411 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Deprecate the `headers` parameter of `dataframe_to_civis` and always tell Civis
   whether the import has headers or not, rather than autodetecting. (#263, #307)
 - Set `cloudpickle` requirements to <1.2 on Python v3.4. (#309)
+- Fixed an issue in the CLI which prevented users from accessing GET /aliases/{id}
+  and simultaneously generated a warning message. (#298, #316)
 
 ### Changed
 - Loosened version requirements of `pyyaml` to include `pyyaml<=5.99`. (#293)
diff --git a/civis/cli/__main__.py b/civis/cli/__main__.py
index 71613b5..510f50e 100755
--- a/civis/cli/__main__.py
+++ b/civis/cli/__main__.py
@@ -29,6 +29,7 @@ from civis.cli._cli_commands import (
     notebooks_download_cmd, notebooks_new_cmd,
     notebooks_up, notebooks_down, notebooks_open)
 from civis.resources import get_api_spec, CACHED_SPEC_PATH
+from civis.resources._resources import parse_method_name
 from civis._utils import open_session
 from civis.compat import FileNotFoundError
 
@@ -99,27 +100,19 @@ def munge_name(s):
 def make_operation_name(path, method, resource_name):
     """Create an appropriate CLI command for an operation.
 
-    E.g., '/imports/files/{id}/runs/{run_id}' -> 'get_imports_files_runs'
+    Examples
+    --------
+    >>> make_operation_name('/scripts/r/{id}/runs/{run_id}', 'get', 'scripts')
+    get-r-runs
     """
-
-    name = path.lower()
-    name = re.sub(r'{[^}]+}', '', name)
-    name = munge_name(name)
+    path = path.lower().lstrip('/')
 
     # Remove resource prefix. Note that the path name for some operations is
     # just the resource name (e.g., /databases).
-    # Munge the resource name for consistency (e.g.,
-    # remote_hosts -> remote-hosts).
-    if name.startswith(munge_name(resource_name)):
-        name = name[len(resource_name):].strip("-")
-
-    # If there's no ID argument at the end, then it'll be a listing operation.
-    if method == 'get' and not path.endswith('}'):
-        method = 'list'
-
-    # Add HTTP method prefix. For operations whose path name is the resource,
-    # make the command just be the HTTP method.
-    name = '{}-{}'.format(method, name) if name else method
+    if path.startswith(resource_name):
+        path = path[len(resource_name):].strip("-")
+
+    name = parse_method_name(method, path).replace("_", "-")
     return name
 
 
@@ -293,7 +286,8 @@ def add_path_commands(path, path_dict, grp, resource):
                                        help="output in JSON instead of YAML"))
 
         if cmd.name in grp.commands:
-            warn("conflicting command name: %s" % cmd.name)
+            warn('conflicting command name "%s" for path "%s"' %
+                 (cmd.name, path))
 
         grp.add_command(cmd)
 


Test patch:
diff --git a/civis/tests/test_cli.py b/civis/tests/test_cli.py
index 41466fa..137cac5 100644
--- a/civis/tests/test_cli.py
+++ b/civis/tests/test_cli.py
@@ -2,7 +2,9 @@ from collections import OrderedDict
 import json
 import os
 
-from civis.cli.__main__ import generate_cli, invoke
+import pytest
+
+from civis.cli.__main__ import generate_cli, invoke, make_operation_name
 from civis.compat import mock
 from civis.tests import TEST_SPEC
 
@@ -34,7 +36,9 @@ def test_generate_cli_civis(mock_retrieve_spec_dict):
         civis_spec = json.load(f, object_pairs_hook=OrderedDict)
     mock_retrieve_spec_dict.return_value = civis_spec
 
-    cli = generate_cli()
+    with pytest.warns(None) as warn_rec:
+        cli = generate_cli()
+    assert len(warn_rec) == 0
 
     # Check a regular command.
     list_runs_cmd = cli.commands['scripts'].commands['list-containers-runs']
@@ -102,3 +106,15 @@ def test_parameter_case(mock_session):
         json={},
         params={'firstParameter': 'a', 'secondParameter': 'b'},
         method='WIBBLE')
+
+
+@pytest.mark.parametrize(
+    "path,method,resource_name,exp",
+    [('/imports/files/{id}/runs/{run_id}', 'get', 'imports', 'get-files-runs'),
+     ('/aliases/{object_type}/{alias}', 'get', 'aliases', 'get-object-type'),
+     ('/workflows/', 'get', 'workflows', 'list'),
+     ('/results/{id}/grants', 'delete', 'results', 'delete-grants'),
+     ]
+)
+def test_make_operation_name(path, method, resource_name, exp):
+    assert make_operation_name(path, method, resource_name) == exp