From 67e8d8319af95a897c9965017487e0f19622947d Mon Sep 17 00:00:00 2001 From: Simon Ray Date: Mon, 21 Apr 2025 22:40:20 +0700 Subject: [PATCH 1/3] add queue --- cloudflare/Cargo.toml | 1 + cloudflare/src/endpoints/api_endpoint.rs | 147 ++++++++++++++++++ cloudflare/src/endpoints/mod.rs | 4 + cloudflare/src/endpoints/queue/consumer.rs | 49 ++++++ .../src/endpoints/queue/consumer_types.rs | 91 +++++++++++ cloudflare/src/endpoints/queue/messages.rs | 26 ++++ .../src/endpoints/queue/messages_types.rs | 42 +++++ cloudflare/src/endpoints/queue/mod.rs | 64 ++++++++ cloudflare/src/endpoints/queue/purge.rs | 25 +++ cloudflare/src/endpoints/queue/types.rs | 64 ++++++++ cloudflare/src/endpoints/shared_types.rs | 12 ++ cloudflare/src/lib.rs | 2 + 12 files changed, 527 insertions(+) create mode 100644 cloudflare/src/endpoints/api_endpoint.rs create mode 100644 cloudflare/src/endpoints/queue/consumer.rs create mode 100644 cloudflare/src/endpoints/queue/consumer_types.rs create mode 100644 cloudflare/src/endpoints/queue/messages.rs create mode 100644 cloudflare/src/endpoints/queue/messages_types.rs create mode 100644 cloudflare/src/endpoints/queue/mod.rs create mode 100644 cloudflare/src/endpoints/queue/purge.rs create mode 100644 cloudflare/src/endpoints/queue/types.rs create mode 100644 cloudflare/src/endpoints/shared_types.rs diff --git a/cloudflare/Cargo.toml b/cloudflare/Cargo.toml index c7a6fbcd..f10b1b51 100644 --- a/cloudflare/Cargo.toml +++ b/cloudflare/Cargo.toml @@ -36,6 +36,7 @@ thiserror = "2" url = "2.2" urlencoding = "2.1.3" uuid = { version = "1.0", features = ["serde"] } +paste = "1.0.15" [dev-dependencies] mockito = { version = "1.6.1" } diff --git a/cloudflare/src/endpoints/api_endpoint.rs b/cloudflare/src/endpoints/api_endpoint.rs new file mode 100644 index 00000000..a2d4d4d3 --- /dev/null +++ b/cloudflare/src/endpoints/api_endpoint.rs @@ -0,0 +1,147 @@ +#[macro_export] +macro_rules! api_resp { + ($name:ident { $($field:ident : $ty:ty),* $(,)? }) => { + paste! { + #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] + #[serde(rename_all = "snake_case")] + pub struct [<$name Response>] { + pub results: [<$name Results>], + pub errors: Option>, + pub messages: Option>, + pub success: Option, + #[serde(flatten)] + pub other: std::collections::HashMap>, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] + #[serde(rename_all = "snake_case")] + pub struct [<$name Results>] { + $( + pub $field: Option<$ty>, + )* + } + } + }; +} +#[macro_export] +macro_rules! api_gen { + ($st:ty) => { + impl ApiResult for $st {} + }; +} +#[macro_export] +macro_rules! api_endpoint { + // Pattern 1: With params (explicit method and params marker) and return type + ($method:ident, $name:ident => $response:ty > $resp_type:ty, $path:expr; $($field:ident),+; params: $params:ty) => { + paste! { + #[derive(Debug)] + pub struct $name<'a> { + $(pub $field: &'a str,)+ + pub params: $params, + } + api_resp!($name { + result: $resp_type, + }); + api_gen!([<$name Response>]); + impl<'a> EndpointSpec for $name<'a> { + type JsonResponse = [<$name Response>]; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::$method + } + + fn path(&self) -> String { + format!($path, $(self.$field),+) + } + + #[inline] + fn body(&self) -> Option { + Some(RequestBody::Json( + serde_json::to_string(&self.params) + .expect("Failed to serialize request body") + )) + } + } + } + }; + + // Pattern 2: Without params (explicit method) and return type + ($method:ident, $name:ident => $response:ty > $resp_type:ty , $path:expr; $($field:ident),+) => { + #[derive(Debug)] + pub struct $name<'a> { + $(pub $field: &'a str,)+ + } + + api_resp!($name { + result: $resp_type, + }); + + impl<'a> EndpointSpec for $name<'a> { + type JsonResponse = $response; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::$method + } + + fn path(&self) -> String { + format!($path, $(self.$field),+) + } + + } + }; + + // Pattern 3: With params (explicit method and params marker) + ($method:ident, $name:ident => $response:ty , $path:expr; $($field:ident),+; params: $params:ty) => { + #[derive(Debug)] + pub struct $name<'a> { + $(pub $field: &'a str,)+ + pub params: $params, + } + + impl<'a> EndpointSpec for $name<'a> { + type JsonResponse = $response; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::$method + } + + fn path(&self) -> String { + format!($path, $(self.$field),+) + } + + #[inline] + fn body(&self) -> Option { + Some(RequestBody::Json( + serde_json::to_string(&self.params) + .expect("Failed to serialize request body") + )) + } + } + }; + + // Pattern 4: Without params (explicit method) and return type + ($method:ident, $name:ident => $response:ty , $path:expr; $($field:ident),+) => { + #[derive(Debug)] + pub struct $name<'a> { + $(pub $field: &'a str,)+ + } + + impl<'a> EndpointSpec for $name<'a> { + type JsonResponse = $response; + type ResponseType = ApiSuccess; + + fn method(&self) -> Method { + Method::$method + } + + fn path(&self) -> String { + format!($path, $(self.$field),+) + } + + } + }; + +} diff --git a/cloudflare/src/endpoints/mod.rs b/cloudflare/src/endpoints/mod.rs index f590cc90..c762a12f 100644 --- a/cloudflare/src/endpoints/mod.rs +++ b/cloudflare/src/endpoints/mod.rs @@ -9,7 +9,11 @@ pub mod argo_tunnel; pub mod cfd_tunnel; pub mod dns; pub mod load_balancing; +#[macro_use] +mod api_endpoint; +pub mod queue; pub mod r2; +pub mod shared_types; pub mod workers; pub mod workerskv; pub mod zones; diff --git a/cloudflare/src/endpoints/queue/consumer.rs b/cloudflare/src/endpoints/queue/consumer.rs new file mode 100644 index 00000000..771187e9 --- /dev/null +++ b/cloudflare/src/endpoints/queue/consumer.rs @@ -0,0 +1,49 @@ +use crate::api_endpoint; +use crate::endpoints::shared_types::APIResponse; +use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; +use crate::framework::response::{ApiResult, ApiSuccess}; +use serde::{Deserialize, Serialize}; + +use super::consumer_types::Consumer; + +// INFO: ref. https://developers.cloudflare.com/api/resources/queues/subresources/consumers/ + +// INFO: Creates a new consumer for a Queue +api_endpoint!( + POST, + CreateQueueConsumer => APIResponse > Consumer, + "accounts/{}/queues/{}/consumers"; + account_id, + queue_id; + params: Consumer +); + +// INFO: Deletes the consumer for a queue. +api_endpoint!( + DELETE, + DeleteQueueConsumer => APIResponse, + "accounts/{}/queues/{}/consumers/{}"; + account_id, + queue_id, + consumer_id +); + +// INFO: Returns the consumers for a Queue +api_endpoint!( + GET, + ListQueueConsumer => APIResponse > Vec , + "accounts/{}/queues/{}/consumers"; + account_id, + queue_id +); + +// INFO: Updates the consumer for a queue, or creates one if it does not exist. +api_endpoint!( + PUT, + UpdateQueueConsumer => APIResponse > Consumer, + "accounts/{}/queues/{}/consumers/{}"; + account_id, + queue_id, + consumer_id; + params: Consumer +); diff --git a/cloudflare/src/endpoints/queue/consumer_types.rs b/cloudflare/src/endpoints/queue/consumer_types.rs new file mode 100644 index 00000000..6470f076 --- /dev/null +++ b/cloudflare/src/endpoints/queue/consumer_types.rs @@ -0,0 +1,91 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[serde(untagged)] +pub enum Consumer { + Worker(MqWorkerConsumer), + HttpPull(MqHttpConsumer), +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct MqWorkerConsumer { + #[serde(skip_serializing_if = "Option::is_none")] + pub consumer_id: Option, // Optional (maxLength: 32) + + #[serde(skip_serializing_if = "Option::is_none")] + pub created_on: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub dead_letter_queue: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub queue_id: Option, // Optional (maxLength: 32) + + #[serde(skip_serializing_if = "Option::is_none")] + pub script: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub script_name: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, + + #[serde(rename = "type")] + pub type_: Option, // "worker" +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct WorkerSettings { + #[serde(skip_serializing_if = "Option::is_none")] + pub batch_size: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub max_concurrency: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub max_retries: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub max_wait_time_ms: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub retry_delay: Option, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct MqHttpConsumer { + #[serde(skip_serializing_if = "Option::is_none")] + pub consumer_id: Option, // Optional (maxLength: 32) + + #[serde(skip_serializing_if = "Option::is_none")] + pub created_on: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub dead_letter_queue: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub queue_id: Option, // Optional (maxLength: 32) + + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, + + #[serde(rename = "type")] + pub type_: Option, // "http_pull" +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct HttpSettings { + #[serde(skip_serializing_if = "Option::is_none")] + pub batch_size: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub max_retries: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub retry_delay: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub visibility_timeout_ms: Option, +} diff --git a/cloudflare/src/endpoints/queue/messages.rs b/cloudflare/src/endpoints/queue/messages.rs new file mode 100644 index 00000000..8f7d6e8a --- /dev/null +++ b/cloudflare/src/endpoints/queue/messages.rs @@ -0,0 +1,26 @@ +use crate::api_endpoint; +use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; +use crate::framework::response::ApiSuccess; + +use super::messages_types::{AckQueueResponse, ActionMessage, PullQueueResponse}; + +// INFO: ref. https://developers.cloudflare.com/api/resources/queues/subresources/messages/ + +// INFO: Acknowledge + Retry messages from a Queue +api_endpoint!( + POST, + AckRetry => AckQueueResponse, + "accounts/{}/queues/{}/messages/ack"; + account_id, + queue_id; + params: ActionMessage +); + +// INFO: Pull a batch of messages from a Queue +api_endpoint!( + POST, + PullQueue => PullQueueResponse, + "/accounts/{}/queues/{}/messages/pull"; + account_id, + queue_id +); diff --git a/cloudflare/src/endpoints/queue/messages_types.rs b/cloudflare/src/endpoints/queue/messages_types.rs new file mode 100644 index 00000000..b12c8e37 --- /dev/null +++ b/cloudflare/src/endpoints/queue/messages_types.rs @@ -0,0 +1,42 @@ +use crate::framework::response::ApiResult; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct ActionMessage { + #[serde(skip_serializing_if = "Option::is_none")] + acks: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + retries: Option>, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct AcksRequest { + // An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. + lease_id: String, +} +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct RetriesRequest { + // The number of seconds to delay before making the message available for another attempt. + #[serde(skip_serializing_if = "Option::is_none")] + delay_seconds: Option, + // An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. + #[serde(skip_serializing_if = "Option::is_none")] + lease_id: Option, +} + +api_resp!(AckQueue { + ack_count: i64, + retry_count: i64, + warnings: Vec, +}); + +api_gen!(AckQueueResponse); + +api_resp!(PullQueue { + id: String, + attempts: i64, + body: String, + lease_id: String, +}); + +api_gen!(PullQueueResponse); diff --git a/cloudflare/src/endpoints/queue/mod.rs b/cloudflare/src/endpoints/queue/mod.rs new file mode 100644 index 00000000..4a183a18 --- /dev/null +++ b/cloudflare/src/endpoints/queue/mod.rs @@ -0,0 +1,64 @@ +use crate::api_endpoint; +use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; +use crate::framework::response::{ApiResult, ApiSuccess}; +use serde::{Deserialize, Serialize}; +pub mod consumer; +pub mod consumer_types; +pub mod messages; +pub mod messages_types; +pub mod purge; +pub mod types; + +use types::*; + +use super::shared_types::APIResponse; + +api_gen!(APIResponse); +// INFO: ref: https://developers.cloudflare.com/api/resources/queues/ + +// INFO: Create a new queue +api_endpoint!( + POST, + CreateQueue => APIResponse > Queue, + "accounts/{}/queues"; + account_id; + params: Queue// Explicit params marker +); + +// INFO: Deletes a queue +api_endpoint!( + DELETE, + DeleteQueue => APIResponse, + "accounts/{}/queues/{}"; + account_id, + queue_id +); + +api_gen!(Queue); + +// INFO: Updates a Queue. +api_endpoint!( + PATCH, + UpdateQueue => Queue > Queue, + "accounts/{}/queues/{}"; + account_id, + queue_id; + params: Queue +); + +// INFO: Get details about a specific queue. +api_endpoint!( + GET, + GetQueue => APIResponse > Queue, + "accounts/{}/queues/{}"; + account_id, + queue_id +); + +// INFO: Returns the queues owned by an account. +api_endpoint!( + GET, + ListQueue => APIResponse > Vec, + "accounts/{}/queues"; + account_id +); diff --git a/cloudflare/src/endpoints/queue/purge.rs b/cloudflare/src/endpoints/queue/purge.rs new file mode 100644 index 00000000..d0cc2f05 --- /dev/null +++ b/cloudflare/src/endpoints/queue/purge.rs @@ -0,0 +1,25 @@ +use crate::api_endpoint; +use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; +use crate::framework::response::{ApiResult, ApiSuccess}; +use serde::{Deserialize, Serialize}; + +use super::types::Queue; + +// INFO: ref: https://developers.cloudflare.com/api/resources/queues/subresources/purge/ + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct ConfirmDelete { + delete_messages_permanently: bool, +} + +api_gen!(ConfirmDelete); + +// INFO:: Deletes all messages from the Queue. +api_endpoint! ( + POST, + PurgeQueue => APIResponse > Queue, + "accounts/{}/queues/{}/purge"; + account_id, + queue_id; + params: ConfirmDelete +); diff --git a/cloudflare/src/endpoints/queue/types.rs b/cloudflare/src/endpoints/queue/types.rs new file mode 100644 index 00000000..6e3e50e9 --- /dev/null +++ b/cloudflare/src/endpoints/queue/types.rs @@ -0,0 +1,64 @@ +use chrono::DateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ProducerType { + Worker, + R2Bucket, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct MqWorkerProducer { + #[serde(skip_serializing_if = "Option::is_none")] + pub script: Option, + #[serde(rename = "type")] + pub type_: ProducerType, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct MqR2Producer { + #[serde(skip_serializing_if = "Option::is_none")] + pub bucket_name: Option, + #[serde(rename = "type")] + pub type_: ProducerType, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[serde(untagged)] +pub enum Producer { + Worker(MqWorkerProducer), + R2Bucket(MqR2Producer), +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct QueueSettings { + #[serde(skip_serializing_if = "Option::is_none")] + pub delivery_delay: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub delivery_paused: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub message_retention_period: Option, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct Queue { + #[serde(skip_serializing_if = "Option::is_none")] + pub consumers: Option>, // Assuming Consumer is just a string for this example + #[serde(skip_serializing_if = "Option::is_none")] + pub consumers_total_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub created_on: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub modified_on: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub producers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub producers_total_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub queue_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub queue_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, +} diff --git a/cloudflare/src/endpoints/shared_types.rs b/cloudflare/src/endpoints/shared_types.rs new file mode 100644 index 00000000..e1089c9c --- /dev/null +++ b/cloudflare/src/endpoints/shared_types.rs @@ -0,0 +1,12 @@ +use crate::framework::response::ResponseInfo; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct APIResponse { + errors: Option>, + messages: Option>, + success: Option, + #[serde(flatten)] + pub other: HashMap, +} diff --git a/cloudflare/src/lib.rs b/cloudflare/src/lib.rs index 6518aa8c..7714cd16 100644 --- a/cloudflare/src/lib.rs +++ b/cloudflare/src/lib.rs @@ -1,4 +1,6 @@ #![forbid(unsafe_code)] +#[macro_use] +extern crate paste; pub mod endpoints; pub mod framework; From 99b2ee517f5b4969c1d53f7ce0ec45d177e18f65 Mon Sep 17 00:00:00 2001 From: Simon Ray Date: Tue, 22 Apr 2025 10:52:40 +0700 Subject: [PATCH 2/3] fix doc & update ref base response type --- cloudflare/src/endpoints/api_endpoint.rs | 93 +++---------------- cloudflare/src/endpoints/mod.rs | 1 - cloudflare/src/endpoints/queue/consumer.rs | 29 ++++-- .../src/endpoints/queue/consumer_types.rs | 27 +++++- cloudflare/src/endpoints/queue/messages.rs | 16 ++-- .../src/endpoints/queue/messages_types.rs | 15 +-- cloudflare/src/endpoints/queue/mod.rs | 39 ++++---- cloudflare/src/endpoints/queue/purge.rs | 14 ++- cloudflare/src/endpoints/shared_types.rs | 12 --- 9 files changed, 105 insertions(+), 141 deletions(-) delete mode 100644 cloudflare/src/endpoints/shared_types.rs diff --git a/cloudflare/src/endpoints/api_endpoint.rs b/cloudflare/src/endpoints/api_endpoint.rs index a2d4d4d3..86fb13ca 100644 --- a/cloudflare/src/endpoints/api_endpoint.rs +++ b/cloudflare/src/endpoints/api_endpoint.rs @@ -1,22 +1,17 @@ #[macro_export] -macro_rules! api_resp { - ($name:ident { $($field:ident : $ty:ty),* $(,)? }) => { +macro_rules! api_results { + ($name:ident { + $( + $(#[$field_meta:meta])* + $field:ident : $ty:ty + ),* $(,)? + }) => { paste! { - #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] - #[serde(rename_all = "snake_case")] - pub struct [<$name Response>] { - pub results: [<$name Results>], - pub errors: Option>, - pub messages: Option>, - pub success: Option, - #[serde(flatten)] - pub other: std::collections::HashMap>, - } - #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub struct [<$name Results>] { $( + $(#[$field_meta])* pub $field: Option<$ty>, )* } @@ -31,69 +26,10 @@ macro_rules! api_gen { } #[macro_export] macro_rules! api_endpoint { - // Pattern 1: With params (explicit method and params marker) and return type - ($method:ident, $name:ident => $response:ty > $resp_type:ty, $path:expr; $($field:ident),+; params: $params:ty) => { - paste! { - #[derive(Debug)] - pub struct $name<'a> { - $(pub $field: &'a str,)+ - pub params: $params, - } - api_resp!($name { - result: $resp_type, - }); - api_gen!([<$name Response>]); - impl<'a> EndpointSpec for $name<'a> { - type JsonResponse = [<$name Response>]; - type ResponseType = ApiSuccess; - - fn method(&self) -> Method { - Method::$method - } - - fn path(&self) -> String { - format!($path, $(self.$field),+) - } - - #[inline] - fn body(&self) -> Option { - Some(RequestBody::Json( - serde_json::to_string(&self.params) - .expect("Failed to serialize request body") - )) - } - } - } - }; - - // Pattern 2: Without params (explicit method) and return type - ($method:ident, $name:ident => $response:ty > $resp_type:ty , $path:expr; $($field:ident),+) => { - #[derive(Debug)] - pub struct $name<'a> { - $(pub $field: &'a str,)+ - } - - api_resp!($name { - result: $resp_type, - }); - - impl<'a> EndpointSpec for $name<'a> { - type JsonResponse = $response; - type ResponseType = ApiSuccess; - - fn method(&self) -> Method { - Method::$method - } - - fn path(&self) -> String { - format!($path, $(self.$field),+) - } - - } - }; - - // Pattern 3: With params (explicit method and params marker) - ($method:ident, $name:ident => $response:ty , $path:expr; $($field:ident),+; params: $params:ty) => { + // Pattern 1: With params (explicit method and params marker) + ($(#[$docs:meta])* + $method:ident, $name:ident => $response:ty , $path:expr; $($field:ident),+; params: $params:ty) => { + $(#[$docs])* #[derive(Debug)] pub struct $name<'a> { $(pub $field: &'a str,)+ @@ -122,8 +58,9 @@ macro_rules! api_endpoint { } }; - // Pattern 4: Without params (explicit method) and return type - ($method:ident, $name:ident => $response:ty , $path:expr; $($field:ident),+) => { + // Pattern 2: Without params (explicit method) and return type + ($(#[$docs:meta])* $method:ident, $name:ident => $response:ty , $path:expr; $($field:ident),+) => { + $(#[$docs])* #[derive(Debug)] pub struct $name<'a> { $(pub $field: &'a str,)+ diff --git a/cloudflare/src/endpoints/mod.rs b/cloudflare/src/endpoints/mod.rs index c762a12f..aae7ac2b 100644 --- a/cloudflare/src/endpoints/mod.rs +++ b/cloudflare/src/endpoints/mod.rs @@ -13,7 +13,6 @@ pub mod load_balancing; mod api_endpoint; pub mod queue; pub mod r2; -pub mod shared_types; pub mod workers; pub mod workerskv; pub mod zones; diff --git a/cloudflare/src/endpoints/queue/consumer.rs b/cloudflare/src/endpoints/queue/consumer.rs index 771187e9..b8e6b3a9 100644 --- a/cloudflare/src/endpoints/queue/consumer.rs +++ b/cloudflare/src/endpoints/queue/consumer.rs @@ -1,46 +1,55 @@ use crate::api_endpoint; -use crate::endpoints::shared_types::APIResponse; use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; use crate::framework::response::{ApiResult, ApiSuccess}; -use serde::{Deserialize, Serialize}; use super::consumer_types::Consumer; // INFO: ref. https://developers.cloudflare.com/api/resources/queues/subresources/consumers/ -// INFO: Creates a new consumer for a Queue +api_gen!(Consumer); api_endpoint!( + /// Create A Queue Consumer + /// Creates a new consumer for a Queue. + /// POST, - CreateQueueConsumer => APIResponse > Consumer, + CreateQueueConsumer => Consumer, "accounts/{}/queues/{}/consumers"; account_id, queue_id; params: Consumer ); -// INFO: Deletes the consumer for a queue. api_endpoint!( + /// Delete Queue Consumer + /// Deletes the consumer for a queue. + /// DELETE, - DeleteQueueConsumer => APIResponse, + DeleteQueueConsumer => (), "accounts/{}/queues/{}/consumers/{}"; account_id, queue_id, consumer_id ); -// INFO: Returns the consumers for a Queue +api_gen!(Vec); + api_endpoint!( + /// List Queue Consumers + /// Returns the consumers for a Queue. + /// GET, - ListQueueConsumer => APIResponse > Vec , + ListQueueConsumer => Vec, "accounts/{}/queues/{}/consumers"; account_id, queue_id ); -// INFO: Updates the consumer for a queue, or creates one if it does not exist. api_endpoint!( + /// Update Queue Consumer + /// Updates the consumer for a queue, or creates one if it does not exist.. + /// PUT, - UpdateQueueConsumer => APIResponse > Consumer, + UpdateQueueConsumer => Consumer, "accounts/{}/queues/{}/consumers/{}"; account_id, queue_id, diff --git a/cloudflare/src/endpoints/queue/consumer_types.rs b/cloudflare/src/endpoints/queue/consumer_types.rs index 6470f076..b74158a3 100644 --- a/cloudflare/src/endpoints/queue/consumer_types.rs +++ b/cloudflare/src/endpoints/queue/consumer_types.rs @@ -10,6 +10,7 @@ pub enum Consumer { #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub struct MqWorkerConsumer { + /// A Resource identifier. #[serde(skip_serializing_if = "Option::is_none")] pub consumer_id: Option, // Optional (maxLength: 32) @@ -19,12 +20,16 @@ pub struct MqWorkerConsumer { #[serde(skip_serializing_if = "Option::is_none")] pub dead_letter_queue: Option, + /// A Resource identifier. + /// Optional (maxLength: 32) #[serde(skip_serializing_if = "Option::is_none")] pub queue_id: Option, // Optional (maxLength: 32) + /// Name of a Worker #[serde(skip_serializing_if = "Option::is_none")] pub script: Option, + /// Name of a Worker #[serde(skip_serializing_if = "Option::is_none")] pub script_name: Option, @@ -32,23 +37,35 @@ pub struct MqWorkerConsumer { pub settings: Option, #[serde(rename = "type")] - pub type_: Option, // "worker" + pub type_: Option, // "worker" +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ConsumerType { + Worker, + HttpPull, } #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct WorkerSettings { + /// The maximum number of messages to include in a batch. #[serde(skip_serializing_if = "Option::is_none")] pub batch_size: Option, + /// Maximum number of concurrent consumers that may consume from this Queue. Set to null to automatically opt in to the platform's maximum (recommended). #[serde(skip_serializing_if = "Option::is_none")] pub max_concurrency: Option, + /// The maximum number of retries #[serde(skip_serializing_if = "Option::is_none")] pub max_retries: Option, + /// The number of milliseconds to wait for a batch to fill up before attempting to deliver it #[serde(skip_serializing_if = "Option::is_none")] pub max_wait_time_ms: Option, + /// The number of seconds to delay before making the message available for another attempt. #[serde(skip_serializing_if = "Option::is_none")] pub retry_delay: Option, } @@ -56,6 +73,7 @@ pub struct WorkerSettings { #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub struct MqHttpConsumer { + /// A Resource identifier. #[serde(skip_serializing_if = "Option::is_none")] pub consumer_id: Option, // Optional (maxLength: 32) @@ -65,6 +83,7 @@ pub struct MqHttpConsumer { #[serde(skip_serializing_if = "Option::is_none")] pub dead_letter_queue: Option, + /// A Resource identifier. #[serde(skip_serializing_if = "Option::is_none")] pub queue_id: Option, // Optional (maxLength: 32) @@ -72,20 +91,24 @@ pub struct MqHttpConsumer { pub settings: Option, #[serde(rename = "type")] - pub type_: Option, // "http_pull" + pub type_: Option, // "http_pull" } #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct HttpSettings { + /// The maximum number of messages to include in a batch. #[serde(skip_serializing_if = "Option::is_none")] pub batch_size: Option, + /// The maximum number of retries #[serde(skip_serializing_if = "Option::is_none")] pub max_retries: Option, + /// The number of seconds to delay before making the message available for another attempt. #[serde(skip_serializing_if = "Option::is_none")] pub retry_delay: Option, + /// The number of milliseconds that a message is exclusively leased. After the timeout, the message becomes available for another attempt. #[serde(skip_serializing_if = "Option::is_none")] pub visibility_timeout_ms: Option, } diff --git a/cloudflare/src/endpoints/queue/messages.rs b/cloudflare/src/endpoints/queue/messages.rs index 8f7d6e8a..028a0333 100644 --- a/cloudflare/src/endpoints/queue/messages.rs +++ b/cloudflare/src/endpoints/queue/messages.rs @@ -1,25 +1,29 @@ use crate::api_endpoint; use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; -use crate::framework::response::ApiSuccess; +use crate::framework::response::{ApiResult, ApiSuccess}; -use super::messages_types::{AckQueueResponse, ActionMessage, PullQueueResponse}; +use super::messages_types::{AckQueueResults, ActionMessage, PullQueueResults}; // INFO: ref. https://developers.cloudflare.com/api/resources/queues/subresources/messages/ -// INFO: Acknowledge + Retry messages from a Queue +api_gen!(AckQueueResults); api_endpoint!( + /// Acknowledge + Retry messages from a Queue. + /// POST, - AckRetry => AckQueueResponse, + AckRetry => AckQueueResults, "accounts/{}/queues/{}/messages/ack"; account_id, queue_id; params: ActionMessage ); -// INFO: Pull a batch of messages from a Queue +api_gen!(PullQueueResults); api_endpoint!( + /// Pull a batch of messages from a Queue. + /// POST, - PullQueue => PullQueueResponse, + PullQueue => PullQueueResults, "/accounts/{}/queues/{}/messages/pull"; account_id, queue_id diff --git a/cloudflare/src/endpoints/queue/messages_types.rs b/cloudflare/src/endpoints/queue/messages_types.rs index b12c8e37..780b054d 100644 --- a/cloudflare/src/endpoints/queue/messages_types.rs +++ b/cloudflare/src/endpoints/queue/messages_types.rs @@ -1,4 +1,3 @@ -use crate::framework::response::ApiResult; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] @@ -11,32 +10,28 @@ pub struct ActionMessage { #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct AcksRequest { - // An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. + /// An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. lease_id: String, } #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct RetriesRequest { - // The number of seconds to delay before making the message available for another attempt. + /// The number of seconds to delay before making the message available for another attempt. #[serde(skip_serializing_if = "Option::is_none")] delay_seconds: Option, - // An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. + /// An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. #[serde(skip_serializing_if = "Option::is_none")] lease_id: Option, } -api_resp!(AckQueue { +api_results!(AckQueue { ack_count: i64, retry_count: i64, warnings: Vec, }); -api_gen!(AckQueueResponse); - -api_resp!(PullQueue { +api_results!(PullQueue { id: String, attempts: i64, body: String, lease_id: String, }); - -api_gen!(PullQueueResponse); diff --git a/cloudflare/src/endpoints/queue/mod.rs b/cloudflare/src/endpoints/queue/mod.rs index 4a183a18..335ce8cd 100644 --- a/cloudflare/src/endpoints/queue/mod.rs +++ b/cloudflare/src/endpoints/queue/mod.rs @@ -1,64 +1,63 @@ use crate::api_endpoint; use crate::framework::endpoint::{EndpointSpec, Method, RequestBody}; use crate::framework::response::{ApiResult, ApiSuccess}; -use serde::{Deserialize, Serialize}; pub mod consumer; pub mod consumer_types; pub mod messages; pub mod messages_types; pub mod purge; pub mod types; - use types::*; -use super::shared_types::APIResponse; - -api_gen!(APIResponse); -// INFO: ref: https://developers.cloudflare.com/api/resources/queues/ - -// INFO: Create a new queue +api_gen!(Queue); api_endpoint!( + /// Create a new queue. + /// POST, - CreateQueue => APIResponse > Queue, + CreateQueue => Queue, "accounts/{}/queues"; account_id; - params: Queue// Explicit params marker + params: Queue ); -// INFO: Deletes a queue api_endpoint!( + /// Deletes a queue. + /// DELETE, - DeleteQueue => APIResponse, + DeleteQueue => (), "accounts/{}/queues/{}"; account_id, queue_id ); -api_gen!(Queue); - -// INFO: Updates a Queue. api_endpoint!( + /// Edit a Queue. + /// PATCH, - UpdateQueue => Queue > Queue, + UpdateQueue => Queue, "accounts/{}/queues/{}"; account_id, queue_id; params: Queue ); -// INFO: Get details about a specific queue. api_endpoint!( + /// Get details about a specific queue.. + /// GET, - GetQueue => APIResponse > Queue, + GetQueue => Queue, "accounts/{}/queues/{}"; account_id, queue_id ); -// INFO: Returns the queues owned by an account. +api_gen!(Vec); + api_endpoint!( + /// Returns the queues owned by an account.. + /// GET, - ListQueue => APIResponse > Vec, + ListQueue => Vec, "accounts/{}/queues"; account_id ); diff --git a/cloudflare/src/endpoints/queue/purge.rs b/cloudflare/src/endpoints/queue/purge.rs index d0cc2f05..41d6b5ee 100644 --- a/cloudflare/src/endpoints/queue/purge.rs +++ b/cloudflare/src/endpoints/queue/purge.rs @@ -9,17 +9,27 @@ use super::types::Queue; #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct ConfirmDelete { + /// Confimation that all messages will be deleted permanently. delete_messages_permanently: bool, } api_gen!(ConfirmDelete); -// INFO:: Deletes all messages from the Queue. api_endpoint! ( + /// Purge Queue + /// Deletes all messages from the Queue. + /// https://developers.cloudflare.com/api/resources/queues/subresources/purge/ POST, - PurgeQueue => APIResponse > Queue, + PurgeQueue => Queue, "accounts/{}/queues/{}/purge"; account_id, queue_id; params: ConfirmDelete ); + +api_results!(QueuePurgeStatus { + /// Indicates if the last purge operation completed successfully. + completed: String, + /// Timestamp when the last purge operation started. + started_at: String +}); diff --git a/cloudflare/src/endpoints/shared_types.rs b/cloudflare/src/endpoints/shared_types.rs deleted file mode 100644 index e1089c9c..00000000 --- a/cloudflare/src/endpoints/shared_types.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::framework::response::ResponseInfo; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] -pub struct APIResponse { - errors: Option>, - messages: Option>, - success: Option, - #[serde(flatten)] - pub other: HashMap, -} From 1eebb6fae3cfc33e3d197bc7b5f64bde871a0dea Mon Sep 17 00:00:00 2001 From: Simon Ray Date: Tue, 22 Apr 2025 11:35:44 +0700 Subject: [PATCH 3/3] fix doc --- cloudflare/src/endpoints/api_endpoint.rs | 1 - cloudflare/src/endpoints/queue/messages_types.rs | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cloudflare/src/endpoints/api_endpoint.rs b/cloudflare/src/endpoints/api_endpoint.rs index 86fb13ca..cd5adc5e 100644 --- a/cloudflare/src/endpoints/api_endpoint.rs +++ b/cloudflare/src/endpoints/api_endpoint.rs @@ -8,7 +8,6 @@ macro_rules! api_results { }) => { paste! { #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] - #[serde(rename_all = "snake_case")] pub struct [<$name Results>] { $( $(#[$field_meta])* diff --git a/cloudflare/src/endpoints/queue/messages_types.rs b/cloudflare/src/endpoints/queue/messages_types.rs index 780b054d..d8cde37c 100644 --- a/cloudflare/src/endpoints/queue/messages_types.rs +++ b/cloudflare/src/endpoints/queue/messages_types.rs @@ -24,8 +24,10 @@ pub struct RetriesRequest { } api_results!(AckQueue { - ack_count: i64, - retry_count: i64, + /// The number of messages that were succesfully acknowledged. + ackCount: u64, + /// The number of messages that were succesfully retried. + retryCount: u64, warnings: Vec, }); @@ -33,5 +35,6 @@ api_results!(PullQueue { id: String, attempts: i64, body: String, + /// An ID that represents an "in-flight" message that has been pulled from a Queue. You must hold on to this ID and use it to acknowledge this message. lease_id: String, });