Skip to content

Commit

Permalink
lock out user from application changes after submission
Browse files Browse the repository at this point in the history
  • Loading branch information
KavikaPalletenne committed Dec 2, 2024
1 parent 706f814 commit 299ee9b
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 20 deletions.
1 change: 1 addition & 0 deletions backend/migrations/20240406031915_create_applications.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CREATE TABLE applications (
private_status application_status NOT NULL DEFAULT 'Pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
submitted BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT FK_applications_campaigns
FOREIGN KEY(campaign_id)
REFERENCES campaigns(id)
Expand Down
12 changes: 11 additions & 1 deletion backend/server/src/handler/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,20 @@ impl ApplicationHandler {
_user: ApplicationOwner,
Path(application_id): Path<i64>,
mut transaction: DBTransaction<'_>,
Json(data): Json<ApplicationRoleUpdate>
Json(data): Json<ApplicationRoleUpdate>,
) -> Result<impl IntoResponse, ChaosError> {
Application::update_roles(application_id, data.roles, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully updated application roles"))
}

pub async fn submit(
_user: ApplicationOwner,
Path(application_id): Path<i64>,
mut transaction: DBTransaction<'_>,
) -> Result<impl IntoResponse, ChaosError> {
Application::submit(application_id, &mut transaction.tx).await?;
transaction.tx.commit().await?;
Ok((StatusCode::OK, "Successfully submitted application"))
}
}
36 changes: 35 additions & 1 deletion backend/server/src/models/answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ impl Answer {
) -> Result<i64, ChaosError> {
answer_data.validate()?;

// Can only answer for applications that haven't been submitted
let _ = sqlx::query!(
"
SELECT id FROM applications WHERE id = $1 AND submitted = false
",
application_id
)
.fetch_one(transaction.deref_mut())
.await?;

let id = snowflake_generator.generate();

sqlx::query!(
Expand Down Expand Up @@ -299,6 +309,16 @@ impl Answer {
.fetch_one(transaction.deref_mut())
.await?;

// Can only answer for applications that haven't been submitted
let _ = sqlx::query!(
"
SELECT id FROM applications WHERE id = $1 AND submitted = false
",
answer.application_id
)
.fetch_one(transaction.deref_mut())
.await?;

let old_data = AnswerData::from_question_type(&answer.question_type);
old_data.delete_from_db(id, transaction).await?;

Expand All @@ -319,10 +339,24 @@ impl Answer {
id: i64,
transaction: &mut Transaction<'_, Postgres>,
) -> Result<(), ChaosError> {
sqlx::query!("DELETE FROM answers WHERE id = $1 RETURNING id", id)
let answer = sqlx::query!("SELECT application_id FROM answers WHERE id = $1", id)
.fetch_one(transaction.deref_mut())
.await?;

// Can only answer for applications that haven't been submitted
let _ = sqlx::query!(
"
SELECT id FROM applications WHERE id = $1 AND submitted = false
",
answer.application_id
)
.fetch_one(transaction.deref_mut())
.await?;

sqlx::query!("DELETE FROM answers WHERE id = $1", id)
.execute(transaction.deref_mut())
.await?;

Ok(())
}
}
Expand Down
61 changes: 43 additions & 18 deletions backend/server/src/models/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub struct ApplicationAppliedRoleDetails {

#[derive(Deserialize)]
pub struct ApplicationRoleUpdate {
pub roles: Vec<i64>
pub roles: Vec<ApplicationRole>,
}

#[derive(Deserialize, Serialize, sqlx::Type, Clone, Debug)]
Expand Down Expand Up @@ -123,7 +123,7 @@ impl Application {
}

/*
Get Application given an application id
Get Application given an application id. Used by application viewers
*/
pub async fn get(
id: i64,
Expand All @@ -138,7 +138,7 @@ impl Application {
u.pronouns AS user_pronouns, u.degree_name AS user_degree_name,
u.degree_starting_year AS user_degree_starting_year
FROM applications a LEFT JOIN users u ON u.id = a.user_id
WHERE a.id = $1
WHERE a.id = $1 AND a.submitted = true
",
id
)
Expand Down Expand Up @@ -180,7 +180,7 @@ impl Application {
}

/*
Get All applications that apply for a given role
Get All applications that apply for a given role. Used by application viewers
*/
pub async fn get_from_role_id(
role_id: i64,
Expand All @@ -195,7 +195,7 @@ impl Application {
u.pronouns AS user_pronouns, u.degree_name AS user_degree_name,
u.degree_starting_year AS user_degree_starting_year
FROM applications a LEFT JOIN users u ON u.id = a.user_id LEFT JOIN application_roles ar on ar.application_id = a.id
WHERE ar.id = $1
WHERE ar.id = $1 AND a.submitted = true
",
role_id
)
Expand Down Expand Up @@ -244,7 +244,7 @@ impl Application {
}

/*
Get All applications that apply for a given campaign
Get All applications that apply for a given campaign. Used by application viewers
*/
pub async fn get_from_campaign_id(
campaign_id: i64,
Expand All @@ -259,7 +259,7 @@ impl Application {
u.pronouns AS user_pronouns, u.degree_name AS user_degree_name,
u.degree_starting_year AS user_degree_starting_year
FROM applications a LEFT JOIN users u ON u.id = a.user_id
WHERE a.campaign_id = $1
WHERE a.campaign_id = $1 AND a.submitted = true
",
campaign_id
)
Expand Down Expand Up @@ -308,7 +308,7 @@ impl Application {
}

/*
Get All applications that are made by a given user
Get All applications that are made by a given user. Used by user
*/
pub async fn get_from_user_id(
user_id: i64,
Expand Down Expand Up @@ -414,20 +414,25 @@ impl Application {

pub async fn update_roles(
id: i64,
roles: Vec<i64>,
roles: Vec<ApplicationRole>,
transaction: &mut Transaction<'_, Postgres>,
) -> Result<(), ChaosError> {
// There can be 0 roles, so we cannot use the `RETURNING id` method,
// as there could be 0 roles. If application_id is wrong, then
// the next query will error, preventing changes to DB.
// Users can only update applications as long as they have not submitted
let _ = sqlx::query!(
"SELECT id FROM applications WHERE id = $1 AND submitted = false",
id
)
.fetch_one(transaction.deref_mut())
.await?;

sqlx::query!(
"
DELETE FROM application_roles WHERE application_id = $1
",
id
)
.execute(transaction.deref_mut())
.await?;
.execute(transaction.deref_mut())
.await?;

// Insert into table application_roles
for role in roles {
Expand All @@ -437,11 +442,31 @@ impl Application {
VALUES ($1, $2, $3)
",
id,
role_applied.campaign_role_id,
role_applied.preference
role.campaign_role_id,
role.preference
)
.execute(transaction.deref_mut())
.await?;
.execute(transaction.deref_mut())
.await?;
}

Ok(())
}

pub async fn submit(
id: i64,
transaction: &mut Transaction<'_, Postgres>,
) -> Result<(), ChaosError> {
// Can only submit once
let _ = sqlx::query!(
"
UPDATE applications SET submitted = true
WHERE id = $1 AND submitted = false RETURNING id
",
id
)
.fetch_one(transaction.deref_mut())
.await?;

Ok(())
}
}

0 comments on commit 299ee9b

Please sign in to comment.