moonstream/moonstreamapi/moonstreamapi/admin/cli.py

487 wiersze
17 KiB
Python
Czysty Zwykły widok Historia

2021-07-28 13:42:50 +00:00
"""
Moonstream CLI
"""
import argparse
2021-11-03 15:28:11 +00:00
import json
2021-12-16 13:26:04 +00:00
import logging
import os
from posix import listdir
from typing import Optional, List, Dict, Any, Union, Callable
2021-07-28 13:42:50 +00:00
2021-12-16 13:26:04 +00:00
from sqlalchemy.orm import with_expression
2021-10-28 16:02:35 +00:00
from moonstreamdb.db import SessionLocal
from ..settings import BUGOUT_BROOD_URL, BUGOUT_SPIRE_URL, MOONSTREAM_APPLICATION_ID
from ..web3_provider import yield_web3_provider
2021-12-12 10:08:15 +00:00
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__)
2021-07-28 13:42:50 +00:00
MIGRATIONS_FOLDER = "./moonstreamapi/admin/migrations"
2021-07-28 13:42:50 +00:00
def parse_boolean_arg(raw_arg: Optional[str]) -> Optional[bool]:
if raw_arg is None:
return None
2021-07-28 13:42:50 +00:00
raw_arg_lower = raw_arg.lower()
if raw_arg_lower in ["t", "true", "1", "y", "yes"]:
return True
return False
2021-07-28 13:42:50 +00:00
2023-05-01 09:53:44 +00:00
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
2023-05-01 09:32:28 +00:00
- 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)
2021-10-28 14:57:52 +00:00
def migrations_run(args: argparse.Namespace) -> None:
web3_session = yield_web3_provider()
2021-10-28 16:02:35 +00:00
db_session = SessionLocal()
try:
2023-05-01 09:32:28 +00:00
if args.id == 20230501:
# fix entity duplicates keys for all subscriptions introduced in 20230213
2023-05-01 09:53:44 +00:00
step_order = ["fix_duplicates_keys_in_entity_subscription"]
2023-04-27 12:37:17 +00:00
step_map: Dict[str, Dict[str, Any]] = {
2023-05-01 09:32:28 +00:00
"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")
2023-05-01 09:53:44 +00:00
step = args.step
migration_run(step_map, args.command, step, step_order)
2023-05-01 09:32:28 +00:00
if args.id == 20230213:
2023-05-01 09:53:44 +00:00
step_order = [
"generate_entity_subscriptions_from_brood_resources",
"update_dashboards_connection",
]
2023-05-01 09:32:28 +00:00
step_map = {
2023-04-27 12:37:17 +00:00
"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",
},
},
2023-04-27 12:37:17 +00:00
"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",
},
},
}
2023-04-27 12:37:17 +00:00
if args.command not in ["upgrade", "downgrade"]:
logger.info("Wrong command. Please use upgrade or downgrade")
step = args.step
2023-05-01 09:53:44 +00:00
migration_run(step_map, args.command, step, step_order)
2023-04-13 14:49:56 +00:00
2023-02-14 14:55:52 +00:00
elif args.id == 20211101:
logger.info("Starting update of subscriptions in Brood resource...")
2021-10-28 16:02:35 +00:00
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)
2021-12-02 20:27:15 +00:00
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
2021-10-28 16:02:35 +00:00
finally:
db_session.close()
2021-10-28 14:57:52 +00:00
2021-12-12 10:08:15 +00:00
def moonworm_tasks_list_handler(args: argparse.Namespace) -> None:
2021-12-13 14:58:47 +00:00
moonworm_tasks.get_list_of_addresses()
2021-12-12 10:08:15 +00:00
def moonworm_tasks_add_subscription_handler(args: argparse.Namespace) -> None:
moonworm_tasks.add_subscription(args.id)
2021-07-28 13:42:50 +00:00
def main() -> None:
2021-08-12 21:00:54 +00:00
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,
)
2021-07-28 13:42:50 +00:00
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"
2021-07-28 13:42:50 +00:00
)
parser_subscription_types.set_defaults(
func=lambda _: parser_subscription_types.print_help()
2021-07-28 13:42:50 +00:00
)
subcommands_subscription_types = parser_subscription_types.add_subparsers()
2021-07-28 13:42:50 +00:00
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"
2021-07-28 13:42:50 +00:00
)
parser_subscription_types_create.add_argument(
2021-07-28 13:42:50 +00:00
"-n",
"--name",
required=True,
type=str,
help="Human-friendly name for the subscription type",
2021-07-28 13:42:50 +00:00
)
parser_subscription_types_create.add_argument(
2021-07-28 13:42:50 +00:00
"-d",
2021-08-03 17:33:31 +00:00
"--description",
2021-07-28 13:42:50 +00:00
required=True,
type=str,
help="Detailed description of the subscription type",
2021-07-28 13:42:50 +00:00
)
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(
2021-08-03 16:26:46 +00:00
"--stripe-product-id",
2021-07-28 13:42:50 +00:00
required=False,
2021-08-03 16:26:46 +00:00
default=None,
2021-07-28 13:42:50 +00:00
type=str,
2021-08-03 16:26:46 +00:00
help="Stripe product id",
)
parser_subscription_types_create.add_argument(
2021-08-03 16:26:46 +00:00
"--stripe-price-id",
required=False,
default=None,
type=str,
help="Stripe price id",
2021-07-28 13:42:50 +00:00
)
parser_subscription_types_create.add_argument(
2021-07-28 13:42:50 +00:00
"--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
2021-07-28 13:42:50 +00:00
)
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(
2021-08-12 21:00:54 +00:00
"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
)
2021-10-28 14:57:52 +00:00
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)
2021-10-28 14:57:52 +00:00
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",
2021-10-28 14:57:52 +00:00
)
parser_migrations_run.add_argument(
"-s",
"--step",
required=False,
type=str,
help="How many steps to run",
)
2021-10-28 14:57:52 +00:00
parser_migrations_run.set_defaults(func=migrations_run)
2021-12-12 10:08:15 +00:00
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)
2021-12-12 10:08:15 +00:00
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)
2021-12-12 10:08:15 +00:00
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()