zino_model/task/
mod.rs

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