Skip to main content

zino_model/message/
mod.rs

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