Merge branch '877-trailing-slash-api' into 'develop'

Fix #877: Ensure API urls answer with and without a trailing slash

Closes #877

See merge request funkwhale/funkwhale!817
This commit is contained in:
Eliot Berriot 2019-07-04 10:36:22 +02:00
commit a6a649d48f
15 changed files with 75 additions and 32 deletions

View File

@ -6,11 +6,12 @@ from rest_framework_jwt import views as jwt_views
from funkwhale_api.activity import views as activity_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.subsonic.views import SubsonicViewSet
router = routers.SimpleRouter()
router = common_routers.OptionalSlashRouter()
router.register(r"settings", GlobalPreferencesViewSet, basename="settings")
router.register(r"activity", activity_views.ActivityViewSet, "activity")
router.register(r"tags", views.TagViewSet, "tags")
@ -79,8 +80,8 @@ v1_patterns += [
r"^oauth/",
include(("funkwhale_api.users.oauth.urls", "oauth"), namespace="oauth"),
),
url(r"^token/$", jwt_views.obtain_jwt_token, name="token"),
url(r"^token/refresh/$", jwt_views.refresh_jwt_token, name="token_refresh"),
url(r"^token/?$", jwt_views.obtain_jwt_token, name="token"),
url(r"^token/refresh/?$", jwt_views.refresh_jwt_token, name="token_refresh"),
]
urlpatterns = [

View File

@ -0,0 +1,7 @@
from rest_framework.routers import SimpleRouter
class OptionalSlashRouter(SimpleRouter):
def __init__(self):
super().__init__()
self.trailing_slash = "/?"

View File

@ -1,8 +1,8 @@
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"tracks", views.TrackFavoriteViewSet, "tracks")
urlpatterns = router.urls

View File

@ -1,8 +1,8 @@
from rest_framework import routers
from funkwhale_api.common import routers
from . import api_views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"fetches", api_views.FetchViewSet, "fetches")
router.register(r"follows/library", api_views.LibraryFollowViewSet, "library-follows")
router.register(r"inbox", api_views.InboxItemViewSet, "inbox")

View File

@ -1,8 +1,8 @@
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"listenings", views.ListeningViewSet, "listenings")
urlpatterns = router.urls

View File

@ -1,12 +1,12 @@
from django.conf.urls import url
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
admin_router = routers.SimpleRouter()
admin_router = routers.OptionalSlashRouter()
admin_router.register(r"admin/settings", views.AdminSettings, "admin-settings")
urlpatterns = [
url(r"^nodeinfo/2.0/$", views.NodeInfo.as_view(), name="nodeinfo-2.0"),
url(r"^settings/$", views.InstanceSettings.as_view(), name="settings"),
url(r"^nodeinfo/2.0/?$", views.NodeInfo.as_view(), name="nodeinfo-2.0"),
url(r"^settings/?$", views.InstanceSettings.as_view(), name="settings"),
] + admin_router.urls

View File

@ -1,28 +1,28 @@
from django.conf.urls import include, url
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
federation_router = routers.SimpleRouter()
federation_router = routers.OptionalSlashRouter()
federation_router.register(r"domains", views.ManageDomainViewSet, "domains")
library_router = routers.SimpleRouter()
library_router = routers.OptionalSlashRouter()
library_router.register(r"albums", views.ManageAlbumViewSet, "albums")
library_router.register(r"artists", views.ManageArtistViewSet, "artists")
library_router.register(r"libraries", views.ManageLibraryViewSet, "libraries")
library_router.register(r"tracks", views.ManageTrackViewSet, "tracks")
library_router.register(r"uploads", views.ManageUploadViewSet, "uploads")
moderation_router = routers.SimpleRouter()
moderation_router = routers.OptionalSlashRouter()
moderation_router.register(
r"instance-policies", views.ManageInstancePolicyViewSet, "instance-policies"
)
users_router = routers.SimpleRouter()
users_router = routers.OptionalSlashRouter()
users_router.register(r"users", views.ManageUserViewSet, "users")
users_router.register(r"invitations", views.ManageInvitationViewSet, "invitations")
other_router = routers.SimpleRouter()
other_router = routers.OptionalSlashRouter()
other_router.register(r"accounts", views.ManageActorViewSet, "accounts")
urlpatterns = [

View File

@ -1,8 +1,8 @@
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"content-filters", views.UserFilterViewSet, "content-filters")
urlpatterns = router.urls

View File

@ -1,9 +1,9 @@
from django.conf.urls import url
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"search", views.SearchViewSet, "search")
urlpatterns = [
url(

View File

@ -1,8 +1,8 @@
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"sessions", views.RadioSessionViewSet, "sessions")
router.register(r"radios", views.RadioViewSet, "radios")
router.register(r"tracks", views.RadioSessionTrackViewSet, "tracks")

View File

@ -1,8 +1,8 @@
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"users", views.UserViewSet, "users")
urlpatterns = router.urls

View File

@ -1,11 +1,11 @@
from django.conf.urls import url
from django.views.decorators.csrf import csrf_exempt
from rest_framework import routers
from funkwhale_api.common import routers
from . import views
router = routers.SimpleRouter()
router = routers.OptionalSlashRouter()
router.register(r"apps", views.ApplicationViewSet, "apps")
router.register(r"grants", views.GrantViewSet, "grants")

View File

@ -8,12 +8,12 @@ from . import views
urlpatterns = [
url(r"^$", views.RegisterView.as_view(), name="rest_register"),
url(
r"^verify-email/$",
r"^verify-email/?$",
registration_views.VerifyEmailView.as_view(),
name="rest_verify_email",
),
url(
r"^change-password/$",
r"^change-password/?$",
rest_auth_views.PasswordChangeView.as_view(),
name="change_password",
),
@ -28,7 +28,7 @@ urlpatterns = [
# view from:
# djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
url(
r"^account-confirm-email/(?P<key>\w+)/$",
r"^account-confirm-email/(?P<key>\w+)/?$",
TemplateView.as_view(),
name="account_confirm_email",
),

View File

@ -0,0 +1,34 @@
import pytest
from django import urls
@pytest.mark.parametrize(
"url",
[
"/api/v1/artists",
"/api/v1/albums",
"/api/v1/tracks",
"/api/v1/libraries",
"/api/v1/uploads",
"/api/v1/playlists",
"/api/v1/favorites/tracks",
"/api/v1/auth/registration/verify-email",
"/api/v1/auth/registration/change-password",
"/api/v1/auth/registration/account-confirm-email/key",
"/api/v1/history/listenings",
"/api/v1/radios/sessions",
"/api/v1/users/users/me",
"/api/v1/federation/follows/library",
"/api/v1/manage/accounts",
"/api/v1/oauth/apps",
"/api/v1/moderation/content-filters",
"/api/v1/token",
"/api/v1/token/refresh",
"/api/v1/instance/settings",
"/api/v1/instance/nodeinfo/2.0",
],
)
@pytest.mark.parametrize("suffix", ["", "/"])
def test_optional_trailing_slash(url, suffix):
match = urls.resolve(url + suffix)
assert match is not None

View File

@ -0,0 +1 @@
Ensure API urls answer with and without a trailing slash (#877)