Skip to content
This repository has been archived by the owner on Mar 18, 2023. It is now read-only.

Commit

Permalink
✨ Add /api/:realm/accounts/:since/active-since
Browse files Browse the repository at this point in the history
  • Loading branch information
eigenein committed Nov 12, 2022
1 parent a8344e4 commit dfed1dd
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 8 deletions.
16 changes: 15 additions & 1 deletion src/database/mongodb/models/account/id_projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ impl TypedDocument for AccountIdProjection {
}

impl AccountIdProjection {
pub async fn retrieve_page(
#[instrument(skip_all, level = "info", fields(realm = realm, limit = limit))]
pub async fn retrieve_recently_active(
from: &Database,
realm: &str,
limit: i64,
Expand All @@ -32,4 +33,17 @@ impl AccountIdProjection {
.build();
Ok(Self::collection(from).find(filter, options).await?)
}

#[instrument(skip_all, level = "info", fields(realm = ?realm, since = ?since))]
pub async fn retrieve_active_since(
from: &Database,
realm: wargaming::Realm,
since: DateTime,
) -> Result<impl Stream<Item = Result<Self, mongodb::error::Error>>> {
let filter = doc! { "rlm": realm.to_str(), "lbts": { "$gte": since } };
let options = FindOptions::builder()
.projection(doc! { "_id": 0, "aid": 1 })
.build();
Ok(Self::collection(from).find(filter, options).await?)
}
}
5 changes: 5 additions & 0 deletions src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::opts::WebOpts;
use crate::prelude::*;
use crate::wargaming::cache::account::{AccountInfoCache, AccountTanksCache};
use crate::wargaming::WargamingApi;
use crate::web::middleware::timeit::TimeItMiddleware;
use crate::web::middleware::{ErrorMiddleware, SecurityHeadersMiddleware, SentryMiddleware};
use crate::web::tracking_code::TrackingCode;

Expand Down Expand Up @@ -120,6 +121,10 @@ async fn create_standalone_app() -> Result<impl Endpoint> {
.at("/random", get(views::random::get_random))
.at("/sitemaps/:realm/sitemap.txt", get(views::sitemaps::get_sitemap))
.at("/api/health", get(views::api::get_health))
.at(
"/api/:realm/accounts/:since/active-since",
get(views::api::get_active_since).with(TimeItMiddleware),
)
.data(i18n::build_resources()?)
.with(Tracing)
.with(CatchPanic::new())
Expand Down
1 change: 1 addition & 0 deletions src/web/middleware.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod error;
pub mod security_headers;
pub mod sentry;
pub mod timeit;

pub use self::error::*;
pub use self::security_headers::*;
Expand Down
31 changes: 31 additions & 0 deletions src/web/middleware/timeit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use poem::{Endpoint, Middleware, Request, Response, Result};

use crate::prelude::*;

pub struct TimeItMiddleware;

impl<E: Endpoint<Output = Response>> Middleware<E> for TimeItMiddleware {
type Output = TimeItMiddlewareImpl<E>;

fn transform(&self, ep: E) -> Self::Output {
TimeItMiddlewareImpl { ep }
}
}

pub struct TimeItMiddlewareImpl<E> {
ep: E,
}

#[poem::async_trait]
impl<E: Endpoint<Output = Response>> Endpoint for TimeItMiddlewareImpl<E> {
type Output = Response;

async fn call(&self, request: Request) -> Result<Self::Output> {
let method = request.method().clone();
let uri = request.uri().clone();
let start_instant = Instant::now();
let response = self.ep.call(request).await;
info!(elapsed = ?start_instant.elapsed(), ?method, ?uri);
response
}
}
19 changes: 18 additions & 1 deletion src/web/views/api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use futures::StreamExt;
use poem::http::StatusCode;
use poem::{handler, IntoResponse, Response};
use poem::web::{Data, Path};
use poem::{handler, Body, IntoResponse, Response};

use crate::database::AccountIdProjection;
use crate::prelude::*;

const CACHE_CONTROL: &str = "no-cache";
Expand All @@ -10,3 +13,17 @@ const CACHE_CONTROL: &str = "no-cache";
pub async fn get_health() -> Result<impl IntoResponse> {
Ok(Response::from(StatusCode::NO_CONTENT).with_header("Cache-Control", CACHE_CONTROL))
}

#[handler]
#[instrument(skip_all, level = "info")]
pub async fn get_active_since(
db: Data<&mongodb::Database>,
Path((realm, since)): Path<(wargaming::Realm, DateTime)>,
) -> Result<impl IntoResponse> {
let stream = AccountIdProjection::retrieve_active_since(&db, realm, since)
.await?
.map(move |account| account.map(|account| format!("{}\n", account.id).into_bytes()));
Ok(Response::from(Body::from_bytes_stream(stream))
.with_header("Cache-Control", CACHE_CONTROL)
.with_content_type("text/plain"))
}
8 changes: 2 additions & 6 deletions src/web/views/sitemaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,10 @@ pub async fn get_sitemap(
Path(realm): Path<String>,
) -> Result<impl IntoResponse> {
let start_instant = Instant::now();
let stream = database::AccountIdProjection::retrieve_page(&db, &realm, 1000).await?;
let stream = database::AccountIdProjection::retrieve_recently_active(&db, &realm, 1000).await?;
info!(elapsed_secs = ?start_instant.elapsed(), "stream ready");
let stream = stream.map(move |account| {
account.map(|account| {
format!("https://yastati.st/{}/{}\n", realm, account.id)
.as_bytes()
.to_vec()
})
account.map(|account| format!("https://yastati.st/{}/{}\n", realm, account.id).into_bytes())
});

Ok(Response::from(Body::from_bytes_stream(stream))
Expand Down

0 comments on commit dfed1dd

Please sign in to comment.