Răsfoiți Sursa

Merge pull request #2760 from pyth-network/cprussin/add-result-count-to-fortuna-log

feat(fortuna): add result count to results
Connor Prussin 5 luni în urmă
părinte
comite
a334a22bdd

+ 17 - 8
apps/fortuna/src/api/explorer.rs

@@ -35,6 +35,12 @@ pub struct ExplorerQueryParams {
     pub offset: Option<u64>,
 }
 
+#[derive(Debug, serde::Serialize, utoipa::ToSchema)]
+pub struct ExplorerResponse {
+    pub requests: Vec<RequestStatus>,
+    pub total_results: u64,
+}
+
 /// Returns the logs of all requests captured by the keeper.
 ///
 /// This endpoint allows you to filter the logs by a specific network ID, a query string (which can be a transaction hash, sender address, or sequence number), and a time range.
@@ -42,13 +48,13 @@ pub struct ExplorerQueryParams {
 #[utoipa::path(
     get,
     path = "/v1/logs",
-    responses((status = 200, description = "A list of Entropy request logs", body = Vec<RequestStatus>)),
+    responses((status = 200, description = "A list of Entropy request logs", body = ExplorerResponse)),
     params(ExplorerQueryParams)
 )]
 pub async fn explorer(
     State(state): State<crate::api::ApiState>,
     Query(query_params): Query<ExplorerQueryParams>,
-) -> anyhow::Result<Json<Vec<RequestStatus>>, RestError> {
+) -> anyhow::Result<Json<ExplorerResponse>, RestError> {
     if let Some(network_id) = &query_params.network_id {
         if !state
             .chains
@@ -89,10 +95,13 @@ pub async fn explorer(
     if let Some(max_timestamp) = query_params.max_timestamp {
         query = query.max_timestamp(max_timestamp);
     }
-    Ok(Json(
-        query
-            .execute()
-            .await
-            .map_err(|_| RestError::TemporarilyUnavailable)?,
-    ))
+
+    let (requests, total_results) = tokio::join!(query.execute(), query.count_results());
+    let requests = requests.map_err(|_| RestError::TemporarilyUnavailable)?;
+    let total_results = total_results.map_err(|_| RestError::TemporarilyUnavailable)?;
+
+    Ok(Json(ExplorerResponse {
+        requests,
+        total_results,
+    }))
 }

+ 1 - 0
apps/fortuna/src/command/run.rs

@@ -45,6 +45,7 @@ pub async fn run_api(
     crate::api::Blob,
     crate::api::BinaryEncoding,
     crate::api::StateTag,
+    crate::api::ExplorerResponse,
     )
     ),
     tags(

+ 64 - 17
apps/fortuna/src/history.rs

@@ -427,8 +427,34 @@ impl<'a> RequestQueryBuilder<'a> {
     }
 
     pub async fn execute(&self) -> Result<Vec<RequestStatus>> {
-        let mut query_builder =
-            QueryBuilder::new("SELECT * FROM request WHERE created_at BETWEEN ");
+        let mut query_builder = self.build_query("*");
+        query_builder.push(" LIMIT ");
+        query_builder.push_bind(self.limit);
+        query_builder.push(" OFFSET ");
+        query_builder.push_bind(self.offset);
+
+        let result: sqlx::Result<Vec<RequestRow>> =
+            query_builder.build_query_as().fetch_all(self.pool).await;
+
+        if let Err(e) = &result {
+            tracing::error!("Failed to fetch request: {}", e);
+        }
+
+        Ok(result?.into_iter().filter_map(|row| row.into()).collect())
+    }
+
+    pub async fn count_results(&self) -> Result<u64> {
+        self.build_query("COUNT(*) AS count")
+            .build_query_scalar::<u64>()
+            .fetch_one(self.pool)
+            .await
+            .map_err(|err| err.into())
+    }
+
+    fn build_query(&self, columns: &str) -> QueryBuilder<Sqlite> {
+        let mut query_builder = QueryBuilder::new(format!(
+            "SELECT {columns} FROM request WHERE created_at BETWEEN "
+        ));
         query_builder.push_bind(self.min_timestamp);
         query_builder.push(" AND ");
         query_builder.push_bind(self.max_timestamp);
@@ -464,21 +490,8 @@ impl<'a> RequestQueryBuilder<'a> {
             query_builder.push_bind(state);
         }
 
-        query_builder.push(" ORDER BY created_at DESC LIMIT ");
-        query_builder.push_bind(self.limit);
-        query_builder.push(" OFFSET ");
-        query_builder.push_bind(self.offset);
-
-        let rows = query_builder
-            .build_query_as::<RequestRow>()
-            .fetch_all(self.pool)
-            .await;
-
-        if let Err(e) = &rows {
-            tracing::error!("Failed to fetch request by time: {}", e);
-        }
-
-        Ok(rows?.into_iter().filter_map(|row| row.into()).collect())
+        query_builder.push(" ORDER BY created_at DESC");
+        query_builder
     }
 }
 
@@ -928,4 +941,38 @@ mod test {
             .unwrap();
         assert_eq!(logs, vec![status]);
     }
+
+    #[tokio::test]
+    async fn test_count_results() {
+        let history = History::new_in_memory().await.unwrap();
+        History::update_request_status(&history.pool, get_random_request_status()).await;
+        History::update_request_status(&history.pool, get_random_request_status()).await;
+        let mut failed_status = get_random_request_status();
+        History::update_request_status(&history.pool, failed_status.clone()).await;
+        failed_status.state = RequestEntryState::Failed {
+            reason: "Failed".to_string(),
+            provider_random_number: None,
+        };
+        History::update_request_status(&history.pool, failed_status.clone()).await;
+
+        let results = history.query().count_results().await.unwrap();
+        assert_eq!(results, 3);
+
+        let results = history
+            .query()
+            .limit(1)
+            .unwrap()
+            .count_results()
+            .await
+            .unwrap();
+        assert_eq!(results, 3);
+
+        let results = history
+            .query()
+            .state(StateTag::Pending)
+            .count_results()
+            .await
+            .unwrap();
+        assert_eq!(results, 2);
+    }
 }