Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Management backend - Base - DB support #38

Merged
merged 11 commits into from
May 17, 2020
16 changes: 16 additions & 0 deletions Vagrantfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
db_file_exists = "test -f /vagrant/edison/db.sql"
restore_db = "sudo -u postgres psql edison < /vagrant/edison/db.sql"
db_restored_msg = "echo \"Database restored.\""
db_not_exists_msg = "echo \"db.sql not exists.\""
try_restore_db = "bash -c '#{db_file_exists} && #{restore_db} && #{db_restored_msg} || #{db_not_exists_msg}'"
save_db_data = "sudo -u postgres pg_dump edison > /vagrant/edison/db.sql"

Vagrant.configure("2") do |config|
config.trigger.before :destroy do |trigger|
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it really makes sense to keep the database data if you destroy the environment. And it also looks like not doing that would enable you to remove alot of code...

You do probably need to make sure that if you run vagrant up after you did vagrant halt (without destroy), you don't end up clearing the DB by mistake...

Copy link
Collaborator Author

@DoRTaL94 DoRTaL94 May 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it really makes sense to keep the database data if you destroy the environment. And it also looks like not doing that would enable you to remove alot of code...

Don't we want to be on the safe side by saving the DB data ? what if by mistake someone performs vagrant destroy ?

You do probably need to make sure that if you run vagrant up after you did vagrant halt (without destroy), you don't end up clearing the DB by mistake...

I tested it and the data inside the DB remained unchanged.
I also removed db.sql content to see if it's doing anything, then (after vagrant halt) I executed vagrant up and still the data remained.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want to be on the safe side by saving the db data ? what if by mistake someone perform vagrant destroy ?

not really - Vagrant is your development environment, I don't think being too careful there is worth the extra effort and code. Also consider that if you're going to run tests with the database, you'll probably want it to be empty to begin with so the tests get a predictable initial state.

I tested it and the data inside the DB remained unchanged.
I also removed db.sql content to see if it's doing anything, then (after vagrant halt) I executed vagrant up and still the data remained.

ok, you need to make sure this remains the case if/when you remove the backup and restore code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really - Vagrant is your development environment, I don't think being too careful there is worth the extra effort and code. Also consider that if you're going to run tests with the database, you'll probably want it to be empty to begin with so the tests get a predictable initial state.

Make sense. We'll remove that code :)

ok, you need to make sure this remains the case if/when you remove the backup and restore code.

OK

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also consider that if you're going to run tests with the database, you'll probably want it to be empty to begin with so the tests get a predictable initial state.

@ifireball
I agree that we don't gain much for saving the DB before 'vagrant destroy', but I don't understand how does it affect the tests?
I thought we need to create separate DB for the tests

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ifireball
I agree that we don't gain much for saving the DB before 'vagrant destroy', but I don't understand how does it affect the tests?
I thought we need to create separate DB for the tests

Well, using a separate DB is probably the safest way to use a DB with tests, but:

  1. It takes a considerable amount of work to make a test fixture that properly handles creating and destroying of DB schemas
  2. It takes a lot of discipline to ensure all the App code lets the tests determine which DB is used
  3. Creating and destroying DB schemas before/after each test can have a significant performance impact on the tests.

Some often a "close approximation" where you get ~80% of the benefit with ~20% of the effort is worthwhile. So its possible to have the development DB be shared between the tests as long as the initial conditions are controlled and the tests know how to cleanup after themselves.

trigger.info = "Saving database data inside synced folder..."
trigger.run_remote = {inline: "#{save_db_data}"}
end

config.trigger.after :up do |trigger|
trigger.info = "Trying to restore database from /vagrant/edison/db.sql..."
trigger.run_remote = {inline: "#{try_restore_db}"}
end

config.vm.box = "ubuntu/bionic64"
config.vm.provision :shell, path: "setup.sh"
Expand Down
2 changes: 2 additions & 0 deletions edison/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import os

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from edison.config import get_config_object


# Put app here so the entire app could import it.
app = Flask(__name__)
app.config.from_object(get_config_object(app.config["ENV"]))
basedir = os.path.abspath(os.path.dirname(__file__))
db = SQLAlchemy(app)
6 changes: 6 additions & 0 deletions edison/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@


app = edison.app
db = edison.db

# Creates all tables defined in the database models and the only ones that are not created yet.
# If there's any change in the database models you should perform a migration to apply this change in the database itself.
# More about database migrations can be found in /edison/migrations/README.
db.create_all()

@app.route("/")
def index():
Expand Down
4 changes: 4 additions & 0 deletions edison/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ def get_config_object(env_keyword: str):
class Config:
ENV_KEYWORD = ""
DEBUG = False
# Turns off the Flask-SQLAlchemy event system
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:[email protected]/edison'

# PostgreSQL connection string should be updated once an actual production environment is established.
class ProductionConfig(Config):
ENV_KEYWORD = "production"

Expand Down
2 changes: 2 additions & 0 deletions edison/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .token import Token
from .user import User
12 changes: 12 additions & 0 deletions edison/models/token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from edison import db


class Token(db.Model):
__tablename__ = 'token_blacklist'

id = db.Column(db.Integer, primary_key=True)
jti = db.Column(db.String(150), nullable=False, unique=True)
creation_timestamp = db.Column(db.TIMESTAMP(timezone=False), nullable=False)

def __repr__(self):
return f"<Token: jti: {self.jti}, creation time: {self.creation_timestamp}>"
25 changes: 25 additions & 0 deletions edison/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from edison import db


class User(db.Model):
__tablename__ = 'users'
__table_args__ = {'extend_existing': True}

id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(150), nullable=False)
first_name = db.Column(db.String(50), nullable=False)
last_name = db.Column(db.String(50), nullable=False)
email = db.Column(db.String(150), nullable=False)

def to_json(self):
return {
"username": self.username,
"first_name": self.first_name,
"last_name": self.last_name,
"email": self.email
}

def __repr__(self):
return f"<User: id = {self.id}, first_name = {self.first_name}, " \
f"last_name = {self.last_name}, username = {self.username}, email = {self.email}>"
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Flask==1.1.1
flask-sqlalchemy==2.4.1
psycopg2-binary==2.8.5
4 changes: 4 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ sudo apt-get install -y postgresql postgresql-contrib
echo "install requirements"
pip3 install -r /vagrant/requirements.txt

echo "configuring database"
sudo -u postgres createdb edison
sudo -u postgres psql -c "ALTER ROLE postgres WITH PASSWORD 'edison';"

export FLASK_ENV=development

echo "running app.py"
Expand Down