#![allow(dead_code)]
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub use backbone_support::application::service::support_events::LoggingSink;
use backbone_support::application::service::support_events::{SupportEvent, SupportEventSink};
use backbone_support::application::service::support_ports::{
ProjectAck, ProjectFromIssue, ProjectPort, SupportRejected,
};
use chrono::{DateTime, Utc};
use sqlx::PgPool;
use uuid::Uuid;
pub fn dburl() -> String {
std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://postgres:postgres@localhost:5433/backbone_support".into())
}
pub async fn pool() -> PgPool {
PgPool::connect(&dburl()).await.expect("connect")
}
pub fn dt(s: &str) -> DateTime<Utc> {
DateTime::parse_from_rfc3339(s).unwrap().with_timezone(&Utc)
}
pub async fn with_company_scope<R, F: std::future::Future<Output = R>>(
pool: &sqlx::PgPool,
company: Uuid,
f: F,
) -> R {
backbone_orm::org_scope::with_org_request_scope(
pool,
backbone_orm::org_scope::OrgScope::for_company_unit(company),
f,
)
.await
.expect("org request scope")
}
#[derive(Clone, Default)]
pub struct CapturingSink {
pub events: Arc<Mutex<Vec<SupportEvent>>>,
}
impl CapturingSink {
pub fn new() -> Self {
Self::default()
}
pub fn events(&self) -> Vec<SupportEvent> {
self.events.lock().unwrap().clone()
}
}
impl SupportEventSink for CapturingSink {
fn publish(&self, event: &SupportEvent) {
self.events.lock().unwrap().push(event.clone());
}
}
#[derive(Clone, Default)]
pub struct FakeProject {
pub opened: Arc<Mutex<HashMap<Uuid, Uuid>>>,
pub calls: Arc<Mutex<u32>>,
}
impl FakeProject {
pub fn new() -> Self {
Self::default()
}
pub fn open_count(&self) -> u32 {
*self.calls.lock().unwrap()
}
}
#[async_trait::async_trait]
impl ProjectPort for FakeProject {
async fn open_delivery_project(
&self,
req: &ProjectFromIssue,
) -> Result<ProjectAck, SupportRejected> {
*self.calls.lock().unwrap() += 1;
let mut m = self.opened.lock().unwrap();
let pid = *m.entry(req.issue_id).or_insert_with(Uuid::new_v4);
Ok(ProjectAck { project_id: pid })
}
}
pub struct RealProject {
pub svc: backbone_project::application::service::project_write_service::ProjectWriteService,
pub pool: PgPool,
}
#[async_trait::async_trait]
impl ProjectPort for RealProject {
async fn open_delivery_project(
&self,
req: &ProjectFromIssue,
) -> Result<ProjectAck, SupportRejected> {
use backbone_project::application::service::project_write_service::NewProject;
let name = format!("Support ISSUE-{} — {}", req.issue_id, req.subject);
let existing: Option<Uuid> = sqlx::query_scalar(
"SELECT id FROM project.projects WHERE project_name=$1 AND (metadata->>'deleted_at') IS NULL LIMIT 1")
.bind(&name).fetch_optional(&self.pool).await
.map_err(|e| SupportRejected { code: "project_lookup_failed".into(), message: e.to_string() })?;
if let Some(id) = existing {
return Ok(ProjectAck { project_id: id });
}
let res = self
.svc
.create_project(NewProject {
project_name: name,
project_type: "external".into(),
customer_id: Some(req.customer_id),
source_so_id: None,
currency: None,
})
.await;
res.map(|id| ProjectAck { project_id: id })
.map_err(|e| SupportRejected {
code: "project_rejected".into(),
message: format!("{e:?}"),
})
}
}