moonstream/backend/moonstreamapi/routes/subscriptions.py

338 wiersze
11 KiB
Python
Czysty Zwykły widok Historia

"""
The Moonstream subscriptions HTTP API
"""
2021-11-11 14:37:19 +00:00
import hashlib
import logging
2021-11-02 15:40:40 +00:00
import json
2021-11-04 14:14:37 +00:00
from typing import List, Optional, Dict, Any
2021-11-03 15:28:11 +00:00
import boto3 # type: ignore
from bugout.data import BugoutResource, BugoutResources
from bugout.exceptions import BugoutResponseException
from fastapi import APIRouter, Depends, Request, Form
from web3 import Web3
2021-11-09 12:33:10 +00:00
from ..actions import (
2021-11-11 14:37:19 +00:00
validate_abi_json,
2021-11-09 12:33:10 +00:00
upload_abi_to_s3,
)
from ..admin import subscription_types
from .. import data
from ..middleware import MoonstreamHTTPException
2021-09-01 12:03:44 +00:00
from ..reporter import reporter
from ..settings import (
2021-07-28 13:42:50 +00:00
MOONSTREAM_APPLICATION_ID,
bugout_client as bc,
MOONSTREAM_S3_SMARTCONTRACTS_ABI_BUCKET,
MOONSTREAM_S3_SMARTCONTRACTS_ABI_PREFIX,
)
from ..web3_provider import yield_web3_provider
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="/subscriptions",
)
BUGOUT_RESOURCE_TYPE_SUBSCRIPTION = "subscription"
@router.post("/", tags=["subscriptions"], response_model=data.SubscriptionResourceData)
async def add_subscription_handler(
2021-07-27 16:51:32 +00:00
request: Request, # subscription_data: data.CreateSubscriptionRequest = Body(...)
address: str = Form(...),
color: str = Form(...),
label: str = Form(...),
subscription_type_id: str = Form(...),
2021-10-26 15:26:03 +00:00
abi: Optional[str] = Form(None),
web3: Web3 = Depends(yield_web3_provider),
2021-07-27 15:08:40 +00:00
) -> data.SubscriptionResourceData:
"""
Add subscription to blockchain stream data for user.
"""
token = request.state.token
2021-07-27 15:08:40 +00:00
2021-11-04 13:03:18 +00:00
if subscription_type_id != "ethereum_whalewatch":
try:
2021-11-04 13:24:56 +00:00
address = web3.toChecksumAddress(address)
2021-11-04 13:03:18 +00:00
except ValueError as e:
raise MoonstreamHTTPException(
status_code=400,
detail=str(e),
internal_error=e,
)
except Exception as e:
logger.error(f"Failed to convert address to checksum address")
raise MoonstreamHTTPException(
status_code=500,
internal_error=e,
detail="Currently unable to convert address to checksum address",
)
active_subscription_types_response = subscription_types.list_subscription_types(
active_only=True
)
available_subscription_type_ids = [
subscription_type.resource_data.get("id")
for subscription_type in active_subscription_types_response.resources
if subscription_type.resource_data.get("id") is not None
2021-07-27 15:08:40 +00:00
]
if subscription_type_id not in available_subscription_type_ids:
raise MoonstreamHTTPException(
status_code=404,
detail=f"Invalid subscription type: {subscription_type_id}.",
2021-07-27 15:08:40 +00:00
)
user = request.state.user
2021-07-27 15:08:40 +00:00
resource_data = {
"type": BUGOUT_RESOURCE_TYPE_SUBSCRIPTION,
"user_id": str(user.id),
"subscription_type_id": subscription_type_id,
"address": address,
"color": color,
"label": label,
2021-10-26 15:26:03 +00:00
"abi": None,
2021-11-02 15:01:42 +00:00
"bucket": None,
"s3_path": None,
}
2021-07-27 15:08:40 +00:00
try:
resource: BugoutResource = bc.create_resource(
token=token,
application_id=MOONSTREAM_APPLICATION_ID,
resource_data=resource_data,
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
2021-09-01 12:03:44 +00:00
logger.error(f"Error creating subscription resource: {str(e)}")
raise MoonstreamHTTPException(status_code=500, internal_error=e)
2021-07-27 15:08:40 +00:00
2021-10-26 15:26:03 +00:00
if abi:
2021-11-04 14:25:15 +00:00
2021-11-11 14:37:19 +00:00
try:
json_abi = json.loads(abi)
except json.JSONDecodeError:
raise MoonstreamHTTPException(status_code=400, detail="Malformed abi body.")
validate_abi_json(json_abi)
2021-11-02 15:40:40 +00:00
2021-11-09 12:33:10 +00:00
update_resource = upload_abi_to_s3(resource=resource, abi=abi, update={})
2021-11-02 15:40:40 +00:00
2021-11-11 14:37:19 +00:00
abi_string = json.dumps(json_abi, sort_keys=True, indent=2)
hash = hashlib.md5(abi_string.encode("utf-8")).hexdigest()
update_resource["abi_hash"] = hash
2021-10-26 15:26:03 +00:00
try:
2021-11-02 15:40:40 +00:00
updated_resource: BugoutResource = bc.update_resource(
2021-10-26 15:26:03 +00:00
token=token,
2021-11-02 15:40:40 +00:00
resource_id=resource.id,
resource_data=data.SubscriptionUpdate(
update=update_resource,
).dict(),
2021-10-26 15:26:03 +00:00
)
2021-11-02 15:40:40 +00:00
resource = updated_resource
2021-10-26 15:26:03 +00:00
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
2021-11-02 15:40:40 +00:00
logger.error(f"Error getting user subscriptions: {str(e)}")
2021-10-26 15:26:03 +00:00
raise MoonstreamHTTPException(status_code=500, internal_error=e)
2021-07-27 15:08:40 +00:00
return data.SubscriptionResourceData(
id=str(resource.id),
2021-11-02 15:40:40 +00:00
user_id=resource.resource_data["user_id"],
address=resource.resource_data["address"],
color=resource.resource_data["color"],
label=resource.resource_data["label"],
abi=resource.resource_data.get("abi"),
subscription_type_id=resource.resource_data["subscription_type_id"],
updated_at=resource.updated_at,
created_at=resource.created_at,
)
@router.delete(
2021-07-27 16:51:32 +00:00
"/{subscription_id}",
tags=["subscriptions"],
response_model=data.SubscriptionResourceData,
)
async def delete_subscription_handler(request: Request, subscription_id: str):
"""
Delete subscriptions.
"""
token = request.state.token
try:
deleted_resource = bc.delete_resource(token=token, resource_id=subscription_id)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
2021-07-27 16:51:32 +00:00
except Exception as e:
2021-09-01 12:03:44 +00:00
logger.error(f"Error deleting subscription: {str(e)}")
raise MoonstreamHTTPException(status_code=500, internal_error=e)
2021-07-27 16:51:32 +00:00
return data.SubscriptionResourceData(
id=str(deleted_resource.id),
user_id=deleted_resource.resource_data["user_id"],
address=deleted_resource.resource_data["address"],
color=deleted_resource.resource_data["color"],
label=deleted_resource.resource_data["label"],
2021-11-02 14:32:40 +00:00
abi=deleted_resource.resource_data.get("abi"),
2021-07-27 16:51:32 +00:00
subscription_type_id=deleted_resource.resource_data["subscription_type_id"],
2021-09-03 12:50:27 +00:00
updated_at=deleted_resource.updated_at,
created_at=deleted_resource.created_at,
2021-07-27 16:51:32 +00:00
)
@router.get("/", tags=["subscriptions"], response_model=data.SubscriptionsListResponse)
async def get_subscriptions_handler(request: Request) -> data.SubscriptionsListResponse:
"""
Get user's subscriptions.
"""
token = request.state.token
params = {
"type": BUGOUT_RESOURCE_TYPE_SUBSCRIPTION,
"user_id": str(request.state.user.id),
}
try:
resources: BugoutResources = bc.list_resources(token=token, params=params)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
logger.error(
2021-09-01 12:03:44 +00:00
f"Error listing subscriptions for user ({request.user.id}) with token ({request.state.token}), error: {str(e)}"
)
2021-09-01 12:03:44 +00:00
reporter.error_report(e)
raise MoonstreamHTTPException(status_code=500, internal_error=e)
2021-07-27 15:08:40 +00:00
return data.SubscriptionsListResponse(
subscriptions=[
2021-07-27 15:08:40 +00:00
data.SubscriptionResourceData(
id=str(resource.id),
user_id=resource.resource_data["user_id"],
2021-07-27 15:08:40 +00:00
address=resource.resource_data["address"],
color=resource.resource_data["color"],
label=resource.resource_data["label"],
2021-11-02 14:32:40 +00:00
abi=resource.resource_data.get("abi"),
2021-07-27 15:08:40 +00:00
subscription_type_id=resource.resource_data["subscription_type_id"],
2021-09-03 12:50:27 +00:00
updated_at=resource.updated_at,
created_at=resource.created_at,
)
for resource in resources.resources
]
)
2021-07-27 15:08:40 +00:00
@router.put(
2021-08-10 16:29:16 +00:00
"/{subscription_id}",
tags=["subscriptions"],
response_model=data.SubscriptionResourceData,
)
async def update_subscriptions_handler(
request: Request,
subscription_id: str,
color: Optional[str] = Form(None),
label: Optional[str] = Form(None),
2021-11-08 12:24:11 +00:00
abi: Optional[str] = Form(None),
2021-08-10 16:29:16 +00:00
) -> data.SubscriptionResourceData:
"""
Get user's subscriptions.
"""
token = request.state.token
2021-11-02 11:40:21 +00:00
update: Dict[str, Any] = {}
2021-08-10 16:29:16 +00:00
if color:
update["color"] = color
if label:
update["label"] = label
2021-11-08 12:24:11 +00:00
if abi:
2021-11-11 14:37:19 +00:00
try:
json_abi = json.loads(abi)
except json.JSONDecodeError:
raise MoonstreamHTTPException(status_code=400, detail="Malformed abi body.")
validate_abi_json(json_abi)
abi_string = json.dumps(json_abi, sort_keys=True, indent=2)
hash = hashlib.md5(abi_string.encode("utf-8")).hexdigest()
2021-11-08 12:24:11 +00:00
try:
subscription_resource: BugoutResource = bc.get_resource(
token=token,
resource_id=subscription_id,
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
logger.error(f"Error creating subscription resource: {str(e)}")
raise MoonstreamHTTPException(status_code=500, internal_error=e)
if subscription_resource.resource_data["abi"] is not None:
raise MoonstreamHTTPException(
status_code=400,
detail="Subscription already have ABI. For add a new ABI create new subscription.",
)
2021-11-09 12:33:10 +00:00
update = upload_abi_to_s3(
resource=subscription_resource, abi=abi, update=update
2021-11-08 12:24:11 +00:00
)
2021-11-11 14:37:19 +00:00
update["abi_hash"] = hash
2021-08-10 16:29:16 +00:00
try:
resource: BugoutResource = bc.update_resource(
token=token,
resource_id=subscription_id,
resource_data=data.SubscriptionUpdate(
update=update,
).dict(),
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
2021-08-10 16:29:16 +00:00
except Exception as e:
2021-09-01 12:03:44 +00:00
logger.error(f"Error getting user subscriptions: {str(e)}")
raise MoonstreamHTTPException(status_code=500, internal_error=e)
2021-08-10 16:29:16 +00:00
return data.SubscriptionResourceData(
id=str(resource.id),
user_id=resource.resource_data["user_id"],
address=resource.resource_data["address"],
color=resource.resource_data["color"],
label=resource.resource_data["label"],
2021-11-02 14:32:40 +00:00
abi=resource.resource_data.get("abi"),
2021-08-10 16:29:16 +00:00
subscription_type_id=resource.resource_data["subscription_type_id"],
2021-09-03 12:50:27 +00:00
updated_at=resource.updated_at,
created_at=resource.created_at,
2021-08-10 16:29:16 +00:00
)
@router.get(
"/types", tags=["subscriptions"], response_model=data.SubscriptionTypesListResponse
2021-07-27 15:08:40 +00:00
)
async def list_subscription_types() -> data.SubscriptionTypesListResponse:
2021-07-27 15:08:40 +00:00
"""
Get availables subscription types.
2021-07-27 15:08:40 +00:00
"""
results: List[data.SubscriptionTypeResourceData] = []
2021-07-27 15:08:40 +00:00
try:
response = subscription_types.list_subscription_types()
results = [
data.SubscriptionTypeResourceData.validate(resource.resource_data)
for resource in response.resources
]
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
2021-07-27 15:08:40 +00:00
except Exception as e:
2021-09-01 12:03:44 +00:00
logger.error(f"Error reading subscription types from Brood API: {str(e)}")
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return data.SubscriptionTypesListResponse(subscription_types=results)