From 6b0dd9b130bf32e82f70e04563923f0f2d7988a3 Mon Sep 17 00:00:00 2001
From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com>
Date: Fri, 2 May 2025 10:38:45 -0700
Subject: [PATCH] feat: add functions for interval-based secret loading

---
 .../bitwarden-sm/src/client_secrets.rs        | 17 ++--
 .../bitwarden-sm/src/secrets/get.rs           | 85 +++++++++++++++++++
 .../bitwarden-sm/src/secrets/mod.rs           |  4 +-
 3 files changed, 99 insertions(+), 7 deletions(-)

diff --git a/bitwarden_license/bitwarden-sm/src/client_secrets.rs b/bitwarden_license/bitwarden-sm/src/client_secrets.rs
index db7d5104d..a8521e4c7 100644
--- a/bitwarden_license/bitwarden-sm/src/client_secrets.rs
+++ b/bitwarden_license/bitwarden-sm/src/client_secrets.rs
@@ -3,12 +3,12 @@ use bitwarden_core::Client;
 use crate::{
     error::SecretsManagerError,
     secrets::{
-        create_secret, delete_secrets, get_secret, get_secrets_by_ids, list_secrets,
-        list_secrets_by_project, sync_secrets, update_secret, SecretCreateRequest,
+        create_secret, delete_secrets, get_secret, get_secrets_by_ids, get_secrets_by_project,
+        list_secrets, list_secrets_by_project, sync_secrets, update_secret, SecretCreateRequest,
         SecretGetRequest, SecretIdentifiersByProjectRequest, SecretIdentifiersRequest,
-        SecretIdentifiersResponse, SecretPutRequest, SecretResponse, SecretsDeleteRequest,
-        SecretsDeleteResponse, SecretsGetRequest, SecretsResponse, SecretsSyncRequest,
-        SecretsSyncResponse,
+        SecretIdentifiersResponse, SecretPutRequest, SecretResponse, SecretsByProjectGetRequest,
+        SecretsDeleteRequest, SecretsDeleteResponse, SecretsGetRequest, SecretsResponse,
+        SecretsSyncRequest, SecretsSyncResponse,
     },
 };
 
@@ -79,6 +79,13 @@ impl SecretsClient {
     ) -> Result<SecretsSyncResponse, SecretsManagerError> {
         sync_secrets(&self.client, input).await
     }
+
+    pub async fn get_by_project(
+        &self,
+        input: &SecretsByProjectGetRequest,
+    ) -> Result<SecretsResponse, SecretsManagerError> {
+        get_secrets_by_project(&self.client, input).await
+    }
 }
 
 /// This trait is for backward compatibility
diff --git a/bitwarden_license/bitwarden-sm/src/secrets/get.rs b/bitwarden_license/bitwarden-sm/src/secrets/get.rs
index 87515df04..a74921b76 100644
--- a/bitwarden_license/bitwarden-sm/src/secrets/get.rs
+++ b/bitwarden_license/bitwarden-sm/src/secrets/get.rs
@@ -1,3 +1,4 @@
+use bitwarden_api_api::models::GetSecretsRequestModel;
 use bitwarden_core::Client;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -5,6 +6,8 @@ use uuid::Uuid;
 
 use crate::{error::SecretsManagerError, secrets::SecretResponse};
 
+use super::SecretsResponse;
+
 #[derive(Serialize, Deserialize, Debug, JsonSchema)]
 #[serde(rename_all = "camelCase", deny_unknown_fields)]
 pub struct SecretGetRequest {
@@ -23,3 +26,85 @@ pub(crate) async fn get_secret(
 
     SecretResponse::process_response(res, &mut key_store.context())
 }
+
+#[derive(Serialize, Deserialize, Debug, JsonSchema)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+/// Request to sync secrets for a specific project
+pub struct SecretsByProjectGetRequest {
+    /// Project ID to sync secrets from
+    pub project_id: Uuid,
+}
+
+// get_secrets_full(id: ProjectId);
+// return: Vec<{ secrets_name: }>
+
+// get_secrets_by_project_view(id: ProjectId)
+// return: Vec<{ secret_id: SecretId, secret_name: &str, revision_date: DateTime<Utc> }>
+
+// get_secret(id: SecretId)
+
+// Will call on initial load to get all the data
+pub(crate) async fn get_secrets_by_project(
+    client: &Client,
+    input: &SecretsByProjectGetRequest,
+) -> Result<SecretsResponse, SecretsManagerError> {
+    let config = client.internal.get_api_configurations().await;
+    // let last_synced_date = input.last_synced_date.map(|date| date.to_rfc3339());
+
+    let secrets_with_project_list =
+        bitwarden_api_api::apis::secrets_api::projects_project_id_secrets_get(
+            &config.api,
+            input.project_id,
+            // last_synced_date,
+        )
+        .await?;
+
+    let secret_ids: Vec<Uuid> = secrets_with_project_list
+        .secrets
+        .unwrap_or_default()
+        .into_iter()
+        .map(|s| s.id.unwrap_or_default())
+        .collect();
+
+    let request = Some(GetSecretsRequestModel { ids: secret_ids });
+
+    let res =
+        bitwarden_api_api::apis::secrets_api::secrets_get_by_ids_post(&config.api, request).await?;
+
+    let key_store = client.internal.get_key_store();
+
+    SecretsResponse::process_response(res, &mut key_store.context())
+}
+
+#[derive(Serialize, Deserialize, Debug, JsonSchema)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct SecretSlimResponse {
+    pub id: Uuid,
+    pub revision_date: chrono::DateTime<chrono::Utc>,
+}
+
+pub(crate) async fn get_secrets_view_by_project(
+    client: &Client,
+    input: &SecretsByProjectGetRequest,
+) -> Result<Vec<SecretSlimResponse>, SecretsManagerError> {
+    let config = client.internal.get_api_configurations().await;
+
+    let secrets_with_project_list =
+        bitwarden_api_api::apis::secrets_api::projects_project_id_secrets_get(
+            &config.api,
+            input.project_id,
+        )
+        .await?;
+
+    secrets_with_project_list
+        .secrets
+        .unwrap_or_default()
+        .into_iter()
+        .map(|s| {
+            Ok(SecretSlimResponse {
+                id: bitwarden_core::require!(s.id),
+                revision_date: bitwarden_core::require!(s.revision_date).parse()?,
+            })
+        })
+        .collect()
+}
diff --git a/bitwarden_license/bitwarden-sm/src/secrets/mod.rs b/bitwarden_license/bitwarden-sm/src/secrets/mod.rs
index 74a7a9c47..946c5f4ab 100644
--- a/bitwarden_license/bitwarden-sm/src/secrets/mod.rs
+++ b/bitwarden_license/bitwarden-sm/src/secrets/mod.rs
@@ -11,8 +11,8 @@ pub(crate) use create::create_secret;
 pub use create::SecretCreateRequest;
 pub(crate) use delete::delete_secrets;
 pub use delete::{SecretsDeleteRequest, SecretsDeleteResponse};
-pub(crate) use get::get_secret;
-pub use get::SecretGetRequest;
+pub(crate) use get::{get_secret, get_secrets_by_project, get_secrets_view_by_project};
+pub use get::{SecretGetRequest, SecretsByProjectGetRequest};
 pub(crate) use get_by_ids::get_secrets_by_ids;
 pub use get_by_ids::SecretsGetRequest;
 pub(crate) use list::{list_secrets, list_secrets_by_project};