2020mod client_request;
2121mod precondition;
2222
23+ use crate :: {
24+ error:: ClientError ,
25+ event:: { Event , EventCandidate , ManagementEvent } ,
26+ } ;
2327use client_request:: {
24- ClientRequest , OneShotRequest , PingRequest , VerifyApiTokenRequest , WriteEventsRequest ,
28+ ClientRequest , ListEventTypesRequest , ListSubjectsRequest , OneShotRequest , PingRequest ,
29+ RegisterEventSchemaRequest , StreamingRequest , VerifyApiTokenRequest , WriteEventsRequest ,
30+ list_event_types:: EventType ,
2531} ;
26-
32+ use futures :: Stream ;
2733pub use precondition:: Precondition ;
2834use reqwest;
2935use url:: Url ;
3036
31- use crate :: {
32- error:: ClientError ,
33- event:: { Event , EventCandidate } ,
34- } ;
35-
3637/// Client for an [EventsourcingDB](https://www.eventsourcingdb.io/) instance.
3738#[ derive( Debug ) ]
3839pub struct Client {
3940 base_url : Url ,
4041 api_token : String ,
41- client : reqwest:: Client ,
42+ reqwest : reqwest:: Client ,
4243}
4344
4445impl Client {
@@ -47,7 +48,7 @@ impl Client {
4748 Client {
4849 base_url,
4950 api_token : api_token. into ( ) ,
50- client : reqwest:: Client :: new ( ) ,
51+ reqwest : reqwest:: Client :: new ( ) ,
5152 }
5253 }
5354
@@ -93,8 +94,8 @@ impl Client {
9394 . map_err ( ClientError :: URLParseError ) ?;
9495
9596 let request = match endpoint. method ( ) {
96- reqwest:: Method :: GET => self . client . get ( url) ,
97- reqwest:: Method :: POST => self . client . post ( url) ,
97+ reqwest:: Method :: GET => self . reqwest . get ( url) ,
98+ reqwest:: Method :: POST => self . reqwest . post ( url) ,
9899 _ => return Err ( ClientError :: InvalidRequestMethod ) ,
99100 }
100101 . header ( "Authorization" , format ! ( "Bearer {}" , self . api_token) ) ;
@@ -132,6 +133,28 @@ impl Client {
132133 }
133134 }
134135
136+ /// Utility function to request an endpoint of the API as a stream.
137+ ///
138+ /// This means, that the response is streamed and returned as a stream of values.
139+ ///
140+ /// # Errors
141+ /// This function will return an error if the request fails or if the URL is invalid.
142+ async fn request_streaming < R : StreamingRequest > (
143+ & self ,
144+ endpoint : R ,
145+ ) -> Result < impl Stream < Item = Result < R :: ItemType , ClientError > > , ClientError > {
146+ let response = self . build_request ( & endpoint) ?. send ( ) . await ?;
147+
148+ if response. status ( ) . is_success ( ) {
149+ Ok ( endpoint. build_stream ( response) )
150+ } else {
151+ Err ( ClientError :: DBApiError (
152+ response. status ( ) ,
153+ response. text ( ) . await . unwrap_or_default ( ) ,
154+ ) )
155+ }
156+ }
157+
135158 /// Pings the DB instance to check if it is reachable.
136159 ///
137160 /// ```
@@ -174,6 +197,130 @@ impl Client {
174197 Ok ( ( ) )
175198 }
176199
200+ /// Registers an event schema with the DB instance.
201+ ///
202+ /// ```
203+ /// use eventsourcingdb_client_rust::event::EventCandidate;
204+ /// use futures::StreamExt;
205+ /// # use serde_json::json;
206+ /// # tokio_test::block_on(async {
207+ /// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
208+ /// let db_url = "http://localhost:3000/";
209+ /// let api_token = "secrettoken";
210+ /// # let db_url = container.get_base_url().await.unwrap();
211+ /// # let api_token = container.get_api_token();
212+ /// let client = eventsourcingdb_client_rust::client::Client::new(db_url, api_token);
213+ /// let event_type = "io.eventsourcingdb.test";
214+ /// let schema = json!({
215+ /// "type": "object",
216+ /// "properties": {
217+ /// "id": {
218+ /// "type": "string"
219+ /// },
220+ /// "name": {
221+ /// "type": "string"
222+ /// }
223+ /// },
224+ /// "required": ["id", "name"]
225+ /// });
226+ /// client.register_event_schema(event_type, &schema).await.expect("Failed to list event types");
227+ /// # })
228+ /// ```
229+ ///
230+ /// # Errors
231+ /// This function will return an error if the request fails or if the provided schema is invalid.
232+ pub async fn register_event_schema (
233+ & self ,
234+ event_type : & str ,
235+ schema : & serde_json:: Value ,
236+ ) -> Result < ManagementEvent , ClientError > {
237+ self . request_oneshot ( RegisterEventSchemaRequest :: try_new ( event_type, schema) ?)
238+ . await
239+ }
240+
241+ /// List all subjects in the DB instance.
242+ ///
243+ /// To get all subjects in the DB, just pass `None` as the `base_subject`.
244+ /// ```
245+ /// use eventsourcingdb_client_rust::event::EventCandidate;
246+ /// use futures::StreamExt;
247+ /// # use serde_json::json;
248+ /// # tokio_test::block_on(async {
249+ /// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
250+ /// let db_url = "http://localhost:3000/";
251+ /// let api_token = "secrettoken";
252+ /// # let db_url = container.get_base_url().await.unwrap();
253+ /// # let api_token = container.get_api_token();
254+ /// let client = eventsourcingdb_client_rust::client::Client::new(db_url, api_token);
255+ /// let mut subject_stream = client.list_subjects(None).await.expect("Failed to list event types");
256+ /// while let Some(subject) = subject_stream.next().await {
257+ /// println!("Found Type {}", subject.expect("Error while reading types"));
258+ /// }
259+ /// # })
260+ /// ```
261+ ///
262+ /// To get all subjects under /test in the DB, just pass `Some("/test")` as the `base_subject`.
263+ /// ```
264+ /// use eventsourcingdb_client_rust::event::EventCandidate;
265+ /// use futures::StreamExt;
266+ /// # use serde_json::json;
267+ /// # tokio_test::block_on(async {
268+ /// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
269+ /// let db_url = "http://localhost:3000/";
270+ /// let api_token = "secrettoken";
271+ /// # let db_url = container.get_base_url().await.unwrap();
272+ /// # let api_token = container.get_api_token();
273+ /// let client = eventsourcingdb_client_rust::client::Client::new(db_url, api_token);
274+ /// let mut subject_stream = client.list_subjects(Some("/test")).await.expect("Failed to list event types");
275+ /// while let Some(subject) = subject_stream.next().await {
276+ /// println!("Found Type {}", subject.expect("Error while reading types"));
277+ /// }
278+ /// # })
279+ /// ```
280+ ///
281+ /// # Errors
282+ /// This function will return an error if the request fails or if the URL is invalid.
283+ pub async fn list_subjects (
284+ & self ,
285+ base_subject : Option < & str > ,
286+ ) -> Result < impl Stream < Item = Result < String , ClientError > > , ClientError > {
287+ let response = self
288+ . request_streaming ( ListSubjectsRequest {
289+ base_subject : base_subject. unwrap_or ( "/" ) ,
290+ } )
291+ . await ?;
292+ Ok ( response)
293+ }
294+
295+ /// List all event types in the DB instance.
296+ ///
297+ /// ```
298+ /// use eventsourcingdb_client_rust::event::EventCandidate;
299+ /// use futures::StreamExt;
300+ /// # use serde_json::json;
301+ /// # tokio_test::block_on(async {
302+ /// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
303+ /// let db_url = "http://localhost:3000/";
304+ /// let api_token = "secrettoken";
305+ /// # let db_url = container.get_base_url().await.unwrap();
306+ /// # let api_token = container.get_api_token();
307+ /// let client = eventsourcingdb_client_rust::client::Client::new(db_url, api_token);
308+ /// let mut type_stream = client.list_event_types().await.expect("Failed to list event types");
309+ /// while let Some(ty) = type_stream.next().await {
310+ /// println!("Found Type {}", ty.expect("Error while reading types").name);
311+ /// }
312+ /// # })
313+ /// ```
314+ ///
315+ /// # Errors
316+ /// This function will return an error if the request fails or if the URL is invalid.
317+ pub async fn list_event_types (
318+ & self ,
319+ ) -> Result < impl Stream < Item = Result < EventType , ClientError > > , ClientError > {
320+ let response = self . request_streaming ( ListEventTypesRequest ) . await ?;
321+ Ok ( response)
322+ }
323+
177324 /// Writes events to the DB instance.
178325 ///
179326 /// ```
0 commit comments