Skip to content

Commit

Permalink
Upgrade to Django 2+ and remove Whitenoise
Browse files Browse the repository at this point in the history
  • Loading branch information
smendes committed Sep 3, 2019
1 parent 83c65d2 commit 02ed564
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 88 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ omit =
*/migrations/*
*/tests.py
*/tests/*.py
venv/*
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
language: python
python:
- '2.7'
- '3.3'
- '3.4'
- '3.6'
- '3.7'
install:
- pip install -r requirements.txt
- pip install coveralls
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Twilio Inc.
Copyright (c) 2019 Twilio Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ Use Twilio to send SMS alerts so that you never miss a critical issue.

## Quickstart

This project is built using the [Django](https://www.djangoproject.com/) web framework. It runs on Python 2.7+ and Python 3.4+.
This project is built using the [Django](https://www.djangoproject.com/) web framework. It runs on Python 3.6+.

To run the app locally, first clone this repository and `cd` into its directory. Then:

1. Create a new virtual environment:
- If using vanilla [virtualenv](https://virtualenv.pypa.io/en/latest/):
- If using vanilla with Python 3 [virtualenv](https://docs.python.org/3/library/venv.html):

```
virtualenv venv
python -m venv venv
source venv/bin/activate
```
Expand All @@ -35,14 +35,14 @@ To run the app locally, first clone this repository and `cd` into its directory.
1. Copy the `.env_example` file to `.env`, and edit it to include your Twilio API credentials (found at https://www.twilio.com/user/account/voice)
1. For the TWILIO_NUMBER variable you'll need to provision a new number in the [Manage Numbers page](https://www.twilio.com/user/account/phone-numbers/incoming) under your account. The phone number should be in E.164 format
1. Run `source .env` to apply the environment variables (or even better, use [autoenv](https://github.com/kennethreitz/autoenv))
1. (Optional) This project integrate [python-dotenv](https://github.com/theskumar/python-dotenv) to automatically load the `.env` file. Alternatively, you can run `source .env` to apply the environment variables (or even use [autoenv](https://github.com/kennethreitz/autoenv))
1. Customize `config/administrators.json` with your phone number.
1. Start the development server
```
python manage.py runserver
```
1. Go to [http://localhost:8000/error](http://localhost:8000/error). You'll receive a text shortly with details on the exception.
1. Go to [http://localhost:8000/error](http://localhost:8000/error/). You'll receive a text shortly with details on the exception.
## Run the tests
Expand Down
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Requirements for server-notifications-django

# Django (1.8 is a long-term support release)
Django==1.8
twilio==6.9.0
whitenoise
Django==2.2.5
twilio==6.29.4
python-dotenv==0.10.3

# Production dependencies
gunicorn

# Test dependencies
black
coverage
flake8
mock
six
88 changes: 55 additions & 33 deletions twilio_notifications/middleware.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,89 @@
from __future__ import unicode_literals
import json
import logging
import os

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponse
from dotenv import load_dotenv
from twilio.rest import Client
from django.core.exceptions import MiddlewareNotUsed
import os
import logging
import json

logger = logging.getLogger(__name__)

dotenv_path = settings.PROJECT_PATH / '.env'
logger.debug(f'Reading .env file at: {dotenv_path}')
load_dotenv(dotenv_path=dotenv_path)


MESSAGE = """[This is a test] ALERT! It appears the server is having issues.
Exception: %s. Go to: http://newrelic.com for more details."""
Exception: {0}"""

NOT_CONFIGURED_MESSAGE = """Cannot initialize Twilio notification
middleware. Required enviroment variables TWILIO_ACCOUNT_SID, or
TWILIO_AUTH_TOKEN or TWILIO_NUMBER missing"""
NOT_CONFIGURED_MESSAGE = (
"Required enviroment variables "
"TWILIO_ACCOUNT_SID or TWILIO_AUTH_TOKEN or TWILIO_NUMBER missing."
)


def load_admins_file():
with open('config/administrators.json') as adminsFile:
admins = json.load(adminsFile)
return admins
admins_json_path = settings.PROJECT_PATH / 'config' / 'administrators.json'
logger.debug(f'Loading administrators info from: {admins_json_path}')
return json.loads(admins_json_path.read_text())


def load_twilio_config():
twilio_account_sid = os.environ.get('TWILIO_ACCOUNT_SID')
twilio_auth_token = os.environ.get('TWILIO_AUTH_TOKEN')
twilio_number = os.environ.get('TWILIO_NUMBER')
logger.debug('Loading Twilio configuration')

twilio_account_sid = os.getenv('TWILIO_ACCOUNT_SID')
twilio_auth_token = os.getenv('TWILIO_AUTH_TOKEN')
twilio_number = os.getenv('TWILIO_NUMBER')

if not all([twilio_account_sid, twilio_auth_token, twilio_number]):
logger.error(NOT_CONFIGURED_MESSAGE)
raise MiddlewareNotUsed
raise ImproperlyConfigured(NOT_CONFIGURED_MESSAGE)

return (twilio_number, twilio_account_sid, twilio_auth_token)


class MessageClient(object):
class MessageClient:
def __init__(self):
(twilio_number, twilio_account_sid,
twilio_auth_token) = load_twilio_config()
logger.debug('Initializing messaging client')

(
twilio_number,
twilio_account_sid,
twilio_auth_token,
) = load_twilio_config()

self.twilio_number = twilio_number
self.twilio_client = Client(twilio_account_sid,
twilio_auth_token)
self.twilio_client = Client(twilio_account_sid, twilio_auth_token)

logger.debug('Twilio client initialized')

def send_message(self, body, to):
self.twilio_client.messages.create(body=body, to=to,
from_=self.twilio_number,
# media_url=['https://demo.twilio.com/owl.png'])
)
self.twilio_client.messages.create(
body=body, to=to, from_=self.twilio_number
)


class TwilioNotificationsMiddleware(object):
def __init__(self):
class TwilioNotificationsMiddleware:
def __init__(self, get_response):
logger.debug('Initializing Twilio notifications middleware')

self.administrators = load_admins_file()
self.client = MessageClient()
self.get_response = get_response

logger.debug('Twilio notifications middleware initialized')

def __call__(self, request):
return self.get_response(request)

def process_exception(self, request, exception):
exception_message = str(exception)
message_to_send = MESSAGE % exception_message
message_to_send = MESSAGE.format(exception)

for admin in self.administrators:
self.client.send_message(message_to_send, admin['phone_number'])

logger.info('Administrators notified')

return None
logger.info('Administrators notified!')
return HttpResponse(
"An error occured. Don't panic! Administrators are notified."
)
43 changes: 27 additions & 16 deletions twilio_notifications/tests/test_notification_middleware.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
from __future__ import unicode_literals
from mock import patch, Mock
from django.core.exceptions import MiddlewareNotUsed
import unittest
import os
import unittest

from twilio_notifications.middleware import TwilioNotificationsMiddleware
from twilio_notifications.middleware import MessageClient, load_twilio_config
from twilio_notifications.middleware import MESSAGE
from django.core.exceptions import ImproperlyConfigured
from mock import Mock, patch
from twilio_notifications.middleware import (
MESSAGE,
MessageClient,
TwilioNotificationsMiddleware,
load_admins_file,
load_twilio_config,
)


class TestNotificationMiddleware(unittest.TestCase):

@patch('twilio_notifications.middleware.load_twilio_config')
@patch('twilio_notifications.middleware.load_admins_file')
def test_notify_on_exception(self, mock_load_admins_file,
mock_load_twilio_config):
def test_notify_on_exception(
self, mock_load_admins_file, mock_load_twilio_config
):

# Given
admin_number = '+15550005555'
Expand All @@ -25,10 +28,13 @@ def test_notify_on_exception(self, mock_load_admins_file,
]

mock_message_client = Mock(spec=MessageClient)
mock_load_twilio_config.return_value = (sending_number,
'4ccou1s1d', 'som3tok3n')
mock_load_twilio_config.return_value = (
sending_number,
'4ccou1s1d',
'som3tok3n',
)

middleware = TwilioNotificationsMiddleware()
middleware = TwilioNotificationsMiddleware(None)
middleware.client = mock_message_client

exception_message = 'Some exception message'
Expand All @@ -38,7 +44,7 @@ def test_notify_on_exception(self, mock_load_admins_file,

# Then
mock_message_client.send_message.assert_called_once_with(
MESSAGE % exception_message, admin_number
MESSAGE.format(exception_message), admin_number
)

def test_correct_load_twilio_config(self):
Expand All @@ -48,13 +54,18 @@ def test_correct_load_twilio_config(self):

try:
load_twilio_config()
except MiddlewareNotUsed:
except ImproperlyConfigured:
self.fail('MiddlewareNotUsed when correctly configured')

def test_fail_load_twilio_config(self):
os.environ['TWILIO_ACCOUNT_SID'] = 'some4acc0un1s1d'
os.environ['TWILIO_AUTH_TOKEN'] = 'sometok3n'
os.environ.pop('TWILIO_NUMBER')

with self.assertRaises(MiddlewareNotUsed):
with self.assertRaises(ImproperlyConfigured):
load_twilio_config()

def test_load_admins_file(self):
administrators = load_admins_file()
self.assertIsInstance(administrators, list)
self.assertGreater(len(administrators), 0)
Loading

0 comments on commit 02ed564

Please sign in to comment.