Skip to content

Api routes documentation #524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6976b51
initial documentation
simzzz Jul 6, 2022
f2ed51e
changed example comments
simzzz Jul 6, 2022
ea5b20f
changed example comments
simzzz Jul 6, 2022
bad29e1
more code examples + fixes
simzzz Aug 2, 2022
7a57ce4
removed unnecessary file
simzzz Aug 3, 2022
48bb03c
formatting
simzzz Aug 3, 2022
d72ec92
fixed CreateCampaign code examples
simzzz Aug 3, 2022
5299193
Merge branch 'aip-61-adex-v5' into api-routes-documentation
simzzz Aug 3, 2022
5b16739
Fixed code examples for CreateCampaign again
simzzz Aug 3, 2022
94a9faa
fixed AnalyticsQuery code example
simzzz Aug 4, 2022
849f2f3
fixed wrong link
simzzz Aug 4, 2022
4837a9d
primitives - sentry - AnalyticsResponse
elpiel Aug 4, 2022
e7cec78
Added AnalyticsResponse examples
simzzz Aug 4, 2022
48abedc
Update sentry/src/routes/campaign.rs
simzzz Aug 4, 2022
91bf650
removed docs folder
simzzz Aug 4, 2022
653fd64
added examples to cargo toml
simzzz Aug 4, 2022
4cc9439
remove unused doc files
elpiel Aug 5, 2022
79e4990
example - analytics_response - remove pagination
elpiel Aug 5, 2022
9ff588a
sentry - Improve docs & create InsertEventsRequest
elpiel Aug 5, 2022
a1ce814
test_harness - use InsertEventsRequest
elpiel Aug 5, 2022
f0f1b03
Pull request fixes
simzzz Aug 5, 2022
72bb5af
Merge branch 'api-routes-documentation' of https://github.com/AmbireT…
simzzz Aug 5, 2022
ae42e68
rustfmt
elpiel Aug 5, 2022
68459e4
Merge branch 'api-routes-documentation' into pr-review
elpiel Aug 5, 2022
68f667a
remove .DS_Store files
elpiel Aug 5, 2022
f80f20b
sentry - routes - fix intra link
elpiel Aug 5, 2022
ab12fa7
Merge pull request #530 from AmbireTech/pr-review
elpiel Aug 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ required-features = ["test-util"]
name = "campaign_list_response"
required-features = ["test-util"]

[[example]]
name = "create_campaign"
required-features = ["test-util"]

[dependencies]
# (De)Serialization
serde = { version = "1.0", features = ["derive"] }
Expand Down
104 changes: 104 additions & 0 deletions primitives/examples/analytics_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use primitives::{
analytics::{
query::{AllowedKey, Time},
AnalyticsQuery, Metric, OperatingSystem, Timeframe,
},
sentry::{DateHour, EventType},
Address, CampaignId, ChainId, IPFS,
};
use std::str::FromStr;

fn main() {
// Empty query - default values only
{
let empty_query = "";
let query: AnalyticsQuery = serde_qs::from_str(empty_query).unwrap();

assert_eq!(100, query.limit);
assert_eq!(EventType::Impression, query.event_type);
assert!(matches!(query.metric, Metric::Count));
assert!(matches!(query.time.timeframe, Timeframe::Day));
}
// Query with different metric/chain/eventType
{
let query_str = "limit=200&eventType=CLICK&metric=paid&timeframe=month";
let query: AnalyticsQuery = serde_qs::from_str(query_str).unwrap();

assert_eq!(200, query.limit);
assert_eq!(EventType::Click, query.event_type);
assert!(matches!(query.metric, Metric::Paid));
assert!(matches!(query.time.timeframe, Timeframe::Month));
}

// Query with allowed keys for guest - country, slotType
{
let query_str = "country=Bulgaria&adSlotType=legacy_300x100";
let query: AnalyticsQuery = serde_qs::from_str(query_str).unwrap();

assert_eq!(Some("Bulgaria".to_string()), query.country);
assert_eq!(Some("legacy_300x100".to_string()), query.ad_slot_type);
}

// 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: AnalyticsQuery = serde_qs::from_str(query_str).unwrap();

assert_eq!(query.limit, 200);
assert_eq!(query.event_type, EventType::Click);
assert!(matches!(query.metric, Metric::Paid));
assert_eq!(query.segment_by, Some(AllowedKey::Country));
assert_eq!(
query.time,
Time {
timeframe: Timeframe::Week,
start: DateHour::from_ymdh(2022, 8, 4, 9),
end: None,
}
);
assert_eq!(
query.campaign_id,
Some(
CampaignId::from_str("0x936da01f9abd4d9d80c702af85c822a8")
.expect("should be valid")
)
);
assert_eq!(
query.ad_unit,
Some(
IPFS::from_str("QmcUVX7fvoLMM93uN2bD3wGTH8MXSxeL8hojYfL2Lhp7mR")
.expect("should be valid")
)
);
assert_eq!(
query.ad_slot,
Some(
IPFS::from_str("Qmasg8FrbuSQpjFu3kRnZF9beg8rEBFrqgi1uXDRwCbX5f")
.expect("should be valid")
)
);
assert_eq!(query.ad_slot_type, Some("legacy_300x100".to_string()));
assert_eq!(
query.advertiser,
Some(
Address::from_str("0xDd589B43793934EF6Ad266067A0d1D4896b0dff0")
.expect("should be valid")
)
);
assert_eq!(
query.publisher,
Some(
Address::from_str("0xE882ebF439207a70dDcCb39E13CA8506c9F45fD9")
.expect("should be valid")
)
);
assert_eq!(query.hostname, Some("localhost".to_string()));
assert_eq!(query.country, Some("Bulgaria".to_string()));
assert_eq!(
query.os_name,
Some(OperatingSystem::Whitelisted("Windows".to_string()))
);
assert_eq!(query.chains, vec!(ChainId::new(1), ChainId::new(1337)));
}
}
26 changes: 26 additions & 0 deletions primitives/examples/analytics_response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use primitives::sentry::AnalyticsResponse;
use serde_json::{from_value, json};

