Skip to content

Commit

Permalink
fixed problems with GUI and search
Browse files Browse the repository at this point in the history
  • Loading branch information
rofrano committed Oct 11, 2017
1 parent 967719f commit b23ffe2
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 22 deletions.
96 changes: 96 additions & 0 deletions .cfignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Local environment
.DS_Store
Thumbs.db

# Vagrant
.vagrant/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject
20 changes: 9 additions & 11 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,20 @@ Vagrant.configure(2) do |config|
# Setup a Python development environment
######################################################################
config.vm.provision "shell", inline: <<-SHELL
wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add -
echo "deb http://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list
apt-get update
apt-get install -y git tree python-pip python-dev
#apt-get upgrade -y
#apt-get dist-upgrade -y
apt-get install -y git zip tree python-pip python-dev cf-cli
apt-get -y autoremove
# Install the Bluemix CLI
cd /tmp
export BLUEMIX_CLI="Bluemix_CLI_0.6.1_amd64"
wget http://public.dhe.ibm.com/cloud/bluemix/cli/bluemix-cli/$BLUEMIX_CLI.tar.gz
tar -xvf $BLUEMIX_CLI.tar.gz
cd Bluemix_CLI/
sudo ./install_bluemix_cli
cd ..
rm -fr Bluemix_CLI/
rm $BLUEMIX_CLI.tar.gz
pip install --upgrade pip
# Make vi look nice
sudo -H -u ubuntu echo "colorscheme desert" > ~/.vimrc
# Install app dependencies
echo "\n******************************"
echo " Installing App Dependencies"
echo "******************************\n"
cd /vagrant
sudo pip install -r requirements.txt
SHELL
Expand Down
32 changes: 28 additions & 4 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Pet(object):
redis = None

def __init__(self, id=0, name=None, category=None, available=True):
""" Constructor that lazily connects to database """
""" Constructor """
self.id = int(id)
self.name = name
self.category = category
Expand Down Expand Up @@ -124,11 +124,21 @@ def find(pet_id):
def __find_by(attribute, value):
""" Generic Query that finds a key with a specific value """
# return [pet for pet in Pet.__data if pet.category == category]
Pet.logger.info('Processing %s query for %s', attribute, value)
if isinstance(value, str):
search_criteria = value.lower() # make case insensitive
else:
search_criteria = value
results = []
for key in Pet.redis.keys():
if key != 'index': # filer out our id index
data = pickle.loads(Pet.redis.get(key))
if data[attribute] == value:
# perform case insensitive search on strings
if isinstance(data[attribute], str):
test_value = data[attribute].lower()
else:
test_value = data[attribute]
if test_value == search_criteria:
results.append(Pet(data['id']).deserialize(data))
return results

Expand All @@ -154,10 +164,13 @@ def find_by_availability(available=True):
@staticmethod
def connect_to_redis(hostname, port, password):
""" Connects to Redis and tests the connection """
Pet.logger.info("Testing Connection to: %s:%s", hostname, port)
Pet.redis = Redis(host=hostname, port=port, password=password)
try:
Pet.redis.ping()
Pet.logger.info("Connection established")
except ConnectionError:
Pet.logger.info("Connection Error from: %s:%s", hostname, port)
Pet.redis = None
return Pet.redis

Expand All @@ -171,10 +184,21 @@ def init_db(redis=None):
2) With Redis running on the local server as with Travis CI
3) With Redis --link in a Docker container called 'redis'
4) Passing in your own Redis connection object
Exception:
----------
redis.ConnectionError - if ping() test fails
"""
if redis:
Pet.logger.info("Using passed in connection...")
Pet.logger.info("Using client connection...")
Pet.redis = redis
try:
Pet.redis.ping()
Pet.logger.info("Connection established")
except ConnectionError:
Pet.logger.error("Client Connection Error!")
Pet.redis = None
raise ConnectionError('Could not connect to the Redis Service')
return
# Get the credentials from the Bluemix environment
if 'VCAP_SERVICES' in os.environ:
Expand All @@ -194,4 +218,4 @@ def init_db(redis=None):
if not Pet.redis:
# if you end up here, redis instance is down.
Pet.logger.fatal('*** FATAL ERROR: Could not connect to the Redis Service')
exit(1)
raise ConnectionError('Could not connect to the Redis Service')
11 changes: 7 additions & 4 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import sys
import logging
from flask import Flask, jsonify, request, url_for, make_response, abort
from werkzeug.exceptions import NotFound
from models import Pet, DataValidationError

# Create Flask application
Expand Down Expand Up @@ -136,7 +135,7 @@ def get_pets(pet_id):
"""
pet = Pet.find(pet_id)
if not pet:
raise NotFound("Pet with id '{}' was not found.".format(pet_id))
abort(HTTP_404_NOT_FOUND, "Pet with id '{}' was not found.".format(pet_id))
return make_response(jsonify(pet.serialize()), HTTP_200_OK)

