We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Create menu items in menuCreate mutation
### What I'm trying to achieve
It would be very handy if API user would be able to create the first level of menu items at the menu creation moment. This way we could reduce the number of database queries needed to create a menu and speed up the whole process. 

### Describe a proposed solution
Change `MenuInput` signature to
```
type MenuInput {
  name: String!
  items: [MenuItemCreate!]
}
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/graphql/menu/mutations.py`
Content:
```
1 import graphene
2 from graphql_jwt.decorators import permission_required
3 
4 from ...menu import models
5 from ..core.mutations import BaseMutation, ModelDeleteMutation, ModelMutation
6 from .types import Menu
7 
8 
9 class MenuInput(graphene.InputObjectType):
10     name = graphene.String(description='Name of the menu.')
11 
12 
13 class MenuItemInput(graphene.InputObjectType):
14     menu = graphene.ID(
15         description='Menu to which item belongs to.', name='menu')
16     name = graphene.String(description='Name of the menu item.')
17     parent = graphene.ID(
18         description='''
19         ID of the parent menu. If empty, menu will be top level
20         menu.''',
21         name='parent')
22     url = graphene.String(description='URL of the pointed item.')
23     category = graphene.ID(
24         description='Category to which item points.', name='category')
25     collection = graphene.ID(
26         description='Collection to which item points.', name='collection')
27     page = graphene.ID(
28         description='Page to which item points.', name='page')
29 
30 
31 class MenuCreate(ModelMutation):
32     class Arguments:
33         input = MenuInput(
34             required=True,
35             description='Fields required to create a menu.')
36 
37     class Meta:
38         description = 'Creates a new Menu'
39         model = models.Menu
40 
41     @classmethod
42     def user_is_allowed(cls, user, input):
43         return user.has_perm('menu.manage_menus')
44 
45 
46 class MenuUpdate(ModelMutation):
47     class Arguments:
48         id = graphene.ID(
49             required=True, description='ID of a menu to update.')
50         input = MenuInput(
51             required=True,
52             description='Fields required to update a menu.')
53 
54     class Meta:
55         description = 'Updates a menu.'
56         model = models.Menu
57 
58     @classmethod
59     def user_is_allowed(cls, user, input):
60         return user.has_perm('menu.manage_menus')
61 
62 
63 class MenuDelete(ModelDeleteMutation):
64     class Arguments:
65         id = graphene.ID(
66             required=True, description='ID of a menu to delete.')
67 
68     class Meta:
69         description = 'Deletes a menu.'
70         model = models.Menu
71 
72     @classmethod
73     def user_is_allowed(cls, user, input):
74         return user.has_perm('menu.manage_menus')
75 
76 
77 class MenuItemCreate(ModelMutation):
78     class Arguments:
79         input = MenuItemInput(
80             required=True,
81             description="""Fields required to update a menu item.
82             Only one of 'url', 'category', 'page', 'collection' is allowed
83             per item""")
84 
85     class Meta:
86         description = 'Creates a new Menu'
87         model = models.MenuItem
88 
89     @classmethod
90     def user_is_allowed(cls, user, input):
91         return user.has_perm('menu.manage_menus')
92 
93     @classmethod
94     def clean_input(cls, info, instance, input, errors):
95         cleaned_input = super().clean_input(info, instance, input, errors)
96         items = [
97             cleaned_input.get('page'), cleaned_input.get('collection'),
98             cleaned_input.get('url'), cleaned_input.get('category')]
99         items = [item for item in items if item is not None]
100         if len(items) > 1:
101             cls.add_error(
102                 errors=errors,
103                 field='items', message='More than one item provided.')
104         return cleaned_input
105 
106 
107 class MenuItemUpdate(MenuItemCreate):
108     class Arguments:
109         id = graphene.ID(
110             required=True, description='ID of a menu item to update.')
111         input = MenuItemInput(
112             required=True,
113             description="""Fields required to update a menu item.
114             Only one of 'url', 'category', 'page', 'collection' is allowed
115             per item""")
116 
117     class Meta:
118         description = 'Updates a menu item.'
119         model = models.MenuItem
120 
121     @classmethod
122     def user_is_allowed(cls, user, input):
123         return user.has_perm('menu.manage_menus')
124 
125     @classmethod
126     def construct_instance(cls, instance, cleaned_data):
127         # Only one item can be assigned per menu item
128         instance.page = None
129         instance.collection = None
130         instance.category = None
131         instance.url = None
132         return super().construct_instance(instance, cleaned_data)
133 
134 
135 class MenuItemDelete(ModelDeleteMutation):
136     class Arguments:
137         id = graphene.ID(
138             required=True, description='ID of a menu item to delete.')
139 
140     class Meta:
141         description = 'Deletes a menu item.'
142         model = models.MenuItem
143 
144     @classmethod
145     def user_is_allowed(cls, user, input):
146         return user.has_perm('menu.manage_menus')
147 
148 
149 class NavigationType(graphene.Enum):
150     MAIN = 'main'
151     SECONDARY = 'secondary'
152 
153     @property
154     def description(self):
155         if self == NavigationType.MAIN:
156             return 'Main storefront\'s navigation.'
157         return 'Secondary storefront\'s navigation.'
158 
159 
160 class AssignNavigation(BaseMutation):
161     menu = graphene.Field(Menu, description='Assigned navigation menu.')
162 
163     class Arguments:
164         menu = graphene.ID(
165             description='ID of the menu.')
166         navigation_type = NavigationType(
167             description='Type of the navigation bar to assign the menu to.',
168             required=True)
169 
170     class Meta:
171         description = 'Assigns storefront\'s navigation menus.'
172 
173     @classmethod
174     @permission_required(['menu.manage_menus', 'site.manage_settings'])
175     def mutate(cls, root, info, navigation_type, menu=None):
176         errors = []
177         site_settings = info.context.site.settings
178         if menu is not None:
179             menu = cls.get_node_or_error(
180                 info, menu, errors=errors, field='menu')
181         if not errors:
182             if navigation_type == NavigationType.MAIN:
183                 site_settings.top_menu = menu
184                 site_settings.save(update_fields=['top_menu'])
185             elif navigation_type == NavigationType.SECONDARY:
186                 site_settings.bottom_menu = menu
187                 site_settings.save(update_fields=['bottom_menu'])
188             else:
189                 raise AssertionError(
190                     'Unknown navigation type: %s' % navigation_type)
191         return AssignNavigation(menu=menu, errors=errors)
192
```
--- 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/saleor/graphql/menu/mutations.py b/saleor/graphql/menu/mutations.py
--- a/saleor/graphql/menu/mutations.py
+++ b/saleor/graphql/menu/mutations.py
@@ -3,22 +3,13 @@
 
 from ...menu import models
 from ..core.mutations import BaseMutation, ModelDeleteMutation, ModelMutation
