From 9a9b631c005055742b77f6a9a17d4b3e7ddde182 Mon Sep 17 00:00:00 2001 From: boxbeam Date: Tue, 9 Apr 2024 22:52:43 -0400 Subject: [PATCH] Implement suggestions and RepositoryAccess listing github-provided repos --- .../0024_github-provided-repos.up.sql | 5 +- ee/tabby-db/schema.sqlite | Bin 147456 -> 143360 bytes ee/tabby-db/src/github_repository_provider.rs | 10 ++- ee/tabby-webserver/src/hub/mod.rs | 76 +++++++++++++++++- .../src/schema/github_repository_provider.rs | 4 +- ee/tabby-webserver/src/schema/mod.rs | 6 +- ee/tabby-webserver/src/service/dao.rs | 1 + .../src/service/github_repository_provider.rs | 17 +++- 8 files changed, 106 insertions(+), 13 deletions(-) diff --git a/ee/tabby-db/migrations/0024_github-provided-repos.up.sql b/ee/tabby-db/migrations/0024_github-provided-repos.up.sql index 7ec9760211bd..dc11e6c1f8d0 100644 --- a/ee/tabby-db/migrations/0024_github-provided-repos.up.sql +++ b/ee/tabby-db/migrations/0024_github-provided-repos.up.sql @@ -5,7 +5,6 @@ CREATE TABLE github_provided_repositories( vendor_id TEXT NOT NULL, name TEXT NOT NULL, git_url TEXT NOT NULL, - active BOOLEAN NOT NULL DEFAULT TRUE, - FOREIGN KEY (github_repository_provider_id) REFERENCES github_repository_provider(id), - CONSTRAINT provided_repository_unique_name UNIQUE (name) + active BOOLEAN NOT NULL DEFAULT FALSE, + FOREIGN KEY (github_repository_provider_id) REFERENCES github_repository_provider(id) ON DELETE CASCADE ); diff --git a/ee/tabby-db/schema.sqlite b/ee/tabby-db/schema.sqlite index c333d74bcc97284a4c79d028a4751672d4534487..09e420b504d3d632745cfc52c8a69f5b5e09f778 100644 GIT binary patch delta 702 zcmZo@;B0unF+rYDd85L8en!Df0$Z+G=idZzz#XLJ7hMOl58JE|g%a2V?kx+(<^#R2au5?d6D4_@!dkGYa*5aHjBdG%y z^OrPWE(%^dxk*wBF1A_HkZJ15$hBGZ}<=i9oTKSILv7 z$mqiLo&btPF1kPYw+uqBmMl$iwRsjd8RzVinAcG)eoB# zwka^O7%&SkzMRe;z^FK}=lsMCDU+ia`!+{2@-S}y8pNo}&Uj$DaulQ2^aUY|MceO3 zF{-kv2rzUo@E_;z<1^*C&TYtL%f-Oaz?RQ?i?x(hi1`3>Hq%;$j)@BsxLTBy*u`C4 z8Qa~rDF;LjdV(^C_08O*htpET3 delta 814 zcmZp8z|qjaIYFLLWuwA#`*KGZVUk7#jleexhiF|fzvc9-EJ5>ARTyX>D$enSM7l^CE z#m3{9jxfE@UVHRHoiEIYmMlF4hMWi&eZo`JjX%TfI*ql0|5J2U6xNI9X8+VTF&JArouX z$i+;Xw@=SSz6=#8Bk-3MO6t*ca zvKTN6GfbGy9>A!`!L^Jlmtg|`kBJKtwwvZLMlx=WX5?Yq{xyhEmz~jXx^fhw*Yv)8 z#-i=_qZn0Ljrkb&GVm?p`NPA(eVWUg!-{<=y93)@)^1iamI@{@pd=7*+kr?1HhDHM zy, limit: Option, skip_id: Option, backwards: bool, ) -> Result> { + let provider_ids = provider_ids + .into_iter() + .map(|id| id.to_string()) + .collect::>() + .join(", "); let repos = query_paged_as!( GithubProvidedRepositoryDAO, "github_provided_repositories", @@ -150,7 +155,8 @@ impl DbConn { limit, skip_id, backwards, - Some(format!("github_repository_provider_id = {provider_id}")) + (!provider_ids.is_empty()) + .then(|| format!("github_repository_provider_id IN ({provider_ids})")) ) .fetch_all(&self.pool) .await?; diff --git a/ee/tabby-webserver/src/hub/mod.rs b/ee/tabby-webserver/src/hub/mod.rs index ea6e1814aacd..5e3bd9de2681 100644 --- a/ee/tabby-webserver/src/hub/mod.rs +++ b/ee/tabby-webserver/src/hub/mod.rs @@ -144,7 +144,10 @@ impl Hub for Arc { } } } + async fn list_repositories(self, _context: tarpc::context::Context) -> Vec { + let mut repositories = vec![]; + let result = self .ctx .repository() @@ -156,9 +159,78 @@ impl Hub for Arc { .map(|r| RepositoryConfig::new_named(r.name, r.git_url)) .collect() }); - result.unwrap_or_else(|e| { + repositories.extend(result.unwrap_or_else(|e| { warn!("Failed to fetch repositories: {e}"); vec![] - }) + })); + + let provider_service = self.ctx.github_repository_provider(); + let repository_providers = provider_service + .list_github_repository_providers(None, None, Some(1024), None) + .await + .unwrap_or_else(|e| { + warn!("Failed to fetch GitHub repository providers: {e}"); + vec![] + }); + + for provider in repository_providers { + let Ok(access_token) = provider_service + .read_github_repository_provider_access_token(provider.id.clone()) + .await + else { + continue; + }; + + let repos = match provider_service + .list_github_provided_repositories_by_provider( + vec![provider.id.clone()], + None, + None, + Some(1024), + None, + ) + .await + { + Ok(repos) => repos, + Err(e) => { + warn!( + "Failed to retrieve repositories provided by {name}: {e}", + name = provider.display_name + ); + continue; + } + }; + repositories.extend(repos.into_iter().filter(|repo| repo.active).map(|repo| { + RepositoryConfig::new_named( + repo.name, + format_authenticated_git_url(repo.git_url, &access_token), + ) + })) + } + + repositories + } +} + +fn format_authenticated_git_url(mut git_url: String, access_token: &str) -> String { + let split_pos = git_url.find("://").map(|i| i + 3).unwrap_or(0); + git_url.insert_str(split_pos, &format!("{access_token}@")); + git_url +} + +#[cfg(test)] +mod tests { + use crate::hub::format_authenticated_git_url; + + #[test] + fn test_format_authenticated_git_url() { + assert_eq!( + format_authenticated_git_url("https://github.com/TabbyML/tabby".into(), "token".into()), + "https://token@github.com/TabbyML/tabby" + ); + assert_eq!( + format_authenticated_git_url("github.com/TabbyML/tabby".into(), "token".into()), + "token@github.com/TabbyML/tabby" + ); } } diff --git a/ee/tabby-webserver/src/schema/github_repository_provider.rs b/ee/tabby-webserver/src/schema/github_repository_provider.rs index 040c03e61221..a65dd3cb0098 100644 --- a/ee/tabby-webserver/src/schema/github_repository_provider.rs +++ b/ee/tabby-webserver/src/schema/github_repository_provider.rs @@ -37,6 +37,7 @@ pub struct GithubProvidedRepository { pub github_repository_provider_id: ID, pub name: String, pub git_url: String, + pub active: bool, } impl NodeType for GithubProvidedRepository { @@ -65,6 +66,7 @@ pub trait GithubRepositoryProviderService: Send + Sync { ) -> Result; async fn get_github_repository_provider(&self, id: ID) -> Result; async fn read_github_repository_provider_secret(&self, id: ID) -> Result; + async fn read_github_repository_provider_access_token(&self, id: ID) -> Result; async fn update_github_repository_provider_access_token( &self, id: ID, @@ -81,7 +83,7 @@ pub trait GithubRepositoryProviderService: Send + Sync { async fn list_github_provided_repositories_by_provider( &self, - provider: ID, + provider: Vec, after: Option, before: Option, first: Option, diff --git a/ee/tabby-webserver/src/schema/mod.rs b/ee/tabby-webserver/src/schema/mod.rs index f28cb475df67..d4316ff70fad 100644 --- a/ee/tabby-webserver/src/schema/mod.rs +++ b/ee/tabby-webserver/src/schema/mod.rs @@ -256,9 +256,9 @@ impl Query { .await } - async fn github_repositories_by_provider( + async fn github_repositories( ctx: &Context, - github_repository_provider_id: ID, + provider_ids: Vec, after: Option, before: Option, first: Option, @@ -275,7 +275,7 @@ impl Query { .locator .github_repository_provider() .list_github_provided_repositories_by_provider( - github_repository_provider_id, + provider_ids, after, before, first, diff --git a/ee/tabby-webserver/src/service/dao.rs b/ee/tabby-webserver/src/service/dao.rs index 91bdbb305084..08542cf39d2b 100644 --- a/ee/tabby-webserver/src/service/dao.rs +++ b/ee/tabby-webserver/src/service/dao.rs @@ -138,6 +138,7 @@ impl From for GithubProvidedRepository { name: value.name, git_url: value.git_url, vendor_id: value.vendor_id, + active: value.active, } } } diff --git a/ee/tabby-webserver/src/service/github_repository_provider.rs b/ee/tabby-webserver/src/service/github_repository_provider.rs index 122ffec727a2..1c9bf110b3b7 100644 --- a/ee/tabby-webserver/src/service/github_repository_provider.rs +++ b/ee/tabby-webserver/src/service/github_repository_provider.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use async_trait::async_trait; use juniper::ID; use tabby_db::DbConn; @@ -46,6 +47,14 @@ impl GithubRepositoryProviderService for GithubRepositoryProviderServiceImpl { Ok(provider.secret) } + async fn read_github_repository_provider_access_token(&self, id: ID) -> Result { + let provider = self.db.get_github_provider(id.as_rowid()?).await?; + let Some(access_token) = provider.access_token else { + return Err(anyhow!("Provider has no access token").into()); + }; + Ok(access_token) + } + async fn update_github_repository_provider_access_token( &self, id: ID, @@ -77,16 +86,20 @@ impl GithubRepositoryProviderService for GithubRepositoryProviderServiceImpl { async fn list_github_provided_repositories_by_provider( &self, - provider: ID, + providers: Vec, after: Option, before: Option, first: Option, last: Option, ) -> Result> { + let providers = providers + .into_iter() + .map(|i| i.as_rowid()) + .collect::, _>>()?; let (limit, skip_id, backwards) = graphql_pagination_to_filter(after, before, last, first)?; let repos = self .db - .list_github_provided_repositories(provider.as_rowid()?, limit, skip_id, backwards) + .list_github_provided_repositories(providers, limit, skip_id, backwards) .await?; Ok(repos