add repository trait
This commit is contained in:
parent
4e82dab1eb
commit
bc0e0b14d0
12 changed files with 169 additions and 17 deletions
15
packages/backend/native-utils/crates/database/Cargo.toml
Normal file
15
packages/backend/native-utils/crates/database/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "database"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["napi"]
|
||||||
|
napi = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
once_cell = "1.17.1"
|
||||||
|
sea-orm = { version = "0.11.3", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
||||||
|
thiserror = "1.0.40"
|
|
@ -0,0 +1,9 @@
|
||||||
|
use sea_orm::error::DbErr;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("The database connections have not been initialized yet")]
|
||||||
|
Uninitialized,
|
||||||
|
#[error("ORM error: {0}")]
|
||||||
|
OrmError(#[from] DbErr),
|
||||||
|
}
|
18
packages/backend/native-utils/crates/database/src/lib.rs
Normal file
18
packages/backend/native-utils/crates/database/src/lib.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use sea_orm::{Database, DatabaseConnection};
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
static DB_CONN: OnceCell<DatabaseConnection> = OnceCell::new();
|
||||||
|
|
||||||
|
pub async fn init_database(connection_uri: impl Into<String>) -> Result<(), Error> {
|
||||||
|
let conn = Database::connect(connection_uri.into()).await?;
|
||||||
|
DB_CONN.get_or_init(move || conn);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_database() -> Result<&'static DatabaseConnection, Error> {
|
||||||
|
DB_CONN.get().ok_or(Error::Uninitialized)
|
||||||
|
}
|
|
@ -6,12 +6,16 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-trait = "0.1.68"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
|
database = { path = "../database" }
|
||||||
jsonschema = "0.17.0"
|
jsonschema = "0.17.0"
|
||||||
once_cell = "1.17.1"
|
once_cell = "1.17.1"
|
||||||
|
parse-display = "0.8.0"
|
||||||
schemars = { version = "0.8.12", features = ["chrono"] }
|
schemars = { version = "0.8.12", features = ["chrono"] }
|
||||||
sea-orm = { version = "0.11.3", features = ["postgres-array", "sqlx-postgres", "runtime-tokio-rustls", "mock"] }
|
sea-orm = { version = "0.11.3", features = ["postgres-array", "sqlx-postgres", "runtime-tokio-rustls", "mock"] }
|
||||||
serde = { version = "1.0.163", features = ["derive"] }
|
serde = { version = "1.0.163", features = ["derive"] }
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
|
thiserror = "1.0.40"
|
||||||
tokio = { version = "1.28.1", features = ["sync"] }
|
tokio = { version = "1.28.1", features = ["sync"] }
|
||||||
utoipa = "3.3.0"
|
utoipa = "3.3.0"
|
||||||
|
|
9
packages/backend/native-utils/crates/model/src/error.rs
Normal file
9
packages/backend/native-utils/crates/model/src/error.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Failed to parse string")]
|
||||||
|
ParseError(#[from] parse_display::ParseError),
|
||||||
|
#[error("Failed to get database connection")]
|
||||||
|
DatabaseConnectionError(#[from] database::error::Error),
|
||||||
|
#[error("Database operation error: {0}")]
|
||||||
|
DatabaseOperationError(#[from] sea_orm::DbErr),
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod repository;
|
pub mod error;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod repository;
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::entity::{antenna, antenna_note, user_group_joining};
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::schema::{antenna::Antenna, json_to_keyword, json_to_string_list};
|
||||||
|
|
||||||
|
use super::Repository;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Repository<Antenna> for antenna::Model {
|
||||||
|
async fn pack(self) -> Result<Antenna, Error> {
|
||||||
|
let db = database::get_database()?;
|
||||||
|
let has_unread_note = antenna_note::Entity::find()
|
||||||
|
.filter(antenna_note::Column::AntennaId.eq(self.id.to_owned()))
|
||||||
|
.filter(antenna_note::Column::Read.eq(false))
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.is_some();
|
||||||
|
let user_group_joining = match self.user_group_joining_id {
|
||||||
|
None => None,
|
||||||
|
Some(id) => user_group_joining::Entity::find_by_id(id).one(db).await?,
|
||||||
|
};
|
||||||
|
let user_group_id = match user_group_joining {
|
||||||
|
None => None,
|
||||||
|
Some(m) => Some(m.user_group_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Antenna {
|
||||||
|
id: self.id,
|
||||||
|
created_at: self.created_at.into(),
|
||||||
|
name: self.name,
|
||||||
|
keywords: json_to_keyword(&self.keywords),
|
||||||
|
exclude_keywords: json_to_keyword(&self.exclude_keywords),
|
||||||
|
src: self.src.try_into()?,
|
||||||
|
user_list_id: self.user_list_id,
|
||||||
|
user_group_id,
|
||||||
|
users: self.users,
|
||||||
|
instances: json_to_string_list(&self.instances),
|
||||||
|
case_sensitive: self.case_sensitive,
|
||||||
|
notify: self.notify,
|
||||||
|
with_replies: self.with_replies,
|
||||||
|
with_file: self.with_file,
|
||||||
|
has_unread_note,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,11 @@
|
||||||
pub mod abuse_user_report;
|
pub mod antenna;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
trait Repository<T: JsonSchema> {
|
||||||
|
async fn pack(self) -> Result<T, Error>;
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
use jsonschema::JSONSchema;
|
use jsonschema::JSONSchema;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use parse_display::FromStr;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use super::Schema;
|
use super::{Keyword, Schema, StringList};
|
||||||
|
use crate::entity::sea_orm_active_enums::AntennaSrcEnum;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
#[derive(Debug, JsonSchema, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Antenna {
|
pub struct Antenna {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub keywords: Vec<Vec<String>>,
|
pub keywords: Keyword,
|
||||||
pub exclude_keywords: Vec<Vec<String>>,
|
pub exclude_keywords: Keyword,
|
||||||
#[schema(inline)]
|
#[schema(inline)]
|
||||||
pub src: AntennaSrcEnum,
|
pub src: AntennaSrc,
|
||||||
pub user_list_id: Option<String>,
|
pub user_list_id: Option<String>,
|
||||||
pub user_group_id: Option<String>,
|
pub user_group_id: Option<String>,
|
||||||
pub users: Vec<String>,
|
pub users: StringList,
|
||||||
pub instances: Vec<String>,
|
pub instances: StringList,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub case_sensitive: bool,
|
pub case_sensitive: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -32,9 +33,10 @@ pub struct Antenna {
|
||||||
pub has_unread_note: bool,
|
pub has_unread_note: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
#[derive(Debug, FromStr, JsonSchema, ToSchema)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum AntennaSrcEnum {
|
#[display(style = "lowercase")]
|
||||||
|
pub enum AntennaSrc {
|
||||||
Home,
|
Home,
|
||||||
All,
|
All,
|
||||||
Users,
|
Users,
|
||||||
|
@ -43,9 +45,18 @@ pub enum AntennaSrcEnum {
|
||||||
Instances,
|
Instances,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schema<Self> for Antenna {}
|
impl TryFrom<AntennaSrcEnum> for AntennaSrc {
|
||||||
|
type Error = parse_display::ParseError;
|
||||||
|
|
||||||
|
fn try_from(value: AntennaSrcEnum) -> Result<Self, Self::Error> {
|
||||||
|
value.to_string().parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- TODO: could be macro
|
||||||
|
impl Schema<Self> for Antenna {}
|
||||||
pub static VALIDATOR: Lazy<JSONSchema> = Lazy::new(|| Antenna::validator());
|
pub static VALIDATOR: Lazy<JSONSchema> = Lazy::new(|| Antenna::validator());
|
||||||
|
// ----
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use jsonschema::JSONSchema;
|
use jsonschema::JSONSchema;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use super::Schema;
|
use super::Schema;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
#[derive(Debug, JsonSchema, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
@ -20,7 +19,7 @@ pub struct App {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This represents `permissions` in `packages/calckey-js/src/consts.ts`.
|
/// This represents `permissions` in `packages/calckey-js/src/consts.ts`.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
#[derive(Debug, JsonSchema, ToSchema)]
|
||||||
pub enum Permission {
|
pub enum Permission {
|
||||||
#[serde(rename = "read:account")]
|
#[serde(rename = "read:account")]
|
||||||
ReadAccount,
|
ReadAccount,
|
||||||
|
|
|
@ -3,6 +3,10 @@ pub mod app;
|
||||||
|
|
||||||
use jsonschema::JSONSchema;
|
use jsonschema::JSONSchema;
|
||||||
use schemars::{schema_for, JsonSchema};
|
use schemars::{schema_for, JsonSchema};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
type Keyword = Vec<Vec<String>>;
|
||||||
|
type StringList = Vec<String>;
|
||||||
|
|
||||||
/// Structs of schema defitions implement this trait in order to
|
/// Structs of schema defitions implement this trait in order to
|
||||||
/// provide the JSON Schema validator [`jsonschema::JSONSchema`].
|
/// provide the JSON Schema validator [`jsonschema::JSONSchema`].
|
||||||
|
@ -19,3 +23,29 @@ trait Schema<T: JsonSchema> {
|
||||||
.expect("Unable to compile schema")
|
.expect("Unable to compile schema")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn json_to_keyword(value: &Value) -> Keyword {
|
||||||
|
match value.as_array() {
|
||||||
|
None => vec![vec![]],
|
||||||
|
Some(or_vec) => or_vec
|
||||||
|
.iter()
|
||||||
|
.map(|and_val| match and_val.as_array() {
|
||||||
|
None => vec![],
|
||||||
|
Some(and_vec) => and_vec
|
||||||
|
.iter()
|
||||||
|
.map(|word| word.as_str().unwrap_or_default().to_string())
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn json_to_string_list(value: &Value) -> StringList {
|
||||||
|
match value.as_array() {
|
||||||
|
None => vec![],
|
||||||
|
Some(v) => v
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_str().unwrap_or_default().to_string())
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue