Compare commits

...

2 Commits

1
.gitignore vendored

@ -13,6 +13,7 @@ __pycache__/
# Distribution / packaging # Distribution / packaging
.Python .Python
env/ env/
*_env/
venv/ venv/
pypyenv/ pypyenv/
build/ build/

@ -1,5 +0,0 @@
black
flake8
pytest
pytest-cov
pip-tools

@ -4,6 +4,15 @@ A websocket service for fo use in the gears application.
# Development # Development
It is helpful to have a clean virtualenv:
```
python -m venv gears_sockets_env
source gears_sockets_env/bin/activate
```
DO NOT add this to version control
# Testing # Testing

@ -2,12 +2,16 @@
API for the gears application API for the gears application
## First time setup ## Development
Create a virtual environment and activate it. Now from the root project directory run `./scripts/bootstrap`. This will install `pip-tools` and sync any dependencies for the first time. It is helpful to have a clean virtualenv:
To run the app you will need a [postgres] database. Create a development and a test database. Update the connection strings within the `gears_api.settings.toml`. At this time, if you choose to, you can remove the demo `Todo` code and replace it with your own Model. Otherwise create your first [alembic] migration using the `alembic revision --autogenerate -m "your revision message"` command. Finally, apply your first migration with `alembic upgrade head`. ```
python -m venv gears_api_env
source gears_sockets_env/bin/activate
```
To run the app you will need a [postgres] database. Create a development and a test database. Update the connection strings within the `gears_api.settings.toml`. At this time, if you choose to, you can remove the demo `Todo` code and replace it with your own Model. Otherwise create your first [alembic] migration using the `alembic revision --autogenerate -m "your revision message"` command. Finally, apply your first migration with `alembic upgrade head`.
## Running the developement server ## Running the developement server
A `manage.py` script has been included with a collection of [click] cli functions to assist in development. A `manage.py` script has been included with a collection of [click] cli functions to assist in development.

@ -8,25 +8,30 @@
appdirs==1.4.3 # via black appdirs==1.4.3 # via black
atomicwrites==1.3.0 # via pytest atomicwrites==1.3.0 # via pytest
attrs==19.1.0 # via black, pytest attrs==19.3.0 # via black, pytest
black==19.3b0 black==19.10b0
bumpversion==0.5.3
click==7.0 # via black, pip-tools click==7.0 # via black, pip-tools
coverage==4.5.4 # via pytest-cov coverage==4.5.4 # via pytest-cov
entrypoints==0.3 # via flake8 entrypoints==0.3 # via flake8
flake8==3.7.8 flake8==3.7.9
importlib-metadata==0.23 # via pluggy, pytest importlib-metadata==0.23 # via pluggy, pytest
mccabe==0.6.1 # via flake8 mccabe==0.6.1 # via flake8
more-itertools==7.2.0 # via pytest, zipp more-itertools==7.2.0 # via pytest, zipp
packaging==19.2 # via pytest packaging==19.2 # via pytest
pip-tools==4.1.0 pathspec==0.6.0 # via black
pip-tools==4.2.0
pluggy==0.13.0 # via pytest pluggy==0.13.0 # via pytest
py==1.8.0 # via pytest py==1.8.0 # via pytest
pycodestyle==2.5.0 # via flake8 pycodestyle==2.5.0 # via flake8
pyflakes==2.1.1 # via flake8 pyflakes==2.1.1 # via flake8
pyparsing==2.4.2 # via packaging pyparsing==2.4.4 # via packaging
pytest-cov==2.7.1 pytest-cov==2.8.1
pytest==5.2.0 pytest==5.2.2
six==1.12.0 # via packaging, pip-tools regex==2019.11.1 # via black
six==1.13.0 # via packaging, pip-tools
toml==0.10.0 # via black toml==0.10.0 # via black
typed-ast==1.4.0 # via black
wcwidth==0.1.7 # via pytest wcwidth==0.1.7 # via pytest
werkzeug==0.16.0
zipp==0.6.0 # via importlib-metadata zipp==0.6.0 # via importlib-metadata

@ -1 +1 @@
from .views import welcome from .views import welcome

