diff --git a/ee/tabby-ui/app/(dashboard)/settings/(integrations)/repository/github/detail/components/detail.tsx b/ee/tabby-ui/app/(dashboard)/settings/(integrations)/repository/github/detail/components/detail.tsx
index 5427f9251f5f..4e5f50cbf409 100644
--- a/ee/tabby-ui/app/(dashboard)/settings/(integrations)/repository/github/detail/components/detail.tsx
+++ b/ee/tabby-ui/app/(dashboard)/settings/(integrations)/repository/github/detail/components/detail.tsx
@@ -5,6 +5,7 @@ import { useRouter, useSearchParams } from 'next/navigation'
import { useQuery } from 'urql'
import { DEFAULT_PAGE_SIZE } from '@/lib/constants'
+import { RepositoryProviderStatus } from '@/lib/gql/generates/graphql'
import { QueryResponseData, QueryVariables, useMutation } from '@/lib/tabby/gql'
import {
listGithubRepositories,
@@ -68,7 +69,7 @@ const DetailPage: React.FC = () => {
return (
-
+
- {provider?.connected ? (
- Connected
- ) : (
- Not Connected
- )}
+ {provider && toStatusBadge(provider.status)}
@@ -108,6 +105,17 @@ const DetailPage: React.FC = () => {
)
}
+function toStatusBadge(status: RepositoryProviderStatus) {
+ switch (status) {
+ case RepositoryProviderStatus.Ready:
+ return Ready
+ case RepositoryProviderStatus.Error:
+ return Error
+ case RepositoryProviderStatus.Error:
+ return Pending
+ }
+}
+
const LinkedRepoTable: React.FC<{
data: GithubRepositories | undefined
onDelete?: () => void
diff --git a/ee/tabby-ui/lib/tabby/query.ts b/ee/tabby-ui/lib/tabby/query.ts
index 3b630e042025..dfb099da152b 100644
--- a/ee/tabby-ui/lib/tabby/query.ts
+++ b/ee/tabby-ui/lib/tabby/query.ts
@@ -186,7 +186,7 @@ export const listGithubRepositoryProviders = graphql(/* GraphQL */ `
node {
id
displayName
- connected
+ status
}
cursor
}
diff --git a/ee/tabby-webserver/graphql/schema.graphql b/ee/tabby-webserver/graphql/schema.graphql
index 0f940e9425bd..f386e0cece92 100644
--- a/ee/tabby-webserver/graphql/schema.graphql
+++ b/ee/tabby-webserver/graphql/schema.graphql
@@ -13,7 +13,7 @@ input UpdateRepositoryProviderInput {
type GitlabRepositoryProvider {
id: ID!
displayName: String!
- connected: Boolean!
+ status: RepositoryProviderStatus!
}
enum Language {
@@ -119,6 +119,12 @@ type UserConnection {
pageInfo: PageInfo!
}
+enum RepositoryProviderStatus {
+ READY
+ PENDING
+ ERROR
+}
+
type DiskUsageStats {
events: DiskUsage!
indexedRepositories: DiskUsage!
@@ -142,7 +148,7 @@ type ServerInfo {
type GithubRepositoryProvider {
id: ID!
displayName: String!
- connected: Boolean!
+ status: RepositoryProviderStatus!
}
input PasswordChangeInput {
diff --git a/ee/tabby-webserver/src/cron/github.rs b/ee/tabby-webserver/src/cron/github.rs
index 4d49a3a1e3fa..bef73e9f4b3b 100644
--- a/ee/tabby-webserver/src/cron/github.rs
+++ b/ee/tabby-webserver/src/cron/github.rs
@@ -38,7 +38,7 @@ async fn refresh_repositories_for_provider(
..
}) if source.status_code.is_client_error() => {
service
- .reset_github_repository_provider_access_token(provider.id.clone())
+ .update_github_repository_provider_sync_status(provider.id.clone(), false)
.await?;
warn!(
"GitHub credentials for provider {} are expired or invalid",
@@ -66,6 +66,9 @@ async fn refresh_repositories_for_provider(
.upsert_github_provided_repository(provider.id.clone(), id, repo.name, url)
.await?;
}
+ service
+ .update_github_repository_provider_sync_status(provider.id.clone(), true)
+ .await?;
Ok(())
}
diff --git a/ee/tabby-webserver/src/cron/gitlab.rs b/ee/tabby-webserver/src/cron/gitlab.rs
index 1639c673d14b..6c7e27ec28fd 100644
--- a/ee/tabby-webserver/src/cron/gitlab.rs
+++ b/ee/tabby-webserver/src/cron/gitlab.rs
@@ -39,7 +39,7 @@ async fn refresh_repositories_for_provider(
Ok(repos) => repos,
Err(e) if e.to_string().contains("401 Unauthorized") => {
service
- .reset_gitlab_repository_provider_access_token(provider.id.clone())
+ .update_gitlab_repository_provider_sync_status(provider.id.clone(), false)
.await?;
warn!(
"GitLab credentials for provider {} are expired or invalid",
@@ -64,6 +64,9 @@ async fn refresh_repositories_for_provider(
)
.await?;
}
+ service
+ .update_gitlab_repository_provider_sync_status(provider.id.clone(), true)
+ .await?;
Ok(())
}
diff --git a/ee/tabby-webserver/src/schema/github_repository_provider.rs b/ee/tabby-webserver/src/schema/github_repository_provider.rs
index fdf4ebaf319e..3412729f2b6e 100644
--- a/ee/tabby-webserver/src/schema/github_repository_provider.rs
+++ b/ee/tabby-webserver/src/schema/github_repository_provider.rs
@@ -2,7 +2,7 @@ use async_trait::async_trait;
use chrono::{DateTime, Utc};
use juniper::{GraphQLObject, ID};
-use super::{repository::RepositoryProvider, Context};
+use super::{repository::RepositoryProvider, types::RepositoryProviderStatus, Context};
use crate::{juniper::relay::NodeType, schema::Result};
#[derive(GraphQLObject, Debug, PartialEq)]
@@ -11,7 +11,7 @@ pub struct GithubRepositoryProvider {
pub id: ID,
pub display_name: String,
- pub connected: bool,
+ pub status: RepositoryProviderStatus,
#[graphql(skip)]
pub access_token: Option,
@@ -75,7 +75,11 @@ pub trait GithubRepositoryProviderService: Send + Sync + RepositoryProvider {
display_name: String,
access_token: String,
) -> Result<()>;
- async fn reset_github_repository_provider_access_token(&self, id: ID) -> Result<()>;
+ async fn update_github_repository_provider_sync_status(
+ &self,
+ id: ID,
+ success: bool,
+ ) -> Result<()>;
async fn list_github_repository_providers(
&self,
diff --git a/ee/tabby-webserver/src/schema/gitlab_repository_provider.rs b/ee/tabby-webserver/src/schema/gitlab_repository_provider.rs
index 71316f44aa6f..b446ddbe80ca 100644
--- a/ee/tabby-webserver/src/schema/gitlab_repository_provider.rs
+++ b/ee/tabby-webserver/src/schema/gitlab_repository_provider.rs
@@ -2,7 +2,7 @@ use async_trait::async_trait;
use chrono::{DateTime, Utc};
use juniper::{GraphQLObject, ID};
-use super::{repository::RepositoryProvider, Context};
+use super::{repository::RepositoryProvider, types::RepositoryProviderStatus, Context};
use crate::{juniper::relay::NodeType, schema::Result};
#[derive(GraphQLObject, Debug, PartialEq)]
@@ -11,7 +11,7 @@ pub struct GitlabRepositoryProvider {
pub id: ID,
pub display_name: String,
- pub connected: bool,
+ pub status: RepositoryProviderStatus,
#[graphql(skip)]
pub access_token: Option,
@@ -75,7 +75,11 @@ pub trait GitlabRepositoryProviderService: Send + Sync + RepositoryProvider {
display_name: String,
access_token: String,
) -> Result<()>;
- async fn reset_gitlab_repository_provider_access_token(&self, id: ID) -> Result<()>;
+ async fn update_gitlab_repository_provider_sync_status(
+ &self,
+ id: ID,
+ success: bool,
+ ) -> Result<()>;
async fn list_gitlab_repository_providers(
&self,
diff --git a/ee/tabby-webserver/src/schema/types.rs b/ee/tabby-webserver/src/schema/types.rs
index 06ff4ceed7c7..caff9449b590 100644
--- a/ee/tabby-webserver/src/schema/types.rs
+++ b/ee/tabby-webserver/src/schema/types.rs
@@ -1,4 +1,4 @@
-use juniper::{GraphQLInputObject, ID};
+use juniper::{GraphQLEnum, GraphQLInputObject, ID};
use validator::Validate;
#[derive(GraphQLInputObject, Validate)]
@@ -23,3 +23,20 @@ pub struct UpdateRepositoryProviderInput {
#[validate(length(code = "access_token", min = 10))]
pub access_token: String,
}
+
+#[derive(GraphQLEnum, Debug, PartialEq)]
+pub enum RepositoryProviderStatus {
+ Ready,
+ Pending,
+ Error,
+}
+
+impl RepositoryProviderStatus {
+ pub fn new(access_token_set: bool, synced_at_set: bool) -> Self {
+ match (access_token_set, synced_at_set) {
+ (true, true) => RepositoryProviderStatus::Ready,
+ (true, false) => RepositoryProviderStatus::Pending,
+ _ => RepositoryProviderStatus::Error,
+ }
+ }
+}
diff --git a/ee/tabby-webserver/src/service/dao.rs b/ee/tabby-webserver/src/service/dao.rs
index 6d8b0dca8765..620abd705e57 100644
--- a/ee/tabby-webserver/src/service/dao.rs
+++ b/ee/tabby-webserver/src/service/dao.rs
@@ -16,6 +16,7 @@ use crate::{
gitlab_repository_provider::{GitlabProvidedRepository, GitlabRepositoryProvider},
job,
setting::{NetworkSetting, SecuritySetting},
+ types::RepositoryProviderStatus,
user_event::{EventKind, UserEvent},
CoreError,
},
@@ -130,7 +131,10 @@ impl From for GithubRepositoryProvider {
Self {
display_name: value.display_name,
id: value.id.as_id(),
- connected: value.access_token.is_some(),
+ status: RepositoryProviderStatus::new(
+ value.access_token.is_some(),
+ value.synced_at.is_some(),
+ ),
access_token: value.access_token,
}
}
@@ -154,7 +158,10 @@ impl From for GitlabRepositoryProvider {
Self {
display_name: value.display_name,
id: value.id.as_id(),
- connected: value.access_token.is_some(),
+ status: RepositoryProviderStatus::new(
+ value.access_token.is_some(),
+ value.synced_at.is_some(),
+ ),
access_token: value.access_token,
}
}
diff --git a/ee/tabby-webserver/src/service/github_repository_provider.rs b/ee/tabby-webserver/src/service/github_repository_provider.rs
index fb78553415fb..b5d764929599 100644
--- a/ee/tabby-webserver/src/service/github_repository_provider.rs
+++ b/ee/tabby-webserver/src/service/github_repository_provider.rs
@@ -50,13 +50,6 @@ impl GithubRepositoryProviderService for GithubRepositoryProviderServiceImpl {
Ok(())
}
- async fn reset_github_repository_provider_access_token(&self, id: ID) -> Result<()> {
- self.db
- .reset_github_provider_access_token(id.as_rowid()?)
- .await?;
- Ok(())
- }
-
async fn list_github_repository_providers(
&self,
ids: Vec,
@@ -183,6 +176,17 @@ impl GithubRepositoryProviderService for GithubRepositoryProviderServiceImpl {
.await?;
Ok(())
}
+
+ async fn update_github_repository_provider_sync_status(
+ &self,
+ id: ID,
+ success: bool,
+ ) -> Result<()> {
+ self.db
+ .update_github_provider_sync_status(id.as_rowid()?, success)
+ .await?;
+ Ok(())
+ }
}
#[async_trait]
@@ -217,7 +221,7 @@ mod tests {
use chrono::Duration;
use super::*;
- use crate::service::AsID;
+ use crate::{schema::types::RepositoryProviderStatus, service::AsID};
#[tokio::test]
async fn test_github_provided_repositories() {
@@ -318,7 +322,7 @@ mod tests {
id: id.clone(),
display_name: "id".into(),
access_token: Some("secret".into()),
- connected: true,
+ status: RepositoryProviderStatus::Pending,
}
);
@@ -330,21 +334,6 @@ mod tests {
assert_eq!(providers.len(), 1);
assert_eq!(providers[0].access_token, Some("secret".into()));
- // Test resetgithub provider tokens
- service
- .reset_github_repository_provider_access_token(id.clone())
- .await
- .unwrap();
-
- assert_eq!(
- service
- .get_github_repository_provider(id.clone())
- .await
- .unwrap()
- .access_token,
- None
- );
-
// Test deleting github provider
service
.delete_github_repository_provider(id.clone())
@@ -392,6 +381,37 @@ mod tests {
);
}
+ #[tokio::test]
+ async fn test_sync_status() {
+ let db = DbConn::new_in_memory().await.unwrap();
+ let service = create(db.clone());
+
+ let provider_id = db
+ .create_github_provider("provider1".into(), "token".into())
+ .await
+ .unwrap();
+
+ service
+ .update_github_repository_provider_sync_status(provider_id.as_id(), true)
+ .await
+ .unwrap();
+
+ let provider = db.get_github_provider(provider_id).await.unwrap();
+
+ assert!(provider.access_token.is_some());
+ assert!(provider.synced_at.is_some());
+
+ service
+ .update_github_repository_provider_sync_status(provider_id.as_id(), false)
+ .await
+ .unwrap();
+
+ let provider = db.get_github_provider(provider_id).await.unwrap();
+
+ assert!(provider.access_token.is_none());
+ assert!(provider.synced_at.is_none());
+ }
+
#[tokio::test]
async fn test_delete_outdated_repos() {
let db = DbConn::new_in_memory().await.unwrap();
diff --git a/ee/tabby-webserver/src/service/gitlab_repository_provider.rs b/ee/tabby-webserver/src/service/gitlab_repository_provider.rs
index 5c4d328d75cd..a948f9377ee0 100644
--- a/ee/tabby-webserver/src/service/gitlab_repository_provider.rs
+++ b/ee/tabby-webserver/src/service/gitlab_repository_provider.rs
@@ -50,13 +50,6 @@ impl GitlabRepositoryProviderService for GitlabRepositoryProviderServiceImpl {
Ok(())
}
- async fn reset_gitlab_repository_provider_access_token(&self, id: ID) -> Result<()> {
- self.db
- .reset_gitlab_provider_access_token(id.as_rowid()?)
- .await?;
- Ok(())
- }
-
async fn list_gitlab_repository_providers(
&self,
ids: Vec,
@@ -186,6 +179,17 @@ impl GitlabRepositoryProviderService for GitlabRepositoryProviderServiceImpl {
.await?;
Ok(())
}
+
+ async fn update_gitlab_repository_provider_sync_status(
+ &self,
+ id: ID,
+ success: bool,
+ ) -> Result<()> {
+ self.db
+ .update_gitlab_provider_sync_status(id.as_rowid()?, success)
+ .await?;
+ Ok(())
+ }
}
fn deduplicate_gitlab_repositories(repositories: &mut Vec) {
@@ -220,7 +224,7 @@ mod tests {
use chrono::Duration;
use super::*;
- use crate::service::AsID;
+ use crate::{schema::types::RepositoryProviderStatus, service::AsID};
#[tokio::test]
async fn test_gitlab_provided_repositories() {
@@ -321,7 +325,7 @@ mod tests {
id: id.clone(),
display_name: "id".into(),
access_token: Some("secret".into()),
- connected: true,
+ status: RepositoryProviderStatus::Pending
}
);
@@ -333,21 +337,6 @@ mod tests {
assert_eq!(providers.len(), 1);
assert_eq!(providers[0].access_token, Some("secret".into()));
- // Test resetgitlab provider tokens
- service
- .reset_gitlab_repository_provider_access_token(id.clone())
- .await
- .unwrap();
-
- assert_eq!(
- service
- .get_gitlab_repository_provider(id.clone())
- .await
- .unwrap()
- .access_token,
- None
- );
-
// Test deleting gitlab provider
service
.delete_gitlab_repository_provider(id.clone())
@@ -364,6 +353,37 @@ mod tests {
);
}
+ #[tokio::test]
+ async fn test_sync_status() {
+ let db = DbConn::new_in_memory().await.unwrap();
+ let service = create(db.clone());
+
+ let provider_id = db
+ .create_gitlab_provider("provider1".into(), "token".into())
+ .await
+ .unwrap();
+
+ service
+ .update_gitlab_repository_provider_sync_status(provider_id.as_id(), true)
+ .await
+ .unwrap();
+
+ let provider = db.get_gitlab_provider(provider_id).await.unwrap();
+
+ assert!(provider.access_token.is_some());
+ assert!(provider.synced_at.is_some());
+
+ service
+ .update_gitlab_repository_provider_sync_status(provider_id.as_id(), false)
+ .await
+ .unwrap();
+
+ let provider = db.get_gitlab_provider(provider_id).await.unwrap();
+
+ assert!(provider.access_token.is_none());
+ assert!(provider.synced_at.is_none());
+ }
+
#[tokio::test]
async fn test_provided_git_urls() {
let db = DbConn::new_in_memory().await.unwrap();