administrative commands for subscription types

pull/105/head
Neeraj Kashyap 2021-08-12 11:27:54 -07:00
rodzic 79c512ca13
commit 07b857d8a2
6 zmienionych plików z 337 dodań i 96 usunięć

Wyświetl plik

@ -0,0 +1,3 @@
"""
Administrative utilities for operating a Moonstream API deployment.
"""

Wyświetl plik

@ -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)

Wyświetl plik

@ -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))

Wyświetl plik

@ -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):

Wyświetl plik

@ -2,4 +2,4 @@
Moonstream library and API version.
"""
MOONSTREAM_VERSION = "0.0.1"
MOONSTREAM_VERSION = "0.0.2"

Wyświetl plik

@ -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"]},
)