From 8114e8e4e91c86d4218cd1ad44cf2680e84d9a7d Mon Sep 17 00:00:00 2001 From: John Rofrano Date: Tue, 2 Nov 2021 06:46:54 -0400 Subject: [PATCH 1/3] Renamed ci.yaml to workflow.yml --- .github/workflows/{ci.yaml => workflow.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ci.yaml => workflow.yml} (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/workflow.yml similarity index 100% rename from .github/workflows/ci.yaml rename to .github/workflows/workflow.yml From d62d614432ffba370d3c50aa0337b2b6be5f1de4 Mon Sep 17 00:00:00 2001 From: John Rofrano Date: Tue, 2 Nov 2021 11:14:00 +0000 Subject: [PATCH 2/3] Added codecov to github actions --- .github/workflows/workflow.yml | 4 +++ .gitignore | 3 ++ dot-env-example | 2 ++ service/routes.py | 55 +++++++++++++++++++++------------- setup.cfg | 6 +++- tests/test_service.py | 20 +++++++++++++ 6 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 dot-env-example diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index ed6c4ada..6fec6618 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -37,3 +37,7 @@ jobs: run: nosetests env: DATABASE_URI: "redis://redis:6379" + + - name: Upload code coverage + uses: codecov/codecov-action@v2 + \ No newline at end of file diff --git a/.gitignore b/.gitignore index ce3912d8..7285d780 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ .DS_Store .Thumbs.db +# Unit test results +unittests.xml + # Ignore ideas .ideas/ diff --git a/dot-env-example b/dot-env-example new file mode 100644 index 00000000..a96c74b3 --- /dev/null +++ b/dot-env-example @@ -0,0 +1,2 @@ +FLASK_APP=service:app +PORT=8080 \ No newline at end of file diff --git a/service/routes.py b/service/routes.py index 717253b4..1d56797c 100644 --- a/service/routes.py +++ b/service/routes.py @@ -21,21 +21,12 @@ import os from redis import Redis from flask import jsonify, url_for, abort -from werkzeug.exceptions import NotFound from redis.exceptions import ConnectionError from . import status from service import app # Connext to the Redis database -counter = None -if 'DATABASE_URI' in os.environ: - DATABASE_URI = os.getenv("DATABASE_URI") - counter = Redis.from_url(DATABASE_URI, encoding="utf-8", decode_responses=True) -else: - REDIS_HOST = os.getenv("REDIS_HOST", "localhost") - REDIS_PORT = int(os.getenv("REDIS_PORT", "6379")) - counter = Redis(host=REDIS_HOST, port=REDIS_PORT, encoding="utf-8", decode_responses=True) - +counter: Redis = None ############################################################ # Index page @@ -57,9 +48,13 @@ def index(): @app.route("/counters", methods=["GET"]) def list_counters(): app.logger.info("Request to list all counters...") - counters = [ - dict(name=key, counter=int(counter.get(key))) for key in counter.keys("*") - ] + try: + counters = [ + dict(name=key, counter=int(counter.get(key))) for key in counter.keys("*") + ] + except ConnectionError as error: + abort(status.HTTP_503_SERVICE_UNAVAILABLE, str(error)) + return jsonify(counters) @@ -69,11 +64,14 @@ def list_counters(): @app.route("/counters/", methods=["POST"]) def create_counters(name): app.logger.info("Request to Create counter...") - count = counter.get(name) - if count is not None: - return jsonify(code=409, error="Counter already exists"), 409 + try: + count = counter.get(name) + if count is not None: + abort(status.HTTP_409_CONFLICT, f"Counter [{name}] already exists") - counter.set(name, 0) + counter.set(name, 0) + except ConnectionError as error: + abort(status.HTTP_503_SERVICE_UNAVAILABLE, str(error)) location_url = url_for("read_counters", name=name, _external=True) return ( @@ -91,7 +89,7 @@ def read_counters(name): app.logger.info("Request to Read counter...") count = counter.get(name) if count is None: - raise NotFound(f"Counter {name} does not exist") + abort(status.HTTP_404_NOT_FOUND, f"Counter [{name}] does not exist") return jsonify(name=name, counter=int(count)) @@ -104,7 +102,7 @@ def update_counters(name): app.logger.info("Request to Update counter...") count = counter.get(name) if count is None: - raise NotFound(f"Counter {name} does not exist") + abort(status.HTTP_404_NOT_FOUND, f"Counter [{name}] does not exist") count = counter.incr(name) return jsonify(name=name, counter=count) @@ -124,9 +122,24 @@ def delete_counters(name): ############################################################ -# Utility for testing +# U T I L I T Y F U N C T I O N S ############################################################ def reset_counters(): global counter - if app.testing: + if app.testing and counter: counter.flushall() + +@app.before_first_request +def init_db(): + global counter + app.logger.info("Initializing Redis database connection") + try: + if 'DATABASE_URI' in os.environ: + DATABASE_URI = os.getenv("DATABASE_URI") + counter = Redis.from_url(DATABASE_URI, encoding="utf-8", decode_responses=True) + else: + REDIS_HOST = os.getenv("REDIS_HOST", "localhost") + REDIS_PORT = int(os.getenv("REDIS_PORT", "6379")) + counter = Redis(host=REDIS_HOST, port=REDIS_PORT, encoding="utf-8", decode_responses=True) + except ConnectionError as error: + abort(status.HTTP_503_SERVICE_UNAVAILABLE, str(error)) diff --git a/setup.cfg b/setup.cfg index 367b92ef..bc61bdf7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,4 +4,8 @@ with-spec=1 spec-color=1 with-coverage=1 cover-erase=1 -cover-package=service \ No newline at end of file +cover-package=service +with-xunit=1 +xunit-file=./unittests.xml +cover-xml=1 +cover-xml-file=./coverage.xml \ No newline at end of file diff --git a/tests/test_service.py b/tests/test_service.py index a330aca5..180e6bf1 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -7,7 +7,9 @@ """ import os import logging +from unittest.mock import patch from unittest import TestCase +from redis.exceptions import ConnectionError from service import status # HTTP Status Codes from service.log_handler import initialize_logging from service.routes import app, reset_counters @@ -125,3 +127,21 @@ def test_update_not_found(self): name = "foo" resp = self.app.put(f"/counters/{name}") self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) + + def test_bad_connection_create(self): + """ Test a no database connection for create """ + # make a call to fire first requeest trigger + resp = self.app.get(f"/counters") + with patch('service.routes.counter.get') as connection_error_mock: + connection_error_mock.side_effect = ConnectionError() + resp = self.app.post(f"/counters/foo") + self.assertEqual(resp.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) + + def test_bad_connection_list(self): + """ Test a no database connection for list """ + # make a call to fire first requeest trigger + resp = self.app.put(f"/counters/foo") + with patch('service.routes.counter.keys') as connection_error_mock: + connection_error_mock.side_effect = ConnectionError() + resp = self.app.get(f"/counters") + self.assertEqual(resp.status_code, status.HTTP_503_SERVICE_UNAVAILABLE) \ No newline at end of file From 1786a6e16b23440b48b09fc67e4568bebbbe510e Mon Sep 17 00:00:00 2001 From: John Rofrano Date: Tue, 2 Nov 2021 11:20:24 +0000 Subject: [PATCH 3/3] Added vscode-pull-request-github --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 07461541..7c0c0a4b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -16,7 +16,8 @@ "donjayamanne.githistory", "redhat.vscode-yaml", "bbenoist.vagrant", - "sonarsource.sonarlint-vscode" + "sonarsource.sonarlint-vscode", + "GitHub.vscode-pull-request-github" ], "forwardPorts": [8080], //"postCreateCommand": "sudo pip install -U pip wheel && sudo pip install -r requirements.txt"