use crate::api::{
AppointmentApi, ConnectorApi, CrudApi, GeocodeApi, ImportApi, JobApi, MessageApi,
NotificationApi, OptimizationApi, ReadOnlyApi, RecommendationApi, RecurringAppointmentApi,
ResourceCapacityApi, TaskApi, TaskAssignmentApi, TaskBudgetEntryApi, TimeEntryApi,
TimeSessionApi, UserApi,
};
use crate::error::{Error, Result};
use crate::transport::Transport;
use crate::{entities, types};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Environment {
#[default]
Production,
Test,
Sandbox,
}
impl Environment {
pub fn base_url(self) -> &'static str {
match self {
Environment::Production => "https://api.dimescheduler.com",
Environment::Test => "https://test.api.dimescheduler.com",
Environment::Sandbox => "https://sandbox.api.dimescheduler.com",
}
}
}
pub struct DimeSchedulerClient {
pub action_uris: CrudApi<entities::ActionUri, types::ActionUrlDto>,
pub appointment_categories: CrudApi<entities::AppointmentCategory, types::CategoryDto>,
pub appointment_contents: CrudApi<entities::AppointmentContent, ()>,
pub appointment_containers:
CrudApi<entities::AppointmentContainer, types::AppointmentContainerDto>,
pub appointment_field_values:
CrudApi<entities::AppointmentFieldValue, types::AppointmentFieldValueDto>,
pub appointment_importances: CrudApi<entities::AppointmentImportance, ()>,
pub appointment_locked: CrudApi<entities::AppointmentLocked, ()>,
pub appointment_planning_quantities: CrudApi<entities::AppointmentPlanningQuantity, ()>,
pub appointment_time_markers: CrudApi<entities::AppointmentTimeMarker, ()>,
pub appointment_uris: CrudApi<entities::AppointmentUri, types::AppointmentUrlDto>,
pub assignments: CrudApi<entities::Assignment, types::AssignmentDto>,
pub captions: CrudApi<entities::Caption, ()>,
pub categories: CrudApi<entities::Category, types::CategoryDto>,
pub containers: CrudApi<entities::Container, types::ContainerDto>,
pub filter_groups: CrudApi<entities::FilterGroup, types::FilterGroupDto>,
pub filter_values: CrudApi<entities::FilterValue, types::FilterValueDto>,
pub pins: CrudApi<entities::Pin, types::PinDto>,
pub project_members: CrudApi<entities::ProjectMember, ()>,
pub resources: CrudApi<entities::Resource, types::ResourceDto>,
pub resource_calendars: CrudApi<entities::ResourceCalendar, ()>,
pub resource_certificates: CrudApi<entities::ResourceCertificate, ()>,
pub resource_filter_values:
CrudApi<entities::ResourceFilterValue, types::ResourceFilterValueDto>,
pub resource_gps_trackings: CrudApi<entities::ResourceGpsTracking, ()>,
pub resource_uris: CrudApi<entities::ResourceUri, types::ResourceUrlDto>,
pub tags: CrudApi<entities::Tag, types::TagDto>,
pub task_containers: CrudApi<entities::TaskContainer, ()>,
pub task_dependencies: CrudApi<entities::TaskDependency, ()>,
pub task_filter_values: CrudApi<entities::TaskFilterValue, types::TaskFilterValueDto>,
pub task_locked: CrudApi<entities::TaskLocked, ()>,
pub task_uris: CrudApi<entities::TaskUri, types::TaskUrlDto>,
pub time_markers: CrudApi<entities::TimeMarker, types::TimeMarkerDto>,
pub appointments: AppointmentApi,
pub jobs: JobApi,
pub tasks: TaskApi,
pub notifications: NotificationApi,
pub resource_capacities: ResourceCapacityApi,
pub time_entries: TimeEntryApi,
pub time_sessions: TimeSessionApi,
pub calendars: ReadOnlyApi<types::CalendarDto>,
pub resource_types: ReadOnlyApi<types::ResourceTypeDto>,
pub appointment_dependencies: ReadOnlyApi<types::AppointmentDependencyDto>,
pub appointment_fields: ReadOnlyApi<types::AppointmentFieldDto>,
pub task_budget_entries: TaskBudgetEntryApi,
pub task_assignments: TaskAssignmentApi,
pub connectors: ConnectorApi,
pub geocoding: GeocodeApi,
pub imports: ImportApi,
pub messages: MessageApi,
pub optimization: OptimizationApi,
pub recommendation: RecommendationApi,
pub recurring_appointments: RecurringAppointmentApi,
pub users: UserApi,
transport: Transport,
}
impl DimeSchedulerClient {
pub fn new(api_key: impl Into<String>) -> Self {
Self::with_environment(api_key, Environment::Production)
}
pub fn with_environment(api_key: impl Into<String>, environment: Environment) -> Self {
Self::from_transport(Transport::new(
environment.base_url().to_string(),
api_key.into(),
))
}
pub fn from_env() -> Result<Self> {
Self::from_optional_key(std::env::var("DIMESCHEDULER_API_KEY").ok())
}
pub(crate) fn from_optional_key(api_key: Option<String>) -> Result<Self> {
match api_key {
Some(key) if !key.is_empty() => Ok(Self::new(key)),
_ => Err(Error::MissingApiKey),
}
}
pub(crate) fn from_transport(transport: Transport) -> Self {
let t = &transport;
Self {
action_uris: CrudApi::new(t.clone(), "/actionUri"),
appointment_categories: CrudApi::new(t.clone(), "/appointmentCategory"),
appointment_contents: CrudApi::new(t.clone(), "/appointmentcontent"),
appointment_containers: CrudApi::new(t.clone(), "/appointmentContainer"),
appointment_field_values: CrudApi::new(t.clone(), "/appointmentFieldValue"),
appointment_importances: CrudApi::new(t.clone(), "/appointmentImportance"),
appointment_locked: CrudApi::new(t.clone(), "/appointmentLocked"),
appointment_planning_quantities: CrudApi::new(
t.clone(),
"/appointmentPlanningQuantity",
),
appointment_time_markers: CrudApi::new(t.clone(), "/appointmentTimeMarker"),
appointment_uris: CrudApi::new(t.clone(), "/appointmentUri"),
assignments: CrudApi::new(t.clone(), "/assignment"),
captions: CrudApi::new(t.clone(), "/caption"),
categories: CrudApi::new(t.clone(), "/category"),
containers: CrudApi::new(t.clone(), "/container"),
filter_groups: CrudApi::new(t.clone(), "/filterGroup"),
filter_values: CrudApi::new(t.clone(), "/filterValue"),
pins: CrudApi::new(t.clone(), "/pin"),
project_members: CrudApi::new(t.clone(), "/projectMember"),
resources: CrudApi::new(t.clone(), "/resource"),
resource_calendars: CrudApi::new(t.clone(), "/resourceCalendar"),
resource_certificates: CrudApi::new(t.clone(), "/resourceCertificate"),
resource_filter_values: CrudApi::new(t.clone(), "/resourceFilterValue"),
resource_gps_trackings: CrudApi::new(t.clone(), "/resourceGpsTracking"),
resource_uris: CrudApi::new(t.clone(), "/resourceUri"),
tags: CrudApi::new(t.clone(), "/tag"),
task_containers: CrudApi::new(t.clone(), "/taskContainer"),
task_dependencies: CrudApi::new(t.clone(), "/taskDependency"),
task_filter_values: CrudApi::new(t.clone(), "/taskFilterValue"),
task_locked: CrudApi::new(t.clone(), "/taskLocked"),
task_uris: CrudApi::new(t.clone(), "/taskUri"),
time_markers: CrudApi::new(t.clone(), "/timeMarker"),
appointments: AppointmentApi::new(t.clone()),
jobs: JobApi::new(t.clone()),
tasks: TaskApi::new(t.clone()),
notifications: NotificationApi::new(t.clone()),
resource_capacities: ResourceCapacityApi::new(t.clone()),
time_entries: TimeEntryApi::new(t.clone()),
time_sessions: TimeSessionApi::new(t.clone()),
calendars: ReadOnlyApi::new(t.clone(), "/calendar"),
resource_types: ReadOnlyApi::new(t.clone(), "/resourcetype"),
appointment_dependencies: ReadOnlyApi::new(t.clone(), "/appointmentDependency"),
appointment_fields: ReadOnlyApi::new(t.clone(), "/appointmentField"),
task_budget_entries: TaskBudgetEntryApi::new(t.clone()),
task_assignments: TaskAssignmentApi::new(t.clone()),
connectors: ConnectorApi::new(t.clone()),
geocoding: GeocodeApi::new(t.clone()),
imports: ImportApi::new(t.clone()),
messages: MessageApi::new(t.clone()),
optimization: OptimizationApi::new(t.clone()),
recommendation: RecommendationApi::new(t.clone()),
recurring_appointments: RecurringAppointmentApi::new(t.clone()),
users: UserApi::new(t.clone()),
transport,
}
}
pub fn base_url(&self) -> &str {
self.transport.base_url()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn environment_urls_are_fixed() {
assert_eq!(
Environment::Production.base_url(),
"https://api.dimescheduler.com"
);
assert_eq!(
Environment::Test.base_url(),
"https://test.api.dimescheduler.com"
);
assert_eq!(
Environment::Sandbox.base_url(),
"https://sandbox.api.dimescheduler.com"
);
assert_eq!(Environment::default(), Environment::Production);
}
#[test]
fn new_targets_production_and_with_environment_targets_the_choice() {
assert_eq!(
DimeSchedulerClient::new("k").base_url(),
"https://api.dimescheduler.com"
);
assert_eq!(
DimeSchedulerClient::with_environment("k", Environment::Sandbox).base_url(),
"https://sandbox.api.dimescheduler.com"
);
}
#[test]
fn from_optional_key_rejects_missing_or_empty_and_accepts_a_value() {
assert!(matches!(
DimeSchedulerClient::from_optional_key(None),
Err(Error::MissingApiKey)
));
assert!(matches!(
DimeSchedulerClient::from_optional_key(Some(String::new())),
Err(Error::MissingApiKey)
));
assert_eq!(
DimeSchedulerClient::from_optional_key(Some("k".into()))
.unwrap()
.base_url(),
"https://api.dimescheduler.com"
);
}
}