From 7ef1ec6c591d24dd44fc150bb9e1cecf92b912d1 Mon Sep 17 00:00:00 2001 From: trivernis Date: Mon, 28 Mar 2022 20:53:04 +0200 Subject: [PATCH 1/4] Fix tests with testdata Signed-off-by: trivernis --- tests/client/test_adding_files.rs | 15 ++++++++++----- tests/client/test_adding_tags.rs | 3 ++- tests/client/test_adding_urls.rs | 31 ++++++++---------------------- tests/{common.rs => common/mod.rs} | 12 ++++++++++++ tests/common/test_data.rs | 17 ++++++++++++++++ 5 files changed, 49 insertions(+), 29 deletions(-) rename tests/{common.rs => common/mod.rs} (72%) create mode 100644 tests/common/test_data.rs diff --git a/tests/client/test_adding_files.rs b/tests/client/test_adding_files.rs index 37b1a7e..285c038 100644 --- a/tests/client/test_adding_files.rs +++ b/tests/client/test_adding_files.rs @@ -1,4 +1,6 @@ -use super::super::common; +use crate::common; +use crate::common::create_testdata; +use crate::common::test_data::get_test_hashes; #[tokio::test] async fn it_adds_files() { @@ -20,23 +22,26 @@ async fn it_adds_binary_files() { #[tokio::test] async fn it_deletes_files() { let client = common::get_client(); - client.delete_files(vec![]).await.unwrap(); + client.delete_files(get_test_hashes()).await.unwrap(); } #[tokio::test] async fn it_undeletes_files() { let client = common::get_client(); - client.undelete_files(vec![]).await.unwrap(); + create_testdata(&client).await; + client.undelete_files(get_test_hashes()).await.unwrap(); } #[tokio::test] async fn it_archives_files() { let client = common::get_client(); - client.archive_files(vec![]).await.unwrap(); + create_testdata(&client).await; + client.archive_files(get_test_hashes()).await.unwrap(); } #[tokio::test] async fn it_unarchives_files() { let client = common::get_client(); - client.unarchive_files(vec![]).await.unwrap(); + create_testdata(&client).await; + client.unarchive_files(get_test_hashes()).await.unwrap(); } diff --git a/tests/client/test_adding_tags.rs b/tests/client/test_adding_tags.rs index c2797f8..3b34952 100644 --- a/tests/client/test_adding_tags.rs +++ b/tests/client/test_adding_tags.rs @@ -1,4 +1,5 @@ use super::super::common; +use crate::common::test_data::EMPTY_HASH; use hydrus_api::api_core::adding_tags::{AddTagsRequestBuilder, TagAction}; use hydrus_api::api_core::common::ServiceIdentifier; @@ -22,7 +23,7 @@ async fn it_adds_tags() { #![allow(deprecated)] let client = common::get_client(); let request = AddTagsRequestBuilder::default() - .add_hash("0000000000000000000000000000000000000000000000000000000000000000") // valid hash, I hope no files are affected + .add_hash(EMPTY_HASH) // valid hash, I hope no files are affected .add_tags( ServiceIdentifier::name("my tags"), vec!["beach".into(), "summer".into()], diff --git a/tests/client/test_adding_urls.rs b/tests/client/test_adding_urls.rs index ef0939a..4e7a629 100644 --- a/tests/client/test_adding_urls.rs +++ b/tests/client/test_adding_urls.rs @@ -1,14 +1,12 @@ use super::super::common; +use crate::common::test_data::{get_test_hashes, get_test_urls, TEST_URL_1}; use hydrus_api::api_core::adding_urls::{AddUrlRequestBuilder, URL_TYPE_POST}; use hydrus_api::api_core::common::ServiceIdentifier; #[tokio::test] async fn it_returns_files_for_an_url() { let client = common::get_client(); - let response = client - .get_url_files("https://www.pixiv.net/member_illust.php?illust_id=83406361&mode=medium") - .await - .unwrap(); + let response = client.get_url_files(TEST_URL_1).await.unwrap(); assert!(response.normalised_url.len() > 0); } @@ -16,10 +14,7 @@ async fn it_returns_files_for_an_url() { #[tokio::test] async fn it_returns_url_information() { let client = common::get_client(); - let info = client - .get_url_info("https://www.pixiv.net/member_illust.php?illust_id=83406361&mode=medium") - .await - .unwrap(); + let info = client.get_url_info(TEST_URL_1).await.unwrap(); assert!(info.normalised_url.len() > 0); assert_eq!(info.url_type, URL_TYPE_POST); } @@ -29,7 +24,7 @@ async fn it_adds_urls() { #![allow(deprecated)] let client = common::get_client(); let request = AddUrlRequestBuilder::default() - .url("https://www.pixiv.net/member_illust.php?illust_id=83406361&mode=medium") + .url(TEST_URL_1) .add_tags( ServiceIdentifier::name("my tags"), vec!["ark mage".to_string(), "grinning".to_string()], @@ -44,14 +39,9 @@ async fn it_adds_urls() { #[tokio::test] async fn it_associates_urls() { let client = common::get_client(); + common::create_testdata(&client).await; client - .associate_urls( - vec![ - "https://www.pixiv.net/member_illust.php?illust_id=83406361&mode=medium" - .to_string(), - ], - vec!["0000000000000000000000000000000000000000000000000000000000000000".to_string()], - ) + .associate_urls(get_test_urls(), get_test_hashes()) .await .unwrap(); } @@ -59,14 +49,9 @@ async fn it_associates_urls() { #[tokio::test] async fn it_disassociates_urls() { let client = common::get_client(); + common::create_testdata(&client).await; client - .disassociate_urls( - vec![ - "https://www.pixiv.net/member_illust.php?illust_id=83406361&mode=medium" - .to_string(), - ], - vec!["0000000000000000000000000000000000000000000000000000000000000000".to_string()], - ) + .disassociate_urls(get_test_urls(), get_test_hashes()) .await .unwrap(); } diff --git a/tests/common.rs b/tests/common/mod.rs similarity index 72% rename from tests/common.rs rename to tests/common/mod.rs index 996e6b5..cac4110 100644 --- a/tests/common.rs +++ b/tests/common/mod.rs @@ -1,8 +1,11 @@ +use hydrus_api::api_core::adding_urls::AddUrlRequestBuilder; use hydrus_api::api_core::client::Client; use hydrus_api::Hydrus; use std::env; use std::sync::{Arc, Mutex, MutexGuard}; use std::time::Duration; +use test_data::TEST_URLS; +pub mod test_data; pub fn setup() { lazy_static::lazy_static! { static ref SETUP_DONE: Arc> = Arc::new(Mutex::new(false)); } @@ -30,3 +33,12 @@ pub fn get_hydrus() -> Hydrus { Hydrus::new(client) } + +pub async fn create_testdata(client: &Client) { + for url in TEST_URLS { + client + .add_url(AddUrlRequestBuilder::default().url(url).build()) + .await + .unwrap(); + } +} diff --git a/tests/common/test_data.rs b/tests/common/test_data.rs new file mode 100644 index 0000000..5e9745f --- /dev/null +++ b/tests/common/test_data.rs @@ -0,0 +1,17 @@ +pub const TEST_HASH_1: &str = "277a138cd1ee79fc1fdb2869c321b848d4861e45b82184487139ef66dd40b62d"; +pub const TEST_HASH_2: &str = "9641a590e66d9f2e5137b6bcba07fdf6cec3ffaa54de2565c3afcc2125ad1160"; +pub const EMPTY_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000"; +pub const TEST_HASHES: &[&str] = &[TEST_HASH_1, TEST_HASH_2]; + +pub const TEST_URL_1: &str = + "https://www.pixiv.net/member_illust.php?illust_id=83406361&mode=medium"; +pub const TEST_URL_2: &str = "https://yande.re/post/show/923576"; +pub const TEST_URLS: &[&str] = &[TEST_URL_1, TEST_URL_2]; + +pub fn get_test_hashes() -> Vec { + TEST_HASHES.iter().map(|h| String::from(*h)).collect() +} + +pub fn get_test_urls() -> Vec { + TEST_URLS.iter().map(|u| String::from(*u)).collect() +} From a0eea94340377271352f4f2f4109e3286e8e5e2d Mon Sep 17 00:00:00 2001 From: trivernis Date: Mon, 28 Mar 2022 21:14:56 +0200 Subject: [PATCH 2/4] Add low level notes api Signed-off-by: trivernis --- Cargo.toml | 6 +-- src/api_core/adding_notes.rs | 73 +++++++++++++++++++++++++++++ src/api_core/client.rs | 24 ++++++++++ src/api_core/common.rs | 16 +++++++ src/api_core/mod.rs | 1 + tests/client/mod.rs | 1 + tests/client/test_deleting_notes.rs | 27 +++++++++++ 7 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 src/api_core/adding_notes.rs create mode 100644 tests/client/test_deleting_notes.rs diff --git a/Cargo.toml b/Cargo.toml index 20c4412..4ea2c91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,11 @@ repository = "https://github.com/trivernis/hydrus-api-rs" [dependencies] serde = { version = "1.0.136", features = ["derive"] } -reqwest = { version = "0.11.9", features = ["json"] } -tracing = "0.1.31" +reqwest = { version = "0.11.10", features = ["json"] } +tracing = "0.1.32" mime = "0.3.16" chrono = "0.4.19" -regex = "1.5.4" +regex = "1.5.5" lazy_static = "1.4.0" [dev-dependencies] diff --git a/src/api_core/adding_notes.rs b/src/api_core/adding_notes.rs new file mode 100644 index 0000000..794d958 --- /dev/null +++ b/src/api_core/adding_notes.rs @@ -0,0 +1,73 @@ +use crate::api_core::common::FileIdentifier; +use crate::api_core::Endpoint; +use std::collections::HashMap; + +pub struct SetNotes; + +impl Endpoint for SetNotes { + type Request = SetNotesRequest; + type Response = (); + + fn path() -> String { + String::from("add_notes/set_notes") + } +} + +#[derive(Serialize, Clone, Debug, Default)] +pub struct SetNotesRequest { + notes: HashMap, + #[serde(skip_serializing_if = "Option::is_none")] + hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + file_id: Option, +} + +impl SetNotesRequest { + pub fn new(id: FileIdentifier, notes: HashMap) -> Self { + let mut request = Self { + notes, + ..Default::default() + }; + match id { + FileIdentifier::ID(id) => request.file_id = Some(id), + FileIdentifier::Hash(hash) => request.hash = Some(hash), + } + + request + } +} + +pub struct DeleteNotes; + +impl Endpoint for DeleteNotes { + type Request = DeleteNotesRequest; + type Response = (); + + fn path() -> String { + String::from("add_notes/delete_notes") + } +} + +#[derive(Serialize, Clone, Debug, Default)] +pub struct DeleteNotesRequest { + note_names: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + file_id: Option, +} + +impl DeleteNotesRequest { + pub fn new(id: FileIdentifier, note_names: Vec) -> Self { + let mut request = Self { + note_names, + ..Default::default() + }; + match id { + FileIdentifier::ID(id) => request.file_id = Some(id), + FileIdentifier::Hash(hash) => request.hash = Some(hash), + } + + request + } +} diff --git a/src/api_core/client.rs b/src/api_core/client.rs index 5c65472..ec4568b 100644 --- a/src/api_core/client.rs +++ b/src/api_core/client.rs @@ -6,6 +6,7 @@ use crate::api_core::adding_files::{ AddFile, AddFileRequest, AddFileResponse, ArchiveFiles, ArchiveFilesRequest, DeleteFiles, DeleteFilesRequest, UnarchiveFiles, UnarchiveFilesRequest, UndeleteFiles, UndeleteFilesRequest, }; +use crate::api_core::adding_notes::{DeleteNotes, DeleteNotesRequest, SetNotes, SetNotesRequest}; use crate::api_core::adding_tags::{AddTags, AddTagsRequest, CleanTags, CleanTagsResponse}; use crate::api_core::adding_urls::{ AddUrl, AddUrlRequest, AddUrlResponse, AssociateUrl, AssociateUrlRequest, GetUrlFiles, @@ -33,6 +34,7 @@ use crate::utils::{ use reqwest::Response; use serde::de::DeserializeOwned; use serde::Serialize; +use std::collections::HashMap; use std::fmt::Debug; static ACCESS_KEY_HEADER: &str = "Hydrus-Client-API-Access-Key"; @@ -375,6 +377,28 @@ impl Client { Ok(()) } + /// Sets the notes for the file + #[tracing::instrument(skip(self), level = "debug")] + pub async fn set_notes( + &self, + id: FileIdentifier, + notes: HashMap, + ) -> Result<()> { + self.post::(SetNotesRequest::new(id, notes)) + .await?; + + Ok(()) + } + + /// Deletes the notes of a file + #[tracing::instrument(skip(self), level = "debug")] + pub async fn delete_notes(&self, id: FileIdentifier, note_names: Vec) -> Result<()> { + self.post::(DeleteNotesRequest::new(id, note_names)) + .await?; + + Ok(()) + } + /// Returns all pages of the client #[tracing::instrument(skip(self), level = "debug")] pub async fn get_pages(&self) -> Result { diff --git a/src/api_core/common.rs b/src/api_core/common.rs index 3af1702..0d8937d 100644 --- a/src/api_core/common.rs +++ b/src/api_core/common.rs @@ -80,6 +80,22 @@ impl FileIdentifier { pub fn hash(hash: S) -> Self { Self::Hash(hash.to_string()) } + + pub fn as_hash(&self) -> Option<&String> { + if let Self::Hash(h) = &self { + Some(h) + } else { + None + } + } + + pub fn as_id(&self) -> Option { + if let Self::ID(id) = &self { + Some(*id) + } else { + None + } + } } #[derive(Clone)] diff --git a/src/api_core/mod.rs b/src/api_core/mod.rs index bf5b68c..b07f339 100644 --- a/src/api_core/mod.rs +++ b/src/api_core/mod.rs @@ -12,6 +12,7 @@ pub mod common; pub mod managing_cookies_and_http_headers; pub mod managing_pages; pub mod searching_and_fetching_files; +pub mod adding_notes; pub use searching_and_fetching_files::file_sort_type; diff --git a/tests/client/mod.rs b/tests/client/mod.rs index 8c61b9d..0ca13cf 100644 --- a/tests/client/mod.rs +++ b/tests/client/mod.rs @@ -5,3 +5,4 @@ mod test_adding_urls; mod test_managing_cookies_and_http_headers; mod test_managing_pages; mod test_searching_and_fetching_files; +mod test_deleting_notes; diff --git a/tests/client/test_deleting_notes.rs b/tests/client/test_deleting_notes.rs new file mode 100644 index 0000000..7cefad9 --- /dev/null +++ b/tests/client/test_deleting_notes.rs @@ -0,0 +1,27 @@ +use super::super::common; +use crate::common::test_data::TEST_HASH_1; +use hydrus_api::api_core::common::FileIdentifier; +use std::collections::HashMap; + +#[tokio::test] +async fn it_sets_notes() { + let client = common::get_client(); + common::create_testdata(&client).await; + let mut test_notes = HashMap::new(); + test_notes.insert("test".to_string(), "value".to_string()); + test_notes.insert("test2".to_string(), "value".to_string()); + + client + .set_notes(FileIdentifier::hash(TEST_HASH_1), test_notes) + .await + .unwrap(); +} + +#[tokio::test] +async fn it_deletes_notes() { + let client = common::get_client(); + client + .delete_notes(FileIdentifier::hash(TEST_HASH_1), vec!["test".to_string()]) + .await + .unwrap(); +} From d7a047ea90164becd4221af02ae6e90d1d59d46d Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 29 Mar 2022 19:41:25 +0200 Subject: [PATCH 3/4] Add support for notes api Signed-off-by: trivernis --- Cargo.toml | 2 +- src/wrapper/builders/mod.rs | 1 + src/wrapper/builders/notes_builder.rs | 47 +++++++++++++++++++++++++++ src/wrapper/hydrus_file.rs | 22 +++++++++++++ tests/wrapper/test_files.rs | 17 ++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/wrapper/builders/notes_builder.rs diff --git a/Cargo.toml b/Cargo.toml index 4ea2c91..a638bf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydrus-api" -version = "0.7.0" +version = "0.7.1" authors = ["trivernis "] edition = "2018" license = "Apache-2.0" diff --git a/src/wrapper/builders/mod.rs b/src/wrapper/builders/mod.rs index 5e65902..a58dde9 100644 --- a/src/wrapper/builders/mod.rs +++ b/src/wrapper/builders/mod.rs @@ -3,3 +3,4 @@ pub mod or_chain_builder; pub mod search_builder; pub mod tag_builder; pub mod tagging_builder; +pub mod notes_builder; diff --git a/src/wrapper/builders/notes_builder.rs b/src/wrapper/builders/notes_builder.rs new file mode 100644 index 0000000..72511ea --- /dev/null +++ b/src/wrapper/builders/notes_builder.rs @@ -0,0 +1,47 @@ +use crate::api_core::common::FileIdentifier; +use crate::error::Result; +use crate::Client; +use std::collections::HashMap; + +/// Builder to create a request for adding notes to a given file +pub struct AddNotesBuilder { + client: Client, + file: FileIdentifier, + notes: HashMap, +} + +impl AddNotesBuilder { + /// Creates a new notes builder for the given file id + pub fn new(client: Client, file: FileIdentifier) -> Self { + Self { + client, + file, + notes: HashMap::new(), + } + } + + /// Adds a single note + pub fn add_note(mut self, name: S1, note: S2) -> Self { + self.notes.insert(name.to_string(), note.to_string()); + + self + } + + /// Adds multiple notes to the builder + pub fn add_notes, S1: ToString, S2: ToString>( + mut self, + notes: I, + ) -> Self { + let notes_iter = notes + .into_iter() + .map(|(k, v): (S1, S2)| (k.to_string(), v.to_string())); + self.notes.extend(notes_iter); + + self + } + + /// Adds all notes mentioned in the builder to the given file + pub async fn run(self) -> Result<()> { + self.client.set_notes(self.file, self.notes).await + } +} diff --git a/src/wrapper/hydrus_file.rs b/src/wrapper/hydrus_file.rs index 60c5cb8..70e2b4c 100644 --- a/src/wrapper/hydrus_file.rs +++ b/src/wrapper/hydrus_file.rs @@ -2,6 +2,7 @@ use crate::api_core::adding_tags::{AddTagsRequestBuilder, TagAction}; use crate::api_core::common::{FileIdentifier, FileMetadataInfo, FileRecord, ServiceIdentifier}; use crate::error::{Error, Result}; use crate::utils::tag_list_to_string_list; +use crate::wrapper::builders::notes_builder::AddNotesBuilder; use crate::wrapper::service::ServiceName; use crate::wrapper::tag::Tag; use crate::Client; @@ -318,6 +319,27 @@ impl HydrusFile { self.client.add_tags(reqwest.build()).await } + /// Creates a builder to add notes to the file + pub fn add_notes(&self) -> AddNotesBuilder { + AddNotesBuilder::new(self.client.clone(), self.id.clone()) + } + + /// Deletes a single note from the file + pub async fn delete_note(&self, name: S1) -> Result<()> { + self.client + .delete_notes(self.id.clone(), vec![name.to_string()]) + .await + } + + /// Deletes multiple notes from the file + pub async fn delete_notes, S: ToString>( + &self, + names: I, + ) -> Result<()> { + let names = names.into_iter().map(|n: S| n.to_string()).collect(); + self.client.delete_notes(self.id.clone(), names).await + } + /// Retrieves the file record bytes pub async fn retrieve(&self) -> Result { self.client.get_file(self.id.clone()).await diff --git a/tests/wrapper/test_files.rs b/tests/wrapper/test_files.rs index f464526..536ff4a 100644 --- a/tests/wrapper/test_files.rs +++ b/tests/wrapper/test_files.rs @@ -73,6 +73,23 @@ async fn it_modifies_tags() { .unwrap(); } +#[tokio::test] +async fn it_adds_notes() { + let file = get_file().await; + file.add_notes() + .add_note("My Note", "My notes content") + .add_notes(vec![("My note 2", "More content")]) + .run() + .await + .unwrap(); +} + +#[tokio::test] +async fn it_deletes_notes() { + let file = get_file().await; + file.delete_note("My Note").await.unwrap(); +} + #[tokio::test] async fn it_retrieves_content() { let file = get_file().await; From 59abaa2cec514a07b119a43e31863963e94abe18 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 29 Mar 2022 19:43:30 +0200 Subject: [PATCH 4/4] Add publish workflow Signed-off-by: trivernis --- .github/workflows/public.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/public.yml diff --git a/.github/workflows/public.yml b/.github/workflows/public.yml new file mode 100644 index 0000000..53c5e7f --- /dev/null +++ b/.github/workflows/public.yml @@ -0,0 +1,24 @@ + +name: Publish a release + +on: + push: + branches: [ main ] +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Login + env: + CRATES_IO_TOKEN: ${{secrets.CRATES_IO_TOKEN}} + run: cargo login "$CRATES_IO_TOKEN" + + - name: Publish to crates.io + run: cargo publish --all-features \ No newline at end of file