+from ..product.types import Category, Collection
+from ..page.types import Page
 from .types import Menu
 
 
-class MenuInput(graphene.InputObjectType):
-    name = graphene.String(description='Name of the menu.')
-
-
 class MenuItemInput(graphene.InputObjectType):
-    menu = graphene.ID(
-        description='Menu to which item belongs to.', name='menu')
     name = graphene.String(description='Name of the menu item.')
-    parent = graphene.ID(
-        description='''
-        ID of the parent menu. If empty, menu will be top level
-        menu.''',
-        name='parent')
     url = graphene.String(description='URL of the pointed item.')
     category = graphene.ID(
         description='Category to which item points.', name='category')
@@ -28,9 +19,28 @@
         description='Page to which item points.', name='page')
 
 
+class MenuItemCreateInput(MenuItemInput):
+    menu = graphene.ID(
+        description='Menu to which item belongs to.', name='menu')
+    parent = graphene.ID(
+        description='''
+        ID of the parent menu. If empty, menu will be top level
+        menu.''',
+        name='parent')
+
+
+class MenuInput(graphene.InputObjectType):
+    name = graphene.String(description='Name of the menu.')
+
+
+class MenuCreateInput(MenuInput):
+    items = graphene.List(
+        MenuItemInput, description='List of menu items.')
+
+
 class MenuCreate(ModelMutation):
     class Arguments:
