Skip to content

Commit

Permalink
Updates documentation. (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtiesling authored Nov 29, 2023
1 parent 7e7bffe commit 2ec3fb9
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 26 deletions.
4 changes: 4 additions & 0 deletions docs/docs/api_reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# API Reference

## FlaskMuckApiView
::: flask_muck.FlaskMuckApiView
65 changes: 65 additions & 0 deletions docs/docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Configuration

Configuration is handled entirely through setting class variables on the FlaskMuckApiViews. As noted in the
[quickstart](quickstart.md) you will likely have some class variable settings that will be shared by most or all of your
view classes. It's advised to set up base classes to handle sharing configuration between views.

## FlaskMuckApiView Class Variables

| Class Variable | Description | Required |
|:------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|
| session `scoped_session` | SqlAlchemy database session used to query and modify the resource. | :octicons-check-circle-16: |
| api_name `str` | Name of the API. Used as the url path appended to your Flask Blueprint. | :octicons-check-circle-16: |
| Model `SqlaModelType` | SqlAlchemy Model used to make queries. | :octicons-check-circle-16: |
| ResponseSchema `type[Schema]` | Marshmallow schema used to serialize the resource returned by any of the views. | :octicons-check-circle-16: |
| decorators `list[Callable]` | List of decorators to apply to all views in the API. This is inherited functionality built into Flask's [class based views](https://flask.palletsprojects.com/en/2.3.x/views/#view-decorators). | |
| parent `Optional[type[FlaskMuckApiView]]` | If set, this API becomes a nested resource API. For more information on nested APIs see the [documentation](nesting_apis.md). | |
| CreateSchema `Optional[type[Schema]]` | Marshmallow schema used to validate the POST request JSON payload sent to create a resource. | |
| UpdateSchema `Optional[type[Schema]]` | Marshmallow schema used to validate the PUT request JSON payload sent to update a resource. | |
| PatchSchema `Optional[type[Schema]]` | Marshmallow schema used to validate the PATCH request JSON payload sent to patch a resource. | |
| DeleteSchema `Optional[type[Schema]]` | Marshmallow schema used to validate the DELETE request JSON payload sent to create a resource. Optional. | |
| DetailSchema `Optional[type[Schema]]` | Optional Marshmallow schema used to serialize the resource returned by the GET /<api_name\>/<ID\>/ endpoint. If this schema is not set the ResponseSchema is used. | |
| pre_create_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called before a resource is created. Ideal for validation. | |
| pre_update_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called before a resource is updated. Ideal for validation. | |
| pre_patch_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called before a resource is patched. Ideal for validation. | |
| pre_delete_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called before a resource is deleted. Ideal for validation. | |
| post_create_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called after a resource is created. Useful for activities such as notifications. Called post commit. | |
| post_update_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called after a resource is updated. Useful for activities such as notifications. Called post commit. | |
| post_patch_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called after a resource is patched. Useful for activities such as notifications. Called post commit. | |
| post_delete_callbacks `list[type[FlaskMuckCallback]]` | List of callback classes to be called after a resource is deleted. Useful for activities such as notifications. Called post commit. | |
| searchable_columns `list[InstrumentedAttribute]` | List of Model columns that will be queried using an "ILIKE" statement when the `search=` query param is used on the GET /resource/ endpoint. | |
| default_pagination_limit `int` | Default pagination limit when retrieving paginated results on the GET /<api_name\>/ endpoint. Default is 20. | |
| one_to_one_api `bool` | If True this API is treated as a one-to-one relationship and the GET /<api_name\>/ endpoint will return a single resource. Generally used in combination with the `parent` setting. | |
| allowed_methods `set[str]` | Set of allowed HTTP methods for this API. Default is `{"GET", "POST", "PUT", "PATCH", "DELETE"}`. This setting is used to control which actions are available for this resource. Not including a method affects which routes will be registered to a Flask Blueprint. | |
| operator_separator `str` | Separator used when assigning operators to search or filter query parameters in the GET /<api_name\>/ endpoint. Default is `"__"`. | |

## Base Class Example

Let's say you have an API with the following requirements for all views:

- Has authentication decorator.
- Has permission checking decorator.
- Does not allow patches.
- Uses "|" as the operator separator for filters.
- Uses the standard database session.

The best way to handle this is to create a base API view that all other API views inherit from.

```python title="Base Class"
class BaseApiView(FlaskMuckApiView):
session = db.session
decorators = [login_required, permission_required]
allowed_methods = {"GET", "POST", "PUT", "DELETE"}
operator_separator = "|"
```

```python title="Concrete Class"
class TurtleApiView(BaseApiView):#(1)!
api_name = 'turtles'
Model = Turtle
...#(2)!
```

1. Concrete view inherits from the BaseApiView and ingerits all of its configuration.
2. Remainder of class variables are configured as normal.

2 changes: 1 addition & 1 deletion docs/docs/nesting_apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ This produces the following nested api resources.
If your models are not using standard integer or UUID primary keys nested APIs may not work correctly.


## Complete Example
## Usage Example
!!! note
This example expands on the example in the [quickstart](quickstart.md). If you have not read through the
[quickstart](quickstart.md) this will make more sense if you do.
Expand Down
6 changes: 6 additions & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,22 @@ theme:
name: Switch to light mode
plugins:
- search
- mkdocstrings
markdown_extensions:
- attr_list
- admonition
- abbr
- md_in_html
- pymdownx.superfences
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
extra_css:
- stylesheets/extra.css
nav:
- About: index.md
- Installation: installation.md
- Quick Start: quickstart.md
- Configuration: configuration.md
- Nesting APIs: nesting_apis.md
- API Reference: api_reference.md
77 changes: 75 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ flask-sqlalchemy = "^3.1.1"
coverage = "^7.3.2"
pytest-cov = "^4.1.0"
mkdocs-material = "^9.4.11"
mkdocstrings = {extras = ["python"], version = "^0.24.0"}

[tool.mypy]
packages = "src"
Expand Down
2 changes: 1 addition & 1 deletion src/flask_muck/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .views import FlaskMuckApiView
from .callback import MuckCallback
from .callback import FlaskMuckCallback

VERSION = "0.0.3b2"
2 changes: 1 addition & 1 deletion src/flask_muck/callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CallbackType(Enum):
post = "post"


class MuckCallback(ABC):
class FlaskMuckCallback(ABC):
def __init__(self, resource: SqlaModel, kwargs: JsonDict):
self.resource = resource
self.kwargs = kwargs
Expand Down
34 changes: 16 additions & 18 deletions src/flask_muck/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from werkzeug.exceptions import MethodNotAllowed, BadRequest, Conflict

from flask_muck.callback import CallbackType
from flask_muck.callback import MuckCallback
from flask_muck.callback import FlaskMuckCallback
from flask_muck.types import SqlaModelType, JsonDict, ResourceId, SqlaModel
from flask_muck.utils import (
get_url_rule,
Expand All @@ -46,26 +46,26 @@ class FlaskMuckApiView(MethodView):
session: scoped_session
api_name: str
Model: SqlaModelType
parent: Optional[type[FlaskMuckApiView]] = None

CreateSchema: type[Schema]
UpdateSchema: type[Schema]
PatchSchema: type[Schema]
DeleteSchema: Optional[type[Schema]] = None
ResponseSchema: type[Schema]
DetailSchema: type[Schema]
CreateSchema: Optional[type[Schema]] = None
UpdateSchema: Optional[type[Schema]] = None
PatchSchema: Optional[type[Schema]] = None
DeleteSchema: Optional[type[Schema]] = None
DetailSchema: Optional[type[Schema]] = None

pre_create_callbacks: list[type[MuckCallback]] = []
pre_update_callbacks: list[type[MuckCallback]] = []
pre_patch_callbacks: list[type[MuckCallback]] = []
pre_delete_callbacks: list[type[MuckCallback]] = []
pre_create_callbacks: list[type[FlaskMuckCallback]] = []
pre_update_callbacks: list[type[FlaskMuckCallback]] = []
pre_patch_callbacks: list[type[FlaskMuckCallback]] = []
pre_delete_callbacks: list[type[FlaskMuckCallback]] = []

post_create_callbacks: list[type[MuckCallback]] = []
post_update_callbacks: list[type[MuckCallback]] = []
post_patch_callbacks: list[type[MuckCallback]] = []
post_delete_callbacks: list[type[MuckCallback]] = []
post_create_callbacks: list[type[FlaskMuckCallback]] = []
post_update_callbacks: list[type[FlaskMuckCallback]] = []
post_patch_callbacks: list[type[FlaskMuckCallback]] = []
post_delete_callbacks: list[type[FlaskMuckCallback]] = []

searchable_columns: Optional[list[InstrumentedAttribute]] = None
parent: Optional[type[FlaskMuckApiView]] = None
searchable_columns: list[InstrumentedAttribute] = []
default_pagination_limit: int = 20
one_to_one_api: bool = False
allowed_methods: set[str] = {"GET", "POST", "PUT", "PATCH", "DELETE"}
Expand All @@ -76,7 +76,6 @@ def query(self) -> Query:
return self.session.query(self.Model)

def dispatch_request(self, **kwargs: Any) -> ResponseReturnValue:
"""Overriden to check the list of allowed_methods."""
if request.method.lower() not in [m.lower() for m in self.allowed_methods]:
raise MethodNotAllowed
return super().dispatch_request(**kwargs)
Expand Down Expand Up @@ -339,7 +338,6 @@ def _get_query_order_by(
else:
_Model = self.Model

order_by = None
if hasattr(_Model, column_name):
column = getattr(_Model, column_name)
if direction == "asc":
Expand Down
6 changes: 3 additions & 3 deletions tests/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from marshmallow import fields as mf
from sqlalchemy.orm import DeclarativeBase, Mapped

from flask_muck import MuckCallback
from flask_muck import FlaskMuckCallback
from flask_muck.views import FlaskMuckApiView


Expand Down Expand Up @@ -110,12 +110,12 @@ def logout_view():


# Add Muck views to generate CRUD REST API.
class PreCallback(MuckCallback):
class PreCallback(FlaskMuckCallback):
def execute(self) -> None:
return


class PostCallback(MuckCallback):
class PostCallback(FlaskMuckCallback):
def execute(self) -> None:
return

Expand Down

0 comments on commit 2ec3fb9

Please sign in to comment.