Skip to content

Commit

Permalink
Merge branch 'develop' into feature/upgrade_rust_1_77_1
Browse files Browse the repository at this point in the history
  • Loading branch information
ja573 authored Apr 16, 2024
2 parents fd15616 + 17f009e commit fb42f2f
Show file tree
Hide file tree
Showing 28 changed files with 168 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- [581](https://github.com/thoth-pub/thoth/issues/581) - Add crossmark policy DOI to imprint record

### Changed
- [591](https://github.com/thoth-pub/thoth/pull/591) - Upgrade rust to `1.77.2` in production and development `Dockerfile`
- [591](https://github.com/thoth-pub/thoth/pull/591) - Added favicons to export API and GraphQL API docs
Expand Down
2 changes: 2 additions & 0 deletions thoth-api/migrations/v0.11.19/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE imprint
DROP COLUMN crossmark_doi;
2 changes: 2 additions & 0 deletions thoth-api/migrations/v0.11.19/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE imprint
ADD COLUMN crossmark_doi TEXT CHECK (crossmark_doi ~* 'https:\/\/doi.org\/10.\d{4,9}\/[-._\;\(\)\/:a-zA-Z0-9]+$');
9 changes: 9 additions & 0 deletions thoth-api/src/graphql/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3035,10 +3035,19 @@ impl Imprint {
&self.imprint_name
}

#[graphql(description = "URL of the imprint's landing page")]
pub fn imprint_url(&self) -> Option<&String> {
self.imprint_url.as_ref()
}

#[graphql(
description = "DOI of the imprint's Crossmark policy page, if publisher participates. Crossmark 'gives readers quick and easy access to the
current status of an item of content, including any corrections, retractions, or updates'. More: https://www.crossref.org/services/crossmark/"
)]
pub fn crossmark_doi(&self) -> Option<&Doi> {
self.crossmark_doi.as_ref()
}

pub fn created_at(&self) -> Timestamp {
self.created_at.clone()
}
Expand Down
4 changes: 4 additions & 0 deletions thoth-api/src/model/imprint/crud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ impl Crud for Imprint {
Direction::Asc => query.order(imprint_url.asc()),
Direction::Desc => query.order(imprint_url.desc()),
},
ImprintField::CrossmarkDoi => match order.direction {
Direction::Asc => query.order(crossmark_doi.asc()),
Direction::Desc => query.order(crossmark_doi.desc()),
},
ImprintField::CreatedAt => match order.direction {
Direction::Asc => query.order(created_at.asc()),
Direction::Desc => query.order(created_at.desc()),
Expand Down
12 changes: 12 additions & 0 deletions thoth-api/src/model/imprint/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::model::Doi;
use serde::Deserialize;
use serde::Serialize;
use strum::Display;
Expand Down Expand Up @@ -27,6 +28,8 @@ pub enum ImprintField {
ImprintName,
#[strum(serialize = "ImprintURL")]
ImprintUrl,
#[strum(serialize = "CrossmarkDOI")]
CrossmarkDoi,
CreatedAt,
UpdatedAt,
}
Expand All @@ -39,6 +42,7 @@ pub struct Imprint {
pub publisher_id: Uuid,
pub imprint_name: String,
pub imprint_url: Option<String>,
pub crossmark_doi: Option<Doi>,
pub created_at: Timestamp,
pub updated_at: Timestamp,
}
Expand All @@ -49,6 +53,7 @@ pub struct ImprintWithPublisher {
pub imprint_id: Uuid,
pub imprint_name: String,
pub imprint_url: Option<String>,
pub crossmark_doi: Option<Doi>,
pub updated_at: Timestamp,
pub publisher: Publisher,
}
Expand All @@ -62,6 +67,7 @@ pub struct NewImprint {
pub publisher_id: Uuid,
pub imprint_name: String,
pub imprint_url: Option<String>,
pub crossmark_doi: Option<Doi>,
}

#[cfg_attr(
Expand All @@ -74,6 +80,7 @@ pub struct PatchImprint {
pub publisher_id: Uuid,
pub imprint_name: String,
pub imprint_url: Option<String>,
pub crossmark_doi: Option<Doi>,
}

#[cfg_attr(feature = "backend", derive(Queryable))]
Expand Down Expand Up @@ -118,6 +125,7 @@ fn test_imprintfield_display() {
assert_eq!(format!("{}", ImprintField::ImprintId), "ID");
assert_eq!(format!("{}", ImprintField::ImprintName), "Imprint");
assert_eq!(format!("{}", ImprintField::ImprintUrl), "ImprintURL");
assert_eq!(format!("{}", ImprintField::CrossmarkDoi), "CrossmarkDOI");
assert_eq!(format!("{}", ImprintField::CreatedAt), "CreatedAt");
assert_eq!(format!("{}", ImprintField::UpdatedAt), "UpdatedAt");
}
Expand All @@ -137,6 +145,10 @@ fn test_imprintfield_fromstr() {
ImprintField::from_str("ImprintURL").unwrap(),
ImprintField::ImprintUrl
);
assert_eq!(
ImprintField::from_str("CrossmarkDOI").unwrap(),
ImprintField::CrossmarkDoi
);
assert_eq!(
ImprintField::from_str("CreatedAt").unwrap(),
ImprintField::CreatedAt
Expand Down
1 change: 1 addition & 0 deletions thoth-api/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ table! {
publisher_id -> Uuid,
imprint_name -> Text,
imprint_url -> Nullable<Text>,
crossmark_doi -> Nullable<Text>,
created_at -> Timestamptz,
updated_at -> Timestamptz,
}
Expand Down
61 changes: 61 additions & 0 deletions thoth-app/src/component/imprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use thoth_api::account::model::AccountAccess;
use thoth_api::account::model::AccountDetails;
use thoth_api::model::imprint::ImprintWithPublisher;
use thoth_api::model::publisher::Publisher;
use thoth_api::model::{Doi, DOI_DOMAIN};
use thoth_errors::ThothError;
use uuid::Uuid;
use yew::html;
Expand All @@ -21,6 +22,7 @@ use crate::agent::notification_bus::Request;
use crate::component::delete_dialogue::ConfirmDeleteComponent;
use crate::component::utils::FormPublisherSelect;
use crate::component::utils::FormTextInput;
use crate::component::utils::FormTextInputExtended;
use crate::component::utils::FormUrlInput;
use crate::component::utils::Loader;
use crate::models::imprint::delete_imprint_mutation::DeleteImprintRequest;
Expand Down Expand Up @@ -59,6 +61,9 @@ pub struct ImprintComponent {
notification_bus: NotificationDispatcher,
// Store props value locally in order to test whether it has been updated on props change
resource_access: AccountAccess,
// Track the user-entered DOI string, which may not be validly formatted
crossmark_doi: String,
crossmark_doi_warning: String,
}

#[derive(Default)]
Expand All @@ -78,6 +83,7 @@ pub enum Msg {
ChangePublisher(Uuid),
ChangeImprintName(String),
ChangeImprintUrl(String),
ChangeCrossmarkDoi(String),
}

#[derive(PartialEq, Eq, Properties)]
Expand All @@ -99,6 +105,8 @@ impl Component for ImprintComponent {
let notification_bus = NotificationBus::dispatcher();
let imprint: ImprintWithPublisher = Default::default();
let resource_access = ctx.props().current_user.resource_access.clone();
let crossmark_doi = Default::default();
let crossmark_doi_warning = Default::default();

ctx.link().send_message(Msg::GetImprint);
ctx.link().send_message(Msg::GetPublishers);
Expand All @@ -112,6 +120,8 @@ impl Component for ImprintComponent {
fetch_publishers,
notification_bus,
resource_access,
crossmark_doi,
crossmark_doi_warning,
}
}

Expand Down Expand Up @@ -154,6 +164,13 @@ impl Component for ImprintComponent {
Some(c) => c.to_owned(),
None => Default::default(),
};
// Initialise user-entered DOI variable to match DOI in database
self.crossmark_doi = self
.imprint
.crossmark_doi
.clone()
.unwrap_or_default()
.to_string();
// If user doesn't have permission to edit this object, redirect to dashboard
if let Some(publishers) =
ctx.props().current_user.resource_access.restricted_to()
Expand Down Expand Up @@ -192,6 +209,13 @@ impl Component for ImprintComponent {
FetchState::Fetching(_) => false,
FetchState::Fetched(body) => match &body.data.update_imprint {
Some(i) => {
self.crossmark_doi = self
.imprint
.crossmark_doi
.clone()
.unwrap_or_default()
.to_string();
self.crossmark_doi_warning.clear();
self.notification_bus.send(Request::NotificationBusMsg((
format!("Saved {}", i.imprint_name),
NotificationStatus::Success,
Expand All @@ -216,11 +240,20 @@ impl Component for ImprintComponent {
}
}
Msg::UpdateImprint => {
// Only update the DOI value with the current user-entered string
// if it is validly formatted - otherwise keep the default.
// If no DOI was provided, no format check is required.
if self.crossmark_doi.is_empty() {
self.imprint.crossmark_doi.neq_assign(None);
} else if let Ok(result) = self.crossmark_doi.parse::<Doi>() {
self.imprint.crossmark_doi.neq_assign(Some(result));
}
let body = UpdateImprintRequestBody {
variables: UpdateVariables {
imprint_id: self.imprint.imprint_id,
imprint_name: self.imprint.imprint_name.clone(),
imprint_url: self.imprint.imprint_url.clone(),
crossmark_doi: self.imprint.crossmark_doi.clone(),
publisher_id: self.imprint.publisher.publisher_id,
},
..Default::default()
Expand Down Expand Up @@ -299,6 +332,27 @@ impl Component for ImprintComponent {
Msg::ChangeImprintUrl(value) => {
self.imprint.imprint_url.neq_assign(value.to_opt_string())
}
Msg::ChangeCrossmarkDoi(value) => {
if self.crossmark_doi.neq_assign(value.trim().to_owned()) {
// If DOI is not correctly formatted, display a warning.
// Don't update self.imprint.crossmark_doi yet, as user may later
// overwrite a new valid value with an invalid one.
self.crossmark_doi_warning.clear();
match self.crossmark_doi.parse::<Doi>() {
Err(e) => {
match e {
// If no DOI was provided, no warning is required.
ThothError::DoiEmptyError => {}
_ => self.crossmark_doi_warning = e.to_string(),
}
}
Ok(value) => self.crossmark_doi = value.to_string(),
}
true
} else {
false
}
}
}
}

Expand Down Expand Up @@ -360,6 +414,13 @@ impl Component for ImprintComponent {
value={ self.imprint.imprint_url.clone() }
oninput={ ctx.link().callback(|e: InputEvent| Msg::ChangeImprintUrl(e.to_value())) }
/>
<FormTextInputExtended
label = "Crossmark DOI"
statictext={ DOI_DOMAIN }
value={ self.crossmark_doi.clone() }
tooltip={ self.crossmark_doi_warning.clone() }
oninput={ ctx.link().callback(|e: InputEvent| Msg::ChangeCrossmarkDoi(e.to_value())) }
/>

<div class="field">
<div class="control">
Expand Down
47 changes: 47 additions & 0 deletions thoth-app/src/component/new_imprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use thoth_api::account::model::AccountAccess;
use thoth_api::account::model::AccountDetails;
use thoth_api::model::imprint::Imprint;
use thoth_api::model::publisher::Publisher;
use thoth_api::model::{Doi, DOI_DOMAIN};
use thoth_errors::ThothError;
use uuid::Uuid;
use yew::html;
Expand All @@ -20,6 +21,7 @@ use crate::agent::notification_bus::NotificationStatus;
use crate::agent::notification_bus::Request;
use crate::component::utils::FormPublisherSelect;
use crate::component::utils::FormTextInput;
use crate::component::utils::FormTextInputExtended;
use crate::component::utils::FormUrlInput;
use crate::models::imprint::create_imprint_mutation::CreateImprintRequest;
use crate::models::imprint::create_imprint_mutation::CreateImprintRequestBody;
Expand All @@ -46,6 +48,9 @@ pub struct NewImprintComponent {
notification_bus: NotificationDispatcher,
// Store props value locally in order to test whether it has been updated on props change
resource_access: AccountAccess,
// Track the user-entered DOI string, which may not be validly formatted
crossmark_doi: String,
crossmark_doi_warning: String,
}

#[derive(Default)]
Expand All @@ -61,6 +66,7 @@ pub enum Msg {
ChangePublisher(Uuid),
ChangeImprintName(String),
ChangeImprintUrl(String),
ChangeCrossmarkDoi(String),
}
#[derive(PartialEq, Eq, Properties)]
pub struct Props {
Expand All @@ -79,6 +85,8 @@ impl Component for NewImprintComponent {
let data: ImprintFormData = Default::default();
let fetch_publishers: FetchPublishers = Default::default();
let resource_access = ctx.props().current_user.resource_access.clone();
let crossmark_doi = Default::default();
let crossmark_doi_warning = Default::default();

ctx.link().send_message(Msg::GetPublishers);

Expand All @@ -90,6 +98,8 @@ impl Component for NewImprintComponent {
fetch_publishers,
notification_bus,
resource_access,
crossmark_doi,
crossmark_doi_warning,
}
}

Expand Down Expand Up @@ -154,10 +164,19 @@ impl Component for NewImprintComponent {
}
}
Msg::CreateImprint => {
// Only update the DOI value with the current user-entered string
// if it is validly formatted - otherwise keep the default.
// If no DOI was provided, no format check is required.
if self.crossmark_doi.is_empty() {
self.imprint.crossmark_doi.neq_assign(None);
} else if let Ok(result) = self.crossmark_doi.parse::<Doi>() {
self.imprint.crossmark_doi.neq_assign(Some(result));
}
let body = CreateImprintRequestBody {
variables: Variables {
imprint_name: self.imprint.imprint_name.clone(),
imprint_url: self.imprint.imprint_url.clone(),
crossmark_doi: self.imprint.crossmark_doi.clone(),
publisher_id: self.publisher_id,
},
..Default::default()
Expand All @@ -178,6 +197,27 @@ impl Component for NewImprintComponent {
Msg::ChangeImprintUrl(value) => {
self.imprint.imprint_url.neq_assign(value.to_opt_string())
}
Msg::ChangeCrossmarkDoi(value) => {
if self.crossmark_doi.neq_assign(value.trim().to_owned()) {
// If DOI is not correctly formatted, display a warning.
// Don't update self.imprint.crossmark_doi yet, as user may later
// overwrite a new valid value with an invalid one.
self.crossmark_doi_warning.clear();
match self.crossmark_doi.parse::<Doi>() {
Err(e) => {
match e {
// If no DOI was provided, no warning is required.
ThothError::DoiEmptyError => {}
_ => self.crossmark_doi_warning = e.to_string(),
}
}
Ok(value) => self.crossmark_doi = value.to_string(),
}
true
} else {
false
}
}
}
}

Expand Down Expand Up @@ -228,6 +268,13 @@ impl Component for NewImprintComponent {
value={ self.imprint.imprint_url.clone() }
oninput={ ctx.link().callback(|e: InputEvent| Msg::ChangeImprintUrl(e.to_value())) }
/>
<FormTextInputExtended
label = "Crossmark DOI"
statictext={ DOI_DOMAIN }
value={ self.crossmark_doi.clone() }
tooltip={ self.crossmark_doi_warning.clone() }
oninput={ ctx.link().callback(|e: InputEvent| Msg::ChangeCrossmarkDoi(e.to_value())) }
/>

<div class="field">
<div class="control">
Expand Down
Loading

0 comments on commit fb42f2f

Please sign in to comment.