Skip to content
This repository has been archived by the owner on Mar 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #65 from lendingblock/master
Browse files Browse the repository at this point in the history
0.5.0
  • Loading branch information
lsbardel authored Aug 1, 2018
2 parents 6680b1b + 97c1ecf commit 1c10780
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 26 deletions.
2 changes: 1 addition & 1 deletion openapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Minimal OpenAPI asynchronous server application
"""

__version__ = '0.4.6'
__version__ = '0.5.0'
52 changes: 31 additions & 21 deletions openapi/db/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,18 @@ async def ensure_connection(self, conn):
yield conn

async def get_list(
self, query=None, table=None, query_schema='query_schema',
dump_schema='response_schema', conn=None
self, *, filters=None, query=None, table=None,
query_schema='query_schema', dump_schema='response_schema',
conn=None
):
"""Get a list of models
"""
table = table if table is not None else self.db_table
params = self.get_filters(query=query, query_schema=query_schema)
limit = params.pop('limit', DEF_PAGINATION_LIMIT)
offset = params.pop('offset', 0)
query = self.get_query(table.select(), params, table=table)
if not filters:
filters = self.get_filters(query=query, query_schema=query_schema)
limit = filters.pop('limit', DEF_PAGINATION_LIMIT)
offset = filters.pop('offset', 0)
query = self.get_query(table.select(), filters, table=table)

# pagination
query = query.offset(offset)
Expand All @@ -58,11 +60,10 @@ async def get_list(
sql, args = compile_query(query)
async with self.ensure_connection(conn) as conn:
values = await conn.fetch(sql, *args)

return self.dump(dump_schema, values)

async def create_one(
self, data=None, table=None, body_schema='body_schema',
self, *, data=None, table=None, body_schema='body_schema',
dump_schema='response_schema', conn=None
):
"""Create a model
Expand All @@ -83,7 +84,9 @@ async def create_one(
data = ((c.name, v) for c, v in zip(table.columns, values[0]))
return self.dump(dump_schema, data)

async def create_list(self, data=None, conn=None):
async def create_list(
self, *, data=None, dump_schema='response_schema', conn=None
):
"""Create multiple models
"""
if data is None:
Expand All @@ -103,16 +106,18 @@ async def create_list(self, data=None, conn=None):
((c.name, v) for c, v in zip(cols, value))
for value in values
]
return self.dump('response_schema', result)
return self.dump(dump_schema, result)

async def get_one(
self, query=None, table=None, query_schema='query_schema',
self, *, filters=None, query=None, table=None,
query_schema='query_schema',
dump_schema='response_schema', conn=None
):
"""Get a single model
"""
table = table if table is not None else self.db_table
filters = self.get_filters(query=query, query_schema=query_schema)
if not filters:
filters = self.get_filters(query=query, query_schema=query_schema)
query = self.get_query(table.select(), filters, table=table)
sql, args = compile_query(query)

Expand All @@ -123,31 +128,36 @@ async def get_one(
raise web.HTTPNotFound()
return self.dump(dump_schema, values[0])

async def update_one(self, data=None, conn=None):
async def update_one(
self, *, data=None, filters=None, table=None,
dump_schema='response_schema', conn=None
):
"""Update a single model
"""
table = table if table is not None else self.db_table
if data is None:
data = self.cleaned('body_schema', await self.json_data(), False)
filters = self.cleaned('path_schema', self.request.match_info)
if not filters:
filters = self.cleaned('path_schema', self.request.match_info)
update = self.get_query(
self.db_table.update(), filters
).values(**data).returning(*self.db_table.columns)
table.update(), filters
).values(**data).returning(*table.columns)
sql, args = compile_query(update)

async with self.ensure_connection(conn) as conn:
try:
values = await conn.fetch(sql, *args)
except UniqueViolationError as exc:
self.handle_unique_violation(exc)

if not values:
raise web.HTTPNotFound()
return self.dump('response_schema', values[0])
return self.dump(dump_schema, values[0])

async def delete_one(self, conn=None):
async def delete_one(self, *, filters=None, conn=None):
"""delete a single model
"""
filters = self.cleaned('path_schema', self.request.match_info)
if not filters:
filters = self.cleaned('path_schema', self.request.match_info)
delete = self.get_query(self.db_table.delete(), filters)
sql, args = compile_query(delete.returning(*self.db_table.columns))

Expand Down Expand Up @@ -258,7 +268,7 @@ def default_filter_field(self, field, op, value):

