Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ launch: ## run standalone app without tty container
docker compose run sh -c "python multidirectory.py --migrate && python ."

rerun_last_migration:
docker exec -it multidirectory_api sh -c "alembic downgrade -1; python multidirectory.py --migrate;"
docker exec -it multidirectory_api sh -c "python multidirectory.py --downgrade -1; python multidirectory.py --migrate;"

rerun_all_migrations:
docker exec -it multidirectory_api sh -c "alembic downgrade base; python multidirectory.py --migrate;"
docker exec -it multidirectory_api sh -c "python multidirectory.py --downgrade base; python multidirectory.py --migrate;"

down: ## shutdown services
docker compose -f docker-compose.test.yml down --remove-orphans
Expand Down
48 changes: 48 additions & 0 deletions app/alembic/versions/21a957c18dce_create_directory_name_indexes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Create Directory.name indexes.

Revision ID: 21a957c18dce
Revises: 1b71cafba681
Create Date: 2026-04-10 11:15:22.133564

"""

import sqlalchemy as sa
from alembic import op
from dishka import AsyncContainer

# revision identifiers, used by Alembic.
revision: None | str = "21a957c18dce"
down_revision: None | str = "1b71cafba681"
branch_labels: None | list[str] = None
depends_on: None | list[str] = None


def upgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Upgrade."""
op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm")
op.create_index(
"idx_Directory_name_hash",
"Directory",
["name"],
unique=False,
postgresql_using="hash",
)
op.create_index(
"idx_Directory_name_gin_trgm",
"Directory",
[sa.literal_column("name gin_trgm_ops")],
unique=False,
postgresql_using="gin",
)


def downgrade(container: AsyncContainer) -> None: # noqa: ARG001
"""Downgrade."""
op.drop_index(
"idx_Directory_name_gin_trgm",
table_name="Directory",
)
op.drop_index(
"idx_Directory_name_hash",
table_name="Directory",
)
5 changes: 5 additions & 0 deletions app/api/ldap_schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
DomainErrorTranslator,
)
from enums import DomainCodes
from ldap_protocol.identity.exceptions import UnauthorizedError
from ldap_protocol.ldap_schema.exceptions import (
AttributeTypeAlreadyExistsError,
AttributeTypeCantModifyError,
Expand All @@ -40,6 +41,10 @@


error_map: ERROR_MAP_TYPE = {
UnauthorizedError: rule(
status=status.HTTP_401_UNAUTHORIZED,
translator=translator,
),
AttributeTypeAlreadyExistsError: rule(
status=status.HTTP_400_BAD_REQUEST,
translator=translator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def _convert_model_to_dto(
)
return AttributeTypeDTO[int](
oid=attr_type.oid,
name=ldap_display_name,
name=attr_type.name,
ldap_display_name=ldap_display_name,
syntax=attr_type.syntax,
single_value=attr_type.single_value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ async def get_paginator(
filters = [qa(EntityType.name) == EntityTypeNames.ATTRIBUTE_TYPE]

if params.query:
filters.append(qa(Directory.name).like(f"%{params.query}%"))
filters.append(qa(Directory.name).ilike(f"%{params.query}%"))

query = (
select(Directory)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async def get_paginator(
filters = [qa(EntityType.name) == EntityTypeNames.OBJECT_CLASS]

if params.query:
filters.append(qa(Directory.name).like(f"%{params.query}%"))
filters.append(qa(Directory.name).ilike(f"%{params.query}%"))

query = (
select(Directory)
Expand Down
7 changes: 7 additions & 0 deletions app/multidirectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ async def migrate_dns_factory(settings: Settings) -> None:
action="store_true",
help="Make migrations",
)
group.add_argument(
"--downgrade",
metavar="REV",
help="Downgrade database to revision",
)
group.add_argument(
"--migrate_dns",
action="store_true",
Expand Down Expand Up @@ -397,5 +402,7 @@ async def migrate_dns_factory(settings: Settings) -> None:
dump_acme_cert()
elif args.migrate:
command.upgrade(Config("alembic.ini"), "head")
elif args.downgrade:
command.downgrade(Config("alembic.ini"), args.downgrade)
elif args.migrate_dns:
dns_migration(settings=settings)
2 changes: 2 additions & 0 deletions app/repo/pg/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ def _compile_create_uc(
text("array_lowercase(path)"),
postgresql_using="hash",
),
Index("idx_Directory_name_hash", "name", postgresql_using="hash"),
Index("idx_Directory_name_gin_trgm", "name", postgresql_using="gin"),
)

groups_table = Table(
Expand Down
2 changes: 1 addition & 1 deletion interface
14 changes: 14 additions & 0 deletions tests/test_api/test_ldap_schema/test_attribute_type_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ async def test_get_list_attribute_types_with_pagination(
assert len(response.json().get("items")) == page_size


@pytest.mark.asyncio
async def test_attribute_type_pagination_search_is_case_insensitive(
http_client: AsyncClient,
) -> None:
"""Test case-insensitive search for attribute type pagination."""
response = await http_client.get(
"/schema/attribute_types",
params={"page_number": 1, "page_size": 50, "query": "PoSiXeMaIl"},
)
assert response.status_code == status.HTTP_200_OK
items = response.json().get("items", [])
assert any(item.get("name") == "posixEmail" for item in items)


@pytest.mark.asyncio
async def test_modify_one_attribute_type_raise_404(
http_client: AsyncClient,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_api/test_ldap_schema/test_object_class_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ async def test_get_list_object_classes_with_pagination(
assert len(response.json().get("items")) == page_size


@pytest.mark.asyncio
async def test_object_class_pagination_search_is_case_insensitive(
http_client: AsyncClient,
) -> None:
"""Test case-insensitive search for object class pagination."""
response = await http_client.get(
"/schema/object_classes",
params={"page_number": 1, "page_size": 50, "query": "InEtOrGpErSoN"},
)
assert response.status_code == status.HTTP_200_OK
items = response.json().get("items", [])
assert any(item.get("name") == "inetOrgPerson" for item in items)


@pytest.mark.parametrize(
"dataset",
test_modify_one_object_class_dataset,
Expand Down
Loading