Skip to content

Commit

Permalink
Merge pull request #40 from diverso-lab/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
drorganvidez authored Jul 3, 2024
2 parents 914a292 + 1299637 commit 6cdeefd
Show file tree
Hide file tree
Showing 30 changed files with 1,285 additions and 1,051 deletions.
1 change: 0 additions & 1 deletion .env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ MARIADB_TEST_DATABASE=uvlhubdb_test
MARIADB_USER=uvlhubdb_user
MARIADB_PASSWORD=uvlhubdb_password
MARIADB_ROOT_PASSWORD=uvlhubdb_root_password
WEBHOOK_TOKEN=asdfasdfasda
WORKING_DIR=/app/
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[flake8]
max-line-length = 120
max-line-length = 120
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ ubuntu-bionic-18.04-cloudimg-console.log
nginx.prod.ssl.conf
.version
entrypoint.sh
deployments.log
deployments.log
test_file.txt
12 changes: 0 additions & 12 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,4 @@ def get_authenticated_user():
return None


def datasets_counter() -> int:
from app.blueprints.dataset.models import DataSet
count = DataSet.query.count()
return count


def feature_models_counter() -> int:
from app.blueprints.dataset.models import FeatureModel
count = FeatureModel.query.count()
return count


app = create_app()
16 changes: 2 additions & 14 deletions app/blueprints/auth/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone

from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
Expand All @@ -11,7 +11,7 @@ class User(db.Model, UserMixin):

email = db.Column(db.String(256), unique=True, nullable=False)
password = db.Column(db.String(256), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
created_at = db.Column(db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))

data_sets = db.relationship('DataSet', backref='user', lazy=True)
profile = db.relationship('UserProfile', backref='user', uselist=False)
Expand All @@ -38,15 +38,3 @@ def save(self):
def delete(self):
db.session.delete(self)
db.session.commit()

@staticmethod
def get_by_id(user_id):
return User.query.get(user_id)

@staticmethod
def get_by_email(email):
return User.query.filter_by(email=email).first()

@staticmethod
def get_all():
return User.query.all()
21 changes: 21 additions & 0 deletions app/blueprints/auth/repositories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from app.blueprints.auth.models import User
from core.repositories.BaseRepository import BaseRepository


class UserRepository(BaseRepository):
def __init__(self):
super().__init__(User)

def create(self, commit: bool = True, **kwargs):
password = kwargs.pop("password")
instance = self.model(**kwargs)
instance.set_password(password)
self.session.add(instance)
if commit:
self.session.commit()
else:
self.session.flush()
return instance

def get_by_email(self, email: str):
return self.model.query.filter_by(email=email).first()
60 changes: 25 additions & 35 deletions app/blueprints/auth/routes.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,50 @@
from flask import (render_template, redirect, url_for, request)
from flask import render_template, redirect, url_for, request
from flask_login import current_user, login_user, logout_user

from app.blueprints.auth import auth_bp
from app.blueprints.auth.forms import SignupForm, LoginForm
from app.blueprints.auth.services import AuthenticationService
from app.blueprints.profile.services import UserProfileService

from app.blueprints.profile.models import UserProfile

authentication_service = AuthenticationService()
user_profile_service = UserProfileService()


@auth_bp.route("/signup/", methods=["GET", "POST"])
def show_signup_form():
if current_user.is_authenticated:
return redirect(url_for('public.index'))

form = SignupForm()
error = None
if form.validate_on_submit():
name = form.name.data
surname = form.surname.data
email = form.email.data
password = form.password.data

from app.blueprints.auth.models import User
user = User.get_by_email(email)
if user is not None:
error = f'Email {email} in use'
else:
# Create user
user = User(email=email)
user.set_password(password)
user.save()

# Create user profile
profile = UserProfile(name=name, surname=surname)
profile.user_id = user.id
profile.save()

# Log user
login_user(user, remember=True)
return redirect(url_for('public.index'))
return render_template("auth/signup_form.html", form=form, error=error)
if not authentication_service.is_email_available(email):
return render_template("auth/signup_form.html", form=form, error=f'Email {email} in use')

try:
user = authentication_service.create_with_profile(**form.data)
except Exception as exc:
return render_template("auth/signup_form.html", form=form, error=f'Error creating user: {exc}')

# Log user
login_user(user, remember=True)
return redirect(url_for('public.index'))

return render_template("auth/signup_form.html", form=form)


@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('public.index'))

form = LoginForm()
if request.method == 'POST':
if form.validate_on_submit():
email = form.email.data
password = form.password.data
if AuthenticationService.login(email, password):
return redirect(url_for('public.index'))
else:
error = 'Invalid credentials'
return render_template("auth/login_form.html", form=form, error=error)
if request.method == 'POST' and form.validate_on_submit():
if authentication_service.login(form.email.data, form.password.data):
return redirect(url_for('public.index'))

return render_template("auth/login_form.html", form=form, error='Invalid credentials')

return render_template('auth/login_form.html', form=form)

Expand Down
59 changes: 54 additions & 5 deletions app/blueprints/auth/services.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
from flask_login import login_user

from app.blueprints.auth.models import User
from app.blueprints.auth.repositories import UserRepository
from app.blueprints.profile.repositories import UserProfileRepository
from core.services.BaseService import BaseService


class AuthenticationService:
class AuthenticationService(BaseService):
def __init__(self):
super().__init__(UserRepository())
self.user_profile_repository = UserProfileRepository()

