mirror of
https://github.com/pds-nest/nest.git
synced 2024-11-24 22:14:18 +00:00
💥 Create test framework
This commit is contained in:
parent
d7e55e556d
commit
008efa1c24
37 changed files with 324 additions and 192 deletions
23
.idea/runConfigurations/Backend__Run_tests.xml
Normal file
23
.idea/runConfigurations/Backend__Run_tests.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Backend: Run tests" type="tests" factoryName="py.test">
|
||||
<module name="backend" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
<env name="FLASK_CONFIG" value="../config.py" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="$USER_HOME$/.cache/pypoetry/virtualenvs/nest-backend-3ypGnHKr-py3.9/bin/python" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/code/backend" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="_new_keywords" value="""" />
|
||||
<option name="_new_parameters" value="""" />
|
||||
<option name="_new_additionalArguments" value="""" />
|
||||
<option name="_new_target" value=""nest_backend"" />
|
||||
<option name="_new_targetType" value=""PYTHON"" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,22 +1,23 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Backend: [Make your own run config!]" type="PythonConfigurationType" factoryName="Python" editBeforeRun="true">
|
||||
<module name="frontend" />
|
||||
<configuration default="false" name="Backend: Start server" type="PythonConfigurationType" factoryName="Python">
|
||||
<module name="backend" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
<env name="FLASK_CONFIG" value="../config.py" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="C:\Users\giova\AppData\Local\pypoetry\Cache\virtualenvs\nest-backend-gZK59SuL-py3.9\Scripts\python.exe" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="SDK_HOME" value="$USER_HOME$/.cache/pypoetry/virtualenvs/nest-backend-3ypGnHKr-py3.9/bin/python" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/code/backend" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="" />
|
||||
<option name="SCRIPT_NAME" value="nest_backend" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="MODULE_MODE" value="true" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
5
code/backend/.gitignore
vendored
5
code/backend/.gitignore
vendored
|
@ -1,3 +1,8 @@
|
|||
# Flask config
|
||||
|
||||
config.py
|
||||
configtest.py
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/nest_backend" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/nest_backend/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/old_unittest_tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/docs/_build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Poetry (backend)" jdkType="Python SDK" />
|
||||
|
|
|
@ -1,57 +1,23 @@
|
|||
"""
|
||||
This is the runner for the server.
|
||||
"""
|
||||
import werkzeug.middleware.proxy_fix
|
||||
from .routes import *
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .gestione import *
|
||||
from flask_cors import CORS
|
||||
from flask_jwt_extended import *
|
||||
from .app import app
|
||||
from .api_spec import spec
|
||||
from .swagger import swagger_ui_blueprint, SWAGGER_URL
|
||||
|
||||
Base.init_app(app=app)
|
||||
jwt = JWTManager(app)
|
||||
cors = CORS(app)
|
||||
app.config['CORS_HEADERS'] = 'Content-Type'
|
||||
|
||||
reverse_proxy_app = werkzeug.middleware.proxy_fix.ProxyFix(app=app, x_for=1, x_proto=0, x_host=1, x_port=0, x_prefix=0)
|
||||
# Routes setup
|
||||
|
||||
app.add_url_rule("/doa", view_func=page_doa, methods=["GET", "POST"])
|
||||
app.add_url_rule("/api/v1/login", view_func=page_login, methods=["POST"])
|
||||
app.add_url_rule("/api/v1/users/", view_func=page_users, methods=["GET", "POST"])
|
||||
app.add_url_rule("/api/v1/users/<string:email>", view_func=page_user, methods=["GET", "PATCH", "DELETE"])
|
||||
app.add_url_rule("/api/v1/repositories/", view_func=page_repositories, methods=["GET", "POST"])
|
||||
app.add_url_rule("/api/v1/repositories/<int:rid>", view_func=page_repository, methods=["GET", "PATCH", "DELETE", "PUT"])
|
||||
app.add_url_rule("/api/v1/repositories/<int:rid>/conditions", view_func=page_repository_conditions,
|
||||
methods=["GET", "POST"])
|
||||
app.add_url_rule("/api/v1/conditions/<int:cid>", view_func=page_condition, methods=["GET", "PATCH", "DELETE"])
|
||||
|
||||
app.register_error_handler(Exception, error_handler)
|
||||
app.register_blueprint(swagger_ui_blueprint, url_prefix=SWAGGER_URL)
|
||||
from .app import app, extension_sqlalchemy
|
||||
|
||||
|
||||
with app.test_request_context():
|
||||
print(" * Getting docs ready...")
|
||||
for fn_name in app.view_functions:
|
||||
if fn_name == 'static':
|
||||
continue
|
||||
view_fn = app.view_functions[fn_name]
|
||||
spec.path(view=view_fn)
|
||||
print(" * Docs have been compiled on http://127.0.0.1:5000/docs")
|
||||
print(" * Swagger docs will be available at http://127.0.0.1:5000/docs")
|
||||
|
||||
|
||||
@app.route("/docs/swagger.json")
|
||||
def create_swagger_doc():
|
||||
return jsonify(spec.to_dict())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with app.app_context():
|
||||
Base.create_all(app=app)
|
||||
with app.app_context():
|
||||
print(" * Creating database tables...")
|
||||
extension_sqlalchemy.create_all(app=app)
|
||||
if not User.query.filter_by(isAdmin=True).all():
|
||||
Base.session.add(
|
||||
print(" * Creating default admin account...")
|
||||
extension_sqlalchemy.session.add(
|
||||
User(email="admin@admin.com", password=gen_password("password"), username="admin", isAdmin=True))
|
||||
Base.session.commit()
|
||||
app.run(debug=__debug__)
|
||||
extension_sqlalchemy.session.commit()
|
||||
print(" * Created! Username: admin | Password: password")
|
||||
|
||||
app.run(debug=__debug__)
|
||||
|
|
|
@ -1,16 +1,101 @@
|
|||
from flask import Flask
|
||||
import os
|
||||
from flask_cors import CORS as FlaskCORS
|
||||
from flask_jwt_extended import JWTManager as FlaskJWTManager
|
||||
from flask_sqlalchemy import SQLAlchemy as FlaskSQLAlchemy
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix as MiddlewareProxyFix
|
||||
|
||||
from . import database, routes, gestione, swagger
|
||||
from .api_spec import spec
|
||||
|
||||
# --- MAIN APP ---
|
||||
|
||||
app = Flask(__name__)
|
||||
if os.getenv('COOKIE_SECRET'):
|
||||
app.secret_key = os.getenv('COOKIE_SECRET')
|
||||
else:
|
||||
app.secret_key = "testing"
|
||||
if os.getenv("JWT_SECRET_KEY"):
|
||||
app.config["JWT_SECRET_KEY"] = os.getenv("JWT_SECRET_KEY")
|
||||
else:
|
||||
app.config["JWT_SECRET_KEY"] = "testing"
|
||||
if os.getenv("DATABASE_URI"):
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URI')
|
||||
else:
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:password@localhost:5432/PdSDev'
|
||||
|
||||
# --- APP CONFIG
|
||||
|
||||
app.config.from_envvar("FLASK_CONFIG")
|
||||
|
||||
# --- EXTENSIONS ---
|
||||
|
||||
app.config['CORS_HEADERS'] = 'Content-Type'
|
||||
extension_cors = FlaskCORS(app=app)
|
||||
|
||||
extension_jwt = FlaskJWTManager(app=app)
|
||||
|
||||
extension_sqlalchemy = database.ext
|
||||
extension_sqlalchemy.init_app(app=app)
|
||||
|
||||
# --- API ROUTES ---
|
||||
|
||||
app.add_url_rule(
|
||||
"/doa",
|
||||
view_func=routes.page_doa,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/login",
|
||||
view_func=routes.page_login,
|
||||
methods=["POST"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/users/",
|
||||
view_func=routes.page_users,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/users/<string:email>",
|
||||
view_func=routes.page_user,
|
||||
methods=["GET", "PATCH", "DELETE"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/repositories/",
|
||||
view_func=routes.page_repositories,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/repositories/<int:rid>",
|
||||
view_func=routes.page_repository,
|
||||
methods=["GET", "PATCH", "DELETE", "PUT"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/repositories/<int:rid>/conditions",
|
||||
view_func=routes.page_repository_conditions,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
app.add_url_rule(
|
||||
"/api/v1/conditions/<int:cid>",
|
||||
view_func=routes.page_condition,
|
||||
methods=["GET", "PATCH", "DELETE"],
|
||||
)
|
||||
|
||||
# --- SWAGGER DOCS ---
|
||||
|
||||
app.register_blueprint(
|
||||
swagger.swagger_ui_blueprint,
|
||||
url_prefix=swagger.SWAGGER_URL
|
||||
)
|
||||
|
||||
with app.test_request_context():
|
||||
for fn_name in app.view_functions:
|
||||
if fn_name == 'static':
|
||||
continue
|
||||
view_fn = app.view_functions[fn_name]
|
||||
spec.path(view=view_fn)
|
||||
|
||||
app.add_url_rule(
|
||||
"/docs/swagger.json",
|
||||
view_func=lambda: gestione.jsonify(spec.to_dict()),
|
||||
methods=["GET"],
|
||||
)
|
||||
|
||||
# --- ERROR HANDLER ---
|
||||
|
||||
app.register_error_handler(
|
||||
Exception,
|
||||
gestione.error_handler
|
||||
)
|
||||
|
||||
# --- REVERSE PROXY ---
|
||||
|
||||
if not __debug__:
|
||||
app = MiddlewareProxyFix(app=app, x_for=1, x_proto=0, x_host=1, x_port=0, x_prefix=0)
|
||||
|
|
|
@ -3,4 +3,4 @@ This module imports all the tables and the declarative base
|
|||
"""
|
||||
|
||||
from .tables import *
|
||||
from .base import Base
|
||||
from .base import ext
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
This module creates the declarative base
|
||||
"""
|
||||
import flask_sqlalchemy
|
||||
from sqlalchemy.orm import backref
|
||||
|
||||
Base = flask_sqlalchemy.SQLAlchemy()
|
||||
ext = flask_sqlalchemy.SQLAlchemy()
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
This module defines the Alert database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
|
||||
class Alert(Base.Model):
|
||||
class Alert(ext.Model):
|
||||
__tablename__ = "alert"
|
||||
id = Base.Column(Base.Integer, primary_key=True)
|
||||
name = Base.Column(Base.String, nullable=False)
|
||||
limit = Base.Column(Base.Integer, nullable=False)
|
||||
window_size = Base.Column(Base.Integer, nullable=False)
|
||||
id = ext.Column(ext.Integer, primary_key=True)
|
||||
name = ext.Column(ext.String, nullable=False)
|
||||
limit = ext.Column(ext.Integer, nullable=False)
|
||||
window_size = ext.Column(ext.Integer, nullable=False)
|
||||
# Foreign Keys
|
||||
repository_id = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"), nullable=False)
|
||||
repository_id = ext.Column(ext.Integer, ext.ForeignKey("repository.id", ondelete="CASCADE"), nullable=False)
|
||||
# Relationships
|
||||
repository = Base.relationship("Repository", back_populates="alerts")
|
||||
notifications = Base.relationship("Notification", back_populates="alert", cascade="all, delete")
|
||||
operations = Base.relationship("BoolOperation", back_populates="alert", cascade="all, delete")
|
||||
repository = ext.relationship("Repository", back_populates="alerts")
|
||||
notifications = ext.relationship("Notification", back_populates="alert", cascade="all, delete")
|
||||
operations = ext.relationship("BoolOperation", back_populates="alert", cascade="all, delete")
|
|
@ -2,12 +2,12 @@
|
|||
This module defines the Authorization database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
|
||||
class Authorization(Base.Model):
|
||||
rid = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"), primary_key=True)
|
||||
email = Base.Column(Base.String, Base.ForeignKey("user.email", ondelete="CASCADE"), primary_key=True)
|
||||
class Authorization(ext.Model):
|
||||
rid = ext.Column(ext.Integer, ext.ForeignKey("repository.id", ondelete="CASCADE"), primary_key=True)
|
||||
email = ext.Column(ext.String, ext.ForeignKey("user.email", ondelete="CASCADE"), primary_key=True)
|
||||
# Relationships
|
||||
repository = Base.relationship("Repository", back_populates="authorizations")
|
||||
user = Base.relationship("User", back_populates="authorizations")
|
||||
repository = ext.relationship("Repository", back_populates="authorizations")
|
||||
user = ext.relationship("User", back_populates="authorizations")
|
|
@ -2,24 +2,25 @@
|
|||
This module defines the BoolOperation database class.
|
||||
"""
|
||||
|
||||
from ..base import Base, backref
|
||||
from ..base import ext
|
||||
from .Enums import OperationType
|
||||
from sqlalchemy.orm import backref
|
||||
|
||||
|
||||
class BoolOperation(Base.Model):
|
||||
class BoolOperation(ext.Model):
|
||||
__tablename__ = "bool_operation"
|
||||
id = Base.Column(Base.Integer, primary_key=True)
|
||||
operation = Base.Column(Base.Enum(OperationType), nullable=False)
|
||||
isRoot = Base.Column(Base.Boolean, default=False, nullable=False)
|
||||
id = ext.Column(ext.Integer, primary_key=True)
|
||||
operation = ext.Column(ext.Enum(OperationType), nullable=False)
|
||||
isRoot = ext.Column(ext.Boolean, default=False, nullable=False)
|
||||
# Foreign Keys
|
||||
condition_id = Base.Column(Base.Integer, Base.ForeignKey("condition.id"))
|
||||
node_1_id = Base.Column(Base.Integer, Base.ForeignKey("bool_operation.id"))
|
||||
node_2_id = Base.Column(Base.Integer, Base.ForeignKey("bool_operation.id"))
|
||||
alert_id = Base.Column(Base.Integer, Base.ForeignKey("alert.id"))
|
||||
condition_id = ext.Column(ext.Integer, ext.ForeignKey("condition.id"))
|
||||
node_1_id = ext.Column(ext.Integer, ext.ForeignKey("bool_operation.id"))
|
||||
node_2_id = ext.Column(ext.Integer, ext.ForeignKey("bool_operation.id"))
|
||||
alert_id = ext.Column(ext.Integer, ext.ForeignKey("alert.id"))
|
||||
# Relationships
|
||||
condition = Base.relationship("Condition", back_populates="operations")
|
||||
node_1 = Base.relationship("BoolOperation", primaryjoin=("bool_operation.c.node_1_id==bool_operation.c.id"),
|
||||
condition = ext.relationship("Condition", back_populates="operations")
|
||||
node_1 = ext.relationship("BoolOperation", primaryjoin=("bool_operation.c.node_1_id==bool_operation.c.id"),
|
||||
remote_side="BoolOperation.id", backref=backref("father_1", uselist=False))
|
||||
node_2 = Base.relationship("BoolOperation", primaryjoin=("bool_operation.c.node_2_id==bool_operation.c.id"),
|
||||
node_2 = ext.relationship("BoolOperation", primaryjoin=("bool_operation.c.node_2_id==bool_operation.c.id"),
|
||||
remote_side="BoolOperation.id", backref=backref("father_2", uselist=False))
|
||||
alert = Base.relationship("Alert", back_populates="operations")
|
||||
alert = ext.relationship("Alert", back_populates="operations")
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
This module defines the Composed database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
|
||||
class Composed(Base.Model):
|
||||
class Composed(ext.Model):
|
||||
__tablename__ = "composed"
|
||||
rid = Base.Column(Base.Integer, Base.ForeignKey("repository.id"), primary_key=True)
|
||||
snowflake = Base.Column(Base.String, Base.ForeignKey("tweet.snowflake"), primary_key=True)
|
||||
rid = ext.Column(ext.Integer, ext.ForeignKey("repository.id"), primary_key=True)
|
||||
snowflake = ext.Column(ext.String, ext.ForeignKey("tweet.snowflake"), primary_key=True)
|
||||
# Relationships
|
||||
repository = Base.relationship("Repository", back_populates="tweets")
|
||||
tweet = Base.relationship("Tweet", back_populates="repositories")
|
||||
repository = ext.relationship("Repository", back_populates="tweets")
|
||||
tweet = ext.relationship("Tweet", back_populates="repositories")
|
|
@ -2,21 +2,21 @@
|
|||
This module defines the Condition database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
from .Enums import ConditionType
|
||||
|
||||
|
||||
class Condition(Base.Model):
|
||||
class Condition(ext.Model):
|
||||
__tablename__ = "condition"
|
||||
id = Base.Column(Base.Integer, primary_key=True)
|
||||
type = Base.Column(Base.Enum(ConditionType), nullable=False)
|
||||
content = Base.Column(Base.String, nullable=False)
|
||||
id = ext.Column(ext.Integer, primary_key=True)
|
||||
type = ext.Column(ext.Enum(ConditionType), nullable=False)
|
||||
content = ext.Column(ext.String, nullable=False)
|
||||
# FK
|
||||
repository_id = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"))
|
||||
repository_id = ext.Column(ext.Integer, ext.ForeignKey("repository.id", ondelete="CASCADE"))
|
||||
# Relationships
|
||||
repository = Base.relationship("Repository", back_populates="conditions", cascade="all, delete")
|
||||
tweets = Base.relationship("Contains", back_populates="condition")
|
||||
operations = Base.relationship("BoolOperation", back_populates="condition")
|
||||
repository = ext.relationship("Repository", back_populates="conditions", cascade="all, delete")
|
||||
tweets = ext.relationship("Contains", back_populates="condition")
|
||||
operations = ext.relationship("BoolOperation", back_populates="condition")
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
This module defines the Contains database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
|
||||
class Contains(Base.Model):
|
||||
class Contains(ext.Model):
|
||||
__tablename__ = "contains"
|
||||
cid = Base.Column(Base.Integer, Base.ForeignKey("condition.id"), primary_key=True)
|
||||
snowflake = Base.Column(Base.String, Base.ForeignKey("tweet.snowflake"), primary_key=True)
|
||||
cid = ext.Column(ext.Integer, ext.ForeignKey("condition.id"), primary_key=True)
|
||||
snowflake = ext.Column(ext.String, ext.ForeignKey("tweet.snowflake"), primary_key=True)
|
||||
# Relationships
|
||||
condition = Base.relationship("Condition", back_populates="tweets")
|
||||
tweet = Base.relationship("Tweet", back_populates="conditions")
|
||||
condition = ext.relationship("Condition", back_populates="tweets")
|
||||
tweet = ext.relationship("Tweet", back_populates="conditions")
|
|
@ -2,13 +2,14 @@
|
|||
This module defines the Notification database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
class Notification(Base.Model):
|
||||
|
||||
class Notification(ext.Model):
|
||||
__tablename__ = "notification"
|
||||
id = Base.Column(Base.Integer, primary_key=True)
|
||||
ora = Base.Column(Base.DateTime, nullable=False)
|
||||
id = ext.Column(ext.Integer, primary_key=True)
|
||||
ora = ext.Column(ext.DateTime, nullable=False)
|
||||
# Foreign Key
|
||||
alert_id = Base.Column(Base.Integer, Base.ForeignKey("alert.id"), nullable=False)
|
||||
alert_id = ext.Column(ext.Integer, ext.ForeignKey("alert.id"), nullable=False)
|
||||
# Relationships
|
||||
alert = Base.relationship("Alert", back_populates="notifications")
|
||||
alert = ext.relationship("Alert", back_populates="notifications")
|
||||
|
|
|
@ -2,30 +2,30 @@
|
|||
This module defines the :class:`.Repository` database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
from .Enums import ConditionMode
|
||||
|
||||
|
||||
class Repository(Base.Model):
|
||||
class Repository(ext.Model):
|
||||
__tablename__ = "repository"
|
||||
|
||||
# Columns
|
||||
id = Base.Column(Base.Integer, primary_key=True)
|
||||
name = Base.Column(Base.String, nullable=False)
|
||||
start = Base.Column(Base.DateTime, nullable=True)
|
||||
end = Base.Column(Base.DateTime, nullable=True)
|
||||
is_active = Base.Column(Base.Boolean, nullable=False, default=False)
|
||||
evaluation_mode = Base.Column(Base.Enum(ConditionMode), default=ConditionMode.all_or.value)
|
||||
id = ext.Column(ext.Integer, primary_key=True)
|
||||
name = ext.Column(ext.String, nullable=False)
|
||||
start = ext.Column(ext.DateTime, nullable=True)
|
||||
end = ext.Column(ext.DateTime, nullable=True)
|
||||
is_active = ext.Column(ext.Boolean, nullable=False, default=False)
|
||||
evaluation_mode = ext.Column(ext.Enum(ConditionMode), default=ConditionMode.all_or.value)
|
||||
|
||||
# Foreign Keys
|
||||
owner_id = Base.Column(Base.String, Base.ForeignKey("user.email", ondelete="CASCADE"), nullable=False)
|
||||
owner_id = ext.Column(ext.String, ext.ForeignKey("user.email", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Relationships
|
||||
owner = Base.relationship("User", back_populates="owner_of")
|
||||
authorizations = Base.relationship("Authorization", back_populates="repository", cascade="all, delete")
|
||||
tweets = Base.relationship("Composed", back_populates="repository", cascade="all, delete")
|
||||
alerts = Base.relationship("Alert", back_populates="repository", cascade="all, delete")
|
||||
conditions = Base.relationship("Condition", back_populates="repository")
|
||||
owner = ext.relationship("User", back_populates="owner_of")
|
||||
authorizations = ext.relationship("Authorization", back_populates="repository", cascade="all, delete")
|
||||
tweets = ext.relationship("Composed", back_populates="repository", cascade="all, delete")
|
||||
alerts = ext.relationship("Alert", back_populates="repository", cascade="all, delete")
|
||||
conditions = ext.relationship("Condition", back_populates="repository")
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
This module defines the Tweet database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
|
||||
class Tweet(Base.Model):
|
||||
class Tweet(ext.Model):
|
||||
__tablename__ = "tweet"
|
||||
snowflake = Base.Column(Base.String, primary_key=True)
|
||||
content = Base.Column(Base.String)
|
||||
location = Base.Column(Base.String) # Todo: see if a dedicated class for locations is needed. This is likely.
|
||||
poster = Base.Column(Base.String) # Todo: see if a dedicated class for posters is needed.
|
||||
snowflake = ext.Column(ext.String, primary_key=True)
|
||||
content = ext.Column(ext.String)
|
||||
location = ext.Column(ext.String) # Todo: see if a dedicated class for locations is needed. This is likely.
|
||||
poster = ext.Column(ext.String) # Todo: see if a dedicated class for posters is needed.
|
||||
# Relationships
|
||||
repositories = Base.relationship("Composed", back_populates="tweet", cascade="all, delete")
|
||||
conditions = Base.relationship("Contains", back_populates="tweet", cascade="all, delete")
|
||||
repositories = ext.relationship("Composed", back_populates="tweet", cascade="all, delete")
|
||||
conditions = ext.relationship("Contains", back_populates="tweet", cascade="all, delete")
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
This module defines the User database class.
|
||||
"""
|
||||
|
||||
from ..base import Base
|
||||
from ..base import ext
|
||||
|
||||
|
||||
class User(Base.Model):
|
||||
class User(ext.Model):
|
||||
__tablename__ = "user"
|
||||
email = Base.Column(Base.String, primary_key=True)
|
||||
username = Base.Column(Base.String, nullable=False)
|
||||
password = Base.Column(Base.LargeBinary, nullable=False)
|
||||
isAdmin = Base.Column(Base.Boolean, default=False)
|
||||
email = ext.Column(ext.String, primary_key=True)
|
||||
username = ext.Column(ext.String, nullable=False)
|
||||
password = ext.Column(ext.LargeBinary, nullable=False)
|
||||
isAdmin = ext.Column(ext.Boolean, default=False)
|
||||
# Relationships
|
||||
owner_of = Base.relationship("Repository", back_populates="owner", cascade="all, delete")
|
||||
authorizations = Base.relationship("Authorization", back_populates="user", cascade="all, delete")
|
||||
owner_of = ext.relationship("Repository", back_populates="owner", cascade="all, delete")
|
||||
authorizations = ext.relationship("Authorization", back_populates="user", cascade="all, delete")
|
||||
|
||||
def to_json(self):
|
||||
return {'email': self.email, 'username': self.username, 'isAdmin': self.isAdmin}
|
||||
|
|
|
@ -9,7 +9,7 @@ from .database import *
|
|||
import bcrypt
|
||||
import functools
|
||||
from flask_jwt_extended import get_jwt_identity
|
||||
from flask import request, jsonify
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
def authenticate(username, password):
|
||||
|
|
|
@ -123,9 +123,9 @@ def page_condition(cid):
|
|||
|
||||
if content := request.json.get("content"):
|
||||
condition.content = content
|
||||
Base.session.commit()
|
||||
ext.session.commit()
|
||||
return json_success(condition.to_json()), 200
|
||||
if request.method == "DELETE":
|
||||
Base.session.delete(condition)
|
||||
Base.session.commit()
|
||||
ext.session.delete(condition)
|
||||
ext.session.commit()
|
||||
return json_success("Deleted."), 200
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from flask import render_template, abort, jsonify, request
|
||||
from nest_backend.database import *
|
||||
from flask import request
|
||||
from flask_jwt_extended import jwt_required
|
||||
from nest_backend.gestione import *
|
||||
from nest_backend.gestione import repository_auth, json_error, json_success, ConditionType, Condition, Repository, \
|
||||
find_user, get_jwt_identity
|
||||
from nest_backend.database import ext as extension_sqlalchemy
|
||||
from flask_cors import cross_origin
|
||||
|
||||
|
||||
|
@ -91,7 +92,7 @@ def page_repository_conditions(rid):
|
|||
return json_error("Missing `content` parameter."), 400
|
||||
|
||||
condition = Condition(content=content, type=type_, repository_id=rid)
|
||||
Base.session.add(condition)
|
||||
Base.session.commit()
|
||||
extension_sqlalchemy.session.add(condition)
|
||||
extension_sqlalchemy.session.commit()
|
||||
|
||||
return json_success(condition.to_json()), 200
|
||||
|
|
|
@ -77,6 +77,6 @@ def page_repositories():
|
|||
if not name:
|
||||
return json_error("Missing one or more parameters"), 400
|
||||
repository = Repository(name=name, owner_id=user.email)
|
||||
Base.session.add(repository)
|
||||
Base.session.commit()
|
||||
ext.session.add(repository)
|
||||
ext.session.commit()
|
||||
return json_success(repository.to_json()), 200
|
||||
|
|
|
@ -174,16 +174,16 @@ def page_repository(rid):
|
|||
except KeyError:
|
||||
return json_error("Unknown `type` specified."), 400
|
||||
repository.evaluation_mode = evaluation_mode
|
||||
Base.session.commit()
|
||||
ext.session.commit()
|
||||
return json_success(repository.to_json()), 200
|
||||
elif request.method == "DELETE":
|
||||
if repository.owner_id != user.email and not user.isAdmin:
|
||||
return json_error("You are not the owner of this repository."), 403
|
||||
try:
|
||||
Base.session.delete(repository)
|
||||
Base.session.commit()
|
||||
ext.session.delete(repository)
|
||||
ext.session.commit()
|
||||
except Exception as e:
|
||||
Base.session.rollback()
|
||||
ext.session.rollback()
|
||||
return json_error("Cant delete repository because of dependencies."), 500
|
||||
return json_success("Success"), 200
|
||||
elif request.method == "PUT":
|
||||
|
@ -200,8 +200,8 @@ def page_repository(rid):
|
|||
# Delete no longer needed conditions.
|
||||
for c in repository.conditions:
|
||||
if c.id not in ids:
|
||||
Base.session.delete(c)
|
||||
Base.session.commit()
|
||||
ext.session.delete(c)
|
||||
ext.session.commit()
|
||||
# Create brand new conditions
|
||||
for c in request.json['conditions']:
|
||||
if not c['id']:
|
||||
|
@ -210,6 +210,6 @@ def page_repository(rid):
|
|||
type_ = ConditionType(type_)
|
||||
except KeyError:
|
||||
return json_error("Unknown `type` specified."), 400
|
||||
Base.session.add(Condition(type=type_, content=c['content'], repository_id=rid))
|
||||
Base.session.commit()
|
||||
ext.session.add(Condition(type=type_, content=c['content'], repository_id=rid))
|
||||
ext.session.commit()
|
||||
return json_success(repository.to_json()), 200
|
||||
|
|
|
@ -127,11 +127,11 @@ def page_user(email):
|
|||
return json_error("User is not admin."), 403
|
||||
if user == target:
|
||||
return json_error("The user cant delete himself. Its a sin."), 406
|
||||
Base.session.delete(target)
|
||||
ext.session.delete(target)
|
||||
try:
|
||||
Base.session.commit()
|
||||
ext.session.commit()
|
||||
except Exception:
|
||||
Base.session.rollback()
|
||||
ext.session.rollback()
|
||||
return json_error("Could not delete the user."), 500
|
||||
return json_success("The user has been deleted.")
|
||||
elif request.method == "PATCH":
|
||||
|
@ -142,5 +142,5 @@ def page_user(email):
|
|||
target.username = request.json.get("username")
|
||||
if request.json.get("password"):
|
||||
target.password = gen_password(request.json.get("password"))
|
||||
Base.session.commit()
|
||||
ext.session.commit()
|
||||
return json_success(target.to_json())
|
||||
|
|
|
@ -68,6 +68,6 @@ def page_users():
|
|||
return json_error("User is not admin. Thou art not authorized."), 403
|
||||
new_user = User(email=request.json.get("email"), password=gen_password(request.json.get("password")),
|
||||
username=request.json.get("username"))
|
||||
Base.session.add(new_user)
|
||||
Base.session.commit()
|
||||
ext.session.add(new_user)
|
||||
ext.session.commit()
|
||||
return json_success(new_user.to_json())
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
from flask_swagger_ui import get_swaggerui_blueprint
|
||||
|
||||
SWAGGER_URL = '/docs/'
|
||||
# FIXME: this will break if the website root isn't /, use url_for() instead!
|
||||
SWAGGER_URL = '/docs'
|
||||
API_URL = "/docs/swagger.json"
|
||||
|
||||
# Call factory function to create our blueprint
|
||||
|
|
7
code/backend/nest_backend/test/conftest.py
Normal file
7
code/backend/nest_backend/test/conftest.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Pytest configuration file.
|
||||
|
||||
Import global fixtures here.
|
||||
"""
|
||||
|
||||
from fixtures.flask_client import flask_client
|
37
code/backend/nest_backend/test/fixtures/flask_client.py
vendored
Normal file
37
code/backend/nest_backend/test/fixtures/flask_client.py
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
import pytest
|
||||
import uuid
|
||||
from nest_backend.app import app
|
||||
from nest_backend.gestione import gen_password
|
||||
from nest_backend.database import ext
|
||||
from nest_backend.database.tables import User
|
||||
|
||||
|
||||
@pytest.fixture(scope="package", autouse=True)
|
||||
def flask_client():
|
||||
# Enter testing mode
|
||||
app.config["TESTING"] = True
|
||||
|
||||
# Get environment variables
|
||||
app.config.from_envvar("FLASK_CONFIG")
|
||||
|
||||
# Initialize database
|
||||
with app.app_context():
|
||||
# Use unique db schemas for each test session
|
||||
uniq_schema = f"test_{uuid.uuid4()}"
|
||||
ext.engine.execute(f"""CREATE SCHEMA "{uniq_schema}";""")
|
||||
for table in ext.Model.metadata.tables.values():
|
||||
table.schema = uniq_schema
|
||||
|
||||
ext.create_all(app=app)
|
||||
if not User.query.filter_by(isAdmin=True).all():
|
||||
ext.session.add(
|
||||
User(email="admin@admin.com", password=gen_password("password"), username="admin", isAdmin=True))
|
||||
ext.session.commit()
|
||||
|
||||
# Prepare test client
|
||||
with app.test_client(use_cookies=False) as client:
|
||||
yield client
|
||||
|
||||
# Teardown schema
|
||||
with app.app_context():
|
||||
ext.engine.execute(f"""DROP SCHEMA "{uniq_schema}" CASCADE;""")
|
9
code/backend/nest_backend/test/test_doa.py
Normal file
9
code/backend/nest_backend/test/test_doa.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import pytest
|
||||
from flask.testing import Client
|
||||
from nest_backend.test.fixtures import flask_client
|
||||
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
def test_doa(flask_client: Client):
|
||||
response = flask_client.get("/doa")
|
||||
assert b"If you see this, the server is fine." in response.data
|
|
@ -7,7 +7,7 @@ from nltk.corpus import stopwords
|
|||
|
||||
|
||||
|
||||
Base.init_app(app=app)
|
||||
ext.init_app(app=app)
|
||||
|
||||
def authenticate():
|
||||
c_k = "GEhtSyP9e98mzFeiOCSW0lvQX"
|
||||
|
@ -81,5 +81,5 @@ if __name__ == "__main__":
|
|||
search_repo_conditions()
|
||||
#print(stopwords.words('italian'))
|
||||
with app.app_context():
|
||||
Base.create_all(app=app)
|
||||
ext.create_all(app=app)
|
||||
#start_exploring()
|
||||
|
|
Loading…
Reference in a new issue