#![warn(missing_docs, non_ascii_idents, trivial_numeric_casts,
unused_crate_dependencies, noop_method_call, single_use_lifetimes, trivial_casts,
unused_lifetimes, nonstandard_style, variant_size_differences)]
#![deny(keyword_idents)]
#![allow(clippy::needless_return)]
mod types;
mod connection;
mod modules;
use std::sync::Arc;
use modules::file::File;
use modules::help::Help;
use modules::alert::Alert;
use modules::bundle::Bundle;
use modules::error::Error;
use modules::search::Search;
use modules::ingest::Ingest;
use modules::submit::Submit;
pub use types::{Authentication, JsonMap};
pub use connection::Connection;
pub struct Client {
pub alert: Alert,
pub bundle: Bundle,
pub error: Error,
pub file: File,
pub help: Help,
pub ingest: Ingest,
pub search: Search,
pub submit: Submit,
}
impl Client {
pub async fn connect(server: String, auth: Authentication) -> Result<Self, types::Error> {
let connection = Arc::new(Connection::connect(server, auth, None, true, Default::default(), None, None).await?);
Ok(Self {
alert: Alert::new(connection.clone()),
bundle: Bundle::new(connection.clone()),
error: Error::new(connection.clone()),
file: File::new(connection.clone()),
help: Help::new(connection.clone()),
ingest: Ingest::new(connection.clone()),
search: Search::new(connection.clone()),
submit: Submit::new(connection)
})
}
pub async fn from_connection(connection: Arc<Connection>) -> Result<Self, types::Error> {
Ok(Self {
alert: Alert::new(connection.clone()),
bundle: Bundle::new(connection.clone()),
error: Error::new(connection.clone()),
file: File::new(connection.clone()),
help: Help::new(connection.clone()),
ingest: Ingest::new(connection.clone()),
search: Search::new(connection.clone()),
submit: Submit::new(connection)
})
}
}
#[cfg(test)]
mod tests {
use assemblyline_models::datastore::submission::{SubmissionParams, SubmissionState, ServiceSelection};
use rand::{thread_rng, Rng};
use crate::{Authentication, Client};
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
pub (crate) async fn prepare_client() -> Client {
init();
let url = std::env::var("ASSEMBLYLINE_URL").unwrap();
let username = std::env::var("ASSEMBLYLINE_USER").unwrap();
let key = std::env::var("ASSEMBLYLINE_KEY").unwrap();
Client::connect(url, Authentication::ApiKey { username, key }).await.unwrap()
}
fn random_body() -> Vec<u8> {
let mut out = vec![];
let mut prng = thread_rng();
let length = 128 + prng.gen_range(0..256);
while out.len() < length {
out.push(prng.gen());
}
out
}
#[tokio::test]
async fn submit_content() {
let client = prepare_client().await;
let result = client.submit.single()
.metadata_item("testbatch".to_owned(), "0".to_owned())
.params(SubmissionParams{ ttl: 1, ..Default::default()})
.fname("test-file".to_owned())
.content(random_body()).await.unwrap();
assert_eq!(result.state, SubmissionState::Submitted);
}
#[tokio::test]
async fn search_single_page() {
let client = prepare_client().await;
let batch: u64 = thread_rng().gen();
let batch: String = batch.to_string();
let _result = client.ingest.single()
.metadata_item("testbatch".to_owned(), batch.clone())
.notification_queue(batch.clone())
.params(SubmissionParams{ priority: 300, ttl: 1, services: ServiceSelection{ selected: Some(vec!["Characterize".to_owned()]), ..Default::default()}, ..Default::default()})
.fname("test-file".to_owned())
.content(random_body()).await.unwrap();
for _ in 0..100 {
match client.ingest.get_message(&batch).await.unwrap() {
Some(message) => {
assert_eq!(message.submission.metadata.get("testbatch").unwrap(), &batch);
break;
},
None => { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; },
}
}
for _ in 0..10 {
let result = client.search.submission(format!("metadata.testbatch: {batch}"))
.search().await.unwrap();
if result.items.len() == 1 {
return
}
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
panic!()
}
}