[Proposal] Modeling
- Dominant language
- Python
- Stars
- 11.1k
- Forks
- 1k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 2
Description
Proof of concept: #1223
Proposed interface
==============
Two additional keys on the route decorator. `input_model` and `output_model` (maybe request/response instead?) The value of these is either `None` (default) meaning no models are applied to this route. Or an instance of `ModelConfig`. Which is defined as such
```python
class ModelConfig(object):
def __init__(self, model, validate=False, cls=None):
self.model = model
self.validate = validate
self.cls = cls
...
```
Where `model` is a marshmallow schema (currently class, though I plan to change it to instance), `validate` is whether or not the model should be enforced, and `cls` is a concrete data type that can be deserialized from the input body, or serialized to a response body.
The request object has a new property `model_body` which will be an instance of `cls` created from deserializing the `json_body`. If the `input_model` `cls` is not set then it will raise an error.
An instance of the `cls` on the `output_model` can be returned from a view function and will be automatically serialized using the schema.
Validate works slightly differently for in put and output. For input it will create a body validator in API gateway and enforce validation at the API Gateway layer. Invalid requests will not trigger a lambda and will return a 400.
For output API gateway does not provide any validation (understandably), so we provide it ourselves using the marshmallow schema. Validate will be manually called, and if any errors are discovered an error will be raised and the validation errors will be logged. A 500 will be returned to the user.
Sample Project
===========
```
$ tree .
.
├── app.py
├── chalicelib
│ ├── __init__.py
│ └── models.py
└── requirements.txt
1 directory, 4 files
```
app.py
```python
from chalice import Chalice
from chalice import ModelConfig
from chalicelib import models
app = Chalice(app_name='model-test')
@app.route('/hi', methods=['POST'],
input_model=ModelConfig(
models.UserSchema, validate=True, cls=models.User))
def say_hi():
user = app.current_request.model_body
return user.make_greeting()
@app.route('/get-user', methods=['POST'],
output_model=ModelConfig(
models.UserSchema, cls=models.User))
def get_user():
totally_a_user_i_got_from_a_db = models.User(
first_name='William',
middle_name='Henry',
last_name='Harrison',
)
return totally_a_user_i_got_from_a_db
```
chalicelib/models.py
```python
import random
from marshmallow import Schema, fields
class UserSchema(Schema):
first_name = fields.String(required=True)
middle_name = fields.String()
last_name = fields.String(required=True)
class User(object):
def __init__(self, first_name, last_name, middle_name=None):
self.first_name = first_name
self.middle_name = middle_name
self.last_name = last_name
def make_greeting(self):
greeting = random.choice(['Hi', 'Hello', 'Sup'])
if self.middle_name is None:
return '%s %s %s' % (greeting, self.first_name, self.last_name)
return '%s %s %s %s' % (
greeting, self.first_name, self.middle_name, self.last_name)
```
Sample Calls
=========
first middle and last
```bash
$ echo '{"first_name": "george", "middle_name": "wilbur", "last_name": "francis"}' | http POST $(chalice url)hi
HTTP/1.1 200 OK
Hello george wilbur francis
```
first and last
```bash
$ echo '{"first_name": "george", "last_name": "francis"}' | http POST $(chalice url)hi
HTTP/1.1 200 OK
Hello george francis
```
just first
```bash
$ echo '{"first_name": "george"}' | http POST $(chalice url)hi
HTTP/1.1 400 Bad Request
{
"message": "Invalid request body"
}
```
example showing output serialization of `User` object
```bash
$ http POST $(chalice url)get-user
HTTP/1.1 200 OK
{
"first_name": "William",
"last_name": "Harrison",
"middle_name": "Henry"
}
```
Contributor guide
Research direction
Review proof of concept #1223 and the proposed route decorator, request model_body property, marshmallow schemas, and API Gateway validation behavior. Use the sample app.py and chalicelib/models.py as the reference flow; done means input and output models, deserialization, validation, and serialization behave as shown in the examples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, python
- Domain
- api, backend, cloud
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100