Skip to content

Commit

Permalink
Merge branch 'dev' into add-social-features
Browse files Browse the repository at this point in the history
  • Loading branch information
SamR1 committed Oct 3, 2023
2 parents 7f5e037 + cc84a0a commit cce86a9
Show file tree
Hide file tree
Showing 17 changed files with 563 additions and 444 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/.tests-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,60 @@ jobs:
sleep 5
nohup flask worker --processes=1 >> nohup.out 2>&1 &
pytest e2e --driver Remote --capability browserName firefox --selenium-host selenium --selenium-port 4444
end2end_package_update:
name: e2e tests after update
runs-on: ubuntu-latest
needs: ["python"]
container: python:3.11
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: fittrackee_test
POSTGRES_USER: fittrackee
POSTGRES_PASSWORD: fittrackee
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
selenium:
image: selenium/standalone-firefox
mailhog:
image: mailhog/mailhog:latest
redis:
image: redis:latest
env:
APP_SETTINGS: fittrackee.config.End2EndTestingConfig
EMAIL_URL: "smtp://mailhog:1025"
REDIS_URL: "redis://redis:6379"
HOST: "0.0.0.0"
PORT: 5000
steps:
- uses: actions/checkout@v3
- name: Update pip and install build
run: python3 -m pip install --upgrade pip build
- name: Create and source virtual environment
run: |
python3 -m venv .venv
. .venv/bin/activate
- name: Install previous version of fittrackee from PyPI
run: python3 -m pip install fittrackee
- name: Run migrations
run: ftcli db upgrade
- name: Build fittrackee package
run: python3 -m build
- name: Install fittrackee package to update instance
run: python3 -m pip install dist/fittrackee-$(cat VERSION).tar.gz
- name: Run migrations to update database
run: ftcli db upgrade
- name: Install pytest and selenium
run: python3 -m pip install pytest==7.4.0 pytest-selenium==4.0.1 selenium==4.9.0 pytest-html==3.2.0
- name: Start application and run tests with Selenium
run: |
setsid nohup fittrackee >> nohup.out 2>&1 &
export TEST_APP_URL=http://$(hostname --ip-address):5000
sleep 5
nohup flask worker --processes=1 >> nohup.out 2>&1 &
pytest e2e --driver Remote --capability browserName firefox --selenium-host selenium --selenium-port 4444
10 changes: 7 additions & 3 deletions fittrackee/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import ProgrammingError
from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.orm import DeclarativeBase
from werkzeug.middleware.proxy_fix import ProxyFix

from fittrackee.emails.email import EmailService
Expand All @@ -39,8 +39,12 @@
)
appLog = logging.getLogger('fittrackee')

db = SQLAlchemy()
BaseModel: DeclarativeMeta = db.Model

class Base(DeclarativeBase):
pass


db = SQLAlchemy(model_class=Base)
bcrypt = Bcrypt()
migrate = Migrate()
email_service = EmailService()
Expand Down
31 changes: 18 additions & 13 deletions fittrackee/application/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@
from sqlalchemy import exc
from sqlalchemy.engine.base import Connection
from sqlalchemy.event import listens_for
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm.mapper import Mapper
from sqlalchemy.orm.session import Session
from sqlalchemy.sql import text

from fittrackee import BaseModel, db
from fittrackee import db
from fittrackee.users.models import User


