Release v0.1.8

This commit is contained in:
2026-07-11 16:49:02 +02:00
parent 620fdda2fc
commit 8bf7296bf5
13 changed files with 175 additions and 11 deletions

View File

@@ -0,0 +1,119 @@
"""file connector spaces
Revision ID: 4f5a6b7c8d9e
Revises: 2e3f4a5b6c7d
Create Date: 2026-07-08 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "4f5a6b7c8d9e"
down_revision = "2e3f4a5b6c7d"
branch_labels = None
depends_on = None
def _scope_fk_target(tables: set[str]) -> str:
return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id"
def upgrade() -> None:
inspector = sa.inspect(op.get_bind())
tables = set(inspector.get_table_names())
scope_fk_target = _scope_fk_target(tables)
if "file_connector_spaces" not in tables:
op.create_table(
"file_connector_spaces",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=False),
sa.Column("owner_type", sa.String(length=20), nullable=False),
sa.Column("owner_user_id", sa.String(length=36), nullable=True),
sa.Column("owner_group_id", sa.String(length=36), nullable=True),
sa.Column("label", sa.String(length=255), nullable=False),
sa.Column("connector_profile_id", sa.String(length=255), nullable=False),
sa.Column("provider", sa.String(length=50), nullable=False),
sa.Column("library_id", sa.String(length=255), nullable=True),
sa.Column("remote_path", sa.String(length=1000), nullable=False),
sa.Column("sync_mode", sa.String(length=30), nullable=False),
sa.Column("read_only", sa.Boolean(), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("created_by_user_id", sa.String(length=36), nullable=True),
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("metadata", sa.JSON(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_spaces_created_by_user_id_users"), ondelete="SET NULL"),
sa.ForeignKeyConstraint(["owner_group_id"], ["access_groups.id"], name=op.f("fk_file_connector_spaces_owner_group_id_groups"), ondelete="SET NULL"),
sa.ForeignKeyConstraint(["owner_user_id"], ["access_users.id"], name=op.f("fk_file_connector_spaces_owner_user_id_users"), ondelete="SET NULL"),
sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_spaces_tenant_id_scopes"), ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_spaces")),
)
inspector = sa.inspect(op.get_bind())
indexes = {item["name"] for item in inspector.get_indexes("file_connector_spaces")}
for column in (
"tenant_id",
"owner_type",
"owner_user_id",
"owner_group_id",
"connector_profile_id",
"provider",
"is_active",
"created_by_user_id",
"deleted_at",
):
name = op.f(f"ix_file_connector_spaces_{column}")
if name not in indexes:
op.create_index(name, "file_connector_spaces", [column], unique=False)
if "ix_file_connector_spaces_owner" not in indexes:
op.create_index(
"ix_file_connector_spaces_owner",
"file_connector_spaces",
["tenant_id", "owner_type", "owner_user_id", "owner_group_id"],
unique=False,
)
if "uq_file_connector_spaces_active_user_label" not in indexes:
op.create_index(
"uq_file_connector_spaces_active_user_label",
"file_connector_spaces",
["tenant_id", "owner_user_id", "label"],
unique=True,
sqlite_where=sa.text("owner_type = 'user' AND deleted_at IS NULL"),
postgresql_where=sa.text("owner_type = 'user' AND deleted_at IS NULL"),
)
if "uq_file_connector_spaces_active_group_label" not in indexes:
op.create_index(
"uq_file_connector_spaces_active_group_label",
"file_connector_spaces",
["tenant_id", "owner_group_id", "label"],
unique=True,
sqlite_where=sa.text("owner_type = 'group' AND deleted_at IS NULL"),
postgresql_where=sa.text("owner_type = 'group' AND deleted_at IS NULL"),
)
def downgrade() -> None:
inspector = sa.inspect(op.get_bind())
if "file_connector_spaces" not in inspector.get_table_names():
return
indexes = {item["name"] for item in inspector.get_indexes("file_connector_spaces")}
for name in (
"uq_file_connector_spaces_active_group_label",
"uq_file_connector_spaces_active_user_label",
"ix_file_connector_spaces_owner",
op.f("ix_file_connector_spaces_deleted_at"),
op.f("ix_file_connector_spaces_created_by_user_id"),
op.f("ix_file_connector_spaces_is_active"),
op.f("ix_file_connector_spaces_provider"),
op.f("ix_file_connector_spaces_connector_profile_id"),
op.f("ix_file_connector_spaces_owner_group_id"),
op.f("ix_file_connector_spaces_owner_user_id"),
op.f("ix_file_connector_spaces_owner_type"),
op.f("ix_file_connector_spaces_tenant_id"),
):
if name in indexes:
op.drop_index(name, table_name="file_connector_spaces")
op.drop_table("file_connector_spaces")

View File

@@ -0,0 +1,94 @@
"""file connector profiles
Revision ID: 5a6b7c8d9e0f
Revises: 4f5a6b7c8d9e
Create Date: 2026-07-08 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "5a6b7c8d9e0f"
down_revision = "4f5a6b7c8d9e"
branch_labels = None
depends_on = None
def _scope_fk_target(inspector) -> str:
tables = set(inspector.get_table_names())
return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id"
def upgrade() -> None:
inspector = sa.inspect(op.get_bind())
scope_fk_target = _scope_fk_target(inspector)
if "file_connector_profiles" not in inspector.get_table_names():
op.create_table(
"file_connector_profiles",
sa.Column("id", sa.String(length=255), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=True),
sa.Column("scope_type", sa.String(length=20), nullable=False),
sa.Column("scope_id", sa.String(length=36), nullable=True),
sa.Column("label", sa.String(length=255), nullable=False),
sa.Column("provider", sa.String(length=50), nullable=False),
sa.Column("endpoint_url", sa.String(length=1000), nullable=True),
sa.Column("base_path", sa.String(length=1000), nullable=True),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("credential_mode", sa.String(length=30), nullable=False),
sa.Column("username", sa.String(length=320), nullable=True),
sa.Column("password_encrypted", sa.Text(), nullable=True),
sa.Column("token_encrypted", sa.Text(), nullable=True),
sa.Column("password_env", sa.String(length=255), nullable=True),
sa.Column("token_env", sa.String(length=255), nullable=True),
sa.Column("secret_ref", sa.String(length=1000), nullable=True),
sa.Column("capabilities", sa.JSON(), nullable=True),
sa.Column("policy", sa.JSON(), nullable=True),
sa.Column("metadata", sa.JSON(), nullable=True),
sa.Column("created_by_user_id", sa.String(length=36), nullable=True),
sa.Column("updated_by_user_id", sa.String(length=36), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_profiles_created_by_user_id_users"), ondelete="SET NULL"),
sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_profiles_tenant_id_scopes"), ondelete="CASCADE"),
sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_profiles_updated_by_user_id_users"), ondelete="SET NULL"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_profiles")),
)
inspector = sa.inspect(op.get_bind())
indexes = {item["name"] for item in inspector.get_indexes("file_connector_profiles")}
for column in (
"tenant_id",
"scope_type",
"scope_id",
"provider",
"enabled",
"created_by_user_id",
"updated_by_user_id",
):
name = op.f(f"ix_file_connector_profiles_{column}")
if name not in indexes:
op.create_index(name, "file_connector_profiles", [column], unique=False)
if "ix_file_connector_profiles_scope" not in indexes:
op.create_index("ix_file_connector_profiles_scope", "file_connector_profiles", ["scope_type", "scope_id"], unique=False)
def downgrade() -> None:
inspector = sa.inspect(op.get_bind())
if "file_connector_profiles" not in inspector.get_table_names():
return
indexes = {item["name"] for item in inspector.get_indexes("file_connector_profiles")}
for name in (
"ix_file_connector_profiles_scope",
op.f("ix_file_connector_profiles_updated_by_user_id"),
op.f("ix_file_connector_profiles_created_by_user_id"),
op.f("ix_file_connector_profiles_enabled"),
op.f("ix_file_connector_profiles_provider"),
op.f("ix_file_connector_profiles_scope_id"),
op.f("ix_file_connector_profiles_scope_type"),
op.f("ix_file_connector_profiles_tenant_id"),
):
if name in indexes:
op.drop_index(name, table_name="file_connector_profiles")
op.drop_table("file_connector_profiles")

