kopia lustrzana https://github.com/bugout-dev/moonstream
487 wiersze
17 KiB
Python
487 wiersze
17 KiB
Python
"""
|
|
Moonstream CLI
|
|
"""
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import os
|
|
from posix import listdir
|
|
from typing import Optional, List, Dict, Any, Union, Callable
|
|
|
|
from sqlalchemy.orm import with_expression
|
|
|
|
from moonstreamdb.db import SessionLocal
|
|
|
|
from ..settings import BUGOUT_BROOD_URL, BUGOUT_SPIRE_URL, MOONSTREAM_APPLICATION_ID
|
|
from ..web3_provider import yield_web3_provider
|
|
|
|
from . import subscription_types, subscriptions, moonworm_tasks
|
|
from .migrations import (
|
|
checksum_address,
|
|
update_dashboard_subscription_key,
|
|
generate_entity_subscriptions,
|
|
)
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MIGRATIONS_FOLDER = "./moonstreamapi/admin/migrations"
|
|
|
|
|
|
def parse_boolean_arg(raw_arg: Optional[str]) -> Optional[bool]:
|
|
if raw_arg is None:
|
|
return None
|
|
|
|
raw_arg_lower = raw_arg.lower()
|
|
if raw_arg_lower in ["t", "true", "1", "y", "yes"]:
|
|
return True
|
|
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"""
|
|
|
|
- id: 20211101
|
|
name: {checksum_address.__name__}
|
|
description: {checksum_address.__doc__}
|
|
"""
|
|
logger.info(migrations_overview)
|
|
|
|
entity_migration_overview = f"""
|
|
- id: 20230213
|
|
name: {generate_entity_subscriptions.__name__}
|
|
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)
|
|
|
|
json_migrations_oreview = "Available migrations files."
|
|
for file in os.listdir(MIGRATIONS_FOLDER):
|
|
if file.endswith(".json"):
|
|
with open(os.path.join(MIGRATIONS_FOLDER, file), "r") as migration_file:
|
|
json_migrations_oreview += "\n\n"
|
|
|
|
migration = json.load(migration_file)
|
|
json_migrations_oreview = "\n".join(
|
|
(json_migrations_oreview, f"- id: {migration['id']}")
|
|
)
|
|
json_migrations_oreview = "\n".join(
|
|
(json_migrations_oreview, f" file: {file}")
|
|
)
|
|
json_migrations_oreview = "\n".join(
|
|
(
|
|
json_migrations_oreview,
|
|
f" description: {migration['description']}",
|
|
)
|
|
)
|
|
|
|
logger.info(json_migrations_oreview)
|
|
|
|
|
|
def migrations_run(args: argparse.Namespace) -> None:
|
|
web3_session = yield_web3_provider()
|
|
db_session = SessionLocal()
|
|
try:
|
|
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,
|
|
"description": "Generate entity subscriptions from brood resources",
|
|
},
|
|
"update_dashboards_connection": {
|
|
"action": generate_entity_subscriptions.update_dashboards_connection,
|
|
"description": "Update dashboards connection",
|
|
},
|
|
},
|
|
"downgrade": {
|
|
"generate_entity_subscriptions_from_brood_resources": {
|
|
"action": generate_entity_subscriptions.delete_generated_entity_subscriptions_from_brood_resources,
|
|
"description": "Delete generated entity subscriptions from brood resources",
|
|
},
|
|
"update_dashboards_connection": {
|
|
"action": generate_entity_subscriptions.restore_dashboard_state,
|
|
"description": "Restore dashboard state",
|
|
},
|
|
},
|
|
}
|
|
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)
|
|
|
|
elif args.id == 20211101:
|
|
logger.info("Starting update of subscriptions in Brood resource...")
|
|
checksum_address.checksum_all_subscription_addresses(web3_session)
|
|
logger.info("Starting update of ethereum_labels in database...")
|
|
checksum_address.checksum_all_labels_addresses(db_session, web3_session)
|
|
elif args.id == 20211202:
|
|
update_dashboard_subscription_key.update_dashboard_resources_key()
|
|
elif args.id == 20211108:
|
|
drop_keys = []
|
|
|
|
if args.file is not None:
|
|
with open(args.file) as migration_json_file:
|
|
migration_json = json.load(migration_json_file)
|
|
|
|
if (
|
|
"match" not in migration_json
|
|
or "update" not in migration_json[args.command]
|
|
or "description" not in migration_json
|
|
):
|
|
print(
|
|
'Migration file plan have incorrect format require specified {"match": {},"description": "","upgrade": { "update": {}, "drop_keys": [] }, "downgrade": { "update": {}, "drop_keys": [] }}'
|
|
)
|
|
return
|
|
|
|
match = migration_json["match"]
|
|
description = migration_json["description"]
|
|
update = migration_json[args.command]["update"]
|
|
file = args.file
|
|
|
|
if "drop_keys" in migration_json[args.command]:
|
|
drop_keys = migration_json[args.command]["drop_keys"]
|
|
|
|
subscriptions.migrate_subscriptions(
|
|
match=match,
|
|
descriptions=description,
|
|
update=update,
|
|
drop_keys=drop_keys,
|
|
file=file,
|
|
)
|
|
|
|
else:
|
|
print("Specified ID or migration FILE is required.")
|
|
return
|
|
finally:
|
|
db_session.close()
|
|
|
|
|
|
def moonworm_tasks_list_handler(args: argparse.Namespace) -> None:
|
|
moonworm_tasks.get_list_of_addresses()
|
|
|
|
|
|
def moonworm_tasks_add_subscription_handler(args: argparse.Namespace) -> None:
|
|
moonworm_tasks.add_subscription(args.id)
|
|
|
|
|
|
def main() -> None:
|
|
cli_description = f"""Moonstream Admin CLI
|
|
|
|
Please make sure that the following environment variables are set in your environment and exported to
|
|
subprocesses:
|
|
1. MOONSTREAM_APPLICATION_ID
|
|
2. MOONSTREAM_ADMIN_ACCESS_TOKEN
|
|
|
|
Current Moonstream application ID: {MOONSTREAM_APPLICATION_ID}
|
|
|
|
This CLI is configured to work with the following API URLs:
|
|
- Brood: {BUGOUT_BROOD_URL} (override by setting BUGOUT_BROOD_URL environment variable)
|
|
- Spire: {BUGOUT_SPIRE_URL} (override by setting BUGOUT_SPIRE_URL environment variable)
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description=cli_description,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
)
|
|
parser.set_defaults(func=lambda _: parser.print_help())
|
|
subcommands = parser.add_subparsers(description="Moonstream commands")
|
|
|
|
parser_subscription_types = subcommands.add_parser(
|
|
"subtypes", description="Manage Moonstream subscription types"
|
|
)
|
|
parser_subscription_types.set_defaults(
|
|
func=lambda _: parser_subscription_types.print_help()
|
|
)
|
|
subcommands_subscription_types = parser_subscription_types.add_subparsers()
|
|
|
|
parser_subscription_types_create = subcommands_subscription_types.add_parser(
|
|
"create", description="Create subscription type"
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"-i", "--id", required=True, type=str, help="ID for the subscription type"
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"-n",
|
|
"--name",
|
|
required=True,
|
|
type=str,
|
|
help="Human-friendly name for the subscription type",
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"-d",
|
|
"--description",
|
|
required=True,
|
|
type=str,
|
|
help="Detailed description of the subscription type",
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"-c",
|
|
"--choices",
|
|
nargs="*",
|
|
help="Available subscription options for from builder.",
|
|
required=True,
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"--icon",
|
|
required=True,
|
|
help="URL to the icon representing this subscription type",
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"--stripe-product-id",
|
|
required=False,
|
|
default=None,
|
|
type=str,
|
|
help="Stripe product id",
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"--stripe-price-id",
|
|
required=False,
|
|
default=None,
|
|
type=str,
|
|
help="Stripe price id",
|
|
)
|
|
parser_subscription_types_create.add_argument(
|
|
"--active",
|
|
action="store_true",
|
|
help="Set this flag to mark the subscription as active",
|
|
)
|
|
parser_subscription_types_create.set_defaults(
|
|
func=subscription_types.cli_create_subscription_type
|
|
)
|
|
|
|
parser_subscription_types_list = subcommands_subscription_types.add_parser(
|
|
"list", description="List subscription types"
|
|
)
|
|
parser_subscription_types_list.add_argument(
|
|
"--active",
|
|
action="store_true",
|
|
help="Set this flag to only list active subscription types",
|
|
)
|
|
parser_subscription_types_list.set_defaults(
|
|
func=subscription_types.cli_list_subscription_types
|
|
)
|
|
|
|
parser_subscription_types_get = subcommands_subscription_types.add_parser(
|
|
"get", description="Get a subscription type by its ID"
|
|
)
|
|
parser_subscription_types_get.add_argument(
|
|
"-i",
|
|
"--id",
|
|
required=True,
|
|
help="ID of the subscription type you would like information about",
|
|
)
|
|
parser_subscription_types_get.set_defaults(
|
|
func=subscription_types.cli_get_subscription_type
|
|
)
|
|
|
|
parser_subscription_types_update = subcommands_subscription_types.add_parser(
|
|
"update", description="Update subscription type"
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"-i", "--id", required=True, type=str, help="ID for the subscription type"
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"-n",
|
|
"--name",
|
|
required=False,
|
|
default=None,
|
|
type=str,
|
|
help="Human-friendly name for the subscription type",
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"-d",
|
|
"--description",
|
|
required=False,
|
|
default=None,
|
|
type=str,
|
|
help="Detailed description of the subscription type",
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"-c",
|
|
"--choices",
|
|
nargs="*",
|
|
help="Available subscription options for form builder.",
|
|
required=False,
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"--icon",
|
|
required=False,
|
|
default=None,
|
|
help="URL to the icon representing this subscription type",
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"--stripe-product-id",
|
|
required=False,
|
|
default=None,
|
|
type=str,
|
|
help="Stripe product id",
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"--stripe-price-id",
|
|
required=False,
|
|
default=None,
|
|
type=str,
|
|
help="Stripe price id",
|
|
)
|
|
parser_subscription_types_update.add_argument(
|
|
"--active",
|
|
required=False,
|
|
type=parse_boolean_arg,
|
|
default=None,
|
|
help="Mark the subscription as active (True) or inactive (False).",
|
|
)
|
|
parser_subscription_types_update.set_defaults(
|
|
func=subscription_types.cli_update_subscription_type
|
|
)
|
|
|
|
parser_subscription_types_delete = subcommands_subscription_types.add_parser(
|
|
"delete", description="Delete a subscription type by its ID"
|
|
)
|
|
parser_subscription_types_delete.add_argument(
|
|
"-i",
|
|
"--id",
|
|
required=True,
|
|
help="ID of the subscription type you would like to delete.",
|
|
)
|
|
parser_subscription_types_delete.set_defaults(
|
|
func=subscription_types.cli_delete_subscription_type
|
|
)
|
|
|
|
parser_subscription_types_canonicalize = subcommands_subscription_types.add_parser(
|
|
"ensure-canonical",
|
|
description="Ensure that the connected Brood API contains resources for each of the canonical subscription types",
|
|
)
|
|
parser_subscription_types_canonicalize.set_defaults(
|
|
func=subscription_types.cli_ensure_canonical_subscription_types
|
|
)
|
|
|
|
parser_migrations = subcommands.add_parser(
|
|
"migrations", description="Manage database, resource and etc migrations"
|
|
)
|
|
parser_migrations.set_defaults(func=lambda _: parser_migrations.print_help())
|
|
subcommands_migrations = parser_migrations.add_subparsers(
|
|
description="Migration commands"
|
|
)
|
|
parser_migrations_list = subcommands_migrations.add_parser(
|
|
"list", description="List migrations"
|
|
)
|
|
parser_migrations_list.set_defaults(func=migrations_list)
|
|
parser_migrations_run = subcommands_migrations.add_parser(
|
|
"run", description="Run migration"
|
|
)
|
|
parser_migrations_run.add_argument(
|
|
"-i", "--id", required=False, type=int, help="Provide migration ID"
|
|
)
|
|
parser_migrations_run.add_argument(
|
|
"-f", "--file", required=False, type=str, help="path to file"
|
|
)
|
|
parser_migrations_run.add_argument(
|
|
"-c",
|
|
"--command",
|
|
default="upgrade",
|
|
choices=["upgrade", "downgrade"],
|
|
type=str,
|
|
help="Command for migration",
|
|
)
|
|
parser_migrations_run.add_argument(
|
|
"-s",
|
|
"--step",
|
|
required=False,
|
|
type=str,
|
|
help="How many steps to run",
|
|
)
|
|
parser_migrations_run.set_defaults(func=migrations_run)
|
|
|
|
parser_moonworm_tasks = subcommands.add_parser(
|
|
"moonworm-tasks", description="Manage tasks for moonworm journal."
|
|
)
|
|
|
|
parser_moonworm_tasks.set_defaults(func=lambda _: parser_migrations.print_help())
|
|
subcommands_moonworm_tasks = parser_moonworm_tasks.add_subparsers(
|
|
description="Moonworm taks commands"
|
|
)
|
|
parser_moonworm_tasks_list = subcommands_moonworm_tasks.add_parser(
|
|
"list", description="Return list of addresses in moonworm journal."
|
|
)
|
|
|
|
parser_moonworm_tasks_list.set_defaults(func=moonworm_tasks_list_handler)
|
|
|
|
parser_moonworm_tasks_add = subcommands_moonworm_tasks.add_parser(
|
|
"add_subscription", description="Manage tasks for moonworm journal."
|
|
)
|
|
|
|
parser_moonworm_tasks_add.add_argument(
|
|
"-i",
|
|
"--id",
|
|
type=str,
|
|
help="Id of subscription for add to moonworm tasks.",
|
|
)
|
|
|
|
parser_moonworm_tasks_add.set_defaults(func=moonworm_tasks_add_subscription_handler)
|
|
|
|
args = parser.parse_args()
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|