From 79f6e8becfa9dbdf7947f3856194882f03636cfb Mon Sep 17 00:00:00 2001 From: Mourits de Beer <31511766+ff137@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:07:17 +0200 Subject: [PATCH] :sparkles: Implement temporary endpoint to patch wallets with old group_id (#750) * :bug: Fix None is not iterable * :art: define UpdateWalletRequestWithGroupId * :sparkles: Implement route to patch wallets with old group_id pattern --- app/models/tenants.py | 8 ++- app/routes/admin/tenants.py | 96 ++++++++++++++++++++++++++++++ app/services/onboarding/tenants.py | 2 +- 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/app/models/tenants.py b/app/models/tenants.py index 5579d81e5..159d73238 100644 --- a/app/models/tenants.py +++ b/app/models/tenants.py @@ -1,7 +1,7 @@ import re from typing import Dict, List, Literal, Optional -from aries_cloudcontroller import CreateWalletRequest +from aries_cloudcontroller import CreateWalletRequest, UpdateWalletRequest from pydantic import BaseModel, Field, field_validator from app.models.trust_registry import TrustRegistryRole @@ -138,6 +138,12 @@ def validate_wallet_label(cls, v): return v +class UpdateWalletRequestWithGroupId(UpdateWalletRequest): + """Adds group_id to the default UpdateWalletRequest body""" + + group_id: Optional[str] = Field(default=None, examples=["some_group_id"]) + + class Tenant(BaseModel): wallet_id: str = Field(..., examples=["545135a4-ecbc-4400-8594-bdb74c51c88d"]) wallet_label: str = Field(..., examples=["Alice"]) diff --git a/app/routes/admin/tenants.py b/app/routes/admin/tenants.py index 69f2fe5e8..c3a0819a0 100644 --- a/app/routes/admin/tenants.py +++ b/app/routes/admin/tenants.py @@ -25,6 +25,7 @@ Tenant, TenantAuth, UpdateTenantRequest, + UpdateWalletRequestWithGroupId, ) from app.models.trust_registry import Actor from app.services.onboarding.tenants import handle_tenant_update, onboard_tenant @@ -329,3 +330,98 @@ async def get_tenants( response = [tenant_from_wallet_record(record) for record in wallets_list] bound_logger.info("Successfully fetched wallets.") return response + + +@router.put("/patch/wallets", include_in_schema=False, response_model=str) +async def patch_wallets( + dry_run: bool = True, + admin_auth: AcaPyAuthVerified = Depends(acapy_auth_tenant_admin), +) -> str: + logger.info("PUT request received: Patch old wallets to use new group_id pattern ") + + async with get_tenant_admin_controller(admin_auth) as admin_controller: + wallets = await handle_acapy_call( + logger=logger, + acapy_call=admin_controller.multitenancy.get_wallets, + ) + wallet_list = wallets.results + + # Filter wallet list to only wallets without the correct way to store group_id + wallets_without_updated_group_id = [ + wallet + for wallet in wallet_list + if wallet.settings.get("wallet.group_id") is None + ] + + num_records_to_change = len(wallets_without_updated_group_id) + logger.info( + "Patching wallet group_id: {} records to be updated", + num_records_to_change, + ) + + records_changed = 0 + for wallet in wallets_without_updated_group_id: + wallet_id = wallet.wallet_id + + # Read the old way of storing group_id attribute: + old_group_id = wallet.group_id + + if not old_group_id: + logger.warning( + "Patching wallet group_id: wallet with id {} has no old group", + wallet_id, + ) + continue + + update_request_body = UpdateWalletRequestWithGroupId(group_id=old_group_id) + + if not dry_run: + updated_wallet = await handle_acapy_call( + logger=logger, + acapy_call=admin_controller.multitenancy.update_wallet, + wallet_id=wallet_id, + body=update_request_body, + ) + + new_group_id = updated_wallet.settings.get("wallet.group_id") + + if old_group_id == new_group_id: + records_changed += 1 + logger.info( + "Successfully updated wallet_id {} to group {}", + wallet_id, + old_group_id, + ) + else: + logger.warning( + "Wallet {} did not update to expected group {}, got {} instead", + wallet_id, + old_group_id, + new_group_id, + ) + + else: + logger.info( + ( + "Patch wallets dry run:\n" + "We will update wallet record: {}\n" + "To be updated to group: {}" + ), + wallet, + old_group_id, + ) + + if not dry_run: + if records_changed == num_records_to_change: + response = "Wallet group_id patching applied successfully." + logger.info(response) + else: + response = ( + f"Only patched {records_changed} of {num_records_to_change} wallets." + ) + logger.warning(response) + else: + response = f"Wallet patching dry run complete, {records_changed} to be updated." + logger.info(response) + + return response diff --git a/app/services/onboarding/tenants.py b/app/services/onboarding/tenants.py index 5e7c6ae8a..ee6226a16 100644 --- a/app/services/onboarding/tenants.py +++ b/app/services/onboarding/tenants.py @@ -30,7 +30,7 @@ async def handle_tenant_update( bound_logger = logger.bind(body={"wallet_id": wallet_id}) bound_logger.bind(body=update_request).info("Handling tenant update") - new_roles = update_request.roles + new_roles = update_request.roles or [] new_label = update_request.wallet_label # See if this wallet belongs to an actor