Skip to main content

thunkmetrc_client/
client.rs

1use 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}