Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

URL Shortener

from flask import Flask, request, render_template, redirect
import json
import os
import random

app = Flask(__name__)

# We might want this to be configurable
LENGTH = 8
FILENAME = "short.json"

@app.get("/")
def main_page():
    return render_template("main.html")

@app.post("/add")
def add_url():
    url = request.form.get('url', '')
    if not url:
        return render_template("main.html", error = "No URL provided")

    short = get_unique_short_string()
    save(url, short)
    short_url = f"{request.url_root}r/{short}"
    return render_template("success.html", short=short, short_url=short_url)

@app.get("/r/<short>")
def handle_short_url(short):
    data = load_data()
    url = data.get(short)
    if url:
        return redirect(url)
    return render_template("missing.html", short=short)


def get_unique_short_string():
    # TODO: handle the extreme case when there are no more unique strings
    # TODO: handle when we start to have too many strings and we start to
    # have too many hits.
    while True:
        short = random_short_string(LENGTH)
        if not get_url(short):
            return short

def random_short_string(length):
    # TODO: instead of random we could take time.time() and represent it in base 62
    character = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    return "".join(random.sample(character, length))

def load_data():
    data = {}
    if os.path.exists(FILENAME):
        with open(FILENAME) as fh:
            data = json.load(fh)
    return data

def save(url, short):
    data = load_data()
    data[short] = url
    with open(FILENAME, "w") as fh:
        json.dump(data, fh)

def get_url(short):
    data = load_data()
    return data.get(short)

import url_shortener
import pytest
from bs4 import BeautifulSoup

@pytest.fixture()
def web():
    return url_shortener.app.test_client()

@pytest.fixture(autouse = True, scope = "function")
def mock_database(monkeypatch, tmpdir):
    mocked_data_file = tmpdir.join('data.json')
    monkeypatch.setattr(url_shortener, 'FILENAME', mocked_data_file)

def test_main_page(web):
    rv = web.get('/')
    assert rv.status == '200 OK'
    assert rv.headers["Content-Type"] == "text/html; charset=utf-8"
    assert '<form action="/add" method="POST">' in rv.data.decode('utf-8')

def test_add_nothing(web):
    rv = web.post('/add')
    assert rv.status == '200 OK'
    assert rv.headers["Content-Type"] == "text/html; charset=utf-8"
    assert b'<span style="color: red">No URL provided</span>' in rv.data

def test_add_data(web):
    url = "https://code-maven.com/"
    rv = web.post('/add', data={ "url": url })
    assert rv.status == '200 OK'
    assert rv.headers["Content-Type"] == "text/html; charset=utf-8"
    soup = BeautifulSoup(rv.data, 'html.parser')
    link = soup.find(id="shortened")
    short = str(link.contents[0])
    assert len(short) == url_shortener.LENGTH

    rv = web.get(f'/r/{short}')
    assert rv.status == '302 FOUND'
    assert rv.headers["Content-Type"] == "text/html; charset=utf-8"
    assert rv.headers["Location"] == url

flask
pytest
beautifulsoup4
<form action="/add" method="POST">
    <input name="url">
    <input type="submit" value="Shorten">
</form>

{% if error %}
  <span style="color: red">{{error}}</span>
{% endif %}

The short URL <span style="color: red">{{short}}</span> you clicked does not exist. (any more?, never existed? who knows?)

Congratulations. You managed to shorten {{url}} to <a id="shortened" href="{{short_url}}">{{short}}</a>.

Copy the URL from here: <input width="100" value="{{ short_url }}">