View File

@@ -0,0 +1,111 @@
"""file connector credentials
Revision ID: 6b7c8d9e0f1a
Revises: 5a6b7c8d9e0f
Create Date: 2026-07-08 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "6b7c8d9e0f1a"
down_revision = "5a6b7c8d9e0f"
branch_labels = None
depends_on = None
def _scope_fk_target(table_names: set[str]) -> str:
return "core_scopes.id" if "core_scopes" in table_names else "tenancy_tenants.id"
def upgrade() -> None:
inspector = sa.inspect(op.get_bind())
table_names = set(inspector.get_table_names())
scope_fk_target = _scope_fk_target(table_names)
if "file_connector_profiles" in table_names:
columns = {item["name"] for item in inspector.get_columns("file_connector_profiles")}
if "credential_profile_id" not in columns:
op.add_column("file_connector_profiles", sa.Column("credential_profile_id", sa.String(length=255), nullable=True))
indexes = {item["name"] for item in inspector.get_indexes("file_connector_profiles")}
index_name = op.f("ix_file_connector_profiles_credential_profile_id")
if index_name not in indexes:
op.create_index(index_name, "file_connector_profiles", ["credential_profile_id"], unique=False)
if "file_connector_credentials" not in table_names:
op.create_table(
"file_connector_credentials",
sa.Column("id", sa.String(length=255), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=True),
sa.Column("scope_type", sa.String(length=20), nullable=False),
sa.Column("scope_id", sa.String(length=36), nullable=True),
sa.Column("label", sa.String(length=255), nullable=False),
sa.Column("provider", sa.String(length=50), nullable=True),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("credential_mode", sa.String(length=30), nullable=False),
sa.Column("username", sa.String(length=320), nullable=True),
sa.Column("password_encrypted", sa.Text(), nullable=True),
sa.Column("token_encrypted", sa.Text(), nullable=True),
sa.Column("password_env", sa.String(length=255), nullable=True),
sa.Column("token_env", sa.String(length=255), nullable=True),
sa.Column("secret_ref", sa.String(length=1000), nullable=True),
sa.Column("policy", sa.JSON(), nullable=True),
sa.Column("metadata", sa.JSON(), nullable=True),
sa.Column("created_by_user_id", sa.String(length=36), nullable=True),
sa.Column("updated_by_user_id", sa.String(length=36), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_credentials_created_by_user_id_users"), ondelete="SET NULL"),
sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_credentials_tenant_id_scopes"), ondelete="CASCADE"),
sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_credentials_updated_by_user_id_users"), ondelete="SET NULL"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_credentials")),
)
inspector = sa.inspect(op.get_bind())
indexes = {item["name"] for item in inspector.get_indexes("file_connector_credentials")}
for column in (
"tenant_id",
"scope_type",
"scope_id",
"provider",
"enabled",
"created_by_user_id",
"updated_by_user_id",
):
name = op.f(f"ix_file_connector_credentials_{column}")
if name not in indexes:
op.create_index(name, "file_connector_credentials", [column], unique=False)
if "ix_file_connector_credentials_scope" not in indexes:
op.create_index("ix_file_connector_credentials_scope", "file_connector_credentials", ["scope_type", "scope_id"], unique=False)
def downgrade() -> None:
inspector = sa.inspect(op.get_bind())
if "file_connector_credentials" in inspector.get_table_names():
indexes = {item["name"] for item in inspector.get_indexes("file_connector_credentials")}
for name in (
"ix_file_connector_credentials_scope",
op.f("ix_file_connector_credentials_updated_by_user_id"),
op.f("ix_file_connector_credentials_created_by_user_id"),
op.f("ix_file_connector_credentials_enabled"),
op.f("ix_file_connector_credentials_provider"),
op.f("ix_file_connector_credentials_scope_id"),
op.f("ix_file_connector_credentials_scope_type"),
op.f("ix_file_connector_credentials_tenant_id"),
):
if name in indexes:
op.drop_index(name, table_name="file_connector_credentials")
op.drop_table("file_connector_credentials")
inspector = sa.inspect(op.get_bind())
if "file_connector_profiles" not in inspector.get_table_names():
return
profile_indexes = {item["name"] for item in inspector.get_indexes("file_connector_profiles")}
profile_index_name = op.f("ix_file_connector_profiles_credential_profile_id")
if profile_index_name in profile_indexes:
op.drop_index(profile_index_name, table_name="file_connector_profiles")
profile_columns = {item["name"] for item in inspector.get_columns("file_connector_profiles")}
if "credential_profile_id" in profile_columns:
op.drop_column("file_connector_profiles", "credential_profile_id")

View File

@@ -0,0 +1,77 @@
"""file connector policies
Revision ID: a7b8c9d0e1f3
Revises: 6b7c8d9e0f1a
Create Date: 2026-07-08 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "a7b8c9d0e1f3"
down_revision = "6b7c8d9e0f1a"
branch_labels = None
depends_on = None
def _scope_fk_target(inspector) -> str:
tables = set(inspector.get_table_names())
return "core_scopes.id" if "core_scopes" in tables else "tenancy_tenants.id"
def upgrade() -> None:
inspector = sa.inspect(op.get_bind())
scope_fk_target = _scope_fk_target(inspector)
if "file_connector_policies" not in inspector.get_table_names():
op.create_table(
"file_connector_policies",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=True),
sa.Column("scope_type", sa.String(length=20), nullable=False),
sa.Column("scope_id", sa.String(length=36), nullable=True),
sa.Column("policy", sa.JSON(), nullable=False),
sa.Column("created_by_user_id", sa.String(length=36), nullable=True),
sa.Column("updated_by_user_id", sa.String(length=36), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["created_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_policies_created_by_user_id_users"), ondelete="SET NULL"),
sa.ForeignKeyConstraint(["tenant_id"], [scope_fk_target], name=op.f("fk_file_connector_policies_tenant_id_scopes"), ondelete="CASCADE"),
sa.ForeignKeyConstraint(["updated_by_user_id"], ["access_users.id"], name=op.f("fk_file_connector_policies_updated_by_user_id_users"), ondelete="SET NULL"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_file_connector_policies")),
sa.UniqueConstraint("tenant_id", "scope_type", "scope_id", name="uq_file_connector_policies_scope"),
)
inspector = sa.inspect(op.get_bind())
indexes = {item["name"] for item in inspector.get_indexes("file_connector_policies")}
for column in (
"tenant_id",
"scope_type",
"scope_id",
"created_by_user_id",
"updated_by_user_id",
):
name = op.f(f"ix_file_connector_policies_{column}")
if name not in indexes:
op.create_index(name, "file_connector_policies", [column], unique=False)
if "ix_file_connector_policies_scope" not in indexes:
op.create_index("ix_file_connector_policies_scope", "file_connector_policies", ["scope_type", "scope_id"], unique=False)
def downgrade() -> None:
inspector = sa.inspect(op.get_bind())
if "file_connector_policies" not in inspector.get_table_names():
return
indexes = {item["name"] for item in inspector.get_indexes("file_connector_policies")}
for name in (
"ix_file_connector_policies_scope",
op.f("ix_file_connector_policies_updated_by_user_id"),
op.f("ix_file_connector_policies_created_by_user_id"),
op.f("ix_file_connector_policies_scope_id"),
op.f("ix_file_connector_policies_scope_type"),
op.f("ix_file_connector_policies_tenant_id"),
):
if name in indexes:
op.drop_index(name, table_name="file_connector_policies")
op.drop_table("file_connector_policies")