use std::time::Duration;
use crate::{SdkError, types::Headers, wit};
pub fn send<T>(name: &str, data: T) -> Result<(), SdkError>
where
T: serde::Serialize,
{
if !crate::component::can_skip_sending_events() {
let data = minicbor_serde::to_vec(data)?;
crate::component::queue_event(name, &data);
}
Ok(())
}
pub struct EventQueue(wit::EventQueue);
impl From<wit::EventQueue> for EventQueue {
fn from(value: wit::EventQueue) -> Self {
Self(value)
}
}
impl EventQueue {
pub fn pop(&self) -> Option<Event> {
self.0.pop().map(Into::into)
}
}
#[non_exhaustive]
pub enum Event {
Operation(ExecutedOperation),
Subgraph(ExecutedSubgraphRequest),
Http(ExecutedHttpRequest),
Extension(ExtensionEvent),
}
impl From<wit::Event> for Event {
fn from(value: wit::Event) -> Self {
match value {
wit::Event::Operation(executed_operation) => Self::Operation(executed_operation.into()),
wit::Event::Subgraph(executed_subgraph_request) => Self::Subgraph(executed_subgraph_request.into()),
wit::Event::Http(executed_http_request) => Self::Http(executed_http_request.into()),
wit::Event::Extension(event) => Self::Extension(event.into()),
}
}
}
#[non_exhaustive]
pub struct ExecutedOperation {
pub name: Option<String>,
pub document: String,
pub prepare_duration: Duration,
pub duration: Duration,
pub cached_plan: bool,
pub status: GraphqlResponseStatus,
pub operation_type: OperationType,
pub complexity: Option<u64>,
pub has_deprecated_fields: bool,
}
impl From<wit::ExecutedOperation> for ExecutedOperation {
fn from(value: wit::ExecutedOperation) -> Self {
ExecutedOperation {
name: value.name,
document: value.document,
prepare_duration: Duration::from_nanos(value.prepare_duration_ns),
duration: Duration::from_nanos(value.duration_ns),
cached_plan: value.cached_plan,
status: value.status.into(),
operation_type: value.operation_type.into(),
complexity: value.complexity,
has_deprecated_fields: value.has_deprecated_fields,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationType {
Query,
Mutation,
Subscription,
}
impl From<wit::OperationType> for OperationType {
fn from(value: wit::OperationType) -> Self {
match value {
wit::OperationType::Query => OperationType::Query,
wit::OperationType::Mutation => OperationType::Mutation,
wit::OperationType::Subscription => OperationType::Subscription,
}
}
}
#[derive(serde::Serialize, Debug, Clone)]
pub enum GraphqlResponseStatus {
Success,
FieldError(FieldError),
RequestError(RequestError),
RefusedRequest,
}
#[derive(serde::Serialize, Debug, Clone)]
#[non_exhaustive]
pub struct FieldError {
pub count: u64,
pub data_is_null: bool,
}
#[derive(serde::Serialize, Debug, Clone)]
#[non_exhaustive]
pub struct RequestError {
pub count: u64,
}
impl From<wit::GraphqlResponseStatus> for GraphqlResponseStatus {
fn from(value: wit::GraphqlResponseStatus) -> Self {
match value {
wit::GraphqlResponseStatus::Success => GraphqlResponseStatus::Success,
wit::GraphqlResponseStatus::FieldError(wit::FieldError { count, data_is_null }) => {
GraphqlResponseStatus::FieldError(FieldError { count, data_is_null })
}
wit::GraphqlResponseStatus::RequestError(wit::RequestError { count }) => {
GraphqlResponseStatus::RequestError(RequestError { count })
}
wit::GraphqlResponseStatus::RefusedRequest => GraphqlResponseStatus::RefusedRequest,
}
}
}
#[non_exhaustive]
pub struct ExecutedSubgraphRequest {
pub subgraph_name: String,
pub method: http::Method,
pub url: String,
pub cache_status: CacheStatus,
pub total_duration: Duration,
pub has_errors: bool,
executions: Vec<wit::SubgraphRequestExecutionKind>,
}
impl ExecutedSubgraphRequest {
pub fn into_executions(self) -> impl Iterator<Item = RequestExecution> {
self.executions.into_iter().map(RequestExecution::from)
}
}
impl From<wit::ExecutedSubgraphRequest> for ExecutedSubgraphRequest {
fn from(value: wit::ExecutedSubgraphRequest) -> Self {
Self {
subgraph_name: value.subgraph_name,
method: value.method.into(),
url: value.url,
cache_status: value.cache_status.into(),
total_duration: Duration::from_nanos(value.total_duration_ns),
has_errors: value.has_errors,
executions: value.executions,
}
}
}
#[non_exhaustive]
pub enum RequestExecution {
InternalServerError,
RequestError,
RateLimited,
Response(SubgraphResponse),
}
impl From<wit::SubgraphRequestExecutionKind> for RequestExecution {
fn from(value: wit::SubgraphRequestExecutionKind) -> Self {
match value {
wit::SubgraphRequestExecutionKind::InternalServerError => Self::InternalServerError,
wit::SubgraphRequestExecutionKind::RequestError => Self::RequestError,
wit::SubgraphRequestExecutionKind::RateLimited => Self::RateLimited,
wit::SubgraphRequestExecutionKind::Response(subgraph_response) => Self::Response(subgraph_response.into()),
}
}
}
#[non_exhaustive]
pub struct SubgraphResponse {
pub connection_time: Duration,
pub response_time: Duration,
pub status_code: http::StatusCode,
pub response_headers: Headers,
}
impl From<wit::SubgraphResponse> for SubgraphResponse {
fn from(value: wit::SubgraphResponse) -> Self {
Self {
connection_time: Duration::from_nanos(value.connection_time_ns),
response_time: Duration::from_nanos(value.response_time_ns),
status_code: http::StatusCode::from_u16(value.status_code).expect("Gateway provides a valid status code"),
response_headers: value.response_headers.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CacheStatus {
Hit,
PartialHit,
Miss,
}
impl From<wit::CacheStatus> for CacheStatus {
fn from(value: wit::CacheStatus) -> Self {
match value {
wit::CacheStatus::Hit => Self::Hit,
wit::CacheStatus::PartialHit => Self::PartialHit,
wit::CacheStatus::Miss => Self::Miss,
}
}
}
impl CacheStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Hit => "hit",
Self::PartialHit => "partial_hit",
Self::Miss => "miss",
}
}
}
impl AsRef<str> for CacheStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[non_exhaustive]
pub struct ExecutedHttpRequest {
pub status_code: http::StatusCode,
pub method: http::Method,
pub url: String,
}
impl From<wit::ExecutedHttpRequest> for ExecutedHttpRequest {
fn from(value: wit::ExecutedHttpRequest) -> Self {
Self {
status_code: http::StatusCode::from_u16(value.status_code).expect("Gateway provides a valid status code"),
method: value.method.into(),
url: value.url,
}
}
}
#[non_exhaustive]
pub struct ExtensionEvent {
pub event_name: String,
pub extension_name: String,
data: Vec<u8>,
}
impl ExtensionEvent {
pub fn deserialize<'de, T>(&'de self) -> Result<T, SdkError>
where
T: serde::Deserialize<'de>,
{
let data = minicbor_serde::from_slice(&self.data)?;
Ok(data)
}
}
impl From<wit::ExtensionEvent> for ExtensionEvent {
fn from(value: wit::ExtensionEvent) -> Self {
Self {
event_name: value.event_name,
extension_name: value.extension_name,
data: value.data,
}
}
}