-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b1a3975
Showing
33 changed files
with
460 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea | ||
venv | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
## api | ||
|
||
### Project Setup | ||
|
||
1. `pip install -r requirements.txt` | ||
2. `chmod +x unit-tests.sh` | ||
3. `./unit-tests.sh` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import click | ||
from werkzeug.middleware.proxy_fix import ProxyFix | ||
|
||
from .factory import Factory | ||
|
||
|
||
def create_app(environment='development'): | ||
f = Factory(environment) | ||
f.set_flask() | ||
f.set_db() | ||
f.set_migration() | ||
f.set_api() | ||
|
||
# from models import Example | ||
|
||
app = f.flask | ||
|
||
from .views import sample_page | ||
|
||
app.register_blueprint(sample_page, url_prefix='/views') | ||
|
||
if app.config['TESTING']: # pragma: no cover | ||
# Setup app for testing | ||
@app.before_first_request | ||
def initialize_app(): | ||
pass | ||
|
||
@app.after_request | ||
def after_request(response): | ||
response.headers.add('Access-Control-Allow-Origin', '*') | ||
response.headers.add('Access-Control-Allow-Headers', | ||
'Content-Type,Authorization') | ||
response.headers.add('Access-Control-Allow-Methods', | ||
'GET,PUT,POST,DELETE') | ||
|
||
return response | ||
|
||
app.wsgi_app = ProxyFix(app.wsgi_app) | ||
|
||
@app.cli.command() | ||
@click.argument('command') | ||
def setup(command): | ||
pass | ||
|
||
return app | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import os | ||
|
||
|
||
class Config: | ||
ERROR_404_HELP = False | ||
|
||
SECRET_KEY = os.getenv('APP_SECRET', 'secret key') | ||
|
||
SQLALCHEMY_DATABASE_URI = 'sqlite:///tutorial.db' | ||
SQLALCHEMY_TRACK_MODIFICATIONS = False | ||
|
||
DOC_USERNAME = 'api' | ||
DOC_PASSWORD = 'password' | ||
|
||
|
||
class DevConfig(Config): | ||
DEBUG = True | ||
|
||
|
||
class TestConfig(Config): | ||
SQLALCHEMY_DATABASE_URI = 'sqlite://' | ||
TESTING = True | ||
DEBUG = True | ||
|
||
|
||
class ProdConfig(Config): | ||
DEBUG = False | ||
|
||
|
||
config = { | ||
'development': DevConfig, | ||
'testing': TestConfig, | ||
'production': ProdConfig | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import logging | ||
import os | ||
import sys | ||
from logging.handlers import RotatingFileHandler | ||
|
||
from flask import Flask | ||
|
||
from .config import config | ||
|
||
|
||
class Factory: | ||
|
||
def __init__(self, environment='development'): | ||
self._environment = os.environ.get('APP_ENVIRONMENT', environment) | ||
self.flask = None | ||
|
||
@property | ||
def environment(self): | ||
return self._environment | ||
|
||
@environment.setter | ||
def environment(self, env): | ||
self._environment = env | ||
|
||
def set_flask(self, **kwargs): | ||
self.flask = Flask(__name__, **kwargs) | ||
self.flask.config.from_object(config[self._environment]) | ||
# setup logging | ||
file_handler = RotatingFileHandler('api.log', maxBytes=10000, backupCount=1) | ||
file_handler.setLevel(logging.INFO) | ||
self.flask.logger.addHandler(file_handler) | ||
stdout = logging.StreamHandler(sys.stdout) | ||
stdout.setLevel(logging.DEBUG) | ||
self.flask.logger.addHandler(stdout) | ||
|
||
return self.flask | ||
|
||
def set_db(self): | ||
from .models.database import db | ||
db.init_app(self.flask) | ||
|
||
def set_migration(self): | ||
from .models.database import db, migrate | ||
migrate.init_app(self.flask, db) | ||
|
||
def set_api(self): | ||
from .resources import api | ||
api.init_app(self.flask, version='1.0.0', title='api') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .base import Example |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from sqlalchemy import Column, DateTime, func, Integer, String | ||
|
||
from .database import db | ||
|
||
|
||
class Base(db.Model): | ||
__abstract__ = True | ||
|
||
id = Column(Integer, primary_key=True) | ||
created_at = Column(DateTime, default=func.now()) | ||
updated_at = Column(DateTime, onupdate=func.now()) | ||
|
||
|
||
class Example(Base): | ||
name = Column(String) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from flask_migrate import Migrate | ||
from flask_sqlalchemy import SQLAlchemy | ||
|
||
db = SQLAlchemy() | ||
migrate = Migrate() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from abc import ABC | ||
|
||
from .base import Example | ||
|
||
|
||
class Datastore(ABC): | ||
|
||
def __init__(self, _db=None): | ||
self.session = None | ||
if _db: | ||
self.session = _db.session | ||
|
||
|
||
class ExampleDatastore(Datastore): | ||
|
||
def __init__(self, _db): | ||
super().__init__(_db) | ||
|
||
def create_example(self, name): | ||
ex = Example(name=name) | ||
self.session.add(ex) | ||
self.session.commit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from .example import ns as example_ns | ||
from ..utils import PatchedApi | ||
|
||
api = PatchedApi() | ||
|
||
api.add_namespace(example_ns) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from flask_restplus import Namespace, Resource, fields | ||
|
||
ns = Namespace('example', description='Examples') | ||
|
||
success_model = ns.model('Success', { | ||
'message': fields.String | ||
}) | ||
|
||
|
||
@ns.route('', endpoint='index') | ||
class IndexPage(Resource): | ||
|
||
@ns.marshal_with(success_model) | ||
def get(self): | ||
""" | ||
Example url | ||
""" | ||
return {'message': 'Success'} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon.ico><title>web</title><link href=../static/css/app.e2713bb0.css rel=preload as=style><link href=../static/js/app.c249faaa.js rel=preload as=script><link href=../static/js/chunk-vendors.b10d6c99.js rel=preload as=script><link href=../static/css/app.e2713bb0.css rel=stylesheet></head><body><noscript><strong>We're sorry but web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=../static/js/chunk-vendors.b10d6c99.js></script><script src=../static/js/app.c249faaa.js></script></body></html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from functools import wraps | ||
|
||
from flask import url_for, current_app, request, Response, Blueprint | ||
from flask_restplus import Api | ||
|
||
|
||
def add_basic_auth(blueprint: Blueprint, username, password, realm='api'): | ||
""" | ||
Add HTTP Basic Auth to a blueprint. | ||
Note this is only for casual use! | ||
""" | ||
|
||
@blueprint.before_request | ||
def basic_http_auth(*args, **kwargs): | ||
auth = request.authorization | ||
if auth is None or auth.password != password or auth.username != username: | ||
return Response('Please login', 401, {'WWW-Authenticate': f'Basic realm="{realm}"'}) | ||
|
||
|
||
def check_auth(username, password): | ||
""" | ||
This function is called to check if a username / | ||
password combination is valid. | ||
""" | ||
return username == current_app.config['DOC_USERNAME'] and password == current_app.config['DOC_PASSWORD'] | ||
|
||
|
||
def authenticate(): | ||
""" | ||
Sends a 401 response that enables basic auth | ||
""" | ||
return Response('Not Authorized', 401, {'WWW-Authenticate': 'Basic realm="api"'}) | ||
|
||
|
||
def requires_auth(f): | ||
@wraps(f) | ||
def decorated(*args, **kwargs): | ||
auth = request.authorization | ||
if not auth or not check_auth(auth.username, auth.password): | ||
return authenticate() | ||
return f(*args, **kwargs) | ||
|
||
return decorated | ||
|
||
|
||
class PatchedApi(Api): | ||
|
||
@property | ||
def specs_url(self): | ||
"""Monkey patch for HTTPS""" | ||
scheme = 'https' if self.is_prod() else 'http' | ||
return url_for(self.endpoint('specs'), _external=True, _scheme=scheme) | ||
|
||
@staticmethod | ||
def is_prod(): | ||
return not current_app.config['TESTING'] | ||
|
||
def _register_apidoc(self, app): | ||
app.view_functions['doc'] = requires_auth(app.view_functions['doc']) | ||
return super()._register_apidoc(app) | ||
|
||
def handle_error(self, e): | ||
if current_app.config['TESTING'] or current_app.config['APP_ENVIRONMENT'] == 'development': | ||
print(e) | ||
super().handle_error(e) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from flask import Blueprint, render_template, abort | ||
from jinja2 import TemplateNotFound | ||
|
||
sample_page = Blueprint('sample_page', 'sample_page', template_folder='templates') | ||
|
||
|
||
@sample_page.route('/sample') | ||
def get_sample(): | ||
try: | ||
return render_template('index.html') | ||
except TemplateNotFound: | ||
abort(404) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[pytest] | ||
addopts = -p no:warnings | ||
env = | ||
APP_ENVIRONMENT=testing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
Flask==1.0.2 | ||
flask-restplus==0.12.1 | ||
Flask-SQLAlchemy==2.4.0 | ||
Flask-Migrate==2.4.0 | ||
psycopg2-binary==2.7.7 | ||
gunicorn==19.9.0 | ||
gevent==1.4.0 | ||
psycogreen==1.0.1 | ||
pytest==4.0.2 | ||
pytest-cov==2.6.1 | ||
pytest-mock==1.10.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import multiprocessing | ||
|
||
import gunicorn | ||
from gevent import monkey | ||
|
||
monkey.patch_all() | ||
|
||
bind = '0.0.0.0:8000' | ||
worker_class = 'gevent' | ||
workers = multiprocessing.cpu_count() * 2 + 1 | ||
loglevel = 'debug' | ||
keepalive = 10 | ||
timeout = 3600 | ||
preload_app = True | ||
|
||
gunicorn.SERVER_SOFTWARE = 'Microsoft-IIS/6.0' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from gevent import monkey | ||
|
||
monkey.patch_all(thread=False) | ||
|
||
import psycogreen.gevent | ||
|
||
psycogreen.gevent.patch_psycopg() | ||
|
||
|
||
import pytest | ||
from flask.testing import FlaskClient | ||
|
||
from app import create_app | ||
from .utils import JSONResponse | ||
|
||
|
||
@pytest.fixture(scope='module') | ||
def flask_app(): | ||
app = create_app(environment='testing') | ||
from app.models.database import db | ||
|
||
with app.app_context(): | ||
db.create_all() | ||
yield app | ||
db.session.close_all() | ||
db.drop_all() | ||
|
||
|
||
@pytest.fixture(scope='module') | ||
def client(flask_app): | ||
app = flask_app | ||
ctx = flask_app.test_request_context() | ||
ctx.push() | ||
app.test_client_class = FlaskClient | ||
app.response_class = JSONResponse | ||
return app.test_client() | ||
|
||
|
||
@pytest.fixture(scope='module') | ||
def db(flask_app): | ||
from app.models.database import db as db_instance | ||
yield db_instance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from flask.testing import FlaskClient | ||
|
||
from app.factory import Factory | ||
|
||
|
||
def test_factory_env_configuration(): | ||
factory_app = Factory('testing') | ||
assert factory_app.environment == 'testing' | ||
factory_app.set_flask() | ||
factory_app.environment = 'development' | ||
assert factory_app.environment == 'development' | ||
|
Oops, something went wrong.