diff --git a/code/backend/nest_backend/__main__.py b/code/backend/nest_backend/__main__.py index bb8b1e1..2b32c79 100644 --- a/code/backend/nest_backend/__main__.py +++ b/code/backend/nest_backend/__main__.py @@ -33,6 +33,11 @@ app.config['CORS_HEADERS'] = 'Content-Type' app.add_url_rule("/doa", view_func=page_doa, methods=["GET", "POST"]) app.add_url_rule("/api/login", view_func=page_login, methods=["POST"]) app.add_url_rule("/api/user/create", view_func=page_user_create, methods=["POST"]) +app.add_url_rule("/api/user/remove", view_func=page_user_delete, methods=["POST"]) +app.add_url_rule("/api/repository/list", view_func=page_repository_list, methods=["POST"]) +app.add_url_rule("/api/repository/create", view_func=page_repository_create, methods=["POST"]) +app.add_url_rule("/api/repository/edit", view_func=page_repository_edit, methods=["POST"]) +app.add_url_rule("/api/repository/add_condition", view_func=page_repository_add_condition, methods=["POST"]) if __name__ == "__main__": Base.create_all() diff --git a/code/backend/nest_backend/database/tables/Repository.py b/code/backend/nest_backend/database/tables/Repository.py index d01c1ed..7089a73 100644 --- a/code/backend/nest_backend/database/tables/Repository.py +++ b/code/backend/nest_backend/database/tables/Repository.py @@ -9,8 +9,9 @@ class Repository(Base.Model): __tablename__ = "repository" id = Base.Column(Base.Integer, primary_key=True) name = Base.Column(Base.String, nullable=False) - start = Base.Column(Base.DateTime, nullable=False) + start = Base.Column(Base.DateTime, nullable=True) end = Base.Column(Base.DateTime, nullable=True) + isActive = Base.Column(Base.Boolean, nullable=False, default=False) # Foreign Keys owner_id = Base.Column(Base.String, Base.ForeignKey("user.email"), nullable=False) # Relationships diff --git a/code/backend/nest_backend/database/tables/__init__.py b/code/backend/nest_backend/database/tables/__init__.py index 1b80d7b..1408a55 100644 --- a/code/backend/nest_backend/database/tables/__init__.py +++ b/code/backend/nest_backend/database/tables/__init__.py @@ -13,3 +13,4 @@ from .Repository import Repository from .Tweet import Tweet from .User import User from .Uses import Uses +from .Enums import ConditionType, OperationType \ No newline at end of file diff --git a/code/backend/nest_backend/gestione.py b/code/backend/nest_backend/gestione.py index 170d630..02c1afa 100644 --- a/code/backend/nest_backend/gestione.py +++ b/code/backend/nest_backend/gestione.py @@ -9,6 +9,7 @@ from .database import * import bcrypt import functools from flask_jwt_extended import get_jwt_identity +from flask import request, jsonify def authenticate(username, password): @@ -60,4 +61,21 @@ def admin_or_403(f): current_user = get_jwt_identity() return f(*args, **kwargs) + return func + + +def repository_auth(f): + @functools.wraps(f) + def func(*args, **kwargs): + user = find_user(get_jwt_identity()) + repository_id = request.json.get("id") + if not repository_id: + return jsonify({"result": "failure", "msg": "Missing one or more parameters."}), 400 + repository = Repository.query.filter_by(id=repository_id) + if not repository: + return jsonify({"result": "failure", "msg": "Can't find repository."}), 404 + if repository.owner_id != user.email: + return jsonify({"result": "failure", + "msg": "Stop right there, criminal scum! Nobody accesses protected data under MY watch!"}), 403 + return f(*args, **kwargs) return func \ No newline at end of file diff --git a/code/backend/nest_backend/routes/__init__.py b/code/backend/nest_backend/routes/__init__.py index 6752c31..1424720 100644 --- a/code/backend/nest_backend/routes/__init__.py +++ b/code/backend/nest_backend/routes/__init__.py @@ -3,4 +3,5 @@ This module imports all the routes that return something to the frontend. """ from .doa import page_doa -from .users import * \ No newline at end of file +from .users import * +from .repository import * diff --git a/code/backend/nest_backend/routes/repository/__init__.py b/code/backend/nest_backend/routes/repository/__init__.py index e69de29..66d676a 100644 --- a/code/backend/nest_backend/routes/repository/__init__.py +++ b/code/backend/nest_backend/routes/repository/__init__.py @@ -0,0 +1,4 @@ +from .repository_add_condition import page_repository_add_condition +from .repository_edit import page_repository_edit +from .repository_create import page_repository_create +from .repository_list import page_repository_list \ No newline at end of file diff --git a/code/backend/nest_backend/routes/repository/repository_add_condition.py b/code/backend/nest_backend/routes/repository/repository_add_condition.py new file mode 100644 index 0000000..8b8439d --- /dev/null +++ b/code/backend/nest_backend/routes/repository/repository_add_condition.py @@ -0,0 +1,34 @@ +from flask import render_template, abort, jsonify, request +from ...database import * +from flask_jwt_extended import jwt_required +from ...gestione import * +from flask_cors import cross_origin + + +@cross_origin() +@jwt_required() +@repository_auth +def page_repository_add_condition(): + """ + API call that allows to add conditions to a repository. + :form id: Repository ID + :form type: The type of the condition. It can either be an 'hashtag', a 'location' or 'time' + :form content: The content of the condition (#PdS2021, Roma, 18:00) + :returns: a JSON string that tells whether or not the procedure was a success. + """ + type = request.json.get("type") + if not type or type not in dir(ConditionType): + return jsonify({"result": "failure", "msg": "Could not understand the type of the condition."}), 400 + content = request.json.get("content") + if not content: + return jsonify({"result": "failure", "msg": "Could not find the content"}), 400 + condition = Condition.query.filter(Condition.content.ilike(str(content))).filter_by(type=ConditionType.__getattr__(str(type)).value).first() + if not condition: + condition = Condition(content=content, type=ConditionType.__getattr__(str(type)).value) + Base.session.add(condition) + repository = Repository.query.filter_by(request.json.get("id")) + if Uses.query.filter_by(cid=condition.id, rid=repository.id): + return jsonify({"result": "failure", "msg": "This condition is already connected to the repository."}), 406 + Base.session.add(Uses(cid=condition.id, rid=repository.id)) + Base.session.commit() + return jsonify({"result": "success", "content": "Condition added successfully."}), 200 \ No newline at end of file diff --git a/code/backend/nest_backend/routes/repository/repository_create.py b/code/backend/nest_backend/routes/repository/repository_create.py index c96264b..820c3e4 100644 --- a/code/backend/nest_backend/routes/repository/repository_create.py +++ b/code/backend/nest_backend/routes/repository/repository_create.py @@ -12,13 +12,14 @@ def page_repository_create(): """ API call that allows an user to create a new repository. :form name: The name of the repository. - :returns: If the user is logged in and + :returns: If the user is logged in and has provided the repository name, a JSON string is returned containing + the return status of the operation and the repository in json format. """ user = find_user(get_jwt_identity()) name = request.json.get("name") if not name: - return jsonify({"result": "failure", "msg": "Missing one or more parameters"}), 40 - repository = Repository(name=name, start=datetime.datetime.now(), owner_id=user.email) + return jsonify({"result": "failure", "msg": "Missing one or more parameters"}), 400 + repository = Repository(name=name, owner_id=user.email) Base.session.add(repository) Base.session.commit() - return jsonify({"result":"success", "content":repository.to_json()}), 200 \ No newline at end of file + return jsonify({"result": "success", "content": repository.to_json()}), 200 diff --git a/code/backend/nest_backend/routes/repository/repository_edit.py b/code/backend/nest_backend/routes/repository/repository_edit.py new file mode 100644 index 0000000..527bd5e --- /dev/null +++ b/code/backend/nest_backend/routes/repository/repository_edit.py @@ -0,0 +1,28 @@ +from flask import render_template, abort, jsonify, request +from ...database import * +from flask_jwt_extended import jwt_required +from ...gestione import * +from flask_cors import cross_origin +import datetime + + +@cross_origin() +@jwt_required() +@repository_auth +def page_repository_edit(): + """ + This API call allows to edit a repository. + :form name: If present, it changes the repository name. + :form close: If present, it closes the repository. + :returns: A JSON formatted string that either contains an error or the updated representation of the repository. + """ + repository = Repository.query.filter_by(id=request.json['id']) + if 'name' in request.json: + repository.name = request.json['name'] + if 'close' in request.json and not repository.end and repository.isActive: + repository.end = datetime.datetime.now() + repository.isActive = False + if 'open' in request.json and not repository.isActive and not repository.end: + repository.isActive = True + Base.session.commit() + return jsonify({"result": "success", "content":repository.to_json()}) \ No newline at end of file diff --git a/code/backend/nest_backend/routes/repository/repository_list.py b/code/backend/nest_backend/routes/repository/repository_list.py new file mode 100644 index 0000000..534fe6e --- /dev/null +++ b/code/backend/nest_backend/routes/repository/repository_list.py @@ -0,0 +1,18 @@ +from flask import render_template, abort, jsonify, request +from ...database import * +from flask_jwt_extended import jwt_required +from ...gestione import * +from flask_cors import cross_origin + + +@cross_origin() +@jwt_required() +def page_repository_list(): + """ + API call that returns the list of repositories. + :returns: a JSON-formatted string that contains under the "content" field the list of repositories that belong to + the user ("owner") and a list of repositories that he can spectate ("spectator"). + """ + user = find_user(get_jwt_identity()) + return {"result": "success", "content": {"owner": [r.to_json() for r in user.owner_of], + "spectator": [r.repository.to_json() for r in user.authorizations]}} diff --git a/code/backend/nest_backend/routes/users/login.py b/code/backend/nest_backend/routes/users/login.py index 407e23c..7ab83c2 100644 --- a/code/backend/nest_backend/routes/users/login.py +++ b/code/backend/nest_backend/routes/users/login.py @@ -15,6 +15,8 @@ def page_login(): The access_token must be included in the Authorization header, using the format Bearer . """ + if not request.json: + abort(400) email = request.json.get("email", None) password = request.json.get("password", None) if authenticate(email, password): diff --git a/code/backend/nest_backend/test/login_test.py b/code/backend/nest_backend/test/login_test.py new file mode 100644 index 0000000..96a07be --- /dev/null +++ b/code/backend/nest_backend/test/login_test.py @@ -0,0 +1,17 @@ +import requests +import json + +auth_code = "" + +def test_login(): + global auth_code + r = requests.post('http://localhost:5000/api/login', json={'email':'admin@admin.com', 'password':'amogus'}) + j = json.loads(r.text) + assert j['result'] == "failure" + r = requests.post('http://localhost:5000/api/login', json={'email':'admin@admin.com', 'password':'password'}) + j = json.loads(r.text) + assert j['result'] == "success" + auth_code = j['access_token'] + +print("Testing del login") +test_login() \ No newline at end of file diff --git a/code/backend/poetry.lock b/code/backend/poetry.lock index 78ee92e..068e0a8 100644 --- a/code/backend/poetry.lock +++ b/code/backend/poetry.lock @@ -1,3 +1,25 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "20.3.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + [[package]] name = "bcrypt" version = "3.2.0" @@ -14,6 +36,14 @@ six = ">=1.4.1" tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] +[[package]] +name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "cffi" version = "1.14.5" @@ -25,6 +55,14 @@ python-versions = "*" [package.dependencies] pycparser = "*" +[[package]] +name = "chardet" +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "click" version = "7.1.2" @@ -33,6 +71,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "flask" version = "1.1.2" @@ -103,6 +149,22 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" [package.extras] docs = ["sphinx"] +[[package]] +name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "itsdangerous" version = "1.1.0" @@ -133,6 +195,28 @@ category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +[[package]] +name = "packaging" +version = "20.9" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["pre-commit", "tox"] + [[package]] name = "psycopg2" version = "2.8.6" @@ -141,6 +225,14 @@ category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +[[package]] +name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "pycparser" version = "2.20" @@ -163,6 +255,53 @@ dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4 docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "pytest" +version = "6.2.3" +description = "pytest: simple powerful testing with Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0.0a1" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "requests" +version = "2.25.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<5" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] + [[package]] name = "six" version = "1.15.0" @@ -202,6 +341,27 @@ postgresql_psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql (<1)", "pymysql"] sqlcipher = ["sqlcipher3-binary"] +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "urllib3" +version = "1.26.4" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotlipy (>=0.6.0)"] + [[package]] name = "werkzeug" version = "1.0.1" @@ -217,9 +377,17 @@ watchdog = ["watchdog"] [metadata] lock-version = "1.1" python-versions = "^3.8.5" -content-hash = "8269162351f5e7827601a662d25048bbc0f58503fa4d251334c45b29feac5e5c" +content-hash = "43d3a980b7c904143a335672261ea79ba00635fc544e1a5c9bb66bc820b15674" [metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] bcrypt = [ {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, {file = "bcrypt-3.2.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7"}, @@ -229,6 +397,10 @@ bcrypt = [ {file = "bcrypt-3.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34"}, {file = "bcrypt-3.2.0.tar.gz", hash = "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29"}, ] +certifi = [ + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, +] cffi = [ {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, @@ -268,10 +440,18 @@ cffi = [ {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, ] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, @@ -333,6 +513,14 @@ greenlet = [ {file = "greenlet-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:5d69bbd9547d3bc49f8a545db7a0bd69f407badd2ff0f6e1a163680b5841d2b0"}, {file = "greenlet-1.0.0.tar.gz", hash = "sha256:719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8"}, ] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] itsdangerous = [ {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, @@ -376,6 +564,14 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] +packaging = [ + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] psycopg2 = [ {file = "psycopg2-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725"}, {file = "psycopg2-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5"}, @@ -393,6 +589,10 @@ psycopg2 = [ {file = "psycopg2-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7"}, {file = "psycopg2-2.8.6.tar.gz", hash = "sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543"}, ] +py = [ + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, @@ -401,6 +601,18 @@ pyjwt = [ {file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"}, {file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"}, ] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, + {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, +] +requests = [ + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, +] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, @@ -441,6 +653,14 @@ sqlalchemy = [ {file = "SQLAlchemy-1.4.10-cp39-cp39-win_amd64.whl", hash = "sha256:8b3f2719f8957e3402bdcbddde8054399c09898e62ef4b2a7f946ae7e78e7945"}, {file = "SQLAlchemy-1.4.10.tar.gz", hash = "sha256:483fb869a3151dae6d1c97d242fe2e1b08f97bc71bd4946229930b9efedc9d9d"}, ] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +urllib3 = [ + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, +] werkzeug = [ {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, {file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"}, diff --git a/code/backend/pyproject.toml b/code/backend/pyproject.toml index 2c77c25..e193638 100644 --- a/code/backend/pyproject.toml +++ b/code/backend/pyproject.toml @@ -12,6 +12,8 @@ Flask-SQLAlchemy = "^2.5.1" bcrypt = "^3.2.0" Flask-JWT-Extended = "^4.1.0" Flask-Cors = "^3.0.10" +pytest = "^6.2.3" +requests = "^2.25.1" [tool.poetry.dev-dependencies]