Open sobolevn opened 5 years ago
New ideas:
# representations.py
@dataclass
class ShortUserRepresentation(object):
id: int
username: str
email: str
# views.py
class _AbstractUserMethod(APIMethod, abstract=True):
queryset = User.objects.filter(is_active=True)
permissions = [permissions.IsAdmin]
class UserListMethod(_AbstractUserMethod):
renderer = ShortUserRepresentation
class UserDetailsMethod(_AbstractUserMethod):
renderer = FullUserRepresentation
class UserCreateMethod(_AbstractUserMethod):
parser = create_model_parser(User, exclude=['id']) # introspects model and returns a dataclass with all fields
renderer = FullUserRepresentation
# To perform logic, define one of:
controller: Callable = function_or_callable_class
def controller(self, request: DjangoRequest, parsed_data: FullUserRepresentationWithoutId) -> Callable:
return _CallableClassWithLogic(request, parsed_data)
# urls.py
api = APIDeclaration(**some_swagger_information)
api.add_endpoint('user', get=UserListMethod, post=UserCreateMethod)
api.add_endpoint('user/{pk}', get=UserDetailsMethod)
class UserCreateMethod(APIMethod):
validators = [
unique('email'),
unique('username'),
should_equal('password1', 'password2'),
]
parser = UserDataclassWithoutId
renderer = UserDataclassWithId
ResponseWrapper
is something that wraps python data like {'username': 'sobolevn'}
into more complex construct: {'data': {'username': 'sobolevn'}, '_meta': {...}, '_pagination': {...}}
Code example:
@HATEOASWrapper
class UserDetailsMethod(_AbstractUserMethod):
renderer = FullUserRepresentation
There are basically two ways of implementing swagger
specific data model and rendering:
I personally prefer the first one. There are several implementations worth mentioning:
I am not of a fan of the idea to use custom templates for swagger-ui
or even have these files and templates in the core library.
May be we can go with using standalone swagger-ui
package?
We should not have any settings. I do not like settings. Since people might do something wrong with them.
It is even better not to have settings, so we can use dependency injection and be free from global variables.
That's how I see it:
api = APIDeclaration(
accepts=['application/json', 'application/xml'],
extra_parser_fields={Email: EmailParser},
auth_backend=JWTAuthBackend, # or = PublicAuthBacked
)
What else needs to be configured?
See:
I am not sure about what parser
should mean. I think of it as some kind of a serialization mechanism that turns raw dict
s into dataclass
es like pavlova
does.
And the renderer
is the opposite. Turning dataclass
es into dict
s, like dataclass.asdict()
does.
But, there's another type of renderer
and parser
I can think of. Which turn dict
into json
formatter text or xml
formatted text and vice-a-versa.
So, maybe we can call the first concept request_payload
and response_payload
and leave parser
and renderer
alone? This naming seems better.
There should be a regular exception handling mechanism. But it will consist of at least three stages:
request_payload
, logical validation)response_payload
, wrappers)We need to declare a lot of things for the swagger
spec to be able to work correctly.
Including: headers, response codes, descriptions, schemas, etc
class UserDetailMethod(APIMethod):
response_payload = UserDataclassWithId
class Meta(APIMethodMetaProtocol):
"""This class is only required to supply meta information, it does not alter behavior."""
errors = [api.HTTP404(description='User with given pk does not exist'), ]
response_headers = [OpenAPIHeaders(name='X-RateLimit-Limit', type='integer')]
See:
We should bundle https://pypi.org/project/swagger-conformance/ with the app. So, we will test that some API methods are incorrect. And its Meta
needs to be fixed.
This should be both .testing
package feature as well as a documentation feature.
We need to provide health-checks by default. Or we can reuse some existing libs that do that.
Initial ideas
swagger
should be supported in the core library, we prefer to getv3
supportdjango-filter
and nativedjango.Paginator
integrationjwt
should be the default auth mechanismGeneral
typing
everywhereSteps
Runs once on startup:
Runs per each request:
csrf
, auth, permissions, etc)json
,xml
, etc formats topython
native structuresUsage examples
Endpoint registration
Endpoint declaration
Serializers
I do not like the concept of serializers. I like
dataclasses
that are now part of thepython
's standard library. Or maybeTypedDict
, but they are onlymypy_extensions
level only.attrs
also should be supported forpython3.6
.When using
drf
currently I always end up writing tons of dataclasses and typed dics, because I always have functions and classes that work with the raw data.That's how it looks:
Any logic can be executed in
__post_init__
hook of a dataclass. Also, when some logical validation is required (likeunique=True
it should be done inlogic_validator
stage.Global
TODO
seve
,rdf
drf
. Custom fields likeNestedField('data.key.value)'? As we use it now for
kira`