#![allow(clippy::type_complexity)]
use std::{future::Future, ops::Deref, pin::Pin};
use async_stream::stream;
use bytes::Bytes;
use freedom_config::Config;
use freedom_models::{
account::Account,
band::Band,
pagination::Paginated,
satellite::Satellite,
satellite_configuration::SatelliteConfiguration,
site::{Site, SiteConfiguration},
task::{Task, TaskRequest, TaskStatusType, TaskType},
user::User,
utils::Embedded,
};
use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use serde_json::Value as JsonValue;
use time::{OffsetDateTime, format_description::well_known::Iso8601};
use url::Url;
use futures_core::Stream;
use crate::error::Error;
pub(crate) mod post;
pub trait Value: std::fmt::Debug + DeserializeOwned + Clone + Send + Sync {}
impl<T> Value for T where T: std::fmt::Debug + DeserializeOwned + Clone + Send + Sync {}
trait PaginatedErr<'a, T> {
fn once_err(self) -> PaginatedStream<'a, T>;
}
impl<'a, T: 'a + Send + Sync> PaginatedErr<'a, T> for Error {
fn once_err(self) -> PaginatedStream<'a, T> {
Box::pin(async_stream::stream! { yield Err(self); })
}
}
pub trait Container<T>: Deref<Target = T> + Value {
fn into_inner(self) -> T;
}
impl<T: Deref<Target = T> + Value> Container<T> for Box<T> {
fn into_inner(self) -> T {
*self
}
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[repr(transparent)]
#[serde(transparent)]
pub struct Inner<T>(T);
impl<T> std::ops::Deref for Inner<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for Inner<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> Container<T> for Inner<T>
where
T: Value,
{
fn into_inner(self) -> T {
self.0
}
}
impl<T> Inner<T> {
pub fn new(inner: T) -> Self {
Self(inner)
}
}
pub type PaginatedStream<'a, T> = Pin<Box<dyn Stream<Item = Result<T, Error>> + 'a + Send + Sync>>;
pub trait Api: Send + Sync {
type Container<T: Value>: Container<T>;
fn config(&self) -> &Config;
fn config_mut(&mut self) -> &mut Config;
fn get(
&self,
url: Url,
) -> impl Future<Output = Result<(Bytes, StatusCode), Error>> + Send + Sync;
fn delete(
&self,
url: Url,
) -> impl Future<Output = Result<(Bytes, StatusCode), Error>> + Send + Sync;
fn post<S>(
&self,
url: Url,
msg: S,
) -> impl Future<Output = Result<(Bytes, StatusCode), Error>> + Send + Sync
where
S: serde::Serialize + Send + Sync;
fn get_json_map<T>(&self, url: Url) -> impl Future<Output = Result<T, Error>> + Send + Sync
where
T: Value,
{
async move {
let (body, status) = self.get(url).await?;
error_on_non_success(&status, &body)?;
let utf8_str = String::from_utf8_lossy(&body);
serde_json::from_str(&utf8_str).map_err(From::from)
}
}
fn get_paginated<T>(&self, head_url: Url) -> PaginatedStream<'_, Self::Container<T>>
where
T: 'static + Value + Send + Sync,
{
let base = self.config().environment().freedom_entrypoint();
let mut current_url = head_url; Box::pin(stream! {
loop {
let pag = self.get_json_map::<Paginated<JsonValue>>(current_url).await?;
for item in pag.items {
let i = serde_json::from_value::<Self::Container<T>>(item).map_err(From::from);
yield i;
}
if let Some(link) = pag.links.get("next") {
current_url = match link.has_host() {
true => link.to_owned(),
false => {
base.clone()
.join(link.as_str())
.map_err(|e| crate::error::Error::pag_item(e.to_string()))?
}
};
} else {
break;
}
}
})
}
fn path_to_url(&self, path: impl AsRef<str>) -> Result<Url, Error> {
let url = self.config().environment().freedom_entrypoint();
url.join(path.as_ref())
.map_err(|error| Error::InvalidUri(error.to_string()))
}
fn delete_band_details(
&self,
id: i32,
) -> impl Future<Output = Result<(), Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("satellite_bands/{id}"))?;
let (body, status) = self.delete(uri).await?;
error_on_non_success(&status, &body)?;
Ok(())
}
}
fn delete_satellite_configuration(
&self,
id: i32,
) -> impl Future<Output = Result<(), Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("satellite_configurations/{id}"))?;
let (body, status) = self.delete(uri).await?;
error_on_non_success(&status, &body)?;
Ok(())
}
}
fn delete_satellite(&self, id: i32) -> impl Future<Output = Result<(), Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("satellites/{id}"))?;
let (body, status) = self.delete(uri).await?;
error_on_non_success(&status, &body)?;
Ok(())
}
}
fn delete_override(&self, id: i32) -> impl Future<Output = Result<(), Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("overrides/{id}"))?;
let (body, status) = self.delete(uri).await?;
error_on_non_success(&status, &body)?;
Ok(())
}
}
fn delete_user(&self, id: i32) -> impl Future<Output = Result<(), Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("users/{id}"))?;
let (body, status) = self.delete(uri).await?;
error_on_non_success(&status, &body)?;
Ok(())
}
}
fn delete_task_request(
&self,
id: i32,
) -> impl Future<Output = Result<(), Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("requests/{id}"))?;
let (body, status) = self.delete(uri).await?;
error_on_non_success(&status, &body)?;
Ok(())
}
}
fn post_json_map<S, T>(
&self,
url: Url,
msg: S,
) -> impl Future<Output = Result<T, Error>> + Send + Sync
where
S: serde::Serialize + Send + Sync,
T: Value,
{
async move {
let (body, status) = self.post(url, msg).await?;
error_on_non_success(&status, &body)?;
let utf8_str = str::from_utf8(&body).map_err(|error| {
Error::Deserialization(format!("Failed to decode response as UTF-8: {error}"))
})?;
serde_json::from_str(utf8_str).map_err(From::from)
}
}
fn get_account_by_name(
&self,
account_name: &str,
) -> impl Future<Output = Result<Self::Container<Account>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("accounts/search/findOneByName")?;
uri.set_query(Some(&format!("name={account_name}")));
self.get_json_map(uri).await
}
}
fn get_file_by_task_id_and_name(
&self,
task_id: i32,
file_name: &str,
) -> impl Future<Output = Result<Bytes, Error>> + Send + Sync {
async move {
let path = format!("downloads/{}/{}", task_id, file_name);
let uri = self.path_to_url(path)?;
let (data, status) = self.get(uri).await?;
error_on_non_success(&status, b"Failed to fetch file")?;
Ok(data)
}
}
fn get_account_by_id(
&self,
account_id: i32,
) -> impl Future<Output = Result<Self::Container<Account>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("accounts/{account_id}"))?;
self.get_json_map(uri).await
}
}
fn get_accounts(&self) -> PaginatedStream<'_, Self::Container<Account>> {
let uri = match self.path_to_url("accounts") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_satellite_bands(&self) -> PaginatedStream<'_, Self::Container<Band>> {
let uri = match self.path_to_url("satellite_bands") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_satellite_band_by_id(
&self,
satellite_band_id: i32,
) -> impl Future<Output = Result<Self::Container<Band>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("satellite_bands/{satellite_band_id}"))?;
self.get_json_map(uri).await
}
}
fn get_satellite_band_by_name(
&self,
satellite_band_name: &str,
) -> impl Future<Output = Result<Self::Container<Band>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("satellite_bands/search/findOneByName")?;
uri.set_query(Some(&format!("name={satellite_band_name}")));
self.get_json_map(uri).await
}
}
fn get_satellite_bands_by_account_name(
&self,
account_name: &str,
) -> PaginatedStream<'_, Self::Container<Band>> {
let mut uri = match self.path_to_url("satellite_bands/search/findAllByAccountName") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!("accountName={account_name}")));
self.get_paginated(uri)
}
fn get_satellite_configurations_by_account_name(
&self,
account_name: &str,
) -> PaginatedStream<'_, Self::Container<SatelliteConfiguration>> {
let mut uri = match self.path_to_url("satellite_configurations/search/findAllByAccountName")
{
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!("accountName={account_name}")));
self.get_paginated(uri)
}
fn get_satellite_configurations(
&self,
) -> PaginatedStream<'_, Self::Container<SatelliteConfiguration>> {
let uri = match self.path_to_url("satellite_configurations") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_satellite_configuration_by_id(
&self,
satellite_configuration_id: i32,
) -> impl Future<Output = Result<Self::Container<SatelliteConfiguration>, Error>> + Send + Sync
{
async move {
let uri = self.path_to_url(format!(
"satellite_configurations/{satellite_configuration_id}"
))?;
self.get_json_map(uri).await
}
}
fn get_satellite_configuration_by_name(
&self,
satellite_configuration_name: &str,
) -> impl Future<Output = Result<Self::Container<SatelliteConfiguration>, Error>> + Send + Sync
{
async move {
let mut uri = self.path_to_url("satellite_configurations/search/findOneByName")?;
uri.set_query(Some(&format!("name={satellite_configuration_name}")));
self.get_json_map(uri).await
}
}
fn get_sites(&self) -> PaginatedStream<'_, Self::Container<Site>> {
let uri = match self.path_to_url("sites") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_site_by_id(
&self,
id: i32,
) -> impl Future<Output = Result<Self::Container<Site>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("sites/{id}"))?;
self.get_json_map(uri).await
}
}
fn get_site_by_name(
&self,
name: impl AsRef<str> + Send + Sync,
) -> impl Future<Output = Result<Self::Container<Site>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("sites/search/findOneByName")?;
let query = format!("name={}", name.as_ref());
uri.set_query(Some(&query));
self.get_json_map(uri).await
}
}
fn get_site_configurations(&self) -> PaginatedStream<'_, Self::Container<SiteConfiguration>> {
let uri = match self.path_to_url("configurations") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_site_configuration_by_id(
&self,
id: i32,
) -> impl Future<Output = Result<Self::Container<SiteConfiguration>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("configurations/{id}"))?;
self.get_json_map(uri).await
}
}
fn get_site_configuration_by_name(
&self,
name: impl AsRef<str> + Send + Sync,
) -> impl Future<Output = Result<Self::Container<SiteConfiguration>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("configurations/search/findOneByName")?;
let query = format!("name={}", name.as_ref());
uri.set_query(Some(&query));
self.get_json_map(uri).await
}
}
fn get_request_by_id(
&self,
task_request_id: i32,
) -> impl Future<Output = Result<Self::Container<TaskRequest>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("requests/{task_request_id}"))?;
self.get_json_map(uri).await
}
}
fn get_requests(&self) -> PaginatedStream<'_, Self::Container<TaskRequest>> {
{
let uri = match self.path_to_url("requests/search/findAll") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
}
fn get_requests_by_target_date_between(
&self,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("requests/search/findAllByTargetDateBetween")?;
uri.set_query(Some(&format!(
"start={}&end={}",
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?,
)));
self.get_json_map(uri).await
}
}
fn get_requests_by_account_and_target_date_between<T>(
&self,
account_uri: T,
start: OffsetDateTime,
end: OffsetDateTime,
) -> PaginatedStream<'_, Self::Container<TaskRequest>>
where
T: AsRef<str> + Send + Sync,
{
let start = match start.format(&Iso8601::DEFAULT).map_err(Error::from) {
Ok(start) => start,
Err(error) => return error.once_err(),
};
let end = match end.format(&Iso8601::DEFAULT).map_err(Error::from) {
Ok(end) => end,
Err(error) => return error.once_err(),
};
let mut uri = match self.path_to_url("requests/search/findAllByAccountAndTargetDateBetween")
{
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!(
"account={}&start={}&end={}",
account_uri.as_ref(),
start,
end
)));
self.get_paginated(uri)
}
fn get_requests_by_account_and_upcoming_today(
&self,
) -> PaginatedStream<'_, Self::Container<TaskRequest>> {
let uri = match self.path_to_url("requests/search/findByAccountUpcomingToday") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_requests_by_configuration<T>(
&self,
configuration_uri: T,
) -> PaginatedStream<'_, Self::Container<TaskRequest>>
where
T: AsRef<str> + Send + Sync,
{
let mut uri =
match self.path_to_url("requests/search/findAllByConfigurationOrderByCreatedAsc") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!(
"configuration={}",
configuration_uri.as_ref()
)));
self.get_paginated::<TaskRequest>(uri)
}
fn get_requests_by_configuration_and_satellite_names_and_target_date_between<T, I, S>(
&self,
configuration_uri: T,
satellites: I,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync
where
T: AsRef<str> + Send + Sync,
I: IntoIterator<Item = S> + Send + Sync,
S: AsRef<str> + Send + Sync,
{
async move {
let satellites_string = crate::utils::list_to_string(satellites);
let mut uri = self.path_to_url(
"requests/search/findAllByConfigurationAndSatelliteNamesAndTargetDateBetween",
)?;
uri.set_query(Some(&format!(
"configuration={}&satelliteNames={}&start={}&end={}",
configuration_uri.as_ref(),
satellites_string,
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?,
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_requests_by_configuration_and_target_date_between<T>(
&self,
configuration_uri: T,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync
where
T: AsRef<str> + Send + Sync,
{
async move {
let mut uri =
self.path_to_url("requests/search/findAllByConfigurationAndTargetDateBetween")?;
uri.set_query(Some(&format!(
"configuration={}&start={}&end={}",
configuration_uri.as_ref(),
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?,
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_requests_by_ids<I, S>(
&self,
ids: I,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync
where
I: IntoIterator<Item = S> + Send + Sync,
S: AsRef<str> + Send + Sync,
{
async move {
let ids_string = crate::utils::list_to_string(ids);
let mut uri = self.path_to_url("requests/search/findAllByIds")?;
uri.set_query(Some(&format!("ids={}", ids_string)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_requests_by_overlapping_public(
&self,
start: OffsetDateTime,
end: OffsetDateTime,
) -> PaginatedStream<'_, Self::Container<TaskRequest>> {
let mut uri = match self.path_to_url("requests/search/findAllByOverlappingPublic") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!(
"start={}&end={}",
start.format(&Iso8601::DEFAULT).unwrap(),
end.format(&Iso8601::DEFAULT).unwrap(),
)));
self.get_paginated(uri)
}
fn get_requests_by_satellite_name<T>(
&self,
satellite_name: T,
) -> PaginatedStream<'_, Self::Container<TaskRequest>>
where
T: AsRef<str> + Send + Sync,
{
let mut uri = match self.path_to_url("requests/search/findBySatelliteName") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!("name={}", satellite_name.as_ref())));
self.get_paginated(uri)
}
fn get_requests_by_satellite_name_and_target_date_between<T>(
&self,
satellite_name: T,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync
where
T: AsRef<str> + Send + Sync,
{
async move {
let mut uri =
self.path_to_url("requests/search/findAllBySatelliteNameAndTargetDateBetween")?;
uri.set_query(Some(&format!(
"name={}&start={}&end={}",
satellite_name.as_ref(),
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_requests_by_status<T>(
&self,
status: T,
) -> PaginatedStream<'_, Self::Container<TaskRequest>>
where
T: TryInto<TaskStatusType> + Send + Sync,
Error: From<<T as TryInto<TaskStatusType>>::Error>,
{
let status: TaskStatusType = match status.try_into() {
Ok(val) => val,
Err(err) => return Error::from(err).once_err(),
};
let mut uri = match self.path_to_url("requests/search/findByStatus") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!("status={}", status.as_ref())));
self.get_paginated(uri)
}
fn get_requests_by_status_and_account_and_target_date_between<T, U>(
&self,
status: T,
account_uri: U,
start: OffsetDateTime,
end: OffsetDateTime,
) -> PaginatedStream<'_, Self::Container<TaskRequest>>
where
T: AsRef<str> + Send + Sync,
U: AsRef<str> + Send + Sync,
{
let mut uri = match self
.path_to_url("requests/search/findAllByStatusAndAccountAndTargetDateBetween")
{
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!(
"status={}&satelliteNames={}&start={}&end={}",
status.as_ref(),
account_uri.as_ref(),
start.format(&Iso8601::DEFAULT).unwrap(),
end.format(&Iso8601::DEFAULT).unwrap()
)));
self.get_paginated(uri)
}
fn get_requests_by_type_and_target_date_between<T>(
&self,
typ: T,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync
where
T: TryInto<TaskType> + Send + Sync,
Error: From<<T as TryInto<TaskType>>::Error>,
{
async move {
let typ: TaskType = typ.try_into()?;
let mut uri = self.path_to_url("requests/search/findAllByTypeAndTargetDateBetween")?;
uri.set_query(Some(&format!(
"type={}&start={}&end={}",
typ.as_ref(),
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_requests_passed_today(
&self,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url("requests/search/findAllPassedToday")?;
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_requests_upcoming_today(
&self,
) -> impl Future<Output = Result<Self::Container<Vec<TaskRequest>>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url("requests/search/findAllUpcomingToday")?;
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<TaskRequest>>>>(uri)
.await?
.items)
}
}
fn get_satellites(&self) -> PaginatedStream<'_, Self::Container<Satellite>> {
let uri = match self.path_to_url("satellites") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn get_satellite_by_id(
&self,
satellite_id: i32,
) -> impl Future<Output = Result<Self::Container<Satellite>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("satellites/{}", satellite_id))?;
self.get_json_map(uri).await
}
}
fn get_satellite_by_name(
&self,
satellite_name: &str,
) -> impl Future<Output = Result<Self::Container<Satellite>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("satellites/findOneByName")?;
uri.set_query(Some(&format!("name={satellite_name}")));
self.get_json_map(uri).await
}
}
fn get_task_by_id(
&self,
task_id: i32,
) -> impl Future<Output = Result<Self::Container<Task>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url(format!("tasks/{}", task_id))?;
self.get_json_map(uri).await
}
}
fn get_tasks_by_account_and_pass_overlapping<T>(
&self,
account_uri: T,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<Task>>, Error>> + Send + Sync
where
T: AsRef<str> + Send + Sync,
{
async move {
let mut uri = self.path_to_url("tasks/search/findByAccountAndPassOverlapping")?;
uri.set_query(Some(&format!(
"account={}&start={}&end={}",
account_uri.as_ref(),
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<Task>>>>(uri)
.await?
.items)
}
}
fn get_tasks_by_account_and_satellite_and_band_and_pass_overlapping<T, U, V>(
&self,
account_uri: T,
satellite_config_uri: U,
band: V,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<Task>>, Error>> + Send + Sync
where
T: AsRef<str> + Send + Sync,
U: AsRef<str> + Send + Sync,
V: AsRef<str> + Send + Sync,
{
async move {
let mut uri = self.path_to_url(
"tasks/search/findByAccountAndSiteConfigurationAndBandAndPassOverlapping",
)?;
uri.set_query(Some(&format!(
"account={}&satellite={}&band={}&start={}&end={}",
account_uri.as_ref(),
satellite_config_uri.as_ref(),
band.as_ref(),
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?,
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<Task>>>>(uri)
.await?
.items)
}
}
fn get_tasks_by_account_and_site_configuration_and_band_and_pass_overlapping<T, U, V>(
&self,
account_uri: T,
site_config_uri: U,
band: V,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<Task>>, Error>> + Send + Sync
where
T: AsRef<str> + Send + Sync,
U: AsRef<str> + Send + Sync,
V: AsRef<str> + Send + Sync,
{
async move {
let mut uri = self.path_to_url(
"tasks/search/findByAccountAndSiteConfigurationAndBandAndPassOverlapping",
)?;
uri.set_query(Some(&format!(
"account={}&siteConfig={}&band={}&start={}&end={}",
account_uri.as_ref(),
site_config_uri.as_ref(),
band.as_ref(),
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<Task>>>>(uri)
.await?
.items)
}
}
fn get_tasks_by_pass_window(
&self,
start: OffsetDateTime,
end: OffsetDateTime,
) -> impl Future<Output = Result<Self::Container<Vec<Task>>, Error>> + Send + Sync {
async move {
let mut uri = self.path_to_url("tasks/search/findByStartBetweenOrderByStartAsc")?;
uri.set_query(Some(&format!(
"start={}&end={}",
start.format(&Iso8601::DEFAULT)?,
end.format(&Iso8601::DEFAULT)?
)));
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<Task>>>>(uri)
.await?
.items)
}
}
fn get_tasks_by_pass_overlapping(
&self,
start: OffsetDateTime,
end: OffsetDateTime,
) -> PaginatedStream<'_, Self::Container<Task>> {
let start = match start.format(&Iso8601::DEFAULT).map_err(Error::from) {
Ok(start) => start,
Err(error) => return error.once_err(),
};
let end = match end.format(&Iso8601::DEFAULT).map_err(Error::from) {
Ok(end) => end,
Err(error) => return error.once_err(),
};
let mut uri = match self.path_to_url("tasks/search/findByOverlapping") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
uri.set_query(Some(&format!("start={}&end={}", start, end)));
self.get_paginated(uri)
}
fn get_tasks_passed_today(
&self,
) -> impl Future<Output = Result<Self::Container<Vec<Task>>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url("tasks/search/findAllPassedToday")?;
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<Task>>>>(uri)
.await?
.items)
}
}
fn get_tasks_upcoming_today(
&self,
) -> impl Future<Output = Result<Self::Container<Vec<Task>>, Error>> + Send + Sync {
async move {
let uri = self.path_to_url("tasks/search/findAllUpcomingToday")?;
Ok(self
.get_json_map::<Embedded<Self::Container<Vec<Task>>>>(uri)
.await?
.items)
}
}
fn get_users(&self) -> PaginatedStream<'_, Self::Container<User>> {
let uri = match self.path_to_url("users") {
Ok(uri) => uri,
Err(err) => return err.once_err(),
};
self.get_paginated(uri)
}
fn new_band_details(&self) -> post::band::BandDetailsBuilder<'_, Self, post::band::NoName>
where
Self: Sized,
{
post::band::new(self)
}
fn new_satellite_configuration(
&self,
) -> post::sat_config::SatelliteConfigurationBuilder<'_, Self, post::sat_config::NoName>
where
Self: Sized,
{
post::sat_config::new(self)
}
fn new_satellite(&self) -> post::satellite::SatelliteBuilder<'_, Self, post::satellite::NoName>
where
Self: Sized,
{
post::satellite::new(self)
}
fn new_override(&self) -> post::overrides::OverrideBuilder<'_, Self, post::overrides::NoName>
where
Self: Sized,
{
post::overrides::new(self)
}
fn new_user(&self) -> post::user::UserBuilder<'_, Self, post::user::NoAccount>
where
Self: Sized,
{
post::user::new(self)
}
fn new_task_request(&self) -> post::TaskRequestBuilder<'_, Self, post::request::NoType>
where
Self: Sized,
{
post::request::new(self)
}
fn new_token_by_site_configuration_id(
&self,
band_id: u32,
site_configuration_id: u32,
) -> impl Future<Output = Result<String, Error>> + Send + Sync {
async move {
let url = self.path_to_url("fps")?;
let payload = serde_json::json!({
"band": format!("/api/satellite_bands/{}", band_id),
"configuration": format!("/api/configurations/{}", site_configuration_id),
});
let value: JsonValue = self.post_json_map(url, &payload).await?;
value
.get("token")
.ok_or(Error::Response(String::from("Missing token field")))?
.as_str()
.ok_or(Error::Response(String::from("Invalid type for token")))
.map(|s| s.to_owned())
}
}
fn new_token_by_satellite_id(
&self,
band_id: u32,
satellite_id: u32,
) -> impl Future<Output = Result<String, Error>> + Send + Sync {
async move {
let url = self.path_to_url("fps")?;
let payload = serde_json::json!({
"band": format!("/api/satellite_bands/{}", band_id),
"satellite": format!("/api/satellites/{}", satellite_id),
});
let value: JsonValue = self.post_json_map(url, &payload).await?;
value
.get("token")
.ok_or(Error::Response(String::from("Missing token field")))?
.as_str()
.ok_or(Error::Response(String::from("Invalid type for token")))
.map(|s| s.to_owned())
}
}
}
pub(crate) fn error_on_non_success(status: &StatusCode, body: &[u8]) -> Result<(), Error> {
if !status.is_success() {
return Err(Error::ResponseStatus {
status: *status,
error: String::from_utf8_lossy(body).to_string(),
});
}
Ok(())
}