allsource_core/domain/entities/
query_usage.rs1use crate::error::{AllSourceError, Result};
3use serde::{Deserialize, Serialize};
4
5pub const QUERY_RETRY_SECONDS: i64 = 3_600;
6pub const MAX_QUERY_RECEIPTS: usize = 4_096;
7
8#[derive(Clone, Debug, Deserialize, Serialize)]
9#[serde(deny_unknown_fields)]
10pub struct QueryUsageRequest {
11 pub operation_id: String,
12 pub fingerprint: String,
13 pub count: u64,
14 pub expected_period: u64,
15}
16
17impl QueryUsageRequest {
18 pub fn expires_at(&self) -> Result<i64> {
19 let invalid = || AllSourceError::ValidationError("Invalid query admission request".into());
20 let (time, nonce) = self.operation_id.split_once(':').ok_or_else(invalid)?;
21 let issued: i64 = time.parse().map_err(|_| invalid())?;
22 let uuid = uuid::Uuid::parse_str(nonce).map_err(|_| invalid())?;
23 if issued < 0
24 || time != issued.to_string()
25 || nonce != uuid.hyphenated().to_string()
26 || !(1..=4).contains(&self.count)
27 || self.fingerprint.len() != 64
28 || !self
29 .fingerprint
30 .bytes()
31 .all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(&c))
32 {
33 return Err(invalid());
34 }
35 issued.checked_add(QUERY_RETRY_SECONDS).ok_or_else(invalid)
36 }
37}
38
39#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
40#[serde(deny_unknown_fields)]
41pub struct QueryUsageReceipt {
42 pub operation_id: String,
43 pub fingerprint: String,
44 pub count: u64,
45 pub period: u64,
46 pub used: u64,
47 pub expires_at: i64,
48}
49
50impl QueryUsageReceipt {
51 pub fn matches(&self, request: &QueryUsageRequest) -> bool {
52 self.fingerprint == request.fingerprint
53 && self.count == request.count
54 && self.period == request.expected_period
55 }
56}
57
58#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
59#[serde(rename_all = "snake_case")]
60pub enum QueryUsageDenial {
61 ExpiredOperation,
62 OperationConflict,
63 PeriodChanged,
64 QuotaExceeded,
65 ReceiptCapacity,
66 InactiveTenant,
67 InvalidQuotaMetadata,
68}
69
70#[derive(Debug, PartialEq)]
71pub enum QueryUsageDecision {
72 Admitted {
73 receipt: QueryUsageReceipt,
74 replayed: bool,
75 },
76 Denied(QueryUsageDenial),
77}
78
79#[derive(Debug, Deserialize)]
80#[serde(deny_unknown_fields)]
81pub struct QueryUsageReset {
82 pub expected_period: u64,
83}
84
85#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
86#[serde(deny_unknown_fields)]
87pub struct QueryUsageResetReceipt {
88 pub previous_period: u64,
89 pub period: u64,
90}
91
92#[derive(Debug, Serialize)]
93pub struct QueryUsageSnapshot {
94 pub period: u64,
95 pub used: u64,
96 pub quota: i64,
97 pub managed: bool,
98}
99
100#[derive(Debug, PartialEq)]
101pub enum QueryUsageResetDecision {
102 Reset {
103 receipt: QueryUsageResetReceipt,
104 replayed: bool,
105 },
106 Denied(QueryUsageDenial),
107}