diff --git a/luncho/blueprints/users.py b/luncho/blueprints/users.py index f4ff0b3..9b2b8c4 100644 --- a/luncho/blueprints/users.py +++ b/luncho/blueprints/users.py @@ -6,13 +6,13 @@ from flask import Blueprint from flask import request from flask import jsonify -# from flask import current_app -from pony.orm import commit +from sqlalchemy.exc import IntegrityError from luncho.helpers import ForceJSON from luncho.server import User +from luncho.server import db users = Blueprint('users', __name__) @@ -23,10 +23,19 @@ def create_user(): """Create a new user. Request must be: { "username": "username", "full_name": "Full Name", "password": "hash" }""" json = request.get_json(force=True) - new_user = User(username=json['username'], - fullname=json['full_name'], - passhash=json['password'], - validated=False) - commit() - return jsonify(status='OK') + try: + new_user = User(username=json['username'], + fullname=json['full_name'], + passhash=json['password'], + validated=False) + + db.session.add(new_user) + db.session.commit() + + return jsonify(status='OK') + except IntegrityError: + resp = jsonify(status='ERROR', + error='username already exists') + resp.status_code = 409 + return resp diff --git a/luncho/database.py b/luncho/database.py deleted file mode 100644 index 2d9dd35..0000000 --- a/luncho/database.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -import datetime - -from flask import current_app - -from pony.orm import Database -from pony.orm import PrimaryKey -from pony.orm import Optional -from pony.orm import Required -# from pony.orm import Set - -db = Database("sqlite", current_app.config['SQLITE_FILENAME'], create_db=True) - - -class User(db.Entity): - """Users.""" - username = PrimaryKey(unicode) - fullname = Required(unicode) - passhash = Required(unicode) - token = Optional(unicode) # 1. if the user never logged in, they will - # not have a token. - # 2. This forces the user to have a single - # login everywhere, per day. - issue_date = Optional(datetime.datetime) - validated = Required(bool, default=False) - -db.generate_mapping(create_tables=True) diff --git a/luncho/helpers.py b/luncho/helpers.py index 2b9cca9..adc3ee6 100644 --- a/luncho/helpers.py +++ b/luncho/helpers.py @@ -18,8 +18,10 @@ class ForceJSON(object): def check_json(*args, **kwargs): json = request.get_json(force=True, silent=True) if not json: - return jsonify(status='ERROR', - error='Request MUST be in JSON format'), 400 + resp = jsonify(status='ERROR', + error='Request MUST be in JSON format') + resp.status_code = 400 + return resp # now we have the JSON, let's check if all the fields are here. missing = [] @@ -30,8 +32,10 @@ class ForceJSON(object): if missing: fields = ', '.join(missing) error = 'Missing fields: {fields}'.format(fields=fields) - return jsonify(status='ERROR', + resp = jsonify(status='ERROR', error=error) + resp.status_code = 400 + return resp return func(*args, **kwargs) return check_json diff --git a/luncho/server.py b/luncho/server.py index d6dad61..20147dd 100644 --- a/luncho/server.py +++ b/luncho/server.py @@ -2,7 +2,6 @@ # -*- encoding: utf-8 -*- import sys -import datetime import logging from flask import Flask @@ -12,7 +11,8 @@ from flask import Flask # Config # ---------------------------------------------------------------------- class Settings(object): - SQLITE_FILENAME = './luncho.db3' + SQLALCHEMY_DATABASE_URI = 'sqlite://./luncho.db3' + DEBUG = True log = logging.getLogger('luncho.server') @@ -26,29 +26,26 @@ app.config.from_envvar('LUCNHO_CONFIG', True) # ---------------------------------------------------------------------- # Database # ---------------------------------------------------------------------- -from pony.orm import db_session -from pony.orm import Database -from pony.orm import PrimaryKey -from pony.orm import Optional -from pony.orm import Required +from flask.ext.sqlalchemy import SQLAlchemy +db = SQLAlchemy(app) -db = Database("sqlite", app.config['SQLITE_FILENAME'], create_db=True) +class User(db.Model): + username = db.Column(db.String, primary_key=True) + fullname = db.Column(db.String, nullable=False) + passhash = db.Column(db.String, nullable=False) + token = db.Column(db.String) + issued_date = db.Column(db.Date) + validated = db.Column(db.Boolean, default=False) -class User(db.Entity): - """Users.""" - username = PrimaryKey(unicode) - fullname = Required(unicode) - passhash = Required(unicode) - token = Optional(unicode) # 1. if the user never logged in, they will - # not have a token. - # 2. This forces the user to have a single - # login everywhere, per day. - issue_date = Optional(datetime.datetime) - validated = Required(bool, default=False) - -db.generate_mapping(create_tables=True) -app.wsgi_app = db_session(app.wsgi_app) + def __init__(self, username, fullname, passhash, token=None, + issued_date=None, validated=False): + self.username = username + self.fullname = fullname + self.passhash = passhash + self.token = token + self.issued_date = issued_date + self.validated = validated # ---------------------------------------------------------------------- # Blueprints diff --git a/manage.py b/manage.py index a7471d8..b64ca0c 100644 --- a/manage.py +++ b/manage.py @@ -9,6 +9,11 @@ from luncho.server import app manager = Manager(app) + +@manager.command +def create_db(): + """Create the database.""" + if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) manager.run() diff --git a/requirements.txt b/requirements.txt index d17b341..42b7a23 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ Flask flask-script -pony +flask-sqlalchemy diff --git a/tests/users_tests.py b/tests/users_tests.py index 0e3ce1d..36396bc 100644 --- a/tests/users_tests.py +++ b/tests/users_tests.py @@ -11,20 +11,68 @@ class TestUsers(unittest.TestCase): """Test users request.""" def setUp(self): - server.app.config['SQLITE_FILENAME'] = ':memory:' + # leave the database blank to make it in memory + server.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' server.app.config['TESTING'] = True - print server.app.config['SQLITE_FILENAME'] self.app = server.app.test_client() + server.db.create_all() + + def tearDown(self): + server.db.drop_all(bind=None) def test_create_user(self): + """Simple user creation.""" + request = {'username': 'username', + 'full_name': 'full name', + 'password': 'hash'} + rv = self.app.put('/user/', + data=json.dumps(request), + content_type='application/json') + + self.assertEqual(rv.status_code, 200) + self.assertEqual(json.loads(rv.data), {'status': 'OK'}) + + def test_duplicate_user(self): + """Check the status for trying to create a user that it is already + in the database.""" + self.test_create_user() # create the first user + + # now duplicate request = {'username': 'username', 'full_name': 'full name', 'password': 'hash'} rv = self.app.put('/user/', data=json.dumps(request), content_type='application/json') - print rv.data + + expected = {"status": "ERROR", + "error": "username already exists"} + + self.assertEqual(rv.status_code, 409) + self.assertEqual(json.loads(rv.data), expected) + + def test_no_json(self): + """Check the status when doing a request that it's not JSON.""" + rv = self.app.put('/user/', + data='', + content_type='text/html') + + expected = {"error": "Request MUST be in JSON format", + "status": "ERROR"} + self.assertEqual(rv.status_code, 400) + self.assertEqual(json.loads(rv.data), expected) + + def test_missing_fields(self): + request = {'password': 'hash'} + rv = self.app.put('/user/', + data=json.dumps(request), + content_type='application/json') + + resp = {'error': 'Missing fields: username, full_name', + 'status': 'ERROR'} + self.assertEqual(rv.status_code, 400) + self.assertEqual(json.loads(rv.data), resp) if __name__ == '__main__': unittest.main()