mirror of
https://github.com/modrinth/code.git
synced 2026-08-26 09:34:50 +00:00
Implement analytics marker events (#6090)
* Analytics events * prepare * change route prefix * update route return * Add mod launcher analytics * more UA strings * fix ci * caching on analytics events * Return parent modpack versions for playtime queries * sqlx prepare * fmt * dummy fixtures
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use sqlx::types::Json;
|
||||
|
||||
use crate::{
|
||||
database::{
|
||||
models::{DBAnalyticsEventId, DatabaseError},
|
||||
redis::RedisPool,
|
||||
},
|
||||
models::v3::analytics_event::AnalyticsEventMeta,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const ANALYTICS_EVENTS_NAMESPACE: &str = "analytics_events";
|
||||
const ANALYTICS_EVENTS_ALL_KEY: &str = "all";
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DBAnalyticsEvent {
|
||||
pub id: DBAnalyticsEventId,
|
||||
pub meta: AnalyticsEventMeta,
|
||||
pub starts: DateTime<Utc>,
|
||||
pub ends: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl DBAnalyticsEvent {
|
||||
pub async fn insert(
|
||||
&self,
|
||||
exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO analytics_events (id, meta, starts, ends)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
",
|
||||
self.id as DBAnalyticsEventId,
|
||||
sqlx::types::Json(&self.meta) as Json<&AnalyticsEventMeta>,
|
||||
self.starts,
|
||||
self.ends,
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&self,
|
||||
exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>,
|
||||
) -> Result<bool, DatabaseError> {
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
UPDATE analytics_events
|
||||
SET meta = $2, starts = $3, ends = $4
|
||||
WHERE id = $1
|
||||
",
|
||||
self.id as DBAnalyticsEventId,
|
||||
sqlx::types::Json(&self.meta) as Json<&AnalyticsEventMeta>,
|
||||
self.starts,
|
||||
self.ends,
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
Ok(result.rows_affected() > 0)
|
||||
}
|
||||
|
||||
pub async fn remove(
|
||||
id: DBAnalyticsEventId,
|
||||
exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>,
|
||||
) -> Result<bool, DatabaseError> {
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
DELETE FROM analytics_events
|
||||
WHERE id = $1
|
||||
",
|
||||
id as DBAnalyticsEventId,
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
Ok(result.rows_affected() > 0)
|
||||
}
|
||||
|
||||
pub async fn get_all(
|
||||
exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>,
|
||||
redis: &RedisPool,
|
||||
) -> Result<Vec<DBAnalyticsEvent>, DatabaseError> {
|
||||
let mut redis = redis.connect().await?;
|
||||
|
||||
if let Some(events) = redis
|
||||
.get_deserialized_from_json(
|
||||
ANALYTICS_EVENTS_NAMESPACE,
|
||||
ANALYTICS_EVENTS_ALL_KEY,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
return Ok(events);
|
||||
}
|
||||
|
||||
let events = sqlx::query!(
|
||||
r#"
|
||||
SELECT id, meta AS "meta: Json<AnalyticsEventMeta>", starts, ends
|
||||
FROM analytics_events
|
||||
ORDER BY starts DESC
|
||||
"#
|
||||
)
|
||||
.fetch(exec)
|
||||
.map(|record| {
|
||||
let record = record?;
|
||||
|
||||
Ok::<_, DatabaseError>(DBAnalyticsEvent {
|
||||
id: DBAnalyticsEventId(record.id),
|
||||
meta: record.meta.0,
|
||||
starts: record.starts,
|
||||
ends: record.ends,
|
||||
})
|
||||
})
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
redis
|
||||
.set_serialized_to_json(
|
||||
ANALYTICS_EVENTS_NAMESPACE,
|
||||
ANALYTICS_EVENTS_ALL_KEY,
|
||||
&events,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
pub async fn clear_cache(redis: &RedisPool) -> Result<(), DatabaseError> {
|
||||
let mut redis = redis.connect().await?;
|
||||
redis
|
||||
.delete(ANALYTICS_EVENTS_NAMESPACE, ANALYTICS_EVENTS_ALL_KEY)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use super::DatabaseError;
|
||||
use crate::database::PgTransaction;
|
||||
use crate::models::ids::{
|
||||
AffiliateCodeId, ChargeId, CollectionId, FileId, ImageId, NotificationId,
|
||||
OAuthAccessTokenId, OAuthClientAuthorizationId, OAuthClientId,
|
||||
OAuthRedirectUriId, OrganizationId, PatId, PayoutId, ProductId,
|
||||
ProductPriceId, ProjectId, ReportId, SessionId, SharedInstanceId,
|
||||
SharedInstanceVersionId, TeamId, TeamMemberId, ThreadId, ThreadMessageId,
|
||||
UserSubscriptionId, VersionId,
|
||||
AffiliateCodeId, AnalyticsEventId, ChargeId, CollectionId, FileId, ImageId,
|
||||
NotificationId, OAuthAccessTokenId, OAuthClientAuthorizationId,
|
||||
OAuthClientId, OAuthRedirectUriId, OrganizationId, PatId, PayoutId,
|
||||
ProductId, ProductPriceId, ProjectId, ReportId, SessionId,
|
||||
SharedInstanceId, SharedInstanceVersionId, TeamId, TeamMemberId, ThreadId,
|
||||
ThreadMessageId, UserSubscriptionId, VersionId,
|
||||
};
|
||||
use ariadne::ids::base62_impl::to_base62;
|
||||
use ariadne::ids::{UserId, random_base62_rng, random_base62_rng_range};
|
||||
@@ -269,6 +269,10 @@ db_id_interface!(
|
||||
AffiliateCodeId,
|
||||
generator: generate_affiliate_code_id @ "affiliate_codes",
|
||||
);
|
||||
db_id_interface!(
|
||||
AnalyticsEventId,
|
||||
generator: generate_analytics_event_id @ "analytics_events",
|
||||
);
|
||||
|
||||
id_type!(CategoryId as i32);
|
||||
id_type!(GameId as i32);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod affiliate_code_item;
|
||||
pub mod analytics_event_item;
|
||||
pub mod categories;
|
||||
pub mod charge_item;
|
||||
pub mod collection_item;
|
||||
@@ -44,6 +45,7 @@ pub mod users_subscriptions_credits;
|
||||
pub mod version_item;
|
||||
|
||||
pub use affiliate_code_item::DBAffiliateCode;
|
||||
pub use analytics_event_item::DBAnalyticsEvent;
|
||||
pub use collection_item::DBCollection;
|
||||
pub use ids::*;
|
||||
pub use image_item::DBImage;
|
||||
|
||||
Reference in New Issue
Block a user