From 0d5cd2e3e6c14e9eb3e8d7006713ae4b5f0eb7b1 Mon Sep 17 00:00:00 2001 From: Viktor Roytman Date: Mon, 19 Dec 2016 14:54:35 -0500 Subject: [PATCH] Squashed commit of the following: commit 1f625325a6993fe1d4d1d50b89912ff03c02b181 Author: Viktor Roytman Date: Mon Dec 19 14:54:05 2016 -0500 Update version to 0.1.2 commit 094d8c44e242a1da0c0127340bba97577243b361 Author: Viktor Roytman Date: Mon Dec 19 14:53:15 2016 -0500 Use session_scope recipe from SQLAlchemy docs --- dev/commands.py | 4 ++-- minigrid/handlers.py | 8 +++++--- minigrid/models.py | 17 +++++++++++++++++ prod/create_initial_user.py | 4 ++-- prod/docker-compose.yml | 2 +- prod/install.sh | 6 +++--- tests/python/test_handlers.py | 20 ++++++++++---------- tests/python/test_models.py | 4 ++-- tests/python/util.py | 15 +++++++++++++++ 9 files changed, 57 insertions(+), 23 deletions(-) diff --git a/dev/commands.py b/dev/commands.py index 54d8a9d..0902bdf 100755 --- a/dev/commands.py +++ b/dev/commands.py @@ -23,8 +23,8 @@ def create_user(*, email): """Create a user with the given e-mail.""" from minigrid import models session = createdb(ensure=False) - with session.begin_nested(): - session.add(models.User(email=email)) + with models.transaction(session) as tx_session: + tx_session.add(models.User(email=email)) print('Created user with e-mail ' + email) diff --git a/minigrid/handlers.py b/minigrid/handlers.py index 424f854..e16e348 100644 --- a/minigrid/handlers.py +++ b/minigrid/handlers.py @@ -19,7 +19,9 @@ class BaseHandler(tornado.web.RequestHandler): @property def session(self): - """The db session. Use session.begin_nested() for transactions.""" + """The database session. + + Use the models.transaction(session) context manager.""" return self.application.session def get_current_user(self): @@ -89,8 +91,8 @@ def post(self): email = self.get_argument('email') reason = None try: - with self.session.begin_nested(): - self.session.add(models.User(email=email)) + with models.transaction(self.session) as session: + session.add(models.User(email=email)) except IntegrityError as error: if 'user_email_check' in error.orig.pgerror: reason = '{} is not a valid e-mail address'.format(email) diff --git a/minigrid/models.py b/minigrid/models.py index 15fb13b..573010c 100644 --- a/minigrid/models.py +++ b/minigrid/models.py @@ -1,4 +1,6 @@ """ORM models.""" +from contextlib import contextmanager + import sqlalchemy as sa from sqlalchemy.dialects import postgresql as pg from sqlalchemy.ext.declarative import declarative_base @@ -31,6 +33,21 @@ def create_engine(): return sa.create_engine(connection_string) +@contextmanager +def transaction(session): + """Provide a transactional scope around a series of operations. + + Taken from http://docs.sqlalchemy.org/en/latest/orm/session_basics.html + #when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it + """ + try: + yield session + session.commit() + except: + session.rollback() + raise + + def pk(): """Return a primary key UUID column.""" return sa.Column( diff --git a/prod/create_initial_user.py b/prod/create_initial_user.py index 7826e66..8a48559 100755 --- a/prod/create_initial_user.py +++ b/prod/create_initial_user.py @@ -34,8 +34,8 @@ def main(): if users: print('At least one user already exists. Log in as that user.') sys.exit(1) - with session.begin_nested(): - session.add(models.User(email=args.email)) + with models.transaction(session) as tx_session: + tx_session.add(models.User(email=args.email)) print('Created initial user with e-mail', args.email) diff --git a/prod/docker-compose.yml b/prod/docker-compose.yml index 9c4fe41..1ca2ed6 100644 --- a/prod/docker-compose.yml +++ b/prod/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: minigrid: - image: selcolumbia/minigrid-server:0.1.1 + image: selcolumbia/minigrid-server:0.1.2 command: ./prod/run.sh --db_host=db --redis_url=redis://redis:6379/0 --minigrid-website-url=https://www.example.com depends_on: - redis diff --git a/prod/install.sh b/prod/install.sh index 0d3ab4e..75abf4f 100755 --- a/prod/install.sh +++ b/prod/install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env sh -# Minigrid Server installer for version 0.1.1 +# Minigrid Server installer for version 0.1.2 set -e # Do you have docker installed? @@ -108,8 +108,8 @@ $SUDO openssl dhparam -out /etc/letsencrypt/live/$LETSENCRYPT_DIR/dhparam.pem 20 printf "========================================\n" printf " Generating configuration \n" printf "========================================\n" -$CURL -L https://raw.githubusercontent.com/SEL-Columbia/minigrid-server/0.1.1/prod/docker-compose.yml > docker-compose.yml -$CURL -L https://raw.githubusercontent.com/SEL-Columbia/minigrid-server/0.1.1/prod/nginx.conf > nginx.conf +$CURL -L https://raw.githubusercontent.com/SEL-Columbia/minigrid-server/0.1.2/prod/docker-compose.yml > docker-compose.yml +$CURL -L https://raw.githubusercontent.com/SEL-Columbia/minigrid-server/0.1.2/prod/nginx.conf > nginx.conf sed -i s/www.example.com/$LETSENCRYPT_DIR/g docker-compose.yml sed -i s/www.example.com/$LETSENCRYPT_DIR/g nginx.conf diff --git a/tests/python/test_handlers.py b/tests/python/test_handlers.py index 5d9ef4c..6400254 100644 --- a/tests/python/test_handlers.py +++ b/tests/python/test_handlers.py @@ -20,14 +20,14 @@ def BeautifulSoup(page): class TestIndex(HTTPTest): def setUp(self): super().setUp() - with self.session.begin_nested(): + with models.transaction(self.session) as session: self.user = models.User(email='a@a.com') - self.session.add(self.user) + session.add(self.user) self.minigrids = ( models.Minigrid(name='a', day_tariff=1, night_tariff=2), models.Minigrid(name='b', day_tariff=10, night_tariff=20), ) - self.session.add_all(self.minigrids) + session.add_all(self.minigrids) def test_get_not_logged_in(self): response = self.fetch('/') @@ -56,14 +56,14 @@ def test_get_logged_in(self, get_current_user): class TestMinigridView(HTTPTest): def setUp(self): super().setUp() - with self.session.begin_nested(): + with models.transaction(self.session) as session: self.user = models.User(email='a@a.com') - self.session.add(self.user) + session.add(self.user) self.minigrids = ( models.Minigrid(name='a', day_tariff=1, night_tariff=2), models.Minigrid(name='b', day_tariff=10, night_tariff=20), ) - self.session.add_all(self.minigrids) + session.add_all(self.minigrids) def test_get_not_logged_in(self): response = self.fetch( @@ -99,12 +99,12 @@ def test_get_success(self, get_current_user): class TestUsersView(HTTPTest): def setUp(self): super().setUp() - with self.session.begin_nested(): + with models.transaction(self.session) as session: self.users = ( models.User(email='a@a.com'), models.User(email='b@b.com'), ) - self.session.add_all(self.users) + session.add_all(self.users) def test_get_not_logged_in(self): response = self.fetch('/users', follow_redirects=False) @@ -179,8 +179,8 @@ def test_verify_no_xsrf(self): class TestAuthentication(HTTPTest): def create_user(self, email='a@a.com'): - with self.session.begin_nested(): - self.session.add(models.User(email=email)) + with models.transaction(self.session) as session: + session.add(models.User(email=email)) def test_login_missing_email(self): log_1 = ExpectLog('tornado.general', '.*Missing argument email') diff --git a/tests/python/test_models.py b/tests/python/test_models.py index 9df29c2..7506806 100644 --- a/tests/python/test_models.py +++ b/tests/python/test_models.py @@ -5,7 +5,7 @@ class TestUser(Test): def test_create(self): - with self.session.begin_nested(): - self.session.add(models.User(email='a@b.com')) + with models.transaction(self.session) as session: + session.add(models.User(email='a@b.com')) user = self.session.query(models.User).one() self.assertEqual(user.email, 'a@b.com') diff --git a/tests/python/util.py b/tests/python/util.py index 4ef5066..ae76370 100644 --- a/tests/python/util.py +++ b/tests/python/util.py @@ -7,6 +7,7 @@ import fakeredis +from sqlalchemy import event from sqlalchemy.orm import sessionmaker from tornado.testing import AsyncHTTPTestCase @@ -43,6 +44,20 @@ def setUp(self): self.connection = engine.connect() self.transaction = self.connection.begin() self.session = Session(bind=self.connection) + self.session.begin_nested() + + @event.listens_for(self.session, 'after_transaction_end') + def restart_savepoint(session, transaction): + """Taken from + + http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html + #joining-a-session-into-an-external-transaction + -such-as-for-test-suites + """ + if transaction.nested and not transaction._parent.nested: + session.expire_all() + session.begin_nested() + super().setUp() def tearDown(self):