kopia lustrzana https://github.com/bugout-dev/moonstream
administrative commands for subscription types
rodzic
79c512ca13
commit
07b857d8a2
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Administrative utilities for operating a Moonstream API deployment.
|
||||
"""
|
|
@ -2,138 +2,143 @@
|
|||
Moonstream CLI
|
||||
"""
|
||||
import argparse
|
||||
from typing import Optional
|
||||
|
||||
from bugout.data import BugoutResources
|
||||
from bugout.exceptions import BugoutResponseException
|
||||
|
||||
from ..settings import (
|
||||
MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
MOONSTREAM_APPLICATION_ID,
|
||||
bugout_client as bc,
|
||||
)
|
||||
from . import subscription_types
|
||||
|
||||
|
||||
class BroodResourcesInteractionException(Exception):
|
||||
pass
|
||||
def parse_boolean_arg(raw_arg: Optional[str]) -> Optional[bool]:
|
||||
if raw_arg is None:
|
||||
return None
|
||||
|
||||
|
||||
class UnExpectedException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def add_subscription_handler(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Handler for "groups subscription add" subcommand.
|
||||
"""
|
||||
new_subscription_id = 0
|
||||
params = {"type": "subscription_type"}
|
||||
|
||||
try:
|
||||
# resolve index
|
||||
try:
|
||||
resources: BugoutResources = bc.list_resources(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN, params=params
|
||||
)
|
||||
new_subscription_id = (
|
||||
max(
|
||||
[
|
||||
int(resource.resource_data["id"])
|
||||
for resource in resources.resources
|
||||
]
|
||||
)
|
||||
+ 1
|
||||
)
|
||||
except BugoutResponseException as e:
|
||||
if e.detail != "Resources not found":
|
||||
raise BroodResourcesInteractionException(
|
||||
f"status_code={e.status_code}, detail={e.detail}"
|
||||
)
|
||||
# If Brood returns 404, then we want to continue execution of the outer try block
|
||||
# with new_subscription_id as 0. That's why we don't have an "else" condition here.
|
||||
except Exception as e:
|
||||
print("Unexpected Exception on request to brood")
|
||||
raise
|
||||
|
||||
subscription_data = {
|
||||
"type": "subscription_type",
|
||||
"id": str(new_subscription_id),
|
||||
"name": args.name,
|
||||
"description": args.description,
|
||||
"stripe_product_id": args.stripe_product_id,
|
||||
"stripe_price_id": args.stripe_price_id,
|
||||
"active": args.active,
|
||||
}
|
||||
|
||||
try:
|
||||
bc.create_resource(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
application_id=MOONSTREAM_APPLICATION_ID,
|
||||
resource_data=subscription_data,
|
||||
)
|
||||
except BugoutResponseException as e:
|
||||
print(f"status_code={e.status_code}, detail={e.detail}")
|
||||
raise BroodResourcesInteractionException(
|
||||
f"status_code={e.status_code}, detail={e.detail}"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Exception in create brood resource error:{e}")
|
||||
raise UnExpectedException("Error in resource creating")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raw_arg_lower = raw_arg.lower()
|
||||
if raw_arg_lower in ["t", "true", "1", "y", "yes"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Moonstream CLI")
|
||||
parser = argparse.ArgumentParser(description="Moonstream Admin CLI")
|
||||
parser.set_defaults(func=lambda _: parser.print_help())
|
||||
subcommands = parser.add_subparsers(description="Moonstream commands")
|
||||
|
||||
parser_subscription = subcommands.add_parser(
|
||||
"subscription-type", description="Manage Moonstream subscription types"
|
||||
parser_subscription_types = subcommands.add_parser(
|
||||
"subtypes", description="Manage Moonstream subscription types"
|
||||
)
|
||||
parser_subscription.set_defaults(func=lambda _: parser_subscription.print_help())
|
||||
subcommands_subscription = parser_subscription.add_subparsers(
|
||||
description="Moonstream subscription commands"
|
||||
parser_subscription_types.set_defaults(
|
||||
func=lambda _: parser_subscription_types.print_help()
|
||||
)
|
||||
subcommands_subscription_types = parser_subscription_types.add_subparsers()
|
||||
|
||||
# Subscriptions command parser
|
||||
parser_subscription_create = subcommands_subscription.add_parser(
|
||||
"create", description="Create Moonstream subscription"
|
||||
parser_subscription_types_create = subcommands_subscription_types.add_parser(
|
||||
"create", description="Create subscription type"
|
||||
)
|
||||
parser_subscription_create.add_argument(
|
||||
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="Title of that subscription",
|
||||
help="Human-friendly name for the subscription type",
|
||||
)
|
||||
parser_subscription_create.add_argument(
|
||||
parser_subscription_types_create.add_argument(
|
||||
"-d",
|
||||
"--description",
|
||||
required=True,
|
||||
type=str,
|
||||
help="Description for user",
|
||||
help="Detailed description of the subscription type",
|
||||
)
|
||||
parser_subscription_create.add_argument(
|
||||
parser_subscription_types_create.add_argument(
|
||||
"--stripe-product-id",
|
||||
required=False,
|
||||
default=None,
|
||||
type=str,
|
||||
help="Stripe product id",
|
||||
)
|
||||
parser_subscription_create.add_argument(
|
||||
parser_subscription_types_create.add_argument(
|
||||
"--stripe-price-id",
|
||||
required=False,
|
||||
default=None,
|
||||
type=str,
|
||||
help="Stripe price id",
|
||||
)
|
||||
parser_subscription_create.add_argument(
|
||||
parser_subscription_types_create.add_argument(
|
||||
"--active",
|
||||
action="store_true",
|
||||
help="Set this flag to create a verified user",
|
||||
help="Set this flag to mark the subscription as active",
|
||||
)
|
||||
parser_subscription_types_create.set_defaults(
|
||||
func=subscription_types.cli_add_subscription_type
|
||||
)
|
||||
|
||||
parser_subscription_types_list = subcommands_subscription_types.add_parser(
|
||||
"list", description="List 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="Create 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(
|
||||
"--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_create.set_defaults(func=add_subscription_handler)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
"""
|
||||
Utilities for managing subscription type resources for a Moonstream application.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from typing import Any, Dict, List, Optional
|
||||
from bugout.app import Bugout
|
||||
|
||||
from bugout.data import BugoutResources, BugoutResource
|
||||
|
||||
from ..settings import (
|
||||
MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
MOONSTREAM_APPLICATION_ID,
|
||||
bugout_client as bc,
|
||||
)
|
||||
|
||||
|
||||
class ConflictingSubscriptionTypesError(Exception):
|
||||
"""
|
||||
Raised when caller tries to add a resource that conflicts with an existing resource.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SubscriptionTypeNotFoundError(Exception):
|
||||
"""
|
||||
Raised when a subscription type is expected to exist as a Brood resource but is not found.
|
||||
"""
|
||||
|
||||
|
||||
class UnexpectedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
BUGOUT_RESOURCE_TYPE = "subscription_type"
|
||||
|
||||
|
||||
def add_subscription_type(
|
||||
id: str,
|
||||
name: str,
|
||||
description: str,
|
||||
stripe_product_id: Optional[str] = None,
|
||||
stripe_price_id: Optional[str] = None,
|
||||
active: bool = False,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Add a new Moonstream subscription type as a Brood resource.
|
||||
|
||||
Args:
|
||||
- id: Moonstream ID for the subscription type. Examples: "ethereum_blockchain", "ethereum_txpool",
|
||||
"ethereum_whalewatch", etc.
|
||||
- name: Human-friendly name for the subscription type, which can be displayed to users.
|
||||
- description: Detailed description of the subscription type for users who would like more
|
||||
information.
|
||||
- stripe_product_id: Optional product ID from Stripe account dashboard.
|
||||
- stripe_price_id: Optional price ID from Stripe account dashboard.
|
||||
- active: Set to True if you would like the subscription type to immediately be available for
|
||||
subscriptions. If you set this to False (which is the default), users will not be able to create
|
||||
subscriptions of this type until you later on set to true.
|
||||
"""
|
||||
params = {"type": BUGOUT_RESOURCE_TYPE, "id": id}
|
||||
|
||||
response: BugoutResources = bc.list_resources(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN, params=params
|
||||
)
|
||||
if response.resources:
|
||||
raise ConflictingSubscriptionTypesError(
|
||||
f"There is already a subscription_type with id: {id}"
|
||||
)
|
||||
|
||||
subscription_data = {
|
||||
"type": BUGOUT_RESOURCE_TYPE,
|
||||
"id": id,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"stripe_product_id": stripe_product_id,
|
||||
"stripe_price_id": stripe_price_id,
|
||||
"active": active,
|
||||
}
|
||||
|
||||
resource = bc.create_resource(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
application_id=MOONSTREAM_APPLICATION_ID,
|
||||
resource_data=subscription_data,
|
||||
)
|
||||
|
||||
return resource.resource_data
|
||||
|
||||
|
||||
def cli_add_subscription_type(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Handler for "mnstr subtypes create".
|
||||
"""
|
||||
result = add_subscription_type(
|
||||
args.id,
|
||||
args.name,
|
||||
args.description,
|
||||
args.stripe_product_id,
|
||||
args.stripe_price_id,
|
||||
args.active,
|
||||
)
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
def list_subscription_types() -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Lists all subscription types registered as Brood resources for this Moonstream application.
|
||||
"""
|
||||
response = bc.list_resources(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN, params={"type": BUGOUT_RESOURCE_TYPE}
|
||||
)
|
||||
resources = response.resources
|
||||
return [resource.resource_data for resource in resources]
|
||||
|
||||
|
||||
def cli_list_subscription_types(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Handler for "mnstr subtypes list".
|
||||
"""
|
||||
results = list_subscription_types()
|
||||
print(json.dumps(results))
|
||||
|
||||
|
||||
def get_subscription_type(id: str) -> Optional[BugoutResource]:
|
||||
"""
|
||||
Retrieves the resource representing the subscription type with the given ID.
|
||||
|
||||
Args:
|
||||
- id: Moonstream ID for the subscription type (not the Brood resource ID).
|
||||
Examples - "ethereum_blockchain", "ethereum_whalewatch", etc.
|
||||
|
||||
Returns: None if there is no subscription type with that ID. Otherwise, returns the full
|
||||
Brood resource. To access the subscription type itself, use the "resource_data" member of the
|
||||
return value. If more than one subscription type is found with the given ID, raises a
|
||||
ConflictingSubscriptionTypesError.
|
||||
"""
|
||||
response = bc.list_resources(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
params={"type": BUGOUT_RESOURCE_TYPE, "id": id},
|
||||
)
|
||||
resources = response.resources
|
||||
|
||||
if not resources:
|
||||
return None
|
||||
if len(resources) > 1:
|
||||
raise ConflictingSubscriptionTypesError(
|
||||
f"More than one resource with the given ID:\n{json.dumps(resources, indent=2)}"
|
||||
)
|
||||
return resources[0]
|
||||
|
||||
|
||||
def cli_get_subscription_type(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Handler for "mnstr subtypes get".
|
||||
"""
|
||||
resource = get_subscription_type(args.id)
|
||||
if resource is None:
|
||||
print(f"Could not find resource with ID: {id}")
|
||||
else:
|
||||
print(resource.json())
|
||||
|
||||
|
||||
def update_subscription_type(
|
||||
id: str,
|
||||
name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
stripe_product_id: Optional[str] = None,
|
||||
stripe_price_id: Optional[str] = None,
|
||||
active: Optional[bool] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Update a Moonstream subscription type using the Brood Resources API.
|
||||
|
||||
Args:
|
||||
- id: Moonstream ID for the subscription type. Examples: "ethereum_blockchain", "ethereum_txpool",
|
||||
"ethereum_whalewatch", etc.
|
||||
- name: Human-friendly name for the subscription type, which can be displayed to users.
|
||||
- description: Detailed description of the subscription type for users who would like more
|
||||
information.
|
||||
- stripe_product_id: Optional product ID from Stripe account dashboard.
|
||||
- stripe_price_id: Optional price ID from Stripe account dashboard.
|
||||
- active: Set to True if you would like the subscription type to immediately be available for
|
||||
subscriptions. If you set this to False (which is the default), users will not be able to create
|
||||
subscriptions of this type until you later on set to true.
|
||||
"""
|
||||
|
||||
resource = get_subscription_type(id)
|
||||
if resource is None:
|
||||
raise SubscriptionTypeNotFoundError(
|
||||
f"Could not find subscription type with ID: {id}."
|
||||
)
|
||||
|
||||
brood_resource_id = resource.id
|
||||
updated_resource_data = resource.resource_data
|
||||
if name is not None:
|
||||
updated_resource_data["name"] = name
|
||||
if description is not None:
|
||||
updated_resource_data["description"] = description
|
||||
if stripe_product_id is not None:
|
||||
updated_resource_data["stripe_product_id"] = stripe_product_id
|
||||
if stripe_price_id is not None:
|
||||
updated_resource_data["stripe_price_id"] = stripe_price_id
|
||||
if active is not None:
|
||||
updated_resource_data["active"] = active
|
||||
|
||||
bc.delete_resource(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN, resource_id=brood_resource_id
|
||||
)
|
||||
new_resource = bc.create_resource(
|
||||
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
|
||||
application_id=MOONSTREAM_APPLICATION_ID,
|
||||
resource_data=updated_resource_data,
|
||||
)
|
||||
|
||||
return new_resource.resource_data
|
||||
|
||||
|
||||
def cli_update_subscription_type(args: argparse.Namespace) -> None:
|
||||
"""
|
||||
Handler for "mnstr subtypes update".
|
||||
"""
|
||||
result = update_subscription_type(
|
||||
args.id,
|
||||
args.name,
|
||||
args.description,
|
||||
args.stripe_product_id,
|
||||
args.stripe_price_id,
|
||||
args.active,
|
||||
)
|
||||
print(json.dumps(result))
|
|
@ -10,12 +10,13 @@ class SubscriptionTypeResourceData(BaseModel):
|
|||
id: str
|
||||
name: str
|
||||
description: str
|
||||
subscription_plan_id: Optional[str] = None
|
||||
stripe_product_id: Optional[str] = None
|
||||
stripe_price_id: Optional[str] = None
|
||||
active: bool = False
|
||||
|
||||
|
||||
class SubscriptionTypesListResponce(BaseModel):
|
||||
subscriptions: List[SubscriptionTypeResourceData] = Field(default_factory=list)
|
||||
subscription_types: List[SubscriptionTypeResourceData] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SubscriptionResourceData(BaseModel):
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
Moonstream library and API version.
|
||||
"""
|
||||
|
||||
MOONSTREAM_VERSION = "0.0.1"
|
||||
MOONSTREAM_VERSION = "0.0.2"
|
||||
|
|
|
@ -29,4 +29,5 @@ setup(
|
|||
"Topic :: Software Development :: Libraries",
|
||||
],
|
||||
url="https://github.com/bugout-dev/moonstream",
|
||||
entry_points={"console_scripts": ["mnstr=moonstream.admin.cli:main"]},
|
||||
)
|
||||
|
|
Ładowanie…
Reference in New Issue