@ -17,7 +17,7 @@ def pg_utcnow(element, compiler, **kw):
return "TIMEZONE('utc', CURRENT_TIMESTAMP)" return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
@compiles(CreateColumn, 'postgresql') @compiles(CreateColumn, "postgresql")
def use_identity(element, compiler, **kw): def use_identity(element, compiler, **kw):
text = compiler.visit_create_column(element, **kw) text = compiler.visit_create_column(element, **kw)
text = text.replace("SERIAL", "INT GENERATED BY DEFAULT AS IDENTITY") text = text.replace("SERIAL", "INT GENERATED BY DEFAULT AS IDENTITY")

@ -3,7 +3,11 @@ from molten import App, Route, ResponseRendererMiddleware, Settings
from molten.http import HTTP_404, Request from molten.http import HTTP_404, Request
from molten.openapi import Metadata, OpenAPIHandler, OpenAPIUIHandler from molten.openapi import Metadata, OpenAPIHandler, OpenAPIUIHandler
from molten.settings import SettingsComponent from molten.settings import SettingsComponent
from molten.contrib.sqlalchemy import SQLAlchemyMiddleware, SQLAlchemyEngineComponent, SQLAlchemySessionComponent from molten.contrib.sqlalchemy import (
SQLAlchemyMiddleware,
SQLAlchemyEngineComponent,
SQLAlchemySessionComponent,
)
from wsgicors import CORS from wsgicors import CORS
from whitenoise import WhiteNoise from whitenoise import WhiteNoise
@ -15,9 +19,7 @@ from . import settings
get_schema = OpenAPIHandler( get_schema = OpenAPIHandler(
metadata=Metadata( metadata=Metadata(
title="gears_api", title="gears_api", description="API for the gears application", version="0.0.0"
description="API for the gears application",
version="0.0.0"
) )
) )
@ -73,7 +75,7 @@ def create_app(_components=None, _middleware=None, _routes=None, _renderers=None
components=_components or components, components=_components or components,
middleware=_middleware or middleware, middleware=_middleware or middleware,
routes=_routes or routes, routes=_routes or routes,
renderers=_renderers or renderers renderers=_renderers or renderers,
) )
wrapped_app = CORS(wrapped_app, **settings.strict_get("wsgicors")) wrapped_app = CORS(wrapped_app, **settings.strict_get("wsgicors"))
wrapped_app = WhiteNoise(wrapped_app, **settings.strict_get("whitenoise")) wrapped_app = WhiteNoise(wrapped_app, **settings.strict_get("whitenoise"))

@ -20,13 +20,12 @@ class BaseManager(metaclass=ABCMeta):
"""Converts a SQLAlchemy results proxy into a Schema instance""" """Converts a SQLAlchemy results proxy into a Schema instance"""
pass pass
def raise_409(self, id:int): def raise_409(self, id: int):
"""Raises a 409 HTTP error response in the event of Conflict""" """Raises a 409 HTTP error response in the event of Conflict"""
raise HTTPError( raise HTTPError(
HTTP_409, HTTP_409,
{ {
"status": 409, "status": 409,
"message": f"Entity {self.__class__.__name__} with id: {id} already exists" "message": f"Entity {self.__class__.__name__} with id: {id} already exists",
} },
) )

@ -47,7 +47,9 @@ def initdb():
""" """
Initialize database Initialize database
""" """
click.echo("This feature has been commented out. Please use alembic to manage your database initialization and changes.") click.echo(
"This feature has been commented out. Please use alembic to manage your database initialization and changes."
)
# from gears_api.db import Base # from gears_api.db import Base
# #
# def _init(engine_data: EngineData): # def _init(engine_data: EngineData):

@ -1,7 +1,9 @@
"""isort:skip_file """isort:skip_file
""" """
import os import os
import sys; sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) # noqa import sys
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) # noqa
from alembic import context from alembic import context
from gears_api.index import create_app from gears_api.index import create_app
@ -18,4 +20,4 @@ def run_migrations_online(engine_data: EngineData):
context.run_migrations() context.run_migrations()
app.injector.get_resolver().resolve(run_migrations_online)() app.injector.get_resolver().resolve(run_migrations_online)()

@ -10,7 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = 'dc04b852fef5' revision = "dc04b852fef5"
down_revision = None down_revision = None
branch_labels = None branch_labels = None
depends_on = None depends_on = None

