Skip to content

Commit cb63ca7

Browse files
committed
sentry - routes - units-for-slot - with axum
1 parent 18510f6 commit cb63ca7

File tree

2 files changed

+64
-89
lines changed

2 files changed

+64
-89
lines changed

sentry/src/platform.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
use std::{sync::Arc, time::Duration};
2+
3+
use reqwest::{Client, Error, StatusCode};
4+
15
// previously fetched from the market (in the supermarket) it should now be fetched from the Platform!
26
use primitives::{
37
platform::{AdUnitResponse, AdUnitsResponse},
48
util::ApiUrl,
59
AdUnit, IPFS,
610
};
7-
use reqwest::{Client, Error, StatusCode};
8-
use std::{sync::Arc, time::Duration};
911

1012
pub type Result<T> = std::result::Result<T, Error>;
1113

sentry/src/routes/units_for_slot.rs

Lines changed: 60 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
1-
use std::collections::HashSet;
1+
use std::{collections::HashSet, sync::Arc};
22

3-
use adapter::client::Locked;
3+
use axum::{
4+
extract::TypedHeader,
5+
headers::UserAgent,
6+
http::header::{HeaderMap, HeaderName},
7+
Extension, Json,
8+
};
49
use chrono::Utc;
510
use futures::future::try_join_all;
6-
use hyper::{header::USER_AGENT, Body, Request, Response};
7-
use hyper::{
8-
header::{HeaderName, CONTENT_TYPE},
9-
StatusCode,
10-
};
1111
use once_cell::sync::Lazy;
12+
use reqwest::Url;
13+
use serde::{Deserialize, Serialize};
14+
use slog::{debug, error, warn, Logger};
15+
use woothee::{parser::Parser, woothee::VALUE_UNKNOWN};
16+
17+
use adapter::client::Locked;
1218
use primitives::{
1319
sentry::IMPRESSION,
1420
supermarket::units_for_slot::response,
1521
supermarket::units_for_slot::response::Response as UnitsForSlotResponse,
1622
targeting::{eval_with_callback, get_pricing_bounds, input, input::Input, Output},
1723
AdSlot, AdUnit, Address, Campaign, Config, UnifiedNum, ValidatorId,
1824
};
19-
use reqwest::Url;
20-
use serde::{Deserialize, Serialize};
21-
use slog::{debug, error, warn, Logger};
22-
use woothee::{parser::Parser, woothee::VALUE_UNKNOWN};
2325

2426
use crate::{
2527
db::{
2628
accounting::{get_accounting, Side},
2729
units_for_slot_get_campaigns, CampaignRemaining, DbPool,
2830
},
29-
Application, ResponseError,
31+
response::ResponseError,
32+
Application,
3033
};
3134

3235
pub(crate) static CLOUDFLARE_IPCOUNTRY_HEADER: Lazy<HeaderName> =
@@ -43,36 +46,15 @@ pub struct RequestBody {
4346
pub deposit_assets: Option<HashSet<Address>>,
4447
}
4548