fn main() {
let json = json!({
"analytics": [{
"time": 1659592800,
"value": "3",
"segment": null
},
{
"time": 1659592800,
"value": "10000000000",
"segment": null
},
{
"time": 1659592800,
"value": "100000000",
"segment": "country"
}],
"totalPages": 1,
"page": 0
});

assert!(from_value::<AnalyticsResponse>(json).is_ok());
}
98 changes: 98 additions & 0 deletions primitives/examples/create_campaign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use primitives::{sentry::campaign_create::CreateCampaign, test_util::DUMMY_CAMPAIGN, CampaignId};
use serde_json::json;
use std::str::FromStr;

fn main() {
// CreateCampaign in an HTTP request
{
let create_campaign = CreateCampaign::from_campaign_erased(DUMMY_CAMPAIGN.clone(), None);

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",
"guardian":"0xe061E1EB461EaBE512759aa18A201B20Fe90631D",
"token":"0x2BCaf6968aEC8A3b5126FBfAb5Fd419da6E8AD8E",
"nonce":"987654321"
},
"creator":"0xaCBaDA2d5830d1875ae3D2de207A1363B316Df2F",
"budget":"100000000000",
"validators":[
{
"id":"0x80690751969B234697e9059e04ed72195c3507fa",
"fee":"3000000",
"url":"http://localhost:8005"
},
{
"id":"0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
"fee":"2000000",
"url":"http://localhost:8006"
}
],
"title":"Dummy Campaign",
"pricingBounds":{"CLICK":{"min":"0","max":"0"},"IMPRESSION":{"min":"1","max":"10"}},
"eventSubmission":{"allow":[]},
"targetingRules":[],
"created":1612162800000_u64,
"activeTo":4073414400000_u64
});

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");

assert_eq!(create_campaign, deserialized);
}

// CreateCampaign with a provided ID
{
let mut create_campaign =
CreateCampaign::from_campaign_erased(DUMMY_CAMPAIGN.clone(), None);
create_campaign.id = Some(
CampaignId::from_str("0x936da01f9abd4d9d80c702af85c822a8").expect("Should be valid id"),
);

let create_campaign_json = json!({
"id":"0x936da01f9abd4d9d80c702af85c822a8",
"channel":{
"leader":"0x80690751969B234697e9059e04ed72195c3507fa",
"follower":"0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
"guardian":"0xe061E1EB461EaBE512759aa18A201B20Fe90631D",
"token":"0x2BCaf6968aEC8A3b5126FBfAb5Fd419da6E8AD8E",
"nonce":"987654321"
},
"creator":"0xaCBaDA2d5830d1875ae3D2de207A1363B316Df2F",
"budget":"100000000000",
"validators":[
{
"id":"0x80690751969B234697e9059e04ed72195c3507fa",
"fee":"3000000",
"url":"http://localhost:8005"
},
{
"id":"0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
"fee":"2000000",
"url":"http://localhost:8006"
}
],
"title":"Dummy Campaign",
"pricingBounds":{"CLICK":{"min":"0","max":"0"},"IMPRESSION":{"min":"1","max":"10"}},
"eventSubmission":{"allow":[]},
"targetingRules":[],
"created":1612162800000_u64,
"activeTo":4073414400000_u64
});

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");

assert_eq!(create_campaign, deserialized);
}
}
34 changes: 34 additions & 0 deletions primitives/examples/modify_campaign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use primitives::{sentry::campaign_modify::ModifyCampaign, unified_num::FromWhole, UnifiedNum};
use serde_json::json;
use std::str::FromStr;

