Fixes #1927 Also adds tests to ensure index is working as expected, and migration to rename orgs that have names or slugs identical to other orgs except for case before the new case-insensitive index is built.
95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
"""
|
|
Migration 0032 - Case-insensitive org name duplicates
|
|
"""
|
|
|
|
from uuid import UUID
|
|
|
|
from pymongo.errors import DuplicateKeyError
|
|
|
|
from btrixcloud.migrations import BaseMigration
|
|
from btrixcloud.utils import slug_from_name
|
|
|
|
|
|
MIGRATION_VERSION = "0032"
|
|
|
|
|
|
class Migration(BaseMigration):
|
|
"""Migration class."""
|
|
|
|
# pylint: disable=unused-argument
|
|
def __init__(self, mdb, **kwargs):
|
|
super().__init__(mdb, migration_version=MIGRATION_VERSION)
|
|
|
|
async def migrate_up(self):
|
|
"""Perform migration up.
|
|
|
|
Check for case-insensitive duplicate org names and slugs.
|
|
If found, rename org/slug as necessary to avoid duplicates
|
|
regardless of case.
|
|
"""
|
|
orgs_db = self.mdb["organizations"]
|
|
|
|
org_name_set = set()
|
|
org_slug_set = set()
|
|
|
|
cursor = orgs_db.find({})
|
|
async for org_dict in cursor:
|
|
name = org_dict.get("name", "")
|
|
slug = org_dict.get("slug", "")
|
|
|
|
rename_org = False
|
|
|
|
if name.lower() in org_name_set:
|
|
rename_org = True
|
|
else:
|
|
org_name_set.add(name.lower())
|
|
|
|
if slug.lower() in org_slug_set:
|
|
rename_org = True
|
|
else:
|
|
org_slug_set.add(slug.lower())
|
|
|
|
if rename_org:
|
|
await self.update_org_name_and_slug(
|
|
orgs_db, org_name_set, org_slug_set, name, org_dict.get("_id")
|
|
)
|
|
|
|
# pylint: disable=too-many-arguments
|
|
async def update_org_name_and_slug(
|
|
self,
|
|
orgs_db,
|
|
org_name_set: set[str],
|
|
org_slug_set: set[str],
|
|
old_name: str,
|
|
oid: UUID,
|
|
):
|
|
"""Rename org"""
|
|
count = 2
|
|
suffix = f" {count}"
|
|
|
|
while True:
|
|
org_name = f"{old_name}{suffix}"
|
|
org_slug = slug_from_name(org_name)
|
|
|
|
if org_name.lower() in org_name_set or org_slug.lower() in org_slug_set:
|
|
count += 1
|
|
suffix = f" {count}"
|
|
continue
|
|
|
|
try:
|
|
await orgs_db.find_one_and_update(
|
|
{"_id": oid}, {"$set": {"slug": org_slug, "name": org_name}}
|
|
)
|
|
print(
|
|
f"Renamed org {oid} to {org_name} with slug {org_slug}", flush=True
|
|
)
|
|
break
|
|
except DuplicateKeyError:
|
|
# pylint: disable=raise-missing-from
|
|
count += 1
|
|
suffix = f" {count}"
|
|
# pylint: disable=broad-exception-caught
|
|
except Exception as err:
|
|
print(f"Error renaming org {oid}: {err}", flush=True)
|
|
break
|