main.rs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #[macro_use]
  2. extern crate log;
  3. mod app_context;
  4. mod broadsign;
  5. use actix_web::{middleware, web, App, HttpResponse, HttpServer};
  6. use app_context::{AppContext, Database};
  7. use broadsign::real_time_pop_request::RealTimePopRequest;
  8. // We keep authentication at its simplest form, but you could
  9. // return the api user informations through a Result<UserIdentity> mechanism.
  10. pub fn authenticate(app_context: &web::Data<AppContext>, api_key: &String) -> bool {
  11. app_context.database.user_exists(api_key)
  12. }
  13. pub async fn status_get() -> HttpResponse {
  14. HttpResponse::Ok().finish()
  15. }
  16. pub async fn pop_post(
  17. app_context: web::Data<AppContext>,
  18. pop_data: web::Json<RealTimePopRequest>,
  19. ) -> HttpResponse {
  20. let pop_data: RealTimePopRequest = pop_data.into_inner();
  21. debug!("Received pop submission:\n{:?}", pop_data);
  22. if !authenticate(&app_context, &pop_data.api_key) {
  23. error!("Pop submission refused for api key '{}'", &pop_data.api_key);
  24. return HttpResponse::Unauthorized().body(format!(
  25. "Unauthorized access for api key '{}'",
  26. &pop_data.api_key
  27. ));
  28. }
  29. if !app_context.database.store_pop(&pop_data) {
  30. error!(
  31. "Failed to store {} pops for api key {}.",
  32. pop_data.pops.len(),
  33. pop_data.api_key
  34. );
  35. HttpResponse::InternalServerError().finish();
  36. }
  37. HttpResponse::Ok().finish()
  38. }
  39. #[actix_rt::main]
  40. async fn main() -> std::io::Result<()> {
  41. if !std::env::vars().any(|(k, _)| k == "RUST_LOG") {
  42. std::env::set_var("RUST_LOG", "info");
  43. }
  44. env_logger::init();
  45. info!("=== Starting Real-Time Pop Service ===");
  46. HttpServer::new(move || {
  47. App::new()
  48. .data(AppContext {
  49. database: Database::from_sqlite("pops.db"),
  50. })
  51. .wrap(middleware::Logger::default())
  52. .service(
  53. web::scope("")
  54. .route("/status", web::get().to(status_get))
  55. .route("/pop", web::post().to(pop_post)),
  56. )
  57. })
  58. .bind("0.0.0.0:8080")?
  59. .run()
  60. .await
  61. }
  62. #[cfg(test)]
  63. mod tests_endpoint_status {
  64. use super::*;
  65. use actix_web::http;
  66. #[actix_rt::test]
  67. async fn given_everything_is_running_status_returns_200_ok() {
  68. let resp = status_get().await;
  69. assert_eq!(resp.status(), http::StatusCode::OK);
  70. }
  71. }
  72. #[cfg(test)]
  73. mod tests_endpoint_pop {
  74. use super::*;
  75. use actix_web::{http, web};
  76. use broadsign::real_time_pop_request::{RealTimePopEntry, RealTimePopRequest};
  77. use serde_json::json;
  78. fn make_valid_pop_request() -> RealTimePopRequest {
  79. RealTimePopRequest {
  80. api_key: "some_secure_api_key".to_owned(),
  81. player_id: 123456,
  82. pops: vec![RealTimePopEntry {
  83. display_unit_id: 123,
  84. frame_id: 124,
  85. active_screens_count: 2,
  86. ad_copy_id: 56467,
  87. campaign_id: 61000,
  88. schedule_id: 61001,
  89. impressions: 675,
  90. interactions: 0,
  91. end_time: chrono::NaiveDate::from_ymd(2017, 11, 23).and_hms_milli(13, 27, 12, 500),
  92. duration_ms: 12996,
  93. service_name: "bmb".to_owned(),
  94. service_value: "701".to_owned(),
  95. extra_data: json!(""),
  96. }],
  97. }
  98. }
  99. fn make_app_context() -> web::Data<AppContext> {
  100. web::Data::<AppContext>::new(AppContext {
  101. database: Database::from_sqlite("test.db"),
  102. })
  103. }
  104. #[actix_rt::test]
  105. async fn given_a_valid_pop_and_healthy_server_respond_ok() {
  106. let app_context = make_app_context();
  107. app_context.database.create_user("some_secure_api_key");
  108. let resp = pop_post(app_context, web::Json(make_valid_pop_request())).await;
  109. assert_eq!(resp.status(), http::StatusCode::OK);
  110. }
  111. #[actix_rt::test]
  112. async fn given_an_invalid_api_key_server_responds_401_unauthorized() {
  113. let app_context = make_app_context();
  114. app_context.database.create_user("some_secure_api_key");
  115. let mut request = make_valid_pop_request();
  116. request.api_key = "some_invalid_api_key".to_owned();
  117. let resp = pop_post(app_context, web::Json(request)).await;
  118. assert_eq!(resp.status(), http::StatusCode::UNAUTHORIZED);
  119. }
  120. }