1use super::*;
2
3impl Database {
4 pub async fn create_user(
5 &self,
6 email_address: &str,
7 admin: bool,
8 params: NewUserParams,
9 ) -> Result<NewUserResult> {
10 self.transaction(|tx| async {
11 let tx = tx;
12 let user = user::Entity::insert(user::ActiveModel {
13 email_address: ActiveValue::set(Some(email_address.into())),
14 github_login: ActiveValue::set(params.github_login.clone()),
15 github_user_id: ActiveValue::set(Some(params.github_user_id)),
16 admin: ActiveValue::set(admin),
17 metrics_id: ActiveValue::set(Uuid::new_v4()),
18 ..Default::default()
19 })
20 .on_conflict(
21 OnConflict::column(user::Column::GithubLogin)
22 .update_column(user::Column::GithubLogin)
23 .to_owned(),
24 )
25 .exec_with_returning(&*tx)
26 .await?;
27
28 Ok(NewUserResult {
29 user_id: user.id,
30 metrics_id: user.metrics_id.to_string(),
31 signup_device_id: None,
32 inviting_user_id: None,
33 })
34 })
35 .await
36 }
37
38 pub async fn get_user_by_id(&self, id: UserId) -> Result<Option<user::Model>> {
39 self.transaction(|tx| async move { Ok(user::Entity::find_by_id(id).one(&*tx).await?) })
40 .await
41 }
42
43 pub async fn get_users_by_ids(&self, ids: Vec<UserId>) -> Result<Vec<user::Model>> {
44 self.transaction(|tx| async {
45 let tx = tx;
46 Ok(user::Entity::find()
47 .filter(user::Column::Id.is_in(ids.iter().copied()))
48 .all(&*tx)
49 .await?)
50 })
51 .await
52 }
53
54 pub async fn get_user_by_github_login(&self, github_login: &str) -> Result<Option<User>> {
55 self.transaction(|tx| async move {
56 Ok(user::Entity::find()
57 .filter(user::Column::GithubLogin.eq(github_login))
58 .one(&*tx)
59 .await?)
60 })
61 .await
62 }
63
64 pub async fn get_or_create_user_by_github_account(
65 &self,
66 github_login: &str,
67 github_user_id: Option<i32>,
68 github_email: Option<&str>,
69 ) -> Result<Option<User>> {
70 self.transaction(|tx| async move {
71 let tx = &*tx;
72 if let Some(github_user_id) = github_user_id {
73 if let Some(user_by_github_user_id) = user::Entity::find()
74 .filter(user::Column::GithubUserId.eq(github_user_id))
75 .one(tx)
76 .await?
77 {
78 let mut user_by_github_user_id = user_by_github_user_id.into_active_model();
79 user_by_github_user_id.github_login = ActiveValue::set(github_login.into());
80 Ok(Some(user_by_github_user_id.update(tx).await?))
81 } else if let Some(user_by_github_login) = user::Entity::find()
82 .filter(user::Column::GithubLogin.eq(github_login))
83 .one(tx)
84 .await?
85 {
86 let mut user_by_github_login = user_by_github_login.into_active_model();
87 user_by_github_login.github_user_id = ActiveValue::set(Some(github_user_id));
88 Ok(Some(user_by_github_login.update(tx).await?))
89 } else {
90 let user = user::Entity::insert(user::ActiveModel {
91 email_address: ActiveValue::set(github_email.map(|email| email.into())),
92 github_login: ActiveValue::set(github_login.into()),
93 github_user_id: ActiveValue::set(Some(github_user_id)),
94 admin: ActiveValue::set(false),
95 invite_count: ActiveValue::set(0),
96 invite_code: ActiveValue::set(None),
97 metrics_id: ActiveValue::set(Uuid::new_v4()),
98 ..Default::default()
99 })
100 .exec_with_returning(&*tx)
101 .await?;
102 Ok(Some(user))
103 }
104 } else {
105 Ok(user::Entity::find()
106 .filter(user::Column::GithubLogin.eq(github_login))
107 .one(tx)
108 .await?)
109 }
110 })
111 .await
112 }
113
114 pub async fn get_all_users(&self, page: u32, limit: u32) -> Result<Vec<User>> {
115 self.transaction(|tx| async move {
116 Ok(user::Entity::find()
117 .order_by_asc(user::Column::GithubLogin)
118 .limit(limit as u64)
119 .offset(page as u64 * limit as u64)
120 .all(&*tx)
121 .await?)
122 })
123 .await
124 }
125
126 pub async fn get_user_metrics_id(&self, id: UserId) -> Result<String> {
127 #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
128 enum QueryAs {
129 MetricsId,
130 }
131
132 self.transaction(|tx| async move {
133 let metrics_id: Uuid = user::Entity::find_by_id(id)
134 .select_only()
135 .column(user::Column::MetricsId)
136 .into_values::<_, QueryAs>()
137 .one(&*tx)
138 .await?
139 .ok_or_else(|| anyhow!("could not find user"))?;
140 Ok(metrics_id.to_string())
141 })
142 .await
143 }
144
145 pub async fn set_user_connected_once(&self, id: UserId, connected_once: bool) -> Result<()> {
146 self.transaction(|tx| async move {
147 user::Entity::update_many()
148 .filter(user::Column::Id.eq(id))
149 .set(user::ActiveModel {
150 connected_once: ActiveValue::set(connected_once),
151 ..Default::default()
152 })
153 .exec(&*tx)
154 .await?;
155 Ok(())
156 })
157 .await
158 }
159
160 pub async fn destroy_user(&self, id: UserId) -> Result<()> {
161 self.transaction(|tx| async move {
162 access_token::Entity::delete_many()
163 .filter(access_token::Column::UserId.eq(id))
164 .exec(&*tx)
165 .await?;
166 user::Entity::delete_by_id(id).exec(&*tx).await?;
167 Ok(())
168 })
169 .await
170 }
171
172 pub async fn fuzzy_search_users(&self, name_query: &str, limit: u32) -> Result<Vec<User>> {
173 self.transaction(|tx| async {
174 let tx = tx;
175 let like_string = Self::fuzzy_like_string(name_query);
176 let query = "
177 SELECT users.*
178 FROM users
179 WHERE github_login ILIKE $1
180 ORDER BY github_login <-> $2
181 LIMIT $3
182 ";
183
184 Ok(user::Entity::find()
185 .from_raw_sql(Statement::from_sql_and_values(
186 self.pool.get_database_backend(),
187 query.into(),
188 vec![like_string.into(), name_query.into(), limit.into()],
189 ))
190 .all(&*tx)
191 .await?)
192 })
193 .await
194 }
195
196 pub fn fuzzy_like_string(string: &str) -> String {
197 let mut result = String::with_capacity(string.len() * 2 + 1);
198 for c in string.chars() {
199 if c.is_alphanumeric() {
200 result.push('%');
201 result.push(c);
202 }
203 }
204 result.push('%');
205 result
206 }
207
208 pub async fn create_user_flag(&self, flag: &str) -> Result<FlagId> {
209 self.transaction(|tx| async move {
210 let flag = feature_flag::Entity::insert(feature_flag::ActiveModel {
211 flag: ActiveValue::set(flag.to_string()),
212 ..Default::default()
213 })
214 .exec(&*tx)
215 .await?
216 .last_insert_id;
217
218 Ok(flag)
219 })
220 .await
221 }
222
223 pub async fn add_user_flag(&self, user: UserId, flag: FlagId) -> Result<()> {
224 self.transaction(|tx| async move {
225 user_feature::Entity::insert(user_feature::ActiveModel {
226 user_id: ActiveValue::set(user),
227 feature_id: ActiveValue::set(flag),
228 })
229 .exec(&*tx)
230 .await?;
231
232 Ok(())
233 })
234 .await
235 }
236
237 pub async fn get_user_flags(&self, user: UserId) -> Result<Vec<String>> {
238 self.transaction(|tx| async move {
239 #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
240 enum QueryAs {
241 Flag,
242 }
243
244 let flags = user::Model {
245 id: user,
246 ..Default::default()
247 }
248 .find_linked(user::UserFlags)
249 .select_only()
250 .column(feature_flag::Column::Flag)
251 .into_values::<_, QueryAs>()
252 .all(&*tx)
253 .await?;
254
255 Ok(flags)
256 })
257 .await
258 }
259}