46-
pub(crate) fn not_found() -> Response<Body> {
47-
Response::builder()
48-
.status(StatusCode::NOT_FOUND)
49-
.body(Body::empty())
50-
.expect("Not Found response should be valid")
51-
}
52-
53-
pub(crate) fn service_unavailable() -> Response<Body> {
54-
Response::builder()
55-
.status(StatusCode::SERVICE_UNAVAILABLE)
56-
.body(Body::empty())
57-
.expect("Bad Request response should be valid")
58-
}
59-
6049
pub async fn post_units_for_slot<C>(
61-
req: Request<Body>,
62-
app: &Application<C>,
63-
) -> Result<Response<Body>, ResponseError>
50+
Extension(app): Extension<Arc<Application<C>>>,
51+
Json(request_body): Json<RequestBody>,
52+
user_agent: Option<TypedHeader<UserAgent>>,
53+
headers: HeaderMap,
54+
) -> Result<Json<UnitsForSlotResponse>, ResponseError>
6455
where
6556
C: Locked + 'static,
6657
{
67-
let logger = &app.logger;
68-
let config = &app.config;
69-
70-
let (request_parts, body) = req.into_parts();
71-
72-
let body_bytes = hyper::body::to_bytes(body).await?;
73-
74-
let request_body = serde_json::from_slice::<RequestBody>(&body_bytes)?;
75-
7658
let ad_slot = request_body.ad_slot.clone();
7759

7860
// TODO: remove once we know how/where we will be fetching the rest of the information!
@@ -87,40 +69,45 @@ where
8769
{
8870
Ok(units) => units,
8971
Err(error) => {
90-
error!(&logger, "Error fetching AdUnits for AdSlot"; "AdSlot" => ?ad_slot, "error" => ?error);
72+
error!(&app.logger, "Error fetching AdUnits for AdSlot"; "AdSlot" => ?ad_slot, "error" => ?error);
9173

92-
return Ok(service_unavailable());
74+
return Err(ResponseError::BadRequest(
75+
"Error fetching AdUnits for AdSlot. Please try again later.".into(),
76+
));
9377
}
9478
};
9579

9680
let fallback_unit: Option<AdUnit> = match &ad_slot.fallback_unit {
9781
Some(unit_ipfs) => {
9882
let ad_unit_response = match app.platform_api.fetch_unit(*unit_ipfs).await {
9983
Ok(Some(response)) => {
100-
debug!(&logger, "Fetched AdUnit {:?}", unit_ipfs; "AdUnit" => ?unit_ipfs);
84+
debug!(&app.logger, "Fetched AdUnit {:?}", unit_ipfs; "AdUnit" => ?unit_ipfs);
10185
response
10286
}
10387
Ok(None) => {
10488
warn!(
105-
&logger,
89+
&app.logger,
10690
"AdSlot fallback AdUnit {} not found in Platform",
10791
unit_ipfs;
10892
"AdUnit" => ?unit_ipfs,
10993
"AdSlot" => ?ad_slot,
11094
);
11195

112-
return Ok(not_found());
96+
return Err(ResponseError::NotFound);
11397
}
11498
Err(error) => {
115-
error!(&logger,
99+
error!(&app.logger,
116100
"Error when fetching AdSlot fallback AdUnit ({}) from Platform",
117101
unit_ipfs;
118102
"AdSlot" => ?ad_slot,
119103
"Fallback AdUnit" => ?unit_ipfs,
120104
"error" => ?error
121105
);
122106

123-
return Ok(service_unavailable());
107+
return Err(ResponseError::BadRequest(
108+
"Error when fetching AdSlot fallback AdUnit. Please try again later."
109+
.into(),
110+
));
124111
}
125112
};
126113

@@ -129,44 +116,35 @@ where
129116
None => None,
130117
};
131118

132-
debug!(&logger, "Fetched {} AdUnits for AdSlot", units.len(); "AdSlot" => ?ad_slot);
119+
debug!(&app.logger, "Fetched {} AdUnits for AdSlot", units.len(); "AdSlot" => ?ad_slot);
133120

134121
// For each adUnits apply input
135122
let ua_parser = Parser::new();
136-
let user_agent = request_parts
137-
.headers
138-
.get(USER_AGENT)
139-
.and_then(|h| h.to_str().map(ToString::to_string).ok())
123+
let user_agent = user_agent
124+
.map(|h| h.as_str().to_string())
140125
.unwrap_or_default();
141126
let parsed = ua_parser.parse(&user_agent);
142127
// WARNING! This will return only the OS type, e.g. `Linux` and not the actual distribution name e.g. `Ubuntu`
143128
// By contrast `ua-parser-js` will return `Ubuntu` (distribution) and not the OS type `Linux`.
144129
// `UAParser(...).os.name` (`ua-parser-js: 0.7.22`)
145-
let user_agent_os = parsed
146-
.as_ref()
147-
.map(|p| {
148-
if p.os != VALUE_UNKNOWN {
149-
Some(p.os.to_string())
150-
} else {
151-
None
152-
}
153-
})
154-
.flatten();
130+
let user_agent_os = parsed.as_ref().and_then(|p| {
131+
if p.os != VALUE_UNKNOWN {
132+
Some(p.os.to_string())
133+
} else {
134+
None
135+
}
136+
});
155137

156138
// Corresponds to `UAParser(...).browser.name` (`ua-parser-js: 0.7.22`)
157-
let user_agent_browser_family = parsed
158-
.as_ref()
159-
.map(|p| {
160-
if p.name != VALUE_UNKNOWN {
161-
Some(p.name.to_string())
162-
} else {
163-
None
164-
}
165-
})
166-
.flatten();
139+
let user_agent_browser_family = parsed.as_ref().and_then(|p| {
140+
if p.name != VALUE_UNKNOWN {
141+
Some(p.name.to_string())
142+
} else {
143+
None
144+
}
145+
});
167146

168-
let country = request_parts
169-
.headers
147+
let country = headers
170148
.get(CLOUDFLARE_IPCOUNTRY_HEADER.clone())
171149
.and_then(|h| h.to_str().map(ToString::to_string).ok());
172150

@@ -180,15 +158,15 @@ where
180158
let campaigns_limited_by_earner = get_campaigns(
181159
app.pool.clone(),
182160
app.campaign_remaining.clone(),
183-
config,
161+
&app.config,
184162
&request_body.deposit_assets,
185163
publisher_id,
186164
)
187165
.await
188166
// TODO: Fix mapping this error and Log the error!
189167
.map_err(|err| ResponseError::BadRequest(err.to_string()))?;
190168

191-
debug!(&logger, "Fetched Cache campaigns limited by earner (publisher)"; "campaigns" => campaigns_limited_by_earner.len(), "publisher_id" => %publisher_id);
169+
debug!(&app.logger, "Fetched Cache campaigns limited by earner (publisher)"; "campaigns" => campaigns_limited_by_earner.len(), "publisher_id" => %publisher_id);
192170

193171
// We return those in the result (which means AdView would have those) but we don't actually use them
194172
// we do that in order to have the same variables as the validator, so that the `price` is the same
@@ -205,7 +183,7 @@ where
205183
ad_slot_type: ad_slot.ad_type.clone(),
206184
publisher_id: publisher_id.to_address(),
207185
country,
208-
event_type: IMPRESSION.into(),
186+
event_type: IMPRESSION,
209187
seconds_since_epoch: Utc::now(),
210188
user_agent_os,
211189
user_agent_browser_family: user_agent_browser_family.clone(),
@@ -217,8 +195,8 @@ where
217195
};
218196

219197
let campaigns = apply_targeting(
220-
config,
221-
logger,
198+
&app.config,
199+
&app.logger,
222200
campaigns_limited_by_earner,
223201
targeting_input_base.clone(),
224202
ad_slot,
@@ -234,11 +212,7 @@ where
234212
fallback_unit: fallback_unit.map(|ad_unit| response::AdUnit::from(&ad_unit)),
235213
};
236214

237-
Ok(Response::builder()
238-
.status(StatusCode::OK)
239-
.header(CONTENT_TYPE, "application/json")
240-
.body(Body::from(serde_json::to_string(&response)?))
241-
.expect("Should create response"))
215+
Ok(Json(response))
242216
}
243217

244218
// TODO: Use error instead of std::error::Error
@@ -267,7 +241,7 @@ async fn get_campaigns(
267241

268242
// 2. Check those Campaigns if `Campaign remaining > 0` (in redis)
269243
let campaigns_remaining = campaign_remaining
270-
.get_multiple_with_ids(&active_campaign_ids)
244+
.get_multiple_with_ids(active_campaign_ids)
271245
.await?;
272246

273247
let campaigns_with_remaining = campaigns_remaining
@@ -301,16 +275,15 @@ async fn get_campaigns(
301275
}))
302276
.await?
303277
.into_iter()
304-
.filter_map(|accounting| accounting)
278+
.flatten()
305279
.collect::<Vec<_>>();
306280

307281
// 3. Filter `Campaign`s, that include the `publisher_id` in the Channel balances.
308282
let (mut campaigns_by_earner, rest_of_campaigns): (Vec<Campaign>, Vec<Campaign>) =
309283
campaigns_with_remaining.into_iter().partition(|campaign| {
310284
publisher_accountings
311285
.iter()
312-
.find(|accounting| accounting.channel_id == campaign.channel.id())
313-
.is_some()
286+
.any(|accounting| accounting.channel_id == campaign.channel.id())
314287
});
315288

316289
let campaigns = if campaigns_by_earner.len()
@@ -363,7 +336,7 @@ async fn apply_targeting(
363336
show: true,
364337
boost: 1.0,
365338
// only "IMPRESSION" event can be used for this `Output`
366-
price: vec![(IMPRESSION.into(), pricing_bounds.min)]
339+
price: [(IMPRESSION, pricing_bounds.min)]
367340
.into_iter()
368341
.collect(),
369342
};

0 commit comments

Comments
 (0)