Repository: ansible/tower-cli
Pull request #320 (2017-07-09 11:42:03)

add new format option to just echo id
So a while ago I was staring at these workarounds: https://github.com/strategicdesignteam/openshift-ansible-contrib/commit/a2c0ec681e0445d8e7c8b79339c5fc06cb71f58a#diff-ca224404b5a9b5002bd12ea8577bd914R85

and it occurred to me that various _moderately_ sophisticated tasks quickly become almost impossible with tower-cli, if lookups can not be done through a resource's identity tuple. Example scenario: Change the email of a user with a given first name and last name. Solution (made possible by this PR):

```
tower-cli user modify $(tower-cli user get --last-name="Auditor" --first-name="System" --format=id) --email=dddd@dddd.ddd
```

This is still using special bash syntax, but if it solves someone's problem, then that seems fine. If someone, instead, were to store the outcome and then use it in a subsequent command, this might be useful as well.


Patch:
diff --git a/tower_cli/cli/resource.py b/tower_cli/cli/resource.py
index 8872c5f..96b94a6 100644
--- a/tower_cli/cli/resource.py
+++ b/tower_cli/cli/resource.py
@@ -29,6 +29,7 @@ import click
 from tower_cli.conf import settings, with_global_options
 from tower_cli.utils import parser, debug, secho
 from tower_cli.cli.action import ActionSubcommand
+from tower_cli.exceptions import MultipleRelatedError
 
 
 class ResSubcommand(click.MultiCommand):
@@ -129,6 +130,15 @@ class ResSubcommand(click.MultiCommand):
         return parser.ordered_dump(payload, Dumper=yaml.SafeDumper,
                                    default_flow_style=False)
 
+    def _format_id(self, payload):
+        """Echos only the id"""
+        if 'id' in payload:
+            return str(payload['id'])
+        if 'results' in payload and payload['count'] == 1:
+            return str(payload['results'][0]['id'])
+        raise MultipleRelatedError(
+            'Can not use id format when multiple objects are returned.')
+
     def _format_human(self, payload):
         """Convert the payload into an ASCII table suitable for
         printing on screen and return it.
diff --git a/tower_cli/conf.py b/tower_cli/conf.py
index 70c1c71..b66026d 100644
--- a/tower_cli/conf.py
+++ b/tower_cli/conf.py
@@ -369,8 +369,8 @@ def with_global_options(method):
         '-f', '--format',
         help='Output format. The "human" format is intended for humans '
              'reading output on the CLI; the "json" and "yaml" formats '
-             'provide more data.',
-        type=click.Choice(['human', 'json', 'yaml']),
+             'provide more data, and "id" echos the object id only.',
+        type=click.Choice(['human', 'json', 'yaml', 'id']),
         required=False, callback=_apply_runtime_setting,
         is_eager=True
     )(method)


Test patch:
diff --git a/tests/test_cli_resource.py b/tests/test_cli_resource.py
index 696c3bb..3972d22 100644
--- a/tests/test_cli_resource.py
+++ b/tests/test_cli_resource.py
@@ -352,3 +352,11 @@ class SubcommandTests(unittest.TestCase):
         self.assertIn('spam', output)
         self.assertIn('bar', output)
         self.assertIn('eggs', output)
+
+    def test_echo_id(self):
+        func = self.command._echo_method(lambda: {'id': 5})
+        with mock.patch.object(click, 'secho') as secho:
+            with settings.runtime_values(format='id'):
+                func()
+            output = secho.mock_calls[-1][1][0]
+        self.assertEqual('5', output)