######################################################################
Expand All @@ -153,12 +152,14 @@ def create_pets():
data = {}
# Check for form submission data
if request.headers.get('Content-Type') == 'application/x-www-form-urlencoded':
app.logger.info('Processing FORM data')
data = {
'name': request.form['name'],
'category': request.form['category'],
'available': request.form['available'].lower() in ['true', '1', 't']
}
else:
app.logger.info('Processing JSON data')
data = request.get_json()
pet = Pet()
pet.deserialize(data)
Expand All @@ -179,7 +180,7 @@ def update_pets(pet_id):
"""
pet = Pet.find(pet_id)
if not pet:
raise NotFound("Pet with id '{}' was not found.".format(pet_id))
abort(HTTP_404_NOT_FOUND, "Pet with id '{}' was not found.".format(pet_id))
pet.deserialize(request.get_json())
pet.id = pet_id
pet.save()
Expand Down Expand Up @@ -209,7 +210,7 @@ def purchase_pets(pet_id):
""" Purchase a Pet """
pet = Pet.find(pet_id)
if not pet:
raise NotFound("Pet with id '{}' was not found.".format(pet_id))
abort(HTTP_404_NOT_FOUND, "Pet with id '{}' was not found.".format(pet_id))
if not pet.available:
abort(HTTP_400_BAD_REQUEST, "Pet with id '{}' is not available.".format(pet_id))
pet.available = False
Expand All @@ -220,6 +221,8 @@ def purchase_pets(pet_id):
######################################################################
# U T I L I T Y F U N C T I O N S
######################################################################

@app.before_first_request
def init_db(redis=None):
""" Initlaize the model """
Pet.init_db(redis)
Expand Down
4 changes: 2 additions & 2 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h1 id = "message">Pet Demo REST API Service</h1>
<p class='description'>
GET <a href="/pets" id = "get_pets">/pets</a> to see all the pets.
<br><br>
GET <a href="/pets?category=dog">/pets?category=dog</a> to see all of the pets in the 'dog' category.
GET <a href="/pets?category='dog'">/pets?category=dog</a> to see all of the pets in the 'dog' category.
<br><br>
GET /pets/{id} retrieves a pet from the DB using an ID.
<br><br>
Expand All @@ -36,7 +36,7 @@ <h1 id = "message">Pet Demo REST API Service</h1>
</pre>
Try it now:
<form id="create_pets" action="/pets" method="post" enctype="application/json">
<!-- <input type="hidden" name="available" value="True" /> -->
<input type="hidden" name="available" value="True" />
<div>
<label for="name">What is the name of the pet?</label>
<input name="name" id="name" value="Bowser">
Expand Down
59 changes: 58 additions & 1 deletion tests/test_pets.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,24 @@
nosetests -v --with-spec --spec-color
"""

import os
import json
import unittest
#from redis import Redis
from mock import patch
from redis import Redis, ConnectionError
from models import Pet, DataValidationError

VCAP_SERVICES = {
'rediscloud': [
{'credentials': {
'password': '',
'hostname': '127.0.0.1',
'port': '6379'
}
}
]
}

######################################################################
# T E S T C A S E S
######################################################################
Expand Down Expand Up @@ -151,6 +165,15 @@ def test_pet_not_found(self):
pet = Pet.find(2)
self.assertIs(pet, None)

def test_find_by_name(self):
""" Find a Pet by Name """
Pet(0, "fido", "dog").save()
Pet(0, "kitty", "cat").save()
pets = Pet.find_by_name("fido")
self.assertNotEqual(len(pets), 0)
self.assertEqual(pets[0].category, "dog")
self.assertEqual(pets[0].name, "fido")

def test_find_by_category(self):
""" Find a Pet by Category """
Pet(0, "fido", "dog").save()
Expand All @@ -168,6 +191,40 @@ def test_find_by_availability(self):
self.assertEqual(len(pets), 1)
self.assertEqual(pets[0].name, "kitty")

def test_for_case_insensitive(self):
""" Test for Case Insensitive Search """
Pet(0, "Fido", "DOG").save()
Pet(0, "Kitty", "CAT").save()
pets = Pet.find_by_name("fido")
self.assertNotEqual(len(pets), 0)
self.assertEqual(pets[0].name, "Fido")
pets = Pet.find_by_category("cat")
self.assertNotEqual(len(pets), 0)
self.assertEqual(pets[0].category, "CAT")

def test_passing_connection(self):
""" Pass in the Redis connection """
Pet.init_db(Redis(host='127.0.0.1', port=6379))
self.assertIsNotNone(Pet.redis)

def test_passing_bad_connection(self):
""" Pass in a bad Redis connection """
self.assertRaises(ConnectionError, Pet.init_db, Redis(host='127.0.0.1', port=6300))
self.assertIsNone(Pet.redis)

@patch.dict(os.environ, {'VCAP_SERVICES': json.dumps(VCAP_SERVICES)})
def test_vcap_services(self):
""" Test if VCAP_SERVICES works """
Pet.init_db()
self.assertIsNotNone(Pet.redis)

@patch('redis.Redis.ping')
def test_redis_connection_error(self, ping_error_mock):
""" Test a Bad Redis connection """
ping_error_mock.side_effect = ConnectionError()
self.assertRaises(ConnectionError, Pet.init_db)
self.assertIsNone(Pet.redis)


######################################################################
# M A I N
Expand Down
2 changes: 2 additions & 0 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def test_get_pet_not_found(self):
""" Get a Pet that doesn't exist """
resp = self.app.get('/pets/0')
self.assertEqual(resp.status_code, HTTP_404_NOT_FOUND)
data = json.loads(resp.data)
self.assertIn('was not found', data['message'])

def test_create_pet(self):
""" Create a new Pet """
Expand Down

0 comments on commit b23ffe2

Please sign in to comment.