@ -2,23 +2,31 @@
# This file is autogenerated by pip-compile # This file is autogenerated by pip-compile
# To update, run: # To update, run:
# #
# pip-compile gears_api/requirements.in # pip-compile requirements.in
# #
--trusted-host pypi.python.org --trusted-host pypi.python.org
alembic==1.2.1 alembic==1.3.0
authlib==0.12.1 # via molten-jwt
cffi==1.13.2 # via cryptography
click==7.0 click==7.0
gunicorn==19.9.0 cryptography==2.8 # via authlib
gunicorn==20.0.0
mako==1.1.0 # via alembic mako==1.1.0 # via alembic
markupsafe==1.1.1 # via mako markupsafe==1.1.1 # via mako
molten-jwt==0.3.0
molten==0.7.4 molten==0.7.4
mypy-extensions==0.4.2 # via typing-inspect mypy-extensions==0.4.3 # via typing-inspect
psycopg2-binary==2.8.3 psycopg2-binary==2.8.4
python-dateutil==2.8.0 # via alembic pycparser==2.19 # via cffi
python-dateutil==2.8.1 # via alembic
python-editor==1.0.4 # via alembic python-editor==1.0.4 # via alembic
six==1.12.0 # via python-dateutil six==1.13.0 # via cryptography, python-dateutil
sqlalchemy==1.3.9 sqlalchemy==1.3.10
typing-extensions==3.7.4 # via molten typing-extensions==3.7.4.1 # via molten
typing-inspect==0.3.1 # via molten typing-inspect==0.3.1 # via molten
whitenoise==4.1.4 whitenoise==4.1.4
wsgicors==0.7.0 wsgicors==0.7.0
# The following packages are considered to be unsafe in a requirements file:
# setuptools==41.6.0 # via gunicorn

@ -0,0 +1,10 @@
[bumpversion]
current_version = 0.1.0
[bumpversion:file:setup.py]
search = version='{current_version}'
replace = {new_version}
[bumpversion:file:gears_api/__init__.py]
search = __version__ = '{current_version}'
replace = {new_version}

@ -7,12 +7,14 @@ from gears_api.index import create_app
def truncate_all_tables(session: Session): def truncate_all_tables(session: Session):
table_names = session.execute(""" table_names = session.execute(
"""
select table_name from information_schema.tables select table_name from information_schema.tables
where table_schema = 'public' where table_schema = 'public'
and table_type = 'BASE TABLE' and table_type = 'BASE TABLE'
and table_name != 'alembic_version' and table_name != 'alembic_version'
""") """
)
for (table_name,) in table_names: for (table_name,) in table_names:
# "truncate" can deadlock so we use delete which is guaranteed not to. # "truncate" can deadlock so we use delete which is guaranteed not to.
session.execute(f"delete from {table_name}") session.execute(f"delete from {table_name}")
@ -47,5 +49,7 @@ def load_component(app):
def load(annotation): def load(annotation):
def loader(c: annotation): def loader(c: annotation):
return c return c
return app.injector.get_resolver().resolve(loader)() return app.injector.get_resolver().resolve(loader)()
return load return load

@ -16,8 +16,8 @@ def test_insert_todo(client):
response = client.post("/todos", data=payload) response = client.post("/todos", data=payload)
content = response.json() content = response.json()
assert response.status_code == 201 assert response.status_code == 201
assert type(content['id']) == int assert type(content["id"]) == int
assert content['todo'] == payload['todo'] assert content["todo"] == payload["todo"]
def test_get_individual_todo_by_href(client): def test_get_individual_todo_by_href(client):
@ -34,7 +34,9 @@ def test_update_todo(client):
payload = {"todo": "sample app"} payload = {"todo": "sample app"}
response = client.post("/todos", json=payload) response = client.post("/todos", json=payload)
todo = response.json() todo = response.json()
update_response = client.patch("{}".format(todo.get("href")), json={"complete": True, "todo": "sample app"}) update_response = client.patch(
"{}".format(todo.get("href")), json={"complete": True, "todo": "sample app"}
)
updated_todo = update_response.json() updated_todo = update_response.json()
assert updated_todo["complete"] == True assert updated_todo["complete"] == True

@ -11,5 +11,5 @@ def test_extended_encoder_date_parsing():
def test_extended_encoder_decimal_casting(): def test_extended_encoder_decimal_casting():
json_renderer = ExtJSONRenderer() json_renderer = ExtJSONRenderer()
test_decimal = Decimal('1.0') test_decimal = Decimal("1.0")
assert 1.0 == json_renderer.default(test_decimal) assert 1.0 == json_renderer.default(test_decimal)

Loading…
Cancel
Save