use std::ops::Deref;
use reqwest::Method;
use serde::de::DeserializeOwned;
use crate::api::CrudApi;
use crate::error::Result;
use crate::transport::{encode_segment, Transport};
use crate::types::DimeSchedulerApiResponse;
use crate::{entities, types};
macro_rules! crud_with_extras {
($name:ident, $entity:ty, $dto:ty, $route:literal, $doc:literal) => {
#[doc = $doc]
#[derive(Clone)]
pub struct $name {
crud: CrudApi<$entity, $dto>,
}
impl $name {
pub(crate) fn new(transport: Transport) -> Self {
Self {
crud: CrudApi::new(transport, $route),
}
}
}
impl Deref for $name {
type Target = CrudApi<$entity, $dto>;
fn deref(&self) -> &Self::Target {
&self.crud
}
}
};
}
crud_with_extras!(
AppointmentApi,
entities::Appointment,
types::AppointmentDto,
"/appointment",
"CRUD for appointments plus the date-range query and lookup by id. `get_all` is inherited from `CrudApi` but this route requires query parameters; use `get`."
);
crud_with_extras!(
JobApi,
entities::Job,
types::JobDto,
"/job",
"CRUD for jobs plus lookup by job number."
);
crud_with_extras!(
TaskApi,
entities::Task,
types::TaskDto,
"/task",
"CRUD for tasks plus the filtered, paginated list query."
);
crud_with_extras!(
NotificationApi,
entities::Notification,
types::NotificationDto,
"/notification",
"CRUD for notifications plus the paginated query. `get_all` is inherited from `CrudApi` but this route requires query parameters; use `get`."
);
crud_with_extras!(
ResourceCapacityApi,
entities::ResourceCapacity,
types::ResourceCapacityDto,
"/resourceCapacity",
"CRUD for resource capacities plus the date-range query. `get_all` is inherited from `CrudApi` but this route requires query parameters; use `get`."
);
crud_with_extras!(
TimeEntryApi,
entities::TimeEntry,
types::TimeSheetEntryDto,
"/timeEntry",
"CRUD for time entries plus the date-range query over time sheet entries. `get_all` is inherited from `CrudApi` but this route requires query parameters; use `get`."
);
crud_with_extras!(
TimeSessionApi,
entities::TimeSession,
types::TimeSessionOverviewDto,
"/timeSession",
"CRUD for time sessions plus the date-range query over session overviews. `get_all` is inherited from `CrudApi` but this route requires query parameters; use `get`."
);
impl AppointmentApi {
pub async fn get(
&self,
start_date: &str,
end_date: &str,
resources: &[&str],
) -> Result<Vec<types::AppointmentDto>> {
let mut query = vec![
("startDate", start_date.to_string()),
("endDate", end_date.to_string()),
];
query.extend(resources.iter().map(|r| ("resources", (*r).to_string())));
self.transport().get_json(self.route(), &query).await
}
pub async fn get_by_id(&self, id: &str) -> Result<types::AppointmentDto> {
self.transport()
.get_json(&format!("{}/{}", self.route(), encode_segment(id)), &[])
.await
}
}
impl JobApi {
pub async fn get(&self, job_no: &str) -> Result<types::JobDto> {
self.transport()
.get_json(&format!("{}/{}", self.route(), encode_segment(job_no)), &[])
.await
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TaskListOptions {
pub is_open: Option<bool>,
pub page: Option<u32>,
pub page_size: Option<u32>,
}
impl TaskApi {
pub async fn list(&self, options: &TaskListOptions) -> Result<Vec<types::TaskDto>> {
let mut query = Vec::new();
if let Some(open) = options.is_open {
query.push(("isOpen", open.to_string()));
}
if let Some(page) = options.page {
query.push(("page", page.to_string()));
}
if let Some(size) = options.page_size {
query.push(("pageSize", size.to_string()));
}
self.transport().get_json(self.route(), &query).await
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct NotificationListOptions {
pub sort: Option<String>,
pub group: Option<String>,
pub filter: Option<String>,
}
impl NotificationApi {
pub async fn get(
&self,
page: u32,
limit: u32,
options: &NotificationListOptions,
) -> Result<Vec<types::NotificationDto>> {
let mut query = vec![("page", page.to_string()), ("limit", limit.to_string())];
if let Some(s) = &options.sort {
query.push(("sort", s.clone()));
}
if let Some(g) = &options.group {
query.push(("group", g.clone()));
}
if let Some(f) = &options.filter {
query.push(("filter", f.clone()));
}
self.transport().get_json(self.route(), &query).await
}
}
fn date_range(start: &str, end: &str) -> [(&'static str, String); 2] {
[("start", start.to_string()), ("end", end.to_string())]
}
impl ResourceCapacityApi {
pub async fn get(&self, start: &str, end: &str) -> Result<Vec<types::ResourceCapacityDto>> {
self.transport()
.get_json(self.route(), &date_range(start, end))
.await
}
}
impl TimeEntryApi {
pub async fn get(&self, start: &str, end: &str) -> Result<Vec<types::TimeSheetEntryDto>> {
self.transport()
.get_json(self.route(), &date_range(start, end))
.await
}
}
impl TimeSessionApi {
pub async fn get(&self, start: &str, end: &str) -> Result<Vec<types::TimeSessionOverviewDto>> {
self.transport()
.get_json(self.route(), &date_range(start, end))
.await
}
}
#[derive(Clone)]
pub struct ReadOnlyApi<D> {
transport: Transport,
route: &'static str,
_marker: std::marker::PhantomData<fn() -> D>,
}
impl<D: DeserializeOwned> ReadOnlyApi<D> {
pub(crate) fn new(transport: Transport, route: &'static str) -> Self {
Self {
transport,
route,
_marker: std::marker::PhantomData,
}
}
pub async fn get_all(&self) -> Result<Vec<D>> {
self.transport.get_json(self.route, &[]).await
}
pub fn route(&self) -> &'static str {
self.route
}
}
#[derive(Clone)]
pub struct TaskBudgetEntryApi {
transport: Transport,
}
impl TaskBudgetEntryApi {
const ROUTE: &'static str = "/taskBudgetEntry";
pub(crate) fn new(transport: Transport) -> Self {
Self { transport }
}
pub async fn create(
&self,
entry: &entities::TaskBudgetEntry,
) -> Result<DimeSchedulerApiResponse> {
self.transport
.send_json(Method::POST, Self::ROUTE, entry)
.await
}
pub async fn create_many(
&self,
entries: &[entities::TaskBudgetEntry],
) -> Result<DimeSchedulerApiResponse> {
self.transport
.send_json(Method::POST, Self::ROUTE, entries)
.await
}
pub async fn delete(
&self,
entry: &entities::TaskBudgetEntry,
) -> Result<DimeSchedulerApiResponse> {
self.transport
.send_json(Method::DELETE, Self::ROUTE, entry)
.await
}
pub async fn delete_many(
&self,
entries: &[entities::TaskBudgetEntry],
) -> Result<DimeSchedulerApiResponse> {
self.transport
.send_json(Method::DELETE, Self::ROUTE, entries)
.await
}
}
#[derive(Clone)]
pub struct TaskAssignmentApi {
transport: Transport,
}
impl TaskAssignmentApi {
const ROUTE: &'static str = "/taskAssignment";
pub(crate) fn new(transport: Transport) -> Self {
Self { transport }
}
pub async fn create(&self, request: &types::TaskAssignmentRequest) -> Result<()> {
self.transport
.send_empty(Method::POST, Self::ROUTE, request)
.await
}
pub async fn delete(&self, request: &types::TaskAssignmentRequest) -> Result<()> {
self.transport
.send_empty(Method::DELETE, Self::ROUTE, request)
.await
}
}