--- /dev/null
+from django.test import TestCase
+
+from . import views
+
+class RemoveUTMTestCase(TestCase):
+ def test_preserves_basic(self):
+ expected = 'http://www.myhostname.com/my/path?my=param&my=otherparam#myanchor'
+ actual = views._remove_utm(expected)
+ self.assertEqual(expected, actual)
+
+ def test_removes_utm(self):
+ input_url = 'http://www.myhostname.com/my/path?utm_param=param&my=param#myanchor'
+ expected = 'http://www.myhostname.com/my/path?my=param#myanchor'
+ actual = views._remove_utm(input_url)
+ self.assertEqual(expected, actual)
+
+ def test_preserves_empty_parameters(self):
+ expected = 'http://www.myhostname.com/my/path?my_empty_param=&my=param#myanchor'
+ actual = views._remove_utm(expected)
+ self.assertEqual(expected, actual)
+
+ def test_preserves_flags(self):
+ expected = 'http://www.myhostname.com/my/path?my_flag&my=param#myanchor'
+ actual = views._remove_utm(expected)
+ self.assertEqual(expected, actual)
+from urllib.parse import urlparse, urlunparse, parse_qs
+
+from django.shortcuts import render
from django.views.generic.base import TemplateView
from rest_framework import status, viewsets
from . import serializers
+def _remove_utm(link):
+ parsed_link = urlparse(link)
+ parsed_link = parsed_link._replace(
+ query='&'.join(
+ [
+ p
+ for p in parsed_link.query.split('&')
+ if not p.startswith('utm_')
+ ]
+ )
+ )
+
+ return ''.join((
+ parsed_link.scheme + '://' if parsed_link.scheme else '',
+ parsed_link.netloc,
+ parsed_link.path,
+ ';' + parsed_link.params if parsed_link.params else '',
+ '?' + parsed_link.query if parsed_link.query else '',
+ '#' + parsed_link.fragment if parsed_link.fragment else '',
+ ))
+
+def _follow_redirects(link, remove_utm):
+ while True:
+ if remove_utm:
+ link = _remove_utm(link)
+
+ response = requests.head(link)
+
+ # TODO Handle timeouts
+
+ if 301 <= response.status_code and response.status_code <= 308:
+ # TODO Handle the different kinds of redirects correctly
+
+ link = response.headers.get('Location')
+
+ if not link:
+ # TODO Handle this
+ raise Exception()
+
+ # TODO Handle error responses
+ else:
+ return {
+ 'link': link,
+ 'status': response.status_code,
+ }
+
class IndexView(TemplateView):
template_name = 'bigly/index.html'
index = IndexView.as_view()
+def embiggen(request):
+ serializer = serializers.FollowRedirectsSerializer(data=request.GET)
+
+ if not serializer.is_valid():
+ return render(
+ request,
+ 'bigly/index.html',
+ {
+ 'errors': serializer.errors,
+ },
+ status=400,
+ )
+
+ result = _follow_redirects(
+ link = serializer.data['link'],
+ remove_utm = serializer.data['remove_utm'],
+ )
+
+ return render(
+ request,
+ 'bigly/link_info.html',
+ result,
+ )
+
class FollowRedirectsViewSet(viewsets.ViewSet):
serializer_class = serializers.FollowRedirectsSerializer
status=status.HTTP_400_BAD_REQUEST,
)
- link = serializer.data['link']
-
- while True:
- response = requests.head(link)
-
- # TODO Handle timeouts
-
- if 301 <= response.status_code and response.status_code <= 308:
- # TODO Handle the different kinds of redirects correctly
-
- link = response.headers.get('Location')
-
- if not link:
- # TODO Handle this
- raise Exception()
-
- # TODO Handle error responses
- else:
- return Response(
- {
- 'link': link,
- 'status': response.status_code,
- },
- status=status.HTTP_200_OK,
- )
+ result = _follow_redirects(
+ link = serializer.data['link'],
+ remove_utm = serializer.data['remove_utm'],
+ )
+ return Response(
+ result,
+ status=status.HTTP_200_OK,
+ )
api_follow_redirects = FollowRedirectsViewSet.as_view({
'get': 'follow_redirects',