We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add the possibility to query updates by releases in graphql
We currently have a getUpdates query in graphql that let us query updates using certain attributes, we should add the possibility to give a release name and get all the updates from a release.

For example ```query {getUpdates(releaseName: "F33") {alias}}```

For this we will most likely need to use a join query in the graphlq resolve function (https://github.com/fedora-infra/bodhi/blob/develop/bodhi/server/services/graphql.py#L132) to exploit the relationship between Updates and release.

Some hints https://stackoverflow.com/questions/8561470/sqlalchemy-filtering-by-relationship-attribute

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bodhi/server/services/graphql.py`
Content:
```
1 # Copyright © 2020 Red Hat Inc., and others.
2 #
3 # This file is part of Bodhi.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 """Defines API endpoints related to GraphQL objects."""
19 import graphene
20 from cornice import Service
21 from webob_graphql import serve_graphql_request
22 
23 from bodhi.server.config import config
24 from bodhi.server.graphql_schemas import Release, ReleaseModel, Update, UpdateModel
25 
26 graphql = Service(name='graphql', path='/graphql', description='graphql service')
27 
28 
29 @graphql.get()
30 @graphql.post()
31 def graphql_get(request):
32     """
33     Perform a GET request.
34 
35     Args:
36         request (pyramid.Request): The current request.
37     Returns:
38         The GraphQL response to the request.
39     """
40     context = {'session': request.session}
41     return serve_graphql_request(
42         request, schema, graphiql_enabled=config.get('graphiql_enabled'),
43         context_value=context)
44 
45 
46 class Query(graphene.ObjectType):
47     """Allow querying objects."""
48 
49     allReleases = graphene.List(Release)
50     getReleases = graphene.Field(
51         lambda: graphene.List(Release), name=graphene.String(),
52         id_prefix=graphene.String(), composed_by_bodhi=graphene.Boolean(),
53         state=graphene.String())
54 
55     getUpdates = graphene.Field(
56         lambda: graphene.List(Update), stable_karma=graphene.Int(),
57         stable_days=graphene.Int(), unstable_karma=graphene.Int(),
58         status=graphene.String(), request=graphene.String(),
59         pushed=graphene.Boolean(), critpath=graphene.Boolean(),
60         date_approved=graphene.String(), alias=graphene.String(),
61         user_id=graphene.Int())
62 
63     def resolve_allReleases(self, info):
64         """Answer Queries by fetching data from the Schema."""
65         query = Release.get_query(info)  # SQLAlchemy query
66         return query.all()
67 
68     def resolve_getReleases(self, info, **args):
69         """Answer Release queries with a given argument."""
70         query = Release.get_query(info)
71 
72         id_prefix = args.get("id_prefix")
73         if id_prefix is not None:
74             query = query.filter(ReleaseModel.id_prefix == id_prefix)
75 
76         name = args.get("name")
77         if name is not None:
78             query = query.filter(ReleaseModel.name == name)
79 
80         composed_by_bodhi = args.get("composed_by_bodhi")
81         if composed_by_bodhi is not None:
82             query = query.filter(ReleaseModel.composed_by_bodhi == composed_by_bodhi)
83 
84         state = args.get("state")
85         if state is not None:
86             query = query.filter(ReleaseModel.state == state)
87 
88         return query.all()
89 
90     def resolve_getUpdates(self, info, **args):
91         """Answer Release queries with a given argument."""
92         query = Update.get_query(info)
93 
94         stable_karma = args.get("stable_karma")
95         if stable_karma is not None:
96             query = query.filter(UpdateModel.stable_karma == stable_karma)
97 
98         stable_days = args.get("stable_days")
99         if stable_days is not None:
100             query = query.filter(UpdateModel.stable_days == stable_days)
101 
102         unstable_karma = args.get("unstable_karma")
103         if unstable_karma is not None:
104             query = query.filter(UpdateModel.unstable_karma == unstable_karma)
105 
106         status = args.get("status")
107         if status is not None:
108             query = query.filter(UpdateModel.status == status)
109 
110         request = args.get("request")
111         if request is not None:
112             query = query.filter(UpdateModel.request == request)
113 
114         pushed = args.get("pushed")
115         if pushed is not None:
116             query = query.filter(UpdateModel.pushed == pushed)
117 
118         critpath = args.get("critpath")
119         if critpath is not None:
120             query = query.filter(UpdateModel.critpath == critpath)
121 
122         date_approved = args.get("date_approved")
123         if date_approved is not None:
124             query = query.filter(UpdateModel.date_approved == date_approved)
125 
126         alias = args.get("alias")
127         if alias is not None:
128             query = query.filter(UpdateModel.alias == alias)
129 
130         user_id = args.get("user_id")
131         if user_id is not None:
132             query = query.filter(UpdateModel.user_id == user_id)
133 
134         return query.all()
135 
136 
137 schema = graphene.Schema(query=Query)
138
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bodhi/server/services/graphql.py b/bodhi/server/services/graphql.py
--- a/bodhi/server/services/graphql.py
+++ b/bodhi/server/services/graphql.py
@@ -58,7 +58,7 @@
         status=graphene.String(), request=graphene.String(),
         pushed=graphene.Boolean(), critpath=graphene.Boolean(),
         date_approved=graphene.String(), alias=graphene.String(),
-        user_id=graphene.Int())
+        user_id=graphene.Int(), release_name=graphene.String())
 
     def resolve_allReleases(self, info):
         """Answer Queries by fetching data from the Schema."""
