diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 95dad6832..000000000 Binary files a/.DS_Store and /dev/null differ diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index b4c8a0471..4dc4868db 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -21,32 +21,29 @@ postgres = ["bytes", "tokio-postgres", "deadpool-postgres"] test-util = [] [[example]] -name = "channel_list_query" -required-features = ["test-util"] +name = "analytics_query" [[example]] -name = "campaign_list_query" -required-features = ["test-util"] +name = "analytics_response" [[example]] -name = "campaign_list_response" +name = "campaign_list_query" required-features = ["test-util"] [[example]] -name = "create_campaign" +name = "campaign_list_response" required-features = ["test-util"] [[example]] -name = "modify_campaign" +name = "channel_list_query" required-features = ["test-util"] [[example]] -name = "analytics_query" +name = "create_campaign" required-features = ["test-util"] [[example]] -name = "analytics_response" -required-features = ["test-util"] +name = "modify_campaign" [dependencies] # (De)Serialization diff --git a/primitives/examples/analytics_query.rs b/primitives/examples/analytics_query.rs index d330f3768..5fd4d98ef 100644 --- a/primitives/examples/analytics_query.rs +++ b/primitives/examples/analytics_query.rs @@ -41,8 +41,16 @@ fn main() { // Query with all possible fields (publisher/advertiser/admin) { - let query_str = "limit=200&eventType=CLICK&metric=paid&segmentBy=country&timeframe=week&start=2022-08-04+09:00:00.000000000+UTC&campaignId=0x936da01f9abd4d9d80c702af85c822a8&adUnit=QmcUVX7fvoLMM93uN2bD3wGTH8MXSxeL8hojYfL2Lhp7mR&adSlot=Qmasg8FrbuSQpjFu3kRnZF9beg8rEBFrqgi1uXDRwCbX5f&adSlotType=legacy_300x100&advertiser=0xDd589B43793934EF6Ad266067A0d1D4896b0dff0&publisher=0xE882ebF439207a70dDcCb39E13CA8506c9F45fD9\ - &hostname=localhost&country=Bulgaria&osName=Windows&chains[0]=1&chains[1]=1337"; + let query_str = "limit=200&eventType=CLICK&metric=paid&segmentBy=country\ + &timeframe=week&start=2022-08-04+09:00:00.000000000+UTC\ + &campaignId=0x936da01f9abd4d9d80c702af85c822a8\ + &adUnit=QmcUVX7fvoLMM93uN2bD3wGTH8MXSxeL8hojYfL2Lhp7mR\ + &adSlot=Qmasg8FrbuSQpjFu3kRnZF9beg8rEBFrqgi1uXDRwCbX5f\ + &adSlotType=legacy_300x100\ + &advertiser=0xDd589B43793934EF6Ad266067A0d1D4896b0dff0\ + &publisher=0xE882ebF439207a70dDcCb39E13CA8506c9F45fD9\ + &hostname=localhost&country=Bulgaria&osName=Windows\ + &chains[0]=1&chains[1]=1337"; let query: AnalyticsQuery = serde_qs::from_str(query_str).unwrap(); assert_eq!(query.limit, 200); diff --git a/primitives/examples/create_campaign.rs b/primitives/examples/create_campaign.rs index 7ce46741f..3b742ff50 100644 --- a/primitives/examples/create_campaign.rs +++ b/primitives/examples/create_campaign.rs @@ -3,15 +3,15 @@ use serde_json::json; use std::str::FromStr; fn main() { - // CreateCampaign in an HTTP request + // CreateCampaign in an HTTP request. + // A CampaignId will be randomly generated for the newly created Campaign. { let create_campaign = CreateCampaign::from_campaign_erased(DUMMY_CAMPAIGN.clone(), None); - let create_campaign_str = + let _create_campaign_str = serde_json::to_string(&create_campaign).expect("should serialize"); let create_campaign_json = json!({ - "id":null, "channel":{ "leader":"0x80690751969B234697e9059e04ed72195c3507fa", "follower":"0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7", @@ -34,7 +34,10 @@ fn main() { } ], "title":"Dummy Campaign", - "pricingBounds":{"CLICK":{"min":"0","max":"0"},"IMPRESSION":{"min":"1","max":"10"}}, + "pricingBounds":{ + "CLICK":{"min":"0","max":"0"}, + "IMPRESSION":{"min":"1","max":"10"} + }, "eventSubmission":{"allow":[]}, "targetingRules":[], "created":1612162800000_u64, @@ -43,6 +46,7 @@ fn main() { let create_campaign_json = serde_json::to_string(&create_campaign_json).expect("should serialize"); + let deserialized: CreateCampaign = serde_json::from_str(&create_campaign_json).expect("should deserialize"); diff --git a/primitives/examples/modify_campaign.rs b/primitives/examples/modify_campaign.rs index 3b663f9ca..8087c0c03 100644 --- a/primitives/examples/modify_campaign.rs +++ b/primitives/examples/modify_campaign.rs @@ -1,6 +1,5 @@ use primitives::{sentry::campaign_modify::ModifyCampaign, unified_num::FromWhole, UnifiedNum}; use serde_json::json; -use std::str::FromStr; fn main() { { diff --git a/primitives/src/sentry.rs b/primitives/src/sentry.rs index 601167811..f14d56f42 100644 --- a/primitives/src/sentry.rs +++ b/primitives/src/sentry.rs @@ -319,9 +319,10 @@ pub struct FetchedAnalytics { pub segment: Option, } -// Response returned when getting Analytics - an array of FetchedAnalytics -// -/// # Examples: +/// Response returned when getting Analytics which returns the [`FetchedAnalytics`]. +/// +/// # Examples +/// /// ``` #[doc = include_str!("../examples/analytics_response.rs")] /// ``` @@ -651,6 +652,13 @@ pub struct ValidationErrorResponse { pub validation: Vec, } +/// Request body for posting new [`Event`]s to a [`Campaign`](crate::Campaign). +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct InsertEventsRequest { + pub events: Vec, +} + pub mod channel_list { use crate::{ChainId, Channel, ValidatorId}; use serde::{Deserialize, Serialize}; diff --git a/sentry/.DS_Store b/sentry/.DS_Store deleted file mode 100644 index 69d690d20..000000000 Binary files a/sentry/.DS_Store and /dev/null differ diff --git a/sentry/src/routes.rs b/sentry/src/routes.rs index 518ae3b94..617ff456d 100644 --- a/sentry/src/routes.rs +++ b/sentry/src/routes.rs @@ -106,7 +106,8 @@ //! //! Request body (json): [`ValidatorMessagesCreateRequest`](primitives::sentry::ValidatorMessagesCreateRequest) //! -//! Example: +//! ##### Examples: +//! //! ```json //! { //! "messages": [ @@ -231,7 +232,7 @@ //! **Authentication is required** to validate [`Campaign.creator`](primitives::Campaign::creator) == [`Auth.uid`](crate::Auth::uid) //! //! It will make sure the `Channel` is created if new and it will update -//! the spendable amount using the [`Adapter`]`::get_deposit()`. +//! the spendable amount using the [`Adapter.get_deposit()`](adapter::client::Locked::get_deposit). //! //! The route is handled by [`campaign::create_campaign()`]. //! @@ -239,6 +240,12 @@ //! //! Response: [`Campaign`] //! +//! ##### Examples +//! +//! ``` +#![doc = include_str!("../../primitives/examples/create_campaign.rs")] +//! ``` +//! //! #### POST `/v5/campaign/:id` (auth required) //! //! Modify the [`Campaign`]. Request must be sent by the [`Campaign.creator`](primitives::Campaign::creator). @@ -251,6 +258,12 @@ //! //! Response: [`Campaign`] //! +//! ##### Examples +//! +//! ``` +#![doc = include_str!("../../primitives/examples/modify_campaign.rs")] +//! ``` +//! //! #### POST `/v5/campaign/:id/events` //! //! Add new [`Event`]s (`IMPRESSION`s & `CLICK`s) to the [`Campaign`]. @@ -258,15 +271,7 @@ //! //! The route is handled by [`campaign::insert_events::handle_route()`]. //! -//! Request body (json): -//! -//! ```json -//! { -//! "events": [ -//! // Events -//! ] -//! } -//! ``` +//! Request body (json): [`InsertEventsRequest`](primitives::sentry::InsertEventsRequest) //! //! Response: [`SuccessResponse`] //! @@ -290,10 +295,24 @@ //! //! Allowed keys: [`AllowedKey::Country`][primitives::analytics::query::AllowedKey::Country], [`AllowedKey::AdSlotType`][primitives::analytics::query::AllowedKey::AdSlotType] //! -//! Request query parameters: [`AnalyticsQuery`][primitives::analytics::AnalyticsQuery] +//! Request query parameters: [`AnalyticsQuery`] //! //! Response: [`AnalyticsResponse`] //! +//! ##### Examples +//! +//! Query: +//! +//! ``` +#![doc = include_str!("../../primitives/examples/analytics_query.rs")] +//! ``` +//! +//! Response: +//! +//! ``` +#![doc = include_str!("../../primitives/examples/analytics_response.rs")] +//! ``` +//! //! #### GET `/v5/analytics/for-publisher` (auth required) //! //! Returns all analytics where the currently authenticated address [`Auth.uid`](crate::Auth::uid) is a **publisher**. @@ -302,10 +321,14 @@ //! //! The route is handled by [`get_analytics()`]. //! -//! Request query parameters: [`AnalyticsQuery`][primitives::analytics::AnalyticsQuery] +//! Request query parameters: [`AnalyticsQuery`] //! //! Response: [`AnalyticsResponse`] //! +//! ##### Examples +//! +//! See [GET `/v5/analytics`](#get-v5analytics) +//! //! #### GET `/v5/analytics/for-advertiser` (auth required) //! //! Returns all analytics where the currently authenticated address [`Auth.uid`](crate::Auth::uid) is an **advertiser**. @@ -314,10 +337,14 @@ //! //! The route is handled by [`get_analytics()`]. //! -//! Request query parameters: [`AnalyticsQuery`][primitives::analytics::AnalyticsQuery] +//! Request query parameters: [`AnalyticsQuery`] //! //! Response: [`AnalyticsResponse`] //! +//! ##### Examples +//! +//! See [GET `/v5/analytics`](#get-v5analytics) +//! //! #### GET `/v5/analytics/for-admin` (auth required) //! //! Admin access to the analytics with no restrictions on the keys for filtering. @@ -328,10 +355,14 @@ //! //! The route is handled by [`get_analytics()`]. //! -//! Request query parameters: [`AnalyticsQuery`][primitives::analytics::AnalyticsQuery] +//! Request query parameters: [`AnalyticsQuery`] //! //! Response: [`AnalyticsResponse`] //! +//! ##### Examples +//! +//! See [GET `/v5/analytics`](#get-v5analytics) +//! //! [`Adapter`]: adapter::Adapter //! [`Address`]: primitives::Address //! [`AllowedKey`]: primitives::analytics::query::AllowedKey diff --git a/sentry/src/routes/analytics.rs b/sentry/src/routes/analytics.rs index ac0d98036..f04968ec5 100644 --- a/sentry/src/routes/analytics.rs +++ b/sentry/src/routes/analytics.rs @@ -19,9 +19,9 @@ use primitives::{ }; /// GET `/v5/analytics` routes -/// Request query parameters: [`primitives::analytics::AnalyticsQuery`]. +/// Request query parameters: [`AnalyticsQuery`]. /// -/// Response: [`primitives::sentry::AnalyticsResponse`] +/// Response: [`AnalyticsResponse`] /// /// Analytics routes: /// - GET `/v5/analytics` diff --git a/sentry/src/routes/campaign.rs b/sentry/src/routes/campaign.rs index fec542dbb..382a95749 100644 --- a/sentry/src/routes/campaign.rs +++ b/sentry/src/routes/campaign.rs @@ -144,9 +144,9 @@ pub async fn fetch_campaign_ids_for_channel( /// POST `/v5/campaign` /// -/// Request body (json): [`CreateCampaign`](`primitives::sentry::campaign_create::CreateCampaign`) +/// Request body (json): [`CreateCampaign`](primitives::sentry::campaign_create::CreateCampaign) /// -/// Response: [Campaign](`primitives::Campaign`) +/// Response: [`Campaign`](primitives::Campaign) pub async fn create_campaign( req: Request, app: &Application, @@ -593,8 +593,6 @@ pub mod update_campaign { pub mod insert_events { - use std::collections::HashMap; - use crate::{ access::{self, check_access}, analytics, @@ -608,7 +606,7 @@ pub mod insert_events { use hyper::{Body, Request, Response}; use primitives::{ balances::{Balances, CheckedState, OverflowError}, - sentry::{Event, SuccessResponse}, + sentry::{Event, InsertEventsRequest, SuccessResponse}, Address, Campaign, CampaignId, ChainOf, DomainError, UnifiedNum, ValidatorDesc, }; use slog::{error, Logger}; @@ -642,15 +640,9 @@ pub mod insert_events { /// POST `/v5/campaign/:id/events` /// - /// Request body (json): - /// - /// ```json - /// { - /// "events": [[`Event`](`primitives::Event`)] - /// } - /// ``` + /// Request body (json): [`InsertEventsRequest`] /// - /// Response: [`SuccessResponse`](primitives::sentry::SuccessResponse) + /// Response: [`SuccessResponse`] pub async fn handle_route( req: Request, app: &Application, @@ -669,17 +661,13 @@ pub mod insert_events { .expect("request should have a Campaign loaded"); let body_bytes = hyper::body::to_bytes(req_body).await?; - let mut request_body = serde_json::from_slice::>>(&body_bytes)?; - - let events = request_body - .remove("events") - .ok_or_else(|| ResponseError::BadRequest("invalid request".to_string()))?; + let request_body = serde_json::from_slice::(&body_bytes)?; - let processed = process_events(app, auth, session, campaign_context, events).await?; + process_events(app, auth, session, campaign_context, request_body.events).await?; Ok(Response::builder() .header("Content-type", "application/json") - .body(serde_json::to_string(&SuccessResponse { success: processed })?.into()) + .body(serde_json::to_string(&SuccessResponse { success: true })?.into()) .unwrap()) } @@ -689,7 +677,7 @@ pub mod insert_events { session: &Session, campaign_context: &ChainOf, events: Vec, - ) -> Result { + ) -> Result<(), ResponseError> { let campaign = &campaign_context.context; // handle events - check access @@ -738,7 +726,7 @@ pub mod insert_events { events_success, ); - Ok(true) + Ok(()) } /// Max retries is `5` after which an error logging message will be recorded. diff --git a/test_harness/src/lib.rs b/test_harness/src/lib.rs index ab53f2e43..aba8def05 100644 --- a/test_harness/src/lib.rs +++ b/test_harness/src/lib.rs @@ -233,7 +233,8 @@ mod tests { balances::CheckedState, sentry::{ campaign_create::CreateCampaign, AccountingResponse, AnalyticsResponse, DateHour, - Event, EventType, FetchedAnalytics, FetchedMetric, SuccessResponse, CLICK, IMPRESSION, + Event, EventType, FetchedAnalytics, FetchedMetric, InsertEventsRequest, + SuccessResponse, CLICK, IMPRESSION, }, spender::Spender, test_util::{ @@ -2692,9 +2693,9 @@ mod tests { .join(&format!("v5/campaign/{}/events", campaign_context.context)) .expect("valid endpoint"); - let request_body = vec![("events".to_string(), events)] - .into_iter() - .collect::>(); + let request_body = InsertEventsRequest { + events: events.to_vec(), + }; let auth_token = sentry .adapter