sdk/python/shroomdk/integrations/query_integration/query_integration.py

138 lines
4.3 KiB
Python
Raw Normal View History

2022-07-22 18:57:26 +00:00
from typing import Union
2022-07-20 19:26:53 +00:00
from shroomdk.api import API
2022-07-22 18:57:26 +00:00
from shroomdk.errors import (
QueryRunExecutionError,
QueryRunTimeoutError,
SDKError,
ServerError,
UserError,
)
2022-07-20 19:26:53 +00:00
from shroomdk.models import (
Query,
2022-07-22 18:57:26 +00:00
QueryDefaults,
2022-07-20 19:26:53 +00:00
QueryResultSet,
QueryStatus,
2022-07-22 18:57:26 +00:00
SleepConfig,
2022-07-20 19:26:53 +00:00
)
2022-07-22 18:57:26 +00:00
from shroomdk.models.api import QueryResultJson
from shroomdk.utils.sleep import get_elapsed_linear_seconds, linear_backoff
2022-07-20 19:26:53 +00:00
2022-07-22 18:57:26 +00:00
from .query_result_set_builder import QueryResultSetBuilder
2022-07-20 19:26:53 +00:00
DEFAULTS: QueryDefaults = QueryDefaults(
2022-07-22 18:57:26 +00:00
ttl_minutes=60,
cached=True,
timeout_minutes=20,
retry_interval_seconds=0.5,
page_size=100000,
page_number=1,
2022-07-20 19:26:53 +00:00
)
class QueryIntegration(object):
def __init__(self, api: API, defaults: QueryDefaults = DEFAULTS):
self.api = api
self.defaults = defaults
2022-07-21 20:47:30 +00:00
2022-07-20 19:26:53 +00:00
def run(self, query: Query) -> QueryResultSet:
query = self._set_query_defaults(query)
created_query = self.api.create_query(query)
if created_query.status_code > 299:
2022-07-21 20:47:30 +00:00
if created_query.status_code < 500 and created_query.status_code >= 400:
raise UserError(created_query.status_code, created_query.error_msg)
elif created_query.status_code >= 500:
raise ServerError(created_query.status_code, created_query.error_msg)
else:
2022-07-22 18:57:26 +00:00
raise SDKError(
f"unknown SDK error when calling `api.create_query`, {created_query.error_msg}"
)
2022-07-20 19:26:53 +00:00
query_run = created_query.data
if not query_run:
2022-07-22 18:57:26 +00:00
raise SDKError("expected `created_query.data` from server but got `None`")
2022-07-20 19:26:53 +00:00
2022-07-21 20:47:30 +00:00
query_results = self._get_query_results(
2022-07-22 18:57:26 +00:00
query_run.token,
page_number=query.page_number,
2022-07-21 20:47:30 +00:00
page_size=query.page_size,
2022-07-22 18:57:26 +00:00
timeout_minutes=query.timeout_minutes if query.timeout_minutes else 20,
2022-07-21 20:47:30 +00:00
retry_interval_seconds=query.retry_interval_seconds
2022-07-22 18:57:26 +00:00
if query.retry_interval_seconds
else 1,
2022-07-20 19:26:53 +00:00
)
return QueryResultSetBuilder(query_results).build()
2022-07-21 20:47:30 +00:00
def _set_query_defaults(self, query: Query) -> Query:
query_default_dict = self.defaults.dict()
query_dict = query.dict()
2022-07-22 18:57:26 +00:00
query_default_dict.update(
{k: v for (k, v) in query_dict.items() if v is not None}
)
2022-07-21 20:47:30 +00:00
return Query(**query_default_dict)
2022-07-22 18:57:26 +00:00
def _get_query_results(
self,
query_run_id: str,
page_number: int = 1,
page_size: int = 100000,
attempts: int = 0,
timeout_minutes: Union[int, float] = 20,
retry_interval_seconds: Union[int, float] = 1.0,
) -> QueryResultJson:
2022-07-21 20:47:30 +00:00
2022-07-20 19:26:53 +00:00
query_run = self.api.get_query_result(query_run_id, page_number, page_size)
status_code = query_run.status_code
if status_code > 299:
2022-07-21 20:47:30 +00:00
error_msg = query_run.status_msg if query_run.status_msg else "error"
if query_run.error_msg:
error_msg = query_run.error_msg
2022-07-20 19:26:53 +00:00
if status_code >= 400 and status_code <= 499:
raise UserError(status_code, error_msg)
2022-07-21 20:47:30 +00:00
elif status_code >= 500:
raise ServerError(status_code, error_msg)
2022-07-20 19:26:53 +00:00
if not query_run.data:
2022-07-22 18:57:26 +00:00
raise SDKError(
"valid status msg returned from server but no data exists in the response"
)
2022-07-20 19:26:53 +00:00
query_status = query_run.data.status
if query_status == QueryStatus.Finished:
return query_run.data
2022-07-22 18:57:26 +00:00
2022-07-20 19:26:53 +00:00
if query_status == QueryStatus.Error:
raise QueryRunExecutionError()
2022-07-22 18:57:26 +00:00
2022-07-20 19:26:53 +00:00
should_continue = linear_backoff(
SleepConfig(
2022-07-22 18:57:26 +00:00
attempts=attempts,
timeout_minutes=timeout_minutes,
interval_seconds=retry_interval_seconds,
2022-07-20 19:26:53 +00:00
)
)
if not should_continue:
elapsed_seconds = get_elapsed_linear_seconds(
SleepConfig(
2022-07-22 18:57:26 +00:00
attempts=attempts,
timeout_minutes=timeout_minutes,
interval_seconds=retry_interval_seconds,
2022-07-20 19:26:53 +00:00
)
)
2022-07-22 18:57:26 +00:00
raise QueryRunTimeoutError(elapsed_seconds)
2022-07-20 19:26:53 +00:00
2022-07-22 18:57:26 +00:00
return self._get_query_results(
query_run_id,
page_number,
page_size,
attempts + 1,
timeout_minutes,
retry_interval_seconds,
)