leveled up, minus auth

deb
androiddrew 7 years ago
parent 7b23ad97d8
commit d0a9b556c6

@ -1,3 +1 @@
from cookie_api.renders import JSONRenderer
from cookie_api.app import application_factory
from cookie_api.models import Base
from cookie_api.app import application_factory

@ -1,61 +1,40 @@
import typing
from apistar import Include, Route, http, annotate
from apistar.backends import sqlalchemy_backend
from apistar.backends.sqlalchemy_backend import Session
from apistar.frameworks.wsgi import WSGIApp as App
from apistar.handlers import docs_urls, static_urls
from apistar.interfaces import Router, Injector, Auth
from apistar_jwt.authentication import JWTAuthentication
from apistar_alembic_migrations import commands as migrate_commands
from apistar_mail import mail_component
from apistar import Route, http, App
from apistar_jwt import JWT
import logbook
from sqlalchemy import create_engine
from cookie_api.auth import auth_routes, auth_components
from cookie_api.commands import commands
from cookie_api.logger import global_init
from cookie_api.models import Cookie
#from cookie_api.schema import CookieSchema
from cookie_api.schema import CookieSchema
from cookie_api import logging
from cookie_api.util import SQLAlchemyHook, SQLAlchemySession, Session, ExtJSONResponse
cookie_schema = CookieSchema()
engine = create_engine('postgresql://apistar@localhost:5432/apistar')
logger = logbook.Logger('Cookies')
@annotate(authentication=[JWTAuthentication()])
def get_state(injector: Injector, auth: Auth):
state = injector.state
d = dict()
for k, v in state.items():
d[k] = str(v)
return d
def get_cookies(session: Session) -> typing.List[CookieSchema]:
logger.info("Accessing the Cookies resource")
cookies = session.query(Cookie).all()
return [CookieSchema(cookie) for cookie in cookies]
return ExtJSONResponse([CookieSchema(cookie) for cookie in cookies], 200)
def get_cookie(session: Session, id) -> CookieSchema:
cookie = session.query(Cookie).filter_by(id=id).one_or_none()
if cookie is None:
logger.warn("Someone keeps requesting bad cookie locations")
msg = {"error": "404 Not Found"}
return http.Response(msg, status=404)
return CookieSchema(cookie)
return ExtJSONResponse(msg, 404)
return ExtJSONResponse(CookieSchema(cookie), 200)
def create_cookie(session: Session, json_data: http.RequestData, route: Router):
cookie_data = cookie_schema.load(json_data)
def create_cookie(session: Session, cookie_data: CookieSchema, app: App):
cookie = Cookie(**cookie_data)
session.add(cookie)
session.commit()
headers = {'Location': route.reverse_url('get_cookie', dict(id=cookie.id))}
return http.Response(cookie_schema.dump(cookie), status=201, headers=headers)
headers = {'Location': app.reverse_url('get_cookie', id=cookie.id)}
return ExtJSONResponse(CookieSchema(cookie), 201, headers=headers)
def delete_cookie(session: Session, id: int):
@ -63,17 +42,18 @@ def delete_cookie(session: Session, id: int):
if cookie is None:
msg = {"error": "404 Not Found"}
return http.Response(msg, status=404)
logger.debug("Deleting cookie {} {}".format(cookie.id, cookie.name))
session.delete(cookie)
return {"message": "200 OK"}
session.commit()
return ExtJSONResponse({}, 204)
_routes = [
Route('/state', 'GET', get_state),
Route('/cookies', 'GET', get_cookies),
Route('/cookies', 'POST', create_cookie),
Route('/cookies/{id}', 'GET', get_cookie),
Include('/docs', docs_urls),
Include('/static', static_urls)
Route('/cookies/{id}', 'DELETE', delete_cookie)
]
app_settings = {
@ -82,21 +62,24 @@ app_settings = {
}
}
_routes = _routes + auth_routes
_routes = _routes # + auth_routes
_commands = sqlalchemy_backend.commands + commands + migrate_commands
_hooks = [SQLAlchemyHook]
_components = sqlalchemy_backend.components + auth_components + [mail_component]
_components = [
SQLAlchemySession(engine=engine),
JWT({
'JWT_SECRET': 'thisisasecret',
}),
]
def application_factory(settings={}, routes=_routes, commands=_commands, components=_components):
def application_factory(settings={}, routes=_routes, components=_components, hooks=_hooks):
"""Returns an instance of Cookie API"""
_settings = {**app_settings, **settings}
logging.global_init(_settings)
global_init(_settings)
return App(settings=_settings,
commands=commands,
components=components,
return App(components=components,
event_hooks=hooks,
routes=routes)

@ -1,20 +1,15 @@
"""
import datetime as dt
from apistar import Component, Settings, http, Route, Include, annotate
from apistar.interfaces import Auth
from apistar.backends.sqlalchemy_backend import Session
from apistar_jwt.authentication import get_jwt, JWTAuthentication
from apistar import Component, http, Route, Include
from apistar_jwt.token import JWT
from apistar_mail import Message, Mail
# from apistar_mail import Message, Mail
from sqlalchemy.exc import IntegrityError, InvalidRequestError
from sqlalchemy.orm.exc import NoResultFound
from cookie_api.models import User
from cookie_api.schema import UserSchema, UserCreateSchema
auth_components = [
Component(JWT, init=get_jwt)
]
from cookie_api.schema import
def login(settings: Settings, json_data: http.RequestData, session: Session):
@ -110,3 +105,4 @@ routes = [
]
auth_routes = [Include('/auth', routes)]
"""

@ -1,16 +0,0 @@
from apistar import Command
from apistar.backends.sqlalchemy_backend import Session
from cookie_api.models import User
def create_user(session: Session, email, password):
user = User(email, password)
session.add(user)
session.commit()
print('User added')
commands = [
Command('create_user', create_user)
]

@ -13,4 +13,3 @@ def global_init(settings={}):
else:
logbook.StreamHandler(sys.stdout, level=_level).push_application()

@ -1,25 +0,0 @@
import datetime as dt
import decimal
import json
from apistar import http
from apistar.renderers import Renderer
def extended_encoder(obj):
"""JSON encoder function with support for ISO 8601 datetime serialization and Decimal to float casting"""
if isinstance(obj, dt.datetime):
return obj.isoformat()
elif isinstance(obj, decimal.Decimal):
return float(obj)
class JSONRenderer(Renderer):
"""JSON Render with support for ISO 8601 datetime serialization and Decimal to float casting"""
media_type = 'application/json'
charset = None
def render(self, data: http.ResponseData) -> bytes:
return json.dumps(data, default=extended_encoder).encode('utf-8')
# TODO add an XML render

@ -1,48 +1,13 @@
from apistar import typesystem
# from marshmallow import Schema, fields
from cookie_api.types import Datetime, Decimal
# class CookieSchema(Schema):
# id = fields.Int()
# created_date = fields.DateTime()
# modified_date = fields.DateTime()
# name = fields.Str(required=True)
# recipe_url = fields.Str()
# sku = fields.Str(required=True)
# qoh = fields.Int(required=True)
# unit_cost = fields.Decimal(required=True)
class UserSchema(typesystem.Object):
description = 'A User respresentation'
properties = {
'id': typesystem.integer(),
'created_date': Datetime,
'modified_date': Datetime,
'email': typesystem.string(max_length=255),
'confirmed': typesystem.boolean(),
'admin': typesystem.boolean()
}
class UserCreateSchema(typesystem.Object):
description = 'A User respresentation for creating a user'
properties = {
'email': typesystem.string(max_length=255),
'password': typesystem.string(max_length=255)
}
class CookieSchema(typesystem.Object):
properties = {
'id': typesystem.integer(),
'created_date': Datetime,
'modified_date': Datetime,
'name': typesystem.string(),
'recipe_url': typesystem.string(),
'sku': typesystem.string(),
'qoh': typesystem.integer(),
'unit_cost': Decimal
}
from apistar import types, validators
from cookie_api.util import Decimal
class CookieSchema(types.Type):
id = validators.Integer(allow_null=True)
created_date = validators.DateTime(allow_null=True)
modified_date = validators.DateTime(allow_null=True)
name = validators.String(max_length=50)
recipe_url = validators.String(max_length=255)
sku = validators.String(max_length=55)
qoh = validators.Integer(minimum=0)
unit_cost = Decimal(minimum=0.0)

@ -1,25 +0,0 @@
import datetime
import decimal
import dateutil
from apistar import typesystem
class Datetime(datetime.datetime):
native_type = datetime.datetime
def __new__(cls, *args, **kwargs) -> datetime:
if args and isinstance(args[0], cls.native_type):
return args[0]
if args and isinstance(args[0], str):
try:
return dateutil.parser.parse(args[0])
except ValueError:
raise typesystem.TypeSystemError(cls=cls, code='type') from None
return cls.native_type(*args, **kwargs)
class Decimal(typesystem._NumericType, decimal.Decimal):
native_type = decimal.Decimal

@ -0,0 +1,106 @@
import datetime as dt
import decimal
from math import isfinite
import typing
from apistar import types, validators
from apistar.http import JSONResponse, Response
from apistar.server.components import Component
from sqlalchemy.engine import Engine
from sqlalchemy.orm import sessionmaker, Session, scoped_session
class Decimal(validators.NumericType):
numeric_type = decimal.Decimal
def validate(self, value, definitions=None, allow_coerce=False):
if value is None and self.allow_null:
return None
elif value is None:
self.error('null')
elif isinstance(value, bool):
self.error('type')
elif self.numeric_type is int and isinstance(value, float) and not value.is_integer():
self.error('integer')
elif not isinstance(value, (int, float, decimal.Decimal)) and not allow_coerce:
self.error('type')
elif isinstance(value, float) and not isfinite(value):
self.error('finite')
try:
value = self.numeric_type(value)
except (TypeError, ValueError):
self.error('type')
if self.enum is not None:
if value not in self.enum:
if len(self.enum) == 1:
self.error('exact')
self.error('enum')
if self.minimum is not None:
if self.exclusive_minimum:
if value <= self.minimum:
self.error('exclusive_minimum')
else:
if value < self.minimum:
self.error('minimum')
if self.maximum is not None:
if self.exclusive_maximum:
if value >= self.maximum:
self.error('exclusive_maximum')
else:
if value > self.maximum:
self.error('maximum')
if self.multiple_of is not None:
if isinstance(self.multiple_of, float):
if not (value * (1 / self.multiple_of)).is_integer():
self.error('multiple_of')
else:
if value % self.multiple_of:
self.error('multiple_of')
return value
class ExtJSONResponse(JSONResponse):
"""JSON Response with support for ISO 8601 datetime serialization and Decimal to float casting"""
def default(self, obj: typing.Any) -> typing.Any:
if isinstance(obj, types.Type):
return dict(obj)
if isinstance(obj, dt.datetime):
return obj.isoformat()
elif isinstance(obj, decimal.Decimal):
return float(obj)
error = "Object of type '%s' is not JSON serializable."
return TypeError(error % type(obj).__name_)
DBSession = scoped_session(sessionmaker())
class SQLAlchemySession(Component):
def __init__(self, engine=None):
if not isinstance(engine, Engine):
raise ValueError('SQLAlchemySession must be instantiated with a sqlalchemy.engine.Engine object')
self.engine = engine
DBSession.configure(bind=self.engine)
def resolve(self) -> Session:
return DBSession()
class SQLAlchemyHook:
def on_request(self, session: Session):
return
def on_response(self, session: Session, response: Response):
DBSession.remove()
return response
def on_error(self, session: Session, response: Response):
session.rollback()
DBSession.remove()
return response

@ -1,66 +1,23 @@
alembic==0.9.6
apistar==0.3.9
apistar-alembic-migrations==0.0.6
apistar-jwt==0.2.1
alembic==0.9.9
apistar==0.5.10
apistar-jwt==0.4.2
bcrypt==3.1.4
certifi==2017.7.27.1
cffi==1.11.2
certifi==2018.4.16
cffi==1.11.5
chardet==3.0.4
colorama==0.3.9
coreapi==2.3.3
coreschema==0.0.4
idna==2.6
itypes==1.1.0
Jinja2==2.9.6
Logbook==1.1.0
Jinja2==2.10
Logbook==1.3.3
Mako==1.0.7
MarkupSafe==1.0
marshmallow==2.15.0
psycopg2==2.7.3.1
py==1.4.34
psycopg2==2.7.4
pycparser==2.18
PyJWT==1.5.3
pytest==3.2.3
python-dateutil==2.6.1
PyJWT==1.6.1
python-dateutil==2.7.2
python-editor==1.0.3
requests==2.18.4
six==1.11.0
SQLAlchemy==1.1.14
uritemplate==3.0.0
SQLAlchemy==1.2.7
urllib3==1.22
Werkzeug==0.12.2
whitenoise==3.3.1
alembic==0.9.6
apistar==0.3.9
apistar-alembic-migrations==0.0.6
apistar-jwt==0.2.1
apistar-mail==0.2.0
bcrypt==3.1.4
certifi==2017.7.27.1
cffi==1.11.2
chardet==3.0.4
colorama==0.3.9
coreapi==2.3.3
coreschema==0.0.4
idna==2.6
itsdangerous==0.24
itypes==1.1.0
Jinja2==2.9.6
Logbook==1.1.0
Mako==1.0.7
MarkupSafe==1.0
marshmallow==2.15.0
psycopg2==2.7.3.1
py==1.4.34
pycparser==2.18
PyJWT==1.5.3
pytest==3.2.3
python-dateutil==2.6.1
python-editor==1.0.3
requests==2.18.4
six==1.11.0
SQLAlchemy==1.1.14
uritemplate==3.0.0
urllib3==1.22
Werkzeug==0.12.2
Werkzeug==0.14.1
whitenoise==3.3.1

@ -3,12 +3,13 @@ try:
except ImportError:
# Fall back to psycopg2cffi
from psycopg2cffi import compat
compat.register()
from cookie_api import application_factory
from config import settings
app = application_factory(settings)
app = application_factory()
if __name__ == "__main__":
app.main()
if __name__ == '__main__':
app = application_factory()
app.serve('127.0.0.1', 5000, debug=True)

Loading…
Cancel
Save