collab: Extract contributor endpoints to their own module (#15251)

Marshall Bowers created

This PR extracts the contributor endpoints to their own module for
organizational purposes.

Release Notes:

- N/A

Change summary

crates/collab/src/api.rs              | 112 +-------------------------
crates/collab/src/api/contributors.rs | 121 +++++++++++++++++++++++++++++
2 files changed, 126 insertions(+), 107 deletions(-)

Detailed changes

crates/collab/src/api.rs 🔗

@@ -1,3 +1,4 @@
+pub mod contributors;
 pub mod events;
 pub mod extensions;
 pub mod ips_file;
@@ -5,13 +6,13 @@ pub mod slack;
 
 use crate::{
     auth,
-    db::{ContributorSelector, User, UserId},
+    db::{User, UserId},
     rpc, AppState, Error, Result,
 };
 use anyhow::anyhow;
 use axum::{
     body::Body,
-    extract::{self, Path, Query},
+    extract::{Path, Query},
     http::{self, Request, StatusCode},
     middleware::{self, Next},
     response::IntoResponse,
@@ -19,9 +20,8 @@ use axum::{
     Extension, Json, Router,
 };
 use axum_extra::response::ErasedJson;
-use chrono::{NaiveDateTime, SecondsFormat};
 use serde::{Deserialize, Serialize};
-use std::sync::{Arc, OnceLock};
+use std::sync::Arc;
 use tower::ServiceBuilder;
 
 pub use extensions::fetch_extensions_from_blob_store_periodically;
@@ -31,8 +31,7 @@ pub fn routes(rpc_server: Option<Arc<rpc::Server>>, state: Arc<AppState>) -> Rou
         .route("/user", get(get_authenticated_user))
         .route("/users/:id/access_tokens", post(create_access_token))
         .route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
-        .route("/contributors", get(get_contributors).post(add_contributor))
-        .route("/contributor", get(check_is_contributor))
+        .merge(contributors::router())
         .layer(
             ServiceBuilder::new()
                 .layer(Extension(state))
@@ -126,107 +125,6 @@ async fn get_rpc_server_snapshot(
     Ok(ErasedJson::pretty(rpc_server.snapshot().await))
 }
 
-async fn get_contributors(Extension(app): Extension<Arc<AppState>>) -> Result<Json<Vec<String>>> {
-    Ok(Json(app.db.get_contributors().await?))
-}
-
-#[derive(Debug, Deserialize)]
-struct CheckIsContributorParams {
-    github_user_id: Option<i32>,
-    github_login: Option<String>,
-}
-
-impl CheckIsContributorParams {
-    fn as_contributor_selector(self) -> Result<ContributorSelector> {
-        if let Some(github_user_id) = self.github_user_id {
-            return Ok(ContributorSelector::GitHubUserId { github_user_id });
-        }
-
-        if let Some(github_login) = self.github_login {
-            return Ok(ContributorSelector::GitHubLogin { github_login });
-        }
-
-        Err(anyhow!(
-            "must be one of `github_user_id` or `github_login`."
-        ))?
-    }
-}
-
-#[derive(Debug, Serialize)]
-struct CheckIsContributorResponse {
-    signed_at: Option<String>,
-}
-
-async fn check_is_contributor(
-    Extension(app): Extension<Arc<AppState>>,
-    Query(params): Query<CheckIsContributorParams>,
-) -> Result<Json<CheckIsContributorResponse>> {
-    let params = params.as_contributor_selector()?;
-
-    if RenovateBot::is_renovate_bot(&params) {
-        return Ok(Json(CheckIsContributorResponse {
-            signed_at: Some(
-                RenovateBot::created_at()
-                    .and_utc()
-                    .to_rfc3339_opts(SecondsFormat::Millis, true),
-            ),
-        }));
-    }
-
-    Ok(Json(CheckIsContributorResponse {
-        signed_at: app
-            .db
-            .get_contributor_sign_timestamp(&params)
-            .await?
-            .map(|ts| ts.and_utc().to_rfc3339_opts(SecondsFormat::Millis, true)),
-    }))
-}
-
-/// The Renovate bot GitHub user (`renovate[bot]`).
-///
-/// https://api.github.com/users/renovate[bot]
-struct RenovateBot;
-
-impl RenovateBot {
-    const LOGIN: &'static str = "renovate[bot]";
-    const USER_ID: i32 = 29139614;
-
-    /// Returns the `created_at` timestamp for the Renovate bot user.
-    fn created_at() -> &'static NaiveDateTime {
-        static CREATED_AT: OnceLock<NaiveDateTime> = OnceLock::new();
-        CREATED_AT.get_or_init(|| {
-            chrono::DateTime::parse_from_rfc3339("2017-06-02T07:04:12Z")
-                .expect("failed to parse 'created_at' for 'renovate[bot]'")
-                .naive_utc()
-        })
-    }
-
-    /// Returns whether the given contributor selector corresponds to the Renovate bot user.
-    fn is_renovate_bot(contributor: &ContributorSelector) -> bool {
-        match contributor {
-            ContributorSelector::GitHubLogin { github_login } => github_login == Self::LOGIN,
-            ContributorSelector::GitHubUserId { github_user_id } => {
-                github_user_id == &Self::USER_ID
-            }
-        }
-    }
-}
-
-async fn add_contributor(
-    Extension(app): Extension<Arc<AppState>>,
-    extract::Json(params): extract::Json<AuthenticatedUserParams>,
-) -> Result<()> {
-    let initial_channel_id = app.config.auto_join_channel_id;
-    app.db
-        .add_contributor(
-            &params.github_login,
-            params.github_user_id,
-            params.github_email.as_deref(),
-            initial_channel_id,
-        )
-        .await
-}
-
 #[derive(Deserialize)]
 struct CreateAccessTokenQueryParams {
     public_key: String,

crates/collab/src/api/contributors.rs 🔗

@@ -0,0 +1,121 @@
+use std::sync::{Arc, OnceLock};
+
+use anyhow::anyhow;
+use axum::{
+    extract::{self, Query},
+    routing::get,
+    Extension, Json, Router,
+};
+use chrono::{NaiveDateTime, SecondsFormat};
+use serde::{Deserialize, Serialize};
+
+use crate::api::AuthenticatedUserParams;
+use crate::db::ContributorSelector;
+use crate::{AppState, Result};
+
+pub fn router() -> Router {
+    Router::new()
+        .route("/contributors", get(get_contributors).post(add_contributor))
+        .route("/contributor", get(check_is_contributor))
+}
+
+async fn get_contributors(Extension(app): Extension<Arc<AppState>>) -> Result<Json<Vec<String>>> {
+    Ok(Json(app.db.get_contributors().await?))
+}
+
+#[derive(Debug, Deserialize)]
+struct CheckIsContributorParams {
+    github_user_id: Option<i32>,
+    github_login: Option<String>,
+}
+
+impl CheckIsContributorParams {
+    fn as_contributor_selector(self) -> Result<ContributorSelector> {
+        if let Some(github_user_id) = self.github_user_id {
+            return Ok(ContributorSelector::GitHubUserId { github_user_id });
+        }
+
+        if let Some(github_login) = self.github_login {
+            return Ok(ContributorSelector::GitHubLogin { github_login });
+        }
+
+        Err(anyhow!(
+            "must be one of `github_user_id` or `github_login`."
+        ))?
+    }
+}
+
+#[derive(Debug, Serialize)]
+struct CheckIsContributorResponse {
+    signed_at: Option<String>,
+}
+
+async fn check_is_contributor(
+    Extension(app): Extension<Arc<AppState>>,
+    Query(params): Query<CheckIsContributorParams>,
+) -> Result<Json<CheckIsContributorResponse>> {
+    let params = params.as_contributor_selector()?;
+
+    if RenovateBot::is_renovate_bot(&params) {
+        return Ok(Json(CheckIsContributorResponse {
+            signed_at: Some(
+                RenovateBot::created_at()
+                    .and_utc()
+                    .to_rfc3339_opts(SecondsFormat::Millis, true),
+            ),
+        }));
+    }
+
+    Ok(Json(CheckIsContributorResponse {
+        signed_at: app
+            .db
+            .get_contributor_sign_timestamp(&params)
+            .await?
+            .map(|ts| ts.and_utc().to_rfc3339_opts(SecondsFormat::Millis, true)),
+    }))
+}
+
+/// The Renovate bot GitHub user (`renovate[bot]`).
+///
+/// https://api.github.com/users/renovate[bot]
+struct RenovateBot;
+
+impl RenovateBot {
+    const LOGIN: &'static str = "renovate[bot]";
+    const USER_ID: i32 = 29139614;
+
+    /// Returns the `created_at` timestamp for the Renovate bot user.
+    fn created_at() -> &'static NaiveDateTime {
+        static CREATED_AT: OnceLock<NaiveDateTime> = OnceLock::new();
+        CREATED_AT.get_or_init(|| {
+            chrono::DateTime::parse_from_rfc3339("2017-06-02T07:04:12Z")
+                .expect("failed to parse 'created_at' for 'renovate[bot]'")
+                .naive_utc()
+        })
+    }
+
+    /// Returns whether the given contributor selector corresponds to the Renovate bot user.
+    fn is_renovate_bot(contributor: &ContributorSelector) -> bool {
+        match contributor {
+            ContributorSelector::GitHubLogin { github_login } => github_login == Self::LOGIN,
+            ContributorSelector::GitHubUserId { github_user_id } => {
+                github_user_id == &Self::USER_ID
+            }
+        }
+    }
+}
+
+async fn add_contributor(
+    Extension(app): Extension<Arc<AppState>>,
+    extract::Json(params): extract::Json<AuthenticatedUserParams>,
+) -> Result<()> {
+    let initial_channel_id = app.config.auto_join_channel_id;
+    app.db
+        .add_contributor(
+            &params.github_login,
+            params.github_user_id,
+            params.github_email.as_deref(),
+            initial_channel_id,
+        )
+        .await
+}