@staticmethod
def login(email, password, remember=True):
user = User.get_by_email(email)
def login(self, email, password, remember=True):
user = self.repository.get_by_email(email)
if user is not None and user.check_password(password):
login_user(user, remember=remember)
return True
return False

def is_email_available(self, email: str) -> bool:
return self.repository.get_by_email(email) is None

def create_with_profile(self, **kwargs):
try:
email = kwargs.pop("email", None)
password = kwargs.pop("password", None)
name = kwargs.pop("name", None)
surname = kwargs.pop("surname", None)

if not email:
raise ValueError("Email is required.")
if not password:
raise ValueError("Password is required.")
if not name:
raise ValueError("Name is required.")
if not surname:
raise ValueError("Surname is required.")

user_data = {
"email": email,
"password": password
}

profile_data = {
"name": name,
"surname": surname,
}

user = self.create(commit=False, **user_data)
profile_data["user_id"] = user.id
self.user_profile_repository.create(**profile_data)
self.repository.session.commit()
except Exception as exc:
self.repository.session.rollback()
raise exc
return user

def update_profile(self, user_profile_id, form):
if form.validate():
updated_instance = self.update(user_profile_id, **form.data)
return updated_instance, None

return None, form.errors
110 changes: 90 additions & 20 deletions app/blueprints/auth/tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import pytest
from flask import url_for

from app.blueprints.auth.services import AuthenticationService
from app.blueprints.auth.repositories import UserRepository
from app.blueprints.profile.repositories import UserProfileRepository

@pytest.fixture(scope='module')

@pytest.fixture(scope="module")
def test_client(test_client):
"""
Extends the test_client fixture to add additional specific data for module testing.
for module testing (por example, new users)
"""
with test_client.application.app_context():
# Add HERE new elements to the database that you want to exist in the test context.
Expand All @@ -17,33 +20,100 @@ def test_client(test_client):


def test_login_success(test_client):
response = test_client.post('/login', data=dict(
email='[email protected]',
password='test1234'
), follow_redirects=True)
response = test_client.post(
"/login", data=dict(email="[email protected]", password="test1234"), follow_redirects=True
)

assert response.request.path != url_for('auth.login'), "Login was unsuccessful"
assert response.request.path != url_for("auth.login"), "Login was unsuccessful"

test_client.get('/logout', follow_redirects=True)
test_client.get("/logout", follow_redirects=True)


def test_login_unsuccessful_bad_email(test_client):
response = test_client.post('/login', data=dict(
email='[email protected]',
password='test1234'
), follow_redirects=True)
response = test_client.post(
"/login", data=dict(email="[email protected]", password="test1234"), follow_redirects=True
)

assert response.request.path == url_for('auth.login'), "Login was unsuccessful"
assert response.request.path == url_for("auth.login"), "Login was unsuccessful"

test_client.get('/logout', follow_redirects=True)
test_client.get("/logout", follow_redirects=True)


def test_login_unsuccessful_bad_password(test_client):
response = test_client.post('/login', data=dict(
email='[email protected]',
password='basspassword'
), follow_redirects=True)
response = test_client.post(
"/login", data=dict(email="[email protected]", password="basspassword"), follow_redirects=True
)

assert response.request.path == url_for("auth.login"), "Login was unsuccessful"

test_client.get("/logout", follow_redirects=True)


def test_signup_user_no_name(test_client):
response = test_client.post(
"/signup", data=dict(surname="Foo", email="[email protected]", password="test1234"), follow_redirects=True
)
assert response.request.path == url_for("auth.show_signup_form"), "Signup was unsuccessful"
assert b"This field is required" in response.data, response.data


def test_signup_user_unsuccessful(test_client):
email = "[email protected]"
response = test_client.post(
"/signup", data=dict(name="Test", surname="Foo", email=email, password="test1234"), follow_redirects=True
)
assert response.request.path == url_for("auth.show_signup_form"), "Signup was unsuccessful"
assert f"Email {email} in use".encode("utf-8") in response.data


def test_signup_user_successful(test_client):
response = test_client.post(
"/signup",
data=dict(name="Foo", surname="Example", email="[email protected]", password="foo1234"),
follow_redirects=True,
)
assert response.request.path == url_for("public.index"), "Signup was unsuccessful"


def test_service_create_with_profie_success(clean_database):
data = {
"name": "Test",
"surname": "Foo",
"email": "[email protected]",
"password": "test1234"
}

AuthenticationService().create_with_profile(**data)

assert UserRepository().count() == 1
assert UserProfileRepository().count() == 1


def test_service_create_with_profile_fail_no_email(clean_database):
data = {
"name": "Test",
"surname": "Foo",
"email": "",
"password": "1234"
}

with pytest.raises(ValueError, match="Email is required."):
AuthenticationService().create_with_profile(**data)

assert UserRepository().count() == 0
assert UserProfileRepository().count() == 0


def test_service_create_with_profile_fail_no_password(clean_database):
data = {
"name": "Test",
"surname": "Foo",
"email": "[email protected]",
"password": ""
}

assert response.request.path == url_for('auth.login'), "Login was unsuccessful"
with pytest.raises(ValueError, match="Password is required."):
AuthenticationService().create_with_profile(**data)

test_client.get('/logout', follow_redirects=True)
assert UserRepository().count() == 0
assert UserProfileRepository().count() == 0
Loading

0 comments on commit 6cdeefd

Please sign in to comment.