@@ -131,6 +131,10 @@
         if user_id is not None:
             query = query.filter(UpdateModel.user_id == user_id)
 
+        release_name = args.get("release_name")
+        if release_name is not None:
+            query = query.join(UpdateModel.release).filter(ReleaseModel.name == release_name)
+
         return query.all()

{"golden_diff": "diff --git a/bodhi/server/services/graphql.py b/bodhi/server/services/graphql.py\n--- a/bodhi/server/services/graphql.py\n+++ b/bodhi/server/services/graphql.py\n@@ -58,7 +58,7 @@\n         status=graphene.String(), request=graphene.String(),\n         pushed=graphene.Boolean(), critpath=graphene.Boolean(),\n         date_approved=graphene.String(), alias=graphene.String(),\n-        user_id=graphene.Int())\n+        user_id=graphene.Int(), release_name=graphene.String())\n \n     def resolve_allReleases(self, info):\n         \"\"\"Answer Queries by fetching data from the Schema.\"\"\"\n@@ -131,6 +131,10 @@\n         if user_id is not None:\n             query = query.filter(UpdateModel.user_id == user_id)\n \n+        release_name = args.get(\"release_name\")\n+        if release_name is not None:\n+            query = query.join(UpdateModel.release).filter(ReleaseModel.name == release_name)\n+\n         return query.all()\n", "issue": "Add the possibility to query updates by releases in graphql\nWe currently have a getUpdates query in graphql that let us query updates using certain attributes, we should add the possibility to give a release name and get all the updates from a release.\r\n\r\nFor example ```query {getUpdates(releaseName: \"F33\") {alias}}```\r\n\r\nFor this we will most likely need to use a join query in the graphlq resolve function (https://github.com/fedora-infra/bodhi/blob/develop/bodhi/server/services/graphql.py#L132) to exploit the relationship between Updates and release.\r\n\r\nSome hints https://stackoverflow.com/questions/8561470/sqlalchemy-filtering-by-relationship-attribute\n", "before_files": [{"content": "# Copyright \u00a9 2020 Red Hat Inc., and others.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"Defines API endpoints related to GraphQL objects.\"\"\"\nimport graphene\nfrom cornice import Service\nfrom webob_graphql import serve_graphql_request\n\nfrom bodhi.server.config import config\nfrom bodhi.server.graphql_schemas import Release, ReleaseModel, Update, UpdateModel\n\ngraphql = Service(name='graphql', path='/graphql', description='graphql service')\n\n\n@graphql.get()\n@graphql.post()\ndef graphql_get(request):\n    \"\"\"\n    Perform a GET request.\n\n    Args:\n        request (pyramid.Request): The current request.\n    Returns:\n        The GraphQL response to the request.\n    \"\"\"\n    context = {'session': request.session}\n    return serve_graphql_request(\n        request, schema, graphiql_enabled=config.get('graphiql_enabled'),\n        context_value=context)\n\n\nclass Query(graphene.ObjectType):\n    \"\"\"Allow querying objects.\"\"\"\n\n    allReleases = graphene.List(Release)\n    getReleases = graphene.Field(\n        lambda: graphene.List(Release), name=graphene.String(),\n        id_prefix=graphene.String(), composed_by_bodhi=graphene.Boolean(),\n        state=graphene.String())\n\n    getUpdates = graphene.Field(\n        lambda: graphene.List(Update), stable_karma=graphene.Int(),\n        stable_days=graphene.Int(), unstable_karma=graphene.Int(),\n        status=graphene.String(), request=graphene.String(),\n        pushed=graphene.Boolean(), critpath=graphene.Boolean(),\n        date_approved=graphene.String(), alias=graphene.String(),\n        user_id=graphene.Int())\n\n    def resolve_allReleases(self, info):\n        \"\"\"Answer Queries by fetching data from the Schema.\"\"\"\n        query = Release.get_query(info)  # SQLAlchemy query\n        return query.all()\n\n    def resolve_getReleases(self, info, **args):\n        \"\"\"Answer Release queries with a given argument.\"\"\"\n        query = Release.get_query(info)\n\n        id_prefix = args.get(\"id_prefix\")\n        if id_prefix is not None:\n            query = query.filter(ReleaseModel.id_prefix == id_prefix)\n\n        name = args.get(\"name\")\n        if name is not None:\n            query = query.filter(ReleaseModel.name == name)\n\n        composed_by_bodhi = args.get(\"composed_by_bodhi\")\n        if composed_by_bodhi is not None:\n            query = query.filter(ReleaseModel.composed_by_bodhi == composed_by_bodhi)\n\n        state = args.get(\"state\")\n        if state is not None:\n            query = query.filter(ReleaseModel.state == state)\n\n        return query.all()\n\n    def resolve_getUpdates(self, info, **args):\n        \"\"\"Answer Release queries with a given argument.\"\"\"\n        query = Update.get_query(info)\n\n        stable_karma = args.get(\"stable_karma\")\n        if stable_karma is not None:\n            query = query.filter(UpdateModel.stable_karma == stable_karma)\n\n        stable_days = args.get(\"stable_days\")\n        if stable_days is not None:\n            query = query.filter(UpdateModel.stable_days == stable_days)\n\n        unstable_karma = args.get(\"unstable_karma\")\n        if unstable_karma is not None:\n            query = query.filter(UpdateModel.unstable_karma == unstable_karma)\n\n        status = args.get(\"status\")\n        if status is not None:\n            query = query.filter(UpdateModel.status == status)\n\n        request = args.get(\"request\")\n        if request is not None:\n            query = query.filter(UpdateModel.request == request)\n\n        pushed = args.get(\"pushed\")\n        if pushed is not None:\n            query = query.filter(UpdateModel.pushed == pushed)\n\n        critpath = args.get(\"critpath\")\n        if critpath is not None:\n            query = query.filter(UpdateModel.critpath == critpath)\n\n        date_approved = args.get(\"date_approved\")\n        if date_approved is not None:\n            query = query.filter(UpdateModel.date_approved == date_approved)\n\n        alias = args.get(\"alias\")\n        if alias is not None:\n            query = query.filter(UpdateModel.alias == alias)\n\n        user_id = args.get(\"user_id\")\n        if user_id is not None:\n            query = query.filter(UpdateModel.user_id == user_id)\n\n        return query.all()\n\n\nschema = graphene.Schema(query=Query)\n", "path": "bodhi/server/services/graphql.py"}], "after_files": [{"content": "# Copyright \u00a9 2020 Red Hat Inc., and others.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"Defines API endpoints related to GraphQL objects.\"\"\"\nimport graphene\nfrom cornice import Service\nfrom webob_graphql import serve_graphql_request\n\nfrom bodhi.server.config import config\nfrom bodhi.server.graphql_schemas import Release, ReleaseModel, Update, UpdateModel\n\ngraphql = Service(name='graphql', path='/graphql', description='graphql service')\n\n\n@graphql.get()\n@graphql.post()\ndef graphql_get(request):\n    \"\"\"\n    Perform a GET request.\n\n    Args:\n        request (pyramid.Request): The current request.\n    Returns:\n        The GraphQL response to the request.\n    \"\"\"\n    context = {'session': request.session}\n    return serve_graphql_request(\n        request, schema, graphiql_enabled=config.get('graphiql_enabled'),\n        context_value=context)\n\n\nclass Query(graphene.ObjectType):\n    \"\"\"Allow querying objects.\"\"\"\n\n    allReleases = graphene.List(Release)\n    getReleases = graphene.Field(\n        lambda: graphene.List(Release), name=graphene.String(),\n        id_prefix=graphene.String(), composed_by_bodhi=graphene.Boolean(),\n        state=graphene.String())\n\n    getUpdates = graphene.Field(\n        lambda: graphene.List(Update), stable_karma=graphene.Int(),\n        stable_days=graphene.Int(), unstable_karma=graphene.Int(),\n        status=graphene.String(), request=graphene.String(),\n        pushed=graphene.Boolean(), critpath=graphene.Boolean(),\n        date_approved=graphene.String(), alias=graphene.String(),\n        user_id=graphene.Int(), release_name=graphene.String())\n\n    def resolve_allReleases(self, info):\n        \"\"\"Answer Queries by fetching data from the Schema.\"\"\"\n        query = Release.get_query(info)  # SQLAlchemy query\n        return query.all()\n\n    def resolve_getReleases(self, info, **args):\n        \"\"\"Answer Release queries with a given argument.\"\"\"\n        query = Release.get_query(info)\n\n        id_prefix = args.get(\"id_prefix\")\n        if id_prefix is not None:\n            query = query.filter(ReleaseModel.id_prefix == id_prefix)\n\n        name = args.get(\"name\")\n        if name is not None:\n            query = query.filter(ReleaseModel.name == name)\n\n        composed_by_bodhi = args.get(\"composed_by_bodhi\")\n        if composed_by_bodhi is not None:\n            query = query.filter(ReleaseModel.composed_by_bodhi == composed_by_bodhi)\n\n        state = args.get(\"state\")\n        if state is not None:\n            query = query.filter(ReleaseModel.state == state)\n\n        return query.all()\n\n    def resolve_getUpdates(self, info, **args):\n        \"\"\"Answer Release queries with a given argument.\"\"\"\n        query = Update.get_query(info)\n\n        stable_karma = args.get(\"stable_karma\")\n        if stable_karma is not None:\n            query = query.filter(UpdateModel.stable_karma == stable_karma)\n\n        stable_days = args.get(\"stable_days\")\n        if stable_days is not None:\n            query = query.filter(UpdateModel.stable_days == stable_days)\n\n        unstable_karma = args.get(\"unstable_karma\")\n        if unstable_karma is not None:\n            query = query.filter(UpdateModel.unstable_karma == unstable_karma)\n\n        status = args.get(\"status\")\n        if status is not None:\n            query = query.filter(UpdateModel.status == status)\n\n        request = args.get(\"request\")\n        if request is not None:\n            query = query.filter(UpdateModel.request == request)\n\n        pushed = args.get(\"pushed\")\n        if pushed is not None:\n            query = query.filter(UpdateModel.pushed == pushed)\n\n        critpath = args.get(\"critpath\")\n        if critpath is not None:\n            query = query.filter(UpdateModel.critpath == critpath)\n\n        date_approved = args.get(\"date_approved\")\n        if date_approved is not None:\n            query = query.filter(UpdateModel.date_approved == date_approved)\n\n        alias = args.get(\"alias\")\n        if alias is not None:\n            query = query.filter(UpdateModel.alias == alias)\n\n        user_id = args.get(\"user_id\")\n        if user_id is not None:\n            query = query.filter(UpdateModel.user_id == user_id)\n\n        release_name = args.get(\"release_name\")\n        if release_name is not None:\n            query = query.join(UpdateModel.release).filter(ReleaseModel.name == release_name)\n\n        return query.all()\n\n\nschema = graphene.Schema(query=Query)\n", "path": "bodhi/server/services/graphql.py"}]}