From 692590bff435da3bdb7a0bd1bf9f139c3bdc6eb1 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 24 Sep 2024 15:44:55 -0400 Subject: [PATCH] collab: Fix GitHub user retrieval in seed script (#18296) This PR fixes the GitHub user retrieval in the database seed script. The users returned from the [list users](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#list-users) endpoint don't have a `created_at` timestamp, so we need to fetch them individually. I want to rework this further at a later date, this is just a bandaid to get things working again. Release Notes: - N/A --- crates/collab/src/db/queries/users.rs | 6 ++++ crates/collab/src/seed.rs | 43 +++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs index b755476e338b60979033bebc7b6e829f1e159fff..4443d751542b50b13bd241ee02937a721a7b43a2 100644 --- a/crates/collab/src/db/queries/users.rs +++ b/crates/collab/src/db/queries/users.rs @@ -298,6 +298,12 @@ impl Database { result } + /// Returns all feature flags. + pub async fn list_feature_flags(&self) -> Result> { + self.transaction(|tx| async move { Ok(feature_flag::Entity::find().all(&*tx).await?) }) + .await + } + /// Creates a new feature flag. pub async fn create_user_flag(&self, flag: &str, enabled_for_all: bool) -> Result { self.transaction(|tx| async move { diff --git a/crates/collab/src/seed.rs b/crates/collab/src/seed.rs index 15aa9d159183f8b79856c83048bc7ad3c46d3ab9..035d58109b596dd428d2808683408ff3cbbc9d95 100644 --- a/crates/collab/src/seed.rs +++ b/crates/collab/src/seed.rs @@ -16,13 +16,23 @@ struct GithubUser { created_at: DateTime, } +/// A GitHub user returned from the [List users](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#list-users) endpoint. +/// +/// Notably, this data type does not have the `created_at` field. +#[derive(Debug, Deserialize)] +struct ListGithubUser { + id: i32, + login: String, + email: Option, +} + #[derive(Deserialize)] struct SeedConfig { - // Which users to create as admins. + /// Which users to create as admins. admins: Vec, - // Which channels to create (all admins are invited to all channels) + /// Which channels to create (all admins are invited to all channels). channels: Vec, - // Number of random users to create from the Github API + /// Number of random users to create from the Github API. number_of_users: Option, } @@ -47,11 +57,21 @@ pub async fn seed(config: &Config, db: &Database, force: bool) -> anyhow::Result let flag_names = ["remoting", "language-models"]; let mut flags = Vec::new(); + let existing_feature_flags = db.list_feature_flags().await?; + for flag_name in flag_names { + if existing_feature_flags + .iter() + .any(|flag| flag.flag == flag_name) + { + log::info!("Flag {flag_name:?} already exists"); + continue; + } + let flag = db .create_user_flag(flag_name, false) .await - .unwrap_or_else(|_| panic!("failed to create flag: '{flag_name}'")); + .unwrap_or_else(|err| panic!("failed to create flag: '{flag_name}': {err}")); flags.push(flag); } @@ -121,9 +141,19 @@ pub async fn seed(config: &Config, db: &Database, force: bool) -> anyhow::Result if let Some(last_user_id) = last_user_id { write!(&mut uri, "&since={}", last_user_id).unwrap(); } - let users = fetch_github::>(&client, &uri).await; + let users = fetch_github::>(&client, &uri).await; for github_user in users { + log::info!("Seeding {:?} from GitHub", github_user.login); + + // Fetch the user to get their `created_at` timestamp, since it + // isn't on the list response. + let github_user: GithubUser = fetch_github( + &client, + &format!("https://api.github.com/user/{}", github_user.id), + ) + .await; + last_user_id = Some(github_user.id); user_count += 1; let user = db @@ -143,6 +173,9 @@ pub async fn seed(config: &Config, db: &Database, force: bool) -> anyhow::Result flag, user.id ))?; } + + // Sleep to avoid getting rate-limited by GitHub. + tokio::time::sleep(std::time::Duration::from_millis(250)).await; } } }