prompt: 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()
  
  ```
  
golden_diff: 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()
  
verification_info: {"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"}]}