fn main() {
{
let modify_campaign = ModifyCampaign {
ad_units: None,
budget: Some(UnifiedNum::from_whole(100)),
validators: None,
title: None,
pricing_bounds: None,
event_submission: None,
targeting_rules: None,
};

let modify_campaign_json = json!({
"ad_units": null,
"budget": "10000000000",
"validators": null,
"title": null,
"pricing_bounds": null,
"event_submission": null,
"targeting_rules": null,
});

let modify_campaign_json =
serde_json::to_string(&modify_campaign_json).expect("should serialize");
let deserialized: ModifyCampaign =
serde_json::from_str(&modify_campaign_json).expect("should deserialize");

assert_eq!(modify_campaign, deserialized);
}
}
6 changes: 6 additions & 0 deletions primitives/src/analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ mod postgres {

pub mod query;

// Query used for filtering analytics
//
/// # Examples:
/// ```
#[doc = include_str!("../examples/analytics_query.rs")]
/// ```
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AnalyticsQuery {
Expand Down
2 changes: 1 addition & 1 deletion primitives/src/campaign_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl Validator for Campaign {
.find_chain_of(self.channel.token)
.ok_or(Validation::UnlistedAsset)?;

// Check if the campaign budget is above the minimum deposit configured
// Check if the campaign budget is above the minimum campaign budget configured
if self
.budget
.to_precision(chain_context.token.precision.get())
Expand Down
25 changes: 24 additions & 1 deletion primitives/src/sentry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,18 @@ pub struct FetchedAnalytics {
pub segment: Option<String>,
}

// Response returned when getting Analytics - an array of FetchedAnalytics
//
/// # Examples:
/// ```
#[doc = include_str!("../examples/analytics_response.rs")]
/// ```
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AnalyticsResponse {
pub analytics: Vec<FetchedAnalytics>,
}

/// The value of the requested analytics [`crate::analytics::Metric`].
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
#[serde(untagged)]
Expand Down Expand Up @@ -802,6 +814,12 @@ pub mod campaign_create {

/// All fields are present except the `CampaignId` which is randomly created
/// This struct defines the Body of the request (in JSON)
///
/// # Examples
///
/// ```
#[doc = include_str!("../examples/create_campaign.rs")]
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CreateCampaign {
Expand Down Expand Up @@ -889,7 +907,12 @@ pub mod campaign_modify {
AdUnit, Campaign, EventSubmission, UnifiedNum,
};

// All editable fields stored in one place, used for checking when a budget is changed
/// All editable fields stored in one place, used for checking when a budget is changed
///
/// # Examples:
/// ```
#[doc = include_str!("../examples/modify_campaign.rs")]
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ModifyCampaign {
pub budget: Option<UnifiedNum>,
Expand Down
Empty file.
9 changes: 9 additions & 0 deletions sentry/docs/analytics/glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[`AllowedKey`](`primitives::query::AllowedKey`)

[`Analytics`](`primitives::Analytics`)

[`Auth`](crate::Auth)

[`ResponseError`](`crate::response::ResponseError`)

[`AnalyticsQuery`](`primitives::AnalyticsQuery`)
14 changes: 14 additions & 0 deletions sentry/docs/analytics/tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
In order to call the route successfully you need to:
- GET `/analytics`:
- Use a valid `AnalyticsQuery`, the only keys that you can use are `AllowedKey::Country` and `AllowedKey::AdSlotType`
- If you are using `?segmentBy=...` query parameter it must also be an allowed key, otherwise an error will be returned
- GET `/analytics/for-publisher` (auth required):
- Returns all analytics where the currently authenticated address `Auth.uid` is a **publisher**.
- Use a valid `AnalyticsQuery` with no restrictons on the allowed keys that you can use.
- GET `/analytics/for-advertiser`:
- Returns all analytics where the currently authenticated address `Auth.uid` is a **advertiser**.
- Use a valid `AnalyticsQuery` with no restrictons on the allowed keys that you can use.
- GET `/analytics/for/admin`:
- Admin access to the analytics with no restrictions on the keys for filtering.
- Use a valid `AnalyticsQuery` with no restrictons on the allowed keys that you can use.
- If the request is successful your output will be an array with all the fetched entries which include the time, the value of the metric from the query and the `segment_by` field
Loading