1+ use bon:: Builder ;
12use chrono:: NaiveDateTime ;
3+ use diesel:: dsl:: sql;
24use diesel:: prelude:: * ;
3- use diesel_async:: scoped_futures:: ScopedFutureExt ;
4- use diesel_async:: { AsyncConnection , AsyncPgConnection , RunQueryDsl } ;
5+ use diesel:: sql_types:: Integer ;
6+ use diesel:: upsert:: excluded;
7+ use diesel_async:: { AsyncPgConnection , RunQueryDsl } ;
58
69use crate :: app:: App ;
7- use crate :: controllers:: user:: update:: UserConfirmEmail ;
8- use crate :: email:: Emails ;
910use crate :: util:: errors:: AppResult ;
1011
11- use crate :: models:: { Crate , CrateOwner , Email , NewEmail , Owner , OwnerKind , Rights } ;
12+ use crate :: models:: { Crate , CrateOwner , Email , Owner , OwnerKind , Rights } ;
1213use crate :: schema:: { crate_owners, emails, users} ;
1314use crates_io_diesel_helpers:: lower;
1415
@@ -106,7 +107,7 @@ impl User {
106107}
107108
108109/// Represents a new user record insertable to the `users` table
109- #[ derive( Insertable , Debug , Default ) ]
110+ #[ derive( Insertable , Debug , Builder ) ]
110111#[ diesel( table_name = users, check_for_backend( diesel:: pg:: Pg ) ) ]
111112pub struct NewUser < ' a > {
112113 pub gh_id : i32 ,
@@ -116,80 +117,36 @@ pub struct NewUser<'a> {
116117 pub gh_access_token : & ' a str ,
117118}
118119
119- impl < ' a > NewUser < ' a > {
120- pub fn new (
121- gh_id : i32 ,
122- gh_login : & ' a str ,
123- name : Option < & ' a str > ,
124- gh_avatar : Option < & ' a str > ,
125- gh_access_token : & ' a str ,
126- ) -> Self {
127- NewUser {
128- gh_id,
129- gh_login,
130- name,
131- gh_avatar,
132- gh_access_token,
133- }
120+ impl NewUser < ' _ > {
121+ /// Inserts the user into the database, or fails if the user already exists.
122+ pub async fn insert ( & self , conn : & mut AsyncPgConnection ) -> QueryResult < User > {
123+ diesel:: insert_into ( users:: table)
124+ . values ( self )
125+ . get_result ( conn)
126+ . await
134127 }
135128
136129 /// Inserts the user into the database, or updates an existing one.
137- pub async fn create_or_update (
138- & self ,
139- email : Option < & ' a str > ,
140- emails : & Emails ,
141- conn : & mut AsyncPgConnection ,
142- ) -> QueryResult < User > {
143- use diesel:: dsl:: sql;
144- use diesel:: insert_into;
145- use diesel:: pg:: upsert:: excluded;
146- use diesel:: sql_types:: Integer ;
147-
148- conn. transaction ( |conn| {
149- async move {
150- let user: User = insert_into ( users:: table)
151- . values ( self )
152- // We need the `WHERE gh_id > 0` condition here because `gh_id` set
153- // to `-1` indicates that we were unable to find a GitHub ID for
154- // the associated GitHub login at the time that we backfilled
155- // GitHub IDs. Therefore, there are multiple records in production
156- // that have a `gh_id` of `-1` so we need to exclude those when
157- // considering uniqueness of `gh_id` values. The `> 0` condition isn't
158- // necessary for most fields in the database to be used as a conflict
159- // target :)
160- . on_conflict ( sql :: < Integer > ( "(gh_id) WHERE gh_id > 0" ) )
161- . do_update ( )
162- . set ( (
163- users:: gh_login. eq ( excluded ( users:: gh_login) ) ,
164- users:: name. eq ( excluded ( users:: name) ) ,
165- users:: gh_avatar. eq ( excluded ( users:: gh_avatar) ) ,
166- users:: gh_access_token. eq ( excluded ( users:: gh_access_token) ) ,
167- ) )
168- . get_result ( conn)
169- . await ?;
170-
171- // To send the user an account verification email
172- if let Some ( user_email) = email {
173- let new_email = NewEmail :: builder ( )
174- . user_id ( user. id )
175- . email ( user_email)
176- . build ( ) ;
177-
178- if let Some ( token) = new_email. insert_if_missing ( conn) . await ? {
179- // Swallows any error. Some users might insert an invalid email address here.
180- let email = UserConfirmEmail {
181- user_name : & user. gh_login ,
182- domain : & emails. domain ,
183- token,
184- } ;
185- let _ = emails. send ( user_email, email) . await ;
186- }
187- }
188-
189- Ok ( user)
190- }
191- . scope_boxed ( )
192- } )
193- . await
130+ pub async fn insert_or_update ( & self , conn : & mut AsyncPgConnection ) -> QueryResult < User > {
131+ diesel:: insert_into ( users:: table)
132+ . values ( self )
133+ // We need the `WHERE gh_id > 0` condition here because `gh_id` set
134+ // to `-1` indicates that we were unable to find a GitHub ID for
135+ // the associated GitHub login at the time that we backfilled
136+ // GitHub IDs. Therefore, there are multiple records in production
137+ // that have a `gh_id` of `-1` so we need to exclude those when
138+ // considering uniqueness of `gh_id` values. The `> 0` condition isn't
139+ // necessary for most fields in the database to be used as a conflict
140+ // target :)
141+ . on_conflict ( sql :: < Integer > ( "(gh_id) WHERE gh_id > 0" ) )
142+ . do_update ( )
143+ . set ( (
144+ users:: gh_login. eq ( excluded ( users:: gh_login) ) ,
145+ users:: name. eq ( excluded ( users:: name) ) ,
146+ users:: gh_avatar. eq ( excluded ( users:: gh_avatar) ) ,
147+ users:: gh_access_token. eq ( excluded ( users:: gh_access_token) ) ,
148+ ) )
149+ . get_result ( conn)
150+ . await
194151 }
195152}
0 commit comments