moonstream/moonstreamapi/moonstreamapi/actions.py

796 wiersze
26 KiB
Python
Czysty Zwykły widok Historia

from collections import OrderedDict
2021-12-03 20:53:05 +00:00
import hashlib
2021-08-23 17:03:45 +00:00
import json
2021-12-03 20:53:05 +00:00
from itertools import chain
2021-08-06 14:03:24 +00:00
import logging
from typing import List, Optional, Dict, Any, Union
2021-12-16 13:26:04 +00:00
from enum import Enum
2021-09-06 17:28:16 +00:00
import uuid
2021-09-01 12:03:44 +00:00
2021-08-24 13:50:36 +00:00
import boto3 # type: ignore
2022-02-17 10:22:20 +00:00
from bugout.data import (
BugoutSearchResults,
BugoutSearchResult,
BugoutResource,
BugoutResources,
)
2021-09-29 16:19:31 +00:00
from bugout.journal import SearchOrder
2022-02-17 10:22:20 +00:00
from bugout.exceptions import BugoutResponseException
2023-04-26 14:55:13 +00:00
from entity.data import EntityCollectionsResponse, EntityCollectionResponse # type: ignore
from entity.exceptions import EntityUnexpectedResponse # type: ignore
2021-10-26 10:06:25 +00:00
from ens.utils import is_valid_ens_name # type: ignore
from eth_utils.address import is_address # type: ignore
2021-12-16 13:26:04 +00:00
from moonstreamdb.models import EthereumLabel
2022-03-07 18:08:35 +00:00
from slugify import slugify # type: ignore
2021-08-27 15:28:47 +00:00
from sqlalchemy import text
2021-09-01 12:03:44 +00:00
from sqlalchemy.orm import Session
2021-12-16 13:26:04 +00:00
from web3 import Web3
2021-11-09 12:33:10 +00:00
from web3._utils.validation import validate_abi
2021-08-06 14:03:24 +00:00
from . import data
2021-10-28 15:48:15 +00:00
from .middleware import MoonstreamHTTPException
2021-12-16 13:26:04 +00:00
from .reporter import reporter
2021-09-06 17:21:05 +00:00
from .settings import (
BUGOUT_REQUEST_TIMEOUT_SECONDS,
2021-12-16 13:26:04 +00:00
ETHERSCAN_SMARTCONTRACTS_BUCKET,
2021-09-29 16:19:31 +00:00
MOONSTREAM_ADMIN_ACCESS_TOKEN,
2021-12-16 13:26:04 +00:00
MOONSTREAM_APPLICATION_ID,
2021-09-29 16:19:31 +00:00
MOONSTREAM_DATA_JOURNAL_ID,
MOONSTREAM_S3_SMARTCONTRACTS_ABI_BUCKET,
MOONSTREAM_S3_SMARTCONTRACTS_ABI_PREFIX,
2021-12-03 20:53:05 +00:00
MOONSTREAM_MOONWORM_TASKS_JOURNAL,
MOONSTREAM_ADMIN_ACCESS_TOKEN,
2021-09-06 17:21:05 +00:00
)
from .settings import bugout_client as bc, entity_client as ec
2021-08-06 14:03:24 +00:00
logger = logging.getLogger(__name__)
blockchain_by_subscription_id = {
"ethereum_blockchain": "ethereum",
"polygon_blockchain": "polygon",
2022-08-10 17:06:47 +00:00
"mumbai_blockchain": "mumbai",
"xdai_blockchain": "xdai",
2023-03-06 16:00:23 +00:00
"wyrm_blockchain": "wyrm",
2021-12-13 17:43:22 +00:00
"ethereum_smartcontract": "ethereum",
"polygon_smartcontract": "polygon",
2022-08-10 17:06:47 +00:00
"mumbai_smartcontract": "mumbai",
"xdai_smartcontract": "xdai",
2023-03-06 16:00:23 +00:00
"wyrm_smartcontract": "wyrm",
}
2021-09-29 16:19:31 +00:00
class StatusAPIException(Exception):
"""
Raised during checking Moonstream API statuses.
"""
2022-03-08 15:06:50 +00:00
class NameNormalizationException(Exception):
"""
Raised on actions when slugify can't normalize name.
"""
2022-03-09 13:05:31 +00:00
2022-03-09 12:29:02 +00:00
class ResourceQueryFetchException(Exception):
"""
Exception in queries API
"""
2022-03-08 15:06:50 +00:00
class EntityCollectionNotFoundException(Exception):
"""
Raised when entity collection is not found
"""
2021-10-26 08:12:09 +00:00
class LabelNames(Enum):
ETHERSCAN_SMARTCONTRACT = "etherscan_smartcontract"
COINMARKETCAP_TOKEN = "coinmarketcap_token"
ERC721 = "erc721"
2021-08-24 12:05:17 +00:00
def get_contract_source_info(
db_session: Session, contract_address: str
) -> Optional[data.EthereumSmartContractSourceInfo]:
2021-10-26 08:12:09 +00:00
label = (
2021-10-14 15:01:00 +00:00
db_session.query(EthereumLabel)
.filter(EthereumLabel.address == contract_address)
2021-10-26 08:12:09 +00:00
.filter(EthereumLabel.label == LabelNames.ETHERSCAN_SMARTCONTRACT.value)
.one_or_none()
2021-08-06 14:03:24 +00:00
)
2021-10-26 08:12:09 +00:00
if label is None:
2021-10-14 15:01:00 +00:00
return None
2021-08-06 14:03:24 +00:00
2021-10-26 08:12:09 +00:00
object_uri = label.label_data["object_uri"]
key = object_uri.split("s3://etherscan-smart-contracts/")[1]
s3 = boto3.client("s3")
bucket = ETHERSCAN_SMARTCONTRACTS_BUCKET
try:
raw_obj = s3.get_object(Bucket=bucket, Key=key)
obj_data = json.loads(raw_obj["Body"].read().decode("utf-8"))["data"]
contract_source_info = data.EthereumSmartContractSourceInfo(
name=obj_data["ContractName"],
source_code=obj_data["SourceCode"],
compiler_version=obj_data["CompilerVersion"],
abi=obj_data["ABI"],
)
return contract_source_info
except Exception as e:
logger.error(f"Failed to load smart contract {object_uri}")
reporter.error_report(e)
2021-08-16 14:41:41 +00:00
return None
2021-08-16 14:41:41 +00:00
def get_ens_name(web3: Web3, address: str) -> Optional[str]:
try:
checksum_address = web3.toChecksumAddress(address)
except:
raise ValueError(f"{address} is invalid ethereum address is passed")
try:
ens_name = web3.ens.name(checksum_address)
return ens_name
except Exception as e:
reporter.error_report(e, ["web3", "ens"])
logger.error(
f"Cannot get ens name for address {checksum_address}. Probably node is down"
)
raise e
def get_ens_address(web3: Web3, name: str) -> Optional[str]:
if not is_valid_ens_name(name):
raise ValueError(f"{name} is not valid ens name")
try:
ens_checksum_address = web3.ens.address(name)
if ens_checksum_address is not None:
ordinary_address = ens_checksum_address.lower()
return ordinary_address
return None
except Exception as e:
reporter.error_report(e, ["web3", "ens"])
logger.error(f"Cannot get ens address for name {name}. Probably node is down")
raise e
2021-08-25 14:04:58 +00:00
def get_ethereum_address_info(
2021-10-26 08:12:09 +00:00
db_session: Session, web3: Web3, address: str
2021-08-25 14:04:58 +00:00
) -> Optional[data.EthereumAddressInfo]:
if not is_address(address):
raise ValueError(f"Invalid ethereum address : {address}")
2021-08-30 13:46:51 +00:00
address_info = data.EthereumAddressInfo(address=address)
try:
address_info.ens_name = get_ens_name(web3, address)
except:
pass
2021-08-30 13:46:51 +00:00
etherscan_address_url = f"https://etherscan.io/address/{address}"
etherscan_token_url = f"https://etherscan.io/token/{address}"
2021-08-30 13:46:51 +00:00
blockchain_com_url = f"https://www.blockchain.com/eth/address/{address}"
2021-10-26 08:12:09 +00:00
2021-08-27 15:28:47 +00:00
coinmarketcap_label: Optional[EthereumLabel] = (
db_session.query(EthereumLabel)
2021-10-14 15:01:00 +00:00
.filter(EthereumLabel.address == address)
2021-08-27 15:28:47 +00:00
.filter(EthereumLabel.label == LabelNames.COINMARKETCAP_TOKEN.value)
.order_by(text("created_at desc"))
.limit(1)
.one_or_none()
2021-08-25 14:04:58 +00:00
)
2021-10-26 08:12:09 +00:00
2021-08-27 15:28:47 +00:00
if coinmarketcap_label is not None:
2021-08-30 13:46:51 +00:00
address_info.token = data.EthereumTokenDetails(
name=coinmarketcap_label.label_data["name"],
symbol=coinmarketcap_label.label_data["symbol"],
external_url=[
coinmarketcap_label.label_data["coinmarketcap_url"],
etherscan_token_url,
2021-08-30 13:46:51 +00:00
blockchain_com_url,
],
)
2021-08-27 15:28:47 +00:00
# Checking for smart contract
etherscan_label: Optional[EthereumLabel] = (
db_session.query(EthereumLabel)
2021-10-14 15:01:00 +00:00
.filter(EthereumLabel.address == address)
2021-08-27 15:28:47 +00:00
.filter(EthereumLabel.label == LabelNames.ETHERSCAN_SMARTCONTRACT.value)
.order_by(text("created_at desc"))
.limit(1)
.one_or_none()
)
if etherscan_label is not None:
2021-08-30 13:46:51 +00:00
address_info.smart_contract = data.EthereumSmartContractDetails(
name=etherscan_label.label_data["name"],
external_url=[etherscan_address_url, blockchain_com_url],
)
2021-08-25 14:04:58 +00:00
# Checking for NFT
# Checking for smart contract
erc721_label: Optional[EthereumLabel] = (
db_session.query(EthereumLabel)
2021-10-14 15:01:00 +00:00
.filter(EthereumLabel.address == address)
.filter(EthereumLabel.label == LabelNames.ERC721.value)
.order_by(text("created_at desc"))
.limit(1)
.one_or_none()
)
if erc721_label is not None:
2021-09-06 13:24:14 +00:00
address_info.nft = data.EthereumNFTDetails(
name=erc721_label.label_data.get("name"),
symbol=erc721_label.label_data.get("symbol"),
2021-09-06 13:24:14 +00:00
total_supply=erc721_label.label_data.get("totalSupply"),
external_url=[etherscan_token_url, blockchain_com_url],
)
2021-08-25 14:04:58 +00:00
return address_info
2021-08-16 14:41:41 +00:00
def get_address_labels(
2021-08-24 13:50:36 +00:00
db_session: Session, start: int, limit: int, addresses: Optional[str] = None
) -> data.AddressListLabelsResponse:
2021-08-16 14:41:41 +00:00
"""
Attach labels to addresses.
"""
if addresses is not None:
addresses_list = addresses.split(",")
2021-10-14 15:01:00 +00:00
addresses_obj = addresses_list[start : start + limit]
else:
addresses_obj = []
2021-08-16 14:41:41 +00:00
addresses_response = data.AddressListLabelsResponse(addresses=[])
for address in addresses_obj:
labels_obj = (
db_session.query(EthereumLabel)
2021-10-14 15:01:00 +00:00
.filter(EthereumLabel.address == address)
2021-08-16 14:41:41 +00:00
.all()
)
addresses_response.addresses.append(
data.AddressLabelsResponse(
2021-10-14 15:01:00 +00:00
address=address,
2021-08-16 14:41:41 +00:00
labels=[
data.AddressLabelResponse(
label=label.label, label_data=label.label_data
)
for label in labels_obj
],
)
)
return addresses_response
2021-09-06 17:21:05 +00:00
def create_onboarding_resource(
token: uuid.UUID,
resource_data: Dict[str, Any] = {
"type": data.USER_ONBOARDING_STATE,
2022-03-08 15:10:06 +00:00
"steps": {
"welcome": 0,
"subscriptions": 0,
"stream": 0,
},
2021-09-06 17:21:05 +00:00
"is_complete": False,
},
) -> BugoutResource:
resource = bc.create_resource(
token=token,
application_id=MOONSTREAM_APPLICATION_ID,
resource_data=resource_data,
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
)
return resource
2021-09-29 16:19:31 +00:00
def check_api_status():
crawl_types_timestamp: Dict[str, Any] = {
"ethereum_txpool": None,
"ethereum_trending": None,
}
for crawl_type in crawl_types_timestamp.keys():
try:
search_results: BugoutSearchResults = bc.search(
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
journal_id=MOONSTREAM_DATA_JOURNAL_ID,
query=f"tag:crawl_type:{crawl_type}",
limit=1,
content=False,
timeout=10.0,
order=SearchOrder.DESCENDING,
)
if len(search_results.results) == 1:
crawl_types_timestamp[crawl_type] = search_results.results[0].created_at
except Exception:
raise StatusAPIException(
f"Unable to get status for crawler with type: {crawl_type}"
)
return crawl_types_timestamp
2021-10-28 15:48:15 +00:00
def json_type(evm_type: str) -> type:
if evm_type.startswith(("uint", "int")):
return int
elif evm_type.startswith("bytes") or evm_type == "string" or evm_type == "address":
return str
elif evm_type == "bool":
return bool
else:
raise ValueError(f"Cannot convert to python type {evm_type}")
def dashboards_abi_validation(
2022-03-08 15:10:06 +00:00
dashboard_subscription: data.DashboardMeta,
abi: Any,
2021-10-28 15:48:15 +00:00
):
"""
Validate current dashboard subscription : https://github.com/bugout-dev/moonstream/issues/345#issuecomment-953052444
with contract abi on S3
"""
# maybe its over but not found beter way
abi_functions = {
item["name"]: {inputs["name"]: inputs["type"] for inputs in item["inputs"]}
for item in abi
if item["type"] == "function"
}
if not dashboard_subscription.all_methods:
for method in dashboard_subscription.methods:
if method["name"] not in abi_functions:
# Method not exists
logger.error(
f"Error on dashboard resource validation method:{method['name']}"
f" of subscription: {dashboard_subscription.subscription_id}"
f"does not exists in Abi "
2021-10-28 15:48:15 +00:00
)
raise MoonstreamHTTPException(status_code=400)
2021-11-01 11:53:37 +00:00
if method.get("filters") and isinstance(method["filters"], dict):
2021-10-28 15:48:15 +00:00
for input_argument_name, value in method["filters"].items():
if input_argument_name not in abi_functions[method["name"]]:
# Argument not exists
logger.error(
f"Error on dashboard resource validation type argument: {input_argument_name} of method:{method['name']} "
f" of subscription: {dashboard_subscription.subscription_id} has incorrect"
f"does not exists in Abi"
2021-10-28 15:48:15 +00:00
)
raise MoonstreamHTTPException(status_code=400)
if not isinstance(
2021-11-01 11:53:37 +00:00
value,
2021-10-28 15:48:15 +00:00
json_type(abi_functions[method["name"]][input_argument_name]),
):
# Argument has incorrect type
logger.error(
f"Error on dashboard resource validation type argument: {input_argument_name} of method:{method['name']} "
f" of subscription: {dashboard_subscription.subscription_id} has incorrect type {type(value)}"
f" when {abi_functions[method['name']][input_argument_name]} required."
)
raise MoonstreamHTTPException(status_code=400)
abi_events = {
item["name"]: {inputs["name"]: inputs["type"] for inputs in item["inputs"]}
for item in abi
if item["type"] == "event"
}
if not dashboard_subscription.all_events:
for event in dashboard_subscription.events:
if event["name"] not in abi_events:
logger.error(
f"Error on dashboard resource validation event:{event['name']}"
f" of subscription: {dashboard_subscription.subscription_id}"
f"does not exists in Abi"
2021-10-28 15:48:15 +00:00
)
raise MoonstreamHTTPException(status_code=400)
2021-11-01 11:53:37 +00:00
if event.get("filters") and isinstance(event["filters"], dict):
2021-10-28 15:48:15 +00:00
for input_argument_name, value in event["filters"].items():
2021-11-01 11:53:37 +00:00
if input_argument_name not in abi_events[event["name"]]:
2021-10-28 15:48:15 +00:00
# Argument not exists
logger.error(
f"Error on dashboard resource validation type argument: {input_argument_name} of method:{event['name']} "
f" of subscription: {dashboard_subscription.subscription_id} has incorrect"
f"does not exists in Abi"
2021-10-28 15:48:15 +00:00
)
raise MoonstreamHTTPException(status_code=400)
if not isinstance(
2021-11-01 11:53:37 +00:00
value,
2021-10-28 15:48:15 +00:00
json_type(abi_events[event["name"]][input_argument_name]),
):
logger.error(
f"Error on dashboard resource validation type argument: {input_argument_name} of method:{event['name']} "
f" of subscription: {dashboard_subscription.subscription_id} has incorrect type {type(value)}"
2021-11-01 11:53:37 +00:00
f" when {abi_events[event['name']][input_argument_name]} required."
2021-10-28 15:48:15 +00:00
)
raise MoonstreamHTTPException(status_code=400)
2021-11-01 11:53:37 +00:00
return True
2021-11-11 14:43:51 +00:00
def validate_abi_json(abi: Any) -> None:
2021-11-09 12:33:10 +00:00
"""
Transform string to json and run validation
"""
try:
2021-11-11 14:37:19 +00:00
validate_abi(abi)
2021-11-09 12:33:10 +00:00
except ValueError as e:
raise MoonstreamHTTPException(status_code=400, detail=e)
except:
raise MoonstreamHTTPException(
status_code=400, detail="Error on abi valiadation."
)
def upload_abi_to_s3(
2022-03-08 15:10:06 +00:00
resource: BugoutResource,
abi: str,
update: Dict[str, Any],
2021-11-09 12:33:10 +00:00
) -> Dict[str, Any]:
"""
Uploading ABI to s3 bucket. Return object for updating resource.
"""
s3 = boto3.client("s3")
2021-11-09 12:33:10 +00:00
bucket = MOONSTREAM_S3_SMARTCONTRACTS_ABI_BUCKET
2021-11-09 12:33:10 +00:00
result_bytes = abi.encode("utf-8")
result_key = f"{MOONSTREAM_S3_SMARTCONTRACTS_ABI_PREFIX}/{blockchain_by_subscription_id[resource.resource_data['subscription_type_id']]}/abi/{resource.resource_data['address']}/{resource.id}/abi.json"
2021-11-09 12:33:10 +00:00
s3.put_object(
2021-11-09 12:33:10 +00:00
Body=result_bytes,
Bucket=bucket,
Key=result_key,
ContentType="application/json",
Metadata={"Moonstream": "Abi data"},
)
update["abi"] = True
update["bucket"] = MOONSTREAM_S3_SMARTCONTRACTS_ABI_BUCKET
update["s3_path"] = result_key
2021-11-09 12:33:10 +00:00
return update
2021-12-03 20:53:05 +00:00
def get_all_entries_from_search(
journal_id: str, search_query: str, limit: int, token: str
) -> List[BugoutSearchResult]:
2021-12-03 20:53:05 +00:00
"""
Get all required entries from journal using search interface
"""
offset = 0
results: List[BugoutSearchResult] = []
2021-12-03 20:53:05 +00:00
2021-12-03 22:37:20 +00:00
try:
existing_metods = bc.search(
token=token,
journal_id=journal_id,
query=search_query,
content=False,
timeout=10.0,
limit=limit,
offset=offset,
)
results.extend(existing_metods.results)
2021-12-06 14:38:08 +00:00
except Exception as e:
2021-12-06 15:28:13 +00:00
reporter.error_report(e)
2021-12-03 22:37:20 +00:00
if len(results) != existing_metods.total_results:
for offset in range(limit, existing_metods.total_results, limit):
2021-12-03 20:53:05 +00:00
existing_metods = bc.search(
token=token,
journal_id=journal_id,
query=search_query,
content=False,
timeout=10.0,
limit=limit,
offset=offset,
)
2021-12-03 22:37:20 +00:00
results.extend(existing_metods.results)
2021-12-03 20:53:05 +00:00
return results
2022-03-08 15:10:06 +00:00
def apply_moonworm_tasks(
subscription_type: str,
abi: Any,
address: str,
2023-05-11 14:20:34 +00:00
entries_limit: int = 100,
2022-03-08 15:10:06 +00:00
) -> None:
2021-12-03 20:53:05 +00:00
"""
Get list of subscriptions loads abi and apply them as moonworm tasks if it not exist
"""
2023-05-11 14:20:34 +00:00
moonworm_abi_tasks_entries_pack = []
2021-12-03 20:53:05 +00:00
try:
entries = get_all_entries_from_search(
journal_id=MOONSTREAM_MOONWORM_TASKS_JOURNAL,
search_query=f"tag:address:{address} tag:subscription_type:{subscription_type}",
2023-05-11 14:20:34 +00:00
limit=entries_limit, # load per request
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
)
2021-12-06 15:21:13 +00:00
2023-05-09 12:29:17 +00:00
# create historical crawl task in journal
2023-05-11 14:20:34 +00:00
# will use create_entries_pack for creating entries in journal
2023-05-09 12:29:17 +00:00
existing_tags = [entry.tags for entry in entries]
existing_hashes = [
tag.split(":")[-1]
for tag in chain(*existing_tags)
2021-12-09 14:11:39 +00:00
if "abi_method_hash" in tag
]
abi_hashes_dict = {
hashlib.md5(json.dumps(method).encode("utf-8")).hexdigest(): method
for method in abi
if (method["type"] in ("event", "function"))
and (method.get("stateMutability", "") != "view")
}
for hash in abi_hashes_dict:
if hash not in existing_hashes:
2023-05-29 13:46:11 +00:00
abi_selector = Web3.keccak(
text=abi_hashes_dict[hash]["name"]
+ "("
+ ",".join(
map(lambda x: x["type"], abi_hashes_dict[hash]["inputs"])
)
+ ")"
)[:4].hex()
2023-05-11 14:20:34 +00:00
moonworm_abi_tasks_entries_pack.append(
{
"title": address,
"content": json.dumps(abi_hashes_dict[hash], indent=4),
"tags": [
f"address:{address}",
f"type:{abi_hashes_dict[hash]['type']}",
2021-12-09 14:11:39 +00:00
f"abi_method_hash:{hash}",
2023-05-29 13:46:11 +00:00
f"abi_selector:{abi_selector}",
f"subscription_type:{subscription_type}",
f"abi_name:{abi_hashes_dict[hash]['name']}",
f"status:active",
2023-05-11 14:20:34 +00:00
f"task_type:moonworm",
2023-05-25 13:06:33 +00:00
f"moonworm_task_pickedup:False", # True if task picked up by moonworm-crawler(default each 120 sec)
2023-05-11 14:20:34 +00:00
f"historical_crawl_status:pending", # pending, in_progress, done
f"progress:0", # 0-100 %
],
}
2021-12-06 15:21:13 +00:00
)
except Exception as e:
2023-05-25 13:06:33 +00:00
logger.error(f"Error get moonworm tasks: {str(e)}")
reporter.error_report(e)
2021-12-03 20:53:05 +00:00
2023-05-11 14:20:34 +00:00
if len(moonworm_abi_tasks_entries_pack) > 0:
2023-05-25 13:06:33 +00:00
try:
bc.create_entries_pack(
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
journal_id=MOONSTREAM_MOONWORM_TASKS_JOURNAL,
entries=moonworm_abi_tasks_entries_pack,
timeout=25,
)
except Exception as e:
logger.error(f"Error create moonworm tasks: {str(e)}")
reporter.error_report(e)
2022-02-17 10:22:20 +00:00
2022-03-07 18:08:35 +00:00
def name_normalization(query_name: str) -> str:
"""
2022-03-09 12:29:02 +00:00
Sanitize provided query name.
2022-03-07 18:08:35 +00:00
"""
try:
2022-03-09 13:05:31 +00:00
normalized_query_name = slugify(
query_name, max_length=50, lowercase=False, separator="_"
)
2022-03-07 18:08:35 +00:00
except Exception as e:
2022-03-08 15:06:50 +00:00
logger.error(f"Error in query normalization. Error: {e}")
raise NameNormalizationException(f"Can't normalize name:{query_name}")
2022-03-07 18:08:35 +00:00
return normalized_query_name
2022-02-17 10:22:20 +00:00
def get_query_by_name(query_name: str, token: uuid.UUID) -> str:
2022-03-09 12:29:02 +00:00
"""
Fetch query_id from Brood resources.
"""
try:
query_name = name_normalization(query_name)
except Exception:
raise NameNormalizationException("Unable to normalize query name")
2022-03-07 18:08:35 +00:00
2022-03-07 17:22:00 +00:00
params = {"type": data.BUGOUT_RESOURCE_QUERY_RESOLVER, "name": query_name}
2022-02-17 10:22:20 +00:00
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(f"Error get query, error: {str(e)}")
raise MoonstreamHTTPException(status_code=500, internal_error=e)
available_queries: Dict[str, str] = {
resource.resource_data["name"]: resource.resource_data["entry_id"]
for resource in resources.resources
}
if query_name not in available_queries:
raise MoonstreamHTTPException(status_code=404, detail="Query not found.")
query_id = available_queries[query_name]
return query_id
def get_entity_subscription_collection_id(
resource_type: str,
token: Union[uuid.UUID, str],
user_id: uuid.UUID,
create_if_not_exist: bool = False,
) -> Optional[str]:
"""
2023-02-09 20:01:41 +00:00
Get collection_id from brood resources. If collection not exist and create_if_not_exist is True
"""
params = {
"type": resource_type,
"user_id": str(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(
f"Error listing subscriptions for user ({user_id}) with token ({token}), error: {str(e)}"
)
reporter.error_report(e)
raise MoonstreamHTTPException(status_code=500, internal_error=e)
if len(resources.resources) == 0:
if not create_if_not_exist:
raise EntityCollectionNotFoundException(
"Subscription collection not found."
)
2023-05-09 10:11:34 +00:00
collection_id = generate_collection_for_user(resource_type, token, user_id)
return collection_id
2023-02-08 21:30:45 +00:00
2023-05-08 13:04:32 +00:00
else:
resource = resources.resources[0]
return resource.resource_data["collection_id"]
2023-02-08 21:30:45 +00:00
2023-05-08 13:04:32 +00:00
def generate_collection_for_user(
resource_type: str,
token: Union[uuid.UUID, str],
user_id: uuid.UUID,
) -> str:
try:
# try get collection
2023-05-08 13:04:32 +00:00
collections: EntityCollectionsResponse = ec.list_collections(token=token)
available_collections: Dict[str, str] = {
collection.name: collection.collection_id
for collection in collections.collections
}
2023-05-09 10:11:34 +00:00
subscription_collection_name = f"subscriptions_{user_id}"
if subscription_collection_name not in available_collections:
2023-05-08 13:04:32 +00:00
collection: EntityCollectionResponse = ec.add_collection(
2023-05-09 10:11:34 +00:00
token=token, name=subscription_collection_name
)
2023-05-08 13:04:32 +00:00
collection_id = collection.collection_id
else:
2023-05-09 10:11:34 +00:00
collection_id = available_collections[subscription_collection_name]
2023-05-08 13:04:32 +00:00
except EntityUnexpectedResponse as e:
logger.error(f"Error create collection, error: {str(e)}")
raise MoonstreamHTTPException(
status_code=500, detail="Can't create collection for subscriptions"
)
resource_data = {
"type": resource_type,
"user_id": str(user_id),
"collection_id": str(collection_id),
}
try:
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:
logger.error(f"Error creating subscription resource: {str(e)}")
logger.error(
f"Required create resource data: {resource_data}, and grand access to journal: {collection_id}, for user: {user_id}"
)
raise MoonstreamHTTPException(status_code=500, internal_error=e)
try:
bc.update_journal_scopes(
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
journal_id=collection_id,
holder_type="user",
holder_id=user_id,
permission_list=[
"journals.read",
"journals.entries.read",
"journals.entries.create",
"journals.entries.update",
"journals.entries.delete",
],
)
2023-05-09 10:11:34 +00:00
logger.info(
f"Grand access to journal: {collection_id}, for user: {user_id} successfully"
)
2023-05-08 13:04:32 +00:00
except Exception as e:
logger.error(f"Error updating journal scopes: {str(e)}")
logger.error(
f"Required grand access to journal: {collection_id}, for user: {user_id}"
)
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return collection_id
def generate_s3_access_links(
method_name: str,
bucket: str,
key: str,
http_method: str,
expiration: int = 300,
) -> str:
s3 = boto3.client("s3")
stats_presigned_url = s3.generate_presigned_url(
method_name,
Params={
"Bucket": bucket,
"Key": key,
},
ExpiresIn=expiration,
HttpMethod=http_method,
)
return stats_presigned_url
def query_parameter_hash(params: Dict[str, Any]) -> str:
"""
Generate a hash of the query parameters
"""
2023-03-09 17:49:00 +00:00
hash = hashlib.md5(
json.dumps(OrderedDict(params), sort_keys=True).encode("utf-8")
).hexdigest()
return hash
2023-05-25 14:00:20 +00:00
def get_moonworm_jobs(
address: str,
subscription_type_id: str,
entries_limit: int = 100,
):
entries = get_all_entries_from_search(
journal_id=MOONSTREAM_MOONWORM_TASKS_JOURNAL,
search_query=f"tag:address:{address} tag:subscription_type:{subscription_type_id}",
limit=entries_limit, # load per request
token=MOONSTREAM_ADMIN_ACCESS_TOKEN,
)
return entries