-        input = MenuInput(
+        input = MenuCreateInput(
             required=True,
             description='Fields required to create a menu.')
 
@@ -42,6 +52,45 @@
     def user_is_allowed(cls, user, input):
         return user.has_perm('menu.manage_menus')
 
+    @classmethod
+    def clean_input(cls, info, instance, input, errors):
+        cleaned_input = super().clean_input(info, instance, input, errors)
+        items = []
+        for item in cleaned_input.get('items', []):
+            category = item.get('category')
+            collection = item.get('collection')
+            page = item.get('page')
+            url = item.get('url')
+            if len([i for i in [category, collection, page, url] if i]) > 1:
+                cls.add_error(
+                    errors, 'items', 'More than one item provided.')
+            else:
+                if category:
+                    category = cls.get_node_or_error(
+                        info, category, errors, 'items', only_type=Category)
+                    item['category'] = category
+                elif collection:
+                    collection = cls.get_node_or_error(
+                        info, collection, errors, 'items',
+                        only_type=Collection)
+                    item['collection'] = collection
+                elif page:
+                    page = cls.get_node_or_error(
+                        info, page, errors, 'items', only_type=Page)
+                    item['page'] = page
+                elif not url:
+                    cls.add_error(errors, 'items', 'No menu item provided.')
+                items.append(item)
+        cleaned_input['items'] = items
+        return cleaned_input
+
+    @classmethod
+    def _save_m2m(cls, info, instance, cleaned_data):
+        super()._save_m2m(info, instance, cleaned_data)
+        items = cleaned_data.get('items', [])
+        for item in items:
+            instance.items.create(**item)
+
 
 class MenuUpdate(ModelMutation):
     class Arguments:
@@ -76,7 +125,7 @@
 
 class MenuItemCreate(ModelMutation):
     class Arguments:
-        input = MenuItemInput(
+        input = MenuItemCreateInput(
             required=True,
             description="""Fields required to update a menu item.
             Only one of 'url', 'category', 'page', 'collection' is allowed

{"golden_diff": "diff --git a/saleor/graphql/menu/mutations.py b/saleor/graphql/menu/mutations.py\n--- a/saleor/graphql/menu/mutations.py\n+++ b/saleor/graphql/menu/mutations.py\n@@ -3,22 +3,13 @@\n \n from ...menu import models\n from ..core.mutations import BaseMutation, ModelDeleteMutation, ModelMutation\n+from ..product.types import Category, Collection\n+from ..page.types import Page\n from .types import Menu\n \n \n-class MenuInput(graphene.InputObjectType):\n-    name = graphene.String(description='Name of the menu.')\n-\n-\n class MenuItemInput(graphene.InputObjectType):\n-    menu = graphene.ID(\n-        description='Menu to which item belongs to.', name='menu')\n     name = graphene.String(description='Name of the menu item.')\n-    parent = graphene.ID(\n-        description='''\n-        ID of the parent menu. If empty, menu will be top level\n-        menu.''',\n-        name='parent')\n     url = graphene.String(description='URL of the pointed item.')\n     category = graphene.ID(\n         description='Category to which item points.', name='category')\n@@ -28,9 +19,28 @@\n         description='Page to which item points.', name='page')\n \n \n+class MenuItemCreateInput(MenuItemInput):\n+    menu = graphene.ID(\n+        description='Menu to which item belongs to.', name='menu')\n+    parent = graphene.ID(\n+        description='''\n+        ID of the parent menu. If empty, menu will be top level\n+        menu.''',\n+        name='parent')\n+\n+\n+class MenuInput(graphene.InputObjectType):\n+    name = graphene.String(description='Name of the menu.')\n+\n+\n+class MenuCreateInput(MenuInput):\n+    items = graphene.List(\n+        MenuItemInput, description='List of menu items.')\n+\n+\n class MenuCreate(ModelMutation):\n     class Arguments:\n-        input = MenuInput(\n+        input = MenuCreateInput(\n             required=True,\n             description='Fields required to create a menu.')\n \n@@ -42,6 +52,45 @@\n     def user_is_allowed(cls, user, input):\n         return user.has_perm('menu.manage_menus')\n \n+    @classmethod\n+    def clean_input(cls, info, instance, input, errors):\n+        cleaned_input = super().clean_input(info, instance, input, errors)\n+        items = []\n+        for item in cleaned_input.get('items', []):\n+            category = item.get('category')\n+            collection = item.get('collection')\n+            page = item.get('page')\n+            url = item.get('url')\n+            if len([i for i in [category, collection, page, url] if i]) > 1:\n+                cls.add_error(\n+                    errors, 'items', 'More than one item provided.')\n+            else:\n+                if category:\n+                    category = cls.get_node_or_error(\n+                        info, category, errors, 'items', only_type=Category)\n+                    item['category'] = category\n+                elif collection:\n+                    collection = cls.get_node_or_error(\n+                        info, collection, errors, 'items',\n+                        only_type=Collection)\n+                    item['collection'] = collection\n+                elif page:\n+                    page = cls.get_node_or_error(\n+                        info, page, errors, 'items', only_type=Page)\n+                    item['page'] = page\n+                elif not url:\n+                    cls.add_error(errors, 'items', 'No menu item provided.')\n+                items.append(item)\n+        cleaned_input['items'] = items\n+        return cleaned_input\n+\n+    @classmethod\n+    def _save_m2m(cls, info, instance, cleaned_data):\n+        super()._save_m2m(info, instance, cleaned_data)\n+        items = cleaned_data.get('items', [])\n+        for item in items:\n+            instance.items.create(**item)\n+\n \n class MenuUpdate(ModelMutation):\n     class Arguments:\n@@ -76,7 +125,7 @@\n \n class MenuItemCreate(ModelMutation):\n     class Arguments:\n-        input = MenuItemInput(\n+        input = MenuItemCreateInput(\n             required=True,\n             description=\"\"\"Fields required to update a menu item.\n             Only one of 'url', 'category', 'page', 'collection' is allowed\n", "issue": "Create menu items in menuCreate mutation\n### What I'm trying to achieve\r\nIt would be very handy if API user would be able to create the first level of menu items at the menu creation moment. This way we could reduce the number of database queries needed to create a menu and speed up the whole process. \r\n\r\n### Describe a proposed solution\r\nChange `MenuInput` signature to\r\n```\r\ntype MenuInput {\r\n  name: String!\r\n  items: [MenuItemCreate!]\r\n}\r\n```\r\n\n", "before_files": [{"content": "import graphene\nfrom graphql_jwt.decorators import permission_required\n\nfrom ...menu import models\nfrom ..core.mutations import BaseMutation, ModelDeleteMutation, ModelMutation\nfrom .types import Menu\n\n\nclass MenuInput(graphene.InputObjectType):\n    name = graphene.String(description='Name of the menu.')\n\n\nclass MenuItemInput(graphene.InputObjectType):\n    menu = graphene.ID(\n        description='Menu to which item belongs to.', name='menu')\n    name = graphene.String(description='Name of the menu item.')\n    parent = graphene.ID(\n        description='''\n        ID of the parent menu. If empty, menu will be top level\n        menu.''',\n        name='parent')\n    url = graphene.String(description='URL of the pointed item.')\n    category = graphene.ID(\n        description='Category to which item points.', name='category')\n    collection = graphene.ID(\n        description='Collection to which item points.', name='collection')\n    page = graphene.ID(\n        description='Page to which item points.', name='page')\n\n\nclass MenuCreate(ModelMutation):\n    class Arguments:\n        input = MenuInput(\n            required=True,\n            description='Fields required to create a menu.')\n\n    class Meta:\n        description = 'Creates a new Menu'\n        model = models.Menu\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass MenuUpdate(ModelMutation):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu to update.')\n        input = MenuInput(\n            required=True,\n            description='Fields required to update a menu.')\n\n    class Meta:\n        description = 'Updates a menu.'\n        model = models.Menu\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass MenuDelete(ModelDeleteMutation):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu to delete.')\n\n    class Meta:\n        description = 'Deletes a menu.'\n        model = models.Menu\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass MenuItemCreate(ModelMutation):\n    class Arguments:\n        input = MenuItemInput(\n            required=True,\n            description=\"\"\"Fields required to update a menu item.\n            Only one of 'url', 'category', 'page', 'collection' is allowed\n            per item\"\"\")\n\n    class Meta:\n        description = 'Creates a new Menu'\n        model = models.MenuItem\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n    @classmethod\n    def clean_input(cls, info, instance, input, errors):\n        cleaned_input = super().clean_input(info, instance, input, errors)\n        items = [\n            cleaned_input.get('page'), cleaned_input.get('collection'),\n            cleaned_input.get('url'), cleaned_input.get('category')]\n        items = [item for item in items if item is not None]\n        if len(items) > 1:\n            cls.add_error(\n                errors=errors,\n                field='items', message='More than one item provided.')\n        return cleaned_input\n\n\nclass MenuItemUpdate(MenuItemCreate):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu item to update.')\n        input = MenuItemInput(\n            required=True,\n            description=\"\"\"Fields required to update a menu item.\n            Only one of 'url', 'category', 'page', 'collection' is allowed\n            per item\"\"\")\n\n    class Meta:\n        description = 'Updates a menu item.'\n        model = models.MenuItem\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n    @classmethod\n    def construct_instance(cls, instance, cleaned_data):\n        # Only one item can be assigned per menu item\n        instance.page = None\n        instance.collection = None\n        instance.category = None\n        instance.url = None\n        return super().construct_instance(instance, cleaned_data)\n\n\nclass MenuItemDelete(ModelDeleteMutation):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu item to delete.')\n\n    class Meta:\n        description = 'Deletes a menu item.'\n        model = models.MenuItem\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass NavigationType(graphene.Enum):\n    MAIN = 'main'\n    SECONDARY = 'secondary'\n\n    @property\n    def description(self):\n        if self == NavigationType.MAIN:\n            return 'Main storefront\\'s navigation.'\n        return 'Secondary storefront\\'s navigation.'\n\n\nclass AssignNavigation(BaseMutation):\n    menu = graphene.Field(Menu, description='Assigned navigation menu.')\n\n    class Arguments:\n        menu = graphene.ID(\n            description='ID of the menu.')\n        navigation_type = NavigationType(\n            description='Type of the navigation bar to assign the menu to.',\n            required=True)\n\n    class Meta:\n        description = 'Assigns storefront\\'s navigation menus.'\n\n    @classmethod\n    @permission_required(['menu.manage_menus', 'site.manage_settings'])\n    def mutate(cls, root, info, navigation_type, menu=None):\n        errors = []\n        site_settings = info.context.site.settings\n        if menu is not None:\n            menu = cls.get_node_or_error(\n                info, menu, errors=errors, field='menu')\n        if not errors:\n            if navigation_type == NavigationType.MAIN:\n                site_settings.top_menu = menu\n                site_settings.save(update_fields=['top_menu'])\n            elif navigation_type == NavigationType.SECONDARY:\n                site_settings.bottom_menu = menu\n                site_settings.save(update_fields=['bottom_menu'])\n            else:\n                raise AssertionError(\n                    'Unknown navigation type: %s' % navigation_type)\n        return AssignNavigation(menu=menu, errors=errors)\n", "path": "saleor/graphql/menu/mutations.py"}], "after_files": [{"content": "import graphene\nfrom graphql_jwt.decorators import permission_required\n\nfrom ...menu import models\nfrom ..core.mutations import BaseMutation, ModelDeleteMutation, ModelMutation\nfrom ..product.types import Category, Collection\nfrom ..page.types import Page\nfrom .types import Menu\n\n\nclass MenuItemInput(graphene.InputObjectType):\n    name = graphene.String(description='Name of the menu item.')\n    url = graphene.String(description='URL of the pointed item.')\n    category = graphene.ID(\n        description='Category to which item points.', name='category')\n    collection = graphene.ID(\n        description='Collection to which item points.', name='collection')\n    page = graphene.ID(\n        description='Page to which item points.', name='page')\n\n\nclass MenuItemCreateInput(MenuItemInput):\n    menu = graphene.ID(\n        description='Menu to which item belongs to.', name='menu')\n    parent = graphene.ID(\n        description='''\n        ID of the parent menu. If empty, menu will be top level\n        menu.''',\n        name='parent')\n\n\nclass MenuInput(graphene.InputObjectType):\n    name = graphene.String(description='Name of the menu.')\n\n\nclass MenuCreateInput(MenuInput):\n    items = graphene.List(\n        MenuItemInput, description='List of menu items.')\n\n\nclass MenuCreate(ModelMutation):\n    class Arguments:\n        input = MenuCreateInput(\n            required=True,\n            description='Fields required to create a menu.')\n\n    class Meta:\n        description = 'Creates a new Menu'\n        model = models.Menu\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n    @classmethod\n    def clean_input(cls, info, instance, input, errors):\n        cleaned_input = super().clean_input(info, instance, input, errors)\n        items = []\n        for item in cleaned_input.get('items', []):\n            category = item.get('category')\n            collection = item.get('collection')\n            page = item.get('page')\n            url = item.get('url')\n            if len([i for i in [category, collection, page, url] if i]) > 1:\n                cls.add_error(\n                    errors, 'items', 'More than one item provided.')\n            else:\n                if category:\n                    category = cls.get_node_or_error(\n                        info, category, errors, 'items', only_type=Category)\n                    item['category'] = category\n                elif collection:\n                    collection = cls.get_node_or_error(\n                        info, collection, errors, 'items',\n                        only_type=Collection)\n                    item['collection'] = collection\n                elif page:\n                    page = cls.get_node_or_error(\n                        info, page, errors, 'items', only_type=Page)\n                    item['page'] = page\n                elif not url:\n                    cls.add_error(errors, 'items', 'No menu item provided.')\n                items.append(item)\n        cleaned_input['items'] = items\n        return cleaned_input\n\n    @classmethod\n    def _save_m2m(cls, info, instance, cleaned_data):\n        super()._save_m2m(info, instance, cleaned_data)\n        items = cleaned_data.get('items', [])\n        for item in items:\n            instance.items.create(**item)\n\n\nclass MenuUpdate(ModelMutation):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu to update.')\n        input = MenuInput(\n            required=True,\n            description='Fields required to update a menu.')\n\n    class Meta:\n        description = 'Updates a menu.'\n        model = models.Menu\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass MenuDelete(ModelDeleteMutation):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu to delete.')\n\n    class Meta:\n        description = 'Deletes a menu.'\n        model = models.Menu\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass MenuItemCreate(ModelMutation):\n    class Arguments:\n        input = MenuItemCreateInput(\n            required=True,\n            description=\"\"\"Fields required to update a menu item.\n            Only one of 'url', 'category', 'page', 'collection' is allowed\n            per item\"\"\")\n\n    class Meta:\n        description = 'Creates a new Menu'\n        model = models.MenuItem\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n    @classmethod\n    def clean_input(cls, info, instance, input, errors):\n        cleaned_input = super().clean_input(info, instance, input, errors)\n        items = [\n            cleaned_input.get('page'), cleaned_input.get('collection'),\n            cleaned_input.get('url'), cleaned_input.get('category')]\n        items = [item for item in items if item is not None]\n        if len(items) > 1:\n            cls.add_error(\n                errors=errors,\n                field='items', message='More than one item provided.')\n        return cleaned_input\n\n\nclass MenuItemUpdate(MenuItemCreate):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu item to update.')\n        input = MenuItemInput(\n            required=True,\n            description=\"\"\"Fields required to update a menu item.\n            Only one of 'url', 'category', 'page', 'collection' is allowed\n            per item\"\"\")\n\n    class Meta:\n        description = 'Updates a menu item.'\n        model = models.MenuItem\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n    @classmethod\n    def construct_instance(cls, instance, cleaned_data):\n        # Only one item can be assigned per menu item\n        instance.page = None\n        instance.collection = None\n        instance.category = None\n        instance.url = None\n        return super().construct_instance(instance, cleaned_data)\n\n\nclass MenuItemDelete(ModelDeleteMutation):\n    class Arguments:\n        id = graphene.ID(\n            required=True, description='ID of a menu item to delete.')\n\n    class Meta:\n        description = 'Deletes a menu item.'\n        model = models.MenuItem\n\n    @classmethod\n    def user_is_allowed(cls, user, input):\n        return user.has_perm('menu.manage_menus')\n\n\nclass NavigationType(graphene.Enum):\n    MAIN = 'main'\n    SECONDARY = 'secondary'\n\n    @property\n    def description(self):\n        if self == NavigationType.MAIN:\n            return 'Main storefront\\'s navigation.'\n        return 'Secondary storefront\\'s navigation.'\n\n\nclass AssignNavigation(BaseMutation):\n    menu = graphene.Field(Menu, description='Assigned navigation menu.')\n\n    class Arguments:\n        menu = graphene.ID(\n            description='ID of the menu.')\n        navigation_type = NavigationType(\n            description='Type of the navigation bar to assign the menu to.',\n            required=True)\n\n    class Meta:\n        description = 'Assigns storefront\\'s navigation menus.'\n\n    @classmethod\n    @permission_required(['menu.manage_menus', 'site.manage_settings'])\n    def mutate(cls, root, info, navigation_type, menu=None):\n        errors = []\n        site_settings = info.context.site.settings\n        if menu is not None:\n            menu = cls.get_node_or_error(\n                info, menu, errors=errors, field='menu')\n        if not errors:\n            if navigation_type == NavigationType.MAIN:\n                site_settings.top_menu = menu\n                site_settings.save(update_fields=['top_menu'])\n            elif navigation_type == NavigationType.SECONDARY:\n                site_settings.bottom_menu = menu\n                site_settings.save(update_fields=['bottom_menu'])\n            else:\n                raise AssertionError(\n                    'Unknown navigation type: %s' % navigation_type)\n        return AssignNavigation(menu=menu, errors=errors)\n", "path": "saleor/graphql/menu/mutations.py"}]}