We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
package_patch breaks uploaded resources
## CKAN version
2.10.4

## Describe the bug
When using package_patch to update a dataset, intending **not** to update an uploaded resource the resource filename appears to be lost

### Steps to reproduce
1. Create a dataset containing at least one uploaded file resource
2. Call package_patch to update the dataset, but for the resources only pass the resource ids like specified in the documentation 
(https://docs.ckan.org/en/2.10/api/#ckan.logic.action.patch.package_patch) like:
```
package_patch(**{'id':'b6cc8622-3334-4cdb-8960-e2a3c4269a8d', 
                 'description':'Updated description', 
                 'resources':[{'id':'a97b8889-5efb-440c-b6ad-fa9a9e4d7659'},
                              {'id':'bdbb977f-9faa-4715-88d3-c5e9042e69a4', 
                               'description':'Updated resource description'}]})
```
3. Browse to the updated resource, the download link is missing the file name probably replaced by ___ and the mimetype is lost


### Expected behavior
The resources should be unchanged when no changes are requested

### Additional details
I think part of this issue arises out of url being used to store the filename. It may be cleaner to just add filename as another field in uploaded resources which could also make renaming uploaded files easier.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ckan/logic/action/patch.py`
Content:
```
1 # encoding: utf-8
2 
3 '''API functions for partial updates of existing data in CKAN'''
4 
5 from ckan.logic import (
6     get_action as _get_action,
7     check_access as _check_access,
8     get_or_bust as _get_or_bust,
9     fresh_context as _fresh_context
10 )
11 from ckan.types import Context, DataDict
12 from ckan.types.logic import ActionResult
13 
14 
15 def package_patch(
16         context: Context, data_dict: DataDict) -> ActionResult.PackagePatch:
17     '''Patch a dataset (package).
18 
19     :param id: the id or name of the dataset
20     :type id: string
21 
22     The difference between the update and patch methods is that the patch will
23     perform an update of the provided parameters, while leaving all other
24     parameters unchanged, whereas the update methods deletes all parameters
25     not explicitly provided in the data_dict.
26 
27     You are able to partially update and/or create resources with
28     package_patch. If you are updating existing resources be sure to provide
29     the resource id. Existing resources excluded from the package_patch
30     data_dict will be removed. Resources in the package data_dict without
31     an id will be treated as new resources and will be added. New resources
32     added with the patch method do not create the default views.
33 
34     You must be authorized to edit the dataset and the groups that it belongs
35     to.
36     '''
37     _check_access('package_patch', context, data_dict)
38 
39     show_context: Context = {
40         'model': context['model'],
41         'session': context['session'],
42         'user': context['user'],
43         'auth_user_obj': context['auth_user_obj'],
44         'ignore_auth': context.get('ignore_auth', False),
45         'for_update': True
46     }
47 
48     package_dict = _get_action('package_show')(
49         show_context,
50         {'id': _get_or_bust(data_dict, 'id')})
51 
52     patched = dict(package_dict)
53     patched.update(data_dict)
54     patched['id'] = package_dict['id']
55     return _get_action('package_update')(context, patched)
56 
57 
58 def resource_patch(context: Context,
59                    data_dict: DataDict) -> ActionResult.ResourcePatch:
60     '''Patch a resource
61 
62     :param id: the id of the resource
63     :type id: string
64 
65     The difference between the update and patch methods is that the patch will
66     perform an update of the provided parameters, while leaving all other
67     parameters unchanged, whereas the update methods deletes all parameters
68     not explicitly provided in the data_dict
69     '''
70     _check_access('resource_patch', context, data_dict)
71 
72     show_context: Context = _fresh_context(context)
73     show_context.update({'for_update': True})
74 
75     resource_dict = _get_action('resource_show')(
76         show_context,
77         {'id': _get_or_bust(data_dict, 'id')})
78 
79     patched = dict(resource_dict)
80     patched.update(data_dict)
81     return _get_action('resource_update')(context, patched)
82 
83 
84 def group_patch(context: Context,
85                 data_dict: DataDict) -> ActionResult.GroupPatch:
86     '''Patch a group
87 
88     :param id: the id or name of the group
89     :type id: string
90 
91     The difference between the update and patch methods is that the patch will
92     perform an update of the provided parameters, while leaving all other
93     parameters unchanged, whereas the update methods deletes all parameters
94     not explicitly provided in the data_dict
95     '''
96     _check_access('group_patch', context, data_dict)
97 
98     show_context: Context = _fresh_context(context)
99 
100     group_dict = _get_action('group_show')(
101         show_context,
102         {'id': _get_or_bust(data_dict, 'id')})
103 
104     patched = dict(group_dict)
105     patched.pop('display_name', None)
106     patched.update(data_dict)
107 
108     patch_context = context.copy()
109     patch_context['allow_partial_update'] = True
110     return _get_action('group_update')(patch_context, patched)
111 
112 
113 def organization_patch(
114         context: Context,
115         data_dict: DataDict) -> ActionResult.OrganizationPatch:
116     '''Patch an organization
117 
118     :param id: the id or name of the organization
119     :type id: string
120 
121     The difference between the update and patch methods is that the patch will
122     perform an update of the provided parameters, while leaving all other
123     parameters unchanged, whereas the update methods deletes all parameters
124     not explicitly provided in the data_dict
125     '''
126     _check_access('organization_patch', context, data_dict)
127 
128     show_context: Context = _fresh_context(context)
129 
130     organization_dict = _get_action('organization_show')(
131         show_context,
132         {'id': _get_or_bust(data_dict, 'id')})
133 
134     patched = dict(organization_dict)
135     patched.pop('display_name', None)
136     patched.update(data_dict)
137 
138     patch_context = context.copy()
139     patch_context['allow_partial_update'] = True
140     return _get_action('organization_update')(patch_context, patched)
141 
142 
143 def user_patch(context: Context,
144                data_dict: DataDict) -> ActionResult.UserPatch:
145     '''Patch a user
146 
147     :param id: the id or name of the user
148     :type id: string
149 
150     The difference between the update and patch methods is that the patch will
151     perform an update of the provided parameters, while leaving all other
152     parameters unchanged, whereas the update methods deletes all parameters
153     not explicitly provided in the data_dict
154     '''
155     _check_access('user_patch', context, data_dict)
156 
157     show_context: Context = _fresh_context(context)
158 
159     user_dict = _get_action('user_show')(
160         show_context,
161         {'id': _get_or_bust(data_dict, 'id')})
162 
163     patched = dict(user_dict)
164     patched.pop('display_name', None)
165     patched.update(data_dict)
166     return _get_action('user_update')(context, patched)
167
```
--- 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/ckan/logic/action/patch.py b/ckan/logic/action/patch.py
--- a/ckan/logic/action/patch.py
+++ b/ckan/logic/action/patch.py
@@ -24,12 +24,10 @@
     parameters unchanged, whereas the update methods deletes all parameters
     not explicitly provided in the data_dict.
 
-    You are able to partially update and/or create resources with
-    package_patch. If you are updating existing resources be sure to provide
-    the resource id. Existing resources excluded from the package_patch
-    data_dict will be removed. Resources in the package data_dict without
-    an id will be treated as new resources and will be added. New resources
-    added with the patch method do not create the default views.
+    To partially update resources or other metadata not at the top level
+    of a package use
+    :py:func:`~ckan.logic.action.update.package_revise` instead to maintain
+    existing nested values.
 
     You must be authorized to edit the dataset and the groups that it belongs
     to.

{"golden_diff": "diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py\n--- a/ckan/logic/action/patch.py\n+++ b/ckan/logic/action/patch.py\n@@ -24,12 +24,10 @@\n     parameters unchanged, whereas the update methods deletes all parameters\n     not explicitly provided in the data_dict.\n \n-    You are able to partially update and/or create resources with\n-    package_patch. If you are updating existing resources be sure to provide\n-    the resource id. Existing resources excluded from the package_patch\n-    data_dict will be removed. Resources in the package data_dict without\n-    an id will be treated as new resources and will be added. New resources\n-    added with the patch method do not create the default views.\n+    To partially update resources or other metadata not at the top level\n+    of a package use\n+    :py:func:`~ckan.logic.action.update.package_revise` instead to maintain\n+    existing nested values.\n \n     You must be authorized to edit the dataset and the groups that it belongs\n     to.\n", "issue": "package_patch breaks uploaded resources\n## CKAN version\r\n2.10.4\r\n\r\n## Describe the bug\r\nWhen using package_patch to update a dataset, intending **not** to update an uploaded resource the resource filename appears to be lost\r\n\r\n### Steps to reproduce\r\n1. Create a dataset containing at least one uploaded file resource\r\n2. Call package_patch to update the dataset, but for the resources only pass the resource ids like specified in the documentation \r\n(https://docs.ckan.org/en/2.10/api/#ckan.logic.action.patch.package_patch) like:\r\n```\r\npackage_patch(**{'id':'b6cc8622-3334-4cdb-8960-e2a3c4269a8d', \r\n                 'description':'Updated description', \r\n                 'resources':[{'id':'a97b8889-5efb-440c-b6ad-fa9a9e4d7659'},\r\n                              {'id':'bdbb977f-9faa-4715-88d3-c5e9042e69a4', \r\n                               'description':'Updated resource description'}]})\r\n```\r\n3. Browse to the updated resource, the download link is missing the file name probably replaced by ___ and the mimetype is lost\r\n\r\n\r\n### Expected behavior\r\nThe resources should be unchanged when no changes are requested\r\n\r\n### Additional details\r\nI think part of this issue arises out of url being used to store the filename. It may be cleaner to just add filename as another field in uploaded resources which could also make renaming uploaded files easier.\r\n\n", "before_files": [{"content": "# encoding: utf-8\n\n'''API functions for partial updates of existing data in CKAN'''\n\nfrom ckan.logic import (\n    get_action as _get_action,\n    check_access as _check_access,\n    get_or_bust as _get_or_bust,\n    fresh_context as _fresh_context\n)\nfrom ckan.types import Context, DataDict\nfrom ckan.types.logic import ActionResult\n\n\ndef package_patch(\n        context: Context, data_dict: DataDict) -> ActionResult.PackagePatch:\n    '''Patch a dataset (package).\n\n    :param id: the id or name of the dataset\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict.\n\n    You are able to partially update and/or create resources with\n    package_patch. If you are updating existing resources be sure to provide\n    the resource id. Existing resources excluded from the package_patch\n    data_dict will be removed. Resources in the package data_dict without\n    an id will be treated as new resources and will be added. New resources\n    added with the patch method do not create the default views.\n\n    You must be authorized to edit the dataset and the groups that it belongs\n    to.\n    '''\n    _check_access('package_patch', context, data_dict)\n\n    show_context: Context = {\n        'model': context['model'],\n        'session': context['session'],\n        'user': context['user'],\n        'auth_user_obj': context['auth_user_obj'],\n        'ignore_auth': context.get('ignore_auth', False),\n        'for_update': True\n    }\n\n    package_dict = _get_action('package_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(package_dict)\n    patched.update(data_dict)\n    patched['id'] = package_dict['id']\n    return _get_action('package_update')(context, patched)\n\n\ndef resource_patch(context: Context,\n                   data_dict: DataDict) -> ActionResult.ResourcePatch:\n    '''Patch a resource\n\n    :param id: the id of the resource\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('resource_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n    show_context.update({'for_update': True})\n\n    resource_dict = _get_action('resource_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(resource_dict)\n    patched.update(data_dict)\n    return _get_action('resource_update')(context, patched)\n\n\ndef group_patch(context: Context,\n                data_dict: DataDict) -> ActionResult.GroupPatch:\n    '''Patch a group\n\n    :param id: the id or name of the group\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('group_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n\n    group_dict = _get_action('group_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(group_dict)\n    patched.pop('display_name', None)\n    patched.update(data_dict)\n\n    patch_context = context.copy()\n    patch_context['allow_partial_update'] = True\n    return _get_action('group_update')(patch_context, patched)\n\n\ndef organization_patch(\n        context: Context,\n        data_dict: DataDict) -> ActionResult.OrganizationPatch:\n    '''Patch an organization\n\n    :param id: the id or name of the organization\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('organization_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n\n    organization_dict = _get_action('organization_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(organization_dict)\n    patched.pop('display_name', None)\n    patched.update(data_dict)\n\n    patch_context = context.copy()\n    patch_context['allow_partial_update'] = True\n    return _get_action('organization_update')(patch_context, patched)\n\n\ndef user_patch(context: Context,\n               data_dict: DataDict) -> ActionResult.UserPatch:\n    '''Patch a user\n\n    :param id: the id or name of the user\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('user_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n\n    user_dict = _get_action('user_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(user_dict)\n    patched.pop('display_name', None)\n    patched.update(data_dict)\n    return _get_action('user_update')(context, patched)\n", "path": "ckan/logic/action/patch.py"}], "after_files": [{"content": "# encoding: utf-8\n\n'''API functions for partial updates of existing data in CKAN'''\n\nfrom ckan.logic import (\n    get_action as _get_action,\n    check_access as _check_access,\n    get_or_bust as _get_or_bust,\n    fresh_context as _fresh_context\n)\nfrom ckan.types import Context, DataDict\nfrom ckan.types.logic import ActionResult\n\n\ndef package_patch(\n        context: Context, data_dict: DataDict) -> ActionResult.PackagePatch:\n    '''Patch a dataset (package).\n\n    :param id: the id or name of the dataset\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict.\n\n    To partially update resources or other metadata not at the top level\n    of a package use\n    :py:func:`~ckan.logic.action.update.package_revise` instead to maintain\n    existing nested values.\n\n    You must be authorized to edit the dataset and the groups that it belongs\n    to.\n    '''\n    _check_access('package_patch', context, data_dict)\n\n    show_context: Context = {\n        'model': context['model'],\n        'session': context['session'],\n        'user': context['user'],\n        'auth_user_obj': context['auth_user_obj'],\n        'ignore_auth': context.get('ignore_auth', False),\n        'for_update': True\n    }\n\n    package_dict = _get_action('package_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(package_dict)\n    patched.update(data_dict)\n    patched['id'] = package_dict['id']\n    return _get_action('package_update')(context, patched)\n\n\ndef resource_patch(context: Context,\n                   data_dict: DataDict) -> ActionResult.ResourcePatch:\n    '''Patch a resource\n\n    :param id: the id of the resource\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('resource_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n    show_context.update({'for_update': True})\n\n    resource_dict = _get_action('resource_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(resource_dict)\n    patched.update(data_dict)\n    return _get_action('resource_update')(context, patched)\n\n\ndef group_patch(context: Context,\n                data_dict: DataDict) -> ActionResult.GroupPatch:\n    '''Patch a group\n\n    :param id: the id or name of the group\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('group_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n\n    group_dict = _get_action('group_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(group_dict)\n    patched.pop('display_name', None)\n    patched.update(data_dict)\n\n    patch_context = context.copy()\n    patch_context['allow_partial_update'] = True\n    return _get_action('group_update')(patch_context, patched)\n\n\ndef organization_patch(\n        context: Context,\n        data_dict: DataDict) -> ActionResult.OrganizationPatch:\n    '''Patch an organization\n\n    :param id: the id or name of the organization\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('organization_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n\n    organization_dict = _get_action('organization_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(organization_dict)\n    patched.pop('display_name', None)\n    patched.update(data_dict)\n\n    patch_context = context.copy()\n    patch_context['allow_partial_update'] = True\n    return _get_action('organization_update')(patch_context, patched)\n\n\ndef user_patch(context: Context,\n               data_dict: DataDict) -> ActionResult.UserPatch:\n    '''Patch a user\n\n    :param id: the id or name of the user\n    :type id: string\n\n    The difference between the update and patch methods is that the patch will\n    perform an update of the provided parameters, while leaving all other\n    parameters unchanged, whereas the update methods deletes all parameters\n    not explicitly provided in the data_dict\n    '''\n    _check_access('user_patch', context, data_dict)\n\n    show_context: Context = _fresh_context(context)\n\n    user_dict = _get_action('user_show')(\n        show_context,\n        {'id': _get_or_bust(data_dict, 'id')})\n\n    patched = dict(user_dict)\n    patched.pop('display_name', None)\n    patched.update(data_dict)\n    return _get_action('user_update')(context, patched)\n", "path": "ckan/logic/action/patch.py"}]}