Skip to content

Commit 548d6dc

Browse files
committed
Merge branch 'main' into metadata-and-discovery
2 parents 6c86db2 + e521068 commit 548d6dc

File tree

16 files changed

+816
-8
lines changed

16 files changed

+816
-8
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tokio-util = { version = "0.7.15", features = ["io"] }
2525
tokio-stream = { version = "0.1.17", features = ["io-util"] }
2626
futures = "0.3.31"
2727
tokio = { version = "1.44.2", features = ["io-util"] }
28+
typed-builder = "0.21.0"
2829

2930
[dev-dependencies]
3031
eventsourcingdb-client-rust = { path = ".", features = ["testcontainer"] }

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This is a work in progress and not yet ready for production use.
88
Based on the [compliance criteria](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/) the SDK covers these criteria:
99

1010
- 🚀 [Essentials](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/#essentials)
11-
- [Writing Events](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/#writing-events)
11+
- 🚀 [Writing Events](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/#writing-events)
1212
-[Reading Events](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/#reading-events)
1313
-[Using EventQL](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/#using-eventql)
1414
-[Observing Events](https://docs.eventsourcingdb.io/client-sdks/compliance-criteria/#observing-events)

src/client.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@
1818
//! If this works, it means that the client is correctly configured and you can use it to make requests to the DB.
1919
2020
mod client_request;
21+
mod precondition;
2122

2223
use client_request::{
2324
ClientRequest, ListEventTypesRequest, ListSubjectsRequest, OneShotRequest, PingRequest,
24-
RegisterEventSchemaRequest, StreamingRequest, VerifyApiTokenRequest,
25+
RegisterEventSchemaRequest, StreamingRequest, VerifyApiTokenRequest, WriteEventsRequest,
2526
list_event_types::EventType,
2627
};
2728

2829
use futures::Stream;
30+
pub use precondition::Precondition;
2931
use reqwest;
3032
use url::Url;
3133

32-
use crate::{error::ClientError, event::ManagementEvent};
34+
use crate::{
35+
error::ClientError,
36+
event::{Event, EventCandidate, ManagementEvent},
37+
};
3338

3439
/// Client for an [EventsourcingDB](https://www.eventsourcingdb.io/) instance.
3540
#[derive(Debug)]
@@ -79,6 +84,8 @@ impl Client {
7984
///
8085
/// This function will return a [`reqwest::RequestBuilder`] which can be used to send the request.
8186
///
87+
/// This function will return a [`reqwest::RequestBuilder`] which can be used to send the request.
88+
///
8289
/// # Errors
8390
/// This function will return an error if the request fails or if the URL is invalid.
8491
fn build_request<R: ClientRequest>(
@@ -233,4 +240,42 @@ impl Client {
233240
let response = self.request_streaming(ListEventTypesRequest).await?;
234241
Ok(response)
235242
}
243+
244+
/// Writes events to the DB instance.
245+
///
246+
/// ```
247+
/// use eventsourcingdb_client_rust::event::EventCandidate;
248+
/// # use serde_json::json;
249+
/// # tokio_test::block_on(async {
250+
/// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
251+
/// let db_url = "http://localhost:3000/";
252+
/// let api_token = "secrettoken";
253+
/// # let db_url = container.get_base_url().await.unwrap();
254+
/// # let api_token = container.get_api_token();
255+
/// let client = eventsourcingdb_client_rust::client::Client::new(db_url, api_token);
256+
/// let candidates = vec![
257+
/// EventCandidate::builder()
258+
/// .source("https://www.eventsourcingdb.io".to_string())
259+
/// .data(json!({"value": 1}))
260+
/// .subject("/test".to_string())
261+
/// .r#type("io.eventsourcingdb.test".to_string())
262+
/// .build()
263+
/// ];
264+
/// let written_events = client.write_events(candidates, vec![]).await.expect("Failed to write events");
265+
/// # })
266+
/// ```
267+
///
268+
/// # Errors
269+
/// This function will return an error if the request fails or if the URL is invalid.
270+
pub async fn write_events(
271+
&self,
272+
events: Vec<EventCandidate>,
273+
preconditions: Vec<Precondition>,
274+
) -> Result<Vec<Event>, ClientError> {
275+
self.request_oneshot(WriteEventsRequest {
276+
events,
277+
preconditions,
278+
})
279+
.await
280+
}
236281
}

src/client/client_request.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ mod list_subjects;
55
mod ping;
66
mod register_event_schema;
77
mod verify_api_token;
8+
mod write_events;
89

910
pub use list_event_types::ListEventTypesRequest;
1011
pub use list_subjects::ListSubjectsRequest;
1112
pub use ping::PingRequest;
1213
pub use register_event_schema::RegisterEventSchemaRequest;
1314
pub use verify_api_token::VerifyApiTokenRequest;
15+
pub use write_events::WriteEventsRequest;
1416

1517
use crate::error::ClientError;
1618
use futures::{Stream, stream::TryStreamExt};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use super::{ClientRequest, OneShotRequest};
2+
use crate::{
3+
client::Precondition,
4+
error::ClientError,
5+
event::{Event, EventCandidate},
6+
};
7+
use reqwest::Method;
8+
use serde::Serialize;
9+
10+
#[derive(Debug, Serialize)]
11+
pub struct WriteEventsRequest {
12+
pub events: Vec<EventCandidate>,
13+
pub preconditions: Vec<Precondition>,
14+
}
15+
16+
impl ClientRequest for WriteEventsRequest {
17+
const URL_PATH: &'static str = "/api/v1/write-events";
18+
const METHOD: Method = Method::POST;
19+
20+
fn body(&self) -> Option<Result<impl Serialize, ClientError>> {
21+
Some(Ok(self))
22+
}
23+
}
24+
impl OneShotRequest for WriteEventsRequest {
25+
type Response = Vec<Event>;
26+
}

src/client/precondition.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use serde::Serialize;
2+
3+
/// Enum for different preconditions that can be used when writing events
4+
#[derive(Debug, Serialize)]
5+
#[serde(tag = "type", content = "payload")]
6+
pub enum Precondition {
7+
/// Check if the subject with the given path has no other events
8+
#[serde(rename = "isSubjectPristine")]
9+
IsSubjectPristine {
10+
/// The subject to check
11+
subject: String,
12+
},
13+
/// Check if the subject with the given path has no other events
14+
#[serde(rename = "isSubjectOnEventId")]
15+
IsSubjectOnEventId {
16+
/// The subject to check
17+
subject: String,
18+
/// The event ID to check against
19+
#[serde(rename = "eventId")]
20+
event_id: String,
21+
},
22+
}

src/error.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ pub enum ClientError {
3030
/// There was a problem with the JSON serialization
3131
#[error("The JSON serialization failed: {0}")]
3232
SerdeJsonError(#[from] serde_json::Error),
33-
/// The DB returned an error
34-
#[error("The DB returned an error: {0}")]
35-
DBApiError(StatusCode, String),
3633
/// The DB returned an error in the response
3734
#[error("The DB returned an error in the response: {0}")]
3835
DBError(String),
36+
/// The DB returned an error
37+
#[error("The DB returned an error: {0}")]
38+
DBApiError(StatusCode, String),
3939
// check if this can hold a validation error in the future
4040
/// The passed jsonschema is invalid
4141
#[error("The passed jsonschema is invalid")]
@@ -59,3 +59,12 @@ pub enum ContainerError {
5959
#[error("URL parsing error: {0}")]
6060
URLParseError(#[from] url::ParseError),
6161
}
62+
63+
/// Error type for the event
64+
#[derive(Debug, thiserror::Error)]
65+
pub enum EventError {
66+
/// The passed cloudevent is invalid
67+
#[cfg(feature = "cloudevents")]
68+
#[error("The passed cloudevent is invalid")]
69+
InvalidCloudevent,
70+
}

src/event.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
//! This module holds all event types that are send between the client and the database.
22
3-
mod management_event;
3+
mod event_types;
4+
mod trace_info;
45

5-
pub use management_event::ManagementEvent;
6+
// Reexport relevant types to flatten the module graph for consumers and
7+
// keep private encapsulation of implementation details.
8+
pub use event_types::event::Event;
9+
pub use event_types::event_candidate::EventCandidate;
10+
pub use event_types::management_event::ManagementEvent;
11+
pub use trace_info::TraceInfo;
12+
13+
#[cfg(feature="cloudevents")]
14+
use crate::error::EventError;

src/event/event_types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//! This module holds all possible event types this sdk works with.
2+
3+
pub mod event;
4+
pub mod event_candidate;
5+
pub mod management_event;

0 commit comments

Comments
 (0)