Mygrations
An endpoint to run database migrations via the mygrations library.
- Overview
- cursor
- cursor_dependency_name
- allow_input
- command
- sql
- include_module_migrations
- module_migrations
- url
Overview
You must install mygrations separately to use this endpoint:
pip install mygrations
See the rep for mygrations documentation:
https://github.com/cmancone/mygrations
Mygrations is a stateless database migration system. You define your desired database schema via SQL (using CREATE TABLE commands) and point mygrations at both your SQL files and your database. It will then compare the two and calculate what changes need to be made to bring your database in line with your files. Like most IaC, mygrations then relies on your standard plan/apply combination. Here’s a simple example:
import clearskies
cli = clearskies.contexts.Cli(
clearskies.endpoints.Mygrations(
allow_input=True,
cursor=clearskies.cursors.from_environment.Mysql(),
sql=["./database/"],
)
)
cli()
This would find database connection details in your environment and would look for *.sql files in the database folder. You could then call this script to plan/apply/whatever:
./mygrate.py --command=plan
./mygrate.py --command=apply
Auto-imported migration paths
If any module that was imported into the Di container via add_modules() contains a class that extends clearskies.di.AdditionalMygrationsAutoImport, the SQL paths declared by that class will automatically be prepended to the sql list before migrations run.
The endpoint’s explicit sql configuration takes deduplication priority: if an auto-imported path matches one already declared in sql, only the endpoint’s copy is used (at the end of the final list). Non-existent paths from auto-imported configs are silently skipped.
Discovery order is preserved; paths from modules discovered earlier appear first.
cursor
Optional
cursor_dependency_name
Optional
The dependency name to fetch the cursor from.
If you also pass a cursor instance directly, the directly-passed cursor takes precedence.
allow_input
Optional
Whether or not to allow the mygrations endpoint to accept input for its configuration
To run, the migrations endpoint needs to know which mygration command to use and where the SQL files live. These can be set exclusively via the endpoint configuration, or (if this flag is set to true) they can be set via request parameters through whatever context the endpoint is attached to.
command
Optional
The mygrations command to execute
See https://github.com/cmancone/mygrations#command-line-usage for the relevant docs. Current allowed commands are:
versioncheckplanplan_exportapply
sql
Optional
The SQL to use as a source of truth.
This is a list of strings, and each string value can be one of the following:
- A valid SQL string.
- A path to a file containing SQL.
- A path to a directory containing SQL files.
The combination of all SQL you pass in (either directly or by referencing files/directories) becomes the source of truth that mygrations will attempt to bring your database to.
In addition, any paths declared via AdditionalMygrationsAutoImport subclasses found in modules imported into the Di container are automatically prepended to this list (deduplicated, with this explicit list taking dedup priority).
include_module_migrations
Optional
Whether to include auto-imported module migrations.
If true, the endpoint will include SQL paths from any AdditionalMygrationsAutoImport subclasses discovered by the Di container. If false (the default), auto-imported module migrations are ignored entirely.
module_migrations
Optional
Optional allow-list of AdditionalMygrationsAutoImport subclass names to include.
When include_module_migrations is true and this list is empty (the default), all discovered subclasses contribute their SQL paths. When this list is non-empty, only subclasses whose class name appears in the list are included.
The entries must be the class name (e.g. "GitlabMetadataMigrations"), not the module path.
Example:
clearskies.endpoints.Mygrations(
include_module_migrations=True,
module_migrations=["GitlabMetadataMigrations", "AuditLogMigrations"],
)
url
Optional
Set the URL for the endpoint
When an endpoint is attached directly to a context, then the endpoint’s URL becomes the exact URL to invoke the endpoint. If it is instead attached to an endpoint group, then the URL of the endpoint becomes a suffix on the URL of the group. This is described in more detail in the documentation for endpoint groups, so here’s an example of attaching endpoints directly and setting the URL:
import clearskies
endpoint = clearskies.endpoints.Callable(
lambda: {"hello": "World"},
url="/hello/world",
)
wsgi = clearskies.contexts.WsgiRef(endpoint)
wsgi()
Which then acts as expected:
$ curl 'http://localhost:8080/hello/asdf' | jq
{
"status": "client_error",
"error": "Not Found",
"data": [],
"pagination": {},
"input_errors": {}
}
$ curl 'http://localhost:8080/hello/world' | jq
{
"status": "success",
"error": "",
"data": {
"hello": "world"
},
"pagination": {},
"input_errors": {}
}
Some endpoints allow or require the use of named routing parameters. Named routing paths are created using either the /{name}/ syntax or /:name/. These parameters can be injected into any callable via the routing_data dependency injection name, as well as via their name:
import clearskies
endpoint = clearskies.endpoints.Callable(
lambda first_name, last_name: {"hello": f"{first_name} {last_name}"},
url="/hello/:first_name/{last_name}",
)
wsgi = clearskies.contexts.WsgiRef(endpoint)
wsgi()
Which you can then invoke in the usual way:
$ curl 'http://localhost:8080/hello/bob/brown' | jq
{
"status": "success",
"error": "",
"data": {
"hello": "bob brown"
},
"pagination": {},
"input_errors": {}
}