Skip to content

Commit c341b6c

Browse files
authored
Cannot add problem to another contest if problem has been in contest that deleted (#66)
* Fix bug children's foreign key not deleted * chore remove idea warning * Fix typo
1 parent a499cf3 commit c341b6c

File tree

31 files changed

+177
-158
lines changed

31 files changed

+177
-158
lines changed

backend/src/endpoint/announcement.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl Announcement for ArcServer {
175175

176176
let id = *model.id.as_ref();
177177

178-
tracing::info!(count.announcement = 1, id = id);
178+
info!(count.announcement = 1, id = id);
179179

180180
Ok(id.into())
181181
})
@@ -197,7 +197,7 @@ impl Announcement for ArcServer {
197197
req.bound_check()?;
198198

199199
req.get_or_insert(|req| async move {
200-
tracing::trace!(id = req.id);
200+
trace!(id = req.id);
201201

202202
let mut model = Entity::find_by_id(req.id)
203203
.with_auth(&auth)
@@ -242,7 +242,7 @@ impl Announcement for ArcServer {
242242
if result.rows_affected == 0 {
243243
Err(Error::NotInDB)
244244
} else {
245-
tracing::info!(counter.announcement = -1, id = req.id);
245+
info!(counter.announcement = -1, id = req.id);
246246
Ok(())
247247
}
248248
})

backend/src/endpoint/chat.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl Chat for ArcServer {
5151
.map_err(Into::<Error>::into)?;
5252

5353
let id = *model.id.as_ref();
54-
tracing::debug!(id = id, "chat_created");
54+
debug!(id = id, "chat_created");
5555

5656
Ok(id.into())
5757
})
@@ -80,7 +80,7 @@ impl Chat for ArcServer {
8080
if result.rows_affected == 0 {
8181
Err(Error::NotInDB)
8282
} else {
83-
tracing::info!(counter.chat = -1, id = req.id);
83+
info!(counter.chat = -1, id = req.id);
8484
Ok(())
8585
}
8686
})

backend/src/endpoint/contest.rs

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use super::*;
2-
32
use crate::entity::{
43
contest::{Paginator, *},
5-
*,
4+
problem, *,
65
};
6+
use sea_orm::sea_query::Expr;
77

88
use grpc::backend::contest_server::*;
99

