Skip to main content

headless_lms_models/
email_templates.rs

1use crate::prelude::*;
2use utoipa::ToSchema;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, sqlx::Type, ToSchema)]
5#[sqlx(type_name = "email_template_type", rename_all = "snake_case")]
6#[serde(rename_all = "snake_case")]
7pub enum EmailTemplateType {
8    ResetPasswordEmail,
9    DeleteUserEmail,
10    ConfirmEmailCode,
11    Generic,
12}
13
14#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
15
16pub struct EmailTemplate {
17    pub id: Uuid,
18    pub created_at: DateTime<Utc>,
19    pub updated_at: DateTime<Utc>,
20    pub deleted_at: Option<DateTime<Utc>>,
21    pub content: Option<serde_json::Value>,
22    #[serde(rename = "template_type")]
23    #[schema(rename = "template_type")]
24    pub email_template_type: EmailTemplateType,
25    pub subject: Option<String>,
26    pub exercise_completions_threshold: Option<i32>,
27    pub points_threshold: Option<i32>,
28    pub course_id: Option<Uuid>,
29    pub language: Option<String>,
30}
31
32#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
33
34pub struct EmailTemplateNew {
35    pub template_type: EmailTemplateType,
36    pub language: Option<String>,
37    pub content: Option<serde_json::Value>,
38    pub subject: Option<String>,
39}
40
41#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
42
43pub struct EmailTemplateUpdate {
44    pub template_type: EmailTemplateType,
45    pub subject: String,
46    pub content: serde_json::Value,
47    pub exercise_completions_threshold: Option<i32>,
48    pub points_threshold: Option<i32>,
49}
50
51pub async fn get_email_templates(
52    conn: &mut PgConnection,
53    course_id: Uuid,
54) -> ModelResult<Vec<EmailTemplate>> {
55    let res = sqlx::query_as!(
56        EmailTemplate,
57        r#"
58SELECT *
59FROM email_templates
60WHERE course_id = $1
61  AND deleted_at IS NULL
62        "#,
63        course_id
64    )
65    .fetch_all(conn)
66    .await?;
67    Ok(res)
68}
69
70pub async fn get_all_email_templates(conn: &mut PgConnection) -> ModelResult<Vec<EmailTemplate>> {
71    let res = sqlx::query_as!(
72        EmailTemplate,
73        r#"
74SELECT *
75FROM email_templates
76WHERE deleted_at IS NULL
77        "#,
78    )
79    .fetch_all(conn)
80    .await?;
81    Ok(res)
82}
83
84pub async fn get_generic_email_template_by_type_and_language(
85    conn: &mut PgConnection,
86    template_type: EmailTemplateType,
87    language: &str,
88) -> ModelResult<EmailTemplate> {
89    let res = sqlx::query_as!(
90        EmailTemplate,
91        r#"
92SELECT *
93FROM email_templates
94WHERE email_template_type = $1
95  AND course_id IS NULL
96  AND deleted_at IS NULL
97  AND (
98    language = $2
99    OR language = 'en'
100    OR language IS NULL
101  )
102ORDER BY CASE
103    WHEN language = $2 THEN 0
104    WHEN language = 'en' THEN 1
105    WHEN language IS NULL THEN 2
106    ELSE 3
107  END
108LIMIT 1
109        "#,
110        template_type as EmailTemplateType,
111        language
112    )
113    .fetch_one(conn)
114    .await?;
115    Ok(res)
116}
117
118pub async fn insert_email_template(
119    conn: &mut PgConnection,
120    course_id: Option<Uuid>,
121    email_template: EmailTemplateNew,
122    subject: Option<&'_ str>,
123) -> ModelResult<EmailTemplate> {
124    let subject_to_use = email_template.subject.as_deref().or(subject);
125    let res = sqlx::query_as!(
126        EmailTemplate,
127        r#"
128INSERT INTO email_templates (
129    email_template_type,
130    course_id,
131    subject,
132    language,
133    content
134  )
135VALUES ($1, $2, $3, $4, $5) ON CONFLICT (email_template_type, language, deleted_at)
136WHERE course_id IS NULL
137  AND deleted_at IS NULL DO
138UPDATE
139SET subject = COALESCE(EXCLUDED.subject, email_templates.subject),
140  content = COALESCE(EXCLUDED.content, email_templates.content),
141  updated_at = NOW()
142RETURNING *
143        "#,
144        email_template.template_type as EmailTemplateType,
145        course_id,
146        subject_to_use,
147        email_template.language,
148        email_template.content,
149    )
150    .fetch_one(conn)
151    .await?;
152    Ok(res)
153}
154
155pub async fn get_email_template(
156    conn: &mut PgConnection,
157    email_template_id: Uuid,
158) -> ModelResult<EmailTemplate> {
159    let res = sqlx::query_as!(
160        EmailTemplate,
161        r#"
162SELECT *
163FROM email_templates
164WHERE id = $1
165  AND deleted_at IS NULL
166        "#,
167        email_template_id
168    )
169    .fetch_one(conn)
170    .await?;
171    Ok(res)
172}
173
174pub async fn update_email_template(
175    conn: &mut PgConnection,
176    email_template_id: Uuid,
177    email_template_update: EmailTemplateUpdate,
178) -> ModelResult<EmailTemplate> {
179    let res = sqlx::query_as!(
180        EmailTemplate,
181        r#"
182UPDATE email_templates
183SET email_template_type = $1,
184  subject = $2,
185  content = $3,
186  exercise_completions_threshold = $4,
187  points_threshold = $5
188WHERE id = $6
189  AND deleted_at IS NULL
190RETURNING *
191  "#,
192        email_template_update.template_type as EmailTemplateType,
193        email_template_update.subject,
194        email_template_update.content,
195        email_template_update.exercise_completions_threshold,
196        email_template_update.points_threshold,
197        email_template_id
198    )
199    .fetch_one(conn)
200    .await?;
201    Ok(res)
202}
203
204pub async fn delete_email_template(
205    conn: &mut PgConnection,
206    email_template_id: Uuid,
207) -> ModelResult<EmailTemplate> {
208    let deleted = sqlx::query_as!(
209        EmailTemplate,
210        r#"
211UPDATE email_templates
212SET deleted_at = NOW()
213WHERE id = $1
214  AND deleted_at IS NULL
215RETURNING *
216  "#,
217        email_template_id
218    )
219    .fetch_one(conn)
220    .await?;
221    Ok(deleted)
222}