class AppConfig(BaseModel):
class AppConfig(db.Model): # type: ignore
__tablename__ = 'app_config'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
max_users = db.Column(db.Integer, default=0, nullable=False)
gpx_limit_import = db.Column(db.Integer, default=10, nullable=False)
max_single_file_size = db.Column(
id = mapped_column(db.Integer, primary_key=True, autoincrement=True)
max_users = mapped_column(db.Integer, default=0, nullable=False)
gpx_limit_import = mapped_column(db.Integer, default=10, nullable=False)
max_single_file_size = mapped_column(
db.Integer, default=1048576, nullable=False
)
max_zip_file_size = db.Column(db.Integer, default=10485760, nullable=False)
admin_contact = db.Column(db.String(255), nullable=True)
privacy_policy_date = db.Column(db.DateTime, nullable=True)
privacy_policy = db.Column(db.Text, nullable=True)
about = db.Column(db.Text, nullable=True)
max_zip_file_size = mapped_column(
db.Integer, default=10485760, nullable=False
)
admin_contact = mapped_column(db.String(255), nullable=True)
privacy_policy_date = mapped_column(db.DateTime, nullable=True)
privacy_policy = mapped_column(db.Text, nullable=True)
about = mapped_column(db.Text, nullable=True)

@property
def is_registration_enabled(self) -> bool:
Expand All @@ -33,8 +37,9 @@ def is_registration_enabled(self) -> bool:
except exc.ProgrammingError as e:
# workaround for user model related migrations
if 'psycopg2.errors.UndefinedColumn' in str(e):
result = db.engine.execute("SELECT COUNT(*) FROM users;")
nb_users = result.fetchone()[0]
query = db.session.execute(text("SELECT COUNT(*) FROM users;"))
result = query.fetchone()
nb_users = result[0] if result else 0
else:
raise e
return self.max_users == 0 or nb_users < self.max_users
Expand Down
55 changes: 27 additions & 28 deletions fittrackee/comments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from sqlalchemy.dialects import postgresql
from sqlalchemy.engine.base import Connection
from sqlalchemy.event import listens_for
from sqlalchemy.orm import mapped_column, relationship
from sqlalchemy.orm.mapper import Mapper
from sqlalchemy.orm.session import Session
from sqlalchemy.types import Enum

from fittrackee import BaseModel, db
from fittrackee import db
from fittrackee.privacy_levels import PrivacyLevel, can_view
from fittrackee.utils import encode_uuid

Expand Down Expand Up @@ -56,60 +57,58 @@ def get_comments(
return comments_filter.order_by(Comment.created_at.asc()).all()


class Comment(BaseModel):
class Comment(db.Model): # type: ignore
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
uuid = db.Column(
id = mapped_column(db.Integer, primary_key=True, autoincrement=True)
uuid = mapped_column(
postgresql.UUID(as_uuid=True),
default=uuid4,
unique=True,
nullable=False,
)
user_id = db.Column(
user_id = mapped_column(
db.Integer,
db.ForeignKey('users.id', ondelete='CASCADE'),
index=True,
nullable=False,
)
workout_id = db.Column(
workout_id = mapped_column(
db.Integer,
db.ForeignKey('workouts.id', ondelete="SET NULL"),
index=True,
nullable=True,
)
reply_to = db.Column(
reply_to = mapped_column(
db.Integer,
db.ForeignKey('comments.id', ondelete="SET NULL"),
index=True,
nullable=True,
)
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
modification_date = db.Column(db.DateTime, nullable=True)
text = db.Column(db.String(), nullable=False)
text_visibility = db.Column(
created_at = mapped_column(db.DateTime, default=datetime.datetime.utcnow)
modification_date = mapped_column(db.DateTime, nullable=True)
text = mapped_column(db.String(), nullable=False)
text_visibility = mapped_column(
Enum(PrivacyLevel, name='privacy_levels'),
server_default='PRIVATE',
nullable=False,
)

parent_comment = db.relationship(
'Comment', remote_side=[id], lazy='joined'
)
mentions = db.relationship(
parent_comment = relationship('Comment', remote_side=[id], lazy='joined')
mentions = relationship(
"Mention",
lazy=True,
cascade="all, delete",
backref=db.backref("comment", lazy="joined", single_parent=True),
)
mentioned_users = db.relationship(
mentioned_users = relationship(
"User",
secondary="mentions",
primaryjoin="Comment.id == Mention.comment_id",
secondaryjoin="Mention.user_id == User.id",
lazy="dynamic",
viewonly=True,
)
likes = db.relationship(
likes = relationship(
"User",
secondary="comment_likes",
primaryjoin="Comment.id == CommentLike.comment_id",
Expand Down Expand Up @@ -244,20 +243,20 @@ def serialize(
}


class Mention(BaseModel):
class Mention(db.Model): # type: ignore
__tablename__ = 'mentions'

comment_id = db.Column(
comment_id = mapped_column(
db.Integer,
db.ForeignKey('comments.id', ondelete="CASCADE"),
primary_key=True,
)
user_id = db.Column(
user_id = mapped_column(
db.Integer,
db.ForeignKey('users.id', ondelete="CASCADE"),
primary_key=True,
)
created_at = db.Column(
created_at = mapped_column(
db.DateTime, nullable=False, default=datetime.datetime.utcnow
)

Expand Down Expand Up @@ -409,28 +408,28 @@ def receive_after_flush(session: Session, context: Any) -> None:
).delete()


class CommentLike(BaseModel):
class CommentLike(db.Model): # type: ignore
__tablename__ = 'comment_likes'
__table_args__ = (
db.UniqueConstraint(
'user_id', 'comment_id', name='user_id_comment_id_unique'
),
)
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
created_at = db.Column(db.DateTime, nullable=False)
user_id = db.Column(
id = mapped_column(db.Integer, primary_key=True, autoincrement=True)
created_at = mapped_column(db.DateTime, nullable=False)
user_id = mapped_column(
db.Integer,
db.ForeignKey('users.id', ondelete='CASCADE'),
nullable=False,
)
comment_id = db.Column(
comment_id = mapped_column(
db.Integer,
db.ForeignKey('comments.id', ondelete="CASCADE"),
nullable=False,
)

user = db.relationship("User", lazy=True)
comment = db.relationship("Comment", lazy=True)
user = relationship("User", lazy=True)
comment = relationship("Comment", lazy=True)

def __init__(
self,
Expand Down
3 changes: 2 additions & 1 deletion fittrackee/migrations/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import click
from flask_migrate import upgrade
from sqlalchemy.sql import text

from fittrackee import db
from fittrackee.cli.app import app
Expand Down Expand Up @@ -36,7 +37,7 @@ def drop_db() -> None:
err=True,
)
return
db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
db.session.execute(text("DROP TABLE IF EXISTS alembic_version;"))
db.drop_all()
db.session.commit()
click.echo('Database dropped.')
Expand Down
2 changes: 1 addition & 1 deletion fittrackee/oauth2/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
def clean_tokens(days: int) -> int:
sql = """
DELETE FROM oauth2_token
WHERE oauth2_token.issued_at + oauth2_token.expires_in < %(limit)s;
WHERE oauth2_token.issued_at + oauth2_token.expires_in < :limit;
"""
return clean(sql, days)
39 changes: 20 additions & 19 deletions fittrackee/oauth2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,22 @@
)
from sqlalchemy.engine.base import Connection
from sqlalchemy.event import listens_for
from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.orm import mapped_column, relationship
from sqlalchemy.orm.mapper import Mapper
from sqlalchemy.orm.session import Session
from sqlalchemy.sql import text

from fittrackee import db

BaseModel: DeclarativeMeta = db.Model


class OAuth2Client(BaseModel, OAuth2ClientMixin):
class OAuth2Client(OAuth2ClientMixin, db.Model): # type: ignore
__tablename__ = 'oauth2_client'

id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
id = mapped_column(db.Integer, primary_key=True)
user_id = mapped_column(
db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), index=True
)
user = db.relationship('User')
user = relationship('User')

def serialize(self, with_secret: bool = False) -> Dict:
client = {
Expand Down Expand Up @@ -63,7 +62,9 @@ def receive_after_flush(session: Session, context: Any) -> None:
).delete(synchronize_session=False)


class OAuth2AuthorizationCode(BaseModel, OAuth2AuthorizationCodeMixin):
class OAuth2AuthorizationCode(
OAuth2AuthorizationCodeMixin, db.Model # type: ignore
):
__tablename__ = 'oauth2_code'
__table_args__ = (
db.Index(
Expand All @@ -72,21 +73,21 @@ class OAuth2AuthorizationCode(BaseModel, OAuth2AuthorizationCodeMixin):
),
)

id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
id = mapped_column(db.Integer, primary_key=True)
user_id = mapped_column(
db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), index=True
)
user = db.relationship('User')
user = relationship('User')


class OAuth2Token(BaseModel, OAuth2TokenMixin):
class OAuth2Token(OAuth2TokenMixin, db.Model): # type: ignore
__tablename__ = 'oauth2_token'

id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
id = mapped_column(db.Integer, primary_key=True)
user_id = mapped_column(
db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), index=True
)
user = db.relationship('User')
user = relationship('User')

def is_refresh_token_active(self) -> bool:
if self.is_revoked():
Expand All @@ -98,10 +99,10 @@ def is_refresh_token_active(self) -> bool:
def revoke_client_tokens(cls, client_id: str) -> None:
sql = """
UPDATE oauth2_token
SET access_token_revoked_at = %(revoked_at)s
WHERE client_id = %(client_id)s;
SET access_token_revoked_at = :revoked_at
WHERE client_id = :client_id;
"""
db.engine.execute(
sql, {'client_id': client_id, 'revoked_at': int(time.time())}
db.session.execute(
text(sql), {'client_id': client_id, 'revoked_at': int(time.time())}
)
db.session.commit()
Loading

0 comments on commit cce86a9

Please sign in to comment.