def handle_unique_violation(self, exception):
match = re.match(unique_regex, exception.detail)
if not match:
if not match: # pragma: no cover
raise exception

column = match.group('column')
Expand Down
8 changes: 6 additions & 2 deletions openapi/spec/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,26 @@ def get_filters(self, *, query=None, query_schema='query_schema'):
params.update(path)
return params

def cleaned(self, schema, data, strict=True):
def cleaned(self, schema, data, strict=True, Error=None):
"""Clean data for a given schema name
"""
Schema = self.get_schema(schema)
if isinstance(Schema, list):
Schema = Schema[0]
validated = validate(Schema, data, strict)
if validated.errors:
if schema == 'path_schema':
if Error:
raise Error()
elif schema == 'path_schema':
raise web.HTTPNotFound()
self.raiseValidationError(errors=validated.errors)
return validated.data

def dump(self, schema, data):
"""Dump data using a given schema
"""
if schema is None:
return data
Schema = self.get_schema(schema)
if isinstance(Schema, list):
Schema = Schema[0]
Expand Down
55 changes: 54 additions & 1 deletion tests/example/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
from openapi.db.path import SqlApiPath
from openapi.spec import op
from openapi.exc import JsonHttpException
from .models import Task, TaskAdd, TaskQuery, TaskPathSchema, TaskUpdate
from .models import (
Task, TaskAdd, TaskQuery, TaskPathSchema, TaskUpdate, TaskPathSchema2
)


routes = web.RouteTableDef()
Expand Down Expand Up @@ -281,3 +283,54 @@ async def post(self):
async with conn.transaction():
data = await self.create_list(conn=conn)
return self.json_response(data, status=201)


@routes.view('/tasks2/{task_id}')
class TaskPath2(SqlApiPath):
"""
---
tags:
- name: task
"""
table = 'tasks'
path_schema = TaskPathSchema2

def get_filters(self):
filters = super().get_filters()
return {'id': filters['task_id']}

@op(response_schema=Task)
async def get(self):
"""
---
summary: get an existing Task by ID
responses:
200:
description: the task
"""
data = await self.get_one(filters=self.get_filters())
return self.json_response(data)

@op(response_schema=Task, body_schema=TaskUpdate)
async def patch(self):
"""
---
summary: update an existing Task by ID
responses:
200:
description: the updated task
"""
data = await self.update_one(filters=self.get_filters())
return self.json_response(data)

@op()
async def delete(self):
"""
---
summary: Delete an existing task
responses:
204:
description: Task successfully deleted
"""
await self.delete_one(filters=self.get_filters())
return web.Response(status=204)
5 changes: 5 additions & 0 deletions tests/example/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ class TaskUpdate(TaskAdd):
@dataclass
class TaskPathSchema:
id: int = data_field(required=True)


@dataclass
class TaskPathSchema2:
task_id: int = data_field(required=True)
27 changes: 27 additions & 0 deletions tests/test_db_path_extra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from datetime import datetime
from openapi.testing import jsonBody


async def test_get_update(cli):
response = await cli.post('/tasks', json=dict(title='test task2 2'))
data = await jsonBody(response, 201)
id_ = data['id']
assert data['title'] == 'test task2 2'
#
# now get it
response = await cli.get(f'/tasks2/{id_}')
data = await jsonBody(response, 200)
assert data['title'] == 'test task2 2'
#
# now update
response = await cli.patch(
f'/tasks2/{id_}', json=dict(done=datetime.now().isoformat())
)
data = await jsonBody(response, 200)
assert data['id'] == id_
#
# now delete it
response = await cli.delete(f'/tasks2/{id_}')
assert response.status == 204
response = await cli.delete(f'/tasks2/{id_}')
await jsonBody(response, 404)
14 changes: 13 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import pytest

from openapi import utils
from openapi.exc import JsonHttpException
from openapi.exc import JsonHttpException, ImproperlyConfigured
from openapi.json import dumps
from openapi.db import utils as db


def test_env():
Expand All @@ -23,3 +26,12 @@ def test_json_http_exception_reason():
assert ex.status == 422
assert ex.text == dumps({'message': 'non lo so'})
assert ex.headers['content-type'] == 'application/json; charset=utf-8'


def test_exist_database_none():
assert db.exist_database({}, '') is False
assert db.drop_database({}, '') is False
with pytest.raises(ImproperlyConfigured):
db.create_tables({})
with pytest.raises(ImproperlyConfigured):
db.create_database({}, 'foo')

0 comments on commit 1c10780

Please sign in to comment.