From e7e0e667964a368f913eb741f62047004be519b9 Mon Sep 17 00:00:00 2001 From: Javier Parada Date: Thu, 19 Dec 2024 12:05:10 +0100 Subject: [PATCH] add different errors --- .../src/breaches/infrastructure/sqlx/mod.rs | 3 +- .../sqlx/sqlx_postgres_breach_repository.rs | 144 ++++++++++++++++++ libs/cti/src/shared/domain/errors.rs | 17 +++ 3 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 libs/cti/src/breaches/infrastructure/sqlx/sqlx_postgres_breach_repository.rs diff --git a/libs/cti/src/breaches/infrastructure/sqlx/mod.rs b/libs/cti/src/breaches/infrastructure/sqlx/mod.rs index d0941d9..0d4be41 100644 --- a/libs/cti/src/breaches/infrastructure/sqlx/mod.rs +++ b/libs/cti/src/breaches/infrastructure/sqlx/mod.rs @@ -1,2 +1,3 @@ -pub mod sqlx_breach; \ No newline at end of file +pub mod sqlx_breach; +pub mod sqlx_postgres_breach_repository; \ No newline at end of file diff --git a/libs/cti/src/breaches/infrastructure/sqlx/sqlx_postgres_breach_repository.rs b/libs/cti/src/breaches/infrastructure/sqlx/sqlx_postgres_breach_repository.rs new file mode 100644 index 0000000..ca05f1f --- /dev/null +++ b/libs/cti/src/breaches/infrastructure/sqlx/sqlx_postgres_breach_repository.rs @@ -0,0 +1,144 @@ +use crate::{ + breaches::domain::{entities::{breach::Breach, breach_product::BreachProduct, breach_product_version::BreachProductVersion, breach_vendor::BreachVendor}, repositories::breach_repository::BreachRepository}, cves::domain::entities::cve_id::CveId, shared::domain::errors::DomainError +}; +use async_trait::async_trait; + +use tracing::error; + +use super::sqlx_breach::SqlxBreach; + +pub struct SqlxPostgresBreachRepository { + pool: sqlx::PgPool, +} + +impl SqlxPostgresBreachRepository { + pub fn new(pool: sqlx::PgPool) -> Self { + Self { pool } + } + + pub async fn from_env() -> Self { + let url_load_res = std::env::var("DATABASE_URL"); + if url_load_res.is_err() { + panic!("DATABASE_URL not found in environment variables."); + } + let url = url_load_res.unwrap(); + let pool_res = sqlx::PgPool::connect(&url).await; + if pool_res.is_err() { + panic!("Failed to connect to database: {:?}", pool_res.err()); + } + let pool = pool_res.unwrap(); + sqlx::query("SET search_path TO kernel") + .execute(&pool) + .await + .expect("Schema kernel not found."); + Self::new(pool) + } +} + +#[async_trait] +impl BreachRepository for SqlxPostgresBreachRepository { + async fn find_one( + &self, + cve_id: &CveId, + vendor: &BreachVendor, + product: &BreachProduct, + product_version: &BreachProductVersion, + ) -> Result { + let query = "SELECT * FROM cti.breaches WHERE id = $1"; + let query = sqlx::query_as(query).bind(id.value()); + let key_res: Result = query.fetch_one(&self.pool).await; + if key_res.is_err() { + return match key_res.err().unwrap() { + sqlx::Error::RowNotFound => Err(DomainError::CveNotFound { id: id.value() }), + err => { + error!("Error: {:?}", err); + Err(DomainError::Unknown) + } + }; + } + Ok(key_res.unwrap().to_domain()) + } + + async fn create_one(&self, cve: &Breach) -> Result<(), DomainError> { + let sql_cve: SqlxBreach = SqlxBreach::from_domain(cve); + let query = + "INSERT INTO cti.breaches (id, state, description, assigner_id, assigner_name, date_published, date_updated) VALUES ($1, $2, $3, $4, $5, $6, $7)"; + let res = sqlx::query(query) + .bind(&sql_cve.id) + .bind(&sql_cve.state) + .bind(&sql_cve.description) + .bind(&sql_cve.assigner_id) + .bind(&sql_cve.assigner_name) + .bind(&sql_cve.date_published) + .bind(&sql_cve.date_updated) + .fetch_optional(&self.pool) + .await; + if res.is_err() { + // TODO: check sql error code or message + return match res.err().unwrap() { + sqlx::Error::Database(_) => { + Err(DomainError::CveAlreadyExists { id: cve.id.value() }) + } + err => { + error!("Error: {:?}", err); + Err(DomainError::Unknown) + } + }; + } + Ok(()) + } + + async fn update_one(&self, cve: &Breach) -> Result<(), DomainError> { + let sql_cve: SqlxBreach = SqlxBreach::from_domain(cve); + let query = + "UPDATE cti.breaches SET state = $1, description = $2, assigner_id = $3, assigner_name = $4, date_published = $5, + date_updated = $6 WHERE id = $7"; + let res = sqlx::query(query) + .bind(&sql_cve.state) + .bind(&sql_cve.description) + .bind(&sql_cve.assigner_id) + .bind(&sql_cve.assigner_name) + .bind(&sql_cve.date_published) + .bind(&sql_cve.date_updated) + .bind(&sql_cve.id) + .fetch_optional(&self.pool) + .await; + + if res.is_err() { + // TODO: check sql error code or message + return match res.err().unwrap() { + sqlx::Error::RowNotFound => Err(DomainError::CveNotFound { id: cve.id.value() }), + err => { + error!("Error: {:?}", err); + Err(DomainError::Unknown) + } + }; + } + Ok(()) + } + + async fn delete_one( + &self, + cve_id: &CveId, + vendor: &BreachVendor, + product: &BreachProduct, + product_version: &BreachProductVersion, + ) -> Result<(), DomainError> { + let query = "DELETE FROM cti.breaches WHERE id = $1"; + let res = sqlx::query(query) + .bind(id.value()) + .fetch_optional(&self.pool) + .await; + if res.is_err() { + // TODO: check sql error code or message + return match res.err().unwrap() { + sqlx::Error::RowNotFound => Err(DomainError::CveNotFound { id: id.value() }), + err => { + error!("Error: {:?}", err); + Err(DomainError::Unknown) + } + }; + } + Ok(()) + } +} diff --git a/libs/cti/src/shared/domain/errors.rs b/libs/cti/src/shared/domain/errors.rs index 83670ee..1f39a7a 100644 --- a/libs/cti/src/shared/domain/errors.rs +++ b/libs/cti/src/shared/domain/errors.rs @@ -2,6 +2,7 @@ use thiserror::Error; #[derive(Error, Debug, Clone)] pub enum DomainError { + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - // GENERAL ERRORS // - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11,9 +12,11 @@ pub enum DomainError { #[error("Not valid format for value <{value:?}>.")] ValueObjectError { value: String }, + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - // CVE ERRORS // - - - - - - - - - - - - - - - - - - - - - - - - - - - - + #[error("Cve with id <{id:?}> already exists.")] CveAlreadyExists { id: String }, @@ -22,4 +25,18 @@ pub enum DomainError { #[error("Cve with id <{id:?}> not authorized.")] CveNotAuthorized { id: String }, + + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // BREACH ERRORS + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + #[error("Breach with <{cve_id:?}, {vendor:?}, {product:?}, {product_version:?}> already exists.")] + BreachAlreadyExists { cve_id: String, vendor: String, product: String, product_version: String }, + + #[error("Breach with id <{cve_id:?}, {vendor:?}, {product:?}, {product_version:?}> not found.")] + BreachNotFound { cve_id: String, vendor: String, product: String, product_version: String }, + + #[error("Breach with id <{cve_id:?}, {vendor:?}, {product:?}, {product_version:?}> not authorized.")] + BreachNotAuthorized { cve_id: String, vendor: String, product: String, product_version: String }, }