From 9a3403f78fb8021297a0b351d458f9004a9b2b3e Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Thu, 2 Sep 2021 12:47:54 -0400 Subject: [PATCH] Add a basic link embiggener --- src/bigly/serializers.py | 1 + src/bigly/static/bigly/scripts.js | 1 + src/bigly/static/bigly/styles.css | 60 ++++++++++++- src/bigly/templates/bigly/index.html | 15 ++-- src/bigly/templates/bigly/link_info.html | 23 +++++ src/bigly/test_views.py | 25 ++++++ src/bigly/urls.py | 3 +- src/bigly/views.py | 106 +++++++++++++++++------ 8 files changed, 200 insertions(+), 34 deletions(-) create mode 100644 src/bigly/static/bigly/scripts.js create mode 100644 src/bigly/templates/bigly/link_info.html create mode 100644 src/bigly/test_views.py diff --git a/src/bigly/serializers.py b/src/bigly/serializers.py index bc948fd..8a9a5c4 100644 --- a/src/bigly/serializers.py +++ b/src/bigly/serializers.py @@ -2,3 +2,4 @@ from rest_framework import serializers class FollowRedirectsSerializer(serializers.Serializer): link = serializers.URLField(required=True) + remove_utm = serializers.BooleanField(required=False) diff --git a/src/bigly/static/bigly/scripts.js b/src/bigly/static/bigly/scripts.js new file mode 100644 index 0000000..a76cfe4 --- /dev/null +++ b/src/bigly/static/bigly/scripts.js @@ -0,0 +1 @@ +console.log('Hello, world'); diff --git a/src/bigly/static/bigly/styles.css b/src/bigly/static/bigly/styles.css index 5eaa9c4..b1bc6cf 100644 --- a/src/bigly/static/bigly/styles.css +++ b/src/bigly/static/bigly/styles.css @@ -1,3 +1,61 @@ +html, body, h1, h2, input { + background: none; + border: none; + border-radius: 0; + font-size: 14pt; + font-weight: normal; + margin: 0; + padding: 0; + outline: none; +} + +html { + font-family: Verdana, Helvetica, sans-serif; + height: 100%; + width: 100%; + margin: 0; + padding: 0; +} + body { - color: magenta; + max-width: calc(100% - 6em); + margin: 3em auto; +} + +h1 { + font-size: 12vw; + text-align: center; +} + +h2 { + font-size: 6vw; + text-align: center; +} + +form { + display: flex; + flex-direction: column; + align-items: center; + margin: 3em auto; + max-width: 30em; + width: 100%; +} + +form input[type=url] { + border-bottom: 2px solid black; + width: 100%; + padding: 4px; +} + +form input[type=submit] { + border: 2px solid black; + border-radius: 5px; + margin-top: 1em; + padding: 5px 8px; + cursor: pointer; +} + +form label { + font-size: 12pt; + width: 100%; } diff --git a/src/bigly/templates/bigly/index.html b/src/bigly/templates/bigly/index.html index 4178319..17bf9f7 100644 --- a/src/bigly/templates/bigly/index.html +++ b/src/bigly/templates/bigly/index.html @@ -15,15 +15,16 @@

bigly

make links big again

- Hello, world -
- -
+
+ + + + + +
- + diff --git a/src/bigly/templates/bigly/link_info.html b/src/bigly/templates/bigly/link_info.html new file mode 100644 index 0000000..bdd5265 --- /dev/null +++ b/src/bigly/templates/bigly/link_info.html @@ -0,0 +1,23 @@ +{% load static %} + + + + bigly + + + + + + + + + + + + + + + +
Link:{{ link }}
+ + diff --git a/src/bigly/test_views.py b/src/bigly/test_views.py new file mode 100644 index 0000000..8d9df71 --- /dev/null +++ b/src/bigly/test_views.py @@ -0,0 +1,25 @@ +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) diff --git a/src/bigly/urls.py b/src/bigly/urls.py index d074512..ae0210d 100644 --- a/src/bigly/urls.py +++ b/src/bigly/urls.py @@ -20,6 +20,7 @@ from . import views urlpatterns = ( path('admin/', admin.site.urls), - path('api/v1/follow-redirects', views.api_follow_redirects), + path('api/v1/follow-redirects', views.api_follow_redirects, name='api:follow-redirects'), path('', views.index), + path('embiggen', views.embiggen, name='embiggen'), ) diff --git a/src/bigly/views.py b/src/bigly/views.py index 46bccd6..f88753a 100644 --- a/src/bigly/views.py +++ b/src/bigly/views.py @@ -1,3 +1,6 @@ +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 @@ -6,11 +9,81 @@ import requests 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 @@ -23,32 +96,15 @@ class FollowRedirectsViewSet(viewsets.ViewSet): 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', -- 2.20.1