main.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #[macro_use]
  2. extern crate log;
  3. mod app_context;
  4. mod broadsign;
  5. use actix_web::{middleware, web, App, FromRequest, 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. .app_data(web::Json::<RealTimePopRequest>::configure(|cfg| {
  55. cfg.limit(32000000)
  56. }))
  57. .route("/status", web::get().to(status_get))
  58. .route("/pop", web::post().to(pop_post)),
  59. )
  60. })
  61. .bind("0.0.0.0:8080")?
  62. .run()
  63. .await
  64. }
  65. /*
  66. * Test section
  67. */
  68. #[cfg(test)]
  69. mod tests_endpoint_status {
  70. use super::*;
  71. use actix_web::http;
  72. #[actix_rt::test]
  73. async fn given_everything_is_running_status_returns_200_ok() {
  74. let resp = status_get().await;
  75. assert_eq!(resp.status(), http::StatusCode::OK);
  76. }
  77. }
  78. #[cfg(test)]
  79. mod tests_endpoint_pop {
  80. use super::*;
  81. use actix_web::{http, web};
  82. use broadsign::real_time_pop_request::{RealTimePopEntry, RealTimePopRequest};
  83. use serde_json::json;
  84. fn make_valid_pop_request() -> RealTimePopRequest {
  85. RealTimePopRequest {
  86. api_key: "some_secure_api_key".to_owned(),
  87. player_id: 123456,
  88. pops: vec![RealTimePopEntry {
  89. display_unit_id: 123,
  90. frame_id: 124,
  91. active_screens_count: 2,
  92. ad_copy_id: 56467,
  93. campaign_id: 61000,
  94. schedule_id: 61001,
  95. impressions: 675,
  96. interactions: 0,
  97. end_time: chrono::NaiveDate::from_ymd(2017, 11, 23).and_hms_milli(13, 27, 12, 500),
  98. duration_ms: 12996,
  99. service_name: "bmb".to_owned(),
  100. service_value: "701".to_owned(),
  101. extra_data: Option::Some(json!("")),
  102. }],
  103. }
  104. }
  105. fn make_app_context() -> web::Data<AppContext> {
  106. web::Data::<AppContext>::new(AppContext {
  107. database: Database::from_sqlite("test.db"),
  108. })
  109. }
  110. #[actix_rt::test]
  111. async fn given_a_valid_pop_and_healthy_server_respond_ok() {
  112. let app_context = make_app_context();
  113. app_context.database.create_user("some_secure_api_key");
  114. let resp = pop_post(app_context, web::Json(make_valid_pop_request())).await;
  115. assert_eq!(resp.status(), http::StatusCode::OK);
  116. }
  117. #[actix_rt::test]
  118. async fn given_an_invalid_api_key_server_responds_401_unauthorized() {
  119. let app_context = make_app_context();
  120. app_context.database.create_user("some_secure_api_key");
  121. let mut request = make_valid_pop_request();
  122. request.api_key = "some_invalid_api_key".to_owned();
  123. let resp = pop_post(app_context, web::Json(request)).await;
  124. assert_eq!(resp.status(), http::StatusCode::UNAUTHORIZED);
  125. }
  126. }