Rename operation ids

environments/review-front-wvff-cfe5gn/deployments/13838
wvffle 2022-09-25 13:57:22 +00:00 zatwierdzone przez Georg Krause
rodzic cbbf6c2c40
commit 68face201b
20 zmienionych plików z 14173 dodań i 23 usunięć

Wyświetl plik

@ -42,11 +42,18 @@ class CustomApplicationTokenExt(OpenApiAuthenticationExtension):
def custom_preprocessing_hook(endpoints):
filtered = []
# your modifications to the list of operations that are exposed in the schema
api_type = os.environ.get("API_TYPE", "v1")
for (path, path_regex, method, callback) in endpoints:
if path.startswith("/api/v1/providers"):
continue
if path.startswith("/api/v1/users/users"):
continue
if path.startswith(f"/api/{api_type}"):
filtered.append((path, path_regex, method, callback))
return filtered

Wyświetl plik

@ -99,7 +99,7 @@ CELERY_TASK_ALWAYS_EAGER = False
CSRF_TRUSTED_ORIGINS = [o for o in ALLOWED_HOSTS]
REST_FRAMEWORK["DEFAULT_SCHEMA_CLASS"] = "drf_spectacular.openapi.AutoSchema"
REST_FRAMEWORK["DEFAULT_SCHEMA_CLASS"] = "funkwhale_api.schema.CustomAutoSchema"
SPECTACULAR_SETTINGS = {
"TITLE": "Funkwhale API",
"DESCRIPTION": open("Readme.md", "r").read(),

Wyświetl plik

@ -1,6 +1,8 @@
from rest_framework import viewsets
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from funkwhale_api.common.permissions import ConditionalAuthentication
from funkwhale_api.favorites.models import TrackFavorite
@ -13,6 +15,7 @@ class ActivityViewSet(viewsets.GenericViewSet):
permission_classes = [ConditionalAuthentication]
queryset = TrackFavorite.objects.none()
@extend_schema(operation_id='get_activity')
def list(self, request, *args, **kwargs):
activity = utils.get_activity(user=request.user)
serializer = self.serializer_class(activity, many=True)

Wyświetl plik

@ -5,6 +5,8 @@ from rest_framework import permissions as rest_permissions
from rest_framework import response
from rest_framework import viewsets
from drf_spectacular.utils import extend_schema, extend_schema_view
from django import http
from django.db import transaction
from django.db.models import Count, Prefetch, Q, Sum
@ -43,6 +45,12 @@ class ChannelsMixin(object):
return super().dispatch(request, *args, **kwargs)
@extend_schema_view(
metedata_choices=extend_schema(operation_id='get_channel_metadata_choices'),
subscribe=extend_schema(operation_id='subscribe_channel'),
unsubscribe=extend_schema(operation_id='unsubscribe_channel'),
rss_subscribe=extend_schema(operation_id='subscribe_channel_rss'),
)
class ChannelViewSet(
ChannelsMixin,
MultipleLookupDetailMixin,
@ -322,6 +330,7 @@ class SubscriptionsViewSet(
qs = super().get_queryset()
return qs.filter(actor=self.request.user.actor)
@extend_schema(operation_id='get_all_subscriptions')
@decorators.action(methods=["get"], detail=False)
def all(self, request, *args, **kwargs):
"""

Wyświetl plik

@ -5,6 +5,8 @@ from rest_framework import exceptions
from rest_framework import response
from rest_framework import status
from drf_spectacular.utils import extend_schema
from . import filters
from . import models
from . import mutations as common_mutations
@ -87,6 +89,10 @@ def mutations_route(types):
)
return response.Response(serializer.data, status=status.HTTP_201_CREATED)
return decorators.action(
methods=["get", "post"], detail=True, required_scope="edits"
)(mutations)
return extend_schema(methods=['post'], responses=serializers.APIMutationSerializer())(
extend_schema(methods=['get'], responses=serializers.APIMutationSerializer(many=True))(
decorators.action(
methods=["get", "post"], detail=True, required_scope="edits"
)(mutations)
)
)

Wyświetl plik

@ -12,6 +12,8 @@ from rest_framework import response
from rest_framework import views
from rest_framework import viewsets
from drf_spectacular.utils import extend_schema
from config import plugins
from funkwhale_api.users.oauth import permissions as oauth_permissions
@ -78,6 +80,7 @@ class MutationViewSet(
return super().perform_destroy(instance)
@extend_schema(operation_id='approve_mutation')
@action(detail=True, methods=["post"])
@transaction.atomic
def approve(self, request, *args, **kwargs):
@ -107,6 +110,7 @@ class MutationViewSet(
)
return response.Response({}, status=200)
@extend_schema(operation_id='reject_mutation')
@action(detail=True, methods=["post"])
@transaction.atomic
def reject(self, request, *args, **kwargs):
@ -201,6 +205,7 @@ class AttachmentViewSet(
class TextPreviewView(views.APIView):
permission_classes = []
@extend_schema(operation_id='preview_text')
def post(self, request, *args, **kwargs):
payload = request.data
if "text" not in payload:
@ -273,6 +278,7 @@ class PluginViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
user.plugins.filter(code=kwargs["pk"]).delete()
return response.Response(status=204)
@extend_schema(operation_id='enable_plugin')
@action(detail=True, methods=["post"])
def enable(self, request, *args, **kwargs):
user = request.user
@ -281,6 +287,7 @@ class PluginViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
plugins.enable_conf(kwargs["pk"], True, user)
return response.Response({}, status=200)
@extend_schema(operation_id='disable_plugin')
@action(detail=True, methods=["post"])
def disable(self, request, *args, **kwargs):
user = request.user

Wyświetl plik

@ -2,6 +2,8 @@ from rest_framework import mixins, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, extend_schema_view
from django.db.models import Prefetch
from funkwhale_api.activity import record
@ -38,6 +40,7 @@ class TrackFavoriteViewSet(
return serializers.UserTrackFavoriteSerializer
return serializers.UserTrackFavoriteWriteSerializer
@extend_schema(operation_id='favorite_track')
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
@ -67,6 +70,7 @@ class TrackFavoriteViewSet(
favorite = models.TrackFavorite.add(track=track, user=self.request.user)
return favorite
@extend_schema(operation_id='unfavorite_track')
@action(methods=["delete", "post"], detail=False)
def remove(self, request, *args, **kwargs):
try:
@ -77,6 +81,7 @@ class TrackFavoriteViewSet(
favorite.delete()
return Response([], status=status.HTTP_204_NO_CONTENT)
@extend_schema(operation_id='get_all_favorite_tracks')
@action(methods=["get"], detail=False)
def all(self, request, *args, **kwargs):
"""

Wyświetl plik

@ -10,6 +10,8 @@ from rest_framework import permissions
from rest_framework import response
from rest_framework import viewsets
from drf_spectacular.utils import extend_schema, extend_schema_view
from funkwhale_api.common import preferences
from funkwhale_api.common import utils as common_utils
from funkwhale_api.common.permissions import ConditionalAuthentication
@ -38,6 +40,13 @@ def update_follow(follow, approved):
routes.outbox.dispatch({"type": "Reject"}, context={"follow": follow})
@extend_schema_view(
list=extend_schema(operation_id='get_federation_library_follows'),
create=extend_schema(operation_id='create_federation_library_follow'),
)
# NOTE: For some weird reason, @extend_schema_view doesn't work with `retrieve` and `destroy` methods.
@extend_schema(operation_id='get_federation_library_follow', methods=['get'])
@extend_schema(operation_id='delete_federation_library_follow', methods=['delete'])
class LibraryFollowViewSet(
mixins.CreateModelMixin,
mixins.ListModelMixin,
@ -77,6 +86,7 @@ class LibraryFollowViewSet(
context["actor"] = self.request.user.actor
return context
@extend_schema(operation_id='accept_federation_library_follow')
@decorators.action(methods=["post"], detail=True)
def accept(self, request, *args, **kwargs):
try:
@ -88,6 +98,7 @@ class LibraryFollowViewSet(
update_follow(follow, approved=True)
return response.Response(status=204)
@extend_schema(operation_id='reject_federation_library_follow')
@decorators.action(methods=["post"], detail=True)
def reject(self, request, *args, **kwargs):
try:
@ -100,6 +111,7 @@ class LibraryFollowViewSet(
update_follow(follow, approved=False)
return response.Response(status=204)
@extend_schema(operation_id='get_all_federation_library_follows')
@decorators.action(methods=["get"], detail=False)
def all(self, request, *args, **kwargs):
"""

Wyświetl plik

@ -5,6 +5,8 @@ from rest_framework import permissions
from rest_framework import response
from rest_framework import status
from drf_spectacular.utils import extend_schema
from funkwhale_api.common import utils as common_utils
from . import api_serializers
@ -42,8 +44,12 @@ def fetches_route():
serializer = api_serializers.FetchSerializer(fetch)
return response.Response(serializer.data, status=status.HTTP_201_CREATED)
return decorators.action(
methods=["get", "post"],
detail=True,
permission_classes=[permissions.IsAuthenticated],
)(fetches)
return extend_schema(methods=['post'], responses=api_serializers.FetchSerializer())(
extend_schema(methods=['get'], responses=api_serializers.FetchSerializer(many=True))(
decorators.action(
methods=["get", "post"],
detail=True,
permission_classes=[permissions.IsAuthenticated],
)(fetches)
)
)

Wyświetl plik

@ -53,6 +53,7 @@ class InstanceSettings(generics.GenericAPIView):
]
return api_preferences
@extend_schema(operation_id='get_instance_settings')
def get(self, request):
queryset = self.get_queryset()
data = GlobalPreferenceSerializer(queryset, many=True).data
@ -121,6 +122,7 @@ class SpaManifest(views.APIView):
permission_classes = []
authentication_classes = []
@extend_schema(operation_id='get_spa_manifest')
def get(self, request, *args, **kwargs):
existing_manifest = middleware.get_spa_file(
settings.FUNKWHALE_SPA_HTML_ROOT, "manifest.json"

Wyświetl plik

@ -1,6 +1,8 @@
from rest_framework import mixins, response, viewsets
from rest_framework import decorators as rest_decorators
from drf_spectacular.utils import extend_schema
from django.db import transaction
from django.db.models import Count, Prefetch, Q, Sum, OuterRef, Subquery
from django.db.models.functions import Coalesce, Length
@ -93,6 +95,7 @@ class ManageArtistViewSet(
required_scope = "instance:libraries"
ordering_fields = ["creation_date", "name"]
@extend_schema(operation_id='admin_get_library_artist_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
artist = self.get_object()
@ -135,6 +138,7 @@ class ManageAlbumViewSet(
required_scope = "instance:libraries"
ordering_fields = ["creation_date", "title", "release_date"]
@extend_schema(operation_id='admin_get_library_album_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
album = self.get_object()
@ -196,6 +200,7 @@ class ManageTrackViewSet(
"disc_number",
]
@extend_schema(operation_id='admin_get_track_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
track = self.get_object()
@ -257,6 +262,7 @@ class ManageLibraryViewSet(
filterset_class = filters.ManageLibraryFilterSet
required_scope = "instance:libraries"
@extend_schema(operation_id='admin_get_library_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
library = self.get_object()
@ -424,6 +430,7 @@ class ManageDomainViewSet(
domain.refresh_from_db()
return response.Response(domain.nodeinfo, status=200)
@extend_schema(operation_id='admin_get_federation_domain_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
domain = self.get_object()
@ -468,6 +475,7 @@ class ManageActorViewSet(
return obj
@extend_schema(operation_id='admin_get_account_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
obj = self.get_object()
@ -709,6 +717,7 @@ class ManageChannelViewSet(
required_scope = "instance:libraries"
ordering_fields = ["creation_date", "name"]
@extend_schema(operation_id='admin_get_channel_stats')
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
channel = self.get_object()

Wyświetl plik

@ -16,6 +16,8 @@ from rest_framework import views, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
import requests.exceptions
from funkwhale_api.common import decorators as common_decorators
@ -66,7 +68,9 @@ def get_libraries(filter_uploads):
serializer = federation_api_serializers.LibrarySerializer(qs, many=True)
return Response(serializer.data)
return libraries
return extend_schema(responses=federation_api_serializers.LibrarySerializer(many=True))(
action(methods=["get"], detail=True)(libraries)
)
def refetch_obj(obj, queryset):
@ -167,13 +171,9 @@ class ArtistViewSet(
Prefetch("albums", queryset=albums), TAG_PREFETCH
)
libraries = action(methods=["get"], detail=True)(
get_libraries(
filter_uploads=lambda o, uploads: uploads.filter(
Q(track__artist=o) | Q(track__album__artist=o)
)
)
)
libraries = get_libraries(lambda o, uploads: uploads.filter(
Q(track__artist=o) | Q(track__album__artist=o)
))
class AlbumViewSet(
@ -231,9 +231,7 @@ class AlbumViewSet(
Prefetch("tracks", queryset=tracks), TAG_PREFETCH
)
libraries = action(methods=["get"], detail=True)(
get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track__album=o))
)
libraries = get_libraries(lambda o, uploads: uploads.filter(track__album=o))
def get_serializer_class(self):
if self.action in ["create"]:
@ -430,9 +428,7 @@ class TrackViewSet(
)
return queryset.prefetch_related(TAG_PREFETCH)
libraries = action(methods=["get"], detail=True)(
get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track=o))
)
libraries = get_libraries(lambda o, uploads: uploads.filter(track=o))
def get_serializer_context(self):
context = super().get_serializer_context()
@ -744,6 +740,7 @@ class UploadViewSet(
qs = qs.playable_by(actor)
return qs
@extend_schema(operation_id='get_upload_metadata')
@action(methods=["get"], detail=True, url_path="audio-file-metadata")
def audio_file_metadata(self, request, *args, **kwargs):
upload = self.get_object()
@ -802,6 +799,7 @@ class Search(views.APIView):
required_scope = "libraries"
anonymous_policy = "setting"
@extend_schema(operation_id='get_search_results')
def get(self, request, *args, **kwargs):
query = request.GET.get("query", request.GET.get("q", "")) or ""
query = query.strip()

Wyświetl plik

@ -1,9 +1,12 @@
from django.db import transaction
from django.db.models import Count
from rest_framework import exceptions, mixins, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from funkwhale_api.common import fields, permissions
from funkwhale_api.music import utils as music_utils
from funkwhale_api.users.oauth import permissions as oauth_permissions
@ -38,6 +41,7 @@ class PlaylistViewSet(
filterset_class = filters.PlaylistFilter
ordering_fields = ("id", "name", "creation_date", "modification_date")
@extend_schema(responses=serializers.PlaylistTrackSerializer(many=True))
@action(methods=["get"], detail=True)
def tracks(self, request, *args, **kwargs):
playlist = self.get_object()
@ -48,6 +52,7 @@ class PlaylistViewSet(
data = {"count": len(plts), "results": serializer.data}
return Response(data, status=200)
@extend_schema(operation_id="add_to_playlist", request=serializers.PlaylistAddManySerializer)
@action(methods=["post"], detail=True)
@transaction.atomic
def add(self, request, *args, **kwargs):
@ -72,6 +77,7 @@ class PlaylistViewSet(
data = {"count": len(plts), "results": serializer.data}
return Response(data, status=201)
@extend_schema(operation_id="clear_playlist")
@action(methods=["delete"], detail=True)
@transaction.atomic
def clear(self, request, *args, **kwargs):
@ -93,6 +99,7 @@ class PlaylistViewSet(
),
)
@extend_schema(operation_id="remove_from_playlist")
@action(methods=["post", "delete"], detail=True)
@transaction.atomic
def remove(self, request, *args, **kwargs):
@ -111,6 +118,7 @@ class PlaylistViewSet(
return Response(status=204)
@extend_schema(operation_id="reorder_track_in_playlist")
@action(methods=["post"], detail=True)
@transaction.atomic
def move(self, request, *args, **kwargs):

Wyświetl plik

@ -1,8 +1,11 @@
from django.db.models import Q
from rest_framework import mixins, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from funkwhale_api.common import permissions as common_permissions
from funkwhale_api.music.serializers import TrackSerializer
from funkwhale_api.music import utils as music_utils
@ -63,6 +66,7 @@ class RadioViewSet(
)
return Response(serializer.data)
@extend_schema(operation_id='validate_radio')
@action(methods=["post"], detail=False)
def validate(self, request, *args, **kwargs):
try:
@ -124,6 +128,7 @@ class RadioSessionTrackViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet)
queryset = models.RadioSessionTrack.objects.all()
permission_classes = []
@extend_schema(operation_id='get_next_radio_track')
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

Wyświetl plik

@ -0,0 +1,69 @@
from drf_spectacular.openapi import AutoSchema
from pluralizer import Pluralizer
import re
class CustomAutoSchema(AutoSchema):
method_mapping = {
'get': 'get',
'post': 'create',
'put': 'update',
'patch': 'partial_update',
'delete': 'delete',
}
pluralizer = Pluralizer()
def get_operation_id(self):
# Modified operation id getter from
# https://github.com/tfranzel/drf-spectacular/blob/6973aa48f4ff08f7f33799d50c288fcc79ea8076/drf_spectacular/openapi.py#L424-L441
tokenized_path = self._tokenize_path()
# replace dashes as they can be problematic later in code generation
tokenized_path = [t.replace('-', '_') for t in tokenized_path]
# replace plural forms with singular forms
tokenized_path = [self.pluralizer.singular(t) for t in tokenized_path]
if not tokenized_path:
tokenized_path.append('root')
model = tokenized_path.pop()
if self.method == 'GET' and self._is_list_view():
action = 'get'
model = self.pluralizer.plural(model)
else:
action = self.method_mapping[self.method.lower()]
if re.search(r'<drf_format_suffix\w*:\w+>', self.path_regex):
tokenized_path.append('formatted')
# rename `get_radio_radio_track` to `get_radio_track`
if len(tokenized_path) > 1 and tokenized_path[1] == 'radio' and tokenized_path[1] == 'radio':
tokenized_path.pop(0)
# rename `get_manage_channel` to `admin_get_channel`
elif len(tokenized_path) > 0 and tokenized_path[0] == 'manage':
tokenized_path.pop(0)
# rename `get_manage_library_album` to `admin_get_album`
if len(tokenized_path) > 0 and tokenized_path[0] == 'library':
tokenized_path.pop(0)
# rename `get_manage_user_users` to `admin_get_users`
elif len(tokenized_path) > 0 and tokenized_path[0] == 'user':
tokenized_path.pop(0)
# rename `get_manage_moderation_note` to `moderation_get_note`
elif len(tokenized_path) > 0 and tokenized_path[0] == 'moderation':
tokenized_path.pop(0)
return '_'.join(['moderation', action] + tokenized_path + [model])
return '_'.join(['admin', action] + tokenized_path + [model])
return '_'.join([action] + tokenized_path + [model])

Wyświetl plik

@ -4,9 +4,12 @@ import urllib.parse
from django import http
from django.utils import timezone
from django.db.models import Q
from rest_framework import mixins, permissions, response, views, viewsets
from rest_framework.decorators import action
from drf_spectacular.utils import extend_schema
from oauth2_provider import exceptions as oauth2_exceptions
from oauth2_provider import views as oauth_views
from oauth2_provider.settings import oauth2_settings
@ -83,6 +86,7 @@ class ApplicationViewSet(
qs = qs.filter(user=self.request.user)
return qs
@extend_schema(operation_id='refresh_oauth_token')
@action(
detail=True,
methods=["post"],

Wyświetl plik

@ -12,6 +12,8 @@ from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from funkwhale_api.common import authentication
from funkwhale_api.common import preferences
from funkwhale_api.common import throttling
@ -19,6 +21,7 @@ from funkwhale_api.common import throttling
from . import models, serializers, tasks
@extend_schema(operation_id='register', methods=['post'])
class RegisterView(registration_views.RegisterView):
serializer_class = serializers.RegisterSerializer
permission_classes = []
@ -43,18 +46,22 @@ class RegisterView(registration_views.RegisterView):
return user
@extend_schema(operation_id='verify_email')
class VerifyEmailView(registration_views.VerifyEmailView):
action = "verify-email"
@extend_schema(operation_id='change_password')
class PasswordChangeView(rest_auth_views.PasswordChangeView):
action = "password-change"
@extend_schema(operation_id='reset_password')
class PasswordResetView(rest_auth_views.PasswordResetView):
action = "password-reset"
@extend_schema(operation_id='confirm_password_reset')
class PasswordResetConfirmView(rest_auth_views.PasswordResetConfirmView):
action = "password-reset-confirm"
@ -66,6 +73,8 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
lookup_value_regex = r"[a-zA-Z0-9-_.]+"
required_scope = "profile"
@extend_schema(operation_id='get_authenticated_user', methods=['get'])
@extend_schema(operation_id='delete_authenticated_user', methods=['delete'])
@action(methods=["get", "delete"], detail=False)
def me(self, request, *args, **kwargs):
"""Return information about the current user or delete it"""
@ -80,6 +89,7 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
serializer = serializers.MeSerializer(request.user)
return Response(serializer.data)
@extend_schema(operation_id='update_settings')
@action(methods=["post"], detail=False, url_name="settings", url_path="settings")
def set_settings(self, request, *args, **kwargs):
"""Return information about the current user or delete it"""
@ -111,6 +121,7 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
data = {"subsonic_api_token": self.request.user.subsonic_api_token}
return Response(data)
@extend_schema(operation_id='change_email')
@action(
methods=["post"],
required_scope="security",
@ -138,6 +149,8 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
return super().partial_update(request, *args, **kwargs)
@extend_schema(operation_id='login')
@action(methods=['post'], detail=False)
def login(request):
throttling.check_request(request, "login")
if request.method != "POST":
@ -157,6 +170,8 @@ def login(request):
return response
@extend_schema(operation_id='logout')
@action(methods=['post'], detail=False)
def logout(request):
if request.method != "POST":
return http.HttpResponse(status=405)

594
api/operations.json 100644
Wyświetl plik

@ -0,0 +1,594 @@
{
"/api/v1/activity/": {
"get": "get_activity"
},
"/api/v1/albums/": {
"get": "get_albums",
"post": "create_album"
},
"/api/v1/albums/{id}/": {
"get": "get_album",
"delete": "delete_album"
},
"/api/v1/albums/{id}/fetches/": {
"get": "get_album_fetches",
"post": "create_album_fetch"
},
"/api/v1/albums/{id}/libraries/": {
"get": "get_album_libraries"
},
"/api/v1/albums/{id}/mutations/": {
"get": "get_album_mutations",
"post": "create_album_mutation"
},
"/api/v1/artists/": {
"get": "get_artists"
},
"/api/v1/artists/{id}/": {
"get": "get_artist"
},
"/api/v1/artists/{id}/fetches/": {
"get": "get_artist_fetches",
"post": "create_artist_fetch"
},
"/api/v1/artists/{id}/libraries/": {
"get": "get_artist_libraries"
},
"/api/v1/artists/{id}/mutations/": {
"get": "get_artist_mutations",
"post": "create_artist_mutation"
},
"/api/v1/attachments/": {
"post": "create_attachment"
},
"/api/v1/attachments/{uuid}/": {
"get": "get_attachment",
"delete": "delete_attachment"
},
"/api/v1/attachments/{uuid}/proxy/": {
"get": "get_attachment_proxy"
},
"/api/v1/auth/password/change/": {
"post": "change_password"
},
"/api/v1/auth/password/reset/": {
"post": "reset_password"
},
"/api/v1/auth/password/reset/confirm/": {
"post": "confirm_password_reset"
},
"/api/v1/auth/registration/": {
"post": "register"
},
"/api/v1/auth/registration/change-password/": {
"post": "change_password_2"
},
"/api/v1/auth/registration/verify-email/": {
"post": "verify_email"
},
"/api/v1/auth/user/": {
"get": "get_auth_user",
"put": "update_auth_user",
"patch": "partial_update_auth_user"
},
"/api/v1/channels/": {
"get": "get_channels",
"post": "create_channel"
},
"/api/v1/channels/{composite}/": {
"get": "get_channel",
"put": "update_channel",
"patch": "partial_update_channel",
"delete": "delete_channel"
},
"/api/v1/channels/{composite}/rss/": {
"get": "get_channel_rss"
},
"/api/v1/channels/{composite}/subscribe/": {
"post": "subscribe_channel"
},
"/api/v1/channels/{composite}/unsubscribe/": {
"post": "unsubscribe_channel_2",
"delete": "unsubscribe_channel"
},
"/api/v1/channels/metadata-choices/": {
"get": "get_channel_metadata_choices"
},
"/api/v1/channels/rss-subscribe/": {
"post": "subscribe_channel_rss"
},
"/api/v1/favorites/tracks/": {
"get": "get_favorite_tracks",
"post": "favorite_track"
},
"/api/v1/favorites/tracks/{id}/": {
"delete": "delete_favorite_track"
},
"/api/v1/favorites/tracks/all/": {
"get": "get_all_favorite_tracks"
},
"/api/v1/favorites/tracks/remove/": {
"post": "unfavorite_track_2",
"delete": "unfavorite_track"
},
"/api/v1/federation/actors/{full_username}/": {
"get": "get_federation_actor"
},
"/api/v1/federation/actors/{full_username}/libraries/": {
"get": "get_federation_actor_library"
},
"/api/v1/federation/domains/": {
"get": "get_federation_domains"
},
"/api/v1/federation/domains/{name}/": {
"get": "get_federation_domain"
},
"/api/v1/federation/fetches/": {
"post": "create_federation_fetch"
},
"/api/v1/federation/fetches/{id}/": {
"get": "get_federation_fetch"
},
"/api/v1/federation/follows/library/": {
"get": "get_federation_library_follows",
"post": "create_federation_library_follow"
},
"/api/v1/federation/follows/library/{uuid}/": {
"get": "get_federation_library_follow",
"delete": "delete_federation_library_follow"
},
"/api/v1/federation/follows/library/{uuid}/accept/": {
"post": "accept_federation_library_follow"
},
"/api/v1/federation/follows/library/{uuid}/reject/": {
"post": "reject_federation_library_follow"
},
"/api/v1/federation/follows/library/all/": {
"get": "get_all_federation_library_follows"
},
"/api/v1/federation/inbox/": {
"get": "get_federation_inboxes"
},
"/api/v1/federation/inbox/{id}/": {
"get": "get_federation_inbox",
"put": "update_federation_inbox",
"patch": "partial_update_federation_inbox"
},
"/api/v1/federation/inbox/action/": {
"post": "create_federation_inbox_action"
},
"/api/v1/federation/libraries/{uuid}/": {
"get": "get_federation_library"
},
"/api/v1/federation/libraries/{uuid}/scan/": {
"post": "create_federation_library_scan"
},
"/api/v1/federation/libraries/fetch/": {
"post": "create_federation_library_fetch"
},
"/api/v1/history/listenings/": {
"get": "get_history_listenings",
"post": "create_history_listening"
},
"/api/v1/history/listenings/{id}/": {
"get": "get_history_listening"
},
"/api/v1/instance/admin/settings/": {
"get": "get_instance_admin_settings"
},
"/api/v1/instance/admin/settings/{id}/": {
"get": "get_instance_admin_setting",
"put": "update_instance_admin_setting",
"patch": "partial_update_instance_admin_setting"
},
"/api/v1/instance/admin/settings/bulk/": {
"post": "create_instance_admin_setting_bulk"
},
"/api/v1/instance/nodeinfo/2.0/": {
"get": "get_instance_nodeinfo_2.0"
},
"/api/v1/instance/settings/": {
"get": "get_instance_settings"
},
"/api/v1/instance/spa-manifest.json": {
"get": "get_spa_manifest"
},
"/api/v1/libraries/": {
"get": "get_libraries",
"post": "create_library"
},
"/api/v1/libraries/{uuid}/": {
"get": "get_library",
"put": "update_library",
"patch": "partial_update_library",
"delete": "delete_library"
},
"/api/v1/libraries/{uuid}/follows/": {
"get": "get_library_follow"
},
"/api/v1/libraries/fs-import/": {
"get": "get_library_fs_import",
"post": "create_library_fs_import",
"delete": "delete_library_fs_import"
},
"/api/v1/licenses/": {
"get": "get_licenses"
},
"/api/v1/licenses/{code}/": {
"get": "get_license"
},
"/api/v1/listen/{uuid}/": {
"get": "get_listen"
},
"/api/v1/manage/accounts/": {
"get": "admin_get_accounts"
},
"/api/v1/manage/accounts/{id}/": {
"get": "admin_get_account"
},
"/api/v1/manage/accounts/{id}/stats/": {
"get": "admin_get_account_stats"
},
"/api/v1/manage/accounts/action/": {
"post": "admin_create_account_action"
},
"/api/v1/manage/channels/": {
"get": "admin_get_channels"
},
"/api/v1/manage/channels/{composite}/": {
"get": "admin_get_channel",
"delete": "admin_delete_channel"
},
"/api/v1/manage/channels/{composite}/stats/": {
"get": "admin_get_channel_stats"
},
"/api/v1/manage/federation/domains/": {
"get": "admin_get_federation_domains",
"post": "admin_create_federation_domain"
},
"/api/v1/manage/federation/domains/{name}/": {
"get": "admin_get_federation_domain",
"put": "admin_update_federation_domain",
"patch": "admin_partial_update_federation_domain"
},
"/api/v1/manage/federation/domains/{name}/nodeinfo/": {
"get": "admin_get_federation_domain_nodeinfo"
},
"/api/v1/manage/federation/domains/{name}/stats/": {
"get": "admin_get_federation_domain_stats"
},
"/api/v1/manage/federation/domains/action/": {
"post": "admin_create_federation_domain_action"
},
"/api/v1/manage/library/albums/": {
"get": "admin_get_albums"
},
"/api/v1/manage/library/albums/{id}/": {
"get": "admin_get_album",
"delete": "admin_delete_album"
},
"/api/v1/manage/library/albums/{id}/stats/": {
"get": "admin_get_library_album_stats"
},
"/api/v1/manage/library/albums/action/": {
"post": "admin_create_album_action"
},
"/api/v1/manage/library/artists/": {
"get": "admin_get_artists"
},
"/api/v1/manage/library/artists/{id}/": {
"get": "admin_get_artist",
"delete": "admin_delete_artist"
},
"/api/v1/manage/library/artists/{id}/stats/": {
"get": "admin_get_library_artist_stats"
},
"/api/v1/manage/library/artists/action/": {
"post": "admin_create_artist_action"
},
"/api/v1/manage/library/libraries/": {
"get": "admin_get_libraries"
},
"/api/v1/manage/library/libraries/{uuid}/": {
"get": "admin_get_library",
"put": "admin_update_library",
"patch": "admin_partial_update_library",
"delete": "admin_delete_library"
},
"/api/v1/manage/library/libraries/{uuid}/stats/": {
"get": "admin_get_library_stats"
},
"/api/v1/manage/library/libraries/action/": {
"post": "admin_create_library_action"
},
"/api/v1/manage/library/tracks/": {
"get": "admin_get_tracks"
},
"/api/v1/manage/library/tracks/{id}/": {
"get": "admin_get_track",
"delete": "admin_delete_track"
},
"/api/v1/manage/library/tracks/{id}/stats/": {
"get": "admin_get_track_stats"
},
"/api/v1/manage/library/tracks/action/": {
"post": "admin_create_track_action"
},
"/api/v1/manage/library/uploads/": {
"get": "admin_get_uploads"
},
"/api/v1/manage/library/uploads/{uuid}/": {
"get": "admin_get_upload",
"delete": "admin_delete_upload"
},
"/api/v1/manage/library/uploads/action/": {
"post": "admin_create_upload_action"
},
"/api/v1/manage/moderation/instance-policies/": {
"get": "moderation_get_instance_policies",
"post": "moderation_create_instance_policy"
},
"/api/v1/manage/moderation/instance-policies/{id}/": {
"get": "moderation_get_instance_policy",
"put": "moderation_update_instance_policy",
"patch": "moderation_partial_update_instance_policy",
"delete": "moderation_delete_instance_policy"
},
"/api/v1/manage/moderation/notes/": {
"get": "moderation_get_notes",
"post": "moderation_create_note"
},
"/api/v1/manage/moderation/notes/{uuid}/": {
"get": "moderation_get_note",
"delete": "moderation_delete_note"
},
"/api/v1/manage/moderation/reports/": {
"get": "moderation_get_reports"
},
"/api/v1/manage/moderation/reports/{uuid}/": {
"get": "moderation_get_report",
"put": "moderation_update_report",
"patch": "moderation_partial_update_report"
},
"/api/v1/manage/moderation/requests/": {
"get": "moderation_get_requests"
},
"/api/v1/manage/moderation/requests/{uuid}/": {
"get": "moderation_get_request",
"put": "moderation_update_request",
"patch": "moderation_partial_update_request"
},
"/api/v1/manage/tags/": {
"get": "admin_get_tags",
"post": "admin_create_tag"
},
"/api/v1/manage/tags/{name}/": {
"get": "admin_get_tag",
"delete": "admin_delete_tag"
},
"/api/v1/manage/tags/action/": {
"post": "admin_create_tag_action"
},
"/api/v1/manage/users/invitations/": {
"get": "admin_get_invitations",
"post": "admin_create_invitation"
},
"/api/v1/manage/users/invitations/{id}/": {
"get": "admin_get_invitation",
"put": "admin_update_invitation",
"patch": "admin_partial_update_invitation"
},
"/api/v1/manage/users/invitations/action/": {
"post": "admin_create_invitation_action"
},
"/api/v1/manage/users/users/": {
"get": "admin_get_users"
},
"/api/v1/manage/users/users/{id}/": {
"get": "admin_get_user",
"put": "admin_update_user",
"patch": "admin_partial_update_user"
},
"/api/v1/moderation/content-filters/": {
"get": "get_moderation_content_filters",
"post": "create_moderation_content_filter"
},
"/api/v1/moderation/content-filters/{uuid}/": {
"get": "get_moderation_content_filter",
"delete": "delete_moderation_content_filter"
},
"/api/v1/moderation/reports/": {
"post": "create_moderation_report"
},
"/api/v1/mutations/": {
"get": "get_mutations"
},
"/api/v1/mutations/{uuid}/": {
"get": "get_mutation",
"delete": "delete_mutation"
},
"/api/v1/mutations/{uuid}/approve/": {
"post": "approve_mutation"
},
"/api/v1/mutations/{uuid}/reject/": {
"post": "reject_mutation"
},
"/api/v1/oauth/apps/": {
"get": "get_oauth_apps",
"post": "create_oauth_app"
},
"/api/v1/oauth/apps/{client_id}/": {
"get": "get_oauth_app",
"put": "update_oauth_app",
"patch": "partial_update_oauth_app",
"delete": "delete_oauth_app"
},
"/api/v1/oauth/apps/{client_id}/refresh-token/": {
"post": "refresh_oauth_token"
},
"/api/v1/oauth/authorize/": {
"get": "get_oauth_authorize",
"post": "create_oauth_authorize",
"put": "update_oauth_authorize"
},
"/api/v1/oauth/grants/": {
"get": "get_oauth_grants"
},
"/api/v1/oauth/grants/{client_id}/": {
"get": "get_oauth_grant",
"delete": "delete_oauth_grant"
},
"/api/v1/oembed/": {
"get": "get_oembed"
},
"/api/v1/playlists/": {
"get": "get_playlists",
"post": "create_playlist"
},
"/api/v1/playlists/{id}/": {
"get": "get_playlist",
"put": "update_playlist",
"patch": "partial_update_playlist",
"delete": "delete_playlist"
},
"/api/v1/playlists/{id}/add/": {
"post": "add_to_playlist"
},
"/api/v1/playlists/{id}/clear/": {
"delete": "clear_playlist"
},
"/api/v1/playlists/{id}/move/": {
"post": "reorder_track_in_playlist"
},
"/api/v1/playlists/{id}/remove/": {
"post": "remove_from_playlist_2",
"delete": "remove_from_playlist"
},
"/api/v1/playlists/{id}/tracks/": {
"get": "get_playlist_tracks"
},
"/api/v1/plugins/": {
"get": "get_plugins",
"post": "create_plugin"
},
"/api/v1/plugins/{id}/": {
"get": "get_plugin"
},
"/api/v1/plugins/{id}/disable/": {
"post": "disable_plugin"
},
"/api/v1/plugins/{id}/enable/": {
"post": "enable_plugin"
},
"/api/v1/plugins/{id}/scan/": {
"post": "create_plugin_scan"
},
"/api/v1/radios/radios/": {
"get": "get_radio_radios",
"post": "create_radio_radio"
},
"/api/v1/radios/radios/{id}/": {
"get": "get_radio_radio",
"put": "update_radio_radio",
"patch": "partial_update_radio_radio",
"delete": "delete_radio_radio"
},
"/api/v1/radios/radios/{id}/tracks/": {
"get": "get_radio_track"
},
"/api/v1/radios/radios/filters/": {
"get": "get_radio_filter"
},
"/api/v1/radios/radios/validate/": {
"post": "validate_radio"
},
"/api/v1/radios/sessions/": {
"post": "create_radio_session"
},
"/api/v1/radios/sessions/{id}/": {
"get": "get_radio_session"
},
"/api/v1/radios/tracks/": {
"post": "get_next_radio_track"
},
"/api/v1/rate-limit/": {
"get": "get_rate_limit"
},
"/api/v1/search": {
"get": "get_search_results"
},
"/api/v1/stream/{uuid}/": {
"get": "get_stream"
},
"/api/v1/subscriptions/": {
"get": "get_subscriptions"
},
"/api/v1/subscriptions/{uuid}/": {
"get": "get_subscription"
},
"/api/v1/subscriptions/all/": {
"get": "get_all_subscriptions"
},
"/api/v1/tags/": {
"get": "get_tags"
},
"/api/v1/tags/{name}/": {
"get": "get_tag"
},
"/api/v1/text-preview/": {
"post": "preview_text"
},
"/api/v1/tracks/": {
"get": "get_tracks"
},
"/api/v1/tracks/{id}/": {
"get": "get_track",
"delete": "delete_track"
},
"/api/v1/tracks/{id}/fetches/": {
"get": "get_track_fetches",
"post": "create_track_fetch"
},
"/api/v1/tracks/{id}/libraries/": {
"get": "get_track_libraries"
},
"/api/v1/tracks/{id}/mutations/": {
"get": "get_track_mutations",
"post": "create_track_mutation"
},
"/api/v1/uploads/": {
"get": "get_uploads",
"post": "create_upload"
},
"/api/v1/uploads/{uuid}/": {
"get": "get_upload",
"put": "update_upload",
"patch": "partial_update_upload",
"delete": "delete_upload"
},
"/api/v1/uploads/{uuid}/audio-file-metadata/": {
"get": "get_upload_metadata"
},
"/api/v1/uploads/action/": {
"post": "create_upload_action"
},
"/api/v1/users/{username}/": {
"put": "update_user",
"patch": "partial_update_user"
},
"/api/v1/users/{username}/subsonic-token/": {
"get": "get_user_subsonic_token",
"post": "create_user_subsonic_token",
"delete": "delete_user_subsonic_token"
},
"/api/v1/users/change-email/": {
"post": "change_email"
},
"/api/v1/users/me/": {
"get": "get_authenticated_user",
"delete": "delete_authenticated_user"
},
"/api/v1/users/settings/": {
"post": "update_settings"
}
}

Wyświetl plik

@ -58,6 +58,7 @@ django-cache-memoize = "0.1.10"
requests-http-message-signatures = "==0.3.1"
drf-spectacular = "==0.23.1"
sentry-sdk = "==1.9.8"
pluralizer = "==1.2.0"
[tool.poetry.dev-dependencies]
flake8 = "==3.9.2"

13390
api/schema.yml 100644

Plik diff jest za duży Load Diff