Skip to content

Instantly share code, notes, and snippets.

@cuongnv23
Last active May 7, 2017 01:52
Show Gist options
  • Select an option

  • Save cuongnv23/d31a7adbcaebeeea1aa7d0c3470ab7c6 to your computer and use it in GitHub Desktop.

Select an option

Save cuongnv23/d31a7adbcaebeeea1aa7d0c3470ab7c6 to your computer and use it in GitHub Desktop.

How to run

This app is highly recommended to run in a virtualenv.

  • Install requirements:
$ pip install -r requirements.txt
  • Run the app, this app listens on port 5000 by default, ensure port available
$ python urlshorten.py

Tests

This app doesn't support a frontend page, we will use curl to verify

  • Send a POST request with form style
$ curl -X POST -F 'original_url=google.com' localhost:5000/api/v1/url
{
  "shorten_url": "http://localhost:5000/6wASiB"
}
  • Send a GET request with shorten url, expected an HTTP 302 return
$ curl http://localhost:5000/6wASiB -I
HTTP/1.0 302 FOUND
Content-Type: text/html; charset=utf-8
Content-Length: 241
Location: http://google.com
Server: Werkzeug/0.12.1 Python/2.7.9
Date: Sun, 07 May 2017 01:48:29 GMT

The Locaton header points out the request should be redirected to http://google.com. To follow the redrection in curl, use option -L

  • Send a POST request to shorten google.com again, expect the same issued shorten url returned
$ curl -X POST -F 'original_url=google.com' localhost:5000/api/v1/url
{
  "shorten_url": "http://localhost:5000/6wASiB"
}
appdirs==1.4.3
click==6.7
Flask==0.12.1
Flask-SQLAlchemy==2.2
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
packaging==16.8
pyparsing==2.2.0
six==1.10.0
SQLAlchemy==1.1.9
Werkzeug==0.12.1
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import string
from random import SystemRandom as SR
from datetime import datetime
from flask import Flask, request, jsonify, url_for, make_response, redirect
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/url.db'
db = SQLAlchemy(app)
# length of shorten uri
URI_LENGTH = 6
class Url(db.Model):
shorten_url = db.Column(db.String(URI_LENGTH), primary_key=True)
original_url = db.Column(db.String(255), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __init__(self, original_url, shorten_url):
self.original_url = original_url
self.shorten_url = shorten_url
def __repr__(self):
return '<ShortenUrl: %r with OrignalUrl: %r>' % (self.shorten_url,
self.original_url)
# create database
db.create_all()
def generate_uri(length=URI_LENGTH):
"""Generate a random string with `length` chacracter
"""
return ''.join(SR().choice(string.ascii_letters + string.digits)
for _ in range(length))
@app.route("/", methods=['GET'])
def index():
return "URL Shorten application"
@app.route("/<string:url>", methods=['GET'])
def read(url):
"""This API receives an HTTP GET request with shorten url passed and
lookup the shorten url in database. If original url is found, redirects
user to original url. Otherwise, return JSON reponse of HTTP code 404
"""
url = Url.query.filter_by(shorten_url=url).first()
if url:
original_url = url.original_url
if not original_url.startswith('http'):
original_url = 'http://' + original_url
return redirect(original_url)
else:
resp = jsonify({'original_url': 'Not found'})
return make_response(resp, 404)
@app.route("/api/v1/url", methods=['POST'])
def write():
"""Write API receives original url and return shorten url
"""
# if original url already shorten, grab shorten url and return
original_url = request.form['original_url']
url = Url.query.filter_by(original_url=original_url).first()
if url:
shorten_url = url_for("read", url=url.shorten_url, _external=True)
resp = jsonify({'shorten_url': shorten_url})
else:
# if shorten uri already existed, re-generate
while True:
shorten_uri = generate_uri()
uri = Url.query.filter_by(shorten_url=shorten_uri).first()
if shorten_uri != uri:
break
shorten_url = url_for("read", url=shorten_uri, _external=True)
i = Url(original_url, shorten_uri)
db.session.add(i)
db.session.commit()
resp = jsonify({'shorten_url': shorten_url})
return make_response(resp, 201)
if __name__ == '__main__':
app.run(debug=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment