Skip to content

Commit

Permalink
Merge pull request #7 from DanCardin/dc/schema-create-order
Browse files Browse the repository at this point in the history
  • Loading branch information
DanCardin committed Dec 1, 2022
2 parents c471d10 + d5d34cb commit e2164ad
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 3 deletions.
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 = "sqlalchemy-declarative-extensions"
version = "0.2.2"
version = "0.2.3"
description = "Library to declare additional kinds of objects not natively supported by SqlAlchemy/Alembic."

authors = ["Dan Cardin <ddcardin@gmail.com>"]
Expand Down
3 changes: 2 additions & 1 deletion src/sqlalchemy_declarative_extensions/alembic/grant.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
)
from sqlalchemy_declarative_extensions.role.base import Roles
from sqlalchemy_declarative_extensions.role.compare import RoleOp
from sqlalchemy_declarative_extensions.schema.compare import CreateSchemaOp


@comparators.dispatch_for("schema")
Expand All @@ -33,7 +34,7 @@ def compare_grants(autogen_context: AutogenContext, upgrade_ops: UpgradeOps, _):
# Find the index of the last element of a role ops, which this needs to be after.
last_role_index = -1
for index, op in enumerate(upgrade_ops.ops):
if isinstance(op, RoleOp):
if isinstance(op, (CreateSchemaOp, RoleOp)):
last_role_index = index

# Insert after that point
Expand Down
2 changes: 1 addition & 1 deletion src/sqlalchemy_declarative_extensions/alembic/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def compare_schemas(autogen_context, upgrade_ops, schemas: Schemas):
return

result = schema.compare.compare_schemas(autogen_context.connection, schemas)
upgrade_ops.ops.extend(result)
upgrade_ops.ops[0:0] = result


@renderers.dispatch_for(CreateSchemaOp)
Expand Down
36 changes: 36 additions & 0 deletions tests/examples/test_grant_after_schema/alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[alembic]
script_location = migrations

[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
9 changes: 9 additions & 0 deletions tests/examples/test_grant_after_schema/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import pytest
from pytest_mock_resources import create_postgres_fixture, PostgresConfig

alembic_engine = create_postgres_fixture(scope="function", engine_kwargs={"echo": True})


@pytest.fixture(scope="session")
def pmr_postgres_config():
return PostgresConfig(port=None, ci_port=None)
27 changes: 27 additions & 0 deletions tests/examples/test_grant_after_schema/migrations/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from alembic import context
from sqlalchemy import engine_from_config, pool

# isort: split
from models import Base

from sqlalchemy_declarative_extensions.alembic import register_alembic_events

target_metadata = Base.metadata

register_alembic_events(schemas=True, roles=True, grants=True)


connectable = context.config.attributes.get("connection", None)

if connectable is None:
connectable = engine_from_config(
context.config.get_section(context.config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)

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

with context.begin_transaction():
context.run_migrations()
24 changes: 24 additions & 0 deletions tests/examples/test_grant_after_schema/migrations/script.py.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}


def upgrade():
${upgrades if upgrades else "pass"}


def downgrade():
${downgrades if downgrades else "pass"}
Empty file.
17 changes: 17 additions & 0 deletions tests/examples/test_grant_after_schema/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy_declarative_extensions import declarative_database, Roles, Schemas
from sqlalchemy_declarative_extensions.dialects.postgresql import DefaultGrant

_Base = declarative_base()


@declarative_database
class Base(_Base):
__abstract__ = True

schemas = Schemas().are("foo")
roles = Roles(ignore_unspecified=True).are("bar")
grants = [
DefaultGrant.on_tables_in_schema("foo").grant("select", to="bar"),
]
12 changes: 12 additions & 0 deletions tests/examples/test_grant_after_schema/test_migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pytest_alembic import MigrationContext, tests


def test_apply_autogenerated_revision(alembic_runner: MigrationContext, alembic_engine):
result = alembic_runner.generate_revision(
autogenerate=True, prevent_file_generation=False
)
alembic_runner.migrate_up_one()

# Verify this no longer sees changes to make! Failing here would imply the autogenerate
# is not fully normalizing the difference.
tests.test_model_definitions_match_ddl(alembic_runner)
11 changes: 11 additions & 0 deletions tests/grant/test_alembic.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,14 @@ def test_delete_unspecified_grants(pytester):
@pytest.mark.alembic
def test_grant_disabled(pytester):
successful_test_run(pytester, count=1)


@pytest.mark.alembic
def test_grant_after_schema(pytester):
"""Assert grants are emitted after schema creates.
This is necessary because they may reference schemas which have not
yet been created. (note we cannot test this outside alembic because
PMR auto-creates schemas itself, before the create-all statement.)
"""
successful_test_run(pytester, count=1)

0 comments on commit e2164ad

Please sign in to comment.