@@ -164,7 +164,7 @@ impl Contest for ArcServer {
164164

165165
let id = *model.id.as_ref();
166166

167-
tracing::info!(count.contest = 1, id = id);
167+
info!(count.contest = 1, id = id);
168168

169169
Ok(id.into())
170170
})
@@ -185,7 +185,7 @@ impl Contest for ArcServer {
185185
let (_, perm) = auth.assume_login()?;
186186

187187
req.get_or_insert(|req| async move {
188-
tracing::trace!(id = req.id);
188+
trace!(id = req.id);
189189

190190
let mut model = Entity::find_by_id(req.id).with_auth(&auth).write()?
191191
.one(self.db.deref())
@@ -238,6 +238,8 @@ impl Contest for ArcServer {
238238
let (auth, req) = self.rate_limit(req).in_current_span().await?;
239239

240240
req.get_or_insert(|req| async move {
241+
let txn = self.db.begin().await?;
242+
241243
let result = Entity::delete_by_id(req.id)
242244
.with_auth(&auth)
243245
.write()?
@@ -246,12 +248,20 @@ impl Contest for ArcServer {
246248
.await
247249
.map_err(Into::<Error>::into)?;
248250

251+
problem::Entity::update_many()
252+
.col_expr(problem::Column::ContestId, Expr::value(Value::Int(None)))
253+
.filter(crate::entity::testcase::Column::ProblemId.eq(req.id))
254+
.exec(&txn)
255+
.instrument(info_span!("remove_child"))
256+
.await?;
257+
258+
txn.commit().await.map_err(|_| Error::Retry)?;
259+
249260
if result.rows_affected == 0 {
250-
Err(Error::NotInDB)
251-
} else {
252-
tracing::info!(counter.contest = -1, id = req.id);
253-
Ok(())
261+
return Err(Error::NotInDB);
254262
}
263+
info!(counter.contest = -1, id = req.id);
264+
Ok(())
255265
})
256266
.await
257267
.with_grpc()
@@ -260,47 +270,30 @@ impl Contest for ArcServer {
260270
#[instrument(
261271
skip_all,
262272
level = "info",
263-
name = "oj.backend.Contest/join",
273+
name = "oj.backend.Contest/publish",
264274
err(level = "debug", Display)
265275
)]
266-
async fn join(&self, req: Request<JoinContestRequest>) -> Result<Response<()>, Status> {
276+
async fn publish(&self, req: Request<PublishRequest>) -> Result<Response<()>, Status> {
267277
let (auth, req) = self.rate_limit(req).in_current_span().await?;
268-
let (user_id, _) = auth.assume_login()?;
269278

270279
req.get_or_insert(|req| async move {
271-
let model = Entity::find_by_id(req.id)
280+
let mut model = Entity::find_by_id(req.id)
272281
.with_auth(&auth)
273-
.read()?
282+
.write()?
283+
.columns([Column::Id])
274284
.one(self.db.deref())
275285
.instrument(info_span!("fetch").or_current())
276286
.await
277287
.map_err(Into::<Error>::into)?
278-
.ok_or(Error::NotInDB)?;
279-
// FIXME: abstract away password checking logic
280-
281-
if let Some(tar) = model.password {
282-
let password = req
283-
.password
284-
.as_ref()
285-
.ok_or(Error::NotInPayload("password"))?;
286-
if !self.crypto.hash_eq(password, &tar) {
287-
return Err(Error::PermissionDeny("mismatched password"));
288-
}
289-
}
290-
291-
let pivot = user_contest::ActiveModel {
292-
user_id: ActiveValue::Set(user_id),
293-
contest_id: ActiveValue::Set(model.id),
294-
..Default::default()
295-
};
288+
.ok_or(Error::NotInDB)?
289+
.into_active_model();
296290

297-
pivot
298-
.save(self.db.deref())
299-
.instrument(info_span!("insert_pviot").or_current())
300-
.await
301-
.map_err(Into::<Error>::into)?;
291+
model.public = ActiveValue::Set(true);
302292

303-
tracing::debug!(user_id = user_id, contest_id = req.id);
293+
model
294+
.update(self.db.deref())
295+
.instrument(info_span!("update").or_current())
296+
.await?;
304297
Ok(())
305298
})
306299
.await
@@ -310,10 +303,10 @@ impl Contest for ArcServer {
310303
#[instrument(
311304
skip_all,
312305
level = "info",
313-
name = "oj.backend.Contest/publish",
306+
name = "oj.backend.Contest/unpublish",
314307
err(level = "debug", Display)
315308
)]
316-
async fn publish(&self, req: Request<PublishRequest>) -> Result<Response<()>, Status> {
309+
async fn unpublish(&self, req: Request<PublishRequest>) -> Result<Response<()>, Status> {
317310
let (auth, req) = self.rate_limit(req).in_current_span().await?;
318311

319312
req.get_or_insert(|req| async move {
@@ -328,7 +321,7 @@ impl Contest for ArcServer {
328321
.ok_or(Error::NotInDB)?
329322
.into_active_model();
330323

331-
model.public = ActiveValue::Set(true);
324+
model.public = ActiveValue::Set(false);
332325

333326
model
334327
.update(self.db.deref())
@@ -343,30 +336,47 @@ impl Contest for ArcServer {
343336
#[instrument(
344337
skip_all,
345338
level = "info",
346-
name = "oj.backend.Contest/unpublish",
339+
name = "oj.backend.Contest/join",
347340
err(level = "debug", Display)
348341
)]
349-
async fn unpublish(&self, req: Request<PublishRequest>) -> Result<Response<()>, Status> {
342+
async fn join(&self, req: Request<JoinContestRequest>) -> Result<Response<()>, Status> {
350343
let (auth, req) = self.rate_limit(req).in_current_span().await?;
344+
let (user_id, _) = auth.assume_login()?;
351345

352346
req.get_or_insert(|req| async move {
353-
let mut model = Entity::find_by_id(req.id)
347+
let model = Entity::find_by_id(req.id)
354348
.with_auth(&auth)
355-
.write()?
356-
.columns([Column::Id])
349+
.read()?
357350
.one(self.db.deref())
358351
.instrument(info_span!("fetch").or_current())
359352
.await
360353
.map_err(Into::<Error>::into)?
361-
.ok_or(Error::NotInDB)?
362-
.into_active_model();
354+
.ok_or(Error::NotInDB)?;
355+
// FIXME: abstract away password checking logic
363356

364-
model.public = ActiveValue::Set(false);
357+
if let Some(tar) = model.password {
358+
let password = req
359+
.password
360+
.as_ref()
361+
.ok_or(Error::NotInPayload("password"))?;
362+
if !self.crypto.hash_eq(password, &tar) {
363+
return Err(Error::PermissionDeny("mismatched password"));
364+
}
365+
}
365366

366-
model
367-
.update(self.db.deref())
368-
.instrument(info_span!("update").or_current())
369-
.await?;
367+
let pivot = user_contest::ActiveModel {
368+
user_id: ActiveValue::Set(user_id),
369+
contest_id: ActiveValue::Set(model.id),
370+
..Default::default()
371+
};
372+
373+
pivot
374+
.save(self.db.deref())
375+
.instrument(info_span!("insert_pviot").or_current())
376+
.await
377+
.map_err(Into::<Error>::into)?;
378+
379+
debug!(user_id = user_id, contest_id = req.id);
370380
Ok(())
371381
})
372382
.await

backend/src/endpoint/education.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl Education for ArcServer {
104104

105105
let id = *model.id.as_ref();
106106

107-
tracing::info!(count.education = 1, id = id);
107+
info!(count.education = 1, id = id);
108108

109109
Ok(id.into())
110110
})
@@ -123,7 +123,7 @@ impl Education for ArcServer {
123123
req.bound_check()?;
124124

125125
req.get_or_insert(|req| async move {
126-
tracing::trace!(id = req.id);
126+
trace!(id = req.id);
127127
let mut model = Entity::find_by_id(req.id)
128128
.with_auth(&auth)
129129
.write()?
@@ -167,7 +167,7 @@ impl Education for ArcServer {
167167
if result.rows_affected == 0 {
168168
Err(Error::NotInDB)
169169
} else {
170-
tracing::info!(counter.education = -1, id = req.id);
170+
info!(counter.education = -1, id = req.id);
171171
Ok(())
172172
}
173173
})

backend/src/endpoint/imgur.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ impl Image for ArcServer {
1919
req.get_or_insert(|req| async move {
2020
let url = self.imgur.upload(req.data).await?;
2121

22-
tracing::debug!(counter.image = 1, uri = url);
22+
debug!(counter.image = 1, uri = url);
2323
Ok(UploadResponse { url })
2424
})
2525
.await

backend/src/endpoint/problem.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use super::*;
22
use grpc::backend::problem_server::*;
3+
use sea_orm::sea_query::Expr;
34

4-
use crate::entity::{contest, problem::Paginator, problem::*};
5+
use crate::entity::{contest, problem::Paginator, problem::*, testcase};
56

67
impl<'a> From<WithAuth<'a, Model>> for ProblemFullInfo {
78
fn from(value: WithAuth<'a, Model>) -> Self {
@@ -139,7 +140,7 @@ impl Problem for ArcServer {
139140

140141
let id = *model.id.as_ref();
141142

142-
tracing::info!(count.problem = 1, id = id);
143+
info!(count.problem = 1, id = id);
143144

144145
Ok(id.into())
145146
})
@@ -191,17 +192,28 @@ impl Problem for ArcServer {
191192
async fn remove(&self, req: Request<RemoveRequest>) -> Result<Response<()>, Status> {
192193
let (auth, req) = self.rate_limit(req).in_current_span().await?;
193194
req.get_or_insert(|req| async move {
195+
let txn = self.db.begin().await?;
196+
194197
let result = Entity::delete_by_id(req.id)
195198
.with_auth(&auth)
196199
.write()?
197-
.exec(self.db.deref())
200+
.exec(&txn)
198201
.instrument(info_span!("remove").or_current())
199-
.await
200-
.map_err(Into::<Error>::into)?;
202+
.await?;
203+
204+
testcase::Entity::update_many()
205+
.col_expr(testcase::Column::ProblemId, Expr::value(Value::Int(None)))
206+
.filter(testcase::Column::ProblemId.eq(req.id))
207+
.exec(&txn)
208+
.instrument(info_span!("remove_child"))
209+
.await?;
210+
211+
txn.commit().await.map_err(|_| Error::Retry)?;
201212

202213
if result.rows_affected == 0 {
203214
return Err(Error::NotInDB);
204215
}
216+
info!(count.problem = -1, id = req.id);
205217
Ok(())
206218
})
207219
.await
@@ -286,7 +298,7 @@ impl Problem for ArcServer {
286298

287299
let mut model = model.ok_or(Error::NotInDB)?.into_active_model();
288300
if let Some(x) = model.contest_id.into_value() {
289-
tracing::debug!(old_id = x.to_string());
301+
debug!(old_id = x.to_string());
290302
}
291303
model.contest_id = ActiveValue::Set(None);
292304
model

backend/src/endpoint/submit.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ impl Submit for ArcServer {
9191
async fn info(&self, req: Request<Id>) -> Result<Response<SubmitInfo>, Status> {
9292
let (auth, req) = self.rate_limit(req).in_current_span().await?;
9393

94-
tracing::debug!(id = req.id);
94+
debug!(id = req.id);
9595

9696
let model = Entity::read_filter(Entity::find_by_id(req.id), &auth)?
9797
.one(self.db.deref())
98-
.instrument(tracing::debug_span!("fetch").or_current())
98+
.instrument(debug_span!("fetch").or_current())
9999
.await
100100
.map_err(Into::<Error>::into)?
101101
.ok_or(Error::NotInDB)?;
@@ -156,7 +156,7 @@ impl Submit for ArcServer {
156156
.instrument(info_span!("construct_submit").or_current())
157157
.await?;
158158

159-
tracing::info!(counter.submit = 1, id = id);
159+
info!(counter.submit = 1, id = id);
160160

161161
Ok(id.into())
162162
})
@@ -189,7 +189,7 @@ impl Submit for ArcServer {
189189
if result.rows_affected == 0 {
190190
Err(Error::NotInDB)
191191
} else {
192-
tracing::info!(counter.submit = -1, id = req.id);
192+
info!(counter.submit = -1, id = req.id);
193193
Ok(())
194194
}
195195
})

0 commit comments

Comments
 (0)