thunkmetrc_client/
client.rs1use reqwest::Client;
2use serde_json::Value;
3use std::error::Error;
4use log::{debug, warn};
5use serde::Deserialize;
6use crate::services::*;
7
8#[derive(Debug)]
9pub struct ApiError {
10 pub status: reqwest::StatusCode,
11 pub headers: reqwest::header::HeaderMap,
12 pub message: String,
13}
14
15impl std::fmt::Display for ApiError {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "API Error: {} - {}", self.status, self.message)
18 }
19}
20
21impl Error for ApiError {}
22
23#[derive(Clone)]
24pub struct MetrcClient {
25 client: Client,
26 base_url: String,
27 vendor_key: String,
28 user_key: String,
29}
30
31impl MetrcClient {
32 pub fn new(base_url: &str, vendor_key: &str, user_key: &str) -> Self {
33 MetrcClient {
34 client: Client::new(),
35 base_url: base_url.trim_end_matches('/').to_string(),
36 vendor_key: vendor_key.to_string(),
37 user_key: user_key.to_string(),
38 }
39 }
40 pub fn additives_templates(&self) -> AdditivesTemplatesClient {
41 AdditivesTemplatesClient { client: self }
42 }
43 pub fn caregivers_status(&self) -> CaregiversStatusClient {
44 CaregiversStatusClient { client: self }
45 }
46 pub fn employees(&self) -> EmployeesClient {
47 EmployeesClient { client: self }
48 }
49 pub fn facilities(&self) -> FacilitiesClient {
50 FacilitiesClient { client: self }
51 }
52 pub fn harvests(&self) -> HarvestsClient {
53 HarvestsClient { client: self }
54 }
55 pub fn items(&self) -> ItemsClient {
56 ItemsClient { client: self }
57 }
58 pub fn lab_tests(&self) -> LabTestsClient {
59 LabTestsClient { client: self }
60 }
61 pub fn locations(&self) -> LocationsClient {
62 LocationsClient { client: self }
63 }
64 pub fn packages(&self) -> PackagesClient {
65 PackagesClient { client: self }
66 }
67 pub fn patient_check_ins(&self) -> PatientCheckInsClient {
68 PatientCheckInsClient { client: self }
69 }
70 pub fn patients(&self) -> PatientsClient {
71 PatientsClient { client: self }
72 }
73 pub fn patients_status(&self) -> PatientsStatusClient {
74 PatientsStatusClient { client: self }
75 }
76 pub fn plant_batches(&self) -> PlantBatchesClient {
77 PlantBatchesClient { client: self }
78 }
79 pub fn plants(&self) -> PlantsClient {
80 PlantsClient { client: self }
81 }
82 pub fn processing_job(&self) -> ProcessingJobClient {
83 ProcessingJobClient { client: self }
84 }
85 pub fn retail_id(&self) -> RetailIdClient {
86 RetailIdClient { client: self }
87 }
88 pub fn sales(&self) -> SalesClient {
89 SalesClient { client: self }
90 }
91 pub fn sandbox(&self) -> SandboxClient {
92 SandboxClient { client: self }
93 }
94 pub fn strains(&self) -> StrainsClient {
95 StrainsClient { client: self }
96 }
97 pub fn sublocations(&self) -> SublocationsClient {
98 SublocationsClient { client: self }
99 }
100 pub fn tags(&self) -> TagsClient {
101 TagsClient { client: self }
102 }
103 pub fn transfers(&self) -> TransfersClient {
104 TransfersClient { client: self }
105 }
106 pub fn transporters(&self) -> TransportersClient {
107 TransportersClient { client: self }
108 }
109 pub fn units_of_measure(&self) -> UnitsOfMeasureClient {
110 UnitsOfMeasureClient { client: self }
111 }
112 pub fn waste_methods(&self) -> WasteMethodsClient {
113 WasteMethodsClient { client: self }
114 }
115 pub fn webhooks(&self) -> WebhooksClient {
116 WebhooksClient { client: self }
117 }
118
119 pub(crate) async fn send(&self, method: reqwest::Method, path: &str, body: Option<&Value>) -> Result<Option<Value>, Box<dyn Error + Send + Sync>> {
120 let url = format!("{}{}", self.base_url, path);
121 debug!("Sending Request: {} {}", method, url);
122
123 let mut req = self.client.request(method, &url);
124 req = req.basic_auth(&self.vendor_key, Some(&self.user_key));
125
126 if let Some(b) = body {
127 req = req.json(b);
128 }
129
130 let resp = req.send().await?;
131 let status = resp.status();
132 let headers = resp.headers().clone();
133
134 if !status.is_success() {
135 let code = status.as_u16();
136 warn!("API Error Response: {}", code);
137
138 let text = resp.text().await.unwrap_or_default();
139 let mut message = format!("Unexpected code {}", status);
140 #[derive(Deserialize)]
141 struct ApiErrorBody {
142 #[serde(rename = "Message")]
143 message: Option<String>,
144 #[serde(rename = "ValidationErrors")]
145 _validation_errors: Option<Vec<String>>,
146 }
147
148 if let Ok(body) = serde_json::from_str::<ApiErrorBody>(&text) {
149 if let Some(m) = body.message {
150 message = m;
151 }
152 }
153
154 return Err(Box::new(ApiError {
155 status,
156 headers,
157 message,
158 }));
159 }
160 if status == reqwest::StatusCode::NO_CONTENT {
161 return Ok(None);
162 }
163 let json: Value = resp.json().await?;
164 Ok(Some(json))
165 }
166}