tmf_client/
lib.rs

1//! # TMF Client Library
2//!
3//! ## Description
4//! Interact with TMF compliant APIs using an SDK
5//!
6//! ## Supported TMF APIs
7//!
8//! Currently supports:
9//!
10//! - TMF620 Product Catalog Management
11//! - TMF622 Product Order Management
12//! - TMF629 Customer Management
13//! - TMF632 Party Management
14//! - TMF633 Service Catalog Management
15//! - TMF637 Product Inventory Management
16//! - TMF638 Service Inventory Management
17//! - TMF639 Resource Inventory Management
18//! - TMF645 Service Qualification Management
19//! - TMF648 Quote Management
20//! - TMF663 Shopping Cart Management
21//! - TMF674 Geographic Site Management
22//!
23//! ## Features
24//! All TMF APIs can be conditionally compiled. Deafult includes all APIs using V4 specifications.
25
26#![warn(missing_docs)]
27
28pub mod common;
29pub mod tmf;
30
31use common::tmf_error::TMFError;
32#[cfg(feature = "tmf620")]
33use tmf::tmf620::TMF620;
34#[cfg(feature = "tmf622")]
35use tmf::tmf622::TMF622;
36#[cfg(feature = "tmf629")]
37use tmf::tmf629::TMF629;
38#[cfg(feature = "tmf632")]
39use tmf::tmf632::TMF632;
40#[cfg(feature = "tmf633")]
41use tmf::tmf633::TMF633;
42#[cfg(feature = "tmf637")]
43use tmf::tmf637::TMF637;
44#[cfg(feature = "tmf638")]
45use tmf::tmf638::TMF638;
46#[cfg(feature = "tmf639")]
47use tmf::tmf639::TMF639;
48#[cfg(feature = "tmf645")]
49use tmf::tmf645::TMF645;
50#[cfg(feature = "tmf648")]
51use tmf::tmf648::TMF648;
52#[cfg(feature = "tmf663")]
53use tmf::tmf663::TMF663;
54#[cfg(feature = "tmf674")]
55use tmf::tmf674::TMF674;
56
57use tmflib::{HasId, Uri};
58
59#[cfg(feature = "insecure")]
60const INSECURE: bool = true;
61#[cfg(not(feature = "insecure"))]
62const INSECURE: bool = false;
63
64/// Default port for the TMF API
65pub const DEFAULT_PORT: u16 = 8001;
66
67/// Configuration for the TMF Client
68#[derive(Clone, Debug, Default)]
69pub struct Config {
70    /// The host URI for the TMF API  
71    pub host: Uri,
72    /// The port to use for the TMF API
73    ///
74    pub port: u16,
75    /// The base path for the TMF API
76    pub insecure: bool,
77}
78
79impl Config {
80    /// Create a new configuration for the TMF Client
81    /// ```
82    /// # use tmf_client::Config;
83    /// let config = Config::new("http://localhost", 8000);
84    /// ```
85    pub fn new(host: impl Into<String>, port: u16) -> Config {
86        Config {
87            host: Uri::from(host.into()),
88            port,
89            insecure: INSECURE,
90        }
91    }
92}
93
94/// Fields for filtering output
95#[derive(Clone, Default, Debug)]
96pub struct QueryOptions {
97    /// Specific set of fields delimited by comma
98    pub fields: Option<String>,
99    /// Limit the number of results returned
100    pub limit: Option<u16>,
101    /// Offset the results returned
102    pub offset: Option<u16>,
103    /// Filter on name
104    pub name: Option<String>,
105}
106
107impl QueryOptions {
108    /// Setting the field on an existing QueryOptions
109    /// ```
110    /// # use tmf_client::QueryOptions;
111    /// let opt = QueryOptions::default()
112    ///     .fields("id,name,description".to_string());
113    /// ```
114    pub fn fields(mut self, fields: String) -> QueryOptions {
115        self.fields = Some(fields);
116        self
117    }
118    /// Set the limit on the number of results returned
119    /// ```
120    /// # use tmf_client::QueryOptions;
121    /// let opt = QueryOptions::default()
122    ///     .limit(10);
123    /// ```
124    pub fn limit(mut self, limit: u16) -> QueryOptions {
125        self.limit = Some(limit);
126        self
127    }
128
129    /// Set the offset on the number of results returned
130    /// ```
131    /// # use tmf_client::QueryOptions;
132    /// let opt = QueryOptions::default()
133    ///     .offset(5);
134    /// ```
135    pub fn offset(mut self, offset: u16) -> QueryOptions {
136        self.offset = Some(offset);
137        self
138    }
139
140    /// Set the name to filter on
141    /// ```
142    /// # use tmf_client::QueryOptions;
143    /// let opt = QueryOptions::default()
144    ///     .name("MyService".to_string());
145    /// ```
146    /// This will filter the results to only include those with the specified name.
147    /// If the name is not set, it will not filter on name.
148    ///
149    pub fn name(mut self, name: impl Into<String>) -> QueryOptions {
150        self.name = Some(name.into());
151        self
152    }
153}
154
155impl From<QueryOptions> for String {
156    fn from(val: QueryOptions) -> Self {
157        let limit = match val.limit {
158            Some(l) => format!("limit={l}"),
159            None => String::default(),
160        };
161        let offset = match val.offset {
162            Some(o) => format!("offset={o}"),
163            None => String::default(),
164        };
165        let name = match val.name {
166            Some(n) => format!("name={n}"),
167            None => String::default(),
168        };
169        format!("{limit}&{offset}&{name}")
170    }
171}
172
173/// Standard set of operations for all TMF objects
174#[cfg(feature = "blocking")]
175pub trait BlockingOperations {
176    /// The TMF object type that this trait operates on
177    type TMF: HasId;
178
179    /// Get a specific TMF object by Id
180    /// ```
181    /// # use tmf_client::{TMFClient,BlockingOperations};
182    /// let categories = TMFClient::new("http://localhost",Some(8000))
183    ///     .tmf620()
184    ///     .category()
185    ///     .get("ID123");
186    /// ```
187    fn get(&self, id: impl Into<String>) -> Result<Vec<Self::TMF>, TMFError>;
188    /// Get a list of tmf objects applying optional filter
189    /// ```
190    /// # use tmf_client::{TMFClient,QueryOptions,BlockingOperations};
191    /// let filter = QueryOptions::default()
192    ///     .limit(15)
193    ///     .offset(10);
194    /// let categories = TMFClient::new("http://localhost",Some(8000))
195    ///     .tmf620()
196    ///     .category()
197    ///     .list(Some(filter));
198    /// ```
199    fn list(&self, filter: Option<QueryOptions>) -> Result<Vec<Self::TMF>, TMFError>;
200    /// Create a new instance of a TMF object
201    fn create(&self, item: Self::TMF) -> Result<Self::TMF, TMFError>;
202    /// Update an existing TMF Object using the provided patch object
203    fn update(&self, id: impl Into<String>, patch: Self::TMF) -> Result<Self::TMF, TMFError>;
204    /// Delete a specific tmf object by Id
205    /// ```
206    /// # use tmf_client::{TMFClient,BlockingOperations};
207    /// let categories = TMFClient::new("http://localhost",None)
208    ///     .tmf620()
209    ///     .category()
210    ///     .delete("ID123");
211    /// ```
212    fn delete(&self, id: impl Into<String>) -> Result<Self::TMF, TMFError>;
213}
214
215/// Standard set of operations for all TMF objects
216#[cfg(not(feature = "blocking"))]
217pub trait AsyncOperations {
218    /// The TMF object type that this trait operates on
219    type TMF: HasId;
220
221    /// Get a specific TMF object by Id
222    fn get(
223        &self,
224        id: impl Into<String>,
225    ) -> impl std::future::Future<Output = Result<Vec<Self::TMF>, TMFError>>;
226    /// Get a list of tmf objects applying optional filter
227    fn list(
228        &self,
229        filter: Option<QueryOptions>,
230    ) -> impl std::future::Future<Output = Result<Vec<Self::TMF>, TMFError>>;
231    /// Create a new instance of a TMF object
232    fn create(
233        &self,
234        item: Self::TMF,
235    ) -> impl std::future::Future<Output = Result<Self::TMF, TMFError>>;
236    /// Update an existing TMF Object using the provided patch object
237    fn update(
238        &self,
239        id: impl Into<String>,
240        patch: Self::TMF,
241    ) -> impl std::future::Future<Output = Result<Self::TMF, TMFError>>;
242    /// Delete a specific tmf object by Id
243    fn delete(
244        &self,
245        id: impl Into<String>,
246    ) -> impl std::future::Future<Output = Result<Self::TMF, TMFError>>;
247}
248
249/// Trait to create a new instance of a TMF object
250#[allow(clippy::new_ret_no_self)]
251pub trait HasNew<T: Clone> {
252    /// Create a new instance of the TMF object passin in the destination host Uri
253    fn new(config: Config) -> T;
254}
255
256/// TMF Client
257pub struct TMFClient {
258    config: Config,
259    #[cfg(feature = "tmf620")]
260    tmf620: Option<TMF620>,
261    #[cfg(feature = "tmf622")]
262    tmf622: Option<TMF622>,
263    #[cfg(feature = "tmf629")]
264    tmf629: Option<TMF629>,
265    #[cfg(feature = "tmf632")]
266    tmf632: Option<TMF632>,
267    #[cfg(feature = "tmf633")]
268    tmf633: Option<TMF633>,
269    #[cfg(feature = "tmf637")]
270    tmf637: Option<TMF637>,
271    #[cfg(feature = "tmf638")]
272    tmf638: Option<TMF638>,
273    #[cfg(feature = "tmf639")]
274    tmf639: Option<TMF639>,
275    #[cfg(feature = "tmf645")]
276    tmf645: Option<TMF645>,
277    #[cfg(feature = "tmf648")]
278    tmf648: Option<TMF648>,
279    #[cfg(feature = "tmf663")]
280    tmf663: Option<TMF663>,
281    #[cfg(feature = "tmf674")]
282    tmf674: Option<TMF674>,
283}
284
285// Create a new instance
286fn instantiate<T: Clone + HasNew<T>>(api: &mut Option<T>, config: Config) -> T {
287    match api {
288        Some(instance) => instance.clone(),
289        None => {
290            let new_api = T::new(config);
291            api.replace(new_api.clone());
292            new_api
293        }
294    }
295}
296
297impl TMFClient {
298    /// Create a new TMFClient instance
299    /// ```
300    /// # use tmf_client::TMFClient;
301    /// let client = TMFClient::new("http://localhost",Some(8000));
302    /// ```
303    pub fn new(host: impl Into<String>, port: Option<u16>) -> TMFClient {
304        TMFClient {
305            config: Config::new(host, port.unwrap_or(DEFAULT_PORT)),
306            #[cfg(feature = "tmf620")]
307            tmf620: None,
308            #[cfg(feature = "tmf622")]
309            tmf622: None,
310            #[cfg(feature = "tmf629")]
311            tmf629: None,
312            #[cfg(feature = "tmf632")]
313            tmf632: None,
314            #[cfg(feature = "tmf633")]
315            tmf633: None,
316            #[cfg(feature = "tmf637")]
317            tmf637: None,
318            #[cfg(feature = "tmf638")]
319            tmf638: None,
320            #[cfg(feature = "tmf639")]
321            tmf639: None,
322            #[cfg(feature = "tmf645")]
323            tmf645: None,
324            #[cfg(feature = "tmf648")]
325            tmf648: None,
326            #[cfg(feature = "tmf663")]
327            tmf663: None,
328            #[cfg(feature = "tmf674")]
329            tmf674: None,
330        }
331    }
332
333    /// Create access to TMF620 API
334    /// ```
335    /// # use tmf_client::TMFClient;
336    /// let tmf620 = TMFClient::new("http://localhost",None)
337    ///     .tmf620();
338    /// ```
339    #[cfg(feature = "tmf620")]
340    pub fn tmf620(&mut self) -> TMF620 {
341        // // let instance =
342        let config = self.config.clone();
343        let instance: TMF620 = instantiate(&mut self.tmf620, config);
344        self.tmf620.replace(instance.clone());
345        instance
346    }
347
348    /// Create access to TMF622 API
349    /// ```
350    /// # use tmf_client::TMFClient;
351    /// let tmf620 = TMFClient::new("http://localhost",None)
352    ///     .tmf622();
353    /// ```
354    #[cfg(feature = "tmf622")]
355    pub fn tmf622(&mut self) -> TMF622 {
356        instantiate(&mut self.tmf622, self.config.clone())
357    }
358
359    /// Create access to TMF632 API
360    /// ```
361    /// # use tmf_client::TMFClient;
362    /// let tmf632 = TMFClient::new("http://localhost",None)
363    ///     .tmf629();
364    /// ```
365    #[cfg(feature = "tmf629")]
366    pub fn tmf629(&mut self) -> TMF629 {
367        instantiate(&mut self.tmf629, self.config.clone())
368    }
369
370    /// Create access to TMF632 API
371    /// ```
372    /// # use tmf_client::TMFClient;
373    /// let tmf632 = TMFClient::new("http://localhost",None)
374    ///     .tmf632();
375    /// ```
376    #[cfg(feature = "tmf632")]
377    pub fn tmf632(&mut self) -> TMF632 {
378        instantiate(&mut self.tmf632, self.config.clone())
379    }
380
381    /// Create access to TMF633 API
382    /// ```
383    /// # use tmf_client::TMFClient;
384    /// let tmf633 = TMFClient::new("http://localhost",None)
385    ///     .tmf633();
386    /// ```
387    #[cfg(feature = "tmf633")]
388    pub fn tmf633(&mut self) -> TMF633 {
389        instantiate(&mut self.tmf633, self.config.clone())
390    }
391
392    /// Create access to TMF637 API
393    /// ```
394    /// # use tmf_client::TMFClient;
395    /// let tmf637 = TMFClient::new("http://localhost",None)
396    ///     .tmf637();
397    /// ```
398    #[cfg(feature = "tmf637")]
399    pub fn tmf637(&mut self) -> TMF637 {
400        instantiate(&mut self.tmf637, self.config.clone())
401    }
402
403    /// Create access to TMF638 API
404    /// ```
405    /// # use tmf_client::TMFClient;                
406    /// let tmf638 = TMFClient::new("http://localhost",None)
407    ///     .tmf638();
408    /// ```
409    #[cfg(feature = "tmf638")]
410    pub fn tmf638(&mut self) -> TMF638 {
411        instantiate(&mut self.tmf638, self.config.clone())
412    }
413
414    /// Create access to TMF639 API
415    /// ```
416    /// # use tmf_client::TMFClient;
417    /// let tmf639 = TMFClient::new("http://localhost",None)
418    ///     .tmf639();
419    /// ```
420    #[cfg(feature = "tmf639")]
421    pub fn tmf639(&mut self) -> TMF639 {
422        instantiate(&mut self.tmf639, self.config.clone())
423    }
424
425    /// Create access to TMF645 API
426    /// ```
427    /// # use tmf_client::TMFClient;
428    /// let tmf645 = TMFClient::new("http://localhost",None)
429    ///     .tmf645();
430    /// ```
431    #[cfg(feature = "tmf645")]
432    pub fn tmf645(&mut self) -> TMF645 {
433        instantiate(&mut self.tmf645, self.config.clone())
434    }
435
436    /// Create access to TMF648 API
437    /// ```
438    /// # use tmf_client::TMFClient;
439    /// let tmf648 = TMFClient::new("http://localhost",None)
440    ///     .tmf648();
441    /// ```
442    #[cfg(feature = "tmf648")]
443    pub fn tmf648(&mut self) -> TMF648 {
444        instantiate(&mut self.tmf648, self.config.clone())
445    }
446
447    /// Create access to TMF663 API
448    /// ```
449    /// # use tmf_client::TMFClient;
450    /// let tmf663 = TMFClient::new("http://localhost",None)
451    ///     .tmf663();
452    /// ```
453    #[cfg(feature = "tmf663")]
454    pub fn tmf663(&mut self) -> TMF663 {
455        instantiate(&mut self.tmf663, self.config.clone())
456    }
457
458    /// Create access to TMF674 API
459    /// ```
460    /// # use tmf_client::TMFClient;
461    /// let tmf674 = TMFClient::new("http://localhost",None)
462    ///     .tmf674();
463    /// ```
464    #[cfg(feature = "tmf674")]
465    pub fn tmf674(&mut self) -> TMF674 {
466        instantiate(&mut self.tmf674, self.config.clone())
467    }
468}
469
470#[cfg(test)]
471mod tests {
472    use super::*;
473    #[test]
474    fn test_filter_limit() {
475        let filter = QueryOptions::default().limit(111);
476
477        assert_eq!(filter.limit, Some(111));
478    }
479
480    #[test]
481    fn test_filter_offset() {
482        let filter = QueryOptions::default().offset(222);
483
484        assert_eq!(filter.offset, Some(222));
485    }
486}