-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from JoshYuJump/3.0-stable
Async Resource
- Loading branch information
Showing
22 changed files
with
492 additions
and
136 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
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
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 |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = '3.0.0-rc.1' | ||
__version__ = '3.0.0-rc.2' |
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,19 @@ | ||
from collections import defaultdict | ||
|
||
from sqlalchemy import case | ||
from sqlalchemy.ext.hybrid import Comparator | ||
from sqlalchemy.util.langhelpers import dictlike_iteritems | ||
|
||
|
||
class CaseComparator(Comparator): | ||
def __init__(self, whens, expression): | ||
super().__init__(expression) | ||
self.whens, self.reversed_whens = dictlike_iteritems(whens), defaultdict(list) | ||
for k, v in self.whens: | ||
self.reversed_whens[v].append(k) | ||
|
||
def __clause_element__(self): | ||
return case(self.whens, self.expression) | ||
|
||
def __eq__(self, other): | ||
return super().__clause_element__().in_(self.reversed_whens[other]) |
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
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
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
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
This file was deleted.
Oops, something went wrong.
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,183 @@ | ||
"""Generic Routes | ||
Generic routes include list/get/create/update/delete | ||
Used by `resource.RouterGenerator` | ||
# TODO: Optimized boilerplate code | ||
""" | ||
|
||
import inspect | ||
import logging | ||
import typing | ||
from typing import Callable | ||
|
||
from fastapi import Request | ||
|
||
from ..paginate import paginate | ||
from ..schemas import ListRequest | ||
|
||
|
||
# --- Utilities functions for route handler --- | ||
def pick_route(func, async_route, route): | ||
"""Pick coroutine or generic function for route""" | ||
return async_route if inspect.iscoroutinefunction(func) else route | ||
|
||
|
||
# noinspection PyUnresolvedReferences,PyProtectedMember | ||
def list_(router_generator) -> Callable: | ||
""" | ||
list action default using fastapi-pagination to process paginate | ||
""" | ||
resource = router_generator.cls() | ||
action_func = getattr(resource, 'list') | ||
|
||
# noinspection PyProtectedMember,PyShadowingNames,PyUnresolvedReferences | ||
def route(request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
params = request.query_params._dict | ||
|
||
# parse filters | ||
filters = {} | ||
for k, v in params.items(): | ||
if k not in router_generator._ordered_filters: | ||
continue | ||
# noinspection PyBroadException | ||
try: | ||
# Convert param and retrieve param | ||
params_converter = router_generator._ordered_filters.get(k) | ||
if isinstance(params_converter, typing._GenericAlias): | ||
params_converter = params_converter.__args__[0] | ||
filters[k] = params_converter(v) | ||
except Exception as ex: | ||
logging.warning( | ||
'Query params `%s`(value: %s) type convert failed, ' | ||
'exception: %s', k, v, ex | ||
) | ||
continue | ||
|
||
schema_in = ListRequest(**params, filters=filters) | ||
|
||
result = action_func(schema_in) | ||
return paginate(result) | ||
|
||
# noinspection PyProtectedMember,PyShadowingNames,PyUnresolvedReferences | ||
async def async_route(request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
params = request.query_params._dict | ||
|
||
# parse filters | ||
filters = {} | ||
for k, v in params.items(): | ||
if k not in router_generator._ordered_filters: | ||
continue | ||
# noinspection PyBroadException | ||
try: | ||
# Convert param and retrieve param | ||
params_converter = router_generator._ordered_filters.get(k) | ||
if isinstance(params_converter, typing._GenericAlias): | ||
params_converter = params_converter.__args__[0] | ||
filters[k] = params_converter(v) | ||
except Exception as ex: | ||
logging.warning( | ||
'Query params `%s`(value: %s) type convert failed, ' | ||
'exception: %s', k, v, ex | ||
) | ||
continue | ||
|
||
schema_in = ListRequest(**params, filters=filters) | ||
|
||
result = await action_func(schema_in) | ||
return paginate(result) | ||
|
||
# update route's signatures | ||
parameters = [] | ||
for k, v in router_generator._ordered_filters.items(): | ||
default = inspect.Parameter.empty | ||
if isinstance(v, typing._GenericAlias): | ||
default = None if type(None) in v.__args__ else default | ||
parameters.append( | ||
inspect.Parameter( | ||
name=k, | ||
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, | ||
default=default, | ||
annotation=v, | ||
) | ||
) | ||
sig = inspect.signature(route) | ||
parameters.extend(sig.parameters.values()) | ||
route.__signature__ = sig.replace(parameters=parameters) | ||
|
||
return pick_route(action_func, async_route, route) | ||
|
||
|
||
def get_(router_generator) -> Callable: | ||
resource = router_generator.cls() | ||
action_func = getattr(resource, 'get') | ||
|
||
def route(id: int, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return action_func(pk=id) | ||
|
||
async def async_route(id: int, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return await action_func(pk=id) | ||
|
||
return pick_route(action_func, async_route, route) | ||
|
||
|
||
def create_(router_generator) -> Callable: | ||
resource = router_generator.cls() | ||
action_func = getattr(resource, 'create') | ||
|
||
def route(schema_in: resource.schema, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return action_func(schema_in) | ||
|
||
async def async_route(schema_in: resource.schema, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return await action_func(schema_in) | ||
|
||
return pick_route(action_func, async_route, route) | ||
|
||
|
||
def update_(router_generator) -> Callable: | ||
resource = router_generator.cls() | ||
action_func = getattr(resource, 'update') | ||
|
||
def route(schema_in: resource.schema, id: int, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return action_func(schema_in, pk=id) | ||
|
||
async def async_route( | ||
schema_in: resource.schema, id: int, request: Request = None | ||
): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return await action_func(schema_in, pk=id) | ||
|
||
return pick_route(action_func, async_route, route) | ||
|
||
|
||
def delete_(router_generator) -> Callable: | ||
resource = router_generator.cls() | ||
action_func = getattr(resource, 'delete') | ||
|
||
def route(id: int, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return action_func(pk=id) | ||
|
||
async def async_route(id: int, request: Request = None): | ||
resource._request = request | ||
router_generator.check_permissions(resource) | ||
return await action_func(pk=id) | ||
|
||
return pick_route(action_func, async_route, route) |
Oops, something went wrong.