We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[FR] :checkered_flag: Simple API endpoint to change user metadata
### Please verify that this feature request has NOT been suggested before.

- [X] I checked and didn't find similar feature request

### Problem statement

we currently provide an API endpoint for editing user data via a form. Fetching and changing endpoints are different and take different parameters.

### Suggested solution

I think it would be better to provide a RetrieveUpdateAPI endpoint under `/api/user/me` (somewhat a convention). This endpoint could provide and patch all the metadata for a user.

### Describe alternatives you've considered

N/A

### Examples of other systems

_No response_

### Do you want to develop this?

- [X] I want to develop this.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `InvenTree/users/api.py`
Content:
```
1 """DRF API definition for the 'users' app"""
2 
3 from django.contrib.auth.models import User
4 from django.core.exceptions import ObjectDoesNotExist
5 from django.urls import include, path, re_path
6 
7 from django_filters.rest_framework import DjangoFilterBackend
8 from rest_framework import filters, permissions, status
9 from rest_framework.authtoken.models import Token
10 from rest_framework.response import Response
11 from rest_framework.views import APIView
12 
13 from InvenTree.mixins import ListAPI, RetrieveAPI
14 from InvenTree.serializers import UserSerializer
15 from users.models import Owner, RuleSet, check_user_role
16 from users.serializers import OwnerSerializer
17 
18 
19 class OwnerList(ListAPI):
20     """List API endpoint for Owner model.
21 
22     Cannot create.
23     """
24 
25     queryset = Owner.objects.all()
26     serializer_class = OwnerSerializer
27 
28     def filter_queryset(self, queryset):
29         """Implement text search for the "owner" model.
30 
31         Note that an "owner" can be either a group, or a user,
32         so we cannot do a direct text search.
33 
34         A "hack" here is to post-process the queryset and simply
35         remove any values which do not match.
36 
37         It is not necessarily "efficient" to do it this way,
38         but until we determine a better way, this is what we have...
39         """
40         search_term = str(self.request.query_params.get('search', '')).lower()
41 
42         queryset = super().filter_queryset(queryset)
43 
44         if not search_term:
45             return queryset
46 
47         results = []
48 
49         # Extract search term f
50 
51         for result in queryset.all():
52             if search_term in result.name().lower():
53                 results.append(result)
54 
55         return results
56 
57 
58 class OwnerDetail(RetrieveAPI):
59     """Detail API endpoint for Owner model.
60 
61     Cannot edit or delete
62     """
63 
64     queryset = Owner.objects.all()
65     serializer_class = OwnerSerializer
66 
67 
68 class RoleDetails(APIView):
69     """API endpoint which lists the available role permissions for the current user.
70 
71     (Requires authentication)
72     """
73 
74     permission_classes = [
75         permissions.IsAuthenticated
76     ]
77 
78     def get(self, request, *args, **kwargs):
79         """Return the list of roles / permissions available to the current user"""
80         user = request.user
81 
82         roles = {}
83 
84         for ruleset in RuleSet.RULESET_CHOICES:
85 
86             role, text = ruleset
87 
88             permissions = []
89 
90             for permission in RuleSet.RULESET_PERMISSIONS:
91                 if check_user_role(user, role, permission):
92 
93                     permissions.append(permission)
94 
95             if len(permissions) > 0:
96                 roles[role] = permissions
97             else:
98                 roles[role] = None  # pragma: no cover
99 
100         data = {
101             'user': user.pk,
102             'username': user.username,
103             'roles': roles,
104             'is_staff': user.is_staff,
105             'is_superuser': user.is_superuser,
106         }
107 
108         return Response(data)
109 
110 
111 class UserDetail(RetrieveAPI):
112     """Detail endpoint for a single user."""
113 
114     queryset = User.objects.all()
115     serializer_class = UserSerializer
116     permission_classes = (permissions.IsAuthenticated,)
117 
118 
119 class UserList(ListAPI):
120     """List endpoint for detail on all users."""
121 
122     queryset = User.objects.all()
123     serializer_class = UserSerializer
124     permission_classes = (permissions.IsAuthenticated,)
125 
126     filter_backends = [
127         DjangoFilterBackend,
128         filters.SearchFilter,
129     ]
130 
131     search_fields = [
132         'first_name',
133         'last_name',
134         'username',
135     ]
136 
137 
138 class GetAuthToken(APIView):
139     """Return authentication token for an authenticated user."""
140 
141     permission_classes = [
142         permissions.IsAuthenticated,
143     ]
144 
145     def get(self, request, *args, **kwargs):
146         """Return an API token if the user is authenticated
147 
148         - If the user already has a token, return it
149         - Otherwise, create a new token
150         """
151         if request.user.is_authenticated:
152             # Get the user token (or create one if it does not exist)
153             token, created = Token.objects.get_or_create(user=request.user)
154             return Response({
155                 'token': token.key,
156             })
157 
158     def delete(self, request):
159         """User has requested deletion of API token"""
160         try:
161             request.user.auth_token.delete()
162             return Response({"success": "Successfully logged out."},
163                             status=status.HTTP_202_ACCEPTED)
164         except (AttributeError, ObjectDoesNotExist):
165             return Response({"error": "Bad request"},
166                             status=status.HTTP_400_BAD_REQUEST)
167 
168 
169 user_urls = [
170 
171     re_path(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'),
172     re_path(r'token/?$', GetAuthToken.as_view(), name='api-token'),
173 
174     re_path(r'^owner/', include([
175         path('<int:pk>/', OwnerDetail.as_view(), name='api-owner-detail'),
176         re_path(r'^.*$', OwnerList.as_view(), name='api-owner-list'),
177     ])),
178 
179     re_path(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='user-detail'),
180     path('', UserList.as_view()),
181 ]
182
```
--- 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/InvenTree/users/api.py b/InvenTree/users/api.py
--- a/InvenTree/users/api.py
+++ b/InvenTree/users/api.py
@@ -10,7 +10,7 @@
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
-from InvenTree.mixins import ListAPI, RetrieveAPI
+from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateAPI
 from InvenTree.serializers import UserSerializer
 from users.models import Owner, RuleSet, check_user_role
 from users.serializers import OwnerSerializer
