mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 19:46:33 +00:00
some validation
This commit is contained in:
Generated
+3
-1
@@ -76,10 +76,12 @@
|
||||
"name": "delphi_severity",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"hidden",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"severe"
|
||||
"severe",
|
||||
"malware"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3
-1
@@ -76,10 +76,12 @@
|
||||
"name": "delphi_severity",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"hidden",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"severe"
|
||||
"severe",
|
||||
"malware"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT MAX(created)\n FROM payouts_values\n ",
|
||||
"query": "\n SELECT MAX(date)\n FROM payout_period_days\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "max",
|
||||
"type_info": "Timestamptz"
|
||||
"type_info": "Date"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -16,5 +16,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "154d32137068456f7c0739271790d46bbe78053eda81fb2c227d95262cb4140a"
|
||||
"hash": "3962c046a8877433f2e5afd6064c0dfb11c7093d60093e8265d8cc5937c2c02c"
|
||||
}
|
||||
Generated
+3
-1
@@ -41,10 +41,12 @@
|
||||
"name": "delphi_severity",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"hidden",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"severe"
|
||||
"severe",
|
||||
"malware"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT LEAST(\n COALESCE(\n (SELECT MAX(created)::date FROM payouts_values),\n $1\n ),\n COALESCE(\n (SELECT MIN(period) FROM payout_periods),\n $1\n ),\n $1\n ) AS \"first_period!\"\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "first_period!",
|
||||
"type_info": "Date"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Date"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "805f9e937806491b75494627147259e3cc4cb82ba8f8645f748ad77b5abb81c8"
|
||||
}
|
||||
Generated
+3
-1
@@ -26,10 +26,12 @@
|
||||
"name": "delphi_severity",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"hidden",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"severe"
|
||||
"severe",
|
||||
"malware"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3
-1
@@ -22,10 +22,12 @@
|
||||
"name": "delphi_severity",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"hidden",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"severe"
|
||||
"severe",
|
||||
"malware"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3
-1
@@ -19,10 +19,12 @@
|
||||
"name": "delphi_severity",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"hidden",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"severe"
|
||||
"severe",
|
||||
"malware"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use chrono::{DateTime, Days, NaiveDate, Utc};
|
||||
use chrono::{DateTime, Days, Months, NaiveDate, Utc};
|
||||
use dashmap::DashMap;
|
||||
use eyre::Result;
|
||||
use rust_decimal::Decimal;
|
||||
@@ -29,6 +29,39 @@ pub struct DayEstimate {
|
||||
pub impressions: u128,
|
||||
}
|
||||
|
||||
/// Validate that an estimate contains exactly one complete entry for every
|
||||
/// calendar day in its payout period.
|
||||
pub(crate) fn validate_complete_period_estimate(
|
||||
estimate: &PeriodEstimate,
|
||||
) -> Result<()> {
|
||||
let period_end = estimate
|
||||
.period
|
||||
.date()
|
||||
.checked_add_months(Months::new(1))
|
||||
.wrap_err("calculating payout period end")?;
|
||||
let mut expected_date = estimate.period.date();
|
||||
|
||||
for day in &estimate.days {
|
||||
if day.date != expected_date {
|
||||
return Err(eyre::eyre!(
|
||||
"expected Aditude estimate for `{expected_date}`, found `{}`",
|
||||
day.date,
|
||||
));
|
||||
}
|
||||
expected_date = expected_date
|
||||
.succ_opt()
|
||||
.wrap_err("calculating next payout estimate date")?;
|
||||
}
|
||||
|
||||
if expected_date != period_end {
|
||||
return Err(eyre::eyre!(
|
||||
"missing Aditude estimate for `{expected_date}`",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get per-month and per-day estimated ad provider info for an inclusive date
|
||||
/// range.
|
||||
///
|
||||
@@ -193,4 +226,27 @@ mod tests {
|
||||
assert_eq!(estimate.days[0].raw_estimated_revenue_usd, dec!(12.34));
|
||||
assert_eq!(estimate.days[0].impressions, 5678);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validates_every_day_in_period() {
|
||||
let period =
|
||||
YearMonth::from_day1(NaiveDate::from_ymd_opt(2026, 2, 1).unwrap());
|
||||
let mut date = period.date();
|
||||
let period_end = NaiveDate::from_ymd_opt(2026, 3, 1).unwrap();
|
||||
let mut days = Vec::new();
|
||||
while date < period_end {
|
||||
days.push(DayEstimate {
|
||||
date,
|
||||
raw_estimated_revenue_usd: dec!(1),
|
||||
impressions: 1,
|
||||
});
|
||||
date = date.succ_opt().unwrap();
|
||||
}
|
||||
let mut estimate = PeriodEstimate { period, days };
|
||||
|
||||
validate_complete_period_estimate(&estimate).unwrap();
|
||||
|
||||
estimate.days.remove(10);
|
||||
assert!(validate_complete_period_estimate(&estimate).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,15 @@ use crate::{
|
||||
database::{
|
||||
PgPool,
|
||||
models::{
|
||||
DBUserId, generate_payout_run_id,
|
||||
DBUserId, DatabaseError, generate_payout_run_id,
|
||||
payout_run_item::{DBPayoutRun, PayoutRunStatus},
|
||||
},
|
||||
},
|
||||
models::{ids::PayoutRunId, pats::Scopes},
|
||||
queue::{payout_run::estimate, session::AuthQueue},
|
||||
queue::{
|
||||
payout_run::{estimate, validate_complete_period_estimate},
|
||||
session::AuthQueue,
|
||||
},
|
||||
routes::ApiError,
|
||||
util::{
|
||||
error::Context,
|
||||
@@ -57,6 +60,7 @@ pub struct StartPayoutRunResponse {
|
||||
(status = BAD_REQUEST, description = "Invalid payout run input"),
|
||||
(status = UNAUTHORIZED, description = "Invalid authentication or TOTP code"),
|
||||
(status = CONFLICT, description = "Payout period is unavailable or another run is active"),
|
||||
(status = FAILED_DEPENDENCY, description = "Aditude returned an incomplete period"),
|
||||
),
|
||||
security(("bearer_auth" = ["SESSION_ACCESS"])),
|
||||
)]
|
||||
@@ -151,6 +155,8 @@ pub async fn start_run(
|
||||
let estimate = estimates
|
||||
.pop()
|
||||
.wrap_internal_err("missing requested payout estimate")?;
|
||||
validate_complete_period_estimate(&estimate)
|
||||
.wrap_failed_dependency_err("validating payout estimate")?;
|
||||
let payload = PayoutRunPayload {
|
||||
raw_actual_revenue_usd: body.raw_actual_revenue_usd,
|
||||
adjustments: body.adjustments.clone(),
|
||||
@@ -272,9 +278,16 @@ pub async fn start_run(
|
||||
cancelled_by: None,
|
||||
error: None,
|
||||
};
|
||||
run.upsert(&mut transaction)
|
||||
.await
|
||||
.wrap_internal_err("creating scheduled payout run")?;
|
||||
let create_run_result = run.upsert(&mut transaction).await;
|
||||
if let Err(DatabaseError::Database(sqlx::Error::Database(error))) =
|
||||
&create_run_result
|
||||
&& error.constraint() == Some("payout_runs_single_active")
|
||||
{
|
||||
return Err(ApiError::Conflict(eyre::eyre!(
|
||||
"another payout run is already scheduled or running",
|
||||
)));
|
||||
}
|
||||
create_run_result.wrap_internal_err("creating scheduled payout run")?;
|
||||
|
||||
transaction
|
||||
.commit()
|
||||
|
||||
Reference in New Issue
Block a user