diff --git a/apprise_api/.coveragerc b/.coveragerc similarity index 100% rename from apprise_api/.coveragerc rename to .coveragerc diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..617b209 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +language: python + +dist: xenial + +matrix: + include: + - python: "3.4" + env: TOXENV=py34 + - python: "3.5" + env: TOXENV=py35 + - python: "3.6" + env: TOXENV=py36 + - python: "3.7" + env: TOXENV=py37 + - python: "pypy3.5-6.0" + env: TOXENV=pypy3 + +install: + - pip install codecov + - pip install -r dev-requirements.txt + - pip install -r requirements.txt + +# run tests +script: + - tox + +after_success: + - tox -e coverage-report + - codecov + +notifications: + email: false diff --git a/README.md b/README.md index 70e8375..9cef82c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Apprise API was designed to easily fit into existing (and new) eco-systems that [![Paypal](https://img.shields.io/badge/paypal-donate-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MHANV39UZNQ5E) [![Discord](https://img.shields.io/discord/558793703356104724.svg?colorB=7289DA&label=Discord&logo=Discord&logoColor=7289DA&style=flat-square)](https://discord.gg/MMPeN2D)
+[![Build Status](https://travis-ci.org/caronc/apprise-api.svg?branch=master)](https://travis-ci.org/caronc/apprise-api) +[![CodeCov Status](https://codecov.io/github/caronc/apprise-pai/branch/master/graph/badge.svg)](https://codecov.io/github/caronc/apprise-api) [![Docker Pulls](https://img.shields.io/docker/pulls/caronc/apprise.svg?style=flat-square)](https://hub.docker.com/r/caronc/apprise) ## Screenshots @@ -58,8 +60,8 @@ docker-compose up - `{KEY}` must be 1-64 alphanumeric characters in length. In addition to this, the underscore (`_`) and dash (`-`) are also accepted. - You must `POST` to URLs defined above in order for them to respond. - Specify the `Content-Type` of `application/json` to use the JSON support. -- There is no authentication required to use this API; this is by design. It's intention is to be a lightweight and fast micro-service parked behind the systems designed to handle security. -- There are no persistent store dependencies for the purpose of simplicity. Configuration is hashed and written straight to disk. +- There is no authentication (or SSL encryption) required to use this API; this is by design. The intentio here to be a lightweight and fast micro-service that can be parked behind another tier that was designed to handle security. +- There are no additional dependencies should you choose to use the optional persistent store (mounted as `/config`). ### Environment Variables @@ -67,8 +69,8 @@ The use of environment variables allow you to provide over-rides to default sett | Variable | Description | |--------------------- | ----------- | -| `APPRISE_CONFIG_DIR` | Defines the persistent store location of all configuration files saved. By default:
- Content is written to the `apprise_api/var/config` directory when just using the _Django_ `manage runserver` script. -| `APPRISE_STATELESS_URLS` | A default set of URLs to notify when using the stateless `/notify` reference (no reference to `{KEY}` variables). +| `APPRISE_CONFIG_DIR` | Defines the persistent store location of all configuration files saved. By default:
- Configuration is written to the `apprise_api/var/config` directory when just using the _Django_ `manage runserver` script. +| `APPRISE_STATELESS_URLS` | A default set of URLs to notify when using the stateless `/notify` reference (no reference to `{KEY}` variables). Use this option if you don't intend to use the persistent store at all. | `SECRET_KEY` | A Django variable acting as a *salt* for most things that require security. This API uses it for the hash sequences when writing the configuration files to disk. | `ALLOWED_HOSTS` | A list of strings representing the host/domain names that this API can serve. This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations. By default this is set to `*` allowing any host. Use space to delimit more then one host. | `DEBUG` | This defaults to `False` however can be set to `True`if defined with a non-zero value (such as `1`). diff --git a/apprise_api/api/forms.py b/apprise_api/api/forms.py index 25178fe..4fbb359 100644 --- a/apprise_api/api/forms.py +++ b/apprise_api/api/forms.py @@ -25,7 +25,7 @@ import apprise from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ # Define our potential configuration types CONFIG_FORMATS = ( diff --git a/apprise_api/api/views.py b/apprise_api/api/views.py index 45781b8..b9f4249 100644 --- a/apprise_api/api/views.py +++ b/apprise_api/api/views.py @@ -29,7 +29,7 @@ from django.conf import settings from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from django.views.decorators.gzip import gzip_page -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from .utils import ConfigCache from .forms import AddByUrlForm from .forms import AddByConfigForm @@ -127,7 +127,7 @@ class AddView(View): # Prepare our default response try: # load our JSON content - content = json.loads(request.body) + content = json.loads(request.body.decode('utf-8')) except (AttributeError, ValueError): # could not parse JSON response... @@ -338,7 +338,7 @@ class NotifyView(View): # Prepare our default response try: # load our JSON content - content = json.loads(request.body) + content = json.loads(request.body.decode('utf-8')) except (AttributeError, ValueError): # could not parse JSON response... @@ -456,7 +456,7 @@ class StatelessNotifyView(View): # Prepare our default response try: # load our JSON content - content = json.loads(request.body) + content = json.loads(request.body.decode('utf-8')) except (AttributeError, ValueError): # could not parse JSON response... diff --git a/dev-requirements.txt b/dev-requirements.txt index fb6efbf..734ae31 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,6 @@ -pytest flake8 +mock pytest-django +pytest pytest-cov +tox diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..6ba2371 --- /dev/null +++ b/tox.ini @@ -0,0 +1,62 @@ +[tox] +envlist = py34,py35,py36,py37,pypy3,coverage-report +skipsdist = true + +[testenv] +# Prevent random setuptools/pip breakages like +# https://github.com/pypa/setuptools/issues/1042 from breaking our builds. +setenv = + VIRTUALENV_NO_DOWNLOAD=1 +deps= + -r{toxinidir}/requirements.txt + -r{toxinidir}/dev-requirements.txt +commands = + coverage run --parallel -m pytest {posargs} apprise_api + flake8 apprise_api --count --show-source --statistics + +[testenv:py34] +deps= + -r{toxinidir}/requirements.txt + -r{toxinidir}/dev-requirements.txt +commands = + coverage run --parallel -m pytest {posargs} apprise_api + flake8 apprise_api --count --show-source --statistics + +[testenv:py35] +deps= + -r{toxinidir}/requirements.txt + -r{toxinidir}/dev-requirements.txt +commands = + coverage run --parallel -m pytest {posargs} apprise_api + flake8 apprise_api --count --show-source --statistics + +[testenv:py36] +deps= + -r{toxinidir}/requirements.txt + -r{toxinidir}/dev-requirements.txt +commands = + coverage run --parallel -m pytest {posargs} apprise_api + flake8 apprise_api --count --show-source --statistics + +[testenv:py37] +deps= + -r{toxinidir}/requirements.txt + -r{toxinidir}/dev-requirements.txt +commands = + coverage run --parallel -m pytest {posargs} apprise_api + flake8 apprise_api --count --show-source --statistics + +[testenv:pypy3] +deps= + -r{toxinidir}/requirements.txt + -r{toxinidir}/dev-requirements.txt +commands = + coverage run --parallel -m pytest {posargs} apprise_api + flake8 apprise_api --count --show-source --statistics + +[testenv:coverage-report] +deps = coverage +skip_install = true +commands= + coverage combine apprise_api + coverage report apprise_api