Repository: fedora-infra/bodhi

Issue:
no way to query according to update id
I've found no way to retrieve an update object when I know the update id. Neither `query(packages=)` nor `query(builds=)` seem to take it. It used to be possible with `query(package=update_id)`.

```
> result = bodhi.query(builds='hedgewars-0.9.21.1-3.fc21')
> print result['updates'][0]['title']
hedgewars-0.9.21.1-3.fc21,ghc-bytestring-show-0.3.5.6-2.fc21
> print result['updates'][0]['alias']
FEDORA-2015-2256

> result = bodhi.query(builds='hedgewars-0.9.21.1-3.fc21,ghc-bytestring-show-0.3.5.6-2.fc21')
> print len(result['updates'])
1

> result = bodhi.query(builds='FEDORA-2015-2256')
> print len(result['updates'])
0

> result = bodhi.query(packages='FEDORA-2015-2256')
> print len(result['updates'])
0
```

python-fedora-0.5.2-1.fc22.noarch



Discussion:
So yeah... in Bodhi1 this was kind of a hack that would query for the updateid when passed in as a 'package'

```
if package:
    try:
        try:
            update = PackageUpdate.byTitle(package)
        except SQLObjectNotFound:
            update = PackageUpdate.select(PackageUpdate.q.updateid==package)
```

I think I'll add an `updateid`/`alias` argument to the bodhi2 query API for this.


Resolving patch:
diff --git a/bodhi/cli.py b/bodhi/cli.py
--- a/bodhi/cli.py
+++ b/bodhi/cli.py
@@ -42,8 +42,10 @@ def cli():
 @click.option('--suggest', help='Post-update user suggestion',
               type=click.Choice(['logout', 'reboot']))
 @click.option('--file', help='A text file containing all the update details')
+@click.option('--staging', help='Use the staging bodhi instance',
+              is_flag=True, default=False)
 def new(username, password, **kwargs):
-    client = BodhiClient(username, password)
+    client = BodhiClient(username, password, staging=kwargs['staging'])
 
     if kwargs['file'] is None:
         updates = [kwargs]
@@ -53,12 +55,14 @@ def new(username, password, **kwargs):
 
     for update in updates:
         resp = client.new(**update)
-        print_resp(resp)
+        print_resp(resp, client)
 
 
 @cli.command()
+@click.option('--updateid', help='Query by update ID (eg: FEDORA-2015-0001)')
 @click.option('--approved-since', help='Approved after a specific timestamp')
 @click.option('--modified-since', help='Modified after a specific timestamp')
+@click.option('--builds', help='Query updates based on builds')
 @click.option('--bugs', help='A list of bug numbers')
 @click.option('--critpath', is_flag=True, default=None,
               help='Query only critical path packages')
@@ -79,24 +83,29 @@ def new(username, password, **kwargs):
                                  'unpushed', 'processing']))
 @click.option('--suggest', help='Filter by post-update user suggestion',
               type=click.Choice(['logout', 'reboot']))
-@click.option('--type', default='bugfix', help='Filter by update type',
+@click.option('--type', default=None, help='Filter by update type',
               type=click.Choice(['newpackage', 'security',
                                  'bugfix', 'enhancement',]))
 @click.option('--user', help='Updates submitted by a specific user')
+@click.option('--staging', help='Use the staging bodhi instance',
+              is_flag=True, default=False)
 def query(**kwargs):
-    client = BodhiClient()
+    client = BodhiClient(staging=kwargs['staging'])
     resp = client.query(**kwargs)
-    print_resp(resp)
-
-
-def print_resp(resp):
-    if resp.status_code == 200:
-        try:
-            data = resp.json()
-            click.echo(data)
-        except ValueError:
-            click.echo('Unable to decode JSON:')
-            click.echo(resp.text)
+    print_resp(resp, client)
+
+
+def print_resp(resp, client):
+    if 'updates' in resp:
+        if len(resp.updates) == 1:
+            click.echo(client.update_str(resp.updates[0]))
+        else:
+            for update in resp.updates:
+                click.echo(client.update_str(update, minimal=True).strip())
+        click.echo('%s updates found (%d shown)' % (resp.total,
+            len(resp.updates)))
+    elif 'title' in resp:
+        click.echo(client.update_str(resp))
     else:
         click.echo(resp.text)
 
diff --git a/bodhi/schemas.py b/bodhi/schemas.py
--- a/bodhi/schemas.py
+++ b/bodhi/schemas.py
@@ -387,6 +387,13 @@ class ListUserSchema(PaginatedSchema, SearchableSchema):
 
 
 class ListUpdateSchema(PaginatedSchema, SearchableSchema, Cosmetics):
+    alias = Builds(
+        colander.Sequence(accept_scalar=True),
+        location="querystring",
+        missing=None,
+        preparer=[splitter],
+    )
+
     approved_since = colander.SchemaNode(
         colander.DateTime(),
         location="querystring",
@@ -513,6 +520,13 @@ class ListUpdateSchema(PaginatedSchema, SearchableSchema, Cosmetics):
         missing=None,
     )
 
+    updateid = Builds(
+        colander.Sequence(accept_scalar=True),
+        location="querystring",
+        missing=None,
+        preparer=[splitter],
+    )
+
 
 class ListBuildSchema(PaginatedSchema):
     nvr = colander.SchemaNode(
diff --git a/bodhi/services/updates.py b/bodhi/services/updates.py
--- a/bodhi/services/updates.py
+++ b/bodhi/services/updates.py
@@ -222,6 +222,13 @@ def query_updates(request):
     if user is not None:
         query = query.filter(Update.user==user)
 
+    updateid = data.get('updateid')
+    if updateid is not None:
+        query = query.filter(or_(*[Update.alias==uid for uid in updateid]))
+    alias = data.get('alias')
+    if alias is not None:
+        query = query.filter(or_(*[Update.alias==a for a in alias]))
+
     query = query.order_by(Update.date_submitted.desc())
     total = query.count()
 


Test patch:
diff --git a/bodhi/tests/functional/test_updates.py b/bodhi/tests/functional/test_updates.py
--- a/bodhi/tests/functional/test_updates.py
+++ b/bodhi/tests/functional/test_updates.py
@@ -1563,3 +1563,22 @@ def test_redirect_to_package(self):
 
         # But be sure that we don't redirect if the package doesn't exist
         res = self.app.get('/updates/non-existant', status=404)
+
+    def test_list_updates_by_alias_and_updateid(self):
+        upd = self.db.query(Update).filter(Update.alias != None).first()
+        res = self.app.get('/updates/', {"alias": upd.alias})
+        body = res.json_body
+        self.assertEquals(len(body['updates']), 1)
+        up = body['updates'][0]
+        self.assertEquals(up['title'], upd.title)
+        self.assertEquals(up['alias'], upd.alias)
+
+        res = self.app.get('/updates/', {"updateid": upd.alias})
+        body = res.json_body
+        self.assertEquals(len(body['updates']), 1)
+        up = body['updates'][0]
+        self.assertEquals(up['title'], upd.title)
+
+        res = self.app.get('/updates/', {"updateid": 'BLARG'})
+        body = res.json_body
+        self.assertEquals(len(body['updates']), 0)