hotdata 0.17.0

Powerful data platform API for datasets, queries, and analytics.
Documentation
/*
 * Hotdata API
 *
 * Powerful data platform API for instant databases, queries, and analytics.
 *
 * The version of the OpenAPI document: 1.0.0
 * Contact: developers@hotdata.dev
 * Generated by: https://openapi-generator.tech
 */

use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};

/// struct for typed errors of method [`get_result`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetResultError {
    Status400(models::ApiErrorResponse),
    Status404(models::ApiErrorResponse),
    Status409(models::GetResultResponse),
    Status413(models::ApiErrorResponse),
    Status429(models::ApiErrorResponse),
    Status503(models::ApiErrorResponse),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`list_results`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListResultsError {
    Status400(models::ApiErrorResponse),
    Status404(models::ApiErrorResponse),
    UnknownValue(serde_json::Value),
}

/// Retrieve a persisted query result by ID. The response format for the `ready` state is selected by `Accept` header or `?format=` query param; non-ready states use the same status codes and JSON body shape regardless of format.  | Result status         | Status × body                                                                | |-----------------------|------------------------------------------------------------------------------| | `ready` + JSON        | 200 `application/json` — `GetResultResponse` with `columns`, `rows`, etc.    | | `ready` + Arrow       | 200 `application/vnd.apache.arrow.stream` — schema, RecordBatches, EOS       | | `ready` + CSV         | 200 `text/csv; charset=utf-8` — single header row, streamed batch-by-batch   | | `ready` + Markdown    | 200 `text/markdown; charset=utf-8` — GitHub-flavored pipe table, streamed   | | `ready` + Parquet     | 200 `application/vnd.apache.parquet` — raw parquet bytes (no conversion)     | | `pending`/`processing`| 202 `application/json` `{status, result_id}` + `Retry-After`                 | | `failed`              | 409 `application/json` `{status, result_id, error_message}`                  | | not found             | 404 `application/json` (`ApiErrorResponse`)                                  |  `?format=` accepts `arrow`, `json`, `csv`, `md`, `parquet` and takes precedence over `Accept`. `markdown` is accepted as a runtime alias for `md`. Use `?offset=N&limit=M` to slice the result; `offset` defaults to 0 and `limit` is unbounded by default. Both must be non-negative; invalid values return 400. When a finite `limit` doesn't reach the end of the result, a `Link` header with `rel=\"next\"` points at the following page. `?offset`/`?limit` are ignored for `format=parquet` since that path returns the underlying file unchanged.  JSON is the only format with a size limit. Building a JSON body requires holding the whole slice in memory at once, so a request whose JSON body would exceed the instance's per-fetch memory budget is refused with 413 and the error code `RESULT_TOO_LARGE`, and a request that would push the total across concurrent JSON fetches over that budget is refused with 429. If the result's size cannot be determined at all, the fetch is refused with 503 rather than served unchecked. None of the three returns partial data. To read a result of any size, request it as `?format=arrow`, `csv`, `md`, or `parquet` — those stream and are never size-limited. Paging with `offset`/`limit` is not an alternative: the rows come back in no guaranteed order, so pages are not a reliable way to read one result in parts.  Ready responses (Arrow, CSV, Markdown, JSON) carry `X-Total-Row-Count` (the full result row count, independent of offset/limit); the JSON body repeats it as `total_row_count`, so a client that cannot read response headers can still compare it against `row_count` to tell a complete result from a windowed one. Responses are streamed end-to-end, so a client can disconnect at any time and the server stops reading.  IEEE special floats (`±Inf`, `NaN`) have no canonical JSON representation. For cross-format consistency the JSON, CSV, and Markdown paths emit them as `null` / empty cells, and JSON `nullable[]` is widened to match. The Arrow IPC and Parquet bodies are binary round-trip formats and preserve the raw IEEE values; callers cross-checking a result across CSV and Parquet should not byte-compare those slots.
pub async fn get_result(
    configuration: &configuration::Configuration,
    id: &str,
    x_database_id: &str,
    offset: Option<i32>,
    limit: Option<i32>,
    format: Option<models::ResultsFormatQuery>,
) -> Result<models::GetResultResponse, Error<GetResultError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_id = id;
    let p_header_x_database_id = x_database_id;
    let p_query_offset = offset;
    let p_query_limit = limit;
    let p_query_format = format;

    let uri_str = format!(
        "{}/v1/results/{id}",
        configuration.base_path,
        id = crate::apis::urlencode(p_path_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    if let Some(ref param_value) = p_query_offset {
        req_builder = req_builder.query(&[("offset", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_limit {
        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_format {
        req_builder = req_builder.query(&[("format", &param_value.to_string())]);
    }
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    req_builder = req_builder.header("X-Database-Id", p_header_x_database_id.to_string());
    if let Some(apikey) = configuration.api_keys.get("X-Workspace-Id") {
        let key = apikey.key.clone();
        let value = match apikey.prefix {
            Some(ref prefix) => format!("{} {}", prefix, key),
            None => key,
        };
        req_builder = req_builder.header("X-Workspace-Id", value);
    };
    if let Some(token) = configuration.resolve_bearer_token().await {
        req_builder = req_builder.bearer_auth(token);
    };

    let req = req_builder.build()?;
    crate::http_log::log_request(&req);
    // Route through the shared retry helper so HTTP 429 (OVERLOADED admission
    // shedding) is retried per `configuration.retry` on every generated op, not
    // just the hand-written query path. See crate::http::execute_retrying.
    let resp = crate::http::execute_retrying(configuration, req).await?;

    let status = resp.status();
    crate::http_log::log_response_status(status);
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        crate::http_log::log_response_body(&content);
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetResultResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetResultResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        crate::http_log::log_response_body(&content);
        let entity: Option<GetResultError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// List stored results for the database named by the required X-Database-Id header.
pub async fn list_results(
    configuration: &configuration::Configuration,
    x_database_id: &str,
    limit: Option<i32>,
    offset: Option<i32>,
) -> Result<models::ListResultsResponse, Error<ListResultsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_header_x_database_id = x_database_id;
    let p_query_limit = limit;
    let p_query_offset = offset;

    let uri_str = format!("{}/v1/results", configuration.base_path);
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    if let Some(ref param_value) = p_query_limit {
        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_offset {
        req_builder = req_builder.query(&[("offset", &param_value.to_string())]);
    }
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    req_builder = req_builder.header("X-Database-Id", p_header_x_database_id.to_string());
    if let Some(apikey) = configuration.api_keys.get("X-Workspace-Id") {
        let key = apikey.key.clone();
        let value = match apikey.prefix {
            Some(ref prefix) => format!("{} {}", prefix, key),
            None => key,
        };
        req_builder = req_builder.header("X-Workspace-Id", value);
    };
    if let Some(token) = configuration.resolve_bearer_token().await {
        req_builder = req_builder.bearer_auth(token);
    };

    let req = req_builder.build()?;
    crate::http_log::log_request(&req);
    // Route through the shared retry helper so HTTP 429 (OVERLOADED admission
    // shedding) is retried per `configuration.retry` on every generated op, not
    // just the hand-written query path. See crate::http::execute_retrying.
    let resp = crate::http::execute_retrying(configuration, req).await?;

    let status = resp.status();
    crate::http_log::log_response_status(status);
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        crate::http_log::log_response_body(&content);
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListResultsResponse`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListResultsResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        crate::http_log::log_response_body(&content);
        let entity: Option<ListResultsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}