diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 792c8d9..7f9ebb5 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -49,8 +49,11 @@ jobs: - name: Check types statically run: poetry run mypy . --strict + - name: Install mongodb + run: sudo docker run --name mongo -d -p 27017:27017 mongo + - name: Test with pytest run: poetry run pytest env: - MONGO_URI: ${{ secrets.MongoUri }} + MONGO_URI: mongodb://localhost:27017 FLASK_ENV: testing diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..705d71c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM python:3.8 + +ARG INSTALL_PATH="/ito" + +ENV PIP_DISABLE_PIP_VERSION_CHECK=on +ENV FLASK_ENV "development" +ENV MONGO_URI "mongodb://localhost:27017" + +WORKDIR ${INSTALL_PATH} + +RUN pip install poetry + +COPY . ${INSTALL_PATH}/ + +RUN mv config.py.example config.py && \ + poetry config virtualenvs.create false && \ + poetry install + +CMD poetry run flask run --host 0.0.0.0 --port=5001 \ No newline at end of file diff --git a/app.py b/app.py index 86b7726..22728f7 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,30 @@ from flask import Flask -from app import create_app +import os +import json +from pymongo.errors import DuplicateKeyError # type: ignore +from flask import Flask, Response, request +from db.db import DBConnection +from models.api import APIError -app: Flask = create_app() +app = Flask(__name__) +dbConn = DBConnection(os.environ.get("MONGO_URI")) + + +@app.route("/report", methods=["GET", "POST"]) +def report() -> Response: + if request.method == "GET": + return Response( + json.dumps(dbConn.get_reportsigs()), 200, mimetype="application/json" + ) + else: + data = request.get_json() + if "reportsig" not in data or "timestamp" not in data: + return APIError(400, "Missing values in request").as_response() + reportsig = data["reportsig"] + timestamp = data["timestamp"] + + try: + dbConn.insert_reportsig(reportsig, timestamp) + except: + pass + return Response(None, 200) diff --git a/app/__init__.py b/app/__init__.py deleted file mode 100644 index a1f21ba..0000000 --- a/app/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -from flask import Flask -from app.persistence.db import mongo -import os - -from .routes.v0 import cases -from .routes.v0 import contacts - - -def create_app() -> Flask: - app = Flask(__name__) - - app.register_blueprint(cases) - app.register_blueprint(contacts) - - env = os.environ.get("FLASK_ENV", "production") - if env == "production": - app.config.from_object("config.ProductionConfig") - elif env == "development": - app.config.from_object("config.DevelopmentConfig") - elif env == "testing": - app.config.from_object("config.TestingConfig") - mongo_uri = os.environ.get("MONGO_URI") - if mongo_uri is not None: - app.config.update({"MONGO_URI": mongo_uri}) - - mongo.init_app(app) - return app diff --git a/app/model/case.py b/app/model/case.py deleted file mode 100644 index 1f55cfb..0000000 --- a/app/model/case.py +++ /dev/null @@ -1,35 +0,0 @@ -import json -from uuid import UUID -from typing import List, Optional -from flask import Response -from datetime import datetime - -# https://confluence.ito-app.org/display/IN/API+Documentation - - -class Case: - uuid: UUID - lat: Optional[float] - lon: Optional[float] - trust_level: int - upload_timestamp: datetime - - def __init__( - self, - uuid: UUID, - lat: Optional[float], - lon: Optional[float], - trust_level: int, - upload_timestamp: datetime, - ): - self.uuid = uuid - self.lat = lat - self.lon = lon - self.trust_level = trust_level - self.upload_timestamp = upload_timestamp - - def toJSON(self) -> str: - return json.dumps(self, default=lambda o: o.__dict__) - - def as_response(self, code: int) -> Response: - return Response(self.toJSON(), status=code, mimetype="application/json") diff --git a/app/model/contact.py b/app/model/contact.py deleted file mode 100644 index 1cc887d..0000000 --- a/app/model/contact.py +++ /dev/null @@ -1,37 +0,0 @@ -import json -from uuid import UUID -from typing import List, Optional -from flask import Response -from datetime import datetime - -# https://confluence.ito-app.org/display/IN/API+Documentation - - -class Contact: - uuid: UUID - lat: Optional[float] - lon: Optional[float] - trust_level: int - upload_timestamp: datetime - case_uuid: str - - def __init__( - self, - uuid: UUID, - lat: Optional[float], - lon: Optional[float], - trust_level: int, - upload_timestamp: datetime, - case_uuid: str, - ): - self.uuid = uuid - self.lat = lat - self.lon = lon - self.trust_level = trust_level - self.upload_timestamp = upload_timestamp - self.case_uuid = case_uuid - - def as_response(self, code: int) -> Response: - return Response( - json.dumps(self.__dict__), status=code, mimetype="application/json" - ) diff --git a/app/persistence/db.py b/app/persistence/db.py deleted file mode 100644 index a41035d..0000000 --- a/app/persistence/db.py +++ /dev/null @@ -1,62 +0,0 @@ -from flask import Flask -from flask_pymongo import PyMongo # type: ignore -from datetime import datetime, timedelta -from typing import Optional, Dict, Any, Iterator, List -from uuid import uuid4, UUID -from random import randrange, uniform -import time -from itertools import repeat -from app.model.case import Case -from app.model.contact import Contact - -mongo = PyMongo() - - -def insert_cases(cases: List[Case]) -> None: - mongo.db.cases.insert_many(cases) - - -def count_cases() -> int: - return int(mongo.db.cases.count_documents({})) - - -def insert_contacts(contacts: List[Contact]) -> None: - mongo.db.contacts.insert_many(contacts) - return - - -def random_time_in_the_past() -> datetime: - # FIXME: use a cryptographically secure RNG - now: datetime = datetime.now() - one_day_ago: datetime = now - timedelta(days=1) - noise_minutes: int = randrange(start=0, stop=60 * 24 * 6, step=1) - # anything between 1 and 7 days ago - return one_day_ago - timedelta(minutes=noise_minutes) - - -def insert_random_cases(n: int) -> None: - for _ in repeat(None, n): - mongo.db.cases.insert( - Case( - uuid4(), - round(uniform(-90, 90), 1), - round(uniform(-180, 180), 1), - 1, - random_time_in_the_past(), - ) - ) - - -def generate_random_cases(n: int) -> List[Case]: - cases: List[Case] = [] - for _ in repeat(None, n): - cases.append( - Case( - uuid4(), - round(uniform(-90, 90), 1), - round(uniform(-180, 180), 1), - 1, - random_time_in_the_past(), - ) - ) - return cases diff --git a/app/routes/__init__.py b/app/routes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/routes/v0/__init__.py b/app/routes/v0/__init__.py deleted file mode 100644 index 91f6c29..0000000 --- a/app/routes/v0/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -__all__ = ["cases", "contacts"] - -from .cases import * -from .contacts import * diff --git a/app/routes/v0/cases.py b/app/routes/v0/cases.py deleted file mode 100644 index c94d2d9..0000000 --- a/app/routes/v0/cases.py +++ /dev/null @@ -1,21 +0,0 @@ -from flask import Blueprint, request, Response, abort, current_app -from app.model import ApiError -from app.model.case import Case -from app.persistence.db import insert_cases -from typing import Any, Optional, List - -cases = Blueprint("v0.cases", __name__, url_prefix="/v0/cases") - - -@cases.route("/report", methods=["POST"]) -def report() -> Response: - # TODO: check that user's infection has been verified - if not (current_app.config["DEBUG"] or current_app.config["TESTING"]): - return ApiError(501, "only available in dev and testing for now").as_response() - cases: Optional[List[Case]] = request.get_json() - if cases is None: - return ApiError( - 400, "please use the application/json content type", - ).as_response() - insert_cases(cases) - return Response(None, status=201) diff --git a/app/routes/v0/contacts.py b/app/routes/v0/contacts.py deleted file mode 100644 index 377b006..0000000 --- a/app/routes/v0/contacts.py +++ /dev/null @@ -1,20 +0,0 @@ -from flask import Blueprint, request, Response, abort, current_app -from app.model import ApiError -from app.model.contact import Contact -from app.persistence.db import insert_contacts -from typing import Any, Optional, List - -contacts = Blueprint("v0.contacts", __name__, url_prefix="/v0/contacts") - - -@contacts.route("/report", methods=["POST"]) -def report() -> Response: - if not (current_app.config["DEBUG"] or current_app.config["TESTING"]): - return ApiError(501, "only available in dev and testing for now").as_response() - contacts: Optional[List[Contact]] = request.get_json() - if contacts is None: - return ApiError( - 400, "please use the application/json content type" - ).as_response() - insert_contacts(contacts) - return Response(None, status=201) diff --git a/conftest.py b/conftest.py index e7343e3..2c18995 100644 --- a/conftest.py +++ b/conftest.py @@ -1,10 +1,8 @@ from flask import Flask import pytest # type: ignore - -from app import create_app +from app import app as theapp @pytest.fixture # type: ignore def app() -> Flask: - app = create_app() - return app + return theapp diff --git a/app/persistence/__init__.py b/db/__init__.py similarity index 100% rename from app/persistence/__init__.py rename to db/__init__.py diff --git a/db/db.py b/db/db.py new file mode 100644 index 0000000..0b56271 --- /dev/null +++ b/db/db.py @@ -0,0 +1,27 @@ +from flask import Flask +from pymongo import MongoClient, DESCENDING # type: ignore +from typing import Optional, Dict, Any, Iterator, List +from uuid import uuid4, UUID +from datetime import datetime +import time +from random import randrange + + +class DBConnection: + db: Any + + def __init__(self, hostUri: Optional[str]): + if hostUri is None: + hostUri = "mongodb://localhost:27017" + client = MongoClient(hostUri) + self.db = client.ito + + # Create index for reportsigs collection to make sure that reportsig + # is unique. + self.db.reportsigs.create_index([("reportsig", DESCENDING)], unique=True) + + def insert_reportsig(self, reportsig: str, timestamp: datetime) -> None: + self.db.reportsigs.insert_one({"reportsig": reportsig, "timestamp": timestamp}) + + def get_reportsigs(self) -> List[str]: + return list(self.db.reportsigs.find({}, {"_id": False})) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..00cbed9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.7" +services: + mongodb: + image: mongo:latest + ports: + - 27017:27017 + networks: + - ito-network + + ito-upload: + build: + context: . + dockerfile: Dockerfile + environment: + - MONGO_URI=mongodb://mongodb:27017 + ports: + - 5001:5001 + networks: + - ito-network + depends_on: + - mongodb + +networks: + ito-network: + +volumes: + db-data: \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..0a0e47b --- /dev/null +++ b/models/__init__.py @@ -0,0 +1 @@ +from .api import * diff --git a/app/model/__init__.py b/models/api.py similarity index 97% rename from app/model/__init__.py rename to models/api.py index abecfea..42fed09 100644 --- a/app/model/__init__.py +++ b/models/api.py @@ -3,7 +3,7 @@ from typing import Optional -class ApiError: +class APIError: code: int message: str diff --git a/poetry.lock b/poetry.lock index 535c457..9147d82 100644 --- a/poetry.lock +++ b/poetry.lock @@ -85,18 +85,6 @@ dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxco docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] dotenv = ["python-dotenv"] -[[package]] -category = "main" -description = "PyMongo support for Flask applications" -name = "flask-pymongo" -optional = false -python-versions = "*" -version = "2.3.0" - -[package.dependencies] -Flask = ">=0.11" -PyMongo = ">=3.3" - [[package]] category = "main" description = "Various helpers to pass data to untrusted environments and back." @@ -311,7 +299,7 @@ description = "Alternative regular expression module, to replace re." name = "regex" optional = false python-versions = "*" -version = "2020.2.20" +version = "2020.4.4" [[package]] category = "dev" @@ -351,7 +339,7 @@ description = "Backported and Experimental Type Hints for Python 3.5+" name = "typing-extensions" optional = false python-versions = "*" -version = "3.7.4.1" +version = "3.7.4.2" [[package]] category = "dev" @@ -367,14 +355,14 @@ description = "The comprehensive WSGI web application library." name = "werkzeug" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.0.0" +version = "1.0.1" [package.extras] -dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] +dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] watchdog = ["watchdog"] [metadata] -content-hash = "1ae8e16eedb84283afb6c391050ccc4bea0a699fe2efe5b53ff771d14c6e40fe" +content-hash = "d7b9ab986240c755960003ef58899ff2a9c3e78dee517f2224a6cae8fd697871" python-versions = "^3.8" [metadata.files] @@ -406,10 +394,6 @@ flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, ] -flask-pymongo = [ - {file = "Flask-PyMongo-2.3.0.tar.gz", hash = "sha256:620eb02dc8808a5fcb90f26cab6cba9d6bf497b15032ae3ca99df80366e33314"}, - {file = "Flask_PyMongo-2.3.0-py2.py3-none-any.whl", hash = "sha256:8a9577a2c6d00b49f21cb5a5a8d72561730364a2d745551a85349ab02f86fc73"}, -] itsdangerous = [ {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, @@ -588,27 +572,27 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ - {file = "regex-2020.2.20-cp27-cp27m-win32.whl", hash = "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb"}, - {file = "regex-2020.2.20-cp27-cp27m-win_amd64.whl", hash = "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74"}, - {file = "regex-2020.2.20-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400"}, - {file = "regex-2020.2.20-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0"}, - {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc"}, - {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"}, - {file = "regex-2020.2.20-cp36-cp36m-win32.whl", hash = "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69"}, - {file = "regex-2020.2.20-cp36-cp36m-win_amd64.whl", hash = "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b"}, - {file = "regex-2020.2.20-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e"}, - {file = "regex-2020.2.20-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242"}, - {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce"}, - {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab"}, - {file = "regex-2020.2.20-cp37-cp37m-win32.whl", hash = "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431"}, - {file = "regex-2020.2.20-cp37-cp37m-win_amd64.whl", hash = "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1"}, - {file = "regex-2020.2.20-cp38-cp38-manylinux1_i686.whl", hash = "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045"}, - {file = "regex-2020.2.20-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26"}, - {file = "regex-2020.2.20-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2"}, - {file = "regex-2020.2.20-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70"}, - {file = "regex-2020.2.20-cp38-cp38-win32.whl", hash = "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d"}, - {file = "regex-2020.2.20-cp38-cp38-win_amd64.whl", hash = "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa"}, - {file = "regex-2020.2.20.tar.gz", hash = "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5"}, + {file = "regex-2020.4.4-cp27-cp27m-win32.whl", hash = "sha256:90742c6ff121a9c5b261b9b215cb476eea97df98ea82037ec8ac95d1be7a034f"}, + {file = "regex-2020.4.4-cp27-cp27m-win_amd64.whl", hash = "sha256:24f4f4062eb16c5bbfff6a22312e8eab92c2c99c51a02e39b4eae54ce8255cd1"}, + {file = "regex-2020.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:08119f707f0ebf2da60d2f24c2f39ca616277bb67ef6c92b72cbf90cbe3a556b"}, + {file = "regex-2020.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c9423a150d3a4fc0f3f2aae897a59919acd293f4cb397429b120a5fcd96ea3db"}, + {file = "regex-2020.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c087bff162158536387c53647411db09b6ee3f9603c334c90943e97b1052a156"}, + {file = "regex-2020.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1cbe0fa0b7f673400eb29e9ef41d4f53638f65f9a2143854de6b1ce2899185c3"}, + {file = "regex-2020.4.4-cp36-cp36m-win32.whl", hash = "sha256:0ce9537396d8f556bcfc317c65b6a0705320701e5ce511f05fc04421ba05b8a8"}, + {file = "regex-2020.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:7e1037073b1b7053ee74c3c6c0ada80f3501ec29d5f46e42669378eae6d4405a"}, + {file = "regex-2020.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4385f12aa289d79419fede43f979e372f527892ac44a541b5446617e4406c468"}, + {file = "regex-2020.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a58dd45cb865be0ce1d5ecc4cfc85cd8c6867bea66733623e54bd95131f473b6"}, + {file = "regex-2020.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ccccdd84912875e34c5ad2d06e1989d890d43af6c2242c6fcfa51556997af6cd"}, + {file = "regex-2020.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ea4adf02d23b437684cd388d557bf76e3afa72f7fed5bbc013482cc00c816948"}, + {file = "regex-2020.4.4-cp37-cp37m-win32.whl", hash = "sha256:2294f8b70e058a2553cd009df003a20802ef75b3c629506be20687df0908177e"}, + {file = "regex-2020.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:e91ba11da11cf770f389e47c3f5c30473e6d85e06d7fd9dcba0017d2867aab4a"}, + {file = "regex-2020.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5635cd1ed0a12b4c42cce18a8d2fb53ff13ff537f09de5fd791e97de27b6400e"}, + {file = "regex-2020.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:23069d9c07e115537f37270d1d5faea3e0bdded8279081c4d4d607a2ad393683"}, + {file = "regex-2020.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c162a21e0da33eb3d31a3ac17a51db5e634fc347f650d271f0305d96601dc15b"}, + {file = "regex-2020.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:fb95debbd1a824b2c4376932f2216cc186912e389bdb0e27147778cf6acb3f89"}, + {file = "regex-2020.4.4-cp38-cp38-win32.whl", hash = "sha256:2a3bf8b48f8e37c3a40bb3f854bf0121c194e69a650b209628d951190b862de3"}, + {file = "regex-2020.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bfed051dbff32fd8945eccca70f5e22b55e4148d2a8a45141a3b053d6455ae3"}, + {file = "regex-2020.4.4.tar.gz", hash = "sha256:295badf61a51add2d428a46b8580309c520d8b26e769868b922750cf3ce67142"}, ] sentinels = [ {file = "sentinels-1.0.0.tar.gz", hash = "sha256:7be0704d7fe1925e397e92d18669ace2f619c92b5d4eb21a89f31e026f9ff4b1"}, @@ -646,15 +630,15 @@ typed-ast = [ {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.1-py2-none-any.whl", hash = "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d"}, - {file = "typing_extensions-3.7.4.1-py3-none-any.whl", hash = "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"}, - {file = "typing_extensions-3.7.4.1.tar.gz", hash = "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2"}, + {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, + {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, + {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, ] wcwidth = [ {file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, {file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, ] werkzeug = [ - {file = "Werkzeug-1.0.0-py2.py3-none-any.whl", hash = "sha256:6dc65cf9091cf750012f56f2cad759fa9e879f511b5ff8685e456b4e3bf90d16"}, - {file = "Werkzeug-1.0.0.tar.gz", hash = "sha256:169ba8a33788476292d04186ab33b01d6add475033dfc07215e6d219cc077096"}, + {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/pyproject.toml b/pyproject.toml index a253340..266cd4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ authors = [] [tool.poetry.dependencies] python = "^3.8" Flask = "^1.1.2" -Flask-PyMongo = "^2.3.0" python-dateutil = "^2.8.1" +pymongo = "^3.10.1" [tool.poetry.dev-dependencies] black = "^19.10b0" diff --git a/tests/v0/test_cases.py b/tests/v0/test_cases.py index 900d83d..08935b0 100644 --- a/tests/v0/test_cases.py +++ b/tests/v0/test_cases.py @@ -1,32 +1,39 @@ -from flask import url_for -from uuid import uuid4, UUID -from app.persistence.db import count_cases, generate_random_cases import json -from flask.testing import FlaskClient -from flask import Response from datetime import datetime +from flask import url_for, Response +from flask.testing import FlaskClient -def test_insert(client: FlaskClient): - # TODO: actually test that case is inserted - prev_count: int = count_cases() - n: int = 10 - cases: list = generate_random_cases(n) +def test_report_post(client: FlaskClient): + default_reportsig = "teststr" + + data = { + "reportsig": default_reportsig, + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + } res: Response = client.post( - url_for("v0.cases.report"), - data=json.dumps([case.__dict__ for case in cases], cls=CaseEncoder), - content_type="application/json", + url_for(".report"), data=json.dumps(data), content_type="application/json" ) - assert res.status_code == 201 - assert count_cases() == (prev_count + n) + assert res.status_code == 200 + +def test_report_post_duplicate(client: FlaskClient): + duplicate_reportsig = "duplicate" -# copied from https://stackoverflow.com/a/48159596/9926795 -class CaseEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, UUID): - # if the obj is uuid, we simply return the value of uuid - return obj.hex - if isinstance(obj, datetime): - return obj.isoformat() - return json.JSONEncoder.default(self, obj) + data1 = { + "reportsig": duplicate_reportsig, + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + } + res1: Response = client.post( + url_for(".report"), data=json.dumps(data1), content_type="application/json" + ) + assert res1.status_code == 200 + + data2 = { + "reportsig": duplicate_reportsig, + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + } + res2: Response = client.post( + url_for(".report"), data=json.dumps(data2), content_type="application/json" + ) + assert res2.status_code == 200