diff --git a/Dockerfile b/Dockerfile index 77629af..4dc6c53 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ -FROM python:3.8 +FROM python:3.8-slim ENV TZ=Europe/Paris RUN mkdir -p /var/www RUN apt update -RUN apt install ffmpeg libglu1-mesa libsm6 libxext6 libxmu6 -y +RUN apt install ffmpeg libglu1-mesa libsm6 libxext6 libxmu6 -y && rm -rf /var/lib/apt/lists/* COPY . /var/www/back/ WORKDIR /var/www/back/ RUN pip3 install --no-cache-dir -r requirements.txt -ENTRYPOINT ["python", "run.py"] \ No newline at end of file +ENTRYPOINT ["python", "run.py", "-e", "contact@exo-dev.fr", "-p", "Dev"] \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py index 8c45022..5492cc7 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -87,6 +87,7 @@ def my_expired_token_callback(expired_token, jwt_payload): from .docs.views import docs as docs_bp from .modules.authentications.views import authentications as authentications_bp from .modules.users.views import users as users_bp +from .modules.documents.views import documents as documents_bp from .modules.projects.views import projects as projects_bp from .modules.datas.views import datas as datas_bp @@ -100,6 +101,7 @@ def my_expired_token_callback(expired_token, jwt_payload): app.register_blueprint(docs_bp, url_prefix="/api/docs") app.register_blueprint(authentications_bp, url_prefix="/api/auth") app.register_blueprint(users_bp, url_prefix="/api/users") +app.register_blueprint(documents_bp, url_prefix="/api/documents") app.register_blueprint(projects_bp, url_prefix="/api/projects") diff --git a/app/config.py b/app/config.py index fd81ec6..709ae59 100644 --- a/app/config.py +++ b/app/config.py @@ -4,6 +4,8 @@ from os import environ, path from dotenv import load_dotenv from loguru import logger as errorLogger +from app.utils.methods import hashStringWithSaltB64 +import argparse # Get app base directory basedir = path.abspath(path.join(path.dirname(__file__), "..")) @@ -11,6 +13,11 @@ load_dotenv() +parser = argparse.ArgumentParser("run.py") +parser.add_argument('-e', '--email', help="The email you want to use for the demo account", type=str) +parser.add_argument('-p', '--password', help="The password you want to use for the demo account", type=str) +args = parser.parse_args() + class BaseConfig(object): """Base config class.""" @@ -65,6 +72,27 @@ class Production(BaseConfig): "production": Production, } +def dbchecker(conn, check): + exists = False + try: + cur = conn.cursor() + cur.execute(check) + exists = cur.fetchone()[0] + cur.close() + except psycopg2.Error as e: + errorLogger.error(e) + return exists + +def sql_execute(conn, commands, f): + try: + cur = conn.cursor() + if f: + cur.execute(open(commands, "r").read()) + else: + cur.execute(commands) + cur.close() + except psycopg2.Error as e: + errorLogger.error(e) class ConfigDb(BaseConfig): config_user = environ.get("BDD_CONFIG_USER") @@ -92,66 +120,28 @@ class ConfigDb(BaseConfig): + config_db ) SQLALCHEMY_TRACK_MODIFICATIONS = False - - try: - conn = psycopg2.connect( - f"dbname='{config_db}' user='{config_user}' host='{config_host}' password='{config_pwd}' port='{config_port}'" - ) - conn.close() - except: - errorLogger.error( - "ERROR: Fail to connect to DB with " - + " " - + config_db - + " " - + config_user - + " " - + config_host - + " " - + config_pwd - + " " - + config_port - ) - sys.exit(1) - elif config_user and config_port and config_host and config_db: - # conf SQLALCHEMY for manage pool queue - POOL_PRE_PING = True - POOL_SIZE = 32 - MAX_OVERFLOW = 64 - # DB conf for SQLALCHEMY - SQLALCHEMY_DATABASE_URI = ( - "postgresql+psycopg2://" - + config_user - + ":" - + config_pwd - + "@" - + config_host - + ":" - + config_port - + "/" - + config_db - ) - SQLALCHEMY_TRACK_MODIFICATIONS = False - + try: conn = psycopg2.connect( f"dbname='{config_db}' user='{config_user}' host='{config_host}' password='{config_pwd}' port='{config_port}'" ) - conn.close() - except: - errorLogger.error( - "ERROR: Fail to connect to DB with " - + " " - + config_db - + " " - + config_user - + " " - + config_host - + " " - + config_pwd - + " " - + config_port - ) + conn.autocommit = True + if not dbchecker(conn,"select exists(select relname from pg_class where relname='authentications')"): + sql_execute(conn,"sql/create-tables.sql", True) + cmd = "INSERT INTO base.authentications (email, password, role, status) VALUES ('%s', '%s', 'SUPERADMIN', 'ACTIVE');" % (args.email, hashStringWithSaltB64(args.password)) + sql_execute(conn,cmd, False) + uid = dbchecker(conn,"SELECT id FROM base.authentications WHERE email = '" + args.email + "'") + sql_execute(conn,"INSERT INTO base.users (firstname, lastname, authentication_id) VALUES ('Démo', 'Métropole', '" + str(uid) + "');", False) + conn.close() + else: + if not dbchecker(conn,"select exists(select email from base.authentications where email='" + args.email + "')"): + cmd = "INSERT INTO base.authentications (email, password, role, status) VALUES ('%s', '%s', 'SUPERADMIN', 'ACTIVE');" % (args.email, hashStringWithSaltB64(args.password)) + sql_execute(conn,cmd, False) + uid = dbchecker(conn,"SELECT id FROM base.authentications WHERE email = '" + args.email + "'") + sql_execute(conn,"INSERT INTO base.users (firstname, lastname, authentication_id) VALUES ('Démo', 'Métropole', '" + str(uid) + "');", False) + conn.close() + except psycopg2.Error as e: + errorLogger.error(e) sys.exit(1) else: print(f'config_user="{config_user}"') diff --git a/app/core/views.py b/app/core/views.py index 53bbad1..c957fb1 100644 --- a/app/core/views.py +++ b/app/core/views.py @@ -9,7 +9,7 @@ from app.utils.methods import * from app.utils.generatePdf import * -# Create Blueprint & get logger +# Create Blueprint core = Blueprint("core", __name__) diff --git a/app/docs/views.py b/app/docs/views.py index 68ee5fa..bcb1b39 100644 --- a/app/docs/views.py +++ b/app/docs/views.py @@ -7,7 +7,7 @@ from app.utils.constants import * from app.utils.methods import * -# Create Blueprint & get logger +# Create Blueprint docs = Blueprint("docs", __name__) diff --git a/app/models.py b/app/models.py index e2906a0..4467816 100644 --- a/app/models.py +++ b/app/models.py @@ -36,7 +36,35 @@ def __str__(self): + '">' ) +class Documents(db.Model): + __table_args__ = {"schema": schema} + + # fields + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + date_create = db.Column(db.Date) + type = db.Column(db.String) + title = db.Column(db.String) + file_name = db.Column(db.String) + data = db.Column(db.String) + # foreign keys + user_id = db.Column(db.Integer, db.ForeignKey(schema + ".users.id")) + + # objects links + user = db.relationship("Users", foreign_keys=user_id) + + + def __str__(self): + return ( + "' + ) + class Users(db.Model): __table_args__ = {"schema": schema} @@ -45,7 +73,6 @@ class Users(db.Model): firstname = db.Column(db.String) date_archived = db.Column(db.Date) lastname = db.Column(db.String) - phone = db.Column(db.String, nullable=True, default=None) # foreign keys authentication_id = db.Column( @@ -57,7 +84,6 @@ class Users(db.Model): ) def __str__(self): - phonePart = "NULL" if (self.phone == None) else str(self.phone) return ( "' ) diff --git a/app/modules/authentications/views.py b/app/modules/authentications/views.py index 5c7c167..560f835 100644 --- a/app/modules/authentications/views.py +++ b/app/modules/authentications/views.py @@ -23,9 +23,8 @@ # Content mails import -# Create Blueprint & get logger +# Create Blueprint authentications = Blueprint("authentications", __name__) -logger = LocalProxy(lambda: current_app.logger) @authentications.before_request @@ -124,7 +123,7 @@ def loginAuth(): identity=userIdentity, expires_delta=expires ) - response = jsonify({"token": access_token}) + response = jsonify({"token": access_token, "firstname": user.firstname, "lastname": user.lastname}) response.status_code = 200 # add token to response headers - so SwaggerUI can use it response.headers.extend({"jwt-token": access_token}) @@ -179,7 +178,6 @@ def getUserDatas(): "id_user": userDB.id, "firstname": userDB.firstname, "lastname": userDB.lastname, - "phone": userDB.phone, } return jsonify({"user": authUser}), 200 @@ -420,7 +418,7 @@ def create(): server = ServerSMTP() sender = str(os.getenv("SMTP_SENDER")) to = [str(newAuth.email)] - subject = "[Exo-Dev] Bienvenue dans le boilerplate !" + subject = "Bienvenue dans le projet Maquette!" codage = "UTF-8" typetext = "html" @@ -529,7 +527,7 @@ def resetPassword(): server = ServerSMTP() sender = str(os.getenv("SMTP_SENDER")) to = [str(authDB.email)] - subject = "[Exo-Dev] Processus de réinitialisation de mot de passe" + subject = "Processus de réinitialisation de mot de passe" codage = "UTF-8" typetext = "html" @@ -767,7 +765,7 @@ def resendMailsValidAccount(id): server = ServerSMTP() sender = str(os.getenv("SMTP_SENDER")) to = [str(authentication.email)] - subject = "[Exo-Dev] Invitation à activer son compte" + subject = "Invitation à activer son compte" codage = "UTF-8" typetext = "html" diff --git a/app/modules/dataprocess/views.py b/app/modules/dataprocess/views.py index 2bbfb2a..0c8d570 100644 --- a/app/modules/dataprocess/views.py +++ b/app/modules/dataprocess/views.py @@ -27,9 +27,8 @@ debug_mode = environ.get("DEBUG_MODE") -# Create Blueprint & get logger +# Create Blueprint dataprocess = Blueprint("dataprocess", __name__) -logger = LocalProxy(lambda: current_app.logger) def voxelize(tempfile): diff --git a/app/modules/datas/views.py b/app/modules/datas/views.py index ca8432c..41a4752 100644 --- a/app/modules/datas/views.py +++ b/app/modules/datas/views.py @@ -17,9 +17,8 @@ data_schema = DatasSchema() datas_schema = DatasSchema(many=True) -# Create Blueprint & get logger +# Create Blueprint datas = Blueprint("datas", __name__) -logger = LocalProxy(lambda: current_app.logger) diff --git a/app/modules/documents/views.py b/app/modules/documents/views.py new file mode 100644 index 0000000..56abab4 --- /dev/null +++ b/app/modules/documents/views.py @@ -0,0 +1,284 @@ +from app import db, infoLogger, errorLogger +from app.models import Documents +from app.schemas import DocumentSchema, DocumentOnlySchema +from os import environ +from os.path import basename +from flask import Blueprint, current_app, jsonify, request +from flask_jwt_extended import jwt_required, get_jwt_identity +from werkzeug.local import LocalProxy + +# model import +document_schema = DocumentSchema() +documents_schema = DocumentSchema(many=True) +detail_document_schema = DocumentOnlySchema() + +# Utils import +from app.utils.methods import * +from app.utils.constants import * + + +# Create Blueprint +documents = Blueprint("documents", __name__) + +@documents.before_request +def before_request_func(): + current_app.logger.name = "documents" + + +@documents.route("", methods=["GET"]) +@jwt_required() +def listAll(): + """ + List all documents + --- + tags: + - documents + security: + - Bearer: [] + responses: + 200: + description: List all documents + 500: + description: Internal Server Error + """ + infoLogger.info(f"{request.method} → {request.path}") + + # Access the identity of the current user with get_jwt_identity + # currentUser = get_jwt_identity() + + documents = Documents.query.all() + infoLogger.info("List " + str(len(documents)) + " documents") + + return jsonify(documents_schema.dump(documents)), 200 + + +@documents.route("/", methods=["GET"]) +@jwt_required() +def getById(id): + """ + Show documents' information with his ID + --- + tags: + - documents + security: + - Bearer: [] + parameters: + - in: path + name: id + type: integer + required: true + responses: + 200: + description: Documents information + 404: + description: Not Found + 500: + description: Internal Server Error + """ + infoLogger.info(f"{request.method} → {request.path}") + + # Access the identity of the current user with get_jwt_identity + # currentUser = get_jwt_identity() + + document = Documents.query.filter_by(id=id).first() + + if document is not None: + return jsonify(detail_document_schema.dump(document)), 200 + else: + return jsonify({"msg": "Not Found"}), 404 + + +@documents.route("", methods=["POST"]) +@jwt_required() +def create(): + """ + Create a document + --- + tags: + - documents + security: + - Bearer: [] + parameters: + - in: body + name: body + schema: + required: + - title + - type + - file_name + - data + properties: + title: + type: string + description: title of the document + type: + type: string + description: type of document [] + file_name: + type: string + description: name of the file for DL + data: + type: string + description: base64 of the document + responses: + 200: + description: OK + 400: + description: Bad Request + 403: + description: Forbidden + 500: + description: Internal Server Error + """ + infoLogger.info(f"{request.method} → {request.path}") + + # Access the identity of the current user with get_jwt_identity + currentUser = get_jwt_identity() + + if "role" in currentUser and currentUser["role"] == "USER": + return jsonify({"msg": "Forbidden"}), 403 + + # Get Body of request + form = request.json + + if "title" in form and "type" in form and "data" in form and "file_name" in form: + newDocument = Documents( + # required + date_create=datetime.datetime.utcnow(), + user_id=currentUser["user_id"], + title=form["title"], + type=form["type"], + file_name=form["file_name"], + data=form["data"], + ) + + db.session.add(newDocument) + db.session.commit() + + return jsonify({"msg": "OK", "id": newDocument.id}), 200 + else: + return jsonify({"msg": "Bad Request"}), 400 + + +@documents.route("/", methods=["PATCH"]) +@jwt_required() +def updateById(id): + """ + Update a document + --- + tags: + - documents + security: + - Bearer: [] + parameters: + - in: path + name: id + type: integer + required: true + - in: body + name: body + schema: + properties: + title: + type: string + description: title of the document + type: + type: string + description: type of document [] + file_name: + type: string + description: name of the file for DL + data: + type: string + description: base64 of the document + responses: + 200: + description: Skill updated with success + 400: + description: Bad Request + 403: + description: Forbidden + 500: + description: Internal Server Error + """ + infoLogger.info(f"{request.method} → {request.path}") + + # Access the identity of the current user with get_jwt_identity + currentUser = get_jwt_identity() + + # Get Body of request + form = request.json + document = Documents.query.get(id) + + if document is not None: + has_access = False + if "role" in currentUser and currentUser["role"] == "SUPERADMIN": + has_access = True + elif document.user_id == currentUser["user_id"]: + has_access = True + + if has_access: + if "title" in form: + document.title = form["title"] + if "file_name" in form: + document.file_name = form["file_name"] + if "type" in form: + document.type = form["type"] + if "data" in form: + document.data = form["data"] + + db.session.commit() + return jsonify({"msg": "OK"}), 200 + else: + return jsonify({"msg": "Forbidden"}), 403 + else: + return jsonify({"msg": "Not Found"}), 404 + + +@documents.route("/", methods=["DELETE"]) +@jwt_required() +def deleteById(id): + """ + Delete a document + --- + tags: + - documents + security: + - Bearer: [] + parameters: + - in: path + name: id + type: integer + required: true + responses: + 200: + description: OK + 403: + description: Bad Request + 404: + description: Not Found + 500: + description: Internal Server Error + """ + infoLogger.info(f"{request.method} → {request.path}") + + # Access the identity of the current user with get_jwt_identity + currentUser = get_jwt_identity() + + document = Documents.query.get(id) + + if document: + has_access = False + if "role" in currentUser and currentUser["role"] == "SUPERADMIN": + has_access = True + elif document.user_id == currentUser["user_id"]: + has_access = True + + if has_access: + db.session.delete(document) + db.session.commit() + return jsonify({"msg": "OK"}), 200 + else: + return jsonify({"msg": "Forbidden"}), 401 + else: + return jsonify({"msg": "Not Found"}), 404 diff --git a/app/modules/projects/views.py b/app/modules/projects/views.py index 44b29c3..288ad1e 100644 --- a/app/modules/projects/views.py +++ b/app/modules/projects/views.py @@ -15,9 +15,9 @@ project_schema = ProjectsSchema() projects_schema = ProjectsSchema(many=True) -# Create Blueprint & get logger +# Create Blueprint projects = Blueprint("projects", __name__) -logger = LocalProxy(lambda: current_app.logger) + @projects.before_request @@ -113,6 +113,8 @@ def create(): - nb_plaques_h - nb_plaques_v - ratio + - model_id + - csv_id properties: name: type: string @@ -129,6 +131,12 @@ def create(): ratio: type: integer description: ratio + model_id: + type: integer + description: ratio + csv_id: + type: integer + description: ratio responses: 200: description: OK @@ -147,8 +155,7 @@ def create(): if currentUser: # Get Body of request form = request.json - - if 'name' in form and 'bbox' in form and 'nb_plaques_h' in form and 'nb_plaques_v' in form and 'ratio' in form: + if 'model_id' in form and 'csv_id' in form and 'name' in form and 'bbox' in form and 'nb_plaques_h' in form and 'nb_plaques_v' in form and 'ratio' in form: newproject = Projects( date_create=datetime.datetime.utcnow(), user_id=currentUser['user_id'], @@ -156,7 +163,9 @@ def create(): bbox=form['bbox'], nb_plaques_h=form['nb_plaques_h'], nb_plaques_v=form['nb_plaques_v'], - ratio=form['ratio'] + ratio=form['ratio'], + model_id=form['model_id'], + csv_id=form['csv_id'] ) db.session.add(newproject) db.session.commit() @@ -208,6 +217,12 @@ def updateById(id): ratio: type: integer description: ratio + model_id: + type: integer + description: ratio + csv_id: + type: integer + description: ratio responses: 200: description: OK @@ -233,6 +248,10 @@ def updateById(id): project.nb_plaques_v = form['nb_plaques_v'] if 'ratio' in form: project.ratio = form['ratio'] + if 'model_id' in form: + project.model_id = form['model_id'] + if 'csv_id' in form: + project.csv_id = form['csv_id'] db.session.commit() return jsonify({'msg': 'OK'}), 200 else: diff --git a/app/modules/users/views.py b/app/modules/users/views.py index f5ce3ef..5932ee0 100644 --- a/app/modules/users/views.py +++ b/app/modules/users/views.py @@ -21,9 +21,8 @@ # Utils import -# Create Blueprint & get logger +# Create Blueprint users = Blueprint("users", __name__) -logger = LocalProxy(lambda: current_app.logger) @users.before_request @@ -259,9 +258,6 @@ def create(): lastname: type: string description: lastname of the user - phone: - type: string - description: phone number of the user responses: 200: description: OK @@ -281,11 +277,6 @@ def create(): form = request.json if currentUser["role"] == "SUPERADMIN": - if "phone" not in form or ("phone" in form and form["phone"] == None): - form["phone"] = None - elif "phone" in form and len(form["phone"]) == 0: - form["phone"] = None - if "firstname" in form and "lastname" in form and "authentication_id" in form: auth = Authentications.query.get(form["authentication_id"]) @@ -295,7 +286,6 @@ def create(): authentication_id=auth.id, firstname=form["firstname"], lastname=form["lastname"], - phone=form["phone"], ) db.session.add(newUser) @@ -335,9 +325,6 @@ def patchById(id): lastname: type: string description: lastname of the user - phone: - type: string - description: phone number of the user responses: 200: description: OK @@ -360,11 +347,6 @@ def patchById(id): user = Users.query.get(id) # nullable inputs - if "phone" not in form or ("phone" in form and form["phone"] == None): - form["phone"] = None - elif "phone" in form and len(form["phone"]) == 0: - form["phone"] = None - if user is not None: if currentUser["role"] == "SUPERADMIN" or currentUser["user_id"] == user.id: @@ -372,8 +354,6 @@ def patchById(id): user.firstname = form["firstname"] if "lastname" in form: user.lastname = form["lastname"] - if form["phone"] != None: - user.phone = form["phone"] db.session.commit() return jsonify({"msg": "OK"}), 200 diff --git a/app/schemas.py b/app/schemas.py index 35b92eb..fd00aa1 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -1,7 +1,18 @@ from app import ma -from app.models import Users, Projects, Datas +from app.models import Users, Projects, Datas, Documents +class DocumentOnlySchema(ma.SQLAlchemyAutoSchema): + class Meta: + model = Documents + + +class DocumentSchema(ma.SQLAlchemyAutoSchema): + user = ma.Nested("UserSchema", only=("id",), many=False) + + class Meta: + fields = ("id", "title", "type", "user") + class AuthenticationSchema(ma.SQLAlchemyAutoSchema): user = ma.Nested( "UserAdminSchema", @@ -34,6 +45,9 @@ class Meta: class ProjectsSchema(ma.SQLAlchemyAutoSchema): + user = ma.Nested("UserSchema", only=("id",), many=False) + model = ma.Nested("DocumentOnlySchema", only=("id",), many=False) + csv = ma.Nested("DocumentOnlySchema", only=("id",), many=False) class Meta: model = Projects diff --git a/package.json b/package.json index 3a1a374..8f5ef59 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name": "maquette-back", - "version": "1.1.0", + "version": "2.0.0", "private": true } \ No newline at end of file diff --git a/sql/MCD.png b/sql/MCD.png deleted file mode 100644 index cc3ef5e..0000000 Binary files a/sql/MCD.png and /dev/null differ diff --git a/sql/MLD.png b/sql/MLD.png deleted file mode 100644 index 0b36f4a..0000000 Binary files a/sql/MLD.png and /dev/null differ diff --git a/sql/create-tables.sql b/sql/create-tables.sql index 491ad27..6612766 100644 --- a/sql/create-tables.sql +++ b/sql/create-tables.sql @@ -40,7 +40,6 @@ CREATE TABLE base.users ( date_archived TIMESTAMP WITHOUT TIME ZONE NULL DEFAULT NULL, firstname character varying(100) NOT NULL, lastname character varying(100) NOT NULL, - phone character varying(100) NULL, authentication_id INT, FOREIGN KEY (authentication_id) REFERENCES base.authentications (id) MATCH SIMPLE @@ -49,6 +48,20 @@ CREATE TABLE base.users ( ); ALTER TABLE base.users OWNER TO adm; +CREATE TABLE base.documents ( + id serial PRIMARY KEY, + date_create timestamp without time zone NOT NULL DEFAULT now(), + type character varying(255) NOT NULL, + title character varying(255) NOT NULL, + file_name character varying(255) NOT NULL, + data text NOT NULL, + user_id INT NOT NULL, + FOREIGN KEY (user_id) + REFERENCES base.users (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION +); +ALTER TABLE base.documents OWNER TO adm; CREATE TABLE base.projects ( id serial PRIMARY KEY, @@ -62,6 +75,16 @@ CREATE TABLE base.projects ( FOREIGN KEY (user_id) REFERENCES base.users (id) MATCH SIMPLE ON UPDATE NO ACTION + ON DELETE NO ACTION, + model_id INTEGER NULL, + FOREIGN KEY (model_id) + REFERENCES base.documents (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION, + csv_id INTEGER NULL, + FOREIGN KEY (csv_id) + REFERENCES base.documents (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION ); ALTER TABLE base.projects OWNER TO adm; @@ -82,4 +105,4 @@ CREATE TABLE base.datas ( ON UPDATE NO ACTION ON DELETE NO ACTION ); -ALTER TABLE base.datas OWNER TO adm; +ALTER TABLE base.datas OWNER TO adm; \ No newline at end of file diff --git a/sql/insert-datas.sql b/sql/insert-datas.sql index dbeaae0..83d618f 100644 --- a/sql/insert-datas.sql +++ b/sql/insert-datas.sql @@ -3,13 +3,13 @@ INSERT INTO base.authentications (email, password, role, status) VALUES ('x.thiermant@exo-dev.fr', 'meoK4zlCmwC5B9lvkNR1fWDYFwpNfLTV+SNZ3tV34cSZ6grjOUKbALkH2W+Q1HV9YNgXCk18tNX5I1ne1XfhxPpVN31F63BZERnonDsB32WMyuUDmcBofjTf+sUERpgV', 'SUPERADMIN', 'ACTIVE'), -('contact@exo-dev.fr', 'meoK4zlCmwC5B9lvkNR1fWDYFwpNfLTV+SNZ3tV34cSZ6grjOUKbALkH2W+Q1HV9YNgXCk18tNX5I1ne1XfhxPpVN31F63BZERnonDsB32WMyuUDmcBofjTf+sUERpgV', 'USER', 'ACTIVE'), -('contact@exo-dev.fr', 'meoK4zlCmwC5B9lvkNR1fWDYFwpNfLTV+SNZ3tV34cSZ6grjOUKbALkH2W+Q1HV9YNgXCk18tNX5I1ne1XfhxPpVN31F63BZERnonDsB32WMyuUDmcBofjTf+sUERpgV', 'ADMIN', 'ACTIVE'); +('contact@exo-dev.fr', 'meoK4zlCmwC5B9lvkNR1fWDYFwpNfLTV+SNZ3tV34cSZ6grjOUKbALkH2W+Q1HV9YNgXCk18tNX5I1ne1XfhxPpVN31F63BZERnonDsB32WMyuUDmcBofjTf+sUERpgV', 'ADMIN', 'ACTIVE'), +('contact@exo-dev.fr', 'meoK4zlCmwC5B9lvkNR1fWDYFwpNfLTV+SNZ3tV34cSZ6grjOUKbALkH2W+Q1HV9YNgXCk18tNX5I1ne1XfhxPpVN31F63BZERnonDsB32WMyuUDmcBofjTf+sUERpgV', 'USER', 'ACTIVE'); INSERT INTO base.users -(firstname, lastname, phone, authentication_id) +(firstname, lastname, authentication_id) VALUES -('Xavier', 'Thiermant', '0654455523', 1), -('Contact', 'User', NULL, 2), -('Sofiane', 'Hamlaoui', NULL, 3); +('Xavier', 'Thiermant', 1), +('Sofiane', 'Hamlaoui', 2), +('Contact', 'User', 3); diff --git a/tests/endpoint_test.py b/tests/endpoint_test.py deleted file mode 100644 index 19dba33..0000000 --- a/tests/endpoint_test.py +++ /dev/null @@ -1,518 +0,0 @@ -import json -import unittest -import sys -import os -import pytest -from sqlalchemy import ( - create_engine, - Column, - Integer, - String, - DateTime, - Text, - ForeignKey, -) -from sqlalchemy.engine import URL -from sqlalchemy.orm import declarative_base, sessionmaker -import random -import string - -# USERS -# +-----------------------------------+ -# | | -# +---------------------------------+ | | -# | | | | -# | | | | -# +---> Creates Authentication ID +-----------------------------------------------------> Delete User + Auth ID | -# | | | | | -# | | | | | -# | +---------------------------------+ | | -# | +-------------------^---------------+ -# | | -# | | -# | | -# +--------+---------+ +--------------------+ +-----------------+ +---------------------------+------------+ -# | | | | | | | | -# | Create +-----ID---->| Fetch by ID +----ID---->| Patch by ID +---ID----> List all + contains ID* | -# | | | | | | | | -# +------------------+ +--------------------+ +-----------------+ +----------------------------------------+ -# -# - - -# Auth ID -# -# +--------------------------------------------------------------------------------------Email---------------------------------------------------------------------------------------------+ -# | | -# | | -# | | -# | | -# | | -# | | -# | | -# | | -# | | -# | | -# | | -# | | -# +---------------+---------------+ +------------------------+ +---------------------------+ +-----------------------------+ +----------------v------------------+ -# | | | | | | | | | | -# | Auth ID Creation using | | | | List all Auth IDs | | | | | -# | | | | | | | | | | -# | Email +-----ID------> Fetch by Auth ID +--------ID---------> & +--------ID--------> Delete Auth ID | | Password Reset using Auth ID Email| -# | | | | | contains new Auth ID | | | | | -# | | | | | | | | | | -# +-------------------------------+ +------------------------+ +---------------------------+ +-----------------------------+ | | -# +-----------------------------------+ - - -from dotenv import load_dotenv - -load_dotenv() -url = URL.create( - drivername=os.getenv("BDD_DB_SYSTEM"), - host=os.getenv("BDD_CONFIG_HOST"), - username=os.getenv("BDD_CONFIG_USER"), - password=os.getenv("BDD_CONFIG_PASSWD"), - database=os.getenv("BDD_CONFIG_DB"), - port=os.getenv("BDD_CONFIG_PORT"), -) - -engine = create_engine(url) -Session = sessionmaker(bind=engine) - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -sys.path.append(os.path.dirname(SCRIPT_DIR)) - -from app import db -import app as tested_app - -adminAcct = {"login": "x.thiermant@exo-dev.fr", "password": "!Ex0D3v!"} - -Base = declarative_base() - - -def random_char(char_num): - return "".join(random.choice(string.ascii_letters) for _ in range(char_num)) - - -class BoilerplateTest(unittest.TestCase): - def setUp(self): - tested_app.app.config["TESTING"] = True - self.app = tested_app.app.test_client() - # Base.metadata.create_all(engine) // no need for the momeny ! - self.session = Session() - - # # Utils # # - - def get_token(self): - # get Token for next tests - r = self.app.post( - "/api/auth/login", - content_type="application/json", - data=json.dumps(adminAcct), - ) - res = r.json["token"] - headers = {"Authorization": f"Bearer {res}"} - return headers - - def get_auth_id(self): - header = self.get_token() - payload = { - "email": random_char(7).lower() + "@exo-dev.fr", - "role": "SUPERADMIN", - } - r = self.app.post( - "/api/auth", - content_type="application/json", - headers=header, - data=json.dumps(payload), - ) - if r.status_code == 200: - # For global usage - global newuserId - newuserId = r.json["id"] - return newuserId - else: - raise ValueError(r) - - # # Default Routes test # # - - # Status - def test_status(self): - r = self.app.get("/api/core/status") - self.assertIn(b"Running", r.data) - - # Login Test # # - - # Sucess - def test_login(self): - r = self.app.post( - "/api/auth/login", - content_type="application/json", - data=json.dumps(adminAcct), - ) - self.assertEqual(r.status_code, 200) - - # Fail - def test_fail_login(self): - payload = {"login": "xthiermant", "password": "toto"} - r = self.app.post( - "/api/auth/login", content_type="application/json", data=json.dumps(payload) - ) - self.assertEqual(r.status_code, 400) - - # Auth Test # # - - # Auth ID Creation Test - def test_auth_id_creation(self): - header = self.get_token() - global usedmail - usedmail = random_char(7).lower() + "@exo-dev.fr" - payload = {"email": usedmail, "role": "SUPERADMIN"} - r = self.app.post( - "/api/auth", - content_type="application/json", - headers=header, - data=json.dumps(payload), - ) - if r.status_code == 200: - # For global usage - global testid - testid = r.json["id"] - self.assertEqual(r.status_code, 200) - else: - raise ValueError(r) - - # Fetch by Auth ID Test - def test_auth_id_fetch(self): - header = self.get_token() - r = self.app.get("/api/auth/" + str(testid), headers=header) - self.assertEqual(r.status_code, 200) - - # List all auth IDs Test - def test_auth_id_list(self): - header = self.get_token() - r = self.app.get("/api/auth", headers=header) - self.assertEqual(r.status_code, 200) - - # Delete Auth ID Test - @pytest.mark.order(-1) - def test_auth_id_delete(self): - header = self.get_token() - r = self.app.delete( - "/api/auth/" + str(testid), content_type="application/json", headers=header - ) - self.assertEqual(r.status_code, 200) - - # Password reset Test - def test_password_reset(self): - header = self.get_token() - r = self.app.post( - "/api/auth/reset", - content_type="application/json", - headers=header, - data=json.dumps({"email": usedmail}), - ) - self.assertEqual(r.status_code, 200) - - # Users Test # # - - # User Creation Test - - def test_user_creation(self): - header = self.get_token() - auth_id = self.get_auth_id() - payload = { - "authentication_id": auth_id, - "firstname": "Sofiane", - "lastname": "Hamlaoui", - } - # For global usage - global newuser - r = self.app.post( - "/api/users", - content_type="application/json", - headers=header, - data=json.dumps(payload), - ) - newuser = r.json["id"] - self.assertEqual(r.status_code, 200) - - # User Fetch Test - def test_user_fetch(self): - header = self.get_token() - - r = self.app.get( - "/api/users", - content_type="application/json", - headers=header, - data=json.dumps({"id": newuser}), - ) - self.assertEqual(r.status_code, 200) - - # User Patch Test - def test_user_patch(self): - header = self.get_token() - - r = self.app.patch( - "/api/users/" + str(newuser), - content_type="application/json", - headers=header, - data=json.dumps( - {"id": newuser, "firstname": "SofianePatch2", "lastname": "LastPatch2"} - ), - ) - self.assertEqual(r.status_code, 200) - - # List all users Test - def test_list_allusers(self): - header = self.get_token() - - r = self.app.get("/api/users", headers=header) - self.assertEqual(r.status_code, 200) - - # Delete user Test - @pytest.mark.order(-2) - def test_user_delete(self): - header = self.get_token() - r = self.app.delete("/api/users/" + str(newuser), headers=header) - self.assertEqual(r.status_code, 200) - - # Notifications Test # # - - def test_list_allnotifs(self): - header = self.get_token() - - r = self.app.get("/api/notifications", headers=header) - self.assertEqual(r.status_code, 200) - - def test_create_notif(self): - header = self.get_token() - global newnotif - r = self.app.post( - "/api/notifications", - content_type="application/json", - headers=header, - data=json.dumps( - { - "title": "Notif Title", - "type": "WARNING", - "content": "Random Content", - "link": "https://notifExoDev", - } - ), - ) - newnotif = r.json["id"] - self.assertEqual(r.status_code, 200) - - @pytest.mark.order(-3) - def test_delete_notif(self): - header = self.get_token() - r = self.app.delete("/api/notifications/" + str(newnotif), headers=header) - self.assertEqual(r.status_code, 200) - - def test_patch_notif(self): - header = self.get_token() - r = self.app.patch( - "/api/notifications/" + str(newnotif), - content_type="application/json", - headers=header, - data=json.dumps( - { - "title": "Notif Title Patch", - "type": "WARNING", - "content": "Random ContentPatch", - "link": "https://notifExoDevPatch", - } - ), - ) - self.assertEqual(r.status_code, 200) - - # Documents Test # # - - def test_list_all_docs(self): - header = self.get_token() - - r = self.app.get("/api/documents", headers=header) - self.assertEqual(r.status_code, 200) - - def test_create_doc(self): - header = self.get_token() - global newdoc - r = self.app.post( - "/api/documents", - content_type="application/json", - headers=header, - data=json.dumps( - { - "data": "Random", - "file_name": "doc.pdf", - "title": "Top Secret", - "type": "pdf", - } - ), - ) - newdoc = r.json["id"] - self.assertEqual(r.status_code, 200) - - @pytest.mark.order(-4) - def test_delete_doc(self): - header = self.get_token() - r = self.app.delete("/api/documents/" + str(newdoc), headers=header) - self.assertEqual(r.status_code, 200) - - def test_info_doc(self): - header = self.get_token() - r = self.app.get("/api/documents/" + str(newdoc), headers=header) - self.assertEqual(r.status_code, 200) - - def test_patch_doc(self): - header = self.get_token() - r = self.app.patch( - "/api/documents/" + str(newdoc), - content_type="application/json", - headers=header, - data=json.dumps( - { - "data": "Random", - "file_name": "doc.pdf", - "title": "Top Secret Patch", - "type": "pdf", - } - ), - ) - self.assertEqual(r.status_code, 200) - - # Customers Test # # - - def test_list_allcustomers(self): - header = self.get_token() - - r = self.app.get("/api/customers/select", headers=header) - self.assertEqual(r.status_code, 200) - - def test_create_customer(self): - header = self.get_token() - global newcustomer - r = self.app.post( - "/api/customers", - content_type="application/json", - headers=header, - data=json.dumps( - { - "is_individual": True, - "naf_code": "6654", - "name": "Exo-Dev", - "office_address": "Lyon", - "office_address_comp": "Lyon", - "office_city": "Lyon", - "office_phone": "033156454", - "office_postal_code": "69000", - "siret": "654645", - "status": "ARCHIVED", - "tva_code": "4231", - "user_id": 1, - } - ), - ) - newcustomer = r.json["id"] - self.assertEqual(r.status_code, 200) - - def test_info_customer(self): - header = self.get_token() - r = self.app.get("/api/customers/" + str(newcustomer), headers=header) - self.assertEqual(r.status_code, 200) - - def test_patch_customer(self): - header = self.get_token() - r = self.app.patch( - "/api/customers/" + str(newcustomer), - content_type="application/json", - headers=header, - data=json.dumps( - { - "is_individual": "true", - "naf_code": "6654", - "name": "Exo-Dev Patch", - "office_address": "Lyon", - "office_address_comp": "Lyon", - "office_city": "Lyon", - "office_phone": "033156454", - "office_postal_code": "69000", - "siret": "654645", - "status": "ARCHIVED", - "tva_code": "4231", - "user_id": 1, - } - ), - ) - self.assertEqual(r.status_code, 200) - - @pytest.mark.order(-5) - def test_delete_customer(self): - header = self.get_token() - r = self.app.delete("/api/customers/" + str(newcustomer), headers=header) - self.assertEqual(r.status_code, 200) - - def test_list_allcontacts(self): - header = self.get_token() - - r = self.app.get("/api/contacts", headers=header) - self.assertEqual(r.status_code, 200) - - @pytest.mark.order(after="test_create_customer") - def test_create_contact(self): - header = self.get_token() - global newcontact - r = self.app.post( - "/api/contacts/customers", - content_type="application/json", - headers=header, - data=json.dumps( - { - "customer_id": newcustomer, - "email": "s.hamlaoui@exo-dev.fr", - "firstname": "Sofiane", - "job_title": "DevOps", - "lastname": "Elon", - "phone": "+33119", - } - ), - ) - newcontact = r.json["id"] - self.assertEqual(r.status_code, 200) - - def test_info_contact(self): - header = self.get_token() - r = self.app.get("/api/contacts/" + str(newcontact), headers=header) - self.assertEqual(r.status_code, 200) - - @pytest.mark.order(after="test_create_contact") - def test_patch_contact(self): - header = self.get_token() - r = self.app.patch( - "/api/contacts/" + str(newcontact), - content_type="application/json", - headers=header, - data=json.dumps( - { - "customer_id": newcustomer, - "email": "LYON Patch", - "firstname": "LYON Patch", - "job_title": "LYON Patch", - "lastname": "LYON Patch", - "phone": "LYON Patch", - } - ), - ) - self.assertEqual(r.status_code, 200) - - @pytest.mark.order(-6) - def test_delete_contact(self): - header = self.get_token() - r = self.app.delete("/api/contacts/" + str(newcontact), headers=header) - self.assertEqual(r.status_code, 200) - -if __name__ == "__main__": - unittest.main() diff --git a/tests/pytest.ini b/tests/pytest.ini deleted file mode 100644 index 2d33f22..0000000 --- a/tests/pytest.ini +++ /dev/null @@ -1,4 +0,0 @@ -# pytest.ini -[pytest] -filterwarnings = - ignore::UserWarning \ No newline at end of file