diff --git a/backend/moonstreamapi/admin/cli.py b/backend/moonstreamapi/admin/cli.py
index ddc274ba..a765e820 100644
--- a/backend/moonstreamapi/admin/cli.py
+++ b/backend/moonstreamapi/admin/cli.py
@@ -39,6 +39,35 @@ def parse_boolean_arg(raw_arg: Optional[str]) -> Optional[bool]:
return False
+def migration_run(step_map, command, step, step_order):
+ if step is None:
+ # run all steps
+
+ if step_order is None:
+ raise ValueError(
+ f"step_order is required when running all steps for {command}"
+ )
+
+ if command == "downgrade":
+ step_order = reversed(step_order)
+
+ for step in step_order:
+ logger.info(
+ f"Starting step {step}: {step_map[command][step]['description']}"
+ )
+ migration_function = step_map[command][step]["action"]
+ if callable(migration_function):
+ migration_function()
+ elif step in step_map[command]:
+ logger.info(f"Starting step {step}: {step_map[command][step]['description']}")
+ migration_function = step_map[command][step]["action"]
+ if callable(migration_function):
+ migration_function()
+ else:
+ logger.error(f"Step {step} not found in {command}")
+ logger.info(f"Available steps: {step_map[command].keys()}")
+
+
def migrations_list(args: argparse.Namespace) -> None:
migrations_overview = f"""
@@ -55,6 +84,9 @@ description: {generate_entity_subscriptions.__doc__}
steps:
- step 1: generate_entity_subscriptions_from_brood_resources - Generate entity subscriptions from brood resources
- step 2: update_dashboards_connection - Update dashboards connection
+- id: 20230501
+name: fix_duplicates_keys_in_entity_subscription
+description: Fix entity duplicates keys for all subscriptions introduced in 20230213
"""
logger.info(entity_migration_overview)
@@ -85,8 +117,31 @@ def migrations_run(args: argparse.Namespace) -> None:
web3_session = yield_web3_provider()
db_session = SessionLocal()
try:
- if args.id == 20230213:
+ if args.id == 20230501:
+ # fix entity duplicates keys for all subscriptions introduced in 20230213
+
+ step_order = ["fix_duplicates_keys_in_entity_subscription"]
step_map: Dict[str, Dict[str, Any]] = {
+ "upgrade": {
+ "fix_duplicates_keys_in_entity_subscription": {
+ "action": generate_entity_subscriptions.fix_duplicates_keys_in_entity_subscription,
+ "description": "Fix entity duplicates keys for all subscriptions introduced in 20230213",
+ },
+ },
+ "downgrade": {},
+ }
+ if args.command not in ["upgrade", "downgrade"]:
+ logger.info("Wrong command. Please use upgrade or downgrade")
+ step = args.step
+
+ migration_run(step_map, args.command, step, step_order)
+
+ if args.id == 20230213:
+ step_order = [
+ "generate_entity_subscriptions_from_brood_resources",
+ "update_dashboards_connection",
+ ]
+ step_map = {
"upgrade": {
"generate_entity_subscriptions_from_brood_resources": {
"action": generate_entity_subscriptions.generate_entity_subscriptions_from_brood_resources,
@@ -112,26 +167,7 @@ def migrations_run(args: argparse.Namespace) -> None:
logger.info("Wrong command. Please use upgrade or downgrade")
step = args.step
- if step is None:
- # run all steps
-
- for step in step_map[args.command]:
- logger.info(
- f"Starting step {step}: {step_map[args.command][step]['description']}"
- )
- migration_function = step_map[args.command][step]["action"]
- if callable(migration_function):
- migration_function()
- elif step in step_map[args.command]:
- logger.info(
- f"Starting step {step}: {step_map[args.command][step]['description']}"
- )
- migration_function = step_map[args.command][step]["action"]
- if callable(migration_function):
- migration_function()
- else:
- logger.info(f"Step {step} does not exist")
- logger.info(f"Available steps: {step_map[args.command].keys()}")
+ migration_run(step_map, args.command, step, step_order)
elif args.id == 20211101:
logger.info("Starting update of subscriptions in Brood resource...")
diff --git a/backend/moonstreamapi/admin/migrations/generate_entity_subscriptions.py b/backend/moonstreamapi/admin/migrations/generate_entity_subscriptions.py
index 45d68a1f..5b73c7bc 100644
--- a/backend/moonstreamapi/admin/migrations/generate_entity_subscriptions.py
+++ b/backend/moonstreamapi/admin/migrations/generate_entity_subscriptions.py
@@ -716,3 +716,140 @@ def restore_dashboard_state():
traceback.print_exc()
logger.error(f"Failed to update dashboard: {str(e)}")
continue
+
+
+def fix_duplicates_keys_in_entity_subscription():
+ """
+ Migration generate_entity_subscriptions_from_brood_resources
+ create duplicates keys "secondary_fields" subscriptions secondary_fields
+ "secondary_fields": {
+ "secondary_fields": {
+ ...
+ }
+ }
+ That function will remove internal secondary_fields and flat all keys to one level upper
+ """
+
+ # get all entities from subscriptions
+
+ subscriptions: BugoutResources = bc.list_resources(
+ token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
+ params={"type": BUGOUT_RESOURCE_TYPE_ENTITY_SUBSCRIPTION},
+ timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
+ )
+
+ # get collection ids from that resources
+
+ collection_id_user_id_mappig = {}
+
+ for subscription in subscriptions.resources:
+ if "collection_id" in subscription.resource_data:
+ if (
+ subscription.resource_data["collection_id"]
+ not in collection_id_user_id_mappig
+ ):
+ collection_id_user_id_mappig[
+ subscription.resource_data["collection_id"]
+ ] = subscription.resource_data["user_id"]
+ else:
+ raise Exception(
+ f"Duplicate collection_id {subscription.resource_data['collection_id']} in subscriptions"
+ )
+ # go through all collections and fix entities.
+ # Will creating one new entity with same data but without "type:subscription" in required_fields
+
+ for collection_id, user_id in collection_id_user_id_mappig.items():
+ # get collection entities
+
+ collection_entities = ec.search_entities(
+ token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
+ collection_id=collection_id,
+ required_field=[f"type:subscription"],
+ limit=1000,
+ )
+
+ logger.info(
+ f"Amount of entities in user: {user_id} collection {collection_id}: {len(collection_entities.entities)}"
+ )
+
+ for entity in collection_entities.entities:
+ # get entity data
+
+ if entity.secondary_fields is None:
+ continue
+
+ secondary_fields = entity.secondary_fields
+
+ if "secondary_fields" not in secondary_fields:
+ continue
+
+ secondary_fields = secondary_fields["secondary_fields"]
+
+ # get entity id
+
+ entity_id = entity.entity_id
+
+ # get entity type
+
+ entity_type = None
+
+ # extract required fields
+ for entity_required_field in entity.required_fields:
+ if "type" in entity_required_field:
+ entity_type = entity_required_field["type"]
+ if entity_type != "subscription":
+ continue
+
+ # Create new entity with same data but without "type:subscription" in required_fields
+
+ try:
+ new_required_fields = [
+ entity_field
+ for entity_field in entity.required_fields
+ if "type" not in entity_field
+ ]
+ new_required_fields.append(
+ {"type": "copy_of_malformed_entity_20230213"}
+ )
+ new_required_fields.append({"entity_id": str(entity_id)})
+
+ new_entity = ec.add_entity(
+ token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
+ collection_id=collection_id,
+ blockchain=entity.blockchain,
+ address=entity.address,
+ name=entity.name,
+ required_fields=new_required_fields,
+ secondary_fields=entity.secondary_fields,
+ )
+ logger.info(
+ f"Entity {new_entity.entity_id} created successfully for collection {collection_id}"
+ )
+
+ except Exception as e:
+ logger.error(
+ f"Failed to create entity {entity_id} for collection {collection_id}: {str(e)}, user_id: {user_id}"
+ )
+ continue
+
+ # Update old entity without secondary_fields duplicate
+
+ try:
+ ec.update_entity(
+ token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
+ collection_id=collection_id,
+ entity_id=entity_id,
+ blockchain=entity.blockchain,
+ address=entity.address,
+ name=entity.name,
+ required_fields=entity.required_fields,
+ secondary_fields=secondary_fields,
+ )
+ logger.info(
+ f"Entity {entity_id} updated successfully for collection {collection_id}"
+ )
+
+ except Exception as e:
+ logger.error(
+ f"Failed to update entity {entity_id} for collection {collection_id}: {str(e)}, user_id: {user_id}"
+ )
diff --git a/backend/setup.py b/backend/setup.py
index 136996cb..dc940b79 100644
--- a/backend/setup.py
+++ b/backend/setup.py
@@ -14,7 +14,7 @@ setup(
"appdirs",
"boto3",
"bugout>=0.1.19",
- "moonstream-entity>=0.0.4",
+ "moonstream-entity>=0.0.5",
"fastapi",
"moonstreamdb>=0.3.3",
"humbug",
diff --git a/crawlers/mooncrawl/setup.py b/crawlers/mooncrawl/setup.py
index c7eb9437..96271e8d 100644
--- a/crawlers/mooncrawl/setup.py
+++ b/crawlers/mooncrawl/setup.py
@@ -39,7 +39,7 @@ setup(
"fastapi",
"moonstreamdb>=0.3.3",
"moonstream>=0.1.1",
- "moonstream-entity>=0.0.4",
+ "moonstream-entity>=0.0.5",
"moonworm[moonstream]>=0.6.2",
"humbug",
"pydantic==1.9.2",
diff --git a/frontend/src/components/SubscriptionCard.js b/frontend/src/components/SubscriptionCard.js
index 3dd5fbf6..6e7e1b82 100644
--- a/frontend/src/components/SubscriptionCard.js
+++ b/frontend/src/components/SubscriptionCard.js
@@ -308,21 +308,20 @@ const SubscriptionCard = ({ subscription, isDesktopView, iconLink }) => {
{subscription.abi ? (