stygian_graph/adapters/graphql_plugins/
jobber.rs1use std::collections::HashMap;
8
9use crate::ports::graphql_plugin::GraphQlTargetPlugin;
10use crate::ports::{GraphQlAuth, GraphQlAuthKind};
11
12pub struct JobberPlugin;
28
29impl GraphQlTargetPlugin for JobberPlugin {
30 fn name(&self) -> &'static str {
31 "jobber"
32 }
33
34 fn endpoint(&self) -> &'static str {
35 "https://api.getjobber.com/api/graphql"
36 }
37
38 fn version_headers(&self) -> HashMap<String, String> {
39 [(
40 "X-JOBBER-GRAPHQL-VERSION".to_string(),
41 "2025-04-16".to_string(),
42 )]
43 .into()
44 }
45
46 fn default_auth(&self) -> Option<GraphQlAuth> {
47 std::env::var("JOBBER_ACCESS_TOKEN")
48 .ok()
49 .map(|token| GraphQlAuth {
50 kind: GraphQlAuthKind::Bearer,
51 token,
52 header_name: None,
53 })
54 }
55
56 fn description(&self) -> &'static str {
57 "Jobber field-service management GraphQL API"
58 }
59}
60
61#[cfg(test)]
62#[allow(unsafe_code, clippy::expect_used)] mod tests {
64 use super::*;
65 use std::sync::Mutex;
66
67 static ENV_LOCK: Mutex<()> = Mutex::new(());
69
70 #[test]
71 fn plugin_name_is_jobber() {
72 assert_eq!(JobberPlugin.name(), "jobber");
73 }
74
75 #[test]
76 fn endpoint_is_correct() {
77 assert_eq!(
78 JobberPlugin.endpoint(),
79 "https://api.getjobber.com/api/graphql"
80 );
81 }
82
83 #[test]
84 fn version_header_is_set() {
85 let headers = JobberPlugin.version_headers();
86 assert_eq!(
87 headers.get("X-JOBBER-GRAPHQL-VERSION").map(String::as_str),
88 Some("2025-04-16")
89 );
90 }
91
92 #[test]
93 fn default_auth_reads_env() {
94 let key = "JOBBER_ACCESS_TOKEN";
95 let _guard = ENV_LOCK
96 .lock()
97 .unwrap_or_else(std::sync::PoisonError::into_inner);
98 let prev = std::env::var(key).ok();
99 unsafe { std::env::set_var(key, "test-token-abc") };
101
102 let auth = JobberPlugin.default_auth();
103 assert!(auth.is_some(), "auth should be Some when env var is set");
104 let auth = auth.expect("auth should be Some when env var is set");
105 assert_eq!(auth.kind, GraphQlAuthKind::Bearer);
106 assert_eq!(auth.token, "test-token-abc");
107 assert!(auth.header_name.is_none());
108
109 match prev {
111 Some(v) => unsafe { std::env::set_var(key, v) },
112 None => unsafe { std::env::remove_var(key) },
113 }
114 }
115
116 #[test]
117 fn default_auth_absent_when_no_env() {
118 let key = "JOBBER_ACCESS_TOKEN";
119 let _guard = ENV_LOCK
120 .lock()
121 .unwrap_or_else(std::sync::PoisonError::into_inner);
122 let prev = std::env::var(key).ok();
123 unsafe { std::env::remove_var(key) };
125
126 assert!(JobberPlugin.default_auth().is_none());
127
128 if let Some(v) = prev {
130 unsafe { std::env::set_var(key, v) };
131 }
132 }
133
134 #[test]
135 fn description_is_non_empty() {
136 assert!(!JobberPlugin.description().is_empty());
137 }
138}