Skip to content

Commit

Permalink
Added database constraints for inactive works with no withdrawn date …
Browse files Browse the repository at this point in the history
…and withdrawn date < publication date; added validation errors for same constraints; made withdrawn date required on input form in component
  • Loading branch information
brendan-oconnell committed Apr 24, 2024
1 parent 1110978 commit 6d15f3c
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 14 deletions.
5 changes: 4 additions & 1 deletion thoth-api/migrations/v0.12.3/down.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ ALTER TABLE series
ALTER COLUMN issn_digital SET NOT NULL;

ALTER TABLE work
DROP CONSTRAINT work_withdrawn_date_check,
DROP CONSTRAINT work_active_withdrawn_date_check,
DROP CONSTRAINT work_inactive_no_withdrawn_date_check,
DROP CONSTRAINT work_withdrawn_date_after_publication_date_check,
DROP COLUMN withdrawn_date;

11 changes: 9 additions & 2 deletions thoth-api/migrations/v0.12.3/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ UPDATE work
OR work_status = 'out-of-print');

ALTER TABLE work
ADD CONSTRAINT work_withdrawn_date_check CHECK
ADD CONSTRAINT work_active_withdrawn_date_check CHECK
((work_status = 'withdrawn-from-sale' OR work_status = 'out-of-print')
OR (work_status NOT IN ('withdrawn-from-sale', 'out-of-print') AND withdrawn_date IS NULL));
OR (work_status NOT IN ('withdrawn-from-sale', 'out-of-print') AND withdrawn_date IS NULL)),

ADD CONSTRAINT work_inactive_no_withdrawn_date_check CHECK
(((work_status = 'withdrawn-from-sale' OR work_status = 'out-of-print') AND withdrawn_date IS NOT NULL)
OR (work_status NOT IN ('withdrawn-from-sale', 'out-of-print'))),

ADD CONSTRAINT work_withdrawn_date_after_publication_date_check CHECK
(withdrawn_date IS NULL OR (publication_date < withdrawn_date));
4 changes: 4 additions & 0 deletions thoth-api/src/graphql/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::model::publisher::*;
use crate::model::reference::*;
use crate::model::series::*;
use crate::model::subject::*;
use crate::model::work::crud::WorkValidation;
use crate::model::work::*;
use crate::model::work_relation::*;
use crate::model::Convert;
Expand Down Expand Up @@ -1516,6 +1517,8 @@ impl MutationRoot {
.account_access
.can_edit(publisher_id_from_imprint_id(&context.db, data.imprint_id)?)?;

data.validate()?;

Work::create(&context.db, &data).map_err(|e| e.into())
}

Expand Down Expand Up @@ -1705,6 +1708,7 @@ impl MutationRoot {
work.can_be_chapter(&context.db)?;
}

