From 72877e31ed72e1a6a45eb9bc8d6c8fe9a85e49c7 Mon Sep 17 00:00:00 2001 From: Georg Krause Date: Sat, 15 Oct 2022 18:15:17 +0200 Subject: [PATCH] feat(api): Prepare API for version 2 --- api/config/{urls.py => urls/__init__.py} | 10 ++- api/config/urls/api.py | 88 ++++++++++++++++++++++++ api/config/urls/api_v2.py | 15 ++++ api/config/urls/spa.py | 45 ++++++++++++ api/config/urls/subsonic.py | 18 +++++ api/tests/test_urls.py | 17 +++++ changes/changelog.d/1808.feature | 1 + 7 files changed, 193 insertions(+), 1 deletion(-) rename api/config/{urls.py => urls/__init__.py} (87%) create mode 100644 api/config/urls/api.py create mode 100644 api/config/urls/api_v2.py create mode 100644 api/config/urls/spa.py create mode 100644 api/config/urls/subsonic.py create mode 100644 api/tests/test_urls.py create mode 100644 changes/changelog.d/1808.feature diff --git a/api/config/urls.py b/api/config/urls/__init__.py similarity index 87% rename from api/config/urls.py rename to api/config/urls/__init__.py index 3af9bd87d..d43a3d4b8 100644 --- a/api/config/urls.py +++ b/api/config/urls/__init__.py @@ -11,10 +11,18 @@ from django.views import defaults as default_views from config import plugins plugins_patterns = plugins.trigger_filter(plugins.URLS, [], enabled=True) + +api_patterns = [ + url("v1/", include("config.urls.api")), + url("v2/", include("config.urls.api_v2")), + url("subsonic/", include("config.urls.subsonic")), +] + + urlpatterns = [ # Django Admin, use {% url 'admin:index' %} url(settings.ADMIN_URL, admin.site.urls), - url(r"^api/", include(("config.api_urls", "api"), namespace="api")), + url(r"^api/", include((api_patterns, "api"), namespace="api")), url( r"^", include( diff --git a/api/config/urls/api.py b/api/config/urls/api.py new file mode 100644 index 000000000..b43728ba1 --- /dev/null +++ b/api/config/urls/api.py @@ -0,0 +1,88 @@ +from django.conf.urls import include, url + +from funkwhale_api.activity import views as activity_views +from funkwhale_api.audio import views as audio_views +from funkwhale_api.common import views as common_views +from funkwhale_api.common import routers as common_routers +from funkwhale_api.music import views +from funkwhale_api.playlists import views as playlists_views +from funkwhale_api.tags import views as tags_views + +router = common_routers.OptionalSlashRouter() +router.register(r"activity", activity_views.ActivityViewSet, "activity") +router.register(r"tags", tags_views.TagViewSet, "tags") +router.register(r"plugins", common_views.PluginViewSet, "plugins") +router.register(r"tracks", views.TrackViewSet, "tracks") +router.register(r"uploads", views.UploadViewSet, "uploads") +router.register(r"libraries", views.LibraryViewSet, "libraries") +router.register(r"listen", views.ListenViewSet, "listen") +router.register(r"stream", views.StreamViewSet, "stream") +router.register(r"artists", views.ArtistViewSet, "artists") +router.register(r"channels", audio_views.ChannelViewSet, "channels") +router.register(r"subscriptions", audio_views.SubscriptionsViewSet, "subscriptions") +router.register(r"albums", views.AlbumViewSet, "albums") +router.register(r"licenses", views.LicenseViewSet, "licenses") +router.register(r"playlists", playlists_views.PlaylistViewSet, "playlists") +router.register(r"mutations", common_views.MutationViewSet, "mutations") +router.register(r"attachments", common_views.AttachmentViewSet, "attachments") +v1_patterns = router.urls + +v1_patterns += [ + url(r"^oembed/$", views.OembedView.as_view(), name="oembed"), + url( + r"^instance/", + include(("funkwhale_api.instance.urls", "instance"), namespace="instance"), + ), + url( + r"^manage/", + include(("funkwhale_api.manage.urls", "manage"), namespace="manage"), + ), + url( + r"^moderation/", + include( + ("funkwhale_api.moderation.urls", "moderation"), namespace="moderation" + ), + ), + url( + r"^federation/", + include( + ("funkwhale_api.federation.api_urls", "federation"), namespace="federation" + ), + ), + url( + r"^providers/", + include(("funkwhale_api.providers.urls", "providers"), namespace="providers"), + ), + url( + r"^favorites/", + include(("funkwhale_api.favorites.urls", "favorites"), namespace="favorites"), + ), + url(r"^search$", views.Search.as_view(), name="search"), + url( + r"^radios/", + include(("funkwhale_api.radios.urls", "radios"), namespace="radios"), + ), + url( + r"^history/", + include(("funkwhale_api.history.urls", "history"), namespace="history"), + ), + url( + r"^", + include(("funkwhale_api.users.api_urls", "users"), namespace="users"), + ), + # XXX: remove if Funkwhale 1.1 + url( + r"^users/", + include(("funkwhale_api.users.api_urls", "users"), namespace="users-nested"), + ), + url( + r"^oauth/", + include(("funkwhale_api.users.oauth.urls", "oauth"), namespace="oauth"), + ), + url(r"^rate-limit/?$", common_views.RateLimitView.as_view(), name="rate-limit"), + url( + r"^text-preview/?$", common_views.TextPreviewView.as_view(), name="text-preview" + ), +] + +urlpatterns = [url("", include((v1_patterns, "v1"), namespace="v1"))] diff --git a/api/config/urls/api_v2.py b/api/config/urls/api_v2.py new file mode 100644 index 000000000..95c776a0c --- /dev/null +++ b/api/config/urls/api_v2.py @@ -0,0 +1,15 @@ +from django.conf.urls import include, url + +from funkwhale_api.common import routers as common_routers + +router = common_routers.OptionalSlashRouter() +v2_patterns = router.urls + +v2_patterns += [ + url( + r"^instance/", + include(("funkwhale_api.instance.urls", "instance"), namespace="instance"), + ), +] + +urlpatterns = [url("", include((v2_patterns, "v2"), namespace="v2"))] diff --git a/api/config/urls/spa.py b/api/config/urls/spa.py new file mode 100644 index 000000000..7b4b5e169 --- /dev/null +++ b/api/config/urls/spa.py @@ -0,0 +1,45 @@ +from django import urls + +from funkwhale_api.audio import spa_views as audio_spa_views +from funkwhale_api.federation import spa_views as federation_spa_views +from funkwhale_api.music import spa_views + + +urlpatterns = [ + urls.re_path( + r"^library/tracks/(?P\d+)/?$", spa_views.library_track, name="library_track" + ), + urls.re_path( + r"^library/albums/(?P\d+)/?$", spa_views.library_album, name="library_album" + ), + urls.re_path( + r"^library/artists/(?P\d+)/?$", + spa_views.library_artist, + name="library_artist", + ), + urls.re_path( + r"^library/playlists/(?P\d+)/?$", + spa_views.library_playlist, + name="library_playlist", + ), + urls.re_path( + r"^library/(?P[0-9a-f-]+)/?$", + spa_views.library_library, + name="library_library", + ), + urls.re_path( + r"^channels/(?P[0-9a-f-]+)/?$", + audio_spa_views.channel_detail_uuid, + name="channel_detail", + ), + urls.re_path( + r"^channels/(?P[^/]+)/?$", + audio_spa_views.channel_detail_username, + name="channel_detail", + ), + urls.re_path( + r"^@(?P[^/]+)/?$", + federation_spa_views.actor_detail_username, + name="actor_detail", + ), +] diff --git a/api/config/urls/subsonic.py b/api/config/urls/subsonic.py new file mode 100644 index 000000000..08adebf93 --- /dev/null +++ b/api/config/urls/subsonic.py @@ -0,0 +1,18 @@ +from django.conf.urls import include, url +from rest_framework import routers +from rest_framework.urlpatterns import format_suffix_patterns + +from funkwhale_api.subsonic.views import SubsonicViewSet + +subsonic_router = routers.SimpleRouter(trailing_slash=False) +subsonic_router.register(r"rest", SubsonicViewSet, basename="subsonic") + +subsonic_patterns = format_suffix_patterns(subsonic_router.urls, allowed=["view"]) +urlpatterns = [url("", include((subsonic_patterns, "subsonic"), namespace="subsonic"))] + +# urlpatterns = [ +# url( +# r"^subsonic/rest/", +# include((subsonic_router.urls, "subsonic"), namespace="subsonic"), +# ) +# ] diff --git a/api/tests/test_urls.py b/api/tests/test_urls.py new file mode 100644 index 000000000..c5820f478 --- /dev/null +++ b/api/tests/test_urls.py @@ -0,0 +1,17 @@ +from django.urls import reverse +from django import urls + + +def test_can_resolve_v1(): + path = reverse("api:v1:instance:nodeinfo-2.0") + assert path == "/api/v1/instance/nodeinfo/2.0" + + +def test_can_resolve_subsonic(): + path = reverse("api:subsonic:subsonic-ping") + assert path == "/api/subsonic/rest/ping" + + +def test_can_resolve_v2(): + path = reverse("api:v2:instance:nodeinfo-2.0") + assert path == "/api/v2/instance/nodeinfo/2.0" diff --git a/changes/changelog.d/1808.feature b/changes/changelog.d/1808.feature new file mode 100644 index 000000000..07dc9d95e --- /dev/null +++ b/changes/changelog.d/1808.feature @@ -0,0 +1 @@ +Prepare API for the upcoming version 2