Skip to content

Commit

Permalink
Merge branch 'master' into feature/menus
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanNoelk committed Apr 8, 2018
2 parents ff320ac + 7c626e3 commit 7b7cb6d
Show file tree
Hide file tree
Showing 39 changed files with 1,012 additions and 321 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ database/
*.css.map
*.js.map
*.log
.coverage
9 changes: 7 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ sudo: required
language: python

python:
- "2.7"
- "3.6"

services:
- docker
Expand All @@ -15,4 +15,9 @@ before_install:
- sleep 90

script:
- docker-compose -f test.yml -p test run --rm --entrypoint 'sh tests.sh' api
- docker-compose -f test.yml -p test run --rm
--entrypoint 'sh tests.sh'
-e TRAVIS_JOB_ID=$TRAVIS_JOB_ID
-e TRAVIS_BRANCH=$TRAVIS_BRANCH
-e TRAVIS=$TRAVIS
api
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![API Build Status](https://travis-ci.org/open-eats/openeats-api.svg?branch=master)](https://travis-ci.org/open-eats/openeats-api)
[![Coverage Status](https://coveralls.io/repos/github/open-eats/openeats-api/badge.svg)](https://coveralls.io/github/open-eats/openeats-api)
[![Maintainability](https://api.codeclimate.com/v1/badges/ac4a42717db53286ee8f/maintainability)](https://codeclimate.com/github/open-eats/openeats-api/maintainability)

This is the API that powers OpenEats. It uses Django/Django Rest Framework to power the API. The core responsibilities of the APi are:
- OpenEats REST API
Expand All @@ -23,7 +24,8 @@ To run tests locally:
cd openeats-web
docker-compose -f test.yml -p test build
docker-compose -f test.yml -p test up -d db
docker-compose -f test.yml -p test run --rm --entrypoint 'sh tests.sh' api
docker-compose -f test.yml -p test run --rm --entrypoint sh api
python manage.py test
```

Note: If this is the first time you are running the tests, give the DB some time to build itself once it's build there is no need to wait again.
Expand Down
1 change: 1 addition & 0 deletions base/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Django==2.0.1
django-imagekit==4.0.1
djangorestframework==3.7.7
djangorestframework-bulk==0.2.1
djangorestframework-jwt==1.11.0
django-filter==1.1
django-extensions==1.9.9
django-cors-headers==1.3.1
Expand Down
24 changes: 18 additions & 6 deletions base/settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Django settings for openeats project.
import os
import os, datetime

# We can't set the debug just using the env var.
# Python with evaluate any string as a True bool.
Expand All @@ -26,10 +26,10 @@
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ.get('MYSQL_DATABASE', 'openeats'),
'USER': 'root',
'USER': os.environ.get('MYSQL_USER', 'root'),
'PASSWORD': os.environ.get('MYSQL_ROOT_PASSWORD', ''),
'HOST': 'db',
'PORT': '3306',
'HOST': os.environ.get('MYSQL_HOST', 'db'),
'PORT': os.environ.get('MYSQL_PORT', '3306'),
'OPTIONS': {
'charset': 'utf8mb4'
},
Expand Down Expand Up @@ -150,8 +150,9 @@
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication'
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100,
Expand All @@ -163,6 +164,17 @@
}
}

# http://getblimp.github.io/django-rest-framework-jwt/#additional-settings
JWT_AUTH = {
# We are returning custom data to our UI.
'JWT_RESPONSE_PAYLOAD_HANDLER': 'v1.accounts.jwt_handler.handler',

# Allow for token refresh and increase the timeout of the user token.
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=14),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(weeks=30),
}

# We don't want the API to serve static in production.
# So we are forcing the renderer to be JSON only.
if not DEBUG:
Expand Down
16 changes: 15 additions & 1 deletion tests.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
#!/usr/bin/env sh

# Install test related dependencies
pip install coveralls==1.3.0
apk add git

# Prep the DB for testing
python manage.py reset_db --noinput
python manage.py migrate
python manage.py test -k

# Run the tests and create a coverage report
coverage run --omit="*/migrations*,*/fixtures*" manage.py test -k
ret=$?
if [ $ret -ne 0 ]; then
exit 1
fi

# Submit coverage to Coveralls
coveralls
11 changes: 11 additions & 0 deletions v1/accounts/jwt_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python
# encoding: utf-8

from .serializers import UserSerializer


def handler(token, user=None, request=None):
return {
'token': token,
'id': UserSerializer(user, context={'request': request}).data.get('id')
}
14 changes: 0 additions & 14 deletions v1/accounts/signals.py

This file was deleted.

2 changes: 2 additions & 0 deletions v1/accounts/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env python
# encoding: utf-8
14 changes: 11 additions & 3 deletions v1/accounts/tests.py → v1/accounts/tests/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
# encoding: utf-8

from django.test import TestCase
from rest_framework_jwt.settings import api_settings


class AccountTests(TestCase):
fixtures=['test/users.json']
fixtures = ['test/users.json']

def setUp(self):
self.jwt_decode_handler = api_settings.JWT_DECODE_HANDLER

def test_obtain_authtoken_success(self):
""" Try and login and confirm that the login was successful """
resp = self.client.post(
'/api/v1/accounts/obtain-auth-token/',
{
Expand All @@ -18,9 +23,12 @@ def test_obtain_authtoken_success(self):

self.assertEqual(resp.status_code, 200)
self.assertTrue(resp.json()['id'] == 1)
self.assertTrue(len(resp.json()['token']) == 40)
decoded_token = self.jwt_decode_handler(resp.json()['token'])
self.assertTrue(decoded_token.get('user_id') == 1)
self.assertTrue(decoded_token.get('username') == 'testuser1')

def test_obtain_authtoken_wrong_password(self):
""" Try and login and confirm that the login was unsuccessful """
resp = self.client.post(
'/api/v1/accounts/obtain-auth-token/',
{
Expand All @@ -29,4 +37,4 @@ def test_obtain_authtoken_wrong_password(self):
}
)

self.assertEqual(resp.status_code, 400)
self.assertEqual(resp.status_code, 400)
7 changes: 5 additions & 2 deletions v1/accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# encoding: utf-8

from django.urls import path
from v1.accounts.views import custom_obtain_auth_token
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token

urlpatterns = [path('obtain-auth-token/', custom_obtain_auth_token)]
urlpatterns = [
path('obtain-auth-token/', obtain_jwt_token),
path('refresh-auth-token/', refresh_jwt_token)
]
31 changes: 0 additions & 31 deletions v1/accounts/views.py

This file was deleted.

2 changes: 2 additions & 0 deletions v1/common/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env python
# encoding: utf-8
76 changes: 76 additions & 0 deletions v1/common/tests/test_permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python
# encoding: utf-8

from django.contrib.auth.models import AnonymousUser, User
from django.test import TestCase, RequestFactory
from v1.common.permissions import IsAdminOrReadOnly, IsOwnerOrReadOnly


class PermissionTest(TestCase):
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
# Create a staff user.
self.user = User.objects.create_user(
username='jacob', email='jacob@gmail.com', password='top_secret', is_staff=True
)

def test_is_owner_or_read_only(self):
# Try and access something as an admin user.
# Both get and post should have access.
request = self.factory.get('/admin')
request.user = self.user
self.assertTrue(
IsOwnerOrReadOnly().has_permission(request, None)
)
self.assertTrue(
IsOwnerOrReadOnly().has_object_permission(request, None, None)
)
request = self.factory.post('/admin')
request.user = self.user
self.assertTrue(
IsOwnerOrReadOnly().has_permission(request, None)
)

# Try and access something as an anonymous user.
# Both get should have access but post shouldn't.
request = self.factory.get('/admin')
request.user = AnonymousUser()
self.assertTrue(
IsOwnerOrReadOnly().has_permission(request, None)
)
self.assertTrue(
IsOwnerOrReadOnly().has_object_permission(request, None, None)
)
request = self.factory.post('/admin')
request.user = AnonymousUser()
self.assertFalse(
IsOwnerOrReadOnly().has_permission(request, None)
)

def test_is_admin_or_read_only(self):
# Try and access something as an admin user.
# Both get and post should have access.
request = self.factory.get('/admin')
request.user = self.user
self.assertTrue(
IsAdminOrReadOnly().has_permission(request, None)
)
request = self.factory.post('/admin')
request.user = self.user
self.assertTrue(
IsAdminOrReadOnly().has_permission(request, None)
)

# Try and access something as an anonymous user.
# Both get should have access but post shouldn't.
request = self.factory.get('/admin')
request.user = AnonymousUser()
self.assertTrue(
IsAdminOrReadOnly().has_permission(request, None)
)
request = self.factory.post('/admin')
request.user = AnonymousUser()
self.assertFalse(
IsAdminOrReadOnly().has_permission(request, None)
)
36 changes: 36 additions & 0 deletions v1/common/tests/test_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python
# encoding: utf-8

from django.test import TestCase
from v1.recipe.models import Recipe
from v1.common.recipe_search import get_search_results


class GetSearchResultsTests(TestCase):
fixtures = [
'test/users.json',
'course_data.json',
'cuisine_data.json',
'recipe_data.json',
'ing_data.json'
]

def test_get_search_results(self):
""" Run a search that will return data """
query = get_search_results(
['title', 'ingredient_groups__ingredients__title', 'tags__title'],
Recipe.objects,
'chili'
).distinct()

self.assertTrue(len(query.all()) > 0)

def test_get_search_no_results(self):
""" Run a search that will return no data """
query = get_search_results(
['title', 'ingredient_groups__ingredients__title', 'tags__title'],
Recipe.objects,
'blue berry'
).distinct()

self.assertTrue(len(query.all()) == 0)
2 changes: 1 addition & 1 deletion v1/fixtures/news_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"fields": {
"content": "OpenEats2 is an open source recipe management site. You can share recipes with friends, rate recipes, store your favorite recipes to find easily, and more. If you want to run your own personal OpenEats site visit the <a href=\"https://github.com/ryannoelk/OpenEats\" target=\"_blank\">Github page</a>",
"image": "",
"pub_date": "2010-10-10 13:32:47",
"pub_date": "2010-10-10 13:32:47+00:00",
"slug": "default-news-entry",
"title": "OpenEats 2",
"frontpage": 1
Expand Down
Loading

0 comments on commit 7b7cb6d

Please sign in to comment.