From 637baf897719948cd5d605f37a4c12c94a0a4c61 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 13 Nov 2024 11:37:48 +0000 Subject: [PATCH 01/41] Add last updated queries --- thoth-client/assets/queries.graphql | 16 ++++++ thoth-client/src/lib.rs | 87 ++++++++++++++++++++++++++--- thoth-client/src/queries.rs | 19 +++++++ 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/thoth-client/assets/queries.graphql b/thoth-client/assets/queries.graphql index 8ad72792..14da222f 100644 --- a/thoth-client/assets/queries.graphql +++ b/thoth-client/assets/queries.graphql @@ -256,3 +256,19 @@ query WorkCountQuery( ) { workCount(publishers: $publishers) } + +query WorkLastUpdatedQuery( + $workId: Uuid! +) { + work(workId: $workId) { + updatedAtWithRelations + } +} + +query WorksLastUpdatedQuery( + $publishers: [Uuid!] +) { + works(publishers: $publishers, limit: 1, order: {field: UPDATED_AT_WITH_RELATIONS, direction: DESC}) { + updatedAtWithRelations + } +} diff --git a/thoth-client/src/lib.rs b/thoth-client/src/lib.rs index 2ee4d8a0..e7aab796 100644 --- a/thoth-client/src/lib.rs +++ b/thoth-client/src/lib.rs @@ -3,22 +3,24 @@ mod parameters; #[allow(clippy::derive_partial_eq_without_eq)] mod queries; +pub use crate::parameters::QueryParameters; +use crate::parameters::{WorkQueryVariables, WorksQueryVariables}; +pub use crate::queries::work_query::*; +use crate::queries::{ + work_count_query, work_last_updated_query, work_query, works_last_updated_query, works_query, + WorkCountQuery, WorkLastUpdatedQuery, WorkQuery, WorksLastUpdatedQuery, WorksQuery, +}; +pub use chrono::NaiveDate; use graphql_client::GraphQLQuery; use graphql_client::Response; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; use serde::Serialize; use std::future::Future; +use thoth_api::model::Timestamp; use thoth_errors::{ThothError, ThothResult}; use uuid::Uuid; -pub use crate::parameters::QueryParameters; -use crate::parameters::{WorkQueryVariables, WorksQueryVariables}; -pub use crate::queries::work_query::*; -use crate::queries::{ - work_count_query, work_query, works_query, WorkCountQuery, WorkQuery, WorksQuery, -}; - /// Maximum number of allowed request retries attempts. const MAX_REQUEST_RETRIES: u32 = 5; @@ -154,4 +156,75 @@ impl ThothClient { None => Err(ThothError::EntityNotFound), } } + + /// Get the `updated_at_with_relations` date of a `Work` in Thoth + /// + /// # Errors + /// + /// This method fails if there was an error while sending the request + /// + /// # Example + /// + /// ```no_run + /// # use thoth_errors::ThothResult; + /// # use thoth_client::{ThothClient, NaiveDate}; + /// # use uuid::Uuid; + /// + /// # async fn run() -> ThothResult { + /// let thoth_client = ThothClient::new("https://api.thoth.pub/graphql".to_string()); + /// let publisher_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; + /// let work_last_updated = thoth_client.get_work_last_updated(publisher_id).await?; + /// # Ok(work_last_updated) + /// # } + /// ``` + pub async fn get_work_last_updated(&self, work_id: Uuid) -> ThothResult { + let variables = work_last_updated_query::Variables { work_id }; + let request_body = WorkLastUpdatedQuery::build_query(variables); + let res = self.post_request(&request_body).await.await?; + let response_body: Response = res.json().await?; + match response_body.data { + Some(data) => Ok(data.work.updated_at_with_relations), + None => Err(ThothError::EntityNotFound), + } + } + + /// Get the last `updated_at_with_relations` date of `Work`s in Thoth + /// + /// # Errors + /// + /// This method fails if there was an error while sending the request + /// + /// # Example + /// + /// ```no_run + /// # use thoth_errors::ThothResult; + /// # use thoth_client::{ThothClient, NaiveDate}; + /// # use uuid::Uuid; + /// + /// # async fn run() -> ThothResult { + /// let thoth_client = ThothClient::new("https://api.thoth.pub/graphql".to_string()); + /// let publisher_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; + /// let work_last_updated = thoth_client.get_works_last_updated(Some(vec![publisher_id])).await?; + /// # Ok(work_last_updated) + /// # } + /// ``` + pub async fn get_works_last_updated( + &self, + publishers: Option>, + ) -> ThothResult { + let variables = works_last_updated_query::Variables { publishers }; + let request_body = WorksLastUpdatedQuery::build_query(variables); + let res = self.post_request(&request_body).await.await?; + let response_body: Response = res.json().await?; + match response_body.data { + Some(data) => { + if let Some(work) = data.works.first() { + Ok(work.updated_at_with_relations) + } else { + Err(ThothError::EntityNotFound) + } + } + None => Err(ThothError::EntityNotFound), + } + } } diff --git a/thoth-client/src/queries.rs b/thoth-client/src/queries.rs index 07bb7014..24123891 100644 --- a/thoth-client/src/queries.rs +++ b/thoth-client/src/queries.rs @@ -9,6 +9,7 @@ use thoth_api::model::Doi; use thoth_api::model::Isbn; use thoth_api::model::Orcid; use thoth_api::model::Ror; +use thoth_api::model::Timestamp; use uuid::Uuid; // Juniper v0.16 onwards converts Rust `NaiveDate` to GraphQL scalar `Date`, @@ -72,6 +73,24 @@ pub struct WorksQuery; )] pub struct WorkCountQuery; +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "assets/schema.graphql", + query_path = "assets/queries.graphql", + response_derives = "Debug,Clone,Deserialize,Serialize,PartialEq", + variables_derives = "Debug,PartialEq" +)] +pub struct WorkLastUpdatedQuery; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "assets/schema.graphql", + query_path = "assets/queries.graphql", + response_derives = "Debug,Clone,Deserialize,Serialize,PartialEq", + variables_derives = "Debug,PartialEq" +)] +pub struct WorksLastUpdatedQuery; + // Needed to set work_query::Work as the canonical struct for the shared fragment in the two queries // until https://github.com/graphql-rust/graphql-client/issues/312 gets fixed impl From for work_query::Work { From 50aeb3819c13e47c0c3b15feb06689f0530cf6ba Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 13 Nov 2024 11:38:13 +0000 Subject: [PATCH 02/41] Allow copying timestamps --- thoth-api/src/model/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thoth-api/src/model/mod.rs b/thoth-api/src/model/mod.rs index 42fb26c4..936f0f5f 100644 --- a/thoth-api/src/model/mod.rs +++ b/thoth-api/src/model/mod.rs @@ -99,7 +99,7 @@ pub struct Ror(String); description = "RFC 3339 combined date and time in UTC time zone (e.g. \"1999-12-31T23:59:00Z\")" ) )] -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Timestamp(DateTime); impl Default for Timestamp { From ad55c3b9f61b97c104d22fe23e4596f770c1d093 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 13 Nov 2024 15:52:40 +0000 Subject: [PATCH 03/41] Implement parsing timestamps --- thoth-api/src/model/mod.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/thoth-api/src/model/mod.rs b/thoth-api/src/model/mod.rs index 936f0f5f..8f5ff516 100644 --- a/thoth-api/src/model/mod.rs +++ b/thoth-api/src/model/mod.rs @@ -99,9 +99,20 @@ pub struct Ror(String); description = "RFC 3339 combined date and time in UTC time zone (e.g. \"1999-12-31T23:59:00Z\")" ) )] -#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd)] pub struct Timestamp(DateTime); +impl Timestamp { + pub fn to_rfc3339(&self) -> String { + self.0.to_rfc3339() + } + + pub fn parse_from_rfc3339(input: &str) -> ThothResult { + let timestamp = DateTime::parse_from_rfc3339(input)?.with_timezone(&Utc); + Ok(Timestamp(timestamp)) + } +} + impl Default for Timestamp { fn default() -> Timestamp { Timestamp(TimeZone::timestamp_opt(&Utc, 0, 0).unwrap()) From 3053e4df7a340e8ce2159e845771707a2fa36325 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 13 Nov 2024 15:57:13 +0000 Subject: [PATCH 04/41] Add redis --- .env.example | 2 ++ Makefile | 4 ++++ docker-compose.dev.yml | 6 ++++++ docker-compose.yml | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/.env.example b/.env.example index 921f0225..311a47a3 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,8 @@ THOTH_EXPORT_API=http://localhost:8181 THOTH_DOMAIN=localhost # Full postgres URL DATABASE_URL=postgres://thoth:thoth@localhost/thoth +# Full redis URL +REDIS_URL=redis://localhost:6379 # Authentication cookie secret key SECRET_KEY=an_up_to_255_bytes_random_key # Logging level diff --git a/Makefile b/Makefile index a5158315..6b0bf3bc 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ docker-dev-build \ docker-dev-run \ docker-dev-db \ + docker-dev-redis \ build \ test \ clippy \ @@ -44,6 +45,9 @@ docker-dev-run: docker-dev-db: docker compose -f docker-compose.dev.yml up db +docker-dev-redis: + docker compose -f docker-compose.dev.yml up redis + build: cargo build -vv diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 22314dd3..79df39f5 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -11,6 +11,12 @@ services: env_file: - .env + redis: + image: redis:alpine + container_name: "thoth_redis" + ports: + - "6379:6379" + graphql-api: build: context: . diff --git a/docker-compose.yml b/docker-compose.yml index 0cb1ec36..510311df 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,11 @@ services: env_file: - .env + redis: + image: redis:alpine + container_name: "thoth_redis" + restart: unless-stopped + graphql-api: image: openbookpublishers/thoth container_name: "thoth_graphql_api" From 49c6b9b0c68a3944ef3655bccfab34f0d855f08a Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 13 Nov 2024 15:57:54 +0000 Subject: [PATCH 05/41] Add redis --- Cargo.lock | 54 +++++++++++++++++++++++++++++++++++++++++- thoth-api/Cargo.toml | 3 ++- thoth-api/src/lib.rs | 2 ++ thoth-api/src/redis.rs | 13 ++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 thoth-api/src/redis.rs diff --git a/Cargo.lock b/Cargo.lock index 5a57063b..eea9d2e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "argon2rs" version = "0.2.5" @@ -718,6 +724,20 @@ dependencies = [ "unreachable", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "console" version = "0.15.8" @@ -1463,7 +1483,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474" dependencies = [ - "combine", + "combine 3.8.1", "thiserror", ] @@ -2540,6 +2560,29 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redis" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cccf17a692ce51b86564334614d72dcae1def0fd5ecebc9f02956da74352b5" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "combine 4.6.7", + "futures-util", + "itoa", + "num-bigint", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "socket2", + "tokio", + "tokio-util", + "url", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2944,6 +2987,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.8" @@ -3215,6 +3264,7 @@ dependencies = [ "lazy_static", "phf", "rand", + "redis", "regex", "serde", "serde_derive", @@ -3300,12 +3350,14 @@ name = "thoth-errors" version = "0.12.14" dependencies = [ "actix-web", + "chrono", "csv", "dialoguer", "diesel", "juniper", "marc", "phf", + "redis", "reqwest", "reqwest-middleware", "serde", diff --git a/thoth-api/Cargo.toml b/thoth-api/Cargo.toml index d3703fbf..e92eaef5 100644 --- a/thoth-api/Cargo.toml +++ b/thoth-api/Cargo.toml @@ -13,7 +13,7 @@ travis-ci = { repository = "openbookpublishers/thoth" } maintenance = { status = "actively-developed" } [features] -backend = ["diesel", "diesel-derive-enum", "diesel_migrations", "futures", "actix-web", "jsonwebtoken"] +backend = ["diesel", "diesel-derive-enum", "diesel_migrations", "futures", "actix-web", "jsonwebtoken", "redis"] [dependencies] thoth-errors = { version = "=0.12.14", path = "../thoth-errors" } @@ -32,6 +32,7 @@ juniper = { version = "0.16.1", features = ["chrono", "schema-language", "uuid"] lazy_static = "1.5.0" phf = { version = "0.11", features = ["macros"] } rand = "0.8.5" +redis = { version = "0.27.5", features = ["tokio-comp"], optional = true } regex = "1.10.6" serde = { version = "1.0", features = ["derive"] } serde_derive = "1.0" diff --git a/thoth-api/src/lib.rs b/thoth-api/src/lib.rs index 8b40d443..8495057b 100644 --- a/thoth-api/src/lib.rs +++ b/thoth-api/src/lib.rs @@ -21,6 +21,8 @@ pub mod graphql; #[macro_use] pub mod model; #[cfg(feature = "backend")] +pub mod redis; +#[cfg(feature = "backend")] mod schema; macro_rules! apis { diff --git a/thoth-api/src/redis.rs b/thoth-api/src/redis.rs new file mode 100644 index 00000000..b725f7f7 --- /dev/null +++ b/thoth-api/src/redis.rs @@ -0,0 +1,13 @@ +use dotenv::dotenv; +pub use redis::{AsyncCommands, Client as RedisClient}; +use std::env; + +fn get_redis_url() -> String { + dotenv().ok(); + env::var("REDIS_URL").expect("REDIS_URL must be set") +} + +pub fn redis_client() -> RedisClient { + let redis_url = get_redis_url(); + RedisClient::open(redis_url).expect("Failed to open redis client") +} From 300714ac5cae5bbe773633bc7a9ad06d97678893 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 13 Nov 2024 15:58:07 +0000 Subject: [PATCH 06/41] Add chrono and redis errors --- thoth-errors/Cargo.toml | 2 ++ thoth-errors/src/lib.rs | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/thoth-errors/Cargo.toml b/thoth-errors/Cargo.toml index dfce7b1e..50810817 100644 --- a/thoth-errors/Cargo.toml +++ b/thoth-errors/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/thoth-pub/thoth" readme = "README.md" [dependencies] +chrono = "0.4.31" thiserror = "1.0" reqwest = { version = "0.12", features = ["json"] } serde = "1.0" @@ -24,5 +25,6 @@ csv = "1.3.0" juniper = "0.16.1" marc = { version = "3.1.1", features = ["xml"] } phf = { version = "0.11", features = ["macros"] } +redis = "0.27.5" reqwest-middleware = "0.3.3" xml-rs = "0.8.19" diff --git a/thoth-errors/src/lib.rs b/thoth-errors/src/lib.rs index c5e25123..2e6081ef 100644 --- a/thoth-errors/src/lib.rs +++ b/thoth-errors/src/lib.rs @@ -19,6 +19,8 @@ pub enum ThothError { InvalidSubjectCode(String, String), #[error("Database error: {0}")] DatabaseError(String), + #[error("Redis error: {0}")] + RedisError(String), #[error("{0}")] DatabaseConstraintError(&'static str), #[error("Internal error: {0}")] @@ -35,12 +37,16 @@ pub enum ThothError { InvalidMetadataSpecification(String), #[error("Invalid UUID supplied.")] InvalidUuid, + #[error("Invalid timestamp supplied.")] + InvalidTimestamp, #[error("CSV Error: {0}")] CsvError(String), #[error("MARC Error: {0}")] MarcError(String), #[error("Could not generate {0}: {1}")] IncompleteMetadataRecord(String, String), + #[error("The metadata record has not yet been generated.")] + MetadataRecordNotGenerated, #[error("{0} is not a validly formatted ORCID and will not be saved")] OrcidParseError(String), #[error("{0} is not a validly formatted DOI and will not be saved")] @@ -141,6 +147,9 @@ impl actix_web::error::ResponseError for ThothError { ThothError::DatabaseError { .. } => { HttpResponse::InternalServerError().json("DB error") } + ThothError::RedisError { .. } => { + HttpResponse::InternalServerError().json("Redis error") + } ThothError::IncompleteMetadataRecord(_, _) => { HttpResponse::NotFound().json(self.to_string()) } @@ -253,6 +262,19 @@ impl From for ThothError { } } +impl From for ThothError { + fn from(_: chrono::ParseError) -> Self { + ThothError::InvalidTimestamp + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl From for ThothError { + fn from(e: redis::RedisError) -> Self { + ThothError::RedisError(e.to_string()) + } +} + #[cfg(test)] mod tests { use super::*; From 5c865ac9761b980082a370c33e923680b3dea130 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 10:47:29 +0000 Subject: [PATCH 07/41] Implement deadpool redis --- thoth-api/Cargo.toml | 4 ++-- thoth-api/src/redis.rs | 28 ++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/thoth-api/Cargo.toml b/thoth-api/Cargo.toml index e92eaef5..a3e8d44a 100644 --- a/thoth-api/Cargo.toml +++ b/thoth-api/Cargo.toml @@ -13,7 +13,7 @@ travis-ci = { repository = "openbookpublishers/thoth" } maintenance = { status = "actively-developed" } [features] -backend = ["diesel", "diesel-derive-enum", "diesel_migrations", "futures", "actix-web", "jsonwebtoken", "redis"] +backend = ["diesel", "diesel-derive-enum", "diesel_migrations", "futures", "actix-web", "jsonwebtoken", "deadpool-redis"] [dependencies] thoth-errors = { version = "=0.12.14", path = "../thoth-errors" } @@ -21,6 +21,7 @@ actix-web = { version = "4.8", optional = true } argon2rs = "0.2.5" isbn2 = "0.4.0" chrono = { version = "0.4.31", features = ["serde"] } +deadpool-redis = { version = "0.18.0", optional = true } diesel = { version = "2.2.3", features = ["postgres", "uuid", "chrono", "r2d2", "64-column-tables", "serde_json"], optional = true } diesel-derive-enum = { version = "2.1.0", features = ["postgres"], optional = true } diesel-derive-newtype = "2.1.2" @@ -32,7 +33,6 @@ juniper = { version = "0.16.1", features = ["chrono", "schema-language", "uuid"] lazy_static = "1.5.0" phf = { version = "0.11", features = ["macros"] } rand = "0.8.5" -redis = { version = "0.27.5", features = ["tokio-comp"], optional = true } regex = "1.10.6" serde = { version = "1.0", features = ["derive"] } serde_derive = "1.0" diff --git a/thoth-api/src/redis.rs b/thoth-api/src/redis.rs index b725f7f7..04adc1c7 100644 --- a/thoth-api/src/redis.rs +++ b/thoth-api/src/redis.rs @@ -1,13 +1,33 @@ +use deadpool_redis::{redis::AsyncCommands, Config, Connection, Pool}; use dotenv::dotenv; -pub use redis::{AsyncCommands, Client as RedisClient}; use std::env; +use thoth_errors::{ThothError, ThothResult}; +pub type RedisPool = Pool; +type RedisConnection = Connection; + +pub fn init_pool() -> RedisPool { + Config::from_url(get_redis_url()) + .builder() + .expect("Failed to create redis pool.") + .build() + .expect("Failed to build redis pool.") +} fn get_redis_url() -> String { dotenv().ok(); env::var("REDIS_URL").expect("REDIS_URL must be set") } -pub fn redis_client() -> RedisClient { - let redis_url = get_redis_url(); - RedisClient::open(redis_url).expect("Failed to open redis client") +async fn create_connection(pool: &RedisPool) -> ThothResult { + pool.get().await.map_err(ThothError::from) +} + +pub async fn set(pool: &RedisPool, key: &str, value: &str) -> ThothResult<()> { + let mut con = create_connection(pool).await?; + con.set(key, value).await.map_err(ThothError::from) +} + +pub async fn get(pool: &RedisPool, key: &str) -> ThothResult { + let mut con = create_connection(pool).await?; + con.get(key).await.map_err(ThothError::from) } From 343ee92f6cd6f518b748eca6c676edd5efe899c1 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 10:47:56 +0000 Subject: [PATCH 08/41] Implement deadpool redis --- thoth-errors/Cargo.toml | 2 +- thoth-errors/src/lib.rs | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/thoth-errors/Cargo.toml b/thoth-errors/Cargo.toml index 50810817..e68fc093 100644 --- a/thoth-errors/Cargo.toml +++ b/thoth-errors/Cargo.toml @@ -19,12 +19,12 @@ yewtil = { version = "0.4.0", features = ["fetch"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] actix-web = "4.9" +deadpool-redis = "0.18.0" dialoguer = { version = "0.11.0", features = ["password"] } diesel = "2.2.3" csv = "1.3.0" juniper = "0.16.1" marc = { version = "3.1.1", features = ["xml"] } phf = { version = "0.11", features = ["macros"] } -redis = "0.27.5" reqwest-middleware = "0.3.3" xml-rs = "0.8.19" diff --git a/thoth-errors/src/lib.rs b/thoth-errors/src/lib.rs index 2e6081ef..3ec8312d 100644 --- a/thoth-errors/src/lib.rs +++ b/thoth-errors/src/lib.rs @@ -269,12 +269,19 @@ impl From for ThothError { } #[cfg(not(target_arch = "wasm32"))] -impl From for ThothError { - fn from(e: redis::RedisError) -> Self { +impl From for ThothError { + fn from(e: deadpool_redis::redis::RedisError) -> Self { ThothError::RedisError(e.to_string()) } } +#[cfg(not(target_arch = "wasm32"))] +impl From for ThothError { + fn from(e: deadpool_redis::PoolError) -> Self { + ThothError::InternalError(e.to_string()) + } +} + #[cfg(test)] mod tests { use super::*; From 9a8500cd347244950c89edbe8fe82a64b3d35fbb Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 10:48:22 +0000 Subject: [PATCH 09/41] Implement deadpool redis --- Cargo.lock | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eea9d2e6..2aebf763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -901,6 +901,36 @@ dependencies = [ "syn 2.0.76", ] +[[package]] +name = "deadpool" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" +dependencies = [ + "deadpool-runtime", + "num_cpus", + "tokio", +] + +[[package]] +name = "deadpool-redis" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae6799b68a735270e4344ee3e834365f707c72da09c9a8bb89b45cc3351395" +dependencies = [ + "deadpool", + "redis", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" +dependencies = [ + "tokio", +] + [[package]] name = "deranged" version = "0.3.11" @@ -2136,6 +2166,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.3" @@ -2576,8 +2616,6 @@ dependencies = [ "percent-encoding", "pin-project-lite", "ryu", - "sha1_smol", - "socket2", "tokio", "tokio-util", "url", @@ -2987,12 +3025,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.8" @@ -3252,6 +3284,7 @@ dependencies = [ "argon2rs", "cargo-husky", "chrono", + "deadpool-redis", "diesel", "diesel-derive-enum", "diesel-derive-newtype", @@ -3264,7 +3297,6 @@ dependencies = [ "lazy_static", "phf", "rand", - "redis", "regex", "serde", "serde_derive", @@ -3352,12 +3384,12 @@ dependencies = [ "actix-web", "chrono", "csv", + "deadpool-redis", "dialoguer", "diesel", "juniper", "marc", "phf", - "redis", "reqwest", "reqwest-middleware", "serde", From b0a4754fc186c49b86170ca6333ea7a427f8ad48 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 10:53:22 +0000 Subject: [PATCH 10/41] Timestamp now implements Copy --- thoth-api/src/graphql/model.rs | 70 +++++++++++++------------- thoth-app/src/component/publication.rs | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/thoth-api/src/graphql/model.rs b/thoth-api/src/graphql/model.rs index 830cb905..4214c2c4 100644 --- a/thoth-api/src/graphql/model.rs +++ b/thoth-api/src/graphql/model.rs @@ -2560,12 +2560,12 @@ impl Work { #[graphql(description = "Date and time at which the work record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the work record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Page number on which the work begins (only applicable to chapters)")] @@ -2589,7 +2589,7 @@ impl Work { description = "Date and time at which the work record or any of its linked records was last updated" )] pub fn updated_at_with_relations(&self) -> Timestamp { - self.updated_at_with_relations.clone() + self.updated_at_with_relations } #[graphql(description = "Get this work's imprint")] @@ -2900,12 +2900,12 @@ impl Publication { #[graphql(description = "Date and time at which the publication record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the publication record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql( @@ -3081,12 +3081,12 @@ impl Publisher { #[graphql(description = "Date and time at which the publisher record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the publisher record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get imprints linked to this publisher")] @@ -3160,12 +3160,12 @@ impl Imprint { #[graphql(description = "Date and time at which the imprint record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the imprint record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the publisher to which this imprint belongs")] @@ -3267,12 +3267,12 @@ impl Contributor { #[graphql(description = "Date and time at which the contributor record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the contributor record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get contributions linked to this contributor")] @@ -3345,12 +3345,12 @@ impl Contribution { #[graphql(description = "Date and time at which the contribution record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the contribution record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql( @@ -3473,12 +3473,12 @@ impl Series { #[graphql(description = "Date and time at which the series record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the series record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the imprint linked to this series")] @@ -3541,12 +3541,12 @@ impl Issue { #[graphql(description = "Date and time at which the issue record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the issue record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the series to which the issue belongs")] @@ -3591,12 +3591,12 @@ impl Language { #[graphql(description = "Date and time at which the language record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the language record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the work which has this language")] @@ -3641,12 +3641,12 @@ impl Location { #[graphql(description = "Date and time at which the location record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the location record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the publication linked to this location")] @@ -3681,12 +3681,12 @@ impl Price { #[graphql(description = "Date and time at which the price record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the price record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the publication linked to this price")] @@ -3726,12 +3726,12 @@ impl Subject { #[graphql(description = "Date and time at which the subject record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the subject record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the work to which the subject is linked")] @@ -3775,12 +3775,12 @@ impl Institution { #[graphql(description = "Date and time at which the institution record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the institution record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get fundings linked to this institution")] @@ -3884,12 +3884,12 @@ impl Funding { #[graphql(description = "Date and time at which the funding record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the funding record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the funded work")] @@ -3936,12 +3936,12 @@ impl Affiliation { #[graphql(description = "Date and time at which the affiliation record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the affiliation record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the institution linked to this affiliation")] @@ -3986,12 +3986,12 @@ impl WorkRelation { #[graphql(description = "Date and time at which the work relation record was created")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Date and time at which the work relation record was last updated")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "Get the other work in the relationship")] @@ -4136,12 +4136,12 @@ impl Reference { #[graphql(description = "Timestamp of the creation of this record within Thoth.")] pub fn created_at(&self) -> Timestamp { - self.created_at.clone() + self.created_at } #[graphql(description = "Timestamp of the last update to this record within Thoth.")] pub fn updated_at(&self) -> Timestamp { - self.updated_at.clone() + self.updated_at } #[graphql(description = "The citing work.")] diff --git a/thoth-app/src/component/publication.rs b/thoth-app/src/component/publication.rs index 5cbe20de..278b088f 100644 --- a/thoth-app/src/component/publication.rs +++ b/thoth-app/src/component/publication.rs @@ -106,7 +106,7 @@ impl Component for PublicationComponent { isbn: self.publication.isbn.clone(), // Not used by child form created_at: Default::default(), - updated_at: self.publication.updated_at.clone(), + updated_at: self.publication.updated_at, width_mm: self.publication.width_mm, width_in: self.publication.width_in, height_mm: self.publication.height_mm, From b74ffe2d217a90f7538aa73460ec7210be02c66a Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 10:59:31 +0000 Subject: [PATCH 11/41] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a074f77..8f93618e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + - Implement Redis connection pools using `deadpool-redis` + - Added `WorkLastUpdatedQuery` and `WorksLastUpdatedQuery` queries to thoth-client + - Implement `Copy`, `Ord` and `PartialOrd` traits for `Timestamp` + - Implement parsing from and to RFC 3339 strings for `Timestamp` ## [[0.12.14]](https://github.com/thoth-pub/thoth/releases/tag/v0.12.14) - 2024-11-04 ### Changed From 72f2d306df0e6646a72ca827e0e19c5832b49332 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 11:03:51 +0000 Subject: [PATCH 12/41] Add redis pool to server --- thoth-export-server/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/thoth-export-server/src/lib.rs b/thoth-export-server/src/lib.rs index 187770ea..80a7d54e 100644 --- a/thoth-export-server/src/lib.rs +++ b/thoth-export-server/src/lib.rs @@ -5,6 +5,7 @@ use actix_cors::Cors; use actix_web::{middleware::Logger, web::Data, App, HttpServer}; use paperclip::actix::{web, web::HttpResponse, OpenApiExt}; use paperclip::v2::models::{Contact, DefaultApiRaw, Info, License, OperationProtocol, Tag}; +use thoth_api::redis::init_pool; use thoth_client::ThothClient; mod bibtex; @@ -114,6 +115,7 @@ pub async fn start_server( .wrap(Cors::default().allowed_methods(vec!["GET", "OPTIONS"])) .app_data(Data::new(ThothClient::new(gql_endpoint.clone()))) .app_data(Data::new(ApiConfig::new(public_url.clone()))) + .app_data(Data::new(init_pool())) .service(actix_web::web::resource("/").route(actix_web::web::get().to(index))) .wrap_api_with_spec(spec) .configure(format::route) From 3206aa8b7ac99c0dd31e77bd4cda38f2fc8dd58c Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 11:04:52 +0000 Subject: [PATCH 13/41] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f93618e..9b9c857e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Implement Redis connection pools using `deadpool-redis` + - Implement Redis caching in export API - Added `WorkLastUpdatedQuery` and `WorksLastUpdatedQuery` queries to thoth-client - Implement `Copy`, `Ord` and `PartialOrd` traits for `Timestamp` - Implement parsing from and to RFC 3339 strings for `Timestamp` From 2da19d224873d3d33ff94928ce8401b944c954b5 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 11:08:04 +0000 Subject: [PATCH 14/41] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9c857e..8b072356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `WorkLastUpdatedQuery` and `WorksLastUpdatedQuery` queries to thoth-client - Implement `Copy`, `Ord` and `PartialOrd` traits for `Timestamp` - Implement parsing from and to RFC 3339 strings for `Timestamp` + - Implement `Copy` trait for `WorkType`, `WorkStatus`, and `PublicationType` ## [[0.12.14]](https://github.com/thoth-pub/thoth/releases/tag/v0.12.14) - 2024-11-04 ### Changed From f40d6ed915cde4706ff42258c3b38e93796f9567 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 14 Nov 2024 11:28:52 +0000 Subject: [PATCH 15/41] Implement Copy for all enums --- CHANGELOG.md | 2 +- thoth-api/src/graphql/model.rs | 2 +- thoth-api/src/model/institution/mod.rs | 2 +- thoth-api/src/model/language/mod.rs | 4 ++-- thoth-api/src/model/location/mod.rs | 2 +- thoth-api/src/model/mod.rs | 4 ++-- thoth-api/src/model/price/mod.rs | 2 +- thoth-api/src/model/publication/mod.rs | 2 +- thoth-api/src/model/series/mod.rs | 2 +- thoth-api/src/model/subject/mod.rs | 1 + thoth-api/src/model/work/mod.rs | 4 ++-- thoth-app/src/component/institution.rs | 4 ++-- thoth-app/src/component/languages_form.rs | 8 ++++---- thoth-app/src/component/locations_form.rs | 6 +++--- thoth-app/src/component/new_chapter.rs | 2 +- thoth-app/src/component/new_institution.rs | 4 ++-- thoth-app/src/component/new_series.rs | 4 ++-- thoth-app/src/component/new_work.rs | 8 ++++---- thoth-app/src/component/prices_form.rs | 4 ++-- thoth-app/src/component/publication.rs | 4 ++-- thoth-app/src/component/publication_modal.rs | 6 +++--- thoth-app/src/component/publications_form.rs | 2 +- thoth-app/src/component/series.rs | 4 ++-- thoth-app/src/component/subjects_form.rs | 4 ++-- thoth-app/src/component/utils.rs | 2 +- thoth-app/src/component/work.rs | 16 ++++++++-------- 26 files changed, 53 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b072356..204101f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `WorkLastUpdatedQuery` and `WorksLastUpdatedQuery` queries to thoth-client - Implement `Copy`, `Ord` and `PartialOrd` traits for `Timestamp` - Implement parsing from and to RFC 3339 strings for `Timestamp` - - Implement `Copy` trait for `WorkType`, `WorkStatus`, and `PublicationType` + - Implement `Copy` trait for `WorkType`, `WorkStatus`, `PublicationType`, `CountryCode`, `LanguageRelation`, `LanguageCode`, `LocationPlatform`, `LengthUnit`, `WeightUnit`, `CurrencyCode`, and `SeriesType` ## [[0.12.14]](https://github.com/thoth-pub/thoth/releases/tag/v0.12.14) - 2024-11-04 ### Changed diff --git a/thoth-api/src/graphql/model.rs b/thoth-api/src/graphql/model.rs index 4214c2c4..cec8b994 100644 --- a/thoth-api/src/graphql/model.rs +++ b/thoth-api/src/graphql/model.rs @@ -1752,7 +1752,7 @@ impl MutationRoot { let mut data: PatchWork = child.clone().into(); data.publication_date = w.publication_date; data.withdrawn_date = w.withdrawn_date; - data.work_status = w.work_status.clone(); + data.work_status = w.work_status; child.update(&context.db, &data, &account_id)?; } } diff --git a/thoth-api/src/model/institution/mod.rs b/thoth-api/src/model/institution/mod.rs index c4c78c96..ad47910a 100644 --- a/thoth-api/src/model/institution/mod.rs +++ b/thoth-api/src/model/institution/mod.rs @@ -83,7 +83,7 @@ pub struct PatchInstitution { graphql(description = "Three-letter ISO 3166-1 code representing a country"), ExistingTypePath = "crate::schema::sql_types::CountryCode" )] -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum CountryCode { #[cfg_attr(feature = "backend", graphql(description = "Afghanistan"))] diff --git a/thoth-api/src/model/language/mod.rs b/thoth-api/src/model/language/mod.rs index cea64f7a..f8f4c562 100644 --- a/thoth-api/src/model/language/mod.rs +++ b/thoth-api/src/model/language/mod.rs @@ -17,7 +17,7 @@ use crate::schema::language_history; ), ExistingTypePath = "crate::schema::sql_types::LanguageRelation" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "title_case")] pub enum LanguageRelation { @@ -102,7 +102,7 @@ pub struct PatchLanguage { graphql(description = "Three-letter ISO 639 code representing a language"), ExistingTypePath = "crate::schema::sql_types::LanguageCode" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "UPPERCASE")] pub enum LanguageCode { diff --git a/thoth-api/src/model/location/mod.rs b/thoth-api/src/model/location/mod.rs index fba42a0a..ff2c8700 100644 --- a/thoth-api/src/model/location/mod.rs +++ b/thoth-api/src/model/location/mod.rs @@ -16,7 +16,7 @@ use crate::schema::location_history; graphql(description = "Platform where a publication is hosted or can be acquired"), ExistingTypePath = "crate::schema::sql_types::LocationPlatform" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum LocationPlatform { #[cfg_attr( diff --git a/thoth-api/src/model/mod.rs b/thoth-api/src/model/mod.rs index 8f5ff516..8618db1c 100644 --- a/thoth-api/src/model/mod.rs +++ b/thoth-api/src/model/mod.rs @@ -18,7 +18,7 @@ pub const ROR_DOMAIN: &str = "https://ror.org/"; derive(juniper::GraphQLEnum), graphql(description = "Unit of measurement for physical Work dimensions (mm, cm or in)") )] -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, PartialEq, Eq, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "lowercase")] pub enum LengthUnit { @@ -36,7 +36,7 @@ pub enum LengthUnit { derive(juniper::GraphQLEnum), graphql(description = "Unit of measurement for physical Work weight (grams or ounces)") )] -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, PartialEq, Eq, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "lowercase")] pub enum WeightUnit { diff --git a/thoth-api/src/model/price/mod.rs b/thoth-api/src/model/price/mod.rs index 9c152287..3ed58393 100644 --- a/thoth-api/src/model/price/mod.rs +++ b/thoth-api/src/model/price/mod.rs @@ -66,7 +66,7 @@ pub struct PatchPrice { graphql(description = "Three-letter ISO 4217 code representing a currency"), ExistingTypePath = "crate::schema::sql_types::CurrencyCode" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "UPPERCASE")] pub enum CurrencyCode { diff --git a/thoth-api/src/model/publication/mod.rs b/thoth-api/src/model/publication/mod.rs index 4c061a10..bace541c 100644 --- a/thoth-api/src/model/publication/mod.rs +++ b/thoth-api/src/model/publication/mod.rs @@ -21,7 +21,7 @@ use crate::schema::publication_history; graphql(description = "Format of a publication"), ExistingTypePath = "crate::schema::sql_types::PublicationType" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum PublicationType { #[cfg_attr( diff --git a/thoth-api/src/model/series/mod.rs b/thoth-api/src/model/series/mod.rs index ec1da46b..bd23a165 100644 --- a/thoth-api/src/model/series/mod.rs +++ b/thoth-api/src/model/series/mod.rs @@ -18,7 +18,7 @@ use crate::schema::series_history; graphql(description = "Type of a series"), ExistingTypePath = "crate::schema::sql_types::SeriesType" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "title_case")] pub enum SeriesType { diff --git a/thoth-api/src/model/subject/mod.rs b/thoth-api/src/model/subject/mod.rs index 16571b29..bef66516 100644 --- a/thoth-api/src/model/subject/mod.rs +++ b/thoth-api/src/model/subject/mod.rs @@ -21,6 +21,7 @@ use thoth_errors::ThothResult; )] #[derive( Debug, + Copy, Clone, Default, PartialEq, diff --git a/thoth-api/src/model/work/mod.rs b/thoth-api/src/model/work/mod.rs index 0f47fc00..0d414429 100644 --- a/thoth-api/src/model/work/mod.rs +++ b/thoth-api/src/model/work/mod.rs @@ -29,7 +29,7 @@ use crate::schema::work_history; graphql(description = "Type of a work"), ExistingTypePath = "crate::schema::sql_types::WorkType" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "title_case")] pub enum WorkType { @@ -80,7 +80,7 @@ pub enum WorkType { ), ExistingTypePath = "crate::schema::sql_types::WorkStatus" )] -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Deserialize, Serialize, EnumString, Display)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "title_case")] pub enum WorkStatus { diff --git a/thoth-app/src/component/institution.rs b/thoth-app/src/component/institution.rs index da58b356..b80e3a49 100644 --- a/thoth-app/src/component/institution.rs +++ b/thoth-app/src/component/institution.rs @@ -294,7 +294,7 @@ impl Component for InstitutionComponent { institution_name: self.institution.institution_name.clone(), institution_doi: self.institution.institution_doi.clone(), ror: self.institution.ror.clone(), - country_code: self.institution.country_code.clone(), + country_code: self.institution.country_code, }, ..Default::default() }; @@ -476,7 +476,7 @@ impl Component for InstitutionComponent { /> diff --git a/thoth-app/src/component/languages_form.rs b/thoth-app/src/component/languages_form.rs index 4f7363ba..d38e0004 100644 --- a/thoth-app/src/component/languages_form.rs +++ b/thoth-app/src/component/languages_form.rs @@ -194,8 +194,8 @@ impl Component for LanguagesFormComponent { let body = CreateLanguageRequestBody { variables: Variables { work_id: ctx.props().work_id, - language_relation: self.new_language.language_relation.clone(), - language_code: self.new_language.language_code.clone(), + language_relation: self.new_language.language_relation, + language_code: self.new_language.language_code, main_language: self.new_language.main_language, }, ..Default::default() @@ -304,7 +304,7 @@ impl Component for LanguagesFormComponent { > diff --git a/thoth-app/src/component/new_series.rs b/thoth-app/src/component/new_series.rs index 5ab0732c..1154a3d9 100644 --- a/thoth-app/src/component/new_series.rs +++ b/thoth-app/src/component/new_series.rs @@ -190,7 +190,7 @@ impl Component for NewSeriesComponent { Msg::CreateSeries => { let body = CreateSeriesRequestBody { variables: Variables { - series_type: self.series.series_type.clone(), + series_type: self.series.series_type, series_name: self.series.series_name.clone(), issn_print: self.series.issn_print.clone(), issn_digital: self.series.issn_digital.clone(), @@ -263,7 +263,7 @@ impl Component for NewSeriesComponent {
Some(Publication { publication_id: self.publication.publication_id, - publication_type: self.publication.publication_type.clone(), + publication_type: self.publication.publication_type, work_id: self.publication.work_id, isbn: self.publication.isbn.clone(), // Not used by child form @@ -299,7 +299,7 @@ impl Component for PublicationComponent { VNode { - if Some(c.name.clone()) == self.value { + if Some(c.name) == self.value { html! {