Skip to content

Commit 180f893

Browse files
authored
Merge pull request #220 from buildingSMART/IVS-623_Redirect_Browsers_to_SwaggerUI
IVS-623 - Redirect browsers hitting /api to Swagger UI
2 parents 374064f + f734d2d commit 180f893

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

backend/core/settings.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@
164164

165165
SPECTACULAR_SETTINGS = {
166166
'TITLE': 'IFC Validation Service API (PREVIEW)',
167-
'DESCRIPTION': 'API for the buildingSMART Validation Service',
167+
'DESCRIPTION': (
168+
'API for the buildingSMART Validation Service.\n\n'
169+
'[API Quickstart Documentation]'
170+
'(https://github.com/buildingSMART/validate/blob/docs/gh-pages/docs/dev/api_quickstart.md)'
171+
),
168172
'VERSION': os.environ.get("VERSION", "UNDEFINED"),
169173
'SERVE_INCLUDE_SCHEMA': False,
170174

backend/core/urls.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,40 @@
11
import re
22
from django.contrib import admin
3+
from django.http import HttpResponseRedirect
34
from django.urls import include, path, re_path
45
from django.contrib.auth.decorators import login_required
56
from django.contrib.admin.views.decorators import staff_member_required
67
from django.views.static import serve
8+
from django.views import View
79

810
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
911

1012
from .views_auth import login, logout, callback, whoami
1113

1214
from core.settings import MEDIA_ROOT, MEDIA_URL, STATIC_URL, STATIC_ROOT, DEVELOPMENT
1315

16+
class APIRedirectView(View):
17+
"""
18+
Redirect browser requests to Swagger UI, API clients to continue normally
19+
"""
20+
def get(self, request, *args, **kwargs):
21+
22+
# for browsers, redirect to Swagger UI
23+
accept_header = request.META.get('HTTP_ACCEPT', '')
24+
if 'text/html' in accept_header: # is request from a browser?
25+
return HttpResponseRedirect('/api/swagger-ui/')
26+
27+
# for API clients, redirect to schema
28+
return HttpResponseRedirect('/api/schema/')
1429

1530
urlpatterns = [
1631

1732
# Django Admin
1833
path("admin/", admin.site.urls),
1934

35+
# API redirect for browsers
36+
path('api/', APIRedirectView.as_view(), name='api-redirect'),
37+
2038
# API info + documentation
2139
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
2240
path('api/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),

e2e/tests/validate_api.test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,5 +316,66 @@ test.describe('API - ValidationRequest', () => {
316316
expect(response.statusText()).toBe('Unauthorized');
317317
expect(response.status()).toBe(401);
318318
});
319+
});
320+
321+
test.describe('API - Browsers vs Clients', () => {
322+
323+
test('Browsers will be redirected to /api/swagger-ui', async ({ request }) => {
324+
325+
// root of /api
326+
const response = await request.get(`${BASE_URL}/api/`, {
327+
headers: {
328+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8'
329+
},
330+
maxRedirects: 0
331+
});
332+
333+
// check if the response is correct - 302 Found
334+
expect(response.statusText()).toBe('Found');
335+
expect(response.status()).toBe(302);
336+
expect(response.headers()['location']).toBe('/api/swagger-ui/');
337+
});
338+
339+
test('Browsers are redirected to /api/swagger-ui', async ({ request }) => {
340+
341+
// root of /api
342+
const response = await request.get(`${BASE_URL}/api/`, {
343+
headers: {
344+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8'
345+
},
346+
maxRedirects: 5
347+
});
348+
349+
// check if the response is correct - 200 OK
350+
expect(response.statusText()).toBe('OK');
351+
expect(response.status()).toBe(200);
352+
expect(response.url()).toBe(`${BASE_URL}/api/swagger-ui/`);
353+
});
354+
355+
test('API clients will be redirected to /api/schema', async ({ request }) => {
356+
357+
// root of /api
358+
const response = await request.get(`${BASE_URL}/api/`, {
359+
maxRedirects: 0
360+
});
361+
362+
// check if the response is correct - 302 Found
363+
expect(response.statusText()).toBe('Found');
364+
expect(response.status()).toBe(302);
365+
expect(response.headers()['location']).toBe('/api/schema/');
366+
});
367+
368+
test('API clients are redirected to /api/schema', async ({ request }) => {
369+
370+
// root of /api
371+
const response = await request.get(`${BASE_URL}/api/`, {
372+
maxRedirects: 5
373+
});
374+
375+
// check if the response is correct - 200 OK
376+
expect(response.statusText()).toBe('OK');
377+
expect(response.status()).toBe(200);
378+
expect(response.url()).toBe(`${BASE_URL}/api/schema/`);
379+
});
319380

320381
});

0 commit comments

Comments
 (0)