Skip to main content

zino_model/source/
mod.rs

1//! The `source` model and related services.
2
3use serde::{Deserialize, Serialize};
4use zino_core::{
5    Map, Uuid,
6    datetime::DateTime,
7    error::Error,
8    extension::JsonObjectExt,
9    model::{Model, ModelHooks},
10    validation::Validation,
11};
12use zino_derive::{DecodeRow, Entity, ModelAccessor, Schema};
13
14#[cfg(feature = "tags")]
15use crate::tag::Tag;
16
17#[cfg(any(feature = "owner-id", feature = "maintainer-id"))]
18use crate::user::User;
19
20#[cfg(feature = "maintainer-id")]
21use zino_auth::UserSession;
22
23/// The `source` model.
24#[derive(
25    Debug, Clone, Default, Serialize, Deserialize, DecodeRow, Entity, Schema, ModelAccessor,
26)]
27#[serde(default)]
28#[schema(auto_rename)]
29pub struct Source {
30    // Basic fields.
31    #[schema(read_only)]
32    id: Uuid,
33    #[schema(not_null)]
34    name: String,
35    #[cfg(feature = "namespace")]
36    #[schema(default_value = "Source::model_namespace", index_type = "hash")]
37    namespace: String,
38    #[cfg(feature = "visibility")]
39    #[schema(default_value = "Internal")]
40    visibility: String,
41    #[schema(default_value = "Active", index_type = "hash")]
42    status: String,
43    description: String,
44
45    // Info fields.
46    #[cfg(feature = "tags")]
47    #[schema(reference = "Tag", index_type = "gin")]
48    tags: Vec<Uuid>, // tag.id, tag.namespace = "*:source"
49
50    // Extensions.
51    extra: Map,
52
53    // Revisions.
54    #[cfg(feature = "owner-id")]
55    #[schema(reference = "User")]
56    owner_id: Option<Uuid>, // user.id
57    #[cfg(feature = "maintainer-id")]
58    #[schema(reference = "User")]
59    maintainer_id: Option<Uuid>, // user.id
60    #[schema(read_only, default_value = "now", index_type = "btree")]
61    created_at: DateTime,
62    #[schema(default_value = "now", index_type = "btree")]
63    updated_at: DateTime,
64    version: u64,
65    #[cfg(feature = "edition")]
66    edition: u32,
67}
68
69impl Model for Source {
70    const MODEL_NAME: &'static str = "source";
71
72    #[inline]
73    fn new() -> Self {
74        Self {
75            id: Uuid::now_v7(),
76            ..Self::default()
77        }
78    }
79
80    fn read_map(&mut self, data: &Map) -> Validation {
81        let mut validation = Validation::new();
82        if let Some(result) = data.parse_uuid("id") {
83            match result {
84                Ok(id) => self.id = id,
85                Err(err) => validation.record_fail("id", err),
86            }
87        }
88        if let Some(name) = data.parse_string("name") {
89            self.name = name.into_owned();
90        }
91        if let Some(description) = data.parse_string("description") {
92            self.description = description.into_owned();
93        }
94        #[cfg(feature = "tags")]
95        if let Some(result) = data.parse_array("tags") {
96            match result {
97                Ok(tags) => self.tags = tags,
98                Err(err) => validation.record_fail("tags", err),
99            }
100        }
101        #[cfg(feature = "owner-id")]
102        if let Some(result) = data.parse_uuid("owner_id") {
103            match result {
104                Ok(owner_id) => self.owner_id = Some(owner_id),
105                Err(err) => validation.record_fail("owner_id", err),
106            }
107        }
108        #[cfg(feature = "maintainer-id")]
109        if let Some(result) = data.parse_uuid("maintainer_id") {
110            match result {
111                Ok(maintainer_id) => self.maintainer_id = Some(maintainer_id),
112                Err(err) => validation.record_fail("maintainer_id", err),
113            }
114        }
115        validation
116    }
117}
118
119impl ModelHooks for Source {
120    type Data = ();
121    #[cfg(feature = "maintainer-id")]
122    type Extension = UserSession<Uuid, String>;
123    #[cfg(not(feature = "maintainer-id"))]
124    type Extension = ();
125
126    #[cfg(feature = "maintainer-id")]
127    #[inline]
128    async fn after_extract(&mut self, session: Self::Extension) -> Result<(), Error> {
129        self.maintainer_id = Some(*session.user_id());
130        Ok(())
131    }
132
133    #[cfg(feature = "maintainer-id")]
134    #[inline]
135    async fn before_validation(
136        data: &mut Map,
137        extension: Option<&Self::Extension>,
138    ) -> Result<(), Error> {
139        if let Some(session) = extension {
140            data.upsert("maintainer_id", session.user_id().to_string());
141        }
142        Ok(())
143    }
144}