Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Postgres schemas support #4

Merged
merged 13 commits into from
Dec 27, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
env:
PYTHONPATH: .
run: |
cd tests/poc/
PGPASSWORD=password psql -h localhost -U user -d database -tc "CREATE SCHEMA poc;"
alembic revision --autogenerate
alembic -x data=true -x test=true upgrade head

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## UNRELEASED
### Added
- Database schemas support.
### Removed
- Root Aggregate model.

## [0.3.0] - 2021-11-16
### Added
- Added base implementation of CQRS Pattern.
Expand Down
2 changes: 1 addition & 1 deletion tests/poc/alembic.ini → alembic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[alembic]
# path to migration scripts
script_location = migration_example/
script_location = tests/poc/migration_example/

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
Expand Down
2 changes: 1 addition & 1 deletion kingdom_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Define the package's version.
# - Is used for indexing and distributing the package
# - Follow the conventions defined by PEP440
__version__ = "0.3.0"
__version__ = "1.0.0dev1"


def _get_base_dir() -> str:
Expand Down
21 changes: 11 additions & 10 deletions kingdom_sdk/database/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
from kingdom_sdk.utils import time


def entity_table_factory(name: str, *columns: Column) -> TableFactory_T:
def entity_table_factory(
schema: str, name: str, *columns: Column
) -> TableFactory_T:
return lambda metadata: Table(
name,
metadata,
Expand All @@ -27,18 +29,17 @@ def entity_table_factory(name: str, *columns: Column) -> TableFactory_T:
default=time.generate_now,
),
*columns,
schema=schema,
)


def aggregate_table_factory(name: str, *columns: Column) -> TableFactory_T:
return entity_table_factory(name, *columns)


def root_aggregate_table_factory(
name: str, *columns: Column
def aggregate_table_factory(
schema: str, name: str, *columns: Column
) -> TableFactory_T:
return aggregate_table_factory(name, *columns)
return entity_table_factory(schema, name, *columns)


def relationship_table_factory(name: str, *columns: Column) -> TableFactory_T:
return lambda metadata: Table(name, metadata, *columns)
def relationship_table_factory(
schema: str, name: str, *columns: Column
) -> TableFactory_T:
return lambda metadata: Table(name, metadata, *columns, schema=schema)
8 changes: 1 addition & 7 deletions kingdom_sdk/database/mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from sqlalchemy import Table
from sqlalchemy.orm import mapper

from kingdom_sdk.domain.aggregate import Aggregate, RootAggregate
from kingdom_sdk.domain.aggregate import Aggregate
from kingdom_sdk.domain.entity import Entity


Expand All @@ -22,9 +22,3 @@ def aggregate_mapper(
aggregate: Type[Aggregate], table: Table, **properties: Any
) -> None:
entity_mapper(aggregate, table, **properties)


def root_aggregate_mapper(
aggregate: Type[RootAggregate], table: Table, **properties: Any
) -> None:
aggregate_mapper(aggregate, table, **properties)
48 changes: 48 additions & 0 deletions kingdom_sdk/database/migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from alembic import context
from sqlalchemy import MetaData, create_engine

from kingdom_sdk import config


def run_migrations_offline(schema: str, metadata: MetaData) -> None:
"""Run migrations in 'offline' mode.

This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.

Calls to context.execute() here emit the given string to the
script output.
"""
context.configure(
url=config.get_database_url(),
target_metadata=metadata,
literal_binds=True,
include_schemas=True,
dialect_opts={"paramstyle": "named"},
version_table_schema=schema,
)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online(schema: str, metadata: MetaData) -> None:
"""Run migrations in 'online' mode.

In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = create_engine(config.get_database_url())

with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=metadata,
include_schemas=True,
version_table_schema=schema,
)

with context.begin_transaction():
context.run_migrations()
7 changes: 0 additions & 7 deletions kingdom_sdk/domain/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,3 @@ def next_event(self) -> Event:
@property
def events(self) -> List[Event]:
return self._events


class RootAggregate(Aggregate, ABC):
"""Base class for root aggregates.

There's only one root aggregate for each bounded context.
"""
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "kingdom-sdk"
version = "0.3.0"
version = "1.0.0dev1"
description = "Library containing the core modules for the kingdom-python-server"
authors = ["William Abreu <william@t10.digital>"]
license = "MIT License"
Expand Down
7 changes: 5 additions & 2 deletions tests/poc/context_example/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
from tests.poc.context_example import model

examples_entity = entity_table_factory(
"examples_entity", Column("name", String(255), nullable=False)
"poc", "examples_entity", Column("name", String(255), nullable=False)
)

examples_aggregate = aggregate_table_factory(
"poc",
"examples_aggregate",
Column("value", Float(), nullable=False),
Column("reference_id", ForeignKey("examples_entity.id"), nullable=False),
Column(
"reference_id", ForeignKey("poc.examples_entity.id"), nullable=False
),
)


Expand Down
2 changes: 1 addition & 1 deletion tests/poc/context_example/queries/query.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*skip-formatter*/
SELECT *
FROM examples_entity
FROM poc.examples_entity
WHERE id = {{ id }}
{% if name %}
and name ilike {{ name }}
Expand Down
77 changes: 14 additions & 63 deletions tests/poc/migration_example/env.py
Original file line number Diff line number Diff line change
@@ -1,91 +1,42 @@
import os
import sys
from pathlib import Path

# Fix the path to include repository root.
root = Path(os.getcwd()).parent.parent
sys.path.insert(0, root.as_posix())

from logging.config import fileConfig

from alembic import context
from dotenv import load_dotenv
from sqlalchemy import create_engine, engine, orm
from sqlalchemy import orm

from kingdom_sdk import config as app_config
from kingdom_sdk.database.migrations import (
run_migrations_offline,
run_migrations_online,
)
from tests.poc.context_example.bootstrap import bootstrap

load_dotenv()

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
# This is the Alembic Config object,
# which provides access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# Add your model's MetaData object here.
# For 'autogenerate' support:
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
orm.clear_mappers()
target_metadata = bootstrap()

# Postgres schema name, where the "alembic_version" table will be stored.
SCHEMA_NAME = "poc"

# other values from the config, defined by the needs of env.py,
# Other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def get_engine() -> engine.Engine:
return create_engine(app_config.get_database_url())


def run_migrations_offline():
"""Run migrations in 'offline' mode.

This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.

Calls to context.execute() here emit the given string to the
script output.

"""

context.configure(
url=app_config.get_database_url(),
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online():
"""Run migrations in 'online' mode.

In this scenario we need to create an Engine
and associate a connection with the context.

"""
connectable = get_engine()

with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)

with context.begin_transaction():
context.run_migrations()


if context.is_offline_mode():
run_migrations_offline()
run_migrations_offline(SCHEMA_NAME, target_metadata)
else:
run_migrations_online()
run_migrations_online(SCHEMA_NAME, target_metadata)