diff --git a/CHANGELOG.md b/CHANGELOG.md index a1dc8ef8..67e2dabd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to thoth will be documented in this file. 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). +## [[0.4.7]](https://github.com/thoth-pub/thoth/releases/tag/v0.4.7) - 2021-10-04 +### Added + - [#43](https://github.com/thoth-pub/thoth/issues/43), [#49](https://github.com/thoth-pub/thoth/issues/49) - Implement EBSCO Host's ONIX 2.1 specification + - [#44](https://github.com/thoth-pub/thoth/issues/44) - Implement JSTOR's ONIX 3.0 specification + - [#253](https://github.com/thoth-pub/thoth/issues/253) - Implement Project MUSE ONIX specification tests + +### Changed + - [#242](https://github.com/thoth-pub/thoth/issues/242) - Move API models to object-specific subdirectories + - [#274](https://github.com/thoth-pub/thoth/issues/274) - Add width/height units to CSV specification + - [#263](https://github.com/thoth-pub/thoth/issues/263) - Add `Doi`, `Isbn` and `Orcid` types to client schema + ## [[0.4.6]](https://github.com/thoth-pub/thoth/releases/tag/v0.4.6) - 2021-09-02 ### Added - [#88](https://github.com/thoth-pub/thoth/issues/88) - Implement KBART specification @@ -15,7 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - [#271](https://github.com/thoth-pub/thoth/issues/271) - Make filter parameter optional in `subjectCount` - ## [[0.4.5]](https://github.com/thoth-pub/thoth/releases/tag/v0.4.5) - 2021-08-12 ### Added - [#259](https://github.com/thoth-pub/thoth/issues/259) - Units selection dropdown to Work and NewWork pages, which updates the Width/Height display on change diff --git a/Cargo.lock b/Cargo.lock index 56ca9929..96c13bca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3936,7 +3936,7 @@ dependencies = [ [[package]] name = "thoth" -version = "0.4.6" +version = "0.4.7" dependencies = [ "cargo-husky", "clap", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "thoth-api" -version = "0.4.6" +version = "0.4.7" dependencies = [ "actix-web", "argon2rs", @@ -3980,7 +3980,7 @@ dependencies = [ [[package]] name = "thoth-api-server" -version = "0.4.6" +version = "0.4.7" dependencies = [ "actix-cors", "actix-identity", @@ -3995,7 +3995,7 @@ dependencies = [ [[package]] name = "thoth-app" -version = "0.4.6" +version = "0.4.7" dependencies = [ "anyhow", "chrono", @@ -4018,7 +4018,7 @@ dependencies = [ [[package]] name = "thoth-app-server" -version = "0.4.6" +version = "0.4.7" dependencies = [ "actix-cors", "actix-web", @@ -4027,7 +4027,7 @@ dependencies = [ [[package]] name = "thoth-client" -version = "0.4.6" +version = "0.4.7" dependencies = [ "chrono", "graphql_client", @@ -4041,7 +4041,7 @@ dependencies = [ [[package]] name = "thoth-errors" -version = "0.4.6" +version = "0.4.7" dependencies = [ "actix-web", "csv", @@ -4056,7 +4056,7 @@ dependencies = [ [[package]] name = "thoth-export-server" -version = "0.4.6" +version = "0.4.7" dependencies = [ "actix-cors", "actix-web", diff --git a/Cargo.toml b/Cargo.toml index 230da6ad..cbe02a96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" @@ -16,11 +16,11 @@ maintenance = { status = "actively-developed" } members = ["thoth-api", "thoth-api-server", "thoth-app", "thoth-app-server", "thoth-client", "thoth-errors", "thoth-export-server"] [dependencies] -thoth-api = { version = "0.4.6", path = "thoth-api", features = ["backend"] } -thoth-api-server = { version = "0.4.6", path = "thoth-api-server" } -thoth-app-server = { version = "0.4.6", path = "thoth-app-server" } -thoth-errors = { version = "0.4.6", path = "thoth-errors" } -thoth-export-server = { version = "0.4.6", path = "thoth-export-server" } +thoth-api = { version = "0.4.7", path = "thoth-api", features = ["backend"] } +thoth-api-server = { version = "0.4.7", path = "thoth-api-server" } +thoth-app-server = { version = "0.4.7", path = "thoth-app-server" } +thoth-errors = { version = "0.4.7", path = "thoth-errors" } +thoth-export-server = { version = "0.4.7", path = "thoth-export-server" } clap = "2.33.3" dialoguer = "0.7.1" dotenv = "0.9.0" diff --git a/diesel.toml b/diesel.toml index f38024ab..0b29ac61 100644 --- a/diesel.toml +++ b/diesel.toml @@ -5,13 +5,13 @@ file = "src/schema.rs" import_types = [ "diesel::sql_types::*", - "crate::contribution::model::Contribution_type", - "crate::work::model::Work_type", - "crate::work::model::Work_status", - "crate::publication::model::Publication_type", - "crate::language::model::Language_relation", - "crate::language::model::Language_code", - "crate::series::model::Series_type", - "crate::price::model::Currency_code", - "crate::subject::model::Subject_type" + "crate::model::contribution::Contribution_type", + "crate::model::work::Work_type", + "crate::model::work::Work_status", + "crate::model::publication::Publication_type", + "crate::model::language::Language_relation", + "crate::model::language::Language_code", + "crate::model::series::Series_type", + "crate::model::price::Currency_code", + "crate::model::subject::Subject_type" ] diff --git a/thoth-api-server/Cargo.toml b/thoth-api-server/Cargo.toml index ef597e7c..6b7506d3 100644 --- a/thoth-api-server/Cargo.toml +++ b/thoth-api-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-api-server" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" @@ -9,8 +9,8 @@ repository = "https://github.com/thoth-pub/thoth" readme = "README.md" [dependencies] -thoth-api = { version = "0.4.6", path = "../thoth-api", features = ["backend"] } -thoth-errors = { version = "0.4.6", path = "../thoth-errors" } +thoth-api = { version = "0.4.7", path = "../thoth-api", features = ["backend"] } +thoth-errors = { version = "0.4.7", path = "../thoth-errors" } actix-web = "3.3.2" actix-cors = "0.5.4" actix-identity = "0.3.1" diff --git a/thoth-api/Cargo.toml b/thoth-api/Cargo.toml index 028172f3..c3789716 100644 --- a/thoth-api/Cargo.toml +++ b/thoth-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-api" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" @@ -16,7 +16,7 @@ maintenance = { status = "actively-developed" } backend = ["diesel", "diesel-derive-enum", "diesel_migrations", "futures", "actix-web"] [dependencies] -thoth-errors = { version = "0.4.6", path = "../thoth-errors" } +thoth-errors = { version = "0.4.7", path = "../thoth-errors" } actix-web = { version = "3.3.2", optional = true } argon2rs = "0.2.5" isbn2 = "0.4.0" diff --git a/thoth-api/src/account/service.rs b/thoth-api/src/account/service.rs index 4401b291..b2cc1e58 100644 --- a/thoth-api/src/account/service.rs +++ b/thoth-api/src/account/service.rs @@ -10,7 +10,7 @@ use crate::account::model::NewPublisherAccount; use crate::account::model::PublisherAccount; use crate::account::util::verify; use crate::db::PgPool; -use crate::publisher::model::Publisher; +use crate::model::publisher::Publisher; use thoth_errors::{ThothError, ThothResult}; pub fn login(user_email: &str, user_password: &str, pool: &PgPool) -> ThothResult { diff --git a/thoth-api/src/contribution/mod.rs b/thoth-api/src/contribution/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/contribution/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/contributor/mod.rs b/thoth-api/src/contributor/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/contributor/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/funder/mod.rs b/thoth-api/src/funder/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/funder/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/funding/mod.rs b/thoth-api/src/funding/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/funding/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/graphql/model.rs b/thoth-api/src/graphql/model.rs index a6e3e6d6..4a1d1083 100644 --- a/thoth-api/src/graphql/model.rs +++ b/thoth-api/src/graphql/model.rs @@ -6,14 +6,20 @@ use uuid::Uuid; use crate::account::model::AccountAccess; use crate::account::model::DecodedToken; -use crate::contribution::model::*; -use crate::contributor::model::*; use crate::db::PgPool; -use crate::funder::model::*; -use crate::funding::model::*; -use crate::imprint::model::*; -use crate::issue::model::*; -use crate::language::model::*; +use crate::model::contribution::*; +use crate::model::contributor::*; +use crate::model::funder::*; +use crate::model::funding::*; +use crate::model::imprint::*; +use crate::model::issue::*; +use crate::model::language::*; +use crate::model::price::*; +use crate::model::publication::*; +use crate::model::publisher::*; +use crate::model::series::*; +use crate::model::subject::*; +use crate::model::work::*; use crate::model::Convert; use crate::model::Crud; use crate::model::Doi; @@ -21,12 +27,6 @@ use crate::model::Isbn; use crate::model::LengthUnit; use crate::model::Orcid; use crate::model::Timestamp; -use crate::price::model::*; -use crate::publication::model::*; -use crate::publisher::model::*; -use crate::series::model::*; -use crate::subject::model::*; -use crate::work::model::*; use thoth_errors::{ThothError, ThothResult}; use super::utils::Direction; diff --git a/thoth-api/src/imprint/mod.rs b/thoth-api/src/imprint/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/imprint/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/issue/mod.rs b/thoth-api/src/issue/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/issue/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/language/mod.rs b/thoth-api/src/language/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/language/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/lib.rs b/thoth-api/src/lib.rs index ee1b69c7..414ccb46 100644 --- a/thoth-api/src/lib.rs +++ b/thoth-api/src/lib.rs @@ -14,26 +14,13 @@ extern crate dotenv; extern crate juniper; pub mod account; -pub mod contribution; -pub mod contributor; #[cfg(feature = "backend")] pub mod db; -pub mod funder; -pub mod funding; pub mod graphql; -pub mod imprint; -pub mod issue; -pub mod language; #[macro_use] pub mod model; -pub mod price; -pub mod publication; -pub mod publisher; #[cfg(feature = "backend")] mod schema; -pub mod series; -pub mod subject; -pub mod work; macro_rules! apis { ($($name:ident => $content:expr,)*) => ( diff --git a/thoth-api/src/contribution/crud.rs b/thoth-api/src/model/contribution/crud.rs similarity index 99% rename from thoth-api/src/contribution/crud.rs rename to thoth-api/src/model/contribution/crud.rs index ca9e6fdb..c5a95b00 100644 --- a/thoth-api/src/contribution/crud.rs +++ b/thoth-api/src/model/contribution/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ Contribution, ContributionField, ContributionHistory, ContributionType, NewContribution, NewContributionHistory, PatchContribution, }; @@ -159,7 +159,7 @@ impl Crud for Contribution { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::work::model::Work::from_id(db, &self.work_id)?.publisher_id(db) + crate::model::work::Work::from_id(db, &self.work_id)?.publisher_id(db) } crud_methods!(contribution::table, contribution::dsl::contribution); diff --git a/thoth-api/src/contribution/model.rs b/thoth-api/src/model/contribution/mod.rs similarity index 98% rename from thoth-api/src/contribution/model.rs rename to thoth-api/src/model/contribution/mod.rs index 25d6db37..42f823ee 100644 --- a/thoth-api/src/contribution/model.rs +++ b/thoth-api/src/model/contribution/mod.rs @@ -3,12 +3,12 @@ use strum::Display; use strum::EnumString; use uuid::Uuid; +use crate::model::work::WorkWithRelations; use crate::model::Timestamp; #[cfg(feature = "backend")] use crate::schema::contribution; #[cfg(feature = "backend")] use crate::schema::contribution_history; -use crate::work::model::WorkWithRelations; #[cfg_attr(feature = "backend", derive(DbEnum, juniper::GraphQLEnum))] #[cfg_attr(feature = "backend", DieselType = "Contribution_type")] @@ -216,3 +216,6 @@ fn test_contributiontype_fromstr() { assert!(ContributionType::from_str("Juggler").is_err()); assert!(ContributionType::from_str("Supervisor").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/contributor/crud.rs b/thoth-api/src/model/contributor/crud.rs similarity index 99% rename from thoth-api/src/contributor/crud.rs rename to thoth-api/src/model/contributor/crud.rs index 3737b6e6..26674225 100644 --- a/thoth-api/src/contributor/crud.rs +++ b/thoth-api/src/model/contributor/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ Contributor, ContributorField, ContributorHistory, ContributorOrderBy, NewContributor, NewContributorHistory, PatchContributor, }; diff --git a/thoth-api/src/contributor/model.rs b/thoth-api/src/model/contributor/mod.rs similarity index 99% rename from thoth-api/src/contributor/model.rs rename to thoth-api/src/model/contributor/mod.rs index 1a8c9e60..af373977 100644 --- a/thoth-api/src/contributor/model.rs +++ b/thoth-api/src/model/contributor/mod.rs @@ -172,3 +172,6 @@ fn test_contributorfield_fromstr() { assert!(ContributorField::from_str("Biography").is_err()); assert!(ContributorField::from_str("Institution").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/funder/crud.rs b/thoth-api/src/model/funder/crud.rs similarity index 99% rename from thoth-api/src/funder/crud.rs rename to thoth-api/src/model/funder/crud.rs index 8f19bfa7..a6fbe085 100644 --- a/thoth-api/src/funder/crud.rs +++ b/thoth-api/src/model/funder/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ Funder, FunderField, FunderHistory, FunderOrderBy, NewFunder, NewFunderHistory, PatchFunder, }; use crate::graphql::utils::Direction; diff --git a/thoth-api/src/funder/model.rs b/thoth-api/src/model/funder/mod.rs similarity index 98% rename from thoth-api/src/funder/model.rs rename to thoth-api/src/model/funder/mod.rs index d96e7725..ca0dad44 100644 --- a/thoth-api/src/funder/model.rs +++ b/thoth-api/src/model/funder/mod.rs @@ -146,3 +146,6 @@ fn test_funderfield_fromstr() { assert!(FunderField::from_str("Website").is_err()); assert!(FunderField::from_str("Fundings").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/funding/crud.rs b/thoth-api/src/model/funding/crud.rs similarity index 97% rename from thoth-api/src/funding/crud.rs rename to thoth-api/src/model/funding/crud.rs index 538885ef..f4a9d4e2 100644 --- a/thoth-api/src/funding/crud.rs +++ b/thoth-api/src/model/funding/crud.rs @@ -1,6 +1,4 @@ -use super::model::{ - Funding, FundingField, FundingHistory, NewFunding, NewFundingHistory, PatchFunding, -}; +use super::{Funding, FundingField, FundingHistory, NewFunding, NewFundingHistory, PatchFunding}; use crate::graphql::model::FundingOrderBy; use crate::graphql::utils::Direction; use crate::model::{Crud, DbInsert, HistoryEntry}; @@ -136,7 +134,7 @@ impl Crud for Funding { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::work::model::Work::from_id(db, &self.work_id)?.publisher_id(db) + crate::model::work::Work::from_id(db, &self.work_id)?.publisher_id(db) } crud_methods!(funding::table, funding::dsl::funding); diff --git a/thoth-api/src/funding/model.rs b/thoth-api/src/model/funding/mod.rs similarity index 96% rename from thoth-api/src/funding/model.rs rename to thoth-api/src/model/funding/mod.rs index 29959fe5..dab2f5ef 100644 --- a/thoth-api/src/funding/model.rs +++ b/thoth-api/src/model/funding/mod.rs @@ -1,13 +1,13 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::funder::model::Funder; +use crate::model::funder::Funder; +use crate::model::work::WorkWithRelations; use crate::model::Timestamp; #[cfg(feature = "backend")] use crate::schema::funding; #[cfg(feature = "backend")] use crate::schema::funding_history; -use crate::work::model::WorkWithRelations; #[cfg_attr( feature = "backend", @@ -114,3 +114,6 @@ pub struct NewFundingHistory { pub account_id: Uuid, pub data: serde_json::Value, } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/imprint/crud.rs b/thoth-api/src/model/imprint/crud.rs similarity index 99% rename from thoth-api/src/imprint/crud.rs rename to thoth-api/src/model/imprint/crud.rs index 3b1ba6af..61cbb96c 100644 --- a/thoth-api/src/imprint/crud.rs +++ b/thoth-api/src/model/imprint/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ Imprint, ImprintField, ImprintHistory, ImprintOrderBy, NewImprint, NewImprintHistory, PatchImprint, }; diff --git a/thoth-api/src/imprint/model.rs b/thoth-api/src/model/imprint/mod.rs similarity index 98% rename from thoth-api/src/imprint/model.rs rename to thoth-api/src/model/imprint/mod.rs index 45b26039..e4ebd867 100644 --- a/thoth-api/src/imprint/model.rs +++ b/thoth-api/src/model/imprint/mod.rs @@ -5,8 +5,8 @@ use strum::EnumString; use uuid::Uuid; use crate::graphql::utils::Direction; +use crate::model::publisher::Publisher; use crate::model::Timestamp; -use crate::publisher::model::Publisher; #[cfg(feature = "backend")] use crate::schema::imprint; #[cfg(feature = "backend")] @@ -155,3 +155,6 @@ fn test_imprintfield_fromstr() { assert!(ImprintField::from_str("Publisher").is_err()); assert!(ImprintField::from_str("Website").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/issue/crud.rs b/thoth-api/src/model/issue/crud.rs similarity index 97% rename from thoth-api/src/issue/crud.rs rename to thoth-api/src/model/issue/crud.rs index b52b1449..0a386a13 100644 --- a/thoth-api/src/issue/crud.rs +++ b/thoth-api/src/model/issue/crud.rs @@ -1,4 +1,4 @@ -use super::model::{Issue, IssueField, IssueHistory, NewIssue, NewIssueHistory, PatchIssue}; +use super::{Issue, IssueField, IssueHistory, NewIssue, NewIssueHistory, PatchIssue}; use crate::graphql::model::IssueOrderBy; use crate::graphql::utils::Direction; use crate::model::{Crud, DbInsert, HistoryEntry}; @@ -114,7 +114,7 @@ impl Crud for Issue { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::work::model::Work::from_id(db, &self.work_id)?.publisher_id(db) + crate::model::work::Work::from_id(db, &self.work_id)?.publisher_id(db) } crud_methods!(issue::table, issue::dsl::issue); diff --git a/thoth-api/src/issue/model.rs b/thoth-api/src/model/issue/mod.rs similarity index 96% rename from thoth-api/src/issue/model.rs rename to thoth-api/src/model/issue/mod.rs index 50ad9a54..5fb9d37b 100644 --- a/thoth-api/src/issue/model.rs +++ b/thoth-api/src/model/issue/mod.rs @@ -1,12 +1,12 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; +use crate::model::series::SeriesWithImprint; use crate::model::Timestamp; #[cfg(feature = "backend")] use crate::schema::issue; #[cfg(feature = "backend")] use crate::schema::issue_history; -use crate::series::model::SeriesWithImprint; #[cfg_attr( feature = "backend", @@ -95,3 +95,6 @@ impl Default for IssueWithSeries { } } } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/language/crud.rs b/thoth-api/src/model/language/crud.rs similarity index 98% rename from thoth-api/src/language/crud.rs rename to thoth-api/src/model/language/crud.rs index ab102364..6b9ad0a9 100644 --- a/thoth-api/src/language/crud.rs +++ b/thoth-api/src/model/language/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ Language, LanguageCode, LanguageField, LanguageHistory, LanguageRelation, NewLanguage, NewLanguageHistory, PatchLanguage, }; @@ -131,7 +131,7 @@ impl Crud for Language { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::work::model::Work::from_id(db, &self.work_id)?.publisher_id(db) + crate::model::work::Work::from_id(db, &self.work_id)?.publisher_id(db) } crud_methods!(language::table, language::dsl::language); diff --git a/thoth-api/src/language/model.rs b/thoth-api/src/model/language/mod.rs similarity index 99% rename from thoth-api/src/language/model.rs rename to thoth-api/src/model/language/mod.rs index 074329cb..94657655 100644 --- a/thoth-api/src/language/model.rs +++ b/thoth-api/src/model/language/mod.rs @@ -1646,3 +1646,6 @@ fn test_languagecode_fromstr() { assert!(LanguageRelation::from_str("ESP").is_err()); assert!(LanguageRelation::from_str("ZZZ").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/model/mod.rs b/thoth-api/src/model/mod.rs index c0a14468..e4a7a559 100644 --- a/thoth-api/src/model/mod.rs +++ b/thoth-api/src/model/mod.rs @@ -311,7 +311,7 @@ where /// ------------- /// /// ```ignore -/// use crate::imprint::model::{NewImprint, PatchImprint, Imprint, NewImprintHistory}; +/// use crate::model::imprint::{NewImprint, PatchImprint, Imprint, NewImprintHistory}; /// use crate::crud_methods; /// use crate::model::Crud; /// @@ -399,7 +399,7 @@ macro_rules! crud_methods { /// ------------- /// /// ```ignore -/// use crate::imprint::model::{ImprintHistory, NewImprintHistory}; +/// use crate::model::imprint::{ImprintHistory, NewImprintHistory}; /// use crate::db_insert; /// use crate::model::DbInsert; /// @@ -728,3 +728,17 @@ fn test_convert_units_from_to() { 190.0 ); } + +pub mod contribution; +pub mod contributor; +pub mod funder; +pub mod funding; +pub mod imprint; +pub mod issue; +pub mod language; +pub mod price; +pub mod publication; +pub mod publisher; +pub mod series; +pub mod subject; +pub mod work; diff --git a/thoth-api/src/price/crud.rs b/thoth-api/src/model/price/crud.rs similarity index 97% rename from thoth-api/src/price/crud.rs rename to thoth-api/src/model/price/crud.rs index 1c202357..161f4652 100644 --- a/thoth-api/src/price/crud.rs +++ b/thoth-api/src/model/price/crud.rs @@ -1,6 +1,4 @@ -use super::model::{ - CurrencyCode, NewPrice, NewPriceHistory, PatchPrice, Price, PriceField, PriceHistory, -}; +use super::{CurrencyCode, NewPrice, NewPriceHistory, PatchPrice, Price, PriceField, PriceHistory}; use crate::graphql::model::PriceOrderBy; use crate::graphql::utils::Direction; use crate::model::{Crud, DbInsert, HistoryEntry}; @@ -122,7 +120,7 @@ impl Crud for Price { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::publication::model::Publication::from_id(db, &self.publication_id)?.publisher_id(db) + crate::model::publication::Publication::from_id(db, &self.publication_id)?.publisher_id(db) } crud_methods!(price::table, price::dsl::price); diff --git a/thoth-api/src/price/model.rs b/thoth-api/src/model/price/mod.rs similarity index 99% rename from thoth-api/src/price/model.rs rename to thoth-api/src/model/price/mod.rs index b082230d..a083eca5 100644 --- a/thoth-api/src/price/model.rs +++ b/thoth-api/src/model/price/mod.rs @@ -1009,3 +1009,6 @@ fn test_currencycode_fromstr() { assert_eq!(CurrencyCode::from_str("ZWN").unwrap(), CurrencyCode::Zwn); assert_eq!(CurrencyCode::from_str("ZWR").unwrap(), CurrencyCode::Zwr); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/publication/crud.rs b/thoth-api/src/model/publication/crud.rs similarity index 99% rename from thoth-api/src/publication/crud.rs rename to thoth-api/src/model/publication/crud.rs index 71047141..729e87c6 100644 --- a/thoth-api/src/publication/crud.rs +++ b/thoth-api/src/model/publication/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ NewPublication, NewPublicationHistory, PatchPublication, Publication, PublicationField, PublicationHistory, PublicationOrderBy, PublicationType, }; @@ -164,7 +164,7 @@ impl Crud for Publication { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::work::model::Work::from_id(db, &self.work_id)?.publisher_id(db) + crate::model::work::Work::from_id(db, &self.work_id)?.publisher_id(db) } crud_methods!(publication::table, publication::dsl::publication); diff --git a/thoth-api/src/publication/model.rs b/thoth-api/src/model/publication/mod.rs similarity index 98% rename from thoth-api/src/publication/model.rs rename to thoth-api/src/model/publication/mod.rs index 1f9013d6..854e9422 100644 --- a/thoth-api/src/publication/model.rs +++ b/thoth-api/src/model/publication/mod.rs @@ -4,14 +4,14 @@ use strum::EnumString; use uuid::Uuid; use crate::graphql::utils::Direction; +use crate::model::price::Price; +use crate::model::work::WorkWithRelations; use crate::model::Isbn; use crate::model::Timestamp; -use crate::price::model::Price; #[cfg(feature = "backend")] use crate::schema::publication; #[cfg(feature = "backend")] use crate::schema::publication_history; -use crate::work::model::WorkWithRelations; #[cfg_attr(feature = "backend", derive(DbEnum, juniper::GraphQLEnum))] #[cfg_attr(feature = "backend", DieselType = "Publication_type")] @@ -259,3 +259,6 @@ fn test_publicationfield_fromstr() { assert!(PublicationField::from_str("Work Title").is_err()); assert!(PublicationField::from_str("Work DOI").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/publisher/crud.rs b/thoth-api/src/model/publisher/crud.rs similarity index 99% rename from thoth-api/src/publisher/crud.rs rename to thoth-api/src/model/publisher/crud.rs index ee087df0..9c5aab36 100644 --- a/thoth-api/src/publisher/crud.rs +++ b/thoth-api/src/model/publisher/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ NewPublisher, NewPublisherHistory, PatchPublisher, Publisher, PublisherField, PublisherHistory, PublisherOrderBy, }; diff --git a/thoth-api/src/publisher/model.rs b/thoth-api/src/model/publisher/mod.rs similarity index 99% rename from thoth-api/src/publisher/model.rs rename to thoth-api/src/model/publisher/mod.rs index 4360f4ae..39d7e263 100644 --- a/thoth-api/src/publisher/model.rs +++ b/thoth-api/src/model/publisher/mod.rs @@ -160,3 +160,6 @@ fn test_publisherfield_fromstr() { assert!(PublisherField::from_str("Website").is_err()); assert!(PublisherField::from_str("Imprint").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/series/crud.rs b/thoth-api/src/model/series/crud.rs similarity index 99% rename from thoth-api/src/series/crud.rs rename to thoth-api/src/model/series/crud.rs index ab439293..db562398 100644 --- a/thoth-api/src/series/crud.rs +++ b/thoth-api/src/model/series/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ NewSeries, NewSeriesHistory, PatchSeries, Series, SeriesField, SeriesHistory, SeriesOrderBy, SeriesType, }; @@ -167,7 +167,7 @@ impl Crud for Series { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::imprint::model::Imprint::from_id(db, &self.imprint_id)?.publisher_id(db) + crate::model::imprint::Imprint::from_id(db, &self.imprint_id)?.publisher_id(db) } crud_methods!(series::table, series::dsl::series); diff --git a/thoth-api/src/series/model.rs b/thoth-api/src/model/series/mod.rs similarity index 98% rename from thoth-api/src/series/model.rs rename to thoth-api/src/model/series/mod.rs index 07e9eefb..dcfd88bf 100644 --- a/thoth-api/src/series/model.rs +++ b/thoth-api/src/model/series/mod.rs @@ -5,7 +5,7 @@ use strum::EnumString; use uuid::Uuid; use crate::graphql::utils::Direction; -use crate::imprint::model::ImprintWithPublisher; +use crate::model::imprint::ImprintWithPublisher; use crate::model::Timestamp; #[cfg(feature = "backend")] use crate::schema::series; @@ -235,3 +235,6 @@ fn test_seriesfield_fromstr() { assert!(SeriesField::from_str("Publisher").is_err()); assert!(SeriesField::from_str("Issues").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/subject/crud.rs b/thoth-api/src/model/subject/crud.rs similarity index 98% rename from thoth-api/src/subject/crud.rs rename to thoth-api/src/model/subject/crud.rs index 757eea81..cc856b6f 100644 --- a/thoth-api/src/subject/crud.rs +++ b/thoth-api/src/model/subject/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ NewSubject, NewSubjectHistory, PatchSubject, Subject, SubjectField, SubjectHistory, SubjectType, }; use crate::graphql::model::SubjectOrderBy; @@ -131,7 +131,7 @@ impl Crud for Subject { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::work::model::Work::from_id(db, &self.work_id)?.publisher_id(db) + crate::model::work::Work::from_id(db, &self.work_id)?.publisher_id(db) } crud_methods!(subject::table, subject::dsl::subject); diff --git a/thoth-api/src/subject/model.rs b/thoth-api/src/model/subject/mod.rs similarity index 99% rename from thoth-api/src/subject/model.rs rename to thoth-api/src/model/subject/mod.rs index 0f7af2c7..a9b25e35 100644 --- a/thoth-api/src/subject/model.rs +++ b/thoth-api/src/model/subject/mod.rs @@ -7708,3 +7708,6 @@ static THEMA_CODES: Map<&'static str, &'static str> = phf_map! { "6X" => "Styles (XYZ)", "6XZ" => "Zydeco" }; + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/work/crud.rs b/thoth-api/src/model/work/crud.rs similarity index 99% rename from thoth-api/src/work/crud.rs rename to thoth-api/src/model/work/crud.rs index dd164e8e..65efd142 100644 --- a/thoth-api/src/work/crud.rs +++ b/thoth-api/src/model/work/crud.rs @@ -1,4 +1,4 @@ -use super::model::{ +use super::{ NewWork, NewWorkHistory, PatchWork, Work, WorkField, WorkHistory, WorkOrderBy, WorkStatus, WorkType, }; @@ -428,7 +428,7 @@ impl Crud for Work { } fn publisher_id(&self, db: &crate::db::PgPool) -> ThothResult { - crate::imprint::model::Imprint::from_id(db, &self.imprint_id)?.publisher_id(db) + crate::model::imprint::Imprint::from_id(db, &self.imprint_id)?.publisher_id(db) } crud_methods!(work::table, work::dsl::work); diff --git a/thoth-api/src/work/model.rs b/thoth-api/src/model/work/mod.rs similarity index 98% rename from thoth-api/src/work/model.rs rename to thoth-api/src/model/work/mod.rs index 0f1861bb..a206d991 100644 --- a/thoth-api/src/work/model.rs +++ b/thoth-api/src/model/work/mod.rs @@ -4,20 +4,20 @@ use strum::Display; use strum::EnumString; use uuid::Uuid; -use crate::contribution::model::Contribution; -use crate::funding::model::FundingWithFunder; use crate::graphql::utils::Direction; -use crate::imprint::model::ImprintWithPublisher; -use crate::issue::model::IssueWithSeries; -use crate::language::model::Language; +use crate::model::contribution::Contribution; +use crate::model::funding::FundingWithFunder; +use crate::model::imprint::ImprintWithPublisher; +use crate::model::issue::IssueWithSeries; +use crate::model::language::Language; +use crate::model::publication::Publication; +use crate::model::subject::Subject; use crate::model::Doi; use crate::model::Timestamp; -use crate::publication::model::Publication; #[cfg(feature = "backend")] use crate::schema::work; #[cfg(feature = "backend")] use crate::schema::work_history; -use crate::subject::model::Subject; #[cfg_attr(feature = "backend", derive(DbEnum, juniper::GraphQLEnum))] #[cfg_attr(feature = "backend", DieselType = "Work_type")] @@ -663,3 +663,6 @@ fn test_workfield_fromstr() { assert!(WorkField::from_str("Contributors").is_err()); assert!(WorkField::from_str("Publisher").is_err()); } + +#[cfg(feature = "backend")] +pub mod crud; diff --git a/thoth-api/src/price/mod.rs b/thoth-api/src/price/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/price/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/publication/mod.rs b/thoth-api/src/publication/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/publication/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/publisher/mod.rs b/thoth-api/src/publisher/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/publisher/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/schema.rs b/thoth-api/src/schema.rs index 0caba078..59d64a3c 100644 --- a/thoth-api/src/schema.rs +++ b/thoth-api/src/schema.rs @@ -19,7 +19,7 @@ table! { table! { use diesel::sql_types::*; - use crate::contribution::model::Contribution_type; + use crate::model::contribution::Contribution_type; contribution (contribution_id) { contribution_id -> Uuid, @@ -182,8 +182,8 @@ table! { table! { use diesel::sql_types::*; - use crate::language::model::Language_relation; - use crate::language::model::Language_code; + use crate::model::language::Language_relation; + use crate::model::language::Language_code; language (language_id) { language_id -> Uuid, @@ -210,7 +210,7 @@ table! { table! { use diesel::sql_types::*; - use crate::price::model::Currency_code; + use crate::model::price::Currency_code; price (price_id) { price_id -> Uuid, @@ -236,7 +236,7 @@ table! { table! { use diesel::sql_types::*; - use crate::publication::model::Publication_type; + use crate::model::publication::Publication_type; publication (publication_id) { publication_id -> Uuid, @@ -300,7 +300,7 @@ table! { table! { use diesel::sql_types::*; - use crate::series::model::Series_type; + use crate::model::series::Series_type; series (series_id) { series_id -> Uuid, @@ -329,7 +329,7 @@ table! { table! { use diesel::sql_types::*; - use crate::subject::model::Subject_type; + use crate::model::subject::Subject_type; subject (subject_id) { subject_id -> Uuid, @@ -356,8 +356,8 @@ table! { table! { use diesel::sql_types::*; - use crate::work::model::Work_type; - use crate::work::model::Work_status; + use crate::model::work::Work_type; + use crate::model::work::Work_status; work (work_id) { work_id -> Uuid, diff --git a/thoth-api/src/series/mod.rs b/thoth-api/src/series/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/series/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/subject/mod.rs b/thoth-api/src/subject/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/subject/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-api/src/work/mod.rs b/thoth-api/src/work/mod.rs deleted file mode 100644 index 5abe36b1..00000000 --- a/thoth-api/src/work/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "backend")] -pub mod crud; -pub mod model; diff --git a/thoth-app-server/Cargo.toml b/thoth-app-server/Cargo.toml index 70a641d1..db3648e1 100644 --- a/thoth-app-server/Cargo.toml +++ b/thoth-app-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-app-server" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" diff --git a/thoth-app/Cargo.toml b/thoth-app/Cargo.toml index c5d63e9a..5527080b 100644 --- a/thoth-app/Cargo.toml +++ b/thoth-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-app" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" @@ -33,5 +33,5 @@ serde = { version = "1.0.115", features = ["derive"] } serde_json = "1.0" url = "2.1.1" uuid = { version = "0.7", features = ["serde", "v4"] } -thoth-api = { version = "0.4.6", path = "../thoth-api" } -thoth-errors = { version = "0.4.6", path = "../thoth-errors" } +thoth-api = { version = "0.4.7", path = "../thoth-api" } +thoth-errors = { version = "0.4.7", path = "../thoth-errors" } diff --git a/thoth-app/manifest.json b/thoth-app/manifest.json index 21caea6b..83546eaa 100644 --- a/thoth-app/manifest.json +++ b/thoth-app/manifest.json @@ -9,7 +9,7 @@ "start_url": "/?homescreen=1", "background_color": "#ffffff", "theme_color": "#ffdd57", - "version": "0.4.6", + "version": "0.4.7", "icons": [ { "src": "\/android-icon-36x36.png", diff --git a/thoth-app/src/component/catalogue.rs b/thoth-app/src/component/catalogue.rs index 1280b7fc..cea10a90 100644 --- a/thoth-app/src/component/catalogue.rs +++ b/thoth-app/src/component/catalogue.rs @@ -1,4 +1,4 @@ -use thoth_api::work::model::WorkWithRelations; +use thoth_api::model::work::WorkWithRelations; use yew::html; use yew::prelude::Component; use yew::prelude::FocusEvent; diff --git a/thoth-app/src/component/contributions_form.rs b/thoth-app/src/component/contributions_form.rs index 17069c8e..0f45607c 100644 --- a/thoth-app/src/component/contributions_form.rs +++ b/thoth-app/src/component/contributions_form.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use thoth_api::contribution::model::Contribution; -use thoth_api::contribution::model::ContributionType; -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contribution::Contribution; +use thoth_api::model::contribution::ContributionType; +use thoth_api::model::contributor::Contributor; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/contributor.rs b/thoth-app/src/component/contributor.rs index 7bd40313..90de1f8c 100644 --- a/thoth-app/src/component/contributor.rs +++ b/thoth-app/src/component/contributor.rs @@ -1,5 +1,5 @@ -use thoth_api::contribution::model::ContributionWithWork; -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contribution::ContributionWithWork; +use thoth_api::model::contributor::Contributor; use thoth_api::model::{Orcid, ORCID_DOMAIN}; use thoth_errors::ThothError; use uuid::Uuid; diff --git a/thoth-app/src/component/contributors.rs b/thoth-app/src/component/contributors.rs index c8b92117..afc8977e 100644 --- a/thoth-app/src/component/contributors.rs +++ b/thoth-app/src/component/contributors.rs @@ -3,9 +3,9 @@ use crate::models::contributor::contributors_query::ContributorsRequestBody; use crate::models::contributor::contributors_query::FetchActionContributors; use crate::models::contributor::contributors_query::FetchContributors; use crate::models::contributor::contributors_query::Variables; -use thoth_api::contributor::model::Contributor; -use thoth_api::contributor::model::ContributorField; -use thoth_api::contributor::model::ContributorOrderBy; +use thoth_api::model::contributor::Contributor; +use thoth_api::model::contributor::ContributorField; +use thoth_api::model::contributor::ContributorOrderBy; pagination_component! { ContributorsComponent, diff --git a/thoth-app/src/component/funder.rs b/thoth-app/src/component/funder.rs index e6136745..e0061ca2 100644 --- a/thoth-app/src/component/funder.rs +++ b/thoth-app/src/component/funder.rs @@ -1,5 +1,5 @@ -use thoth_api::funder::model::Funder; -use thoth_api::funding::model::FundingWithWork; +use thoth_api::model::funder::Funder; +use thoth_api::model::funding::FundingWithWork; use thoth_api::model::{Doi, DOI_DOMAIN}; use thoth_errors::ThothError; use uuid::Uuid; diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index 853777e5..649546ae 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -3,9 +3,9 @@ use crate::models::funder::funders_query::FetchFunders; use crate::models::funder::funders_query::FundersRequest; use crate::models::funder::funders_query::FundersRequestBody; use crate::models::funder::funders_query::Variables; -use thoth_api::funder::model::Funder; -use thoth_api::funder::model::FunderField; -use thoth_api::funder::model::FunderOrderBy; +use thoth_api::model::funder::Funder; +use thoth_api::model::funder::FunderField; +use thoth_api::model::funder::FunderOrderBy; pagination_component! { FundersComponent, diff --git a/thoth-app/src/component/fundings_form.rs b/thoth-app/src/component/fundings_form.rs index bffdba5c..453e16dd 100644 --- a/thoth-app/src/component/fundings_form.rs +++ b/thoth-app/src/component/fundings_form.rs @@ -1,5 +1,5 @@ -use thoth_api::funder::model::Funder; -use thoth_api::funding::model::FundingWithFunder; +use thoth_api::model::funder::Funder; +use thoth_api::model::funding::FundingWithFunder; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/imprint.rs b/thoth-app/src/component/imprint.rs index 1f86e6e8..7d9bae19 100644 --- a/thoth-app/src/component/imprint.rs +++ b/thoth-app/src/component/imprint.rs @@ -1,6 +1,6 @@ use thoth_api::account::model::AccountDetails; -use thoth_api::imprint::model::ImprintWithPublisher; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::imprint::ImprintWithPublisher; +use thoth_api::model::publisher::Publisher; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/imprints.rs b/thoth-app/src/component/imprints.rs index 1b4e1efe..f0ad913d 100644 --- a/thoth-app/src/component/imprints.rs +++ b/thoth-app/src/component/imprints.rs @@ -3,9 +3,9 @@ use crate::models::imprint::imprints_query::FetchImprints; use crate::models::imprint::imprints_query::ImprintsRequest; use crate::models::imprint::imprints_query::ImprintsRequestBody; use crate::models::imprint::imprints_query::Variables; -use thoth_api::imprint::model::ImprintField; -use thoth_api::imprint::model::ImprintOrderBy; -use thoth_api::imprint::model::ImprintWithPublisher; +use thoth_api::model::imprint::ImprintField; +use thoth_api::model::imprint::ImprintOrderBy; +use thoth_api::model::imprint::ImprintWithPublisher; pagination_component! { ImprintsComponent, diff --git a/thoth-app/src/component/issues_form.rs b/thoth-app/src/component/issues_form.rs index 25c2012e..51c46bed 100644 --- a/thoth-app/src/component/issues_form.rs +++ b/thoth-app/src/component/issues_form.rs @@ -1,6 +1,6 @@ use thoth_api::account::model::AccountDetails; -use thoth_api::issue::model::IssueWithSeries; -use thoth_api::series::model::SeriesWithImprint; +use thoth_api::model::issue::IssueWithSeries; +use thoth_api::model::series::SeriesWithImprint; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/languages_form.rs b/thoth-app/src/component/languages_form.rs index 3262b5db..18a67e66 100644 --- a/thoth-app/src/component/languages_form.rs +++ b/thoth-app/src/component/languages_form.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use thoth_api::language::model::Language; -use thoth_api::language::model::LanguageCode; -use thoth_api::language::model::LanguageRelation; +use thoth_api::model::language::Language; +use thoth_api::model::language::LanguageCode; +use thoth_api::model::language::LanguageRelation; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/new_contributor.rs b/thoth-app/src/component/new_contributor.rs index b3d464ac..e9869161 100644 --- a/thoth-app/src/component/new_contributor.rs +++ b/thoth-app/src/component/new_contributor.rs @@ -1,4 +1,4 @@ -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contributor::Contributor; use thoth_api::model::{Orcid, ORCID_DOMAIN}; use thoth_errors::ThothError; use yew::html; diff --git a/thoth-app/src/component/new_funder.rs b/thoth-app/src/component/new_funder.rs index 8484ef81..2bcc15a3 100644 --- a/thoth-app/src/component/new_funder.rs +++ b/thoth-app/src/component/new_funder.rs @@ -1,4 +1,4 @@ -use thoth_api::funder::model::Funder; +use thoth_api::model::funder::Funder; use thoth_api::model::{Doi, DOI_DOMAIN}; use thoth_errors::ThothError; use yew::html; diff --git a/thoth-app/src/component/new_imprint.rs b/thoth-app/src/component/new_imprint.rs index e7c23b24..89c0d23e 100644 --- a/thoth-app/src/component/new_imprint.rs +++ b/thoth-app/src/component/new_imprint.rs @@ -1,6 +1,6 @@ use thoth_api::account::model::AccountDetails; -use thoth_api::imprint::model::Imprint; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::imprint::Imprint; +use thoth_api::model::publisher::Publisher; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/new_publisher.rs b/thoth-app/src/component/new_publisher.rs index 9a26f330..46892b95 100644 --- a/thoth-app/src/component/new_publisher.rs +++ b/thoth-app/src/component/new_publisher.rs @@ -1,4 +1,4 @@ -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; use yew::html; use yew::prelude::*; use yew::ComponentLink; diff --git a/thoth-app/src/component/new_series.rs b/thoth-app/src/component/new_series.rs index c55ef969..2a6912a4 100644 --- a/thoth-app/src/component/new_series.rs +++ b/thoth-app/src/component/new_series.rs @@ -1,8 +1,8 @@ use std::str::FromStr; use thoth_api::account::model::AccountDetails; -use thoth_api::imprint::model::ImprintWithPublisher; -use thoth_api::series::model::Series; -use thoth_api::series::model::SeriesType; +use thoth_api::model::imprint::ImprintWithPublisher; +use thoth_api::model::series::Series; +use thoth_api::model::series::SeriesType; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/new_work.rs b/thoth-app/src/component/new_work.rs index 0429bce1..2ec5eba0 100644 --- a/thoth-app/src/component/new_work.rs +++ b/thoth-app/src/component/new_work.rs @@ -1,10 +1,10 @@ use std::str::FromStr; use thoth_api::account::model::AccountDetails; -use thoth_api::imprint::model::ImprintWithPublisher; +use thoth_api::model::imprint::ImprintWithPublisher; +use thoth_api::model::work::WorkStatus; +use thoth_api::model::work::WorkType; +use thoth_api::model::work::WorkWithRelations; use thoth_api::model::{Doi, LengthUnit, DOI_DOMAIN}; -use thoth_api::work::model::WorkStatus; -use thoth_api::work::model::WorkType; -use thoth_api::work::model::WorkWithRelations; use thoth_errors::ThothError; use uuid::Uuid; use yew::html; diff --git a/thoth-app/src/component/prices_form.rs b/thoth-app/src/component/prices_form.rs index e5f988b8..1e3f1188 100644 --- a/thoth-app/src/component/prices_form.rs +++ b/thoth-app/src/component/prices_form.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use thoth_api::price::model::CurrencyCode; -use thoth_api::price::model::Price; +use thoth_api::model::price::CurrencyCode; +use thoth_api::model::price::Price; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/publication.rs b/thoth-app/src/component/publication.rs index 12af937e..ca88dfe1 100644 --- a/thoth-app/src/component/publication.rs +++ b/thoth-app/src/component/publication.rs @@ -1,6 +1,6 @@ use thoth_api::account::model::AccountDetails; -use thoth_api::price::model::Price; -use thoth_api::publication::model::PublicationWithRelations; +use thoth_api::model::price::Price; +use thoth_api::model::publication::PublicationWithRelations; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/publications.rs b/thoth-app/src/component/publications.rs index d1f1fa87..c3ceba55 100644 --- a/thoth-app/src/component/publications.rs +++ b/thoth-app/src/component/publications.rs @@ -3,9 +3,9 @@ use crate::models::publication::publications_query::FetchPublications; use crate::models::publication::publications_query::PublicationsRequest; use crate::models::publication::publications_query::PublicationsRequestBody; use crate::models::publication::publications_query::Variables; -use thoth_api::publication::model::PublicationField; -use thoth_api::publication::model::PublicationOrderBy; -use thoth_api::publication::model::PublicationWithRelations; +use thoth_api::model::publication::PublicationField; +use thoth_api::model::publication::PublicationOrderBy; +use thoth_api::model::publication::PublicationWithRelations; pagination_component! { PublicationsComponent, diff --git a/thoth-app/src/component/publications_form.rs b/thoth-app/src/component/publications_form.rs index b468d28e..27d7ebeb 100644 --- a/thoth-app/src/component/publications_form.rs +++ b/thoth-app/src/component/publications_form.rs @@ -1,7 +1,7 @@ use std::str::FromStr; +use thoth_api::model::publication::Publication; +use thoth_api::model::publication::PublicationType; use thoth_api::model::Isbn; -use thoth_api::publication::model::Publication; -use thoth_api::publication::model::PublicationType; use thoth_errors::ThothError; use uuid::Uuid; use yew::html; diff --git a/thoth-app/src/component/publisher.rs b/thoth-app/src/component/publisher.rs index 19569878..aa5d2652 100644 --- a/thoth-app/src/component/publisher.rs +++ b/thoth-app/src/component/publisher.rs @@ -1,5 +1,5 @@ use thoth_api::account::model::AccountDetails; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/publishers.rs b/thoth-app/src/component/publishers.rs index 452b8413..94b57832 100644 --- a/thoth-app/src/component/publishers.rs +++ b/thoth-app/src/component/publishers.rs @@ -3,9 +3,9 @@ use crate::models::publisher::publishers_query::FetchPublishers; use crate::models::publisher::publishers_query::PublishersRequest; use crate::models::publisher::publishers_query::PublishersRequestBody; use crate::models::publisher::publishers_query::Variables; -use thoth_api::publisher::model::Publisher; -use thoth_api::publisher::model::PublisherField; -use thoth_api::publisher::model::PublisherOrderBy; +use thoth_api::model::publisher::Publisher; +use thoth_api::model::publisher::PublisherField; +use thoth_api::model::publisher::PublisherOrderBy; pagination_component! { PublishersComponent, diff --git a/thoth-app/src/component/series.rs b/thoth-app/src/component/series.rs index 8654a663..55111b76 100644 --- a/thoth-app/src/component/series.rs +++ b/thoth-app/src/component/series.rs @@ -1,8 +1,8 @@ use std::str::FromStr; use thoth_api::account::model::AccountDetails; -use thoth_api::imprint::model::ImprintWithPublisher; -use thoth_api::series::model::SeriesType; -use thoth_api::series::model::SeriesWithImprint; +use thoth_api::model::imprint::ImprintWithPublisher; +use thoth_api::model::series::SeriesType; +use thoth_api::model::series::SeriesWithImprint; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/serieses.rs b/thoth-app/src/component/serieses.rs index a25b521a..01b3b021 100644 --- a/thoth-app/src/component/serieses.rs +++ b/thoth-app/src/component/serieses.rs @@ -3,9 +3,9 @@ use crate::models::series::serieses_query::FetchSerieses; use crate::models::series::serieses_query::SeriesesRequest; use crate::models::series::serieses_query::SeriesesRequestBody; use crate::models::series::serieses_query::Variables; -use thoth_api::series::model::SeriesField; -use thoth_api::series::model::SeriesOrderBy; -use thoth_api::series::model::SeriesWithImprint; +use thoth_api::model::series::SeriesField; +use thoth_api::model::series::SeriesOrderBy; +use thoth_api::model::series::SeriesWithImprint; pagination_component! { SeriesesComponent, diff --git a/thoth-app/src/component/subjects_form.rs b/thoth-app/src/component/subjects_form.rs index a0252aec..88bfac6c 100644 --- a/thoth-app/src/component/subjects_form.rs +++ b/thoth-app/src/component/subjects_form.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use thoth_api::subject::model::Subject; -use thoth_api::subject::model::SubjectType; +use thoth_api::model::subject::Subject; +use thoth_api::model::subject::SubjectType; use uuid::Uuid; use yew::html; use yew::prelude::*; diff --git a/thoth-app/src/component/utils.rs b/thoth-app/src/component/utils.rs index fd7195d2..db90d596 100644 --- a/thoth-app/src/component/utils.rs +++ b/thoth-app/src/component/utils.rs @@ -1,15 +1,15 @@ -use thoth_api::contribution::model::ContributionType; -use thoth_api::imprint::model::ImprintWithPublisher; -use thoth_api::language::model::LanguageCode; -use thoth_api::language::model::LanguageRelation; +use thoth_api::model::contribution::ContributionType; +use thoth_api::model::imprint::ImprintWithPublisher; +use thoth_api::model::language::LanguageCode; +use thoth_api::model::language::LanguageRelation; +use thoth_api::model::price::CurrencyCode; +use thoth_api::model::publication::PublicationType; +use thoth_api::model::publisher::Publisher; +use thoth_api::model::series::SeriesType; +use thoth_api::model::subject::SubjectType; +use thoth_api::model::work::WorkStatus; +use thoth_api::model::work::WorkType; use thoth_api::model::LengthUnit; -use thoth_api::price::model::CurrencyCode; -use thoth_api::publication::model::PublicationType; -use thoth_api::publisher::model::Publisher; -use thoth_api::series::model::SeriesType; -use thoth_api::subject::model::SubjectType; -use thoth_api::work::model::WorkStatus; -use thoth_api::work::model::WorkType; use uuid::Uuid; use yew::html; use yew::virtual_dom::VNode; diff --git a/thoth-app/src/component/work.rs b/thoth-app/src/component/work.rs index 6ae8588f..993aa56f 100644 --- a/thoth-app/src/component/work.rs +++ b/thoth-app/src/component/work.rs @@ -1,16 +1,16 @@ use std::str::FromStr; use thoth_api::account::model::AccountDetails; -use thoth_api::contribution::model::Contribution; -use thoth_api::funding::model::FundingWithFunder; -use thoth_api::imprint::model::ImprintWithPublisher; -use thoth_api::issue::model::IssueWithSeries; -use thoth_api::language::model::Language; +use thoth_api::model::contribution::Contribution; +use thoth_api::model::funding::FundingWithFunder; +use thoth_api::model::imprint::ImprintWithPublisher; +use thoth_api::model::issue::IssueWithSeries; +use thoth_api::model::language::Language; +use thoth_api::model::publication::Publication; +use thoth_api::model::subject::Subject; +use thoth_api::model::work::WorkStatus; +use thoth_api::model::work::WorkType; +use thoth_api::model::work::WorkWithRelations; use thoth_api::model::{Doi, LengthUnit, DOI_DOMAIN}; -use thoth_api::publication::model::Publication; -use thoth_api::subject::model::Subject; -use thoth_api::work::model::WorkStatus; -use thoth_api::work::model::WorkType; -use thoth_api::work::model::WorkWithRelations; use thoth_errors::ThothError; use uuid::Uuid; use yew::html; diff --git a/thoth-app/src/component/works.rs b/thoth-app/src/component/works.rs index eb785673..c2ef2908 100644 --- a/thoth-app/src/component/works.rs +++ b/thoth-app/src/component/works.rs @@ -3,9 +3,9 @@ use crate::models::work::works_query::FetchWorks; use crate::models::work::works_query::Variables; use crate::models::work::works_query::WorksRequest; use crate::models::work::works_query::WorksRequestBody; -use thoth_api::work::model::WorkField; -use thoth_api::work::model::WorkOrderBy; -use thoth_api::work::model::WorkWithRelations; +use thoth_api::model::work::WorkField; +use thoth_api::model::work::WorkOrderBy; +use thoth_api::model::work::WorkWithRelations; pagination_component! { WorksComponent, diff --git a/thoth-app/src/models/contribution/create_contribution_mutation.rs b/thoth-app/src/models/contribution/create_contribution_mutation.rs index d35cfb53..9c4e175e 100644 --- a/thoth-app/src/models/contribution/create_contribution_mutation.rs +++ b/thoth-app/src/models/contribution/create_contribution_mutation.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contribution::model::Contribution; -use thoth_api::contribution::model::ContributionType; +use thoth_api::model::contribution::Contribution; +use thoth_api::model::contribution::ContributionType; use uuid::Uuid; const CREATE_CONTRIBUTION_MUTATION: &str = " diff --git a/thoth-app/src/models/contribution/delete_contribution_mutation.rs b/thoth-app/src/models/contribution/delete_contribution_mutation.rs index 3c3b56eb..9c613a82 100644 --- a/thoth-app/src/models/contribution/delete_contribution_mutation.rs +++ b/thoth-app/src/models/contribution/delete_contribution_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contribution::model::Contribution; +use thoth_api::model::contribution::Contribution; use uuid::Uuid; const DELETE_CONTRIBUTION_MUTATION: &str = " diff --git a/thoth-app/src/models/contribution/mod.rs b/thoth-app/src/models/contribution/mod.rs index afebcef2..cdf7470d 100644 --- a/thoth-app/src/models/contribution/mod.rs +++ b/thoth-app/src/models/contribution/mod.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contribution::model::Contribution; -use thoth_api::contribution::model::ContributionType; +use thoth_api::model::contribution::Contribution; +use thoth_api::model::contribution::ContributionType; use yew::prelude::html; use yew::Html; diff --git a/thoth-app/src/models/contributor/contributor_activity_query.rs b/thoth-app/src/models/contributor/contributor_activity_query.rs index e2faa038..1454eb92 100644 --- a/thoth-app/src/models/contributor/contributor_activity_query.rs +++ b/thoth-app/src/models/contributor/contributor_activity_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contribution::model::ContributionWithWork; +use thoth_api::model::contribution::ContributionWithWork; use uuid::Uuid; use crate::graphql_query_builder; diff --git a/thoth-app/src/models/contributor/contributor_query.rs b/thoth-app/src/models/contributor/contributor_query.rs index 32e66c26..55bb6d26 100644 --- a/thoth-app/src/models/contributor/contributor_query.rs +++ b/thoth-app/src/models/contributor/contributor_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contributor::Contributor; use uuid::Uuid; pub const CONTRIBUTOR_QUERY: &str = " diff --git a/thoth-app/src/models/contributor/contributors_query.rs b/thoth-app/src/models/contributor/contributors_query.rs index a64f846e..41446142 100644 --- a/thoth-app/src/models/contributor/contributors_query.rs +++ b/thoth-app/src/models/contributor/contributors_query.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contributor::model::Contributor; -use thoth_api::contributor::model::ContributorOrderBy; +use thoth_api::model::contributor::Contributor; +use thoth_api::model::contributor::ContributorOrderBy; pub const CONTRIBUTORS_QUERY: &str = " query ContributorsQuery($limit: Int, $offset: Int, $filter: String, $order: ContributorOrderBy) { diff --git a/thoth-app/src/models/contributor/create_contributor_mutation.rs b/thoth-app/src/models/contributor/create_contributor_mutation.rs index 26250a61..b7395aad 100644 --- a/thoth-app/src/models/contributor/create_contributor_mutation.rs +++ b/thoth-app/src/models/contributor/create_contributor_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contributor::Contributor; use thoth_api::model::Orcid; const CREATE_CONTRIBUTOR_MUTATION: &str = " diff --git a/thoth-app/src/models/contributor/delete_contributor_mutation.rs b/thoth-app/src/models/contributor/delete_contributor_mutation.rs index c19c78ba..85b3930f 100644 --- a/thoth-app/src/models/contributor/delete_contributor_mutation.rs +++ b/thoth-app/src/models/contributor/delete_contributor_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contributor::Contributor; use uuid::Uuid; const DELETE_CONTRIBUTOR_MUTATION: &str = " diff --git a/thoth-app/src/models/contributor/mod.rs b/thoth-app/src/models/contributor/mod.rs index cf5eb32e..2f1bf5fa 100644 --- a/thoth-app/src/models/contributor/mod.rs +++ b/thoth-app/src/models/contributor/mod.rs @@ -1,4 +1,4 @@ -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contributor::Contributor; use yew::html; use yew::prelude::Html; use yew::Callback; diff --git a/thoth-app/src/models/contributor/update_contributor_mutation.rs b/thoth-app/src/models/contributor/update_contributor_mutation.rs index b349c27e..91799a01 100644 --- a/thoth-app/src/models/contributor/update_contributor_mutation.rs +++ b/thoth-app/src/models/contributor/update_contributor_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::contributor::model::Contributor; +use thoth_api::model::contributor::Contributor; use thoth_api::model::Orcid; use uuid::Uuid; diff --git a/thoth-app/src/models/funder/create_funder_mutation.rs b/thoth-app/src/models/funder/create_funder_mutation.rs index 3ada3924..c31048d3 100644 --- a/thoth-app/src/models/funder/create_funder_mutation.rs +++ b/thoth-app/src/models/funder/create_funder_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funder::model::Funder; +use thoth_api::model::funder::Funder; use thoth_api::model::Doi; const CREATE_FUNDER_MUTATION: &str = " diff --git a/thoth-app/src/models/funder/delete_funder_mutation.rs b/thoth-app/src/models/funder/delete_funder_mutation.rs index 05a42249..8c8c7e65 100644 --- a/thoth-app/src/models/funder/delete_funder_mutation.rs +++ b/thoth-app/src/models/funder/delete_funder_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funder::model::Funder; +use thoth_api::model::funder::Funder; use uuid::Uuid; const DELETE_FUNDER_MUTATION: &str = " diff --git a/thoth-app/src/models/funder/funder_activity_query.rs b/thoth-app/src/models/funder/funder_activity_query.rs index 5b6994bf..0168b454 100644 --- a/thoth-app/src/models/funder/funder_activity_query.rs +++ b/thoth-app/src/models/funder/funder_activity_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funding::model::FundingWithWork; +use thoth_api::model::funding::FundingWithWork; use uuid::Uuid; use crate::graphql_query_builder; diff --git a/thoth-app/src/models/funder/funder_query.rs b/thoth-app/src/models/funder/funder_query.rs index cfe8a084..0f1eb9cd 100644 --- a/thoth-app/src/models/funder/funder_query.rs +++ b/thoth-app/src/models/funder/funder_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funder::model::Funder; +use thoth_api::model::funder::Funder; use uuid::Uuid; pub const FUNDER_QUERY: &str = " diff --git a/thoth-app/src/models/funder/funders_query.rs b/thoth-app/src/models/funder/funders_query.rs index 44d1ea6c..e112196d 100644 --- a/thoth-app/src/models/funder/funders_query.rs +++ b/thoth-app/src/models/funder/funders_query.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funder::model::Funder; -use thoth_api::funder::model::FunderOrderBy; +use thoth_api::model::funder::Funder; +use thoth_api::model::funder::FunderOrderBy; pub const FUNDERS_QUERY: &str = " query FundersQuery($limit: Int, $offset: Int, $filter: String, $order: FunderOrderBy) { diff --git a/thoth-app/src/models/funder/mod.rs b/thoth-app/src/models/funder/mod.rs index ffb6b87b..2ec74e27 100644 --- a/thoth-app/src/models/funder/mod.rs +++ b/thoth-app/src/models/funder/mod.rs @@ -1,4 +1,4 @@ -use thoth_api::funder::model::Funder; +use thoth_api::model::funder::Funder; use yew::html; use yew::prelude::Html; use yew::Callback; diff --git a/thoth-app/src/models/funder/update_funder_mutation.rs b/thoth-app/src/models/funder/update_funder_mutation.rs index 39cdf024..5efe8bfa 100644 --- a/thoth-app/src/models/funder/update_funder_mutation.rs +++ b/thoth-app/src/models/funder/update_funder_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funder::model::Funder; +use thoth_api::model::funder::Funder; use thoth_api::model::Doi; use uuid::Uuid; diff --git a/thoth-app/src/models/funding/create_funding_mutation.rs b/thoth-app/src/models/funding/create_funding_mutation.rs index 924ac7f7..a90b4ebe 100644 --- a/thoth-app/src/models/funding/create_funding_mutation.rs +++ b/thoth-app/src/models/funding/create_funding_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funding::model::FundingWithFunder; +use thoth_api::model::funding::FundingWithFunder; use uuid::Uuid; const CREATE_FUNDING_MUTATION: &str = " diff --git a/thoth-app/src/models/funding/delete_funding_mutation.rs b/thoth-app/src/models/funding/delete_funding_mutation.rs index e8cc3b0e..bdc7b477 100644 --- a/thoth-app/src/models/funding/delete_funding_mutation.rs +++ b/thoth-app/src/models/funding/delete_funding_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::funding::model::Funding; +use thoth_api::model::funding::Funding; use uuid::Uuid; const DELETE_FUNDING_MUTATION: &str = " diff --git a/thoth-app/src/models/imprint/create_imprint_mutation.rs b/thoth-app/src/models/imprint/create_imprint_mutation.rs index 38886c60..fd87fd47 100644 --- a/thoth-app/src/models/imprint/create_imprint_mutation.rs +++ b/thoth-app/src/models/imprint/create_imprint_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::imprint::model::Imprint; +use thoth_api::model::imprint::Imprint; use uuid::Uuid; const CREATE_IMPRINT_MUTATION: &str = " diff --git a/thoth-app/src/models/imprint/delete_imprint_mutation.rs b/thoth-app/src/models/imprint/delete_imprint_mutation.rs index f0efec6a..016f4bc6 100644 --- a/thoth-app/src/models/imprint/delete_imprint_mutation.rs +++ b/thoth-app/src/models/imprint/delete_imprint_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::imprint::model::Imprint; +use thoth_api::model::imprint::Imprint; use uuid::Uuid; const DELETE_IMPRINT_MUTATION: &str = " diff --git a/thoth-app/src/models/imprint/imprint_query.rs b/thoth-app/src/models/imprint/imprint_query.rs index 1b109e86..7f36688a 100644 --- a/thoth-app/src/models/imprint/imprint_query.rs +++ b/thoth-app/src/models/imprint/imprint_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::imprint::model::ImprintWithPublisher; +use thoth_api::model::imprint::ImprintWithPublisher; use uuid::Uuid; pub const IMPRINT_QUERY: &str = " diff --git a/thoth-app/src/models/imprint/imprints_query.rs b/thoth-app/src/models/imprint/imprints_query.rs index 138309f7..28764295 100644 --- a/thoth-app/src/models/imprint/imprints_query.rs +++ b/thoth-app/src/models/imprint/imprints_query.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::imprint::model::ImprintOrderBy; -use thoth_api::imprint::model::ImprintWithPublisher; +use thoth_api::model::imprint::ImprintOrderBy; +use thoth_api::model::imprint::ImprintWithPublisher; const IMPRINTS_QUERY: &str = " query ImprintsQuery($limit: Int, $offset: Int, $filter: String, $publishers: [Uuid!], $order: ImprintOrderBy) { diff --git a/thoth-app/src/models/imprint/mod.rs b/thoth-app/src/models/imprint/mod.rs index e4ec17a9..babffe1b 100644 --- a/thoth-app/src/models/imprint/mod.rs +++ b/thoth-app/src/models/imprint/mod.rs @@ -1,5 +1,5 @@ -use thoth_api::imprint::model::Imprint; -use thoth_api::imprint::model::ImprintWithPublisher; +use thoth_api::model::imprint::Imprint; +use thoth_api::model::imprint::ImprintWithPublisher; use yew::html; use yew::prelude::Html; use yew::Callback; diff --git a/thoth-app/src/models/imprint/update_imprint_mutation.rs b/thoth-app/src/models/imprint/update_imprint_mutation.rs index ea43b789..45a4efca 100644 --- a/thoth-app/src/models/imprint/update_imprint_mutation.rs +++ b/thoth-app/src/models/imprint/update_imprint_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::imprint::model::Imprint; +use thoth_api::model::imprint::Imprint; use uuid::Uuid; const UPDATE_IMPRINT_MUTATION: &str = " diff --git a/thoth-app/src/models/issue/create_issue_mutation.rs b/thoth-app/src/models/issue/create_issue_mutation.rs index 2eb51f36..94941bdc 100644 --- a/thoth-app/src/models/issue/create_issue_mutation.rs +++ b/thoth-app/src/models/issue/create_issue_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::issue::model::IssueWithSeries; +use thoth_api::model::issue::IssueWithSeries; use uuid::Uuid; const CREATE_ISSUE_MUTATION: &str = " diff --git a/thoth-app/src/models/issue/delete_issue_mutation.rs b/thoth-app/src/models/issue/delete_issue_mutation.rs index 5d583d4d..e2014b54 100644 --- a/thoth-app/src/models/issue/delete_issue_mutation.rs +++ b/thoth-app/src/models/issue/delete_issue_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::issue::model::Issue; +use thoth_api::model::issue::Issue; use uuid::Uuid; const DELETE_ISSUE_MUTATION: &str = " diff --git a/thoth-app/src/models/language/create_language_mutation.rs b/thoth-app/src/models/language/create_language_mutation.rs index 18f2b2a5..d9a3ee58 100644 --- a/thoth-app/src/models/language/create_language_mutation.rs +++ b/thoth-app/src/models/language/create_language_mutation.rs @@ -1,8 +1,8 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::language::model::Language; -use thoth_api::language::model::LanguageCode; -use thoth_api::language::model::LanguageRelation; +use thoth_api::model::language::Language; +use thoth_api::model::language::LanguageCode; +use thoth_api::model::language::LanguageRelation; use uuid::Uuid; const CREATE_LANGUAGE_MUTATION: &str = " diff --git a/thoth-app/src/models/language/delete_language_mutation.rs b/thoth-app/src/models/language/delete_language_mutation.rs index 5517ee74..abdff435 100644 --- a/thoth-app/src/models/language/delete_language_mutation.rs +++ b/thoth-app/src/models/language/delete_language_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::language::model::Language; +use thoth_api::model::language::Language; use uuid::Uuid; const DELETE_LANGUAGE_MUTATION: &str = " diff --git a/thoth-app/src/models/language/mod.rs b/thoth-app/src/models/language/mod.rs index 70411bce..c53d981e 100644 --- a/thoth-app/src/models/language/mod.rs +++ b/thoth-app/src/models/language/mod.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::language::model::LanguageCode; -use thoth_api::language::model::LanguageRelation; +use thoth_api::model::language::LanguageCode; +use thoth_api::model::language::LanguageRelation; #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] diff --git a/thoth-app/src/models/price/create_price_mutation.rs b/thoth-app/src/models/price/create_price_mutation.rs index 0af67d0b..ccedbf2e 100644 --- a/thoth-app/src/models/price/create_price_mutation.rs +++ b/thoth-app/src/models/price/create_price_mutation.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::price::model::CurrencyCode; -use thoth_api::price::model::Price; +use thoth_api::model::price::CurrencyCode; +use thoth_api::model::price::Price; use uuid::Uuid; const CREATE_PRICE_MUTATION: &str = " diff --git a/thoth-app/src/models/price/delete_price_mutation.rs b/thoth-app/src/models/price/delete_price_mutation.rs index 78b29fcf..c3496dbc 100644 --- a/thoth-app/src/models/price/delete_price_mutation.rs +++ b/thoth-app/src/models/price/delete_price_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::price::model::Price; +use thoth_api::model::price::Price; use uuid::Uuid; const DELETE_PRICE_MUTATION: &str = " diff --git a/thoth-app/src/models/price/mod.rs b/thoth-app/src/models/price/mod.rs index 3978ad64..42c48d1e 100644 --- a/thoth-app/src/models/price/mod.rs +++ b/thoth-app/src/models/price/mod.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::price::model::CurrencyCode; +use thoth_api::model::price::CurrencyCode; #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] diff --git a/thoth-app/src/models/publication/create_publication_mutation.rs b/thoth-app/src/models/publication/create_publication_mutation.rs index 2b292d7b..f4e3909b 100644 --- a/thoth-app/src/models/publication/create_publication_mutation.rs +++ b/thoth-app/src/models/publication/create_publication_mutation.rs @@ -1,8 +1,8 @@ use serde::Deserialize; use serde::Serialize; +use thoth_api::model::publication::Publication; +use thoth_api::model::publication::PublicationType; use thoth_api::model::Isbn; -use thoth_api::publication::model::Publication; -use thoth_api::publication::model::PublicationType; use uuid::Uuid; const CREATE_PUBLICATION_MUTATION: &str = " diff --git a/thoth-app/src/models/publication/delete_publication_mutation.rs b/thoth-app/src/models/publication/delete_publication_mutation.rs index 82e49353..902a094c 100644 --- a/thoth-app/src/models/publication/delete_publication_mutation.rs +++ b/thoth-app/src/models/publication/delete_publication_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publication::model::Publication; +use thoth_api::model::publication::Publication; use uuid::Uuid; const DELETE_PUBLICATION_MUTATION: &str = " diff --git a/thoth-app/src/models/publication/mod.rs b/thoth-app/src/models/publication/mod.rs index e9f11a3c..5836d385 100644 --- a/thoth-app/src/models/publication/mod.rs +++ b/thoth-app/src/models/publication/mod.rs @@ -1,8 +1,8 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publication::model::Publication; -use thoth_api::publication::model::PublicationType; -use thoth_api::publication::model::PublicationWithRelations; +use thoth_api::model::publication::Publication; +use thoth_api::model::publication::PublicationType; +use thoth_api::model::publication::PublicationWithRelations; use yew::html; use yew::prelude::Html; use yew::Callback; diff --git a/thoth-app/src/models/publication/publication_query.rs b/thoth-app/src/models/publication/publication_query.rs index d26131a6..ca371027 100644 --- a/thoth-app/src/models/publication/publication_query.rs +++ b/thoth-app/src/models/publication/publication_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publication::model::PublicationWithRelations; +use thoth_api::model::publication::PublicationWithRelations; use uuid::Uuid; pub const PUBLICATION_QUERY: &str = " diff --git a/thoth-app/src/models/publication/publications_query.rs b/thoth-app/src/models/publication/publications_query.rs index 75ab52f3..d6a145a9 100644 --- a/thoth-app/src/models/publication/publications_query.rs +++ b/thoth-app/src/models/publication/publications_query.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publication::model::PublicationOrderBy; -use thoth_api::publication::model::PublicationWithRelations; +use thoth_api::model::publication::PublicationOrderBy; +use thoth_api::model::publication::PublicationWithRelations; pub const PUBLICATIONS_QUERY: &str = " query PublicationsQuery($limit: Int, $offset: Int, $filter: String, $publishers: [Uuid!], $order: PublicationOrderBy) { diff --git a/thoth-app/src/models/publisher/create_publisher_mutation.rs b/thoth-app/src/models/publisher/create_publisher_mutation.rs index 3c2867c6..66c63ed2 100644 --- a/thoth-app/src/models/publisher/create_publisher_mutation.rs +++ b/thoth-app/src/models/publisher/create_publisher_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; const CREATE_PUBLISHER_MUTATION: &str = " mutation CreatePublisher( diff --git a/thoth-app/src/models/publisher/delete_publisher_mutation.rs b/thoth-app/src/models/publisher/delete_publisher_mutation.rs index 3b590a4c..d7c96913 100644 --- a/thoth-app/src/models/publisher/delete_publisher_mutation.rs +++ b/thoth-app/src/models/publisher/delete_publisher_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; use uuid::Uuid; const DELETE_PUBLISHER_MUTATION: &str = " diff --git a/thoth-app/src/models/publisher/mod.rs b/thoth-app/src/models/publisher/mod.rs index 5edb94d5..8a89b9ee 100644 --- a/thoth-app/src/models/publisher/mod.rs +++ b/thoth-app/src/models/publisher/mod.rs @@ -1,4 +1,4 @@ -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; use yew::html; use yew::prelude::Html; use yew::Callback; diff --git a/thoth-app/src/models/publisher/publisher_query.rs b/thoth-app/src/models/publisher/publisher_query.rs index 8b1563f7..09c4cc95 100644 --- a/thoth-app/src/models/publisher/publisher_query.rs +++ b/thoth-app/src/models/publisher/publisher_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; use uuid::Uuid; pub const PUBLISHER_QUERY: &str = " diff --git a/thoth-app/src/models/publisher/publishers_query.rs b/thoth-app/src/models/publisher/publishers_query.rs index 2f02ce6a..4292fcaa 100644 --- a/thoth-app/src/models/publisher/publishers_query.rs +++ b/thoth-app/src/models/publisher/publishers_query.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publisher::model::Publisher; -use thoth_api::publisher::model::PublisherOrderBy; +use thoth_api::model::publisher::Publisher; +use thoth_api::model::publisher::PublisherOrderBy; const PUBLISHERS_QUERY: &str = " query PublishersQuery($limit: Int, $offset: Int, $filter: String, $publishers: [Uuid!], $order: PublisherOrderBy) { diff --git a/thoth-app/src/models/publisher/update_publisher_mutation.rs b/thoth-app/src/models/publisher/update_publisher_mutation.rs index 6cc87457..9d92e4fa 100644 --- a/thoth-app/src/models/publisher/update_publisher_mutation.rs +++ b/thoth-app/src/models/publisher/update_publisher_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::publisher::model::Publisher; +use thoth_api::model::publisher::Publisher; use uuid::Uuid; const UPDATE_PUBLISHER_MUTATION: &str = " diff --git a/thoth-app/src/models/series/create_series_mutation.rs b/thoth-app/src/models/series/create_series_mutation.rs index b4cdd1bf..0df6e64f 100644 --- a/thoth-app/src/models/series/create_series_mutation.rs +++ b/thoth-app/src/models/series/create_series_mutation.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::series::model::Series; -use thoth_api::series::model::SeriesType; +use thoth_api::model::series::Series; +use thoth_api::model::series::SeriesType; use uuid::Uuid; const CREATE_SERIES_MUTATION: &str = " diff --git a/thoth-app/src/models/series/delete_series_mutation.rs b/thoth-app/src/models/series/delete_series_mutation.rs index 55fd0bfb..dda5dddf 100644 --- a/thoth-app/src/models/series/delete_series_mutation.rs +++ b/thoth-app/src/models/series/delete_series_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::series::model::Series; +use thoth_api::model::series::Series; use uuid::Uuid; const DELETE_SERIES_MUTATION: &str = " diff --git a/thoth-app/src/models/series/mod.rs b/thoth-app/src/models/series/mod.rs index c9dc2704..c8d311c0 100644 --- a/thoth-app/src/models/series/mod.rs +++ b/thoth-app/src/models/series/mod.rs @@ -1,8 +1,8 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::series::model::Series; -use thoth_api::series::model::SeriesType; -use thoth_api::series::model::SeriesWithImprint; +use thoth_api::model::series::Series; +use thoth_api::model::series::SeriesType; +use thoth_api::model::series::SeriesWithImprint; use yew::html; use yew::prelude::Html; use yew::Callback; diff --git a/thoth-app/src/models/series/series_query.rs b/thoth-app/src/models/series/series_query.rs index 30ece406..6538d90d 100644 --- a/thoth-app/src/models/series/series_query.rs +++ b/thoth-app/src/models/series/series_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::series::model::SeriesWithImprint; +use thoth_api::model::series::SeriesWithImprint; use uuid::Uuid; pub const SERIES_QUERY: &str = " diff --git a/thoth-app/src/models/series/serieses_query.rs b/thoth-app/src/models/series/serieses_query.rs index 8192828a..6d2fd3a1 100644 --- a/thoth-app/src/models/series/serieses_query.rs +++ b/thoth-app/src/models/series/serieses_query.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::series::model::SeriesOrderBy; -use thoth_api::series::model::SeriesWithImprint; +use thoth_api::model::series::SeriesOrderBy; +use thoth_api::model::series::SeriesWithImprint; pub const SERIESES_QUERY: &str = " query SeriesesQuery($limit: Int, $offset: Int, $filter: String, $publishers: [Uuid!], $order: SeriesOrderBy) { diff --git a/thoth-app/src/models/series/update_series_mutation.rs b/thoth-app/src/models/series/update_series_mutation.rs index 947c1090..a2388c73 100644 --- a/thoth-app/src/models/series/update_series_mutation.rs +++ b/thoth-app/src/models/series/update_series_mutation.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::series::model::Series; -use thoth_api::series::model::SeriesType; +use thoth_api::model::series::Series; +use thoth_api::model::series::SeriesType; use uuid::Uuid; const UPDATE_SERIES_MUTATION: &str = " diff --git a/thoth-app/src/models/subject/create_subject_mutation.rs b/thoth-app/src/models/subject/create_subject_mutation.rs index 9661b7f5..105db072 100644 --- a/thoth-app/src/models/subject/create_subject_mutation.rs +++ b/thoth-app/src/models/subject/create_subject_mutation.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::subject::model::Subject; -use thoth_api::subject::model::SubjectType; +use thoth_api::model::subject::Subject; +use thoth_api::model::subject::SubjectType; use uuid::Uuid; const CREATE_SUBJECT_MUTATION: &str = " diff --git a/thoth-app/src/models/subject/delete_subject_mutation.rs b/thoth-app/src/models/subject/delete_subject_mutation.rs index af39d8a4..4dc9995e 100644 --- a/thoth-app/src/models/subject/delete_subject_mutation.rs +++ b/thoth-app/src/models/subject/delete_subject_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::subject::model::Subject; +use thoth_api::model::subject::Subject; use uuid::Uuid; const DELETE_SUBJECT_MUTATION: &str = " diff --git a/thoth-app/src/models/subject/mod.rs b/thoth-app/src/models/subject/mod.rs index f4fcfd7c..58e3935f 100644 --- a/thoth-app/src/models/subject/mod.rs +++ b/thoth-app/src/models/subject/mod.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::subject::model::SubjectType; +use thoth_api::model::subject::SubjectType; #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] diff --git a/thoth-app/src/models/work/create_work_mutation.rs b/thoth-app/src/models/work/create_work_mutation.rs index e25df819..6fabe892 100644 --- a/thoth-app/src/models/work/create_work_mutation.rs +++ b/thoth-app/src/models/work/create_work_mutation.rs @@ -1,10 +1,10 @@ use serde::Deserialize; use serde::Serialize; +use thoth_api::model::work::Work; +use thoth_api::model::work::WorkStatus; +use thoth_api::model::work::WorkType; use thoth_api::model::Doi; use thoth_api::model::LengthUnit; -use thoth_api::work::model::Work; -use thoth_api::work::model::WorkStatus; -use thoth_api::work::model::WorkType; use uuid::Uuid; const CREATE_WORK_MUTATION: &str = " diff --git a/thoth-app/src/models/work/delete_work_mutation.rs b/thoth-app/src/models/work/delete_work_mutation.rs index f73940dd..80a83e0c 100644 --- a/thoth-app/src/models/work/delete_work_mutation.rs +++ b/thoth-app/src/models/work/delete_work_mutation.rs @@ -1,6 +1,6 @@ use serde::Deserialize; use serde::Serialize; -use thoth_api::work::model::Work; +use thoth_api::model::work::Work; use uuid::Uuid; const DELETE_WORK_MUTATION: &str = " diff --git a/thoth-app/src/models/work/mod.rs b/thoth-app/src/models/work/mod.rs index 6fd7e7e0..d8df5fcf 100644 --- a/thoth-app/src/models/work/mod.rs +++ b/thoth-app/src/models/work/mod.rs @@ -2,11 +2,11 @@ use serde::Deserialize; use serde::Serialize; use std::str::FromStr; use std::string::ParseError; +use thoth_api::model::work::Work; +use thoth_api::model::work::WorkStatus; +use thoth_api::model::work::WorkType; +use thoth_api::model::work::WorkWithRelations; use thoth_api::model::LengthUnit; -use thoth_api::work::model::Work; -use thoth_api::work::model::WorkStatus; -use thoth_api::work::model::WorkType; -use thoth_api::work::model::WorkWithRelations; use yew::html; use yew::prelude::Html; use yew::Callback; @@ -118,6 +118,8 @@ impl MetadataTable for WorkWithRelations { pub trait DisplayWork { fn onix_projectmuse_endpoint(&self) -> String; fn onix_oapen_endpoint(&self) -> String; + fn onix_jstor_endpoint(&self) -> String; + fn onix_ebsco_host_endpoint(&self) -> String; fn csv_endpoint(&self) -> String; fn kbart_endpoint(&self) -> String; fn cover_alt_text(&self) -> String; @@ -141,6 +143,20 @@ impl DisplayWork for WorkWithRelations { ) } + fn onix_jstor_endpoint(&self) -> String { + format!( + "{}/specifications/onix_3.0::jstor/work/{}", + THOTH_EXPORT_API, &self.work_id + ) + } + + fn onix_ebsco_host_endpoint(&self) -> String { + format!( + "{}/specifications/onix_2.1::ebsco_host/work/{}", + THOTH_EXPORT_API, &self.work_id + ) + } + fn csv_endpoint(&self) -> String { format!( "{}/specifications/csv::thoth/work/{}", @@ -356,6 +372,18 @@ impl DisplayWork for WorkWithRelations { > {"ONIX (OAPEN)"} + + {"ONIX (JSTOR)"} + + + {"ONIX (EBSCO Host)"} + ", "Ross Higman "] edition = "2018" license = "Apache-2.0" @@ -9,8 +9,8 @@ repository = "https://github.com/thoth-pub/thoth" readme = "README.md" [dependencies] -thoth-api = {version = "0.4.6", path = "../thoth-api" } -thoth-errors = {version = "0.4.6", path = "../thoth-errors" } +thoth-api = {version = "0.4.7", path = "../thoth-api" } +thoth-errors = {version = "0.4.7", path = "../thoth-errors" } graphql_client = "0.9.0" chrono = { version = "0.4", features = ["serde"] } reqwest = { version = "0.10", features = ["json"] } diff --git a/thoth-client/assets/queries.graphql b/thoth-client/assets/queries.graphql index a103c1fd..7d7263cd 100644 --- a/thoth-client/assets/queries.graphql +++ b/thoth-client/assets/queries.graphql @@ -14,8 +14,12 @@ fragment Work on Work { longAbstract generalNote place - width - height + widthMm: width(units: MM) + widthCm: width(units: CM) + widthIn: width(units: IN) + heightMm: height(units: MM) + heightCm: height(units: CM) + heightIn: height(units: IN) pageCount pageBreakdown imageCount @@ -32,6 +36,7 @@ fragment Work on Work { imprintName publisher { publisherName + publisherUrl } } issues { diff --git a/thoth-client/assets/schema.json b/thoth-client/assets/schema.json index 98baa4da..d12752be 100644 --- a/thoth-client/assets/schema.json +++ b/thoth-client/assets/schema.json @@ -132,7 +132,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -148,15 +148,56 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "\"\"", + "description": "A query string to search. This argument is a test, do not rely on it. At present it simply searches for case insensitive literals on imprint_name and imprint_url", + "name": "filter", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": "{field: \"IMPRINT_NAME\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "ImprintOrderBy", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get imprints linked to this publisher", "isDeprecated": false, "name": "imprints", "type": { @@ -3293,7 +3334,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -3309,7 +3350,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -3757,7 +3798,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -3773,7 +3814,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -3864,6 +3905,59 @@ "name": "Float", "possibleTypes": null }, + { + "description": "Field to use when sorting languages list", + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "LANGUAGE_ID" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "WORK_ID" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "LANGUAGE_CODE" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "LANGUAGE_RELATION" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "MAIN_LANGUAGE" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CREATED_AT" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "UPDATED_AT" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "LanguageField", + "possibleTypes": null + }, { "description": "The amount of money, in any currency, that a publication costs.", "enumValues": null, @@ -3943,7 +4037,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -3959,7 +4053,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -3987,59 +4081,6 @@ "name": "Price", "possibleTypes": null }, - { - "description": "Field to use when sorting languages list", - "enumValues": [ - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "LANGUAGE_ID" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "WORK_ID" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "LANGUAGE_CODE" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "LANGUAGE_RELATION" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "MAIN_LANGUAGE" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "CREATED_AT" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "UPDATED_AT" - } - ], - "fields": null, - "inputFields": null, - "interfaces": null, - "kind": "ENUM", - "name": "LanguageField", - "possibleTypes": null - }, { "description": null, "enumValues": null, @@ -4183,7 +4224,7 @@ "name": "funderDoi", "type": { "kind": "SCALAR", - "name": "String", + "name": "Doi", "ofType": null } }, @@ -4198,7 +4239,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -4214,15 +4255,46 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"PROGRAM\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "FundingOrderBy", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get fundings linked to this funder", "isDeprecated": false, "name": "fundings", "type": { @@ -4309,6 +4381,63 @@ "name": "ContributorField", "possibleTypes": null }, + { + "description": "RFC 3339 combined date and time in UTC time zone", + "enumValues": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "SCALAR", + "name": "Timestamp", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "BOOK_CHAPTER" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "MONOGRAPH" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "EDITED_BOOK" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "TEXTBOOK" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "JOURNAL_ISSUE" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "BOOK_SET" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "WorkType", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -4391,50 +4520,32 @@ "possibleTypes": null }, { - "description": null, + "description": "Unit of measurement for physical Work dimensions (mm, cm or in)", "enumValues": [ { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "BOOK_CHAPTER" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "MONOGRAPH" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "EDITED_BOOK" + "name": "MM" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "TEXTBOOK" + "name": "CM" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "JOURNAL_ISSUE" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BOOK_SET" + "name": "IN" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", - "name": "WorkType", + "name": "LengthUnit", "possibleTypes": null }, { @@ -4500,7 +4611,7 @@ "name": "orcid", "type": { "kind": "SCALAR", - "name": "String", + "name": "Orcid", "ofType": null } }, @@ -4683,6 +4794,22 @@ "ofType": null } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "imprintId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -4694,7 +4821,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -4710,7 +4837,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -4732,9 +4859,40 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"ISSUE_ORDINAL\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "IssueOrderBy", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get issues linked to this series", "isDeprecated": false, "name": "issues", "type": { @@ -4967,43 +5125,24 @@ "possibleTypes": null }, { - "description": null, + "description": "Digital Object Identifier. Expressed as `^https:\\/\\/doi\\.org\\/10\\.\\d{4,9}\\/[-._\\;\\(\\)\\/:a-zA-Z0-9]+$`", "enumValues": null, "fields": null, - "inputFields": [ - { - "defaultValue": null, - "description": null, - "name": "funderName", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "funderDoi", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - ], + "inputFields": null, "interfaces": null, - "kind": "INPUT_OBJECT", - "name": "NewFunder", + "kind": "SCALAR", + "name": "Doi", "possibleTypes": null }, { "description": "Field to use when sorting issues list", "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "ISSUE_ID" + }, { "deprecationReason": null, "description": null, @@ -5045,6 +5184,12 @@ { "description": "Field to use when sorting contributions list", "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CONTRIBUTION_ID" + }, { "deprecationReason": null, "description": null, @@ -5110,6 +5255,12 @@ "description": null, "isDeprecated": false, "name": "FULL_NAME" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CONTRIBUTION_ORDINAL" } ], "fields": null, @@ -5180,35 +5331,7 @@ { "defaultValue": null, "description": null, - "name": "imprintId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "publisherId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "imprintName", + "name": "funderName", "type": { "kind": "NON_NULL", "name": null, @@ -5222,17 +5345,17 @@ { "defaultValue": null, "description": null, - "name": "imprintUrl", + "name": "funderDoi", "type": { "kind": "SCALAR", - "name": "String", + "name": "Doi", "ofType": null } } ], "interfaces": null, "kind": "INPUT_OBJECT", - "name": "PatchImprint", + "name": "NewFunder", "possibleTypes": null }, { @@ -5275,7 +5398,7 @@ "possibleTypes": null }, { - "description": "Field and order to use when sorting series list", + "description": "Field and order to use when sorting subjects list", "enumValues": null, "fields": null, "inputFields": [ @@ -5288,7 +5411,7 @@ "name": null, "ofType": { "kind": "ENUM", - "name": "SeriesField", + "name": "SubjectField", "ofType": null } } @@ -5310,7 +5433,7 @@ ], "interfaces": null, "kind": "INPUT_OBJECT", - "name": "SeriesOrderBy", + "name": "SubjectOrderBy", "possibleTypes": null }, { @@ -5385,7 +5508,7 @@ "name": "orcid", "type": { "kind": "SCALAR", - "name": "String", + "name": "Orcid", "ofType": null } }, @@ -5412,7 +5535,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -5428,15 +5551,56 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"CONTRIBUTION_TYPE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "ContributionOrderBy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "contributionType", + "type": { + "kind": "ENUM", + "name": "ContributionType", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get contributions linked to this contributor", "isDeprecated": false, "name": "contributions", "type": { @@ -5468,6 +5632,22 @@ "description": "A person's involvement in the production of a written text.", "enumValues": null, "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "contributionId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -5567,7 +5747,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -5583,7 +5763,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -5782,7 +5962,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -5798,7 +5978,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -5827,7 +6007,7 @@ "possibleTypes": null }, { - "description": "Field and order to use when sorting subjects list", + "description": "Field and order to use when sorting seriess list", "enumValues": null, "fields": null, "inputFields": [ @@ -5840,7 +6020,7 @@ "name": null, "ofType": { "kind": "ENUM", - "name": "SubjectField", + "name": "SeriesField", "ofType": null } } @@ -5862,7 +6042,7 @@ ], "interfaces": null, "kind": "INPUT_OBJECT", - "name": "SubjectOrderBy", + "name": "SeriesOrderBy", "possibleTypes": null }, { @@ -6000,7 +6180,7 @@ "name": "orcid", "type": { "kind": "SCALAR", - "name": "String", + "name": "Orcid", "ofType": null } }, @@ -6080,7 +6260,7 @@ "name": "isbn", "type": { "kind": "SCALAR", - "name": "String", + "name": "Isbn", "ofType": null } }, @@ -6107,7 +6287,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -6123,15 +6303,56 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"CURRENCY_CODE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "PriceOrderBy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific currency to filter by", + "name": "currencyCode", + "type": { + "kind": "ENUM", + "name": "CurrencyCode", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get prices linked to this publication", "isDeprecated": false, "name": "prices", "type": { @@ -6246,7 +6467,7 @@ { "defaultValue": null, "description": null, - "name": "publicationId", + "name": "imprintId", "type": { "kind": "NON_NULL", "name": null, @@ -6260,13 +6481,13 @@ { "defaultValue": null, "description": null, - "name": "publicationType", + "name": "publisherId", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "ENUM", - "name": "PublicationType", + "kind": "SCALAR", + "name": "Uuid", "ofType": null } } @@ -6274,13 +6495,13 @@ { "defaultValue": null, "description": null, - "name": "workId", + "name": "imprintName", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Uuid", + "name": "String", "ofType": null } } @@ -6288,17 +6509,7 @@ { "defaultValue": null, "description": null, - "name": "isbn", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "publicationUrl", + "name": "imprintUrl", "type": { "kind": "SCALAR", "name": "String", @@ -6308,7 +6519,7 @@ ], "interfaces": null, "kind": "INPUT_OBJECT", - "name": "PatchPublication", + "name": "PatchImprint", "possibleTypes": null }, { @@ -6319,7 +6530,7 @@ { "defaultValue": null, "description": null, - "name": "seriesId", + "name": "publicationId", "type": { "kind": "NON_NULL", "name": null, @@ -6333,27 +6544,13 @@ { "defaultValue": null, "description": null, - "name": "seriesType", + "name": "publicationType", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "ENUM", - "name": "SeriesType", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "seriesName", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", + "name": "PublicationType", "ofType": null } } @@ -6361,13 +6558,13 @@ { "defaultValue": null, "description": null, - "name": "issnPrint", + "name": "workId", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "String", + "name": "Uuid", "ofType": null } } @@ -6375,45 +6572,27 @@ { "defaultValue": null, "description": null, - "name": "issnDigital", + "name": "isbn", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } + "kind": "SCALAR", + "name": "Isbn", + "ofType": null } }, { "defaultValue": null, "description": null, - "name": "seriesUrl", + "name": "publicationUrl", "type": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - { - "defaultValue": null, - "description": null, - "name": "imprintId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } } ], "interfaces": null, "kind": "INPUT_OBJECT", - "name": "PatchSeries", + "name": "PatchPublication", "possibleTypes": null }, { @@ -6423,7 +6602,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "FUNDING_ID" + "name": "FUNDER_ID" }, { "deprecationReason": null, @@ -6435,7 +6614,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "FUNDER_ID" + "name": "FUNDING_ID" }, { "deprecationReason": null, @@ -6558,13 +6737,27 @@ { "defaultValue": null, "description": null, - "name": "workId", + "name": "seriesType", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "SeriesType", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "seriesName", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Uuid", + "name": "String", "ofType": null } } @@ -6572,33 +6765,21 @@ { "defaultValue": null, "description": null, - "name": "issueOrdinal", + "name": "issnPrint", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Int", + "name": "String", "ofType": null } } - } - ], - "interfaces": null, - "kind": "INPUT_OBJECT", - "name": "PatchIssue", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": [ + }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "name", + "name": "issnDigital", "type": { "kind": "NON_NULL", "name": null, @@ -6610,11 +6791,9 @@ } }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "description", + "name": "seriesUrl", "type": { "kind": "SCALAR", "name": "String", @@ -6622,78 +6801,90 @@ } }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "args", + "name": "imprintId", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__InputValue", - "ofType": null - } - } + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "PatchSeries", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "issueId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null } } }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "type", + "name": "seriesId", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "__Type", + "kind": "SCALAR", + "name": "Uuid", "ofType": null } } }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "isDeprecated", + "name": "workId", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Boolean", + "name": "Uuid", "ofType": null } } }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "deprecationReason", + "name": "issueOrdinal", "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } } } ], - "inputFields": null, - "interfaces": [], - "kind": "OBJECT", - "name": "__Field", + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "PatchIssue", "possibleTypes": null }, { @@ -6728,30 +6919,6 @@ "ofType": null } }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "locations", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "__DirectiveLocation", - "ofType": null - } - } - } - } - }, { "args": [], "deprecationReason": null, @@ -6778,26 +6945,26 @@ }, { "args": [], - "deprecationReason": "Use the locations array instead", + "deprecationReason": null, "description": null, - "isDeprecated": true, - "name": "onOperation", + "isDeprecated": false, + "name": "type", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "Boolean", + "kind": "OBJECT", + "name": "__Type", "ofType": null } } }, { "args": [], - "deprecationReason": "Use the locations array instead", + "deprecationReason": null, "description": null, - "isDeprecated": true, - "name": "onFragment", + "isDeprecated": false, + "name": "isDeprecated", "type": { "kind": "NON_NULL", "name": null, @@ -6810,25 +6977,21 @@ }, { "args": [], - "deprecationReason": "Use the locations array instead", + "deprecationReason": null, "description": null, - "isDeprecated": true, - "name": "onField", + "isDeprecated": false, + "name": "deprecationReason", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } + "kind": "SCALAR", + "name": "String", + "ofType": null } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", - "name": "__Directive", + "name": "__Field", "possibleTypes": null }, { @@ -7041,7 +7204,7 @@ "name": "doi", "type": { "kind": "SCALAR", - "name": "String", + "name": "Doi", "ofType": null } }, @@ -7265,6 +7428,141 @@ "name": "PatchWork", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "description", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "locations", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "__DirectiveLocation", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "args", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__InputValue", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": "Use the locations array instead", + "description": null, + "isDeprecated": true, + "name": "onOperation", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": "Use the locations array instead", + "description": null, + "isDeprecated": true, + "name": "onFragment", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": "Use the locations array instead", + "description": null, + "isDeprecated": true, + "name": "onField", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "__Directive", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -7560,7 +7858,7 @@ "name": "doi", "type": { "kind": "SCALAR", - "name": "String", + "name": "Doi", "ofType": null } }, @@ -7894,6 +8192,20 @@ "ofType": null } } + }, + { + "defaultValue": null, + "description": null, + "name": "units", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "LengthUnit", + "ofType": null + } + } } ], "deprecationReason": null, @@ -8297,6 +8609,20 @@ "ofType": null } } + }, + { + "defaultValue": null, + "description": null, + "name": "units", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "LengthUnit", + "ofType": null + } + } } ], "deprecationReason": null, @@ -8814,21 +9140,7 @@ { "defaultValue": null, "description": null, - "name": "workId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "contributorId", + "name": "contributionId", "type": { "kind": "NON_NULL", "name": null, @@ -8838,20 +9150,6 @@ "ofType": null } } - }, - { - "defaultValue": null, - "description": null, - "name": "contributionType", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "ContributionType", - "ofType": null - } - } } ], "deprecationReason": null, @@ -8935,21 +9233,7 @@ { "defaultValue": null, "description": null, - "name": "seriesId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "workId", + "name": "issueId", "type": { "kind": "NON_NULL", "name": null, @@ -9180,6 +9464,22 @@ "description": "A work published as a number in a periodical.", "enumValues": null, "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "issueId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -9239,7 +9539,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -9255,7 +9555,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -9304,6 +9604,20 @@ "enumValues": null, "fields": null, "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "contributionId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + }, { "defaultValue": null, "description": null, @@ -9417,6 +9731,20 @@ "ofType": null } } + }, + { + "defaultValue": null, + "description": null, + "name": "contributionOrdinal", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } } ], "interfaces": null, @@ -9642,16 +9970,6 @@ "name": "Int", "possibleTypes": null }, - { - "description": "NaiveDateTime", - "enumValues": null, - "fields": null, - "inputFields": null, - "interfaces": null, - "kind": "SCALAR", - "name": "NaiveDateTime", - "possibleTypes": null - }, { "description": null, "enumValues": [ @@ -9799,6 +10117,20 @@ "ofType": null } } + }, + { + "defaultValue": null, + "description": null, + "name": "contributionOrdinal", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } } ], "interfaces": null, @@ -9937,156 +10269,99 @@ "possibleTypes": null }, { - "description": null, - "enumValues": [ - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BIC" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BISAC" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "THEMA" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "LCC" - }, + "description": "Field and order to use when sorting languages list", + "enumValues": null, + "fields": null, + "inputFields": [ { - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "CUSTOM" + "name": "field", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "LanguageField", + "ofType": null + } + } }, { - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "KEYWORD" + "name": "direction", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "Direction", + "ofType": null + } + } } ], - "fields": null, - "inputFields": null, "interfaces": null, - "kind": "ENUM", - "name": "SubjectType", + "kind": "INPUT_OBJECT", + "name": "LanguageOrderBy", "possibleTypes": null }, { - "description": "Field to use when sorting publishers list", - "enumValues": [ - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "PUBLISHER_ID" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "PUBLISHER_NAME" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "PUBLISHER_SHORTNAME" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "PUBLISHER_URL" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "CREATED_AT" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "UPDATED_AT" - } - ], + "description": "13-digit International Standard Book Number, with its parts separated by hyphens", + "enumValues": null, "fields": null, "inputFields": null, "interfaces": null, - "kind": "ENUM", - "name": "PublisherField", + "kind": "SCALAR", + "name": "Isbn", "possibleTypes": null }, { - "description": "Field to use when sorting series list", + "description": null, "enumValues": [ { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "SERIES_ID" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "SERIES_TYPE" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "SERIES_NAME" + "name": "BIC" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "ISSNPRINT" + "name": "BISAC" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "ISSNDIGITAL" + "name": "THEMA" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "SERIES_URL" + "name": "LCC" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "CREATED_AT" + "name": "CUSTOM" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "UPDATED_AT" + "name": "KEYWORD" } ], "fields": null, "inputFields": null, "interfaces": null, "kind": "ENUM", - "name": "SeriesField", + "name": "SubjectType", "possibleTypes": null }, { @@ -10129,42 +10404,50 @@ "possibleTypes": null }, { - "description": "Field and order to use when sorting languages list", - "enumValues": null, - "fields": null, - "inputFields": [ + "description": "Field to use when sorting publishers list", + "enumValues": [ { - "defaultValue": null, + "deprecationReason": null, "description": null, - "name": "field", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "LanguageField", - "ofType": null - } - } + "isDeprecated": false, + "name": "PUBLISHER_ID" }, { - "defaultValue": null, + "deprecationReason": null, "description": null, - "name": "direction", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "Direction", - "ofType": null - } - } + "isDeprecated": false, + "name": "PUBLISHER_NAME" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "PUBLISHER_SHORTNAME" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "PUBLISHER_URL" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CREATED_AT" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "UPDATED_AT" } ], + "fields": null, + "inputFields": null, "interfaces": null, - "kind": "INPUT_OBJECT", - "name": "LanguageOrderBy", + "kind": "ENUM", + "name": "PublisherField", "possibleTypes": null }, { @@ -10215,7 +10498,23 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "imprintId", + "name": "imprintId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "publisherId", "type": { "kind": "NON_NULL", "name": null, @@ -10265,7 +10564,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -10281,7 +10580,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -10303,9 +10602,70 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "\"\"", + "description": "A query string to search. This argument is a test, do not rely on it. At present it simply searches for case insensitive literals on full_title, doi, reference, short_abstract, long_abstract, and landing_page", + "name": "filter", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": "{field: \"FULL_TITLE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "WorkOrderBy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "workType", + "type": { + "kind": "ENUM", + "name": "WorkType", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific status to filter by", + "name": "workStatus", + "type": { + "kind": "ENUM", + "name": "WorkStatus", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get works linked to this imprint", "isDeprecated": false, "name": "works", "type": { @@ -10378,6 +10738,44 @@ "name": "WorkOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "workType", + "type": { + "kind": "ENUM", + "name": "WorkType", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific status to filter by", + "name": "workStatus", + "type": { + "kind": "ENUM", + "name": "WorkStatus", + "ofType": null + } } ], "deprecationReason": null, @@ -10433,6 +10831,37 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "doi", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Doi", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Query a single work using its DOI", + "isDeprecated": false, + "name": "workByDoi", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Work", + "ofType": null + } + } + }, { "args": [ { @@ -10444,6 +10873,44 @@ "name": "String", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "workType", + "type": { + "kind": "ENUM", + "name": "WorkType", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific status to filter by", + "name": "workStatus", + "type": { + "kind": "ENUM", + "name": "WorkStatus", + "ofType": null + } } ], "deprecationReason": null, @@ -10501,6 +10968,34 @@ "name": "PublicationOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "publicationType", + "type": { + "kind": "ENUM", + "name": "PublicationType", + "ofType": null + } } ], "deprecationReason": null, @@ -10567,6 +11062,34 @@ "name": "String", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "publicationType", + "type": { + "kind": "ENUM", + "name": "PublicationType", + "ofType": null + } } ], "deprecationReason": null, @@ -10624,6 +11147,24 @@ "name": "PublisherOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } } ], "deprecationReason": null, @@ -10666,7 +11207,7 @@ } ], "deprecationReason": null, - "description": "Query a publisher using its id", + "description": "Query a single publisher using its id", "isDeprecated": false, "name": "publisher", "type": { @@ -10690,6 +11231,24 @@ "name": "String", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } } ], "deprecationReason": null, @@ -10747,6 +11306,24 @@ "name": "ImprintOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } } ], "deprecationReason": null, @@ -10803,7 +11380,36 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "\"\"", + "description": "A query string to search. This argument is a test, do not rely on it. At present it simply searches for case insensitive literals on imprint_name and imprint_url", + "name": "filter", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + } + ], "deprecationReason": null, "description": "Get the total number of imprints", "isDeprecated": false, @@ -10972,6 +11578,34 @@ "name": "ContributionOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "contributionType", + "type": { + "kind": "ENUM", + "name": "ContributionType", + "ofType": null + } } ], "deprecationReason": null, @@ -11001,48 +11635,20 @@ { "defaultValue": null, "description": null, - "name": "workId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "contributorId", + "name": "contributionId", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "contributionType", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "ContributionType", + "name": "Uuid", "ofType": null } } } ], "deprecationReason": null, - "description": "Query a single contribution using its identifiers", + "description": "Query a single contribution using its id", "isDeprecated": false, "name": "contribution", "type": { @@ -11056,7 +11662,18 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": null, + "description": null, + "name": "contributionType", + "type": { + "kind": "ENUM", + "name": "ContributionType", + "ofType": null + } + } + ], "deprecationReason": null, "description": "Get the total number of contributions", "isDeprecated": false, @@ -11112,6 +11729,34 @@ "name": "SeriesOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "seriesType", + "type": { + "kind": "ENUM", + "name": "SeriesType", + "ofType": null + } } ], "deprecationReason": null, @@ -11178,6 +11823,34 @@ "name": "String", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "seriesType", + "type": { + "kind": "ENUM", + "name": "SeriesType", + "ofType": null + } } ], "deprecationReason": null, @@ -11225,6 +11898,24 @@ "name": "IssueOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } } ], "deprecationReason": null, @@ -11254,21 +11945,7 @@ { "defaultValue": null, "description": null, - "name": "seriesId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Uuid", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "workId", + "name": "issueId", "type": { "kind": "NON_NULL", "name": null, @@ -11281,7 +11958,7 @@ } ], "deprecationReason": null, - "description": "Query a single issue using its identifiers", + "description": "Query a single issue using its id", "isDeprecated": false, "name": "issue", "type": { @@ -11341,6 +12018,44 @@ "name": "LanguageOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific language to filter by", + "name": "languageCode", + "type": { + "kind": "ENUM", + "name": "LanguageCode", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific relation to filter by", + "name": "languageRelation", + "type": { + "kind": "ENUM", + "name": "LanguageRelation", + "ofType": null + } } ], "deprecationReason": null, @@ -11397,7 +12112,28 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": null, + "description": null, + "name": "languageCode", + "type": { + "kind": "ENUM", + "name": "LanguageCode", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "languageRelation", + "type": { + "kind": "ENUM", + "name": "LanguageRelation", + "ofType": null + } + } + ], "deprecationReason": null, "description": "Get the total number of languages associated to works", "isDeprecated": false, @@ -11443,6 +12179,34 @@ "name": "PriceOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific currency to filter by", + "name": "currencyCode", + "type": { + "kind": "ENUM", + "name": "CurrencyCode", + "ofType": null + } } ], "deprecationReason": null, @@ -11499,7 +12263,18 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": null, + "description": null, + "name": "currencyCode", + "type": { + "kind": "ENUM", + "name": "CurrencyCode", + "ofType": null + } + } + ], "deprecationReason": null, "description": "Get the total number of prices associated to works", "isDeprecated": false, @@ -11536,6 +12311,16 @@ "ofType": null } }, + { + "defaultValue": "\"\"", + "description": "A query string to search. This argument is a test, do not rely on it. At present it simply searches for case insensitive literals on subject_code", + "name": "filter", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "defaultValue": "{field: \"SUBJECT_TYPE\", direction: \"ASC\"}", "description": "The order in which to sort the results", @@ -11545,6 +12330,34 @@ "name": "SubjectOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "subjectType", + "type": { + "kind": "ENUM", + "name": "SubjectType", + "ofType": null + } } ], "deprecationReason": null, @@ -11601,7 +12414,32 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": null, + "description": null, + "name": "filter", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "subjectType", + "type": { + "kind": "ENUM", + "name": "SubjectType", + "ofType": null + } + } + ], "deprecationReason": null, "description": "Get the total number of subjects associated to works", "isDeprecated": false, @@ -11713,7 +12551,18 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "\"\"", + "description": "A query string to search. This argument is a test, do not rely on it. At present it simply searches for case insensitive literals on funderName and funderDoi", + "name": "filter", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + ], "deprecationReason": null, "description": "Get the total number of funders", "isDeprecated": false, @@ -11759,6 +12608,24 @@ "name": "FundingOrderBy", "ofType": null } + }, + { + "defaultValue": "[]", + "description": "If set, only shows results connected to publishers with these IDs", + "name": "publishers", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + } } ], "deprecationReason": null, @@ -11831,20 +12698,79 @@ } } ], - "inputFields": null, - "interfaces": [], - "kind": "OBJECT", - "name": "QueryRoot", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "QueryRoot", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "SCALAR", + "name": "Boolean", + "possibleTypes": null + }, + { + "description": "Field to use when sorting series list", + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "SERIES_ID" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "SERIES_TYPE" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "SERIES_NAME" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "ISSN_PRINT" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "ISSN_DIGITAL" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "SERIES_URL" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CREATED_AT" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "UPDATED_AT" + } + ], "fields": null, "inputFields": null, "interfaces": null, - "kind": "SCALAR", - "name": "Boolean", + "kind": "ENUM", + "name": "SeriesField", "possibleTypes": null }, { @@ -12270,6 +13196,22 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "imprintId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Uuid", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -12278,7 +13220,7 @@ "name": "doi", "type": { "kind": "SCALAR", - "name": "String", + "name": "Doi", "ofType": null } }, @@ -12307,9 +13249,20 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "\"MM\"", + "description": "Unit of measurement in which to represent the width (mm, cm or in)", + "name": "units", + "type": { + "kind": "ENUM", + "name": "LengthUnit", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Width of the physical Work (in mm, cm or in)", "isDeprecated": false, "name": "width", "type": { @@ -12319,9 +13272,20 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "\"MM\"", + "description": "Unit of measurement in which to represent the height (mm, cm or in)", + "name": "units", + "type": { + "kind": "ENUM", + "name": "LengthUnit", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Height of the physical Work (in mm, cm or in)", "isDeprecated": false, "name": "height", "type": { @@ -12549,7 +13513,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -12565,7 +13529,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "NaiveDateTime", + "name": "Timestamp", "ofType": null } } @@ -12587,9 +13551,50 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"CONTRIBUTION_TYPE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "ContributionOrderBy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "contributionType", + "type": { + "kind": "ENUM", + "name": "ContributionType", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get contributions linked to this work", "isDeprecated": false, "name": "contributions", "type": { @@ -12611,9 +13616,60 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"LANGUAGE_CODE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "LanguageOrderBy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific language to filter by", + "name": "languageCode", + "type": { + "kind": "ENUM", + "name": "LanguageCode", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific relation to filter by", + "name": "languageRelation", + "type": { + "kind": "ENUM", + "name": "LanguageRelation", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get languages linked to this work", "isDeprecated": false, "name": "languages", "type": { @@ -12666,6 +13722,16 @@ "ofType": null } }, + { + "defaultValue": "{field: \"PUBLICATION_TYPE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "PublicationOrderBy", + "ofType": null + } + }, { "defaultValue": null, "description": "A specific type to filter by", @@ -12730,6 +13796,26 @@ "name": "String", "ofType": null } + }, + { + "defaultValue": "{field: \"SUBJECT_TYPE\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "SubjectOrderBy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "A specific type to filter by", + "name": "subjectType", + "type": { + "kind": "ENUM", + "name": "SubjectType", + "ofType": null + } } ], "deprecationReason": null, @@ -12755,9 +13841,40 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"PROGRAM\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "FundingOrderBy", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get fundings linked to this work", "isDeprecated": false, "name": "fundings", "type": { @@ -12779,9 +13896,40 @@ } }, { - "args": [], + "args": [ + { + "defaultValue": "100", + "description": "The number of items to return", + "name": "limit", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "0", + "description": "The number of items to skip", + "name": "offset", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": "{field: \"ISSUE_ORDINAL\", direction: \"ASC\"}", + "description": "The order in which to sort the results", + "name": "order", + "type": { + "kind": "INPUT_OBJECT", + "name": "IssueOrderBy", + "ofType": null + } + } + ], "deprecationReason": null, - "description": null, + "description": "Get issues linked to this work", "isDeprecated": false, "name": "issues", "type": { @@ -12809,6 +13957,16 @@ "name": "Work", "possibleTypes": null }, + { + "description": "ORCID (Open Researcher and Contributor ID) identifier. Expressed as `^https:\\/\\/orcid\\.org\\/0000-000(1-[5-9]|2-[0-9]|3-[0-4])\\d{3}-\\d{3}[\\dX]$`", + "enumValues": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "SCALAR", + "name": "Orcid", + "possibleTypes": null + }, { "description": null, "enumValues": [ @@ -12901,7 +14059,7 @@ "name": "funderDoi", "type": { "kind": "SCALAR", - "name": "String", + "name": "Doi", "ofType": null } } @@ -12950,7 +14108,7 @@ "name": "isbn", "type": { "kind": "SCALAR", - "name": "String", + "name": "Isbn", "ofType": null } }, diff --git a/thoth-client/src/queries.rs b/thoth-client/src/queries.rs index acfa6c06..e4ccc24e 100644 --- a/thoth-client/src/queries.rs +++ b/thoth-client/src/queries.rs @@ -2,6 +2,9 @@ use std::fmt; use chrono::naive::NaiveDate; use graphql_client::GraphQLQuery; +use thoth_api::model::Doi; +use thoth_api::model::Isbn; +use thoth_api::model::Orcid; use uuid::Uuid; #[derive(GraphQLQuery)] diff --git a/thoth-errors/Cargo.toml b/thoth-errors/Cargo.toml index 180c30c0..09009e80 100644 --- a/thoth-errors/Cargo.toml +++ b/thoth-errors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-errors" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" diff --git a/thoth-export-server/Cargo.toml b/thoth-export-server/Cargo.toml index 71f9ed8d..fcbc9e89 100644 --- a/thoth-export-server/Cargo.toml +++ b/thoth-export-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-export-server" -version = "0.4.6" +version = "0.4.7" authors = ["Javier Arias ", "Ross Higman "] edition = "2018" license = "Apache-2.0" @@ -9,9 +9,9 @@ repository = "https://github.com/thoth-pub/thoth" readme = "README.md" [dependencies] -thoth-api = { version = "0.4.6", path = "../thoth-api" } -thoth-errors = { version = "0.4.6", path = "../thoth-errors" } -thoth-client = { version = "0.4.6", path = "../thoth-client" } +thoth-api = { version = "0.4.7", path = "../thoth-api" } +thoth-errors = { version = "0.4.7", path = "../thoth-errors" } +thoth-client = { version = "0.4.7", path = "../thoth-client" } actix-web = "3.3.2" actix-cors = "0.5.4" chrono = { version = "0.4", features = ["serde"] } diff --git a/thoth-export-server/src/csv/csv_thoth.rs b/thoth-export-server/src/csv/csv_thoth.rs index 3c00213f..d0340b10 100644 --- a/thoth-export-server/src/csv/csv_thoth.rs +++ b/thoth-export-server/src/csv/csv_thoth.rs @@ -26,8 +26,18 @@ struct CsvThothRow { license: Option, copyright_holder: String, landing_page: Option, - width: Option, - height: Option, + #[serde(rename = "width (mm)")] + width_mm: Option, + #[serde(rename = "width (cm)")] + width_cm: Option, + #[serde(rename = "width (in)")] + width_in: Option, + #[serde(rename = "height (mm)")] + height_mm: Option, + #[serde(rename = "height (cm)")] + height_cm: Option, + #[serde(rename = "height (in)")] + height_in: Option, page_count: Option, page_breakdown: Option, image_count: Option, @@ -96,14 +106,18 @@ impl From for CsvThothRow { title: work.title, subtitle: work.subtitle, edition: work.edition, - doi: work.doi, + doi: work.doi.map(|d| d.to_string()), publication_date: work.publication_date.map(|d| d.to_string()), publication_place: work.place, license: work.license, copyright_holder: work.copyright_holder, landing_page: work.landing_page, - width: work.width, - height: work.height, + width_mm: work.width_mm, + width_cm: work.width_cm, + width_in: work.width_in, + height_mm: work.height_mm, + height_cm: work.height_cm, + height_in: work.height_in, page_count: work.page_count, page_breakdown: work.page_breakdown, image_count: work.image_count, @@ -214,7 +228,10 @@ impl CsvCell for WorkPublications { format!( "(\"{:?}\", \"{}\", \"{}\", {})", self.publication_type, - self.isbn.clone().unwrap_or_else(|| "".to_string()), + self.isbn + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| "".to_string()), self.publication_url .clone() .unwrap_or_else(|| "".to_string()), @@ -240,7 +257,8 @@ impl CsvCell for WorkContributions { self.institution.clone().unwrap_or_else(|| "".to_string()), self.contributor .orcid - .clone() + .as_ref() + .map(|o| o.to_string()) .unwrap_or_else(|| "".to_string()) ) } @@ -291,7 +309,8 @@ impl CsvCell for WorkFundings { self.funder.funder_name, self.funder .funder_doi - .clone() + .as_ref() + .map(|d| d.to_string()) .unwrap_or_else(|| "".to_string()), self.program.clone().unwrap_or_else(|| "".to_string()), self.project_name.clone().unwrap_or_else(|| "".to_string()), @@ -308,6 +327,9 @@ mod tests { use csv::QuoteStyle; use lazy_static::lazy_static; use std::str::FromStr; + use thoth_api::model::Doi; + use thoth_api::model::Isbn; + use thoth_api::model::Orcid; use thoth_client::{ ContributionType, CurrencyCode, LanguageCode, LanguageRelation, PublicationType, WorkContributionsContributor, WorkImprint, WorkImprintPublisher, WorkStatus, WorkType, @@ -323,7 +345,7 @@ mod tests { subtitle: Some("Book Subtitle".to_string()), work_type: WorkType::MONOGRAPH, edition: 1, - doi: Some("https://doi.org/10.00001/BOOK.0001".to_string()), + doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()), publication_date: None, license: Some("http://creativecommons.org/licenses/by/4.0/".to_string()), copyright_holder: "Author 1; Author 2".to_string(), @@ -331,8 +353,12 @@ mod tests { long_abstract: Some("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vel libero eleifend, ultrices purus vitae, suscipit ligula. Aliquam ornare quam et nulla vestibulum, id euismod tellus malesuada. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam ornare bibendum ex nec dapibus. Proin porta risus elementum odio feugiat tempus. Etiam eu felis ac metus viverra ornare. In consectetur neque sed feugiat ornare. Mauris at purus fringilla orci tincidunt pulvinar sed a massa. Nullam vestibulum posuere augue, sit amet tincidunt nisl pulvinar ac.".to_string()), general_note: None, place: Some("León, Spain".to_string()), - width: Some(156.0), - height: Some(234.0), + width_mm: Some(156.0), + width_cm: Some(15.6), + width_in: Some(6.14), + height_mm: Some(234.0), + height_cm: Some(23.4), + height_in: Some(9.21), page_count: Some(334), page_breakdown: Some("x+334".to_string()), image_count: Some(15), @@ -349,6 +375,7 @@ mod tests { imprint_name: "OA Editions Imprint".to_string(), publisher: WorkImprintPublisher { publisher_name: "OA Editions".to_string(), + publisher_url: None, }, }, issues: vec![], @@ -363,7 +390,7 @@ mod tests { institution: None, contribution_ordinal: 1, contributor: WorkContributionsContributor { - orcid: Some("https://orcid.org/0000-0000-0000-0001".to_string()), + orcid: Some(Orcid::from_str("https://orcid.org/0000-0002-0000-0001").unwrap()), }, }, WorkContributions { @@ -392,7 +419,7 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-BBBB-000000000002").unwrap(), publication_type: PublicationType::PAPERBACK, publication_url: Some("https://www.book.com/paperback".to_string()), - isbn: Some("978-1-00000-000-0".to_string()), + isbn: Some(Isbn::from_str("978-3-16-148410-0").unwrap()), prices: vec![ WorkPublicationsPrices { currency_code: CurrencyCode::EUR, @@ -412,7 +439,7 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-CCCC-000000000003").unwrap(), publication_type: PublicationType::HARDBACK, publication_url: Some("https://www.book.com/hardback".to_string()), - isbn: Some("978-1-00000-000-1".to_string()), + isbn: Some(Isbn::from_str("978-1-4028-9462-6").unwrap()), prices: vec![ WorkPublicationsPrices { currency_code: CurrencyCode::EUR, @@ -432,7 +459,7 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-DDDD-000000000004").unwrap(), publication_type: PublicationType::PDF, publication_url: Some("https://www.book.com/pdf".to_string()), - isbn: Some("978-1-00000-000-2".to_string()), + isbn: Some(Isbn::from_str("978-1-56619-909-4").unwrap()), prices: vec![], }, WorkPublications { @@ -446,7 +473,7 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-FFFF-000000000006").unwrap(), publication_type: PublicationType::XML, publication_url: Some("https://www.book.com/xml".to_string()), - isbn: Some("978-1-00000-000-3".to_string()), + isbn: Some(Isbn::from_str("978-92-95055-02-5").unwrap()), prices: vec![], }, ], @@ -491,8 +518,8 @@ mod tests { }; } - const TEST_RESULT: &str = r#""publisher","imprint","work_type","work_status","title","subtitle","edition","doi","publication_date","publication_place","license","copyright_holder","landing_page","width","height","page_count","page_breakdown","image_count","table_count","audio_count","video_count","lccn","oclc","short_abstract","long_abstract","general_note","toc","cover_url","cover_caption","contributions [(type, first_name, last_name, full_name, institution, orcid)]","publications [(type, isbn, url, [(ISO_4217_currency, price)])]","series [(type, name, issn_print, issn_digital, url, issue)]","languages [(relation, ISO_639-3/B_language, is_main)]","BIC [code]","THEMA [code]","BISAC [code]","LCC [code]","custom_categories [category]","keywords [keyword]","funding [(funder, funder_doi, program, project, grant, jurisdiction)]" -"OA Editions","OA Editions Imprint","MONOGRAPH","ACTIVE","Book Title","Book Subtitle","1","https://doi.org/10.00001/BOOK.0001","","León, Spain","http://creativecommons.org/licenses/by/4.0/","Author 1; Author 2","https://www.book.com","156.0","234.0","334","x+334","15","","","","","","Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vel libero eleifend, ultrices purus vitae, suscipit ligula. Aliquam ornare quam et nulla vestibulum, id euismod tellus malesuada. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.","Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vel libero eleifend, ultrices purus vitae, suscipit ligula. Aliquam ornare quam et nulla vestibulum, id euismod tellus malesuada. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam ornare bibendum ex nec dapibus. Proin porta risus elementum odio feugiat tempus. Etiam eu felis ac metus viverra ornare. In consectetur neque sed feugiat ornare. Mauris at purus fringilla orci tincidunt pulvinar sed a massa. Nullam vestibulum posuere augue, sit amet tincidunt nisl pulvinar ac.","","","https://www.book.com/cover","","[(""AUTHOR"", ""Author"", ""1"", ""Author 1"", """", ""https://orcid.org/0000-0000-0000-0001""),(""AUTHOR"", ""Author"", ""2"", ""Author 2"", """", """")]","[(""PAPERBACK"", ""978-1-00000-000-0"", ""https://www.book.com/paperback"", [(""EUR"", ""25.95""),(""GBP"", ""22.95""),(""USD"", ""31.95"")]),(""HARDBACK"", ""978-1-00000-000-1"", ""https://www.book.com/hardback"", [(""EUR"", ""36.95""),(""GBP"", ""32.95""),(""USD"", ""40.95"")]),(""PDF"", ""978-1-00000-000-2"", ""https://www.book.com/pdf"", ),(""HTML"", """", ""https://www.book.com/html"", ),(""XML"", ""978-1-00000-000-3"", ""https://www.book.com/xml"", )]","","[(""ORIGINAL"", ""SPA"", ""true"")]","[""AAA"",""AAB""]","","[""AAA000000"",""AAA000001""]","","[""Category1""]","[""keyword1"",""keyword2""]","" + const TEST_RESULT: &str = r#""publisher","imprint","work_type","work_status","title","subtitle","edition","doi","publication_date","publication_place","license","copyright_holder","landing_page","width (mm)","width (cm)","width (in)","height (mm)","height (cm)","height (in)","page_count","page_breakdown","image_count","table_count","audio_count","video_count","lccn","oclc","short_abstract","long_abstract","general_note","toc","cover_url","cover_caption","contributions [(type, first_name, last_name, full_name, institution, orcid)]","publications [(type, isbn, url, [(ISO_4217_currency, price)])]","series [(type, name, issn_print, issn_digital, url, issue)]","languages [(relation, ISO_639-3/B_language, is_main)]","BIC [code]","THEMA [code]","BISAC [code]","LCC [code]","custom_categories [category]","keywords [keyword]","funding [(funder, funder_doi, program, project, grant, jurisdiction)]" +"OA Editions","OA Editions Imprint","MONOGRAPH","ACTIVE","Book Title","Book Subtitle","1","10.00001/BOOK.0001","","León, Spain","http://creativecommons.org/licenses/by/4.0/","Author 1; Author 2","https://www.book.com","156.0","15.6","6.14","234.0","23.4","9.21","334","x+334","15","","","","","","Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vel libero eleifend, ultrices purus vitae, suscipit ligula. Aliquam ornare quam et nulla vestibulum, id euismod tellus malesuada. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.","Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vel libero eleifend, ultrices purus vitae, suscipit ligula. Aliquam ornare quam et nulla vestibulum, id euismod tellus malesuada. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam ornare bibendum ex nec dapibus. Proin porta risus elementum odio feugiat tempus. Etiam eu felis ac metus viverra ornare. In consectetur neque sed feugiat ornare. Mauris at purus fringilla orci tincidunt pulvinar sed a massa. Nullam vestibulum posuere augue, sit amet tincidunt nisl pulvinar ac.","","","https://www.book.com/cover","","[(""AUTHOR"", ""Author"", ""1"", ""Author 1"", """", ""0000-0002-0000-0001""),(""AUTHOR"", ""Author"", ""2"", ""Author 2"", """", """")]","[(""PAPERBACK"", ""978-3-16-148410-0"", ""https://www.book.com/paperback"", [(""EUR"", ""25.95""),(""GBP"", ""22.95""),(""USD"", ""31.95"")]),(""HARDBACK"", ""978-1-4028-9462-6"", ""https://www.book.com/hardback"", [(""EUR"", ""36.95""),(""GBP"", ""32.95""),(""USD"", ""40.95"")]),(""PDF"", ""978-1-56619-909-4"", ""https://www.book.com/pdf"", ),(""HTML"", """", ""https://www.book.com/html"", ),(""XML"", ""978-92-95055-02-5"", ""https://www.book.com/xml"", )]","","[(""ORIGINAL"", ""SPA"", ""true"")]","[""AAA"",""AAB""]","","[""AAA000000"",""AAA000001""]","","[""Category1""]","[""keyword1"",""keyword2""]","" "#; #[test] diff --git a/thoth-export-server/src/csv/kbart_oclc.rs b/thoth-export-server/src/csv/kbart_oclc.rs index 396a6a02..e450cbb6 100644 --- a/thoth-export-server/src/csv/kbart_oclc.rs +++ b/thoth-export-server/src/csv/kbart_oclc.rs @@ -87,12 +87,12 @@ impl TryFrom for KbartOclcRow { if publication.publication_type == PublicationType::PDF && publication.isbn.is_some() { - online_identifier = publication.isbn.clone(); + online_identifier = publication.isbn.as_ref().map(|i| i.to_string()); } if publication.publication_type == PublicationType::PAPERBACK { print_edition_exists = true; if publication.isbn.is_some() { - print_identifier = publication.isbn.clone(); + print_identifier = publication.isbn.as_ref().map(|i| i.to_string()); } } if publication.publication_type == PublicationType::HARDBACK { @@ -142,7 +142,7 @@ impl TryFrom for KbartOclcRow { num_last_issue_online: None, title_url: work.landing_page.unwrap(), first_author, - title_id: work.doi, + title_id: work.doi.map(|d| d.to_string()), embargo_info: None, coverage_depth: "fulltext".to_string(), notes: None, @@ -177,6 +177,9 @@ mod tests { use csv::QuoteStyle; use lazy_static::lazy_static; use std::str::FromStr; + use thoth_api::model::Doi; + use thoth_api::model::Isbn; + use thoth_api::model::Orcid; use thoth_client::{ ContributionType, PublicationType, WorkContributions, WorkContributionsContributor, WorkImprint, WorkImprintPublisher, WorkIssues, WorkIssuesSeries, WorkPublications, @@ -193,7 +196,7 @@ mod tests { subtitle: Some("Separate Subtitle".to_string()), work_type: WorkType::MONOGRAPH, edition: 1, - doi: Some("https://doi.org/10.00001/BOOK.0001".to_string()), + doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()), publication_date: Some(chrono::NaiveDate::from_ymd(1999, 12, 31)), license: Some("http://creativecommons.org/licenses/by/4.0/".to_string()), copyright_holder: "Author 1; Author 2".to_string(), @@ -203,8 +206,12 @@ mod tests { ), general_note: None, place: Some("León, Spain".to_string()), - width: Some(156.0), - height: Some(234.0), + width_mm: Some(156.0), + width_cm: Some(15.6), + width_in: Some(6.14), + height_mm: Some(234.0), + height_cm: Some(23.4), + height_in: Some(9.21), page_count: Some(334), page_breakdown: Some("x+334".to_string()), image_count: Some(15), @@ -221,6 +228,7 @@ mod tests { imprint_name: "OA Editions Imprint".to_string(), publisher: WorkImprintPublisher { publisher_name: "OA Editions".to_string(), + publisher_url: None, }, }, issues: vec![ @@ -256,7 +264,9 @@ mod tests { institution: None, contribution_ordinal: 1, contributor: WorkContributionsContributor { - orcid: Some("https://orcid.org/0000-0000-0000-0001".to_string()), + orcid: Some( + Orcid::from_str("https://orcid.org/0000-0002-0000-0001").unwrap() + ), }, }, WorkContributions { @@ -277,21 +287,21 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-BBBB-000000000002").unwrap(), publication_type: PublicationType::PAPERBACK, publication_url: Some("https://www.book.com/paperback".to_string()), - isbn: Some("978-1-00000-000-0".to_string()), + isbn: Some(Isbn::from_str("978-3-16-148410-0").unwrap()), prices: vec![], }, WorkPublications { publication_id: Uuid::from_str("00000000-0000-0000-CCCC-000000000003").unwrap(), publication_type: PublicationType::HARDBACK, publication_url: Some("https://www.book.com/hardback".to_string()), - isbn: Some("978-1-00000-000-1".to_string()), + isbn: Some(Isbn::from_str("978-1-4028-9462-6").unwrap()), prices: vec![], }, WorkPublications { publication_id: Uuid::from_str("00000000-0000-0000-DDDD-000000000004").unwrap(), publication_type: PublicationType::PDF, publication_url: Some("https://www.book.com/pdf".to_string()), - isbn: Some("978-1-00000-000-2".to_string()), + isbn: Some(Isbn::from_str("978-1-56619-909-4").unwrap()), prices: vec![], }, WorkPublications { @@ -305,7 +315,7 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-FFFF-000000000006").unwrap(), publication_type: PublicationType::XML, publication_url: Some("https://www.book.com/xml".to_string()), - isbn: Some("978-1-00000-000-3".to_string()), + isbn: Some(Isbn::from_str("978-92-95055-02-5").unwrap()), prices: vec![], }, ], @@ -314,7 +324,7 @@ mod tests { }; } - const TEST_RESULT: &str = "publication_title\tprint_identifier\tonline_identifier\tdate_first_issue_online\tnum_first_vol_online\tnum_first_issue_online\tdate_last_issue_online\tnum_last_vol_online\tnum_last_issue_online\ttitle_url\tfirst_author\ttitle_id\tembargo_info\tcoverage_depth\tnotes\tpublisher_name\tpublication_type\tdate_monograph_published_print\tdate_monograph_published_online\tmonograph_volume\tmonograph_edition\tfirst_editor\tparent_publication_title_id\tpreceding_publication_title_id\taccess_type\nBook Title: Separate Subtitle\t978-1-00000-000-0\t978-1-00000-000-2\t\t\t\t\t\t\thttps://www.book.com\tFirst\thttps://doi.org/10.00001/BOOK.0001\t\tfulltext\t\tOA Editions\tMonograph\t1999\t1999\t20\t1\t\t8765-4321\t\tF\n"; + const TEST_RESULT: &str = "publication_title\tprint_identifier\tonline_identifier\tdate_first_issue_online\tnum_first_vol_online\tnum_first_issue_online\tdate_last_issue_online\tnum_last_vol_online\tnum_last_issue_online\ttitle_url\tfirst_author\ttitle_id\tembargo_info\tcoverage_depth\tnotes\tpublisher_name\tpublication_type\tdate_monograph_published_print\tdate_monograph_published_online\tmonograph_volume\tmonograph_edition\tfirst_editor\tparent_publication_title_id\tpreceding_publication_title_id\taccess_type\nBook Title: Separate Subtitle\t978-3-16-148410-0\t978-1-56619-909-4\t\t\t\t\t\t\thttps://www.book.com\tFirst\t10.00001/BOOK.0001\t\tfulltext\t\tOA Editions\tMonograph\t1999\t1999\t20\t1\t\t8765-4321\t\tF\n"; #[test] fn test_kbart_oclc() { diff --git a/thoth-export-server/src/data.rs b/thoth-export-server/src/data.rs index 2d544d7d..d35844ee 100644 --- a/thoth-export-server/src/data.rs +++ b/thoth-export-server/src/data.rs @@ -19,6 +19,18 @@ lazy_static! { format: concat!(env!("THOTH_EXPORT_API"), "/formats/onix_3.0"), accepted_by: vec![concat!(env!("THOTH_EXPORT_API"), "/platforms/oapen"),], }, + Specification { + id: "onix_3.0::jstor", + name: "JSTOR ONIX 3.0", + format: concat!(env!("THOTH_EXPORT_API"), "/formats/onix_3.0"), + accepted_by: vec![concat!(env!("THOTH_EXPORT_API"), "/platforms/jstor"),], + }, + Specification { + id: "onix_2.1::ebsco_host", + name: "EBSCO Host ONIX 2.1", + format: concat!(env!("THOTH_EXPORT_API"), "/formats/onix_2.1"), + accepted_by: vec![concat!(env!("THOTH_EXPORT_API"), "/platforms/ebsco_host"),], + }, Specification { id: "csv::thoth", name: "Thoth CSV", @@ -63,6 +75,22 @@ lazy_static! { "/specifications/onix_3.0::oapen" ),], }, + Platform { + id: "jstor", + name: "JSTOR", + accepts: vec![concat!( + env!("THOTH_EXPORT_API"), + "/specifications/onix_3.0::jstor" + ),], + }, + Platform { + id: "ebsco_host", + name: "EBSCO Host", + accepts: vec![concat!( + env!("THOTH_EXPORT_API"), + "/specifications/onix_2.1::ebsco_host" + ),], + }, Platform { id: "oclc_kb", name: "OCLC KB", @@ -117,6 +145,15 @@ lazy_static! { concat!(env!("THOTH_EXPORT_API"), "/specifications/onix_3.0::oapen"), ], }, + Format { + id: "onix_2.1", + name: "ONIX", + version: Some("2.1"), + specifications: vec![concat!( + env!("THOTH_EXPORT_API"), + "/specifications/onix_2.1::ebsco_host" + ),], + }, Format { id: "csv", name: "CSV", diff --git a/thoth-export-server/src/record.rs b/thoth-export-server/src/record.rs index 95f3ea91..e074e74e 100644 --- a/thoth-export-server/src/record.rs +++ b/thoth-export-server/src/record.rs @@ -10,17 +10,21 @@ use thoth_client::Work; use thoth_errors::{ThothError, ThothResult}; use crate::csv::{CsvSpecification, CsvThoth, KbartOclc}; -use crate::xml::{Onix3Oapen, Onix3ProjectMuse, XmlSpecification}; +use crate::xml::{Onix21EbscoHost, Onix3Jstor, Onix3Oapen, Onix3ProjectMuse, XmlSpecification}; pub(crate) trait AsRecord {} impl AsRecord for Vec {} pub const DELIMITER_COMMA: u8 = b','; pub const DELIMITER_TAB: u8 = b'\t'; +pub const XML_DECLARATION: &str = "\n"; +pub const DOCTYPE_ONIX21_REF: &str = "\n"; pub(crate) enum MetadataSpecification { Onix3ProjectMuse(Onix3ProjectMuse), Onix3Oapen(Onix3Oapen), + Onix3Jstor(Onix3Jstor), + Onix21EbscoHost(Onix21EbscoHost), CsvThoth(CsvThoth), KbartOclc(KbartOclc), } @@ -54,6 +58,8 @@ where match &self.specification { MetadataSpecification::Onix3ProjectMuse(_) => Self::XML_MIME_TYPE, MetadataSpecification::Onix3Oapen(_) => Self::XML_MIME_TYPE, + MetadataSpecification::Onix3Jstor(_) => Self::XML_MIME_TYPE, + MetadataSpecification::Onix21EbscoHost(_) => Self::XML_MIME_TYPE, MetadataSpecification::CsvThoth(_) => Self::CSV_MIME_TYPE, MetadataSpecification::KbartOclc(_) => Self::TXT_MIME_TYPE, } @@ -63,6 +69,8 @@ where match &self.specification { MetadataSpecification::Onix3ProjectMuse(_) => self.xml_file_name(), MetadataSpecification::Onix3Oapen(_) => self.xml_file_name(), + MetadataSpecification::Onix3Jstor(_) => self.xml_file_name(), + MetadataSpecification::Onix21EbscoHost(_) => self.xml_file_name(), MetadataSpecification::CsvThoth(_) => self.csv_file_name(), MetadataSpecification::KbartOclc(_) => self.txt_file_name(), } @@ -98,9 +106,17 @@ impl MetadataRecord> { fn generate(&self) -> ThothResult { match &self.specification { MetadataSpecification::Onix3ProjectMuse(onix3_project_muse) => { - onix3_project_muse.generate(&self.data) + onix3_project_muse.generate(&self.data, None) + } + MetadataSpecification::Onix3Oapen(onix3_oapen) => { + onix3_oapen.generate(&self.data, None) + } + MetadataSpecification::Onix3Jstor(onix3_jstor) => { + onix3_jstor.generate(&self.data, None) + } + MetadataSpecification::Onix21EbscoHost(onix21_ebsco_host) => { + onix21_ebsco_host.generate(&self.data, Some(DOCTYPE_ONIX21_REF)) } - MetadataSpecification::Onix3Oapen(onix3_oapen) => onix3_oapen.generate(&self.data), MetadataSpecification::CsvThoth(csv_thoth) => { csv_thoth.generate(&self.data, QuoteStyle::Always, DELIMITER_COMMA) } @@ -157,6 +173,10 @@ impl FromStr for MetadataSpecification { Ok(MetadataSpecification::Onix3ProjectMuse(Onix3ProjectMuse {})) } "onix_3.0::oapen" => Ok(MetadataSpecification::Onix3Oapen(Onix3Oapen {})), + "onix_3.0::jstor" => Ok(MetadataSpecification::Onix3Jstor(Onix3Jstor {})), + "onix_2.1::ebsco_host" => { + Ok(MetadataSpecification::Onix21EbscoHost(Onix21EbscoHost {})) + } "csv::thoth" => Ok(MetadataSpecification::CsvThoth(CsvThoth {})), "kbart::oclc" => Ok(MetadataSpecification::KbartOclc(KbartOclc {})), _ => Err(ThothError::InvalidMetadataSpecification(input.to_string())), @@ -169,6 +189,8 @@ impl ToString for MetadataSpecification { match self { MetadataSpecification::Onix3ProjectMuse(_) => "onix_3.0::project_muse".to_string(), MetadataSpecification::Onix3Oapen(_) => "onix_3.0::oapen".to_string(), + MetadataSpecification::Onix3Jstor(_) => "onix_3.0::jstor".to_string(), + MetadataSpecification::Onix21EbscoHost(_) => "onix_2.1::ebsco_host".to_string(), MetadataSpecification::CsvThoth(_) => "csv::thoth".to_string(), MetadataSpecification::KbartOclc(_) => "kbart::oclc".to_string(), } @@ -217,6 +239,24 @@ mod tests { to_test.file_name(), "onix_3.0__oapen__some_id.xml".to_string() ); + let to_test = MetadataRecord::new( + "some_id".to_string(), + MetadataSpecification::Onix3Jstor(Onix3Jstor {}), + vec![], + ); + assert_eq!( + to_test.file_name(), + "onix_3.0__jstor__some_id.xml".to_string() + ); + let to_test = MetadataRecord::new( + "some_id".to_string(), + MetadataSpecification::Onix21EbscoHost(Onix21EbscoHost {}), + vec![], + ); + assert_eq!( + to_test.file_name(), + "onix_2.1__ebsco_host__some_id.xml".to_string() + ); let to_test = MetadataRecord::new( "some_id".to_string(), MetadataSpecification::KbartOclc(KbartOclc {}), diff --git a/thoth-export-server/src/xml/mod.rs b/thoth-export-server/src/xml/mod.rs index c7a2da35..8327da8a 100644 --- a/thoth-export-server/src/xml/mod.rs +++ b/thoth-export-server/src/xml/mod.rs @@ -1,3 +1,4 @@ +use crate::record::XML_DECLARATION; use std::collections::HashMap; use std::io::Write; use thoth_client::Work; @@ -42,9 +43,12 @@ pub(crate) fn write_full_element_block) -> T } pub(crate) trait XmlSpecification { - fn generate(&self, works: &[Work]) -> ThothResult { - let mut buffer = Vec::new(); + fn generate(&self, works: &[Work], doctype: Option<&str>) -> ThothResult { + let mut buffer = format!("{}{}", XML_DECLARATION, doctype.unwrap_or_default()) + .as_bytes() + .to_vec(); let mut writer = EmitterConfig::new() + .write_document_declaration(false) .perform_indent(true) .create_writer(&mut buffer); Self::handle_event(&mut writer, works) @@ -79,3 +83,7 @@ mod onix3_project_muse; pub(crate) use onix3_project_muse::Onix3ProjectMuse; mod onix3_oapen; pub(crate) use onix3_oapen::Onix3Oapen; +mod onix3_jstor; +pub(crate) use onix3_jstor::Onix3Jstor; +mod onix21_ebsco_host; +pub(crate) use onix21_ebsco_host::Onix21EbscoHost; diff --git a/thoth-export-server/src/xml/onix21_ebsco_host.rs b/thoth-export-server/src/xml/onix21_ebsco_host.rs new file mode 100644 index 00000000..1ad42b1a --- /dev/null +++ b/thoth-export-server/src/xml/onix21_ebsco_host.rs @@ -0,0 +1,1101 @@ +use chrono::Utc; +use std::collections::HashMap; +use std::io::Write; +use thoth_client::{ + ContributionType, CurrencyCode, LanguageRelation, PublicationType, SubjectType, Work, + WorkContributions, WorkIssues, WorkLanguages, WorkPublications, WorkStatus, WorkSubjects, +}; +use xml::writer::{EventWriter, XmlEvent}; + +use super::{write_element_block, XmlElement, XmlSpecification}; +use crate::xml::{write_full_element_block, XmlElementBlock}; +use thoth_errors::{ThothError, ThothResult}; + +pub struct Onix21EbscoHost {} + +impl XmlSpecification for Onix21EbscoHost { + fn handle_event(w: &mut EventWriter, works: &[Work]) -> ThothResult<()> { + write_full_element_block("ONIXMessage", None, None, w, |w| { + write_element_block("Header", w, |w| { + write_element_block("FromCompany", w, |w| { + w.write(XmlEvent::Characters("Thoth")).map_err(|e| e.into()) + })?; + write_element_block("FromEmail", w, |w| { + w.write(XmlEvent::Characters("info@thoth.pub")) + .map_err(|e| e.into()) + })?; + write_element_block("SentDate", w, |w| { + w.write(XmlEvent::Characters( + &Utc::today().format("%Y%m%d").to_string(), + )) + .map_err(|e| e.into()) + }) + })?; + + match works.len() { + 0 => Err(ThothError::IncompleteMetadataRecord( + "onix_2.1::ebsco_host".to_string(), + "Not enough data".to_string(), + )), + 1 => XmlElementBlock::::xml_element(works.first().unwrap(), w), + _ => { + for work in works.iter() { + XmlElementBlock::::xml_element(work, w).ok(); + } + Ok(()) + } + } + }) + } +} + +impl XmlElementBlock for Work { + fn xml_element(&self, w: &mut EventWriter) -> ThothResult<()> { + let work_id = format!("urn:uuid:{}", self.work_id.to_string()); + let (main_isbn, isbns) = get_publications_data(&self.publications); + // We only submit PDFs and EPUBs to EBSCO Host, so don't + // generate ONIX for works which do not have either + let pdf_url = self + .publications + .iter() + .find(|p| p.publication_type.eq(&PublicationType::PDF)) + .and_then(|p| p.publication_url.as_ref()); + let epub_url = self + .publications + .iter() + .find(|p| p.publication_type.eq(&PublicationType::EPUB)) + .and_then(|p| p.publication_url.as_ref()); + if pdf_url.is_some() || epub_url.is_some() { + write_element_block("Product", w, |w| { + write_element_block("RecordReference", w, |w| { + w.write(XmlEvent::Characters(&work_id)) + .map_err(|e| e.into()) + })?; + // 03 Notification confirmed on publication + write_element_block("NotificationType", w, |w| { + w.write(XmlEvent::Characters("03")).map_err(|e| e.into()) + })?; + // 01 Publisher + write_element_block("RecordSourceType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("ProductIdentifier", w, |w| { + // 01 Proprietary + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&work_id)) + .map_err(|e| e.into()) + }) + })?; + write_element_block("ProductIdentifier", w, |w| { + // 15 ISBN-13 + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("15")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&main_isbn)) + .map_err(|e| e.into()) + }) + })?; + if let Some(doi) = &self.doi { + write_element_block("ProductIdentifier", w, |w| { + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&doi.to_string())) + .map_err(|e| e.into()) + }) + })?; + } + // DG Electronic book text in proprietary or open standard format + write_element_block("ProductForm", w, |w| { + w.write(XmlEvent::Characters("DG")).map_err(|e| e.into()) + })?; + write_element_block("EpubType", w, |w| { + // 002 PDF + let mut epub_type = "002"; + // We definitely have either a PDF URL or an EPUB URL (or both) + if pdf_url.is_none() { + // 029 EPUB + epub_type = "029"; + } + w.write(XmlEvent::Characters(epub_type)) + .map_err(|e| e.into()) + })?; + for issue in &self.issues { + XmlElementBlock::::xml_element(issue, w).ok(); + } + write_element_block("Title", w, |w| { + // 01 Distinctive title (book) + write_element_block("TitleType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + if let Some(subtitle) = &self.subtitle { + write_element_block("TitleText", w, |w| { + w.write(XmlEvent::Characters(&self.title)) + .map_err(|e| e.into()) + })?; + write_element_block("Subtitle", w, |w| { + w.write(XmlEvent::Characters(subtitle)) + .map_err(|e| e.into()) + }) + } else { + write_element_block("TitleText", w, |w| { + w.write(XmlEvent::Characters(&self.full_title)) + .map_err(|e| e.into()) + }) + } + })?; + write_element_block("WorkIdentifier", w, |w| { + // 01 Proprietary + write_element_block("WorkIDType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("IDTypeName", w, |w| { + w.write(XmlEvent::Characters("Thoth WorkID")) + .map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&work_id)) + .map_err(|e| e.into()) + }) + })?; + let mut websites: HashMap = HashMap::new(); + if let Some(pdf) = pdf_url { + websites.insert( + pdf.to_string(), + ( + "29".to_string(), + "Publisher's website: download the title".to_string(), + ), + ); + } + if let Some(epub) = epub_url { + websites.insert( + epub.to_string(), + ( + "29".to_string(), + "Publisher's website: download the title".to_string(), + ), + ); + } + if let Some(landing_page) = &self.landing_page { + websites.insert( + landing_page.to_string(), + ( + "01".to_string(), + "Publisher's website: web shop".to_string(), + ), + ); + } + for (url, description) in websites.iter() { + write_element_block("Website", w, |w| { + // 01 Publisher’s corporate website + write_element_block("WebsiteRole", w, |w| { + w.write(XmlEvent::Characters(&description.0)) + .map_err(|e| e.into()) + })?; + write_element_block("WebsiteDescription", w, |w| { + w.write(XmlEvent::Characters(&description.1)) + .map_err(|e| e.into()) + })?; + write_element_block("WebsiteLink", w, |w| { + w.write(XmlEvent::Characters(url)).map_err(|e| e.into()) + }) + })?; + } + for contribution in &self.contributions { + XmlElementBlock::::xml_element(contribution, w).ok(); + } + for language in &self.languages { + XmlElementBlock::::xml_element(language, w).ok(); + } + if let Some(page_count) = self.page_count { + write_element_block("Extent", w, |w| { + // 00 Main content + write_element_block("ExtentType", w, |w| { + w.write(XmlEvent::Characters("00")).map_err(|e| e.into()) + })?; + write_element_block("ExtentValue", w, |w| { + w.write(XmlEvent::Characters(&page_count.to_string())) + .map_err(|e| e.into()) + })?; + // 03 Pages + write_element_block("ExtentUnit", w, |w| { + w.write(XmlEvent::Characters("03")).map_err(|e| e.into()) + }) + })?; + } + for subject in &self.subjects { + XmlElementBlock::::xml_element(subject, w).ok(); + } + write_element_block("Audience", w, |w| { + // 01 ONIX audience codes + write_element_block("AudienceCodeType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + // 06 Professional and scholarly + write_element_block("AudienceCodeValue", w, |w| { + w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) + }) + })?; + write_element_block("OtherText", w, |w| { + // 47 Open access statement + // "Should always be accompanied by a link to the complete license (see code 46)" + // (not specified as required by EBSCO Host themselves) + write_element_block("TextTypeCode", w, |w| { + w.write(XmlEvent::Characters("47")).map_err(|e| e.into()) + })?; + write_element_block("Text", w, |w| { + w.write(XmlEvent::Characters("Open access - no commercial use")) + .map_err(|e| e.into()) + }) + })?; + if let Some(license) = &self.license { + write_element_block("OtherText", w, |w| { + // 46 License + write_element_block("TextTypeCode", w, |w| { + w.write(XmlEvent::Characters("46")).map_err(|e| e.into()) + })?; + write_element_block("Text", w, |w| { + w.write(XmlEvent::Characters(license)).map_err(|e| e.into()) + }) + })?; + } + if let Some(labstract) = &self.long_abstract { + write_element_block("OtherText", w, |w| { + // 03 Long description + write_element_block("TextTypeCode", w, |w| { + w.write(XmlEvent::Characters("03")).map_err(|e| e.into()) + })?; + // 06 Default text format + write_element_block("TextFormat", w, |w| { + w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) + })?; + write_element_block("Text", w, |w| { + w.write(XmlEvent::Characters(labstract)) + .map_err(|e| e.into()) + }) + })?; + } + if let Some(cover_url) = &self.cover_url { + write_element_block("MediaFile", w, |w| { + // 04 Image: front cover + write_element_block("MediaFileTypeCode", w, |w| { + w.write(XmlEvent::Characters("04")).map_err(|e| e.into()) + })?; + // 01 URL + write_element_block("MediaFileLinkTypeCode", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("MediaFileLink", w, |w| { + w.write(XmlEvent::Characters(cover_url)) + .map_err(|e| e.into()) + }) + })?; + } + write_element_block("Imprint", w, |w| { + write_element_block("ImprintName", w, |w| { + w.write(XmlEvent::Characters(&self.imprint.imprint_name)) + .map_err(|e| e.into()) + }) + })?; + write_element_block("Publisher", w, |w| { + // 01 Publisher + write_element_block("PublishingRole", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("PublisherName", w, |w| { + w.write(XmlEvent::Characters(&self.imprint.publisher.publisher_name)) + .map_err(|e| e.into()) + })?; + if let Some(publisher_url) = &self.imprint.publisher.publisher_url { + write_element_block("Website", w, |w| { + write_element_block("WebsiteLink", w, |w| { + w.write(XmlEvent::Characters(publisher_url)) + .map_err(|e| e.into()) + }) + })?; + } + Ok(()) + })?; + if let Some(place) = &self.place { + write_element_block("CityOfPublication", w, |w| { + w.write(XmlEvent::Characters(place)).map_err(|e| e.into()) + })?; + } + XmlElement::::xml_element(&self.work_status, w)?; + if let Some(date) = self.publication_date { + write_element_block("PublicationDate", w, |w| { + w.write(XmlEvent::Characters(&date.format("%Y%m%d").to_string())) + .map_err(|e| e.into()) + })?; + write_element_block("CopyrightYear", w, |w| { + w.write(XmlEvent::Characters(&date.format("%Y").to_string())) + .map_err(|e| e.into()) + })?; + } + write_element_block("SalesRights", w, |w| { + // 02 For sale with non-exclusive rights in the specified countries or territories + write_element_block("SalesRightsType", w, |w| { + w.write(XmlEvent::Characters("02")).map_err(|e| e.into()) + })?; + write_element_block("RightsTerritory", w, |w| { + w.write(XmlEvent::Characters("WORLD")).map_err(|e| e.into()) + }) + })?; + if !isbns.is_empty() { + for isbn in &isbns { + write_element_block("RelatedProduct", w, |w| { + // 06 Alternative format + write_element_block("RelationCode", w, |w| { + w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) + })?; + write_element_block("ProductIdentifier", w, |w| { + // 15 ISBN-13 + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("15")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(isbn)).map_err(|e| e.into()) + }) + }) + })?; + } + } + write_element_block("SupplyDetail", w, |w| { + write_element_block("SupplierName", w, |w| { + w.write(XmlEvent::Characters(&self.imprint.publisher.publisher_name)) + .map_err(|e| e.into()) + })?; + // 09 Publisher to end-customers + write_element_block("SupplierRole", w, |w| { + w.write(XmlEvent::Characters("09")).map_err(|e| e.into()) + })?; + // 99 Contact supplier + write_element_block("ProductAvailability", w, |w| { + w.write(XmlEvent::Characters("99")).map_err(|e| e.into()) + })?; + // R Restrictions apply, see note + write_element_block("AudienceRestrictionFlag", w, |w| { + w.write(XmlEvent::Characters("R")).map_err(|e| e.into()) + })?; + write_element_block("AudienceRestrictionNote", w, |w| { + w.write(XmlEvent::Characters("Open access")) + .map_err(|e| e.into()) + })?; + // Works are distributed to EBSCO Host as combined PDF/EPUB + // "digital bundles" - PDF and EPUB may have different prices + // so give the higher of the two. If both are free, EBSCO Host + // request a price point of "0.01 USD" for Open Access titles. + let pdf_price = self + .publications + .iter() + .find(|p| p.publication_type.eq(&PublicationType::PDF)) + .and_then(|p| { + p.prices + .iter() + .find(|pr| pr.currency_code.eq(&CurrencyCode::USD)) + .map(|pr| pr.unit_price) + }) + .unwrap_or_default(); + let epub_price = self + .publications + .iter() + .find(|p| p.publication_type.eq(&PublicationType::EPUB)) + .and_then(|p| { + p.prices + .iter() + .find(|pr| pr.currency_code.eq(&CurrencyCode::USD)) + .map(|pr| pr.unit_price) + }) + .unwrap_or_default(); + let bundle_price = pdf_price.max(epub_price.max(0.01)); + write_element_block("Price", w, |w| { + // 02 RRP including tax + write_element_block("PriceTypeCode", w, |w| { + w.write(XmlEvent::Characters("02")).map_err(|e| e.into()) + })?; + write_element_block("PriceAmount", w, |w| { + w.write(XmlEvent::Characters(&bundle_price.to_string())) + .map_err(|e| e.into()) + })?; + write_element_block("CurrencyCode", w, |w| { + w.write(XmlEvent::Characters("USD")).map_err(|e| e.into()) + }) + }) + }) + }) + } else { + Err(ThothError::IncompleteMetadataRecord( + "onix_2.1::ebsco_host".to_string(), + "No PDF or EPUB URL".to_string(), + )) + } + } +} + +fn get_publications_data(publications: &[WorkPublications]) -> (String, Vec) { + let mut main_isbn = "".to_string(); + let mut isbns: Vec = Vec::new(); + + for publication in publications { + if let Some(isbn) = &publication.isbn.as_ref().map(|i| i.to_string()) { + isbns.push(isbn.replace("-", "")); + // The default product ISBN is the PDF's + if publication.publication_type.eq(&PublicationType::PDF) { + main_isbn = isbn.replace("-", ""); + } + // Books that don't have a PDF ISBN will use the paperback's + if publication.publication_type.eq(&PublicationType::PAPERBACK) && main_isbn.is_empty() + { + main_isbn = isbn.replace("-", ""); + } + } + } + + (main_isbn, isbns) +} + +impl XmlElement for WorkStatus { + const ELEMENT: &'static str = "PublishingStatus"; + + fn value(&self) -> &'static str { + match self { + WorkStatus::UNSPECIFIED => "00", + WorkStatus::CANCELLED => "01", + WorkStatus::FORTHCOMING => "02", + WorkStatus::POSTPONED_INDEFINITELY => "03", + WorkStatus::ACTIVE => "04", + WorkStatus::NO_LONGER_OUR_PRODUCT => "05", + WorkStatus::OUT_OF_STOCK_INDEFINITELY => "06", + WorkStatus::OUT_OF_PRINT => "07", + WorkStatus::INACTIVE => "08", + WorkStatus::UNKNOWN => "09", + WorkStatus::REMAINDERED => "10", + WorkStatus::WITHDRAWN_FROM_SALE => "11", + WorkStatus::RECALLED => "15", + WorkStatus::Other(_) => unreachable!(), + } + } +} + +impl XmlElement for SubjectType { + const ELEMENT: &'static str = "SubjectSchemeIdentifier"; + + fn value(&self) -> &'static str { + match self { + SubjectType::BIC => "12", + SubjectType::BISAC => "10", + SubjectType::KEYWORD => "20", + SubjectType::LCC => "04", + SubjectType::THEMA => "93", + SubjectType::CUSTOM => "B2", + SubjectType::Other(_) => unreachable!(), + } + } +} + +impl XmlElement for LanguageRelation { + const ELEMENT: &'static str = "LanguageRole"; + + fn value(&self) -> &'static str { + match self { + LanguageRelation::ORIGINAL => "01", + LanguageRelation::TRANSLATED_FROM => "02", + LanguageRelation::TRANSLATED_INTO => "01", + LanguageRelation::Other(_) => unreachable!(), + } + } +} + +impl XmlElement for ContributionType { + const ELEMENT: &'static str = "ContributorRole"; + + fn value(&self) -> &'static str { + match self { + ContributionType::AUTHOR => "A01", + ContributionType::EDITOR => "B01", + ContributionType::TRANSLATOR => "B06", + ContributionType::PHOTOGRAPHER => "A13", + ContributionType::ILUSTRATOR => "A12", + ContributionType::MUSIC_EDITOR => "B25", + ContributionType::FOREWORD_BY => "A23", + ContributionType::INTRODUCTION_BY => "A24", + ContributionType::AFTERWORD_BY => "A19", + ContributionType::PREFACE_BY => "A15", + ContributionType::Other(_) => unreachable!(), + } + } +} + +impl XmlElementBlock for WorkContributions { + fn xml_element(&self, w: &mut EventWriter) -> ThothResult<()> { + write_element_block("Contributor", w, |w| { + write_element_block("SequenceNumber", w, |w| { + w.write(XmlEvent::Characters(&self.contribution_ordinal.to_string())) + .map_err(|e| e.into()) + })?; + XmlElement::::xml_element(&self.contribution_type, w)?; + + if let Some(orcid) = &self.contributor.orcid { + write_element_block("PersonNameIdentifier", w, |w| { + // 01 Proprietary + write_element_block("PersonNameIDType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("IDTypeName", w, |w| { + w.write(XmlEvent::Characters("ORCID")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&orcid.to_string())) + .map_err(|e| e.into()) + }) + })?; + } + if let Some(first_name) = &self.first_name { + write_element_block("NamesBeforeKey", w, |w| { + w.write(XmlEvent::Characters(first_name)) + .map_err(|e| e.into()) + })?; + write_element_block("KeyNames", w, |w| { + w.write(XmlEvent::Characters(&self.last_name)) + .map_err(|e| e.into()) + })?; + } else { + write_element_block("PersonName", w, |w| { + w.write(XmlEvent::Characters(&self.full_name)) + .map_err(|e| e.into()) + })?; + } + Ok(()) + }) + } +} + +impl XmlElementBlock for WorkLanguages { + fn xml_element(&self, w: &mut EventWriter) -> ThothResult<()> { + write_element_block("Language", w, |w| { + XmlElement::::xml_element(&self.language_relation, w).ok(); + // not worth implementing XmlElement for LanguageCode as all cases would + // need to be exhaustively matched and the codes are equivalent anyway + write_element_block("LanguageCode", w, |w| { + w.write(XmlEvent::Characters( + &self.language_code.to_string().to_lowercase(), + )) + .map_err(|e| e.into()) + }) + }) + } +} + +impl XmlElementBlock for WorkIssues { + fn xml_element(&self, w: &mut EventWriter) -> ThothResult<()> { + write_element_block("Series", w, |w| { + write_element_block("TitleOfSeries", w, |w| { + w.write(XmlEvent::Characters(&self.series.series_name)) + .map_err(|e| e.into()) + })?; + write_element_block("NumberWithinSeries", w, |w| { + w.write(XmlEvent::Characters(&self.issue_ordinal.to_string())) + .map_err(|e| e.into()) + }) + }) + } +} + +impl XmlElementBlock for WorkSubjects { + fn xml_element(&self, w: &mut EventWriter) -> ThothResult<()> { + write_element_block("Subject", w, |w| { + XmlElement::::xml_element(&self.subject_type, w)?; + match self.subject_type { + SubjectType::KEYWORD | SubjectType::CUSTOM => { + write_element_block("SubjectHeadingText", w, |w| { + w.write(XmlEvent::Characters(&self.subject_code)) + .map_err(|e| e.into()) + }) + } + _ => write_element_block("SubjectCode", w, |w| { + w.write(XmlEvent::Characters(&self.subject_code)) + .map_err(|e| e.into()) + }), + } + }) + } +} + +#[cfg(test)] +mod tests { + // Testing note: XML nodes cannot be guaranteed to be output in the same order every time + // We therefore rely on `assert!(contains)` rather than `assert_eq!` + use super::*; + use std::str::FromStr; + use thoth_api::model::Doi; + use thoth_api::model::Isbn; + use thoth_api::model::Orcid; + use thoth_client::WorkPublicationsPrices; + use thoth_client::{ + ContributionType, LanguageCode, LanguageRelation, PublicationType, + WorkContributionsContributor, WorkImprint, WorkImprintPublisher, WorkIssuesSeries, + WorkStatus, WorkType, + }; + use uuid::Uuid; + + fn generate_test_output(input: &impl XmlElementBlock) -> String { + // Helper function based on `XmlSpecification::generate` + let mut buffer = Vec::new(); + let mut writer = xml::writer::EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut buffer); + let wrapped_output = XmlElementBlock::::xml_element(input, &mut writer) + .map(|_| buffer) + .and_then(|onix| { + String::from_utf8(onix) + .map_err(|_| ThothError::InternalError("Could not parse XML".to_string())) + }); + assert!(wrapped_output.is_ok()); + wrapped_output.unwrap() + } + + #[test] + fn test_onix21_ebsco_host_contributions() { + let mut test_contribution = WorkContributions { + contribution_type: ContributionType::AUTHOR, + first_name: Some("Author".to_string()), + last_name: "1".to_string(), + full_name: "Author 1".to_string(), + main_contribution: true, + biography: None, + institution: None, + contribution_ordinal: 1, + contributor: WorkContributionsContributor { + orcid: Some(Orcid::from_str("https://orcid.org/0000-0002-0000-0001").unwrap()), + }, + }; + + // Test standard output + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" 1"#)); + assert!(output.contains(r#" A01"#)); + assert!(output.contains(r#" "#)); + assert!(output.contains(r#" 01"#)); + assert!(output.contains(r#" ORCID"#)); + assert!(output.contains(r#" 0000-0002-0000-0001"#)); + assert!(output.contains(r#" "#)); + // Given name is output as NamesBeforeKey and family name as KeyNames + assert!(output.contains(r#" Author"#)); + assert!(output.contains(r#" 1"#)); + // PersonName is not output when given name is supplied + assert!(!output.contains(r#" Author 1"#)); + + // Change all possible values to test that output is updated + test_contribution.contribution_type = ContributionType::EDITOR; + test_contribution.contribution_ordinal = 2; + test_contribution.contributor.orcid = None; + test_contribution.first_name = None; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" 2"#)); + assert!(output.contains(r#" B01"#)); + // No ORCID supplied + assert!(!output.contains(r#" "#)); + assert!(!output.contains(r#" 01"#)); + assert!(!output.contains(r#" ORCID"#)); + assert!(!output.contains(r#" 0000-0002-0000-0001"#)); + assert!(!output.contains(r#" "#)); + // No given name supplied, so PersonName is output instead of KeyNames and NamesBeforeKey + assert!(!output.contains(r#" Author"#)); + assert!(!output.contains(r#" 1"#)); + assert!(output.contains(r#" Author 1"#)); + + // Test all remaining contributor roles + test_contribution.contribution_type = ContributionType::TRANSLATOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" B06"#)); + test_contribution.contribution_type = ContributionType::PHOTOGRAPHER; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" A13"#)); + test_contribution.contribution_type = ContributionType::ILUSTRATOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" A12"#)); + test_contribution.contribution_type = ContributionType::MUSIC_EDITOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" B25"#)); + test_contribution.contribution_type = ContributionType::FOREWORD_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" A23"#)); + test_contribution.contribution_type = ContributionType::INTRODUCTION_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" A24"#)); + test_contribution.contribution_type = ContributionType::AFTERWORD_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" A19"#)); + test_contribution.contribution_type = ContributionType::PREFACE_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" A15"#)); + } + + #[test] + fn test_onix21_ebsco_host_languages() { + let mut test_language = WorkLanguages { + language_code: LanguageCode::SPA, + language_relation: LanguageRelation::TRANSLATED_FROM, + main_language: true, + }; + + // Test standard output + let output = generate_test_output(&test_language); + assert!(output.contains(r#" 02"#)); + assert!(output.contains(r#" spa"#)); + + // Change all possible values to test that output is updated + test_language.language_code = LanguageCode::WEL; + for language_relation in [ + LanguageRelation::ORIGINAL, + LanguageRelation::TRANSLATED_INTO, + ] { + test_language.language_relation = language_relation; + let output = generate_test_output(&test_language); + assert!(output.contains(r#" 01"#)); + assert!(output.contains(r#" wel"#)); + } + } + + #[test] + fn test_onix21_ebsco_host_issues() { + let mut test_issue = WorkIssues { + issue_ordinal: 1, + series: WorkIssuesSeries { + series_type: thoth_client::SeriesType::JOURNAL, + series_name: "Name of series".to_string(), + issn_print: "1234-5678".to_string(), + issn_digital: "8765-4321".to_string(), + series_url: None, + }, + }; + + // Test standard output + let output = generate_test_output(&test_issue); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" Name of series"#)); + assert!(output.contains(r#" 1"#)); + + // Change all possible values to test that output is updated + test_issue.issue_ordinal = 2; + test_issue.series.series_name = "Different series".to_string(); + let output = generate_test_output(&test_issue); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" Different series"#)); + assert!(output.contains(r#" 2"#)); + } + + #[test] + fn test_onix21_ebsco_host_subjects() { + let mut test_subject = WorkSubjects { + subject_code: "AAB".to_string(), + subject_type: SubjectType::BIC, + subject_ordinal: 1, + }; + + // Test BIC output + let output = generate_test_output(&test_subject); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" 12"#)); + assert!(output.contains(r#" AAB"#)); + + // Test BISAC output + test_subject.subject_code = "AAA000000".to_string(); + test_subject.subject_type = SubjectType::BISAC; + let output = generate_test_output(&test_subject); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" 10"#)); + assert!(output.contains(r#" AAA000000"#)); + + // Test LCC output + test_subject.subject_code = "JA85".to_string(); + test_subject.subject_type = SubjectType::LCC; + let output = generate_test_output(&test_subject); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" 04"#)); + assert!(output.contains(r#" JA85"#)); + + // Test Thema output + test_subject.subject_code = "JWA".to_string(); + test_subject.subject_type = SubjectType::THEMA; + let output = generate_test_output(&test_subject); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" 93"#)); + assert!(output.contains(r#" JWA"#)); + + // Test keyword output + test_subject.subject_code = "keyword1".to_string(); + test_subject.subject_type = SubjectType::KEYWORD; + let output = generate_test_output(&test_subject); + assert!(output.contains(r#""#)); + assert!(output.contains(r#" 20"#)); + assert!(output.contains(r#" keyword1"#)); + + // Test custom output + test_subject.subject_code = "custom1".to_string(); + test_subject.subject_type = SubjectType::CUSTOM; + let output = generate_test_output(&test_subject); + assert!(output.contains(r#" B2"#)); + assert!(output.contains(r#" custom1"#)); + } + + #[test] + fn test_onix21_ebsco_host_works() { + let mut test_work = Work { + work_id: Uuid::from_str("00000000-0000-0000-AAAA-000000000001").unwrap(), + work_status: WorkStatus::ACTIVE, + full_title: "Book Title: Book Subtitle".to_string(), + title: "Book Title".to_string(), + subtitle: Some("Separate Subtitle".to_string()), + work_type: WorkType::MONOGRAPH, + edition: 1, + doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()), + publication_date: Some(chrono::NaiveDate::from_ymd(1999, 12, 31)), + license: Some("https://creativecommons.org/licenses/by/4.0/".to_string()), + copyright_holder: "Author 1; Author 2".to_string(), + short_abstract: None, + long_abstract: Some("Lorem ipsum dolor sit amet".to_string()), + general_note: None, + place: Some("León, Spain".to_string()), + width_mm: None, + width_cm: None, + width_in: None, + height_mm: None, + height_cm: None, + height_in: None, + page_count: Some(334), + page_breakdown: None, + image_count: None, + table_count: None, + audio_count: None, + video_count: None, + landing_page: Some("https://www.book.com".to_string()), + toc: None, + lccn: None, + oclc: None, + cover_url: Some("https://www.book.com/cover".to_string()), + cover_caption: None, + imprint: WorkImprint { + imprint_name: "OA Editions Imprint".to_string(), + publisher: WorkImprintPublisher { + publisher_name: "OA Editions".to_string(), + publisher_url: Some("https://www.publisher.com".to_string()), + }, + }, + issues: vec![], + contributions: vec![], + languages: vec![], + publications: vec![ + WorkPublications { + publication_id: Uuid::from_str("00000000-0000-0000-AAAA-000000000001").unwrap(), + publication_type: PublicationType::EPUB, + publication_url: Some("https://www.book.com/epub".to_string()), + isbn: Some(Isbn::from_str("978-3-16-148410-0").unwrap()), + prices: vec![], + }, + WorkPublications { + publication_id: Uuid::from_str("00000000-0000-0000-DDDD-000000000004").unwrap(), + publication_type: PublicationType::PDF, + publication_url: Some("https://www.book.com/pdf".to_string()), + isbn: Some(Isbn::from_str("978-1-56619-909-4").unwrap()), + prices: vec![ + WorkPublicationsPrices { + currency_code: CurrencyCode::EUR, + unit_price: 5.95, + }, + WorkPublicationsPrices { + currency_code: CurrencyCode::GBP, + unit_price: 4.95, + }, + WorkPublicationsPrices { + currency_code: CurrencyCode::USD, + unit_price: 7.99, + }, + ], + }, + ], + subjects: vec![], + fundings: vec![], + }; + + // Test standard output + let output = generate_test_output(&test_work); + assert!(output.contains(r#""#)); + assert!(output.contains( + r#" urn:uuid:00000000-0000-0000-aaaa-000000000001"# + )); + assert!(output.contains(r#" 03"#)); + assert!(output.contains(r#" 01"#)); + assert!(output.contains(r#" "#)); + assert!(output.contains(r#" 01"#)); + assert!(output + .contains(r#" urn:uuid:00000000-0000-0000-aaaa-000000000001"#)); + assert!(output.contains(r#" 15"#)); + assert!(output.contains(r#" 9783161484100"#)); + assert!(output.contains(r#" 06"#)); + assert!(output.contains(r#" 10.00001/BOOK.0001"#)); + assert!(output.contains(r#" DG"#)); + assert!(output.contains(r#" 002"#)); + assert!(output.contains(r#" "#)); + assert!(output.contains(r#" <TitleType>01</TitleType>"#)); + assert!(output.contains(r#" <TitleText>Book Title</TitleText>"#)); + assert!(output.contains(r#" <Subtitle>Separate Subtitle</Subtitle>"#)); + assert!(output.contains(r#" <WorkIdentifier>"#)); + assert!(output.contains(r#" <WorkIDType>01</WorkIDType>"#)); + assert!(output.contains(r#" <IDTypeName>Thoth WorkID</IDTypeName>"#)); + assert!(output + .contains(r#" <IDValue>urn:uuid:00000000-0000-0000-aaaa-000000000001</IDValue>"#)); + assert!(output.contains(r#" <Website>"#)); + assert!(output.contains(r#" <WebsiteRole>01</WebsiteRole>"#)); + assert!(output.contains( + r#" <WebsiteDescription>Publisher's website: web shop</WebsiteDescription>"# + )); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com</WebsiteLink>"#)); + assert!(output.contains(r#" <WebsiteRole>29</WebsiteRole>"#)); + assert!(output.contains(r#" <WebsiteDescription>Publisher's website: download the title</WebsiteDescription>"#)); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com/epub</WebsiteLink>"#)); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com/pdf</WebsiteLink>"#)); + assert!(output.contains(r#" <Extent>"#)); + assert!(output.contains(r#" <ExtentType>00</ExtentType>"#)); + assert!(output.contains(r#" <ExtentValue>334</ExtentValue>"#)); + assert!(output.contains(r#" <ExtentUnit>03</ExtentUnit>"#)); + assert!(output.contains(r#" <Audience>"#)); + assert!(output.contains(r#" <AudienceCodeType>01</AudienceCodeType>"#)); + assert!(output.contains(r#" <AudienceCodeValue>06</AudienceCodeValue>"#)); + assert!(output.contains(r#" <OtherText>"#)); + assert!(output.contains(r#" <TextTypeCode>47</TextTypeCode>"#)); + assert!(output.contains(r#" <Text>Open access - no commercial use</Text>"#)); + assert!(output.contains(r#" <TextTypeCode>46</TextTypeCode>"#)); + assert!(output.contains(r#" <Text>https://creativecommons.org/licenses/by/4.0/</Text>"#)); + assert!(output.contains(r#" <TextTypeCode>03</TextTypeCode>"#)); + assert!(output.contains(r#" <TextFormat>06</TextFormat>"#)); + assert!(output.contains(r#" <Text>Lorem ipsum dolor sit amet</Text>"#)); + assert!(output.contains(r#" <MediaFile>"#)); + assert!(output.contains(r#" <MediaFileTypeCode>04</MediaFileTypeCode>"#)); + assert!(output.contains(r#" <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>"#)); + assert!(output.contains(r#" <MediaFileLink>https://www.book.com/cover</MediaFileLink>"#)); + assert!(output.contains(r#" <Imprint>"#)); + assert!(output.contains(r#" <ImprintName>OA Editions Imprint</ImprintName>"#)); + assert!(output.contains(r#" <Publisher>"#)); + assert!(output.contains(r#" <PublishingRole>01</PublishingRole>"#)); + assert!(output.contains(r#" <PublisherName>OA Editions</PublisherName>"#)); + assert!(output.contains(r#" <Website>"#)); + assert!(output.contains(r#" <WebsiteLink>https://www.publisher.com</WebsiteLink>"#)); + assert!(output.contains(r#" <CityOfPublication>León, Spain</CityOfPublication>"#)); + assert!(output.contains(r#" <PublishingStatus>04</PublishingStatus>"#)); + assert!(output.contains(r#" <PublicationDate>19991231</PublicationDate>"#)); + assert!(output.contains(r#" <CopyrightYear>1999</CopyrightYear>"#)); + assert!(output.contains(r#" <SalesRights>"#)); + assert!(output.contains(r#" <SalesRightsType>02</SalesRightsType>"#)); + assert!(output.contains(r#" <RightsTerritory>WORLD</RightsTerritory>"#)); + assert!(output.contains(r#" <RelatedProduct>"#)); + assert!(output.contains(r#" <RelationCode>06</RelationCode>"#)); + assert!(output.contains(r#" <ProductIdentifier>"#)); + assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); + assert!(output.contains(r#" <IDValue>9781566199094</IDValue>"#)); + assert!(output.contains(r#" <SupplyDetail>"#)); + assert!(output.contains(r#" <SupplierName>OA Editions</SupplierName>"#)); + assert!(output.contains(r#" <SupplierRole>09</SupplierRole>"#)); + assert!(output.contains(r#" <ProductAvailability>99</ProductAvailability>"#)); + assert!(output.contains(r#" <AudienceRestrictionFlag>R</AudienceRestrictionFlag>"#)); + assert!(output + .contains(r#" <AudienceRestrictionNote>Open access</AudienceRestrictionNote>"#)); + assert!(output.contains(r#" <Price>"#)); + assert!(output.contains(r#" <PriceTypeCode>02</PriceTypeCode>"#)); + assert!(output.contains(r#" <PriceAmount>7.99</PriceAmount>"#)); + assert!(output.contains(r#" <CurrencyCode>USD</CurrencyCode>"#)); + + // Remove some values to test non-output of optional blocks + test_work.doi = None; + test_work.license = None; + test_work.subtitle = None; + test_work.page_count = None; + test_work.long_abstract = None; + test_work.place = None; + test_work.publication_date = None; + test_work.license = None; + test_work.landing_page = None; + test_work.cover_url = None; + test_work.imprint.publisher.publisher_url = None; + test_work.publications.pop(); // Remove second (PDF) publication + let output = generate_test_output(&test_work); + // No DOI supplied + assert!(!output.contains(r#" <ProductIDType>06</ProductIDType>"#)); + assert!(!output.contains(r#" <IDValue>10.00001/BOOK.0001</IDValue>"#)); + // No subtitle supplied: work FullTitle is used instead of Title + assert!(!output.contains(r#" <TitleText>Book Title</TitleText>"#)); + assert!(!output.contains(r#" <Subtitle>Separate Subtitle</Subtitle>"#)); + assert!(output.contains(r#" <TitleText>Book Title: Book Subtitle</TitleText>"#)); + // No landing page supplied + assert!(!output.contains(r#" <WebsiteRole>01</WebsiteRole>"#)); + assert!(!output.contains( + r#" <WebsiteDescription>Publisher's website: web shop</WebsiteDescription>"# + )); + assert!(!output.contains(r#" <WebsiteLink>https://www.book.com</WebsiteLink>"#)); + // PDF publication removed, hence no PDF URL, + // no PDF RelatedProduct, and EpubType changes + assert!(!output.contains(r#" <WebsiteLink>https://www.book.com/pdf</WebsiteLink>"#)); + assert!(!output.contains(r#" <IDValue>9781566199094</IDValue>"#)); + assert!(!output.contains(r#" <EpubType>002</EpubType>"#)); + assert!(output.contains(r#" <EpubType>029</EpubType>"#)); + // No page count supplied + assert!(!output.contains(r#" <Extent>"#)); + assert!(!output.contains(r#" <ExtentType>00</ExtentType>"#)); + assert!(!output.contains(r#" <ExtentValue>334</ExtentValue>"#)); + assert!(!output.contains(r#" <ExtentUnit>03</ExtentUnit>"#)); + // No long abstract supplied + assert!(!output.contains(r#" <TextTypeCode>03</TextTypeCode>"#)); + assert!(!output.contains(r#" <TextFormat>06</TextFormat>"#)); + assert!(!output.contains(r#" <Text>Lorem ipsum dolor sit amet</Text>"#)); + // No licence supplied + assert!(!output.contains(r#" <TextTypeCode>46</TextTypeCode>"#)); + assert!( + !output.contains(r#" <Text>https://creativecommons.org/licenses/by/4.0/</Text>"#) + ); + // No cover URL supplied + assert!(!output.contains(r#" <MediaFile>"#)); + assert!(!output.contains(r#" <MediaFileTypeCode>04</MediaFileTypeCode>"#)); + assert!(!output.contains(r#" <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>"#)); + assert!( + !output.contains(r#" <MediaFileLink>https://www.book.com/cover</MediaFileLink>"#) + ); + // No publisher website supplied (and no other <Website> blocks remain) + assert!(!output.contains(r#" <Website>"#)); + assert!(!output.contains(r#" <WebsiteLink>https://www.publisher.com</WebsiteLink>"#)); + // No place supplied + assert!(!output.contains(r#" <CityOfPublication>León, Spain</CityOfPublication>"#)); + // No publication date supplied + assert!(!output.contains(r#" <PublicationDate>19991231</PublicationDate>"#)); + assert!(!output.contains(r#" <CopyrightYear>1999</CopyrightYear>"#)); + // No PDF or EPUB price supplied, so default of 0.01 USD is used + assert!(output.contains(r#" <PriceAmount>0.01</PriceAmount>"#)); + + // Remove the remaining (EPUB) publication's URL: error + test_work.publications[0].publication_url = None; + // Can't use helper function for this as it assumes Ok rather than Err + let mut buffer = Vec::new(); + let mut writer = xml::writer::EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut buffer); + let wrapped_output = + XmlElementBlock::<Onix21EbscoHost>::xml_element(&test_work, &mut writer) + .map(|_| buffer) + .and_then(|onix| { + String::from_utf8(onix) + .map_err(|_| ThothError::InternalError("Could not parse XML".to_string())) + }); + assert!(wrapped_output.is_err()); + let output = wrapped_output.unwrap_err().to_string(); + assert_eq!( + output, + "Could not generate onix_2.1::ebsco_host: No PDF or EPUB URL".to_string() + ); + } +} diff --git a/thoth-export-server/src/xml/onix3_jstor.rs b/thoth-export-server/src/xml/onix3_jstor.rs new file mode 100644 index 00000000..8d47b628 --- /dev/null +++ b/thoth-export-server/src/xml/onix3_jstor.rs @@ -0,0 +1,987 @@ +use chrono::Utc; +use std::collections::HashMap; +use std::io::Write; +use thoth_client::{ + ContributionType, LanguageRelation, PublicationType, SubjectType, Work, WorkContributions, + WorkLanguages, WorkPublications, WorkStatus, +}; +use xml::writer::{EventWriter, XmlEvent}; + +use super::{write_element_block, XmlElement, XmlSpecification}; +use crate::xml::{write_full_element_block, XmlElementBlock}; +use thoth_errors::{ThothError, ThothResult}; + +pub struct Onix3Jstor {} + +impl XmlSpecification for Onix3Jstor { + fn handle_event<W: Write>(w: &mut EventWriter<W>, works: &[Work]) -> ThothResult<()> { + let mut attr_map: HashMap<&str, &str> = HashMap::new(); + + attr_map.insert("release", "3.0"); + attr_map.insert("xmlns", "http://ns.editeur.org/onix/3.0/reference"); + + write_full_element_block("ONIXMessage", None, Some(attr_map), w, |w| { + write_element_block("Header", w, |w| { + write_element_block("Sender", w, |w| { + write_element_block("SenderName", w, |w| { + w.write(XmlEvent::Characters("Thoth")).map_err(|e| e.into()) + })?; + write_element_block("EmailAddress", w, |w| { + w.write(XmlEvent::Characters("info@thoth.pub")) + .map_err(|e| e.into()) + }) + })?; + write_element_block("SentDateTime", w, |w| { + w.write(XmlEvent::Characters( + &Utc::now().format("%Y%m%dT%H%M%S").to_string(), + )) + .map_err(|e| e.into()) + }) + })?; + + match works.len() { + 0 => Err(ThothError::IncompleteMetadataRecord( + "onix_3.0::jstor".to_string(), + "Not enough data".to_string(), + )), + 1 => XmlElementBlock::<Onix3Jstor>::xml_element(works.first().unwrap(), w), + _ => { + for work in works.iter() { + XmlElementBlock::<Onix3Jstor>::xml_element(work, w).ok(); + } + Ok(()) + } + } + }) + } +} + +impl XmlElementBlock<Onix3Jstor> for Work { + fn xml_element<W: Write>(&self, w: &mut EventWriter<W>) -> ThothResult<()> { + let work_id = format!("urn:uuid:{}", self.work_id.to_string()); + let (main_isbn, isbns) = get_publications_data(&self.publications); + // We can only generate the document if there's a PDF + if let Some(pdf_url) = self + .publications + .iter() + .find(|p| p.publication_type.eq(&PublicationType::PDF)) + .and_then(|p| p.publication_url.as_ref()) + { + write_element_block("Product", w, |w| { + write_element_block("RecordReference", w, |w| { + w.write(XmlEvent::Characters(&work_id)) + .map_err(|e| e.into()) + })?; + // 03 Notification confirmed on publication + write_element_block("NotificationType", w, |w| { + w.write(XmlEvent::Characters("03")).map_err(|e| e.into()) + })?; + // 01 Publisher + write_element_block("RecordSourceType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("ProductIdentifier", w, |w| { + // 01 Proprietary + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&work_id)) + .map_err(|e| e.into()) + }) + })?; + write_element_block("ProductIdentifier", w, |w| { + // 15 ISBN-13 + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("15")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&main_isbn)) + .map_err(|e| e.into()) + }) + })?; + if let Some(doi) = &self.doi { + write_element_block("ProductIdentifier", w, |w| { + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&doi.to_string())) + .map_err(|e| e.into()) + }) + })?; + } + write_element_block("DescriptiveDetail", w, |w| { + // 00 Single-component retail product + write_element_block("ProductComposition", w, |w| { + w.write(XmlEvent::Characters("00")).map_err(|e| e.into()) + })?; + // EB Digital download and online + write_element_block("ProductForm", w, |w| { + w.write(XmlEvent::Characters("EB")).map_err(|e| e.into()) + })?; + // E107 PDF + write_element_block("ProductFormDetail", w, |w| { + w.write(XmlEvent::Characters("E107")).map_err(|e| e.into()) + })?; + // 10 Text (eye-readable) + write_element_block("PrimaryContentType", w, |w| { + w.write(XmlEvent::Characters("10")).map_err(|e| e.into()) + })?; + if let Some(license) = &self.license { + write_element_block("EpubLicense", w, |w| { + write_element_block("EpubLicenseName", w, |w| { + w.write(XmlEvent::Characters("Creative Commons License")) + .map_err(|e| e.into()) + })?; + write_element_block("EpubLicenseExpression", w, |w| { + write_element_block("EpubLicenseExpressionType", w, |w| { + w.write(XmlEvent::Characters("02")).map_err(|e| e.into()) + })?; + write_element_block("EpubLicenseExpressionLink", w, |w| { + w.write(XmlEvent::Characters(license)).map_err(|e| e.into()) + }) + }) + })?; + } + write_element_block("TitleDetail", w, |w| { + // 01 Distinctive title (book) + write_element_block("TitleType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("TitleElement", w, |w| { + // 01 Product + write_element_block("TitleElementLevel", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + if let Some(subtitle) = &self.subtitle { + write_element_block("TitleText", w, |w| { + w.write(XmlEvent::Characters(&self.title)) + .map_err(|e| e.into()) + })?; + write_element_block("Subtitle", w, |w| { + w.write(XmlEvent::Characters(subtitle)) + .map_err(|e| e.into()) + }) + } else { + write_element_block("TitleText", w, |w| { + w.write(XmlEvent::Characters(&self.full_title)) + .map_err(|e| e.into()) + }) + } + }) + })?; + for contribution in &self.contributions { + XmlElementBlock::<Onix3Jstor>::xml_element(contribution, w).ok(); + } + for language in &self.languages { + XmlElementBlock::<Onix3Jstor>::xml_element(language, w).ok(); + } + if let Some(page_count) = self.page_count { + write_element_block("Extent", w, |w| { + // 00 Main content + write_element_block("ExtentType", w, |w| { + w.write(XmlEvent::Characters("00")).map_err(|e| e.into()) + })?; + write_element_block("ExtentValue", w, |w| { + w.write(XmlEvent::Characters(&page_count.to_string())) + .map_err(|e| e.into()) + })?; + // 03 Pages + write_element_block("ExtentUnit", w, |w| { + w.write(XmlEvent::Characters("03")).map_err(|e| e.into()) + }) + })?; + } + for subject in &self.subjects { + write_element_block("Subject", w, |w| { + XmlElement::<Onix3Jstor>::xml_element(&subject.subject_type, w)?; + write_element_block("SubjectCode", w, |w| { + w.write(XmlEvent::Characters(&subject.subject_code)) + .map_err(|e| e.into()) + }) + })?; + } + Ok(()) + })?; + write_element_block("CollateralDetail", w, |w| { + if let Some(labstract) = &self.long_abstract { + write_element_block("TextContent", w, |w| { + let mut lang_fmt: HashMap<&str, &str> = HashMap::new(); + lang_fmt.insert("language", "eng"); + // 03 Description ("30 Abstract" not implemented in OAPEN) + write_element_block("TextType", w, |w| { + w.write(XmlEvent::Characters("03")).map_err(|e| e.into()) + })?; + // 00 Unrestricted + write_element_block("ContentAudience", w, |w| { + w.write(XmlEvent::Characters("00")).map_err(|e| e.into()) + })?; + write_full_element_block("Text", None, Some(lang_fmt), w, |w| { + w.write(XmlEvent::Characters(labstract)) + .map_err(|e| e.into()) + }) + })?; + } + if let Some(toc) = &self.toc { + write_element_block("TextContent", w, |w| { + // 04 Table of contents + write_element_block("TextType", w, |w| { + w.write(XmlEvent::Characters("04")).map_err(|e| e.into()) + })?; + // 00 Unrestricted + write_element_block("ContentAudience", w, |w| { + w.write(XmlEvent::Characters("00")).map_err(|e| e.into()) + })?; + write_element_block("Text", w, |w| { + w.write(XmlEvent::Characters(toc)).map_err(|e| e.into()) + }) + })?; + } + write_element_block("TextContent", w, |w| { + // 20 Open access statement + write_element_block("TextType", w, |w| { + w.write(XmlEvent::Characters("20")).map_err(|e| e.into()) + })?; + // 00 Unrestricted + write_element_block("ContentAudience", w, |w| { + w.write(XmlEvent::Characters("00")).map_err(|e| e.into()) + })?; + write_element_block("Text", w, |w| { + w.write(XmlEvent::Characters("Open Access")) + .map_err(|e| e.into()) + }) + }) + })?; + write_element_block("PublishingDetail", w, |w| { + write_element_block("Imprint", w, |w| { + write_element_block("ImprintName", w, |w| { + w.write(XmlEvent::Characters(&self.imprint.imprint_name)) + .map_err(|e| e.into()) + }) + })?; + write_element_block("Publisher", w, |w| { + // 01 Publisher + write_element_block("PublishingRole", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + write_element_block("PublisherName", w, |w| { + w.write(XmlEvent::Characters(&self.imprint.publisher.publisher_name)) + .map_err(|e| e.into()) + }) + })?; + if let Some(place) = &self.place { + write_element_block("CityOfPublication", w, |w| { + w.write(XmlEvent::Characters(place)).map_err(|e| e.into()) + })?; + } + XmlElement::<Onix3Jstor>::xml_element(&self.work_status, w)?; + if let Some(date) = self.publication_date { + write_element_block("PublishingDate", w, |w| { + let mut date_fmt: HashMap<&str, &str> = HashMap::new(); + date_fmt.insert("dateformat", "00"); // 00 YYYYMMDD + + write_element_block("PublishingDateRole", w, |w| { + // 01 Publication date + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + })?; + // dateformat="00" YYYYMMDD + write_full_element_block("Date", None, Some(date_fmt), w, |w| { + w.write(XmlEvent::Characters(&date.format("%Y%m%d").to_string())) + .map_err(|e| e.into()) + }) + })?; + } + Ok(()) + })?; + if !isbns.is_empty() { + write_element_block("RelatedMaterial", w, |w| { + for isbn in &isbns { + write_element_block("RelatedProduct", w, |w| { + // 06 Alternative format + write_element_block("ProductRelationCode", w, |w| { + w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) + })?; + write_element_block("ProductIdentifier", w, |w| { + // 15 ISBN-13 + write_element_block("ProductIDType", w, |w| { + w.write(XmlEvent::Characters("15")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(isbn)).map_err(|e| e.into()) + }) + }) + })?; + } + Ok(()) + })?; + } + write_element_block("ProductSupply", w, |w| { + let mut supplies: HashMap<String, (String, String)> = HashMap::new(); + supplies.insert( + pdf_url.to_string(), + ( + "29".to_string(), + "Publisher's website: download the title".to_string(), + ), + ); + if let Some(landing_page) = &self.landing_page { + supplies.insert( + landing_page.to_string(), + ( + "01".to_string(), + "Publisher's website: web shop".to_string(), + ), + ); + } + for (url, description) in supplies.iter() { + write_element_block("SupplyDetail", w, |w| { + write_element_block("Supplier", w, |w| { + // 09 Publisher to end-customers + write_element_block("SupplierRole", w, |w| { + w.write(XmlEvent::Characters("09")).map_err(|e| e.into()) + })?; + write_element_block("SupplierName", w, |w| { + w.write(XmlEvent::Characters( + &self.imprint.publisher.publisher_name, + )) + .map_err(|e| e.into()) + })?; + write_element_block("Website", w, |w| { + // 01 Publisher’s corporate website + write_element_block("WebsiteRole", w, |w| { + w.write(XmlEvent::Characters(&description.0)) + .map_err(|e| e.into()) + })?; + write_element_block("WebsiteDescription", w, |w| { + w.write(XmlEvent::Characters(&description.1)) + .map_err(|e| e.into()) + })?; + write_element_block("WebsiteLink", w, |w| { + w.write(XmlEvent::Characters(url)).map_err(|e| e.into()) + }) + }) + })?; + // 99 Contact supplier + write_element_block("ProductAvailability", w, |w| { + w.write(XmlEvent::Characters("99")).map_err(|e| e.into()) + })?; + // 01 Free of charge + write_element_block("UnpricedItemType", w, |w| { + w.write(XmlEvent::Characters("01")).map_err(|e| e.into()) + }) + })?; + } + Ok(()) + }) + }) + } else { + Err(ThothError::IncompleteMetadataRecord( + "onix_3.0::jstor".to_string(), + "Missing PDF URL".to_string(), + )) + } + } +} + +fn get_publications_data(publications: &[WorkPublications]) -> (String, Vec<String>) { + let mut main_isbn = "".to_string(); + let mut isbns: Vec<String> = Vec::new(); + + for publication in publications { + if let Some(isbn) = &publication.isbn.as_ref().map(|i| i.to_string()) { + isbns.push(isbn.replace("-", "")); + // The default product ISBN is the PDF's + if publication.publication_type.eq(&PublicationType::PDF) { + main_isbn = isbn.replace("-", ""); + } + // Books that don't have a PDF ISBN will use the paperback's + if publication.publication_type.eq(&PublicationType::PAPERBACK) && main_isbn.is_empty() + { + main_isbn = isbn.replace("-", ""); + } + } + } + + (main_isbn, isbns) +} + +impl XmlElement<Onix3Jstor> for WorkStatus { + const ELEMENT: &'static str = "PublishingStatus"; + + fn value(&self) -> &'static str { + match self { + WorkStatus::UNSPECIFIED => "00", + WorkStatus::CANCELLED => "01", + WorkStatus::FORTHCOMING => "02", + WorkStatus::POSTPONED_INDEFINITELY => "03", + WorkStatus::ACTIVE => "04", + WorkStatus::NO_LONGER_OUR_PRODUCT => "05", + WorkStatus::OUT_OF_STOCK_INDEFINITELY => "06", + WorkStatus::OUT_OF_PRINT => "07", + WorkStatus::INACTIVE => "08", + WorkStatus::UNKNOWN => "09", + WorkStatus::REMAINDERED => "10", + WorkStatus::WITHDRAWN_FROM_SALE => "11", + WorkStatus::RECALLED => "15", + WorkStatus::Other(_) => unreachable!(), + } + } +} + +impl XmlElement<Onix3Jstor> for SubjectType { + const ELEMENT: &'static str = "SubjectSchemeIdentifier"; + + fn value(&self) -> &'static str { + match self { + SubjectType::BIC => "12", + SubjectType::BISAC => "10", + SubjectType::KEYWORD => "20", + SubjectType::LCC => "04", + SubjectType::THEMA => "93", + SubjectType::CUSTOM => "B2", + SubjectType::Other(_) => unreachable!(), + } + } +} + +impl XmlElement<Onix3Jstor> for LanguageRelation { + const ELEMENT: &'static str = "LanguageRole"; + + fn value(&self) -> &'static str { + match self { + LanguageRelation::ORIGINAL => "01", + LanguageRelation::TRANSLATED_FROM => "02", + LanguageRelation::TRANSLATED_INTO => "01", + LanguageRelation::Other(_) => unreachable!(), + } + } +} + +impl XmlElement<Onix3Jstor> for ContributionType { + const ELEMENT: &'static str = "ContributorRole"; + + fn value(&self) -> &'static str { + match self { + ContributionType::AUTHOR => "A01", + ContributionType::EDITOR => "B01", + ContributionType::TRANSLATOR => "B06", + ContributionType::PHOTOGRAPHER => "A13", + ContributionType::ILUSTRATOR => "A12", + ContributionType::MUSIC_EDITOR => "B25", + ContributionType::FOREWORD_BY => "A23", + ContributionType::INTRODUCTION_BY => "A24", + ContributionType::AFTERWORD_BY => "A19", + ContributionType::PREFACE_BY => "A15", + ContributionType::Other(_) => unreachable!(), + } + } +} + +impl XmlElementBlock<Onix3Jstor> for WorkContributions { + fn xml_element<W: Write>(&self, w: &mut EventWriter<W>) -> ThothResult<()> { + write_element_block("Contributor", w, |w| { + write_element_block("SequenceNumber", w, |w| { + w.write(XmlEvent::Characters(&self.contribution_ordinal.to_string())) + .map_err(|e| e.into()) + })?; + XmlElement::<Onix3Jstor>::xml_element(&self.contribution_type, w)?; + + if let Some(orcid) = &self.contributor.orcid { + write_element_block("NameIdentifier", w, |w| { + write_element_block("NameIDType", w, |w| { + w.write(XmlEvent::Characters("21")).map_err(|e| e.into()) + })?; + write_element_block("IDValue", w, |w| { + w.write(XmlEvent::Characters(&orcid.to_string())) + .map_err(|e| e.into()) + }) + })?; + } + if let Some(first_name) = &self.first_name { + write_element_block("NamesBeforeKey", w, |w| { + w.write(XmlEvent::Characters(first_name)) + .map_err(|e| e.into()) + })?; + write_element_block("KeyNames", w, |w| { + w.write(XmlEvent::Characters(&self.last_name)) + .map_err(|e| e.into()) + })?; + } else { + write_element_block("PersonName", w, |w| { + w.write(XmlEvent::Characters(&self.full_name)) + .map_err(|e| e.into()) + })?; + } + Ok(()) + }) + } +} + +impl XmlElementBlock<Onix3Jstor> for WorkLanguages { + fn xml_element<W: Write>(&self, w: &mut EventWriter<W>) -> ThothResult<()> { + write_element_block("Language", w, |w| { + XmlElement::<Onix3Jstor>::xml_element(&self.language_relation, w).ok(); + // not worth implementing XmlElement for LanguageCode as all cases would + // need to be exhaustively matched and the codes are equivalent anyway + write_element_block("LanguageCode", w, |w| { + w.write(XmlEvent::Characters( + &self.language_code.to_string().to_lowercase(), + )) + .map_err(|e| e.into()) + }) + }) + } +} + +#[cfg(test)] +mod tests { + // Testing note: XML nodes cannot be guaranteed to be output in the same order every time + // We therefore rely on `assert!(contains)` rather than `assert_eq!` + use super::*; + use std::str::FromStr; + use thoth_api::model::Doi; + use thoth_api::model::Isbn; + use thoth_api::model::Orcid; + use thoth_client::{ + ContributionType, LanguageCode, LanguageRelation, PublicationType, + WorkContributionsContributor, WorkFundings, WorkImprint, WorkImprintPublisher, WorkIssues, + WorkIssuesSeries, WorkStatus, WorkSubjects, WorkType, + }; + use uuid::Uuid; + + fn generate_test_output(input: &impl XmlElementBlock<Onix3Jstor>) -> String { + // Helper function based on `XmlSpecification::generate` + let mut buffer = Vec::new(); + let mut writer = xml::writer::EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut buffer); + let wrapped_output = XmlElementBlock::<Onix3Jstor>::xml_element(input, &mut writer) + .map(|_| buffer) + .and_then(|onix| { + String::from_utf8(onix) + .map_err(|_| ThothError::InternalError("Could not parse XML".to_string())) + }); + assert!(wrapped_output.is_ok()); + wrapped_output.unwrap() + } + + #[test] + fn test_onix3_jstor_contributions() { + let mut test_contribution = WorkContributions { + contribution_type: ContributionType::AUTHOR, + first_name: Some("Author".to_string()), + last_name: "1".to_string(), + full_name: "Author 1".to_string(), + main_contribution: true, + biography: None, + institution: None, + contribution_ordinal: 1, + contributor: WorkContributionsContributor { + orcid: Some(Orcid::from_str("https://orcid.org/0000-0002-0000-0001").unwrap()), + }, + }; + + // Test standard output + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <SequenceNumber>1</SequenceNumber>"#)); + assert!(output.contains(r#" <ContributorRole>A01</ContributorRole>"#)); + assert!(output.contains(r#" <NameIdentifier>"#)); + assert!(output.contains(r#" <NameIDType>21</NameIDType>"#)); + assert!(output.contains(r#" <IDValue>0000-0002-0000-0001</IDValue>"#)); + assert!(output.contains(r#" </NameIdentifier>"#)); + // Given name is output as NamesBeforeKey and family name as KeyNames + assert!(output.contains(r#" <NamesBeforeKey>Author</NamesBeforeKey>"#)); + assert!(output.contains(r#" <KeyNames>1</KeyNames>"#)); + // PersonName is not output when given name is supplied + assert!(!output.contains(r#" <PersonName>Author 1</PersonName>"#)); + + // Change all possible values to test that output is updated + test_contribution.contribution_type = ContributionType::EDITOR; + test_contribution.contribution_ordinal = 2; + test_contribution.contributor.orcid = None; + test_contribution.first_name = None; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <SequenceNumber>2</SequenceNumber>"#)); + assert!(output.contains(r#" <ContributorRole>B01</ContributorRole>"#)); + // No ORCID supplied + assert!(!output.contains(r#" <NameIdentifier>"#)); + assert!(!output.contains(r#" <NameIDType>21</NameIDType>"#)); + assert!(!output.contains(r#" <IDValue>0000-0002-0000-0001</IDValue>"#)); + assert!(!output.contains(r#" </NameIdentifier>"#)); + // No given name supplied, so PersonName is output instead of KeyNames and NamesBeforeKey + assert!(!output.contains(r#" <NamesBeforeKey>Author</NamesBeforeKey>"#)); + assert!(!output.contains(r#" <KeyNames>1</KeyNames>"#)); + assert!(output.contains(r#" <PersonName>Author 1</PersonName>"#)); + + // Test all remaining contributor roles + test_contribution.contribution_type = ContributionType::TRANSLATOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>B06</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::PHOTOGRAPHER; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A13</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::ILUSTRATOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A12</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::MUSIC_EDITOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>B25</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::FOREWORD_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A23</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::INTRODUCTION_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A24</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::AFTERWORD_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A19</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::PREFACE_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A15</ContributorRole>"#)); + } + + #[test] + fn test_onix3_jstor_languages() { + let mut test_language = WorkLanguages { + language_code: LanguageCode::SPA, + language_relation: LanguageRelation::TRANSLATED_FROM, + main_language: true, + }; + + // Test standard output + let output = generate_test_output(&test_language); + assert!(output.contains(r#" <LanguageRole>02</LanguageRole>"#)); + assert!(output.contains(r#" <LanguageCode>spa</LanguageCode>"#)); + + // Change all possible values to test that output is updated + test_language.language_code = LanguageCode::WEL; + for language_relation in [ + LanguageRelation::ORIGINAL, + LanguageRelation::TRANSLATED_INTO, + ] { + test_language.language_relation = language_relation; + let output = generate_test_output(&test_language); + assert!(output.contains(r#" <LanguageRole>01</LanguageRole>"#)); + assert!(output.contains(r#" <LanguageCode>wel</LanguageCode>"#)); + } + } + + #[test] + fn test_onix3_jstor_works() { + let mut test_work = Work { + work_id: Uuid::from_str("00000000-0000-0000-AAAA-000000000001").unwrap(), + work_status: WorkStatus::ACTIVE, + full_title: "Book Title: Book Subtitle".to_string(), + title: "Book Title".to_string(), + subtitle: Some("Book Subtitle".to_string()), + work_type: WorkType::MONOGRAPH, + edition: 1, + doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()), + publication_date: Some(chrono::NaiveDate::from_ymd(1999, 12, 31)), + license: Some("https://creativecommons.org/licenses/by/4.0/".to_string()), + copyright_holder: "Author 1; Author 2".to_string(), + short_abstract: None, + long_abstract: Some("Lorem ipsum dolor sit amet".to_string()), + general_note: None, + place: Some("León, Spain".to_string()), + width_mm: None, + width_cm: None, + width_in: None, + height_mm: None, + height_cm: None, + height_in: None, + page_count: Some(334), + page_breakdown: None, + image_count: None, + table_count: None, + audio_count: None, + video_count: None, + landing_page: Some("https://www.book.com".to_string()), + toc: Some("1. Chapter 1".to_string()), + lccn: None, + oclc: None, + cover_url: None, + cover_caption: None, + imprint: WorkImprint { + imprint_name: "OA Editions Imprint".to_string(), + publisher: WorkImprintPublisher { + publisher_name: "OA Editions".to_string(), + publisher_url: None, + }, + }, + issues: vec![WorkIssues { + issue_ordinal: 1, + series: WorkIssuesSeries { + series_type: thoth_client::SeriesType::JOURNAL, + series_name: "Name of series".to_string(), + issn_print: "1234-5678".to_string(), + issn_digital: "8765-4321".to_string(), + series_url: None, + }, + }], + contributions: vec![], + languages: vec![], + publications: vec![WorkPublications { + publication_id: Uuid::from_str("00000000-0000-0000-DDDD-000000000004").unwrap(), + publication_type: PublicationType::PDF, + publication_url: Some("https://www.book.com/pdf".to_string()), + isbn: Some(Isbn::from_str("978-3-16-148410-0").unwrap()), + prices: vec![], + }], + subjects: vec![ + WorkSubjects { + subject_code: "AAB".to_string(), + subject_type: SubjectType::BIC, + subject_ordinal: 1, + }, + WorkSubjects { + subject_code: "AAA000000".to_string(), + subject_type: SubjectType::BISAC, + subject_ordinal: 2, + }, + WorkSubjects { + subject_code: "JA85".to_string(), + subject_type: SubjectType::LCC, + subject_ordinal: 3, + }, + WorkSubjects { + subject_code: "JWA".to_string(), + subject_type: SubjectType::THEMA, + subject_ordinal: 4, + }, + WorkSubjects { + subject_code: "keyword1".to_string(), + subject_type: SubjectType::KEYWORD, + subject_ordinal: 5, + }, + WorkSubjects { + subject_code: "custom1".to_string(), + subject_type: SubjectType::CUSTOM, + subject_ordinal: 6, + }, + ], + fundings: vec![WorkFundings { + program: Some("Name of program".to_string()), + project_name: Some("Name of project".to_string()), + project_shortname: None, + grant_number: Some("Number of grant".to_string()), + jurisdiction: None, + funder: thoth_client::WorkFundingsFunder { + funder_name: "Name of funder".to_string(), + funder_doi: None, + }, + }], + }; + + // Test standard output + let output = generate_test_output(&test_work); + assert!(output.contains(r#"<Product>"#)); + assert!(output.contains( + r#" <RecordReference>urn:uuid:00000000-0000-0000-aaaa-000000000001</RecordReference>"# + )); + assert!(output.contains(r#" <NotificationType>03</NotificationType>"#)); + assert!(output.contains(r#" <RecordSourceType>01</RecordSourceType>"#)); + assert!(output.contains(r#" <ProductIdentifier>"#)); + assert!(output.contains(r#" <ProductIDType>01</ProductIDType>"#)); + assert!(output + .contains(r#" <IDValue>urn:uuid:00000000-0000-0000-aaaa-000000000001</IDValue>"#)); + assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); + assert!(output.contains(r#" <ProductIDType>06</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>10.00001/BOOK.0001</IDValue>"#)); + assert!(output.contains(r#" <DescriptiveDetail>"#)); + assert!(output.contains(r#" <ProductComposition>00</ProductComposition>"#)); + assert!(output.contains(r#" <ProductForm>EB</ProductForm>"#)); + assert!(output.contains(r#" <ProductFormDetail>E107</ProductFormDetail>"#)); + assert!(output.contains(r#" <PrimaryContentType>10</PrimaryContentType>"#)); + assert!(output.contains(r#" <EpubLicense>"#)); + assert!( + output.contains(r#" <EpubLicenseName>Creative Commons License</EpubLicenseName>"#) + ); + assert!(output.contains(r#" <EpubLicenseExpression>"#)); + assert!( + output.contains(r#" <EpubLicenseExpressionType>02</EpubLicenseExpressionType>"#) + ); + assert!(output.contains(r#" <EpubLicenseExpressionLink>https://creativecommons.org/licenses/by/4.0/</EpubLicenseExpressionLink>"#)); + assert!(output.contains(r#" <TitleDetail>"#)); + assert!(output.contains(r#" <TitleType>01</TitleType>"#)); + assert!(output.contains(r#" <TitleElement>"#)); + assert!(output.contains(r#" <TitleElementLevel>01</TitleElementLevel>"#)); + assert!(output.contains(r#" <TitleText>Book Title</TitleText>"#)); + assert!(output.contains(r#" <Subtitle>Book Subtitle</Subtitle>"#)); + assert!(output.contains(r#" <Extent>"#)); + assert!(output.contains(r#" <ExtentType>00</ExtentType>"#)); + assert!(output.contains(r#" <ExtentValue>334</ExtentValue>"#)); + assert!(output.contains(r#" <ExtentUnit>03</ExtentUnit>"#)); + assert!(output.contains(r#" <Subject>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>12</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>AAB</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>10</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>AAA000000</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>04</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>JA85</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>93</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>JWA</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>20</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>keyword1</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>B2</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>custom1</SubjectCode>"#)); + assert!(output.contains(r#" <CollateralDetail>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>03</TextType>"#)); + assert!(output.contains(r#" <ContentAudience>00</ContentAudience>"#)); + assert!(output.contains(r#" <Text language="eng">Lorem ipsum dolor sit amet</Text>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>04</TextType>"#)); + assert!(output.contains(r#" <Text>1. Chapter 1</Text>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>20</TextType>"#)); + assert!(output.contains(r#" <Text>Open Access</Text>"#)); + assert!(output.contains(r#" <PublishingDetail>"#)); + assert!(output.contains(r#" <Imprint>"#)); + assert!(output.contains(r#" <ImprintName>OA Editions Imprint</ImprintName>"#)); + assert!(output.contains(r#" <Publisher>"#)); + assert!(output.contains(r#" <PublishingRole>01</PublishingRole>"#)); + assert!(output.contains(r#" <PublisherName>OA Editions</PublisherName>"#)); + assert!(output.contains(r#" <CityOfPublication>León, Spain</CityOfPublication>"#)); + assert!(output.contains(r#" <PublishingStatus>04</PublishingStatus>"#)); + assert!(output.contains(r#" <PublishingDate>"#)); + assert!(output.contains(r#" <PublishingDateRole>01</PublishingDateRole>"#)); + assert!(output.contains(r#" <Date dateformat="00">19991231</Date>"#)); + assert!(output.contains(r#" <RelatedProduct>"#)); + assert!(output.contains(r#" <ProductRelationCode>06</ProductRelationCode>"#)); + assert!(output.contains(r#" <ProductIdentifier>"#)); + assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); + assert!(output.contains(r#" <ProductSupply>"#)); + assert!(output.contains(r#" <SupplyDetail>"#)); + assert!(output.contains(r#" <Supplier>"#)); + assert!(output.contains(r#" <SupplierRole>09</SupplierRole>"#)); + assert!(output.contains(r#" <SupplierName>OA Editions</SupplierName>"#)); + assert!(output.contains(r#" <Website>"#)); + assert!(output.contains(r#" <WebsiteRole>01</WebsiteRole>"#)); + assert!(output.contains( + r#" <WebsiteDescription>Publisher's website: web shop</WebsiteDescription>"# + )); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com</WebsiteLink>"#)); + assert!(output.contains(r#" <ProductAvailability>99</ProductAvailability>"#)); + assert!(output.contains(r#" <UnpricedItemType>01</UnpricedItemType>"#)); + assert!(output.contains(r#" <SupplierRole>09</SupplierRole>"#)); + assert!(output.contains(r#" <SupplierName>OA Editions</SupplierName>"#)); + assert!(output.contains(r#" <WebsiteRole>29</WebsiteRole>"#)); + assert!(output.contains(r#" <WebsiteDescription>Publisher's website: download the title</WebsiteDescription>"#)); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com/pdf</WebsiteLink>"#)); + + // Test that OAPEN-only blocks are not output in JSTOR format + assert!(!output.contains(r#" <Audience>"#)); + assert!(!output.contains(r#" <AudienceCodeType>01</AudienceCodeType>"#)); + assert!(!output.contains(r#" <AudienceCodeValue>06</AudienceCodeValue>"#)); + assert!(!output.contains(r#" <PublishingRole>16</PublishingRole>"#)); + assert!(!output.contains(r#" <PublisherName>Name of funder</PublisherName>"#)); + assert!(!output.contains(r#" <Funding>"#)); + assert!(!output.contains(r#" <FundingIdentifier>"#)); + assert!(!output.contains(r#" <FundingIDType>01</FundingIDType>"#)); + assert!(!output.contains(r#" <IDTypeName>programname</IDTypeName>"#)); + assert!(!output.contains(r#" <IDValue>Name of program</IDValue>"#)); + assert!(!output.contains(r#" <IDTypeName>projectname</IDTypeName>"#)); + assert!(!output.contains(r#" <IDValue>Name of project</IDValue>"#)); + assert!(!output.contains(r#" <IDTypeName>grantnumber</IDTypeName>"#)); + assert!(!output.contains(r#" <IDValue>Number of grant</IDValue>"#)); + assert!(!output.contains(r#" <Collection>"#)); + assert!(!output.contains(r#" <CollectionType>10</CollectionType>"#)); + assert!(!output.contains(r#" <TitleElementLevel>02</TitleElementLevel>"#)); + assert!(!output.contains(r#" <PartNumber>1</PartNumber>"#)); + assert!(!output.contains(r#" <TitleText>Name of series</TitleText>"#)); + + // Remove some values to test non-output of optional blocks + test_work.doi = None; + test_work.license = None; + test_work.subtitle = None; + test_work.page_count = None; + test_work.long_abstract = None; + test_work.toc = None; + test_work.place = None; + test_work.publication_date = None; + test_work.landing_page = None; + test_work.subjects.clear(); + let output = generate_test_output(&test_work); + // No DOI supplied + assert!(!output.contains(r#" <ProductIDType>06</ProductIDType>"#)); + assert!(!output.contains(r#" <IDValue>10.00001/BOOK.0001</IDValue>"#)); + // No licence supplied + assert!(!output.contains(r#" <EpubLicense>"#)); + assert!(!output + .contains(r#" <EpubLicenseName>Creative Commons License</EpubLicenseName>"#)); + assert!(!output.contains(r#" <EpubLicenseExpression>"#)); + assert!(!output + .contains(r#" <EpubLicenseExpressionType>02</EpubLicenseExpressionType>"#)); + assert!(!output.contains(r#" <EpubLicenseExpressionLink>https://creativecommons.org/licenses/by/4.0/</EpubLicenseExpressionLink>"#)); + // No subtitle supplied: work FullTitle is used instead of Title + assert!(!output.contains(r#" <TitleText>Book Title</TitleText>"#)); + assert!(!output.contains(r#" <Subtitle>Book Subtitle</Subtitle>"#)); + assert!(output.contains(r#" <TitleText>Book Title: Book Subtitle</TitleText>"#)); + // No page count supplied + assert!(!output.contains(r#" <Extent>"#)); + assert!(!output.contains(r#" <ExtentType>00</ExtentType>"#)); + assert!(!output.contains(r#" <ExtentValue>334</ExtentValue>"#)); + assert!(!output.contains(r#" <ExtentUnit>03</ExtentUnit>"#)); + // No long abstract supplied + assert!(!output.contains(r#" <TextType>03</TextType>"#)); + assert!(!output.contains(r#" <Text language="eng">Lorem ipsum dolor sit amet</Text>"#)); + // No TOC supplied + assert!(!output.contains(r#" <TextType>04</TextType>"#)); + assert!(!output.contains(r#" <Text>1. Chapter 1</Text>"#)); + // CollateralDetail block is still present as it always contains Open Access statement + assert!(output.contains(r#" <CollateralDetail>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <ContentAudience>00</ContentAudience>"#)); + // No place supplied + assert!(!output.contains(r#" <CityOfPublication>León, Spain</CityOfPublication>"#)); + // No publication date supplied + assert!(!output.contains(r#" <PublishingDate>"#)); + assert!(!output.contains(r#" <PublishingDateRole>01</PublishingDateRole>"#)); + assert!(!output.contains(r#" <Date dateformat="00">19991231</Date>"#)); + // No landing page supplied: only one SupplyDetail block, linking to PDF download + assert!(!output.contains(r#" <WebsiteRole>01</WebsiteRole>"#)); + assert!(!output.contains( + r#" <WebsiteDescription>Publisher's website: web shop</WebsiteDescription>"# + )); + assert!(!output.contains(r#" <WebsiteLink>https://www.book.com</WebsiteLink>"#)); + // No subjects supplied + assert!(!output.contains(r#" <Subject>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>12</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>AAB</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>10</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>AAA000000</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>04</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>JA85</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>93</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>JWA</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>20</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>keyword1</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>B2</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>custom1</SubjectCode>"#)); + + // Remove the only publication, which is the PDF + // Result: error (can't generate OAPEN ONIX without PDF URL) + test_work.publications.clear(); + // Can't use helper function for this as it assumes Ok rather than Err + let mut buffer = Vec::new(); + let mut writer = xml::writer::EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut buffer); + let wrapped_output = XmlElementBlock::<Onix3Jstor>::xml_element(&test_work, &mut writer) + .map(|_| buffer) + .and_then(|onix| { + String::from_utf8(onix) + .map_err(|_| ThothError::InternalError("Could not parse XML".to_string())) + }); + assert!(wrapped_output.is_err()); + let output = wrapped_output.unwrap_err().to_string(); + assert_eq!( + output, + "Could not generate onix_3.0::jstor: Missing PDF URL".to_string() + ); + } +} diff --git a/thoth-export-server/src/xml/onix3_oapen.rs b/thoth-export-server/src/xml/onix3_oapen.rs index 8b173f6e..2144ebbd 100644 --- a/thoth-export-server/src/xml/onix3_oapen.rs +++ b/thoth-export-server/src/xml/onix3_oapen.rs @@ -9,7 +9,6 @@ use xml::writer::{EventWriter, XmlEvent}; use super::{write_element_block, XmlElement, XmlSpecification}; use crate::xml::{write_full_element_block, XmlElementBlock}; -use thoth_api::model::DOI_DOMAIN; use thoth_errors::{ThothError, ThothResult}; pub struct Onix3Oapen {} @@ -107,7 +106,7 @@ impl XmlElementBlock<Onix3Oapen> for Work { w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) })?; write_element_block("IDValue", w, |w| { - w.write(XmlEvent::Characters(&doi.replace(DOI_DOMAIN, ""))) + w.write(XmlEvent::Characters(&doi.to_string())) .map_err(|e| e.into()) }) })?; @@ -400,7 +399,7 @@ fn get_publications_data(publications: &[WorkPublications]) -> (String, Vec<Stri let mut isbns: Vec<String> = Vec::new(); for publication in publications { - if let Some(isbn) = &publication.isbn { + if let Some(isbn) = &publication.isbn.as_ref().map(|i| i.to_string()) { isbns.push(isbn.replace("-", "")); // The default product ISBN is the PDF's if publication.publication_type.eq(&PublicationType::PDF) { @@ -504,7 +503,8 @@ impl XmlElementBlock<Onix3Oapen> for WorkContributions { w.write(XmlEvent::Characters("21")).map_err(|e| e.into()) })?; write_element_block("IDValue", w, |w| { - w.write(XmlEvent::Characters(orcid)).map_err(|e| e.into()) + w.write(XmlEvent::Characters(&orcid.to_string())) + .map_err(|e| e.into()) }) })?; } @@ -650,6 +650,9 @@ mod tests { // We therefore rely on `assert!(contains)` rather than `assert_eq!` use super::*; use std::str::FromStr; + use thoth_api::model::Doi; + use thoth_api::model::Isbn; + use thoth_api::model::Orcid; use thoth_client::{ ContributionType, LanguageCode, LanguageRelation, PublicationType, WorkContributionsContributor, WorkImprint, WorkImprintPublisher, WorkIssuesSeries, @@ -685,7 +688,7 @@ mod tests { institution: None, contribution_ordinal: 1, contributor: WorkContributionsContributor { - orcid: Some("https://orcid.org/0000-0000-0000-0001".to_string()), + orcid: Some(Orcid::from_str("https://orcid.org/0000-0002-0000-0001").unwrap()), }, }; @@ -695,7 +698,7 @@ mod tests { assert!(output.contains(r#" <ContributorRole>A01</ContributorRole>"#)); assert!(output.contains(r#" <NameIdentifier>"#)); assert!(output.contains(r#" <NameIDType>21</NameIDType>"#)); - assert!(output.contains(r#" <IDValue>https://orcid.org/0000-0000-0000-0001</IDValue>"#)); + assert!(output.contains(r#" <IDValue>0000-0002-0000-0001</IDValue>"#)); assert!(output.contains(r#" </NameIdentifier>"#)); // Given name is output as NamesBeforeKey and family name as KeyNames assert!(output.contains(r#" <NamesBeforeKey>Author</NamesBeforeKey>"#)); @@ -714,7 +717,7 @@ mod tests { // No ORCID supplied assert!(!output.contains(r#" <NameIdentifier>"#)); assert!(!output.contains(r#" <NameIDType>21</NameIDType>"#)); - assert!(!output.contains(r#" <IDValue>https://orcid.org/0000-0000-0000-0001</IDValue>"#)); + assert!(!output.contains(r#" <IDValue>0000-0002-0000-0001</IDValue>"#)); assert!(!output.contains(r#" </NameIdentifier>"#)); // No given name supplied, so PersonName is output instead of KeyNames and NamesBeforeKey assert!(!output.contains(r#" <NamesBeforeKey>Author</NamesBeforeKey>"#)); @@ -947,7 +950,7 @@ mod tests { subtitle: Some("Book Subtitle".to_string()), work_type: WorkType::MONOGRAPH, edition: 1, - doi: Some("https://doi.org/10.00001/BOOK.0001".to_string()), + doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()), publication_date: Some(chrono::NaiveDate::from_ymd(1999, 12, 31)), license: Some("https://creativecommons.org/licenses/by/4.0/".to_string()), copyright_holder: "Author 1; Author 2".to_string(), @@ -955,8 +958,12 @@ mod tests { long_abstract: Some("Lorem ipsum dolor sit amet".to_string()), general_note: None, place: Some("León, Spain".to_string()), - width: None, - height: None, + width_mm: None, + width_cm: None, + width_in: None, + height_mm: None, + height_cm: None, + height_in: None, page_count: Some(334), page_breakdown: None, image_count: None, @@ -973,6 +980,7 @@ mod tests { imprint_name: "OA Editions Imprint".to_string(), publisher: WorkImprintPublisher { publisher_name: "OA Editions".to_string(), + publisher_url: None, }, }, issues: vec![], @@ -982,7 +990,7 @@ mod tests { publication_id: Uuid::from_str("00000000-0000-0000-DDDD-000000000004").unwrap(), publication_type: PublicationType::PDF, publication_url: Some("https://www.book.com/pdf".to_string()), - isbn: Some("978-1-00000-000-2".to_string()), + isbn: Some(Isbn::from_str("978-3-16-148410-0").unwrap()), prices: vec![], }], subjects: vec![], @@ -1002,7 +1010,7 @@ mod tests { assert!(output .contains(r#" <IDValue>urn:uuid:00000000-0000-0000-aaaa-000000000001</IDValue>"#)); assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); - assert!(output.contains(r#" <IDValue>9781000000002</IDValue>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); assert!(output.contains(r#" <ProductIDType>06</ProductIDType>"#)); assert!(output.contains(r#" <IDValue>10.00001/BOOK.0001</IDValue>"#)); assert!(output.contains(r#" <DescriptiveDetail>"#)); @@ -1060,7 +1068,7 @@ mod tests { assert!(output.contains(r#" <ProductRelationCode>06</ProductRelationCode>"#)); assert!(output.contains(r#" <ProductIdentifier>"#)); assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); - assert!(output.contains(r#" <IDValue>9781000000002</IDValue>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); assert!(output.contains(r#" <ProductSupply>"#)); assert!(output.contains(r#" <SupplyDetail>"#)); assert!(output.contains(r#" <Supplier>"#)); diff --git a/thoth-export-server/src/xml/onix3_project_muse.rs b/thoth-export-server/src/xml/onix3_project_muse.rs index 27068940..c0a1ee74 100644 --- a/thoth-export-server/src/xml/onix3_project_muse.rs +++ b/thoth-export-server/src/xml/onix3_project_muse.rs @@ -9,7 +9,6 @@ use xml::writer::{EventWriter, XmlEvent}; use super::{write_element_block, XmlElement, XmlSpecification}; use crate::xml::{write_full_element_block, XmlElementBlock}; -use thoth_api::model::DOI_DOMAIN; use thoth_errors::{ThothError, ThothResult}; pub struct Onix3ProjectMuse {} @@ -107,7 +106,7 @@ impl XmlElementBlock<Onix3ProjectMuse> for Work { w.write(XmlEvent::Characters("06")).map_err(|e| e.into()) })?; write_element_block("IDValue", w, |w| { - w.write(XmlEvent::Characters(&doi.replace(DOI_DOMAIN, ""))) + w.write(XmlEvent::Characters(&doi.to_string())) .map_err(|e| e.into()) }) })?; @@ -379,7 +378,7 @@ fn get_publications_data(publications: &[WorkPublications]) -> (String, Vec<Stri let mut isbns: Vec<String> = Vec::new(); for publication in publications { - if let Some(isbn) = &publication.isbn { + if let Some(isbn) = &publication.isbn.as_ref().map(|i| i.to_string()) { isbns.push(isbn.replace("-", "")); // The default product ISBN is the PDF's if publication.publication_type.eq(&PublicationType::PDF) { @@ -483,7 +482,8 @@ impl XmlElementBlock<Onix3ProjectMuse> for WorkContributions { w.write(XmlEvent::Characters("21")).map_err(|e| e.into()) })?; write_element_block("IDValue", w, |w| { - w.write(XmlEvent::Characters(orcid)).map_err(|e| e.into()) + w.write(XmlEvent::Characters(&orcid.to_string())) + .map_err(|e| e.into()) }) })?; } @@ -522,3 +522,475 @@ impl XmlElementBlock<Onix3ProjectMuse> for WorkLanguages { }) } } + +#[cfg(test)] +mod tests { + // Testing note: XML nodes cannot be guaranteed to be output in the same order every time + // We therefore rely on `assert!(contains)` rather than `assert_eq!` + use super::*; + use std::str::FromStr; + use thoth_api::model::Doi; + use thoth_api::model::Isbn; + use thoth_api::model::Orcid; + use thoth_client::{ + ContributionType, LanguageCode, LanguageRelation, PublicationType, + WorkContributionsContributor, WorkFundings, WorkImprint, WorkImprintPublisher, WorkIssues, + WorkIssuesSeries, WorkStatus, WorkSubjects, WorkType, + }; + use uuid::Uuid; + + fn generate_test_output(input: &impl XmlElementBlock<Onix3ProjectMuse>) -> String { + // Helper function based on `XmlSpecification::generate` + let mut buffer = Vec::new(); + let mut writer = xml::writer::EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut buffer); + let wrapped_output = XmlElementBlock::<Onix3ProjectMuse>::xml_element(input, &mut writer) + .map(|_| buffer) + .and_then(|onix| { + String::from_utf8(onix) + .map_err(|_| ThothError::InternalError("Could not parse XML".to_string())) + }); + assert!(wrapped_output.is_ok()); + wrapped_output.unwrap() + } + + #[test] + fn test_onix3_projectmuse_contributions() { + let mut test_contribution = WorkContributions { + contribution_type: ContributionType::AUTHOR, + first_name: Some("Author".to_string()), + last_name: "1".to_string(), + full_name: "Author 1".to_string(), + main_contribution: true, + biography: None, + institution: None, + contribution_ordinal: 1, + contributor: WorkContributionsContributor { + orcid: Some(Orcid::from_str("https://orcid.org/0000-0002-0000-0001").unwrap()), + }, + }; + + // Test standard output + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <SequenceNumber>1</SequenceNumber>"#)); + assert!(output.contains(r#" <ContributorRole>A01</ContributorRole>"#)); + assert!(output.contains(r#" <NameIdentifier>"#)); + assert!(output.contains(r#" <NameIDType>21</NameIDType>"#)); + assert!(output.contains(r#" <IDValue>0000-0002-0000-0001</IDValue>"#)); + assert!(output.contains(r#" </NameIdentifier>"#)); + // Given name is output as NamesBeforeKey and family name as KeyNames + assert!(output.contains(r#" <NamesBeforeKey>Author</NamesBeforeKey>"#)); + assert!(output.contains(r#" <KeyNames>1</KeyNames>"#)); + // PersonName is not output when given name is supplied + assert!(!output.contains(r#" <PersonName>Author 1</PersonName>"#)); + + // Change all possible values to test that output is updated + test_contribution.contribution_type = ContributionType::EDITOR; + test_contribution.contribution_ordinal = 2; + test_contribution.contributor.orcid = None; + test_contribution.first_name = None; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <SequenceNumber>2</SequenceNumber>"#)); + assert!(output.contains(r#" <ContributorRole>B01</ContributorRole>"#)); + // No ORCID supplied + assert!(!output.contains(r#" <NameIdentifier>"#)); + assert!(!output.contains(r#" <NameIDType>21</NameIDType>"#)); + assert!(!output.contains(r#" <IDValue>0000-0002-0000-0001</IDValue>"#)); + assert!(!output.contains(r#" </NameIdentifier>"#)); + // No given name supplied, so PersonName is output instead of KeyNames and NamesBeforeKey + assert!(!output.contains(r#" <NamesBeforeKey>Author</NamesBeforeKey>"#)); + assert!(!output.contains(r#" <KeyNames>1</KeyNames>"#)); + assert!(output.contains(r#" <PersonName>Author 1</PersonName>"#)); + + // Test all remaining contributor roles + test_contribution.contribution_type = ContributionType::TRANSLATOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>B06</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::PHOTOGRAPHER; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A13</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::ILUSTRATOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A12</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::MUSIC_EDITOR; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>B25</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::FOREWORD_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A23</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::INTRODUCTION_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A24</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::AFTERWORD_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A19</ContributorRole>"#)); + test_contribution.contribution_type = ContributionType::PREFACE_BY; + let output = generate_test_output(&test_contribution); + assert!(output.contains(r#" <ContributorRole>A15</ContributorRole>"#)); + } + + #[test] + fn test_onix3_projectmuse_languages() { + let mut test_language = WorkLanguages { + language_code: LanguageCode::SPA, + language_relation: LanguageRelation::TRANSLATED_FROM, + main_language: true, + }; + + // Test standard output + let output = generate_test_output(&test_language); + assert!(output.contains(r#" <LanguageRole>02</LanguageRole>"#)); + assert!(output.contains(r#" <LanguageCode>spa</LanguageCode>"#)); + + // Change all possible values to test that output is updated + test_language.language_code = LanguageCode::WEL; + for language_relation in [ + LanguageRelation::ORIGINAL, + LanguageRelation::TRANSLATED_INTO, + ] { + test_language.language_relation = language_relation; + let output = generate_test_output(&test_language); + assert!(output.contains(r#" <LanguageRole>01</LanguageRole>"#)); + assert!(output.contains(r#" <LanguageCode>wel</LanguageCode>"#)); + } + } + + #[test] + fn test_onix3_projectmuse_works() { + let mut test_work = Work { + work_id: Uuid::from_str("00000000-0000-0000-AAAA-000000000001").unwrap(), + work_status: WorkStatus::ACTIVE, + full_title: "Book Title: Book Subtitle".to_string(), + title: "Book Title".to_string(), + subtitle: Some("Book Subtitle".to_string()), + work_type: WorkType::MONOGRAPH, + edition: 1, + doi: Some(Doi::from_str("https://doi.org/10.00001/BOOK.0001").unwrap()), + publication_date: Some(chrono::NaiveDate::from_ymd(1999, 12, 31)), + license: Some("https://creativecommons.org/licenses/by/4.0/".to_string()), + copyright_holder: "Author 1; Author 2".to_string(), + short_abstract: None, + long_abstract: Some("Lorem ipsum dolor sit amet".to_string()), + general_note: None, + place: Some("León, Spain".to_string()), + width_mm: None, + width_cm: None, + width_in: None, + height_mm: None, + height_cm: None, + height_in: None, + page_count: Some(334), + page_breakdown: None, + image_count: None, + table_count: None, + audio_count: None, + video_count: None, + landing_page: Some("https://www.book.com".to_string()), + toc: Some("1. Chapter 1".to_string()), + lccn: None, + oclc: None, + cover_url: None, + cover_caption: None, + imprint: WorkImprint { + imprint_name: "OA Editions Imprint".to_string(), + publisher: WorkImprintPublisher { + publisher_name: "OA Editions".to_string(), + publisher_url: None, + }, + }, + issues: vec![WorkIssues { + issue_ordinal: 1, + series: WorkIssuesSeries { + series_type: thoth_client::SeriesType::JOURNAL, + series_name: "Name of series".to_string(), + issn_print: "1234-5678".to_string(), + issn_digital: "8765-4321".to_string(), + series_url: None, + }, + }], + contributions: vec![], + languages: vec![], + publications: vec![WorkPublications { + publication_id: Uuid::from_str("00000000-0000-0000-DDDD-000000000004").unwrap(), + publication_type: PublicationType::PDF, + publication_url: Some("https://www.book.com/pdf".to_string()), + isbn: Some(Isbn::from_str("978-3-16-148410-0").unwrap()), + prices: vec![], + }], + subjects: vec![ + WorkSubjects { + subject_code: "AAB".to_string(), + subject_type: SubjectType::BIC, + subject_ordinal: 1, + }, + WorkSubjects { + subject_code: "AAA000000".to_string(), + subject_type: SubjectType::BISAC, + subject_ordinal: 2, + }, + WorkSubjects { + subject_code: "JA85".to_string(), + subject_type: SubjectType::LCC, + subject_ordinal: 3, + }, + WorkSubjects { + subject_code: "JWA".to_string(), + subject_type: SubjectType::THEMA, + subject_ordinal: 4, + }, + WorkSubjects { + subject_code: "keyword1".to_string(), + subject_type: SubjectType::KEYWORD, + subject_ordinal: 5, + }, + WorkSubjects { + subject_code: "custom1".to_string(), + subject_type: SubjectType::CUSTOM, + subject_ordinal: 6, + }, + ], + fundings: vec![WorkFundings { + program: Some("Name of program".to_string()), + project_name: Some("Name of project".to_string()), + project_shortname: None, + grant_number: Some("Number of grant".to_string()), + jurisdiction: None, + funder: thoth_client::WorkFundingsFunder { + funder_name: "Name of funder".to_string(), + funder_doi: None, + }, + }], + }; + + // Test standard output + let output = generate_test_output(&test_work); + assert!(output.contains(r#"<Product>"#)); + assert!(output.contains( + r#" <RecordReference>urn:uuid:00000000-0000-0000-aaaa-000000000001</RecordReference>"# + )); + assert!(output.contains(r#" <NotificationType>03</NotificationType>"#)); + assert!(output.contains(r#" <RecordSourceType>01</RecordSourceType>"#)); + assert!(output.contains(r#" <ProductIdentifier>"#)); + assert!(output.contains(r#" <ProductIDType>01</ProductIDType>"#)); + assert!(output + .contains(r#" <IDValue>urn:uuid:00000000-0000-0000-aaaa-000000000001</IDValue>"#)); + assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); + assert!(output.contains(r#" <ProductIDType>06</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>10.00001/BOOK.0001</IDValue>"#)); + assert!(output.contains(r#" <DescriptiveDetail>"#)); + assert!(output.contains(r#" <ProductComposition>00</ProductComposition>"#)); + assert!(output.contains(r#" <ProductForm>EB</ProductForm>"#)); + assert!(output.contains(r#" <ProductFormDetail>E107</ProductFormDetail>"#)); + assert!(output.contains(r#" <PrimaryContentType>10</PrimaryContentType>"#)); + assert!(output.contains(r#" <EpubLicense>"#)); + assert!( + output.contains(r#" <EpubLicenseName>Creative Commons License</EpubLicenseName>"#) + ); + assert!(output.contains(r#" <EpubLicenseExpression>"#)); + assert!( + output.contains(r#" <EpubLicenseExpressionType>02</EpubLicenseExpressionType>"#) + ); + assert!(output.contains(r#" <EpubLicenseExpressionLink>https://creativecommons.org/licenses/by/4.0/</EpubLicenseExpressionLink>"#)); + assert!(output.contains(r#" <TitleDetail>"#)); + assert!(output.contains(r#" <TitleType>01</TitleType>"#)); + assert!(output.contains(r#" <TitleElement>"#)); + assert!(output.contains(r#" <TitleElementLevel>01</TitleElementLevel>"#)); + assert!(output.contains(r#" <TitleText>Book Title</TitleText>"#)); + assert!(output.contains(r#" <Subtitle>Book Subtitle</Subtitle>"#)); + assert!(output.contains(r#" <Extent>"#)); + assert!(output.contains(r#" <ExtentType>00</ExtentType>"#)); + assert!(output.contains(r#" <ExtentValue>334</ExtentValue>"#)); + assert!(output.contains(r#" <ExtentUnit>03</ExtentUnit>"#)); + assert!(output.contains(r#" <Subject>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>12</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>AAB</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>10</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>AAA000000</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>04</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>JA85</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>93</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>JWA</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>20</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>keyword1</SubjectCode>"#)); + assert!(output.contains(r#" <SubjectSchemeIdentifier>B2</SubjectSchemeIdentifier>"#)); + assert!(output.contains(r#" <SubjectCode>custom1</SubjectCode>"#)); + assert!(output.contains(r#" <CollateralDetail>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>03</TextType>"#)); + assert!(output.contains(r#" <ContentAudience>00</ContentAudience>"#)); + assert!(output.contains(r#" <Text language="eng">Lorem ipsum dolor sit amet</Text>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>04</TextType>"#)); + assert!(output.contains(r#" <Text>1. Chapter 1</Text>"#)); + assert!(output.contains(r#" <PublishingDetail>"#)); + assert!(output.contains(r#" <Imprint>"#)); + assert!(output.contains(r#" <ImprintName>OA Editions Imprint</ImprintName>"#)); + assert!(output.contains(r#" <Publisher>"#)); + assert!(output.contains(r#" <PublishingRole>01</PublishingRole>"#)); + assert!(output.contains(r#" <PublisherName>OA Editions</PublisherName>"#)); + assert!(output.contains(r#" <CityOfPublication>León, Spain</CityOfPublication>"#)); + assert!(output.contains(r#" <PublishingStatus>04</PublishingStatus>"#)); + assert!(output.contains(r#" <PublishingDate>"#)); + assert!(output.contains(r#" <PublishingDateRole>19</PublishingDateRole>"#)); + assert!(output.contains(r#" <Date dateformat="01">199912</Date>"#)); + assert!(output.contains(r#" <RelatedProduct>"#)); + assert!(output.contains(r#" <ProductRelationCode>06</ProductRelationCode>"#)); + assert!(output.contains(r#" <ProductIdentifier>"#)); + assert!(output.contains(r#" <ProductIDType>15</ProductIDType>"#)); + assert!(output.contains(r#" <IDValue>9783161484100</IDValue>"#)); + assert!(output.contains(r#" <ProductSupply>"#)); + assert!(output.contains(r#" <SupplyDetail>"#)); + assert!(output.contains(r#" <Supplier>"#)); + assert!(output.contains(r#" <SupplierRole>09</SupplierRole>"#)); + assert!(output.contains(r#" <SupplierName>OA Editions</SupplierName>"#)); + assert!(output.contains(r#" <Website>"#)); + assert!(output.contains(r#" <WebsiteRole>01</WebsiteRole>"#)); + assert!(output.contains( + r#" <WebsiteDescription>Publisher's website: web shop</WebsiteDescription>"# + )); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com</WebsiteLink>"#)); + assert!(output.contains(r#" <ProductAvailability>99</ProductAvailability>"#)); + assert!(output.contains(r#" <UnpricedItemType>04</UnpricedItemType>"#)); + assert!(output.contains(r#" <SupplierRole>09</SupplierRole>"#)); + assert!(output.contains(r#" <SupplierName>OA Editions</SupplierName>"#)); + assert!(output.contains(r#" <WebsiteRole>29</WebsiteRole>"#)); + assert!(output.contains(r#" <WebsiteDescription>Publisher's website: download the title</WebsiteDescription>"#)); + assert!(output.contains(r#" <WebsiteLink>https://www.book.com/pdf</WebsiteLink>"#)); + + // Test that OAPEN-only blocks are not output in Project MUSE format + assert!(!output.contains(r#" <Audience>"#)); + assert!(!output.contains(r#" <AudienceCodeType>01</AudienceCodeType>"#)); + assert!(!output.contains(r#" <AudienceCodeValue>06</AudienceCodeValue>"#)); + assert!(!output.contains(r#" <PublishingRole>16</PublishingRole>"#)); + assert!(!output.contains(r#" <PublisherName>Name of funder</PublisherName>"#)); + assert!(!output.contains(r#" <Funding>"#)); + assert!(!output.contains(r#" <FundingIdentifier>"#)); + assert!(!output.contains(r#" <FundingIDType>01</FundingIDType>"#)); + assert!(!output.contains(r#" <IDTypeName>programname</IDTypeName>"#)); + assert!(!output.contains(r#" <IDValue>Name of program</IDValue>"#)); + assert!(!output.contains(r#" <IDTypeName>projectname</IDTypeName>"#)); + assert!(!output.contains(r#" <IDValue>Name of project</IDValue>"#)); + assert!(!output.contains(r#" <IDTypeName>grantnumber</IDTypeName>"#)); + assert!(!output.contains(r#" <IDValue>Number of grant</IDValue>"#)); + assert!(!output.contains(r#" <Collection>"#)); + assert!(!output.contains(r#" <CollectionType>10</CollectionType>"#)); + assert!(!output.contains(r#" <TitleElementLevel>02</TitleElementLevel>"#)); + assert!(!output.contains(r#" <PartNumber>1</PartNumber>"#)); + assert!(!output.contains(r#" <TitleText>Name of series</TitleText>"#)); + + // Remove some values to test non-output of optional blocks + test_work.doi = None; + test_work.license = None; + test_work.subtitle = None; + test_work.page_count = None; + test_work.long_abstract = None; + test_work.place = None; + test_work.publication_date = None; + test_work.landing_page = None; + test_work.subjects.clear(); + let output = generate_test_output(&test_work); + // No DOI supplied + assert!(!output.contains(r#" <ProductIDType>06</ProductIDType>"#)); + assert!(!output.contains(r#" <IDValue>10.00001/BOOK.0001</IDValue>"#)); + // No licence supplied + assert!(!output.contains(r#" <EpubLicense>"#)); + assert!(!output + .contains(r#" <EpubLicenseName>Creative Commons License</EpubLicenseName>"#)); + assert!(!output.contains(r#" <EpubLicenseExpression>"#)); + assert!(!output + .contains(r#" <EpubLicenseExpressionType>02</EpubLicenseExpressionType>"#)); + assert!(!output.contains(r#" <EpubLicenseExpressionLink>https://creativecommons.org/licenses/by/4.0/</EpubLicenseExpressionLink>"#)); + // No subtitle supplied: work FullTitle is used instead of Title + assert!(!output.contains(r#" <TitleText>Book Title</TitleText>"#)); + assert!(!output.contains(r#" <Subtitle>Book Subtitle</Subtitle>"#)); + assert!(output.contains(r#" <TitleText>Book Title: Book Subtitle</TitleText>"#)); + // No page count supplied + assert!(!output.contains(r#" <Extent>"#)); + assert!(!output.contains(r#" <ExtentType>00</ExtentType>"#)); + assert!(!output.contains(r#" <ExtentValue>334</ExtentValue>"#)); + assert!(!output.contains(r#" <ExtentUnit>03</ExtentUnit>"#)); + // No long abstract supplied: CollateralDetail block only contains TOC + assert!(output.contains(r#" <CollateralDetail>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>04</TextType>"#)); + assert!(output.contains(r#" <ContentAudience>00</ContentAudience>"#)); + assert!(output.contains(r#" <Text>1. Chapter 1</Text>"#)); + assert!(!output.contains(r#" <TextType>03</TextType>"#)); + assert!(!output.contains(r#" <Text language="eng">Lorem ipsum dolor sit amet</Text>"#)); + // No place supplied + assert!(!output.contains(r#" <CityOfPublication>León, Spain</CityOfPublication>"#)); + // No publication date supplied + assert!(!output.contains(r#" <PublishingDate>"#)); + assert!(!output.contains(r#" <PublishingDateRole>19</PublishingDateRole>"#)); + assert!(!output.contains(r#" <Date dateformat="01">199912</Date>"#)); + // No landing page supplied: only one SupplyDetail block, linking to PDF download + assert!(!output.contains(r#" <WebsiteRole>01</WebsiteRole>"#)); + assert!(!output.contains( + r#" <WebsiteDescription>Publisher's website: web shop</WebsiteDescription>"# + )); + assert!(!output.contains(r#" <WebsiteLink>https://www.book.com</WebsiteLink>"#)); + // No subjects supplied + assert!(!output.contains(r#" <Subject>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>12</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>AAB</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>10</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>AAA000000</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>04</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>JA85</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>93</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>JWA</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>20</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>keyword1</SubjectCode>"#)); + assert!(!output.contains(r#" <SubjectSchemeIdentifier>B2</SubjectSchemeIdentifier>"#)); + assert!(!output.contains(r#" <SubjectCode>custom1</SubjectCode>"#)); + + // Replace long abstract but remove TOC + // Result: CollateralDetail block still present, but now only contains long abstract + test_work.long_abstract = Some("Lorem ipsum dolor sit amet".to_string()); + test_work.toc = None; + let output = generate_test_output(&test_work); + assert!(output.contains(r#" <CollateralDetail>"#)); + assert!(output.contains(r#" <TextContent>"#)); + assert!(output.contains(r#" <TextType>03</TextType>"#)); + assert!(output.contains(r#" <ContentAudience>00</ContentAudience>"#)); + assert!(output.contains(r#" <Text language="eng">Lorem ipsum dolor sit amet</Text>"#)); + assert!(!output.contains(r#" <TextType>04</TextType>"#)); + assert!(!output.contains(r#" <Text>1. Chapter 1</Text>"#)); + + // Remove both TOC and long abstract + // Result: No CollateralDetail block present at all + test_work.long_abstract = None; + let output = generate_test_output(&test_work); + assert!(!output.contains(r#" <CollateralDetail>"#)); + assert!(!output.contains(r#" <TextContent>"#)); + assert!(!output.contains(r#" <TextType>03</TextType>"#)); + assert!(!output.contains(r#" <ContentAudience>00</ContentAudience>"#)); + assert!(!output.contains(r#" <Text language="eng">Lorem ipsum dolor sit amet</Text>"#)); + assert!(!output.contains(r#" <TextType>04</TextType>"#)); + assert!(!output.contains(r#" <Text>1. Chapter 1</Text>"#)); + + // Remove the only publication, which is the PDF + // Result: error (can't generate OAPEN ONIX without PDF URL) + test_work.publications.clear(); + // Can't use helper function for this as it assumes Ok rather than Err + let mut buffer = Vec::new(); + let mut writer = xml::writer::EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut buffer); + let wrapped_output = + XmlElementBlock::<Onix3ProjectMuse>::xml_element(&test_work, &mut writer) + .map(|_| buffer) + .and_then(|onix| { + String::from_utf8(onix) + .map_err(|_| ThothError::InternalError("Could not parse XML".to_string())) + }); + assert!(wrapped_output.is_err()); + let output = wrapped_output.unwrap_err().to_string(); + assert_eq!( + output, + "Could not generate onix_3.0::project_muse: Missing PDF URL".to_string() + ); + } +}