fix(docs): Various documentation fixed and improvments

This commit is contained in:
Ferdinand Thiessen 2021-12-23 02:42:00 +01:00
parent 1201505586
commit 5669220b5d
5 changed files with 116 additions and 37 deletions

View File

@ -44,11 +44,12 @@ If not you need to create user and database manually do (or similar on Windows):
) | sudo mysql
Then you can install the database tables, this will update all tables from core + all enabled plugins.
*Hint:* The same command can be later used to upgrade the database after plugins or core are updated.
$ flaschengeist db upgrade heads
## Plugins
To only upgrade one plugin:
To only upgrade one plugin (for example the `events` plugin):
$ flaschengeist db upgrade events@head

View File

@ -22,6 +22,9 @@ migrate = Migrate()
@migrate.configure
def configure_alembic(config):
"""Alembic configuration hook
"""
# Load migration paths from plugins
migrations = [str(p.migrations_path) for p in current_app.config["FG_PLUGINS"].values() if p and p.migrations_path]
if len(migrations) > 0:

View File

@ -1,3 +1,29 @@
"""Flaschengeist Plugins
## Custom database tables
You can add tables by declaring them using the SQLAlchemy syntax,
then use Alembic to generate migrations for your tables.
This allows Flaschengeist to proper up- or downgrade the
database tables if an user updates your plugin.
migrations have to be provided in a directory called `migrations`
next to your plugin. E.G.
myplugin
- __init__.py
- other/
- ...
- migrations/
## Useful Hooks
There are some predefined hooks, which might get handy for you.
For more information, please refer to
- `flaschengeist.utils.hook.HookBefore` and
- `flaschengeist.utils.hook.HookAfter`
"""
import sqlalchemy
import pkg_resources
from werkzeug.datastructures import FileStorage
@ -10,48 +36,81 @@ from flaschengeist.models.user import _Avatar, User
from flaschengeist.models.setting import _PluginSetting
from flaschengeist.utils.hook import HookBefore, HookAfter
__all__ = [
"plugins_installed",
"plugins_loaded",
"before_delete_user",
"before_role_updated",
"before_update_user",
"after_role_updated",
"Plugin",
"AuthPlugin",
]
# Documentation hacks, see https://github.com/mitmproxy/pdoc/issues/320
plugins_installed = HookAfter("plugins.installed")
"""Hook decorator for when all plugins are installed
Possible use case would be to populate the database with some presets.
plugins_installed.__doc__ = """Hook decorator for when all plugins are installed
Args:
hook_result: void (kwargs)
Possible use case would be to populate the database with some presets.
"""
plugins_loaded = HookAfter("plugins.loaded")
"""Hook decorator for when all plugins are loaded
Possible use case would be to check if a specific other plugin is loaded and change own behavior
plugins_loaded.__doc__ = """Hook decorator for when all plugins are loaded
Args:
app: Current flask app instance (args)
hook_result: void (kwargs)
Possible use case would be to check if a specific other plugin is loaded and change own behavior
Passed args:
- *app:* Current flask app instance (args)
"""
before_role_updated = HookBefore("update_role")
"""Hook decorator for when roles are modified
Args:
role: Role object to modify
new_name: New name if the name was changed (None if delete)
before_role_updated.__doc__ = """Hook decorator for when roles are modified
Passed args:
- *role:* `flaschengeist.models.user.Role` to modify
- *new_name:* New name if the name was changed (*None* if delete)
"""
after_role_updated = HookAfter("update_role")
"""Hook decorator for when roles are modified
Args:
role: Role object containing the modified role
new_name: New name if the name was changed (None if deleted)
after_role_updated.__doc__ = """Hook decorator for when roles are modified
Passed args:
- *role:* modified `flaschengeist.models.user.Role`
- *new_name:* New name if the name was changed (*None* if deleted)
"""
before_update_user = HookBefore("update_user")
"""Hook decorator, when ever an user update is done, this is called before.
Args:
user: User object
before_update_user.__doc__ = """Hook decorator, when ever an user update is done, this is called before.
Passed args:
- *user:* `flaschengeist.models.user.User` object
"""
before_delete_user = HookBefore("delete_user")
"""Hook decorator,this is called before an user gets deleted.
Args:
user: User object
before_delete_user.__doc__ = """Hook decorator,this is called before an user gets deleted.
Passed args:
- *user:* `flaschengeist.models.user.User` object
"""
class Plugin:
"""Base class for all Plugins
If your class uses custom models add a static property called ``models``"""
All plugins must be derived from this class.
There are some static properties a plugin must provide,
and some properties a plugin can provide if you might want
to use more functionality.
Required:
- *id*: Unique identifier of your plugin
Optional:
- *blueprint*: `flask.Blueprint` providing your routes
- *permissions*: List of your custom permissions
- *models*: Your models, used for API export
- *version*: Version of your plugin, can also be guessed by Flaschengeist
"""
blueprint = None # You have to override
"""Override with a `flask.blueprint` if the plugin uses custom routes"""
@ -61,14 +120,18 @@ class Plugin:
A good style is to name the permissions with a prefix related to the plugin name,
to prevent clashes with other plugins. E. g. instead of *delete* use *plugin_delete*.
"""
id = "dev.flaschengeist.plugin" # You have to override
"""Override with the unique ID of the plugin (Hint: FQN)"""
name = "plugin" # You have to override
"""Override with human readable name of the plugin"""
models = None # You have to override
"""Override with models module"""
migrations_path = None # Override this with the location of your db migrations directory
"""Override with path to migration files, if custome db tables are used"""
models = None
"""Override with models module
Used for API export, has to be a static property
"""
version = None
"""Override with a custom version, optional
If not set, the version is guessed from the package / distribution
"""
def __init__(self, config=None):
"""Constructor called by create_app
@ -233,14 +296,14 @@ class AuthPlugin(Plugin):
"""
raise NotFound
def set_avatar(self, user: User, file: FileStorage):
def set_avatar(self, user, file):
"""Set the avatar for given user (if supported by auth backend)
Default behavior is to use native Image objects stored on the Flaschengeist server
Args:
user: User to set the avatar for
file: FileStorage object uploaded by the user
file: `werkzeug.datastructures.FileStorage` uploaded by the user
Raises:
MethodNotAllowed: If not supported by Backend
Any valid HTTP exception

View File

@ -131,7 +131,7 @@ def get_balance(userid, current_session: Session):
Route: ``/users/<userid>/balance`` | Method: ``GET``
GET-parameters: ```{from?: string, to?: string}```
GET-parameters: ``{from?: string, to?: string}``
Args:
userid: Userid of user to get balance from
@ -170,7 +170,7 @@ def get_transactions(userid, current_session: Session):
Route: ``/users/<userid>/balance/transactions`` | Method: ``GET``
GET-parameters: ```{from?: string, to?: string, limit?: int, offset?: int}```
GET-parameters: ``{from?: string, to?: string, limit?: int, offset?: int}``
Args:
userid: Userid of user to get transactions from

View File

@ -7,6 +7,7 @@ _hooks_after = {}
def Hook(function=None, id=None):
"""Hook decorator
Use to decorate functions as hooks, so plugins can hook up their custom functions.
"""
# `id` passed as `arg` not `kwarg`
@ -38,8 +39,10 @@ def Hook(function=None, id=None):
def HookBefore(id: str):
"""Decorator for functions to be called before a Hook-Function is called
The hooked up function must accept the same arguments as the function hooked onto,
as the functions are called with the same arguments.
Hint: This enables you to modify the arguments!
"""
if not id or not isinstance(id, str):
@ -54,9 +57,18 @@ def HookBefore(id: str):
def HookAfter(id: str):
"""Decorator for functions to be called after a Hook-Function is called
As with the HookBefore, the hooked up function must accept the same
arguments as the function hooked onto, but also receives a
`hook_result` kwarg containing the result of the function.
Example:
```py
@HookAfter("some.id")
def my_func(hook_result):
# This function is executed after the function registered with "some.id"
print(hook_result) # This is the result of the function
```
"""
if not id or not isinstance(id, str):