Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/ci.yaml → .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ jobs:
run: nosetests
env:
DATABASE_URI: "redis://redis:6379"

- name: Upload code coverage
uses: codecov/codecov-action@v2

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
.DS_Store
.Thumbs.db

# Unit test results
unittests.xml

# Ignore ideas
.ideas/

Expand Down
2 changes: 2 additions & 0 deletions dot-env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FLASK_APP=service:app
PORT=8080
55 changes: 34 additions & 21 deletions service/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)


Expand All @@ -69,11 +64,14 @@ def list_counters():
@app.route("/counters/<name>", 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 (
Expand All @@ -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))

Expand All @@ -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)
Expand All @@ -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))
6 changes: 5 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ with-spec=1
spec-color=1
with-coverage=1
cover-erase=1
cover-package=service
cover-package=service
with-xunit=1
xunit-file=./unittests.xml
cover-xml=1
cover-xml-file=./coverage.xml
20 changes: 20 additions & 0 deletions tests/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)