Skip to content

Commit

Permalink
draft: begin implementing integration / third party repository service
Browse files Browse the repository at this point in the history
  • Loading branch information
boxbeam committed May 10, 2024
1 parent 09c2940 commit 3115e40
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ee/tabby-db/migrations/0029_merged-provider-tables.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ CREATE TABLE integration_access_tokens(
CREATE TABLE provided_repositories(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
integration_access_token_id INTEGER NOT NULL,
kind TEXT NOT NULL,
vendor_id TEXT NOT NULL,
name TEXT NOT NULL,
git_url TEXT NOT NULL,
Expand Down
Binary file modified ee/tabby-db/schema.sqlite
Binary file not shown.
1 change: 1 addition & 0 deletions ee/tabby-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use gitlab_repository_provider::{GitlabProvidedRepositoryDAO, GitlabReposito
pub use invitations::InvitationDAO;
pub use job_runs::JobRunDAO;
pub use oauth_credential::OAuthCredentialDAO;
pub use provided_repositories::ProvidedRepositoryDAO;
pub use repositories::RepositoryDAO;
pub use server_setting::ServerSettingDAO;
use sqlx::{
Expand Down
4 changes: 3 additions & 1 deletion ee/tabby-db/src/provided_repositories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{DateTimeUtc, DbConn};
#[derive(FromRow)]
pub struct ProvidedRepositoryDAO {
pub id: i64,
pub kind: String,
pub vendor_id: String,
pub integration_access_token_id: i64,
pub name: String,
Expand Down Expand Up @@ -51,7 +52,7 @@ impl DbConn {
pub async fn get_provided_repository(&self, id: i64) -> Result<ProvidedRepositoryDAO> {
let repo = query_as!(
ProvidedRepositoryDAO,
"SELECT id, vendor_id, name, git_url, active, integration_access_token_id, created_at, updated_at FROM provided_repositories WHERE id = ?",
"SELECT id, vendor_id, kind, name, git_url, active, integration_access_token_id, created_at, updated_at FROM provided_repositories WHERE id = ?",
id
)
.fetch_one(&self.pool)
Expand Down Expand Up @@ -95,6 +96,7 @@ impl DbConn {
"vendor_id",
"name",
"git_url",
"kind",
"active",
"integration_access_token_id",
"created_at" as "created_at: DateTimeUtc",
Expand Down
60 changes: 49 additions & 11 deletions ee/tabby-schema/src/dao.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ use lazy_static::lazy_static;
use tabby_db::{
EmailSettingDAO, GithubProvidedRepositoryDAO, GithubRepositoryProviderDAO,
GitlabProvidedRepositoryDAO, GitlabRepositoryProviderDAO, InvitationDAO, JobRunDAO,
OAuthCredentialDAO, RepositoryDAO, ServerSettingDAO, UserDAO, UserEventDAO,
OAuthCredentialDAO, ProvidedRepositoryDAO, RepositoryDAO, ServerSettingDAO, UserDAO,
UserEventDAO,
};

use crate::schema::{
auth::{self, OAuthCredential, OAuthProvider},
email::{AuthMethod, EmailSetting, Encryption},
job,
repository::{
GitRepository, GithubProvidedRepository, GithubRepositoryProvider,
GitlabProvidedRepository, GitlabRepositoryProvider, RepositoryProviderStatus,
use crate::{
integration::IntegrationKind,
repository::ProvidedRepository,
schema::{
auth::{self, OAuthCredential, OAuthProvider},
email::{AuthMethod, EmailSetting, Encryption},
job,
repository::{
GitRepository, GithubProvidedRepository, GithubRepositoryProvider,
GitlabProvidedRepository, GitlabRepositoryProvider, RepositoryProviderStatus,
},
setting::{NetworkSetting, SecuritySetting},
user_event::{EventKind, UserEvent},
CoreError,
},
setting::{NetworkSetting, SecuritySetting},
user_event::{EventKind, UserEvent},
CoreError,
};

impl From<InvitationDAO> for auth::Invitation {
Expand Down Expand Up @@ -124,6 +129,22 @@ impl From<ServerSettingDAO> for NetworkSetting {
}
}

impl TryFrom<ProvidedRepositoryDAO> for ProvidedRepository {
type Error = anyhow::Error;
fn try_from(value: ProvidedRepositoryDAO) -> Result<Self, Self::Error> {
Ok(Self {
integration_id: value.integration_access_token_id.as_id(),
active: value.active,
kind: IntegrationKind::from_enum_str(&value.kind)?,
display_name: value.name,
git_url: value.git_url,
vendor_id: value.vendor_id,
created_at: *value.created_at,
updated_at: *value.updated_at,
})
}
}

impl From<GithubRepositoryProviderDAO> for GithubRepositoryProvider {
fn from(value: GithubRepositoryProviderDAO) -> Self {
Self {
Expand Down Expand Up @@ -254,6 +275,23 @@ impl DbEnum for EventKind {
}
}

impl DbEnum for IntegrationKind {
fn as_enum_str(&self) -> &'static str {
match self {
IntegrationKind::Github => "github",
IntegrationKind::Gitlab => "gitlab",
}
}

fn from_enum_str(s: &str) -> anyhow::Result<Self> {
match s {
"github" => Ok(IntegrationKind::Github),
"gitlab" => Ok(IntegrationKind::Gitlab),
_ => bail!("{s} is not a valid value for ProviderKind"),
}
}
}

impl DbEnum for Encryption {
fn as_enum_str(&self) -> &'static str {
match self {
Expand Down
52 changes: 52 additions & 0 deletions ee/tabby-schema/src/schema/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use juniper::ID;
use strum::EnumIter;

use crate::Result;

#[derive(Clone, EnumIter)]
pub enum IntegrationKind {
Github,
Gitlab,
}

pub enum IntegrationStatus {
Ready,
Pending,
Failed,
}

pub struct IntegrationAccessToken {
pub id: ID,
pub kind: IntegrationKind,
pub display_name: String,
pub access_token: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub status: IntegrationStatus,
}

#[async_trait]
pub trait IntegrationService: Send + Sync {
async fn create_integration(
&self,
kind: IntegrationKind,
display_name: String,
access_token: String,
) -> Result<ID>;

async fn delete_integration(&self, id: ID) -> Result<()>;
async fn update_integration(&self, id: ID, display_name: String, access_token: Option<String>);
async fn list_integrations(
&self,
ids: Option<Vec<ID>>,
kind: Option<IntegrationKind>,
after: Option<String>,
before: Option<String>,
first: Option<usize>,
last: Option<usize>,
) -> Result<Vec<IntegrationAccessToken>>;

async fn sync_resources(&self, id: ID) -> Result<()>;
}
1 change: 1 addition & 0 deletions ee/tabby-schema/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod analytic;
pub mod auth;
pub mod constants;
pub mod email;
pub mod integration;
pub mod job;
pub mod license;
pub mod repository;
Expand Down
3 changes: 3 additions & 0 deletions ee/tabby-schema/src/schema/repository/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub use github::{GithubProvidedRepository, GithubRepositoryProvider, GithubRepos
mod gitlab;
use std::{path::PathBuf, sync::Arc};

mod third_party;
pub use third_party::{ProvidedRepository, ThirdPartyRepositoryService};

use async_trait::async_trait;
pub use gitlab::{GitlabProvidedRepository, GitlabRepositoryProvider, GitlabRepositoryService};
use juniper::{GraphQLEnum, GraphQLObject, ID};
Expand Down
32 changes: 32 additions & 0 deletions ee/tabby-schema/src/schema/repository/third_party.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{integration::IntegrationKind, schema::Result};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use juniper::ID;

pub struct ProvidedRepository {
pub integration_id: ID,
pub active: bool,
pub kind: IntegrationKind,
pub display_name: String,
pub git_url: String,
pub vendor_id: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

#[async_trait]
pub trait ThirdPartyRepositoryService: Send + Sync {
async fn list_repositories(
&self,
integration_ids: Option<Vec<ID>>,
kind: Option<IntegrationKind>,
active: Option<bool>,
after: Option<String>,
before: Option<String>,
first: Option<usize>,
last: Option<usize>,
) -> Result<Vec<ProvidedRepository>>;

async fn update_repository_active(&self, id: ID, active: bool) -> Result<()>;
async fn list_active_git_urls(&self) -> Result<Vec<String>>;
}
1 change: 1 addition & 0 deletions ee/tabby-webserver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ gitlab = "0.1610.0"
apalis = { git = "https://github.com/wsxiaoys/apalis", rev = "91526e8", features = ["sqlite", "cron" ] }
uuid.workspace = true
hyper-util = { version = "0.1.3", features = ["client-legacy"] }
strum.workspace = true

[dev-dependencies]
assert_matches = "1.5.0"
Expand Down
1 change: 1 addition & 0 deletions ee/tabby-webserver/src/service/repository/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod git;
mod github;
mod gitlab;
mod third_party;

use std::sync::Arc;

Expand Down
78 changes: 78 additions & 0 deletions ee/tabby-webserver/src/service/repository/third_party.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use juniper::ID;
use std::marker::PhantomData;
use strum::IntoEnumIterator;
use tabby_schema::{
integration::IntegrationKind, repository::ProvidedRepository, AsRowid, DbEnum, Result,
};
use url::Url;

use async_trait::async_trait;
use tabby_db::DbConn;
use tabby_schema::repository::{RepositoryProvider, ThirdPartyRepositoryService};

use crate::service::graphql_pagination_to_filter;

struct ThirdPartyRepositoryServiceImpl {
db: DbConn,
}

#[async_trait]
impl ThirdPartyRepositoryService for ThirdPartyRepositoryServiceImpl {
async fn list_repositories(
&self,
integration_ids: Option<Vec<ID>>,
kind: Option<IntegrationKind>,
active: Option<bool>,
after: Option<String>,
before: Option<String>,
first: Option<usize>,
last: Option<usize>,
) -> Result<Vec<ProvidedRepository>> {
let (limit, skip_id, backwards) = graphql_pagination_to_filter(after, before, first, last)?;

let integration_ids = integration_ids
.into_iter()
.flatten()
.map(|id| id.as_rowid())
.collect::<Result<Vec<_>, _>>()?;

let kind = kind.map(|kind| kind.as_enum_str().to_string());

Ok(self
.db
.list_provided_repositories(integration_ids, kind, active, limit, skip_id, backwards)
.await?
.into_iter()
.map(ProvidedRepository::try_from)
.collect::<Result<_, _>>()?)
}

async fn update_repository_active(&self, id: ID, active: bool) -> Result<()> {
self.db
.update_provided_repository_active(id.as_rowid()?, active)
.await?;
Ok(())
}

async fn list_active_git_urls(&self) -> Result<Vec<String>> {
let mut urls = vec![];
}
}

fn format_authenticated_url(
kind: &IntegrationKind,
git_url: &str,
access_token: &str,
) -> Result<String> {
let mut url = Url::parse(git_url).map_err(anyhow::Error::from)?;
match kind {
IntegrationKind::Github => {
let _ = url.set_username(access_token);
}
IntegrationKind::Gitlab => {
let _ = url.set_username("oauth2");
let _ = url.set_password(Some(access_token));
}
}
Ok(url.to_string())
}

0 comments on commit 3115e40

Please sign in to comment.