data.validate()?;
let account_id = context.token.jwt.as_ref().unwrap().account_id(&context.db);
// update the work and, if it succeeds, synchronise its children statuses and pub. date
match work.update(&context.db, &data, &account_id) {
Expand Down
4 changes: 3 additions & 1 deletion thoth-api/src/model/work/crud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,9 @@ where
Self: WorkProperties,
{
fn validate(&self) -> ThothResult<()> {
self.withdrawn_date_error()
self.withdrawn_date_error()?;
self.no_withdrawn_date_error()?;
self.withdrawn_date_before_publication_date_error()
}
}

Expand Down
42 changes: 35 additions & 7 deletions thoth-api/src/model/work/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,30 +336,46 @@ impl WorkStatus {
fn is_withdrawn_out_of_print(&self) -> bool {
matches!(self, WorkStatus::OutOfPrint | WorkStatus::WithdrawnFromSale)
}

fn is_not_withdrawn_out_of_print(&self) -> bool {
!self.is_withdrawn_out_of_print()
}
}

pub trait WorkProperties {
fn work_status(&self) -> &WorkStatus;
fn publication_date(&self) -> &Option<NaiveDate>;
fn withdrawn_date(&self) -> &Option<NaiveDate>;

fn is_not_withdrawn_out_of_print(&self) -> bool {
self.work_status().is_not_withdrawn_out_of_print()
fn is_withdrawn_out_of_print(&self) -> bool {
self.work_status().is_withdrawn_out_of_print()
}

fn has_withdrawn_date(&self) -> bool {
self.withdrawn_date().is_some()
}

fn withdrawn_date_error(&self) -> ThothResult<()> {
if self.is_not_withdrawn_out_of_print() && self.has_withdrawn_date() {
if !self.is_withdrawn_out_of_print() && self.has_withdrawn_date() {
return Err(ThothError::WithdrawnDateError);
}
Ok(())
}

fn no_withdrawn_date_error(&self) -> ThothResult<()> {
if self.is_withdrawn_out_of_print() && !self.has_withdrawn_date() {
return Err(ThothError::NoWithdrawnDateError);
}
Ok(())
}

fn withdrawn_date_before_publication_date_error(&self) -> ThothResult<()> {
match (self.withdrawn_date(), self.publication_date()) {
(Some(withdrawn_date), Some(publication_date)) if withdrawn_date < publication_date => {
println!("withdrawn_date: {:?}", self.withdrawn_date());
println!("publication_date: {:?}", self.publication_date());
return Err(ThothError::WithdrawnDateBeforePublicationDateError);
}
_ => {}
}
Ok(())
}
}

impl WorkProperties for Work {
Expand All @@ -370,6 +386,10 @@ impl WorkProperties for Work {
fn withdrawn_date(&self) -> &Option<NaiveDate> {
&self.withdrawn_date
}

fn publication_date(&self) -> &Option<NaiveDate> {
&self.publication_date
}
}

impl WorkProperties for NewWork {
Expand All @@ -380,6 +400,10 @@ impl WorkProperties for NewWork {
fn withdrawn_date(&self) -> &Option<NaiveDate> {
&self.withdrawn_date
}

fn publication_date(&self) -> &Option<NaiveDate> {
&self.publication_date
}
}

impl WorkProperties for PatchWork {
Expand All @@ -390,6 +414,10 @@ impl WorkProperties for PatchWork {
fn withdrawn_date(&self) -> &Option<NaiveDate> {
&self.withdrawn_date
}

fn publication_date(&self) -> &Option<NaiveDate> {
&self.publication_date
}
}

impl Work {
Expand Down
1 change: 1 addition & 0 deletions thoth-app/src/component/new_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ impl Component for NewWorkComponent {
label = "Withdrawn Date"
value={ self.work.withdrawn_date.clone() }
oninput={ ctx.link().callback(|e: InputEvent| Msg::ChangeWithdrawnDate(e.to_value())) }
required = true
deactivated={ is_not_withdrawn_or_out_of_print }
/>
<FormTextInput
Expand Down
1 change: 1 addition & 0 deletions thoth-app/src/component/work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ impl Component for WorkComponent {
label = "Withdrawn Date"
value={ self.work.withdrawn_date.clone() }
oninput={ ctx.link().callback(|e: InputEvent| Msg::ChangeWithdrawnDate(e.to_value())) }
required = true
deactivated={ is_not_withdrawn_or_out_of_print }
/>
<FormTextInput
Expand Down
4 changes: 3 additions & 1 deletion thoth-errors/src/database_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ static DATABASE_CONSTRAINT_ERRORS: Map<&'static str, &'static str> = phf_map! {
"series_series_url_check" => "Invalid series URL.",
"subject_subject_code_check" => "Subject codes must not be an empty string.",
"subject_subject_ordinal_check" => "A subject ordinal number must be greater than 0.",
"work_active_withdrawn_date_check" => "Withdrawn Date can only be set for an out of print or withdrawn from sale Work.",
"work_audio_count_check" => "An audio count must be greater than 0.",
"work_bibliography_note_check" => "Bibliography note must not be an empty string.",
"work_chapter_no_edition" => "Chapters must not have an edition number.",
Expand All @@ -135,6 +136,7 @@ static DATABASE_CONSTRAINT_ERRORS: Map<&'static str, &'static str> = phf_map! {
"work_full_title_check" => "Full title must not be an empty string.",
"work_general_note_check" => "General note must not be an empty string.",
"work_image_count_check" => "An image count must be greater than 0.",
"work_inactive_no_withdrawn_date_check" => "An out of print or withdrawn from sale Work must include Withdrawn Date.",
"work_landing_page_check" => "Invalid landing page URL.",
"work_last_page_check" => "Last page must not be an empty string.",
"work_lccn_check" => "LCCN must not be an empty string.",
Expand All @@ -160,7 +162,7 @@ static DATABASE_CONSTRAINT_ERRORS: Map<&'static str, &'static str> = phf_map! {
"work_title_check" => "Title must not be an empty string.",
"work_toc_check" => "Table of content must not be an empty string.",
"work_video_count_check" => "A video count must be greater than 0.",
"work_withdrawn_date_check" => "Withdrawn Date can only be added to out of print or withdrawn from sale Works.",
"work_withdrawn_date_after_publication_date_check" => "Publication date must be before withdrawn date."
};

impl From<diesel::result::Error> for ThothError {
Expand Down
8 changes: 6 additions & 2 deletions thoth-errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,17 @@ pub enum ThothError {
#[error(
"Price values must be greater than zero. To indicate an unpriced Publication, omit all Prices."
)]
WithdrawnDateError,
#[error("Withdrawn Date can only be added to out of print or withdrawn from sale Works.")]
PriceZeroError,
#[error("{0}")]
RequestError(String),
#[error("{0}")]
GraphqlError(String),
#[error("Withdrawn Date must be later than Publication Date.")]
WithdrawnDateBeforePublicationDateError,
#[error("Withdrawn Date can only be added to an Out of Print or Withdrawn From Sale Work.")]
WithdrawnDateError,
#[error("An Out of Print or Withdrawn From Sale Work must have a Withdrawn Date.")]
NoWithdrawnDateError,
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down

0 comments on commit 6d15f3c

Please sign in to comment.