@@ -116,6 +116,14 @@
     permission_classes = (permissions.IsAuthenticated,)
 
 
+class MeUserDetail(RetrieveUpdateAPI, UserDetail):
+    """Detail endpoint for current user."""
+
+    def get_object(self):
+        """Always return the current user object"""
+        return self.request.user
+
+
 class UserList(ListAPI):
     """List endpoint for detail on all users."""
 
@@ -170,6 +178,7 @@
 
     re_path(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'),
     re_path(r'token/?$', GetAuthToken.as_view(), name='api-token'),
+    re_path(r'^me/', MeUserDetail.as_view(), name='api-user-me'),
 
     re_path(r'^owner/', include([
         path('<int:pk>/', OwnerDetail.as_view(), name='api-owner-detail'),

{"golden_diff": "diff --git a/InvenTree/users/api.py b/InvenTree/users/api.py\n--- a/InvenTree/users/api.py\n+++ b/InvenTree/users/api.py\n@@ -10,7 +10,7 @@\n from rest_framework.response import Response\n from rest_framework.views import APIView\n \n-from InvenTree.mixins import ListAPI, RetrieveAPI\n+from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateAPI\n from InvenTree.serializers import UserSerializer\n from users.models import Owner, RuleSet, check_user_role\n from users.serializers import OwnerSerializer\n@@ -116,6 +116,14 @@\n     permission_classes = (permissions.IsAuthenticated,)\n \n \n+class MeUserDetail(RetrieveUpdateAPI, UserDetail):\n+    \"\"\"Detail endpoint for current user.\"\"\"\n+\n+    def get_object(self):\n+        \"\"\"Always return the current user object\"\"\"\n+        return self.request.user\n+\n+\n class UserList(ListAPI):\n     \"\"\"List endpoint for detail on all users.\"\"\"\n \n@@ -170,6 +178,7 @@\n \n     re_path(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'),\n     re_path(r'token/?$', GetAuthToken.as_view(), name='api-token'),\n+    re_path(r'^me/', MeUserDetail.as_view(), name='api-user-me'),\n \n     re_path(r'^owner/', include([\n         path('<int:pk>/', OwnerDetail.as_view(), name='api-owner-detail'),\n", "issue": "[FR] :checkered_flag: Simple API endpoint to change user metadata\n### Please verify that this feature request has NOT been suggested before.\n\n- [X] I checked and didn't find similar feature request\n\n### Problem statement\n\nwe currently provide an API endpoint for editing user data via a form. Fetching and changing endpoints are different and take different parameters.\n\n### Suggested solution\n\nI think it would be better to provide a RetrieveUpdateAPI endpoint under `/api/user/me` (somewhat a convention). This endpoint could provide and patch all the metadata for a user.\n\n### Describe alternatives you've considered\n\nN/A\n\n### Examples of other systems\n\n_No response_\n\n### Do you want to develop this?\n\n- [X] I want to develop this.\n", "before_files": [{"content": "\"\"\"DRF API definition for the 'users' app\"\"\"\n\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.urls import include, path, re_path\n\nfrom django_filters.rest_framework import DjangoFilterBackend\nfrom rest_framework import filters, permissions, status\nfrom rest_framework.authtoken.models import Token\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nfrom InvenTree.mixins import ListAPI, RetrieveAPI\nfrom InvenTree.serializers import UserSerializer\nfrom users.models import Owner, RuleSet, check_user_role\nfrom users.serializers import OwnerSerializer\n\n\nclass OwnerList(ListAPI):\n    \"\"\"List API endpoint for Owner model.\n\n    Cannot create.\n    \"\"\"\n\n    queryset = Owner.objects.all()\n    serializer_class = OwnerSerializer\n\n    def filter_queryset(self, queryset):\n        \"\"\"Implement text search for the \"owner\" model.\n\n        Note that an \"owner\" can be either a group, or a user,\n        so we cannot do a direct text search.\n\n        A \"hack\" here is to post-process the queryset and simply\n        remove any values which do not match.\n\n        It is not necessarily \"efficient\" to do it this way,\n        but until we determine a better way, this is what we have...\n        \"\"\"\n        search_term = str(self.request.query_params.get('search', '')).lower()\n\n        queryset = super().filter_queryset(queryset)\n\n        if not search_term:\n            return queryset\n\n        results = []\n\n        # Extract search term f\n\n        for result in queryset.all():\n            if search_term in result.name().lower():\n                results.append(result)\n\n        return results\n\n\nclass OwnerDetail(RetrieveAPI):\n    \"\"\"Detail API endpoint for Owner model.\n\n    Cannot edit or delete\n    \"\"\"\n\n    queryset = Owner.objects.all()\n    serializer_class = OwnerSerializer\n\n\nclass RoleDetails(APIView):\n    \"\"\"API endpoint which lists the available role permissions for the current user.\n\n    (Requires authentication)\n    \"\"\"\n\n    permission_classes = [\n        permissions.IsAuthenticated\n    ]\n\n    def get(self, request, *args, **kwargs):\n        \"\"\"Return the list of roles / permissions available to the current user\"\"\"\n        user = request.user\n\n        roles = {}\n\n        for ruleset in RuleSet.RULESET_CHOICES:\n\n            role, text = ruleset\n\n            permissions = []\n\n            for permission in RuleSet.RULESET_PERMISSIONS:\n                if check_user_role(user, role, permission):\n\n                    permissions.append(permission)\n\n            if len(permissions) > 0:\n                roles[role] = permissions\n            else:\n                roles[role] = None  # pragma: no cover\n\n        data = {\n            'user': user.pk,\n            'username': user.username,\n            'roles': roles,\n            'is_staff': user.is_staff,\n            'is_superuser': user.is_superuser,\n        }\n\n        return Response(data)\n\n\nclass UserDetail(RetrieveAPI):\n    \"\"\"Detail endpoint for a single user.\"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    permission_classes = (permissions.IsAuthenticated,)\n\n\nclass UserList(ListAPI):\n    \"\"\"List endpoint for detail on all users.\"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    permission_classes = (permissions.IsAuthenticated,)\n\n    filter_backends = [\n        DjangoFilterBackend,\n        filters.SearchFilter,\n    ]\n\n    search_fields = [\n        'first_name',\n        'last_name',\n        'username',\n    ]\n\n\nclass GetAuthToken(APIView):\n    \"\"\"Return authentication token for an authenticated user.\"\"\"\n\n    permission_classes = [\n        permissions.IsAuthenticated,\n    ]\n\n    def get(self, request, *args, **kwargs):\n        \"\"\"Return an API token if the user is authenticated\n\n        - If the user already has a token, return it\n        - Otherwise, create a new token\n        \"\"\"\n        if request.user.is_authenticated:\n            # Get the user token (or create one if it does not exist)\n            token, created = Token.objects.get_or_create(user=request.user)\n            return Response({\n                'token': token.key,\n            })\n\n    def delete(self, request):\n        \"\"\"User has requested deletion of API token\"\"\"\n        try:\n            request.user.auth_token.delete()\n            return Response({\"success\": \"Successfully logged out.\"},\n                            status=status.HTTP_202_ACCEPTED)\n        except (AttributeError, ObjectDoesNotExist):\n            return Response({\"error\": \"Bad request\"},\n                            status=status.HTTP_400_BAD_REQUEST)\n\n\nuser_urls = [\n\n    re_path(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'),\n    re_path(r'token/?$', GetAuthToken.as_view(), name='api-token'),\n\n    re_path(r'^owner/', include([\n        path('<int:pk>/', OwnerDetail.as_view(), name='api-owner-detail'),\n        re_path(r'^.*$', OwnerList.as_view(), name='api-owner-list'),\n    ])),\n\n    re_path(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='user-detail'),\n    path('', UserList.as_view()),\n]\n", "path": "InvenTree/users/api.py"}], "after_files": [{"content": "\"\"\"DRF API definition for the 'users' app\"\"\"\n\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.urls import include, path, re_path\n\nfrom django_filters.rest_framework import DjangoFilterBackend\nfrom rest_framework import filters, permissions, status\nfrom rest_framework.authtoken.models import Token\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nfrom InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateAPI\nfrom InvenTree.serializers import UserSerializer\nfrom users.models import Owner, RuleSet, check_user_role\nfrom users.serializers import OwnerSerializer\n\n\nclass OwnerList(ListAPI):\n    \"\"\"List API endpoint for Owner model.\n\n    Cannot create.\n    \"\"\"\n\n    queryset = Owner.objects.all()\n    serializer_class = OwnerSerializer\n\n    def filter_queryset(self, queryset):\n        \"\"\"Implement text search for the \"owner\" model.\n\n        Note that an \"owner\" can be either a group, or a user,\n        so we cannot do a direct text search.\n\n        A \"hack\" here is to post-process the queryset and simply\n        remove any values which do not match.\n\n        It is not necessarily \"efficient\" to do it this way,\n        but until we determine a better way, this is what we have...\n        \"\"\"\n        search_term = str(self.request.query_params.get('search', '')).lower()\n\n        queryset = super().filter_queryset(queryset)\n\n        if not search_term:\n            return queryset\n\n        results = []\n\n        # Extract search term f\n\n        for result in queryset.all():\n            if search_term in result.name().lower():\n                results.append(result)\n\n        return results\n\n\nclass OwnerDetail(RetrieveAPI):\n    \"\"\"Detail API endpoint for Owner model.\n\n    Cannot edit or delete\n    \"\"\"\n\n    queryset = Owner.objects.all()\n    serializer_class = OwnerSerializer\n\n\nclass RoleDetails(APIView):\n    \"\"\"API endpoint which lists the available role permissions for the current user.\n\n    (Requires authentication)\n    \"\"\"\n\n    permission_classes = [\n        permissions.IsAuthenticated\n    ]\n\n    def get(self, request, *args, **kwargs):\n        \"\"\"Return the list of roles / permissions available to the current user\"\"\"\n        user = request.user\n\n        roles = {}\n\n        for ruleset in RuleSet.RULESET_CHOICES:\n\n            role, text = ruleset\n\n            permissions = []\n\n            for permission in RuleSet.RULESET_PERMISSIONS:\n                if check_user_role(user, role, permission):\n\n                    permissions.append(permission)\n\n            if len(permissions) > 0:\n                roles[role] = permissions\n            else:\n                roles[role] = None  # pragma: no cover\n\n        data = {\n            'user': user.pk,\n            'username': user.username,\n            'roles': roles,\n            'is_staff': user.is_staff,\n            'is_superuser': user.is_superuser,\n        }\n\n        return Response(data)\n\n\nclass UserDetail(RetrieveAPI):\n    \"\"\"Detail endpoint for a single user.\"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    permission_classes = (permissions.IsAuthenticated,)\n\n\nclass MeUserDetail(RetrieveUpdateAPI, UserDetail):\n    \"\"\"Detail endpoint for current user.\"\"\"\n\n    def get_object(self):\n        \"\"\"Always return the current user object\"\"\"\n        return self.request.user\n\n\nclass UserList(ListAPI):\n    \"\"\"List endpoint for detail on all users.\"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    permission_classes = (permissions.IsAuthenticated,)\n\n    filter_backends = [\n        DjangoFilterBackend,\n        filters.SearchFilter,\n    ]\n\n    search_fields = [\n        'first_name',\n        'last_name',\n        'username',\n    ]\n\n\nclass GetAuthToken(APIView):\n    \"\"\"Return authentication token for an authenticated user.\"\"\"\n\n    permission_classes = [\n        permissions.IsAuthenticated,\n    ]\n\n    def get(self, request, *args, **kwargs):\n        \"\"\"Return an API token if the user is authenticated\n\n        - If the user already has a token, return it\n        - Otherwise, create a new token\n        \"\"\"\n        if request.user.is_authenticated:\n            # Get the user token (or create one if it does not exist)\n            token, created = Token.objects.get_or_create(user=request.user)\n            return Response({\n                'token': token.key,\n            })\n\n    def delete(self, request):\n        \"\"\"User has requested deletion of API token\"\"\"\n        try:\n            request.user.auth_token.delete()\n            return Response({\"success\": \"Successfully logged out.\"},\n                            status=status.HTTP_202_ACCEPTED)\n        except (AttributeError, ObjectDoesNotExist):\n            return Response({\"error\": \"Bad request\"},\n                            status=status.HTTP_400_BAD_REQUEST)\n\n\nuser_urls = [\n\n    re_path(r'roles/?$', RoleDetails.as_view(), name='api-user-roles'),\n    re_path(r'token/?$', GetAuthToken.as_view(), name='api-token'),\n    re_path(r'^me/', MeUserDetail.as_view(), name='api-user-me'),\n\n    re_path(r'^owner/', include([\n        path('<int:pk>/', OwnerDetail.as_view(), name='api-owner-detail'),\n        re_path(r'^.*$', OwnerList.as_view(), name='api-owner-list'),\n    ])),\n\n    re_path(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='user-detail'),\n    path('', UserList.as_view()),\n]\n", "path": "InvenTree/users/api.py"}]}