Skip to content

Commit

Permalink
rust: Improve NewObject::get_or_create to properly upsert objects
Browse files Browse the repository at this point in the history
  • Loading branch information
YannikSc committed Jul 4, 2024
1 parent 961cec6 commit 83a7a50
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 55 deletions.
58 changes: 29 additions & 29 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ serde_json = "1.0"
sha1 = "0.10"
signature = "2.2"
ssh-agent-client-rs = "0.9"
ssh-encoding = { version = "0.2.0", features = ["base64"] }
ssh-encoding = { version = "0.2", features = ["base64"] }
ssh-key = { version = "0.6", features = ["serde", "ed25519", "dsa", "crypto", "rsa"] }

[dev-dependencies]
env_logger = "0.11"
tokio = { version = "1.37", features = ["full"] }
tokio = { version = "1.38", features = ["full"] }
clap = "4.5"
futures = "0.3"

Expand Down
8 changes: 4 additions & 4 deletions rust/examples/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ pub async fn main() -> anyhow::Result<()> {

let mut new_sg = NewObject::request_new("service_group").await?;
new_sg
.set("hostname", "yannik-adminapi-rs-2.test.sg")
.set("project", "test")
.add("responsible_admin", "yannik.schwieger")
.add("protocol_ports_inbound", "tcp443");
.set("hostname", "yannik-adminapi-rs-2.test.sg")?
.set("project", "test")?
.add("responsible_admin", "yannik.schwieger")?
.add("protocol_ports_inbound", "tcp443")?;
let mut created_sg = new_sg.commit().await?;
created_sg
.add("protocol_ports_inbound", "tcp80")?
Expand Down
2 changes: 1 addition & 1 deletion rust/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl Server {

if self.attributes.get(&attribute).is_array() {
return Err(anyhow::anyhow!(
"Attribute is a multi attribute, set is not supported!"
"Attribute {attribute} is a multi attribute, set is not supported!"
));
}

Expand Down
16 changes: 15 additions & 1 deletion rust/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,21 @@ impl Commit {
self
}

pub fn update(mut self, changeset: Changeset) -> Self {
pub fn update(mut self, mut changeset: Changeset) -> Self {
let filtered = changeset
.attributes
.into_iter()
.filter(|(_, change)| {
if let AttributeChange::Update { new, old } = change {
return new.ne(old);
}

true
})
.collect();

changeset.attributes = filtered;

self.changed.push(changeset);

self
Expand Down
48 changes: 30 additions & 18 deletions rust/src/new_object.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::ops::{Deref, DerefMut};

use crate::api::{commit_changes, new_object, NewObjectResponse, Server};
use crate::commit::{AttributeValue, Changeset, Commit, Dataset};
use crate::commit::{AttributeValue, Changeset, Commit};
use crate::query::Query;

#[derive(Clone, Debug)]
pub struct NewObject {
object_id: Option<u64>,
attributes: Dataset,
changeset: Changeset,
server: Server,
deferred_changes: Changeset,
}

impl NewObject {
Expand All @@ -18,8 +18,12 @@ impl NewObject {

Ok(Self {
object_id: None,
attributes: result,
changeset: Default::default(),
server: Server {
object_id: 0,
attributes: result,
changes: Default::default(),
},
deferred_changes: Default::default(),
})
}

Expand All @@ -31,31 +35,39 @@ impl NewObject {

if let Ok(server) = Query::builder()
.filter("hostname", hostname.to_string())
.restrict(new_object.attributes.keys())
.restrict(new_object.server.attributes.keys())
.build()
.request()
.await?
.one()
{
new_object.object_id = Some(server.object_id);
new_object.attributes = server.attributes;
new_object.server = server;
}

Ok(new_object)
}

pub fn is_new(&self) -> bool {
self.object_id.is_none()
}

///
/// Commits the new object
///
/// The changes done in [NewObject::deferred] will not be submitted yet, but the returned [Server]
/// object is preloaded with them.
///
pub async fn commit(self) -> anyhow::Result<Server> {
let AttributeValue::String(hostname) = self.attributes.get("hostname") else {
pub async fn commit(mut self) -> anyhow::Result<Server> {
let AttributeValue::String(hostname) = self.server.get("hostname") else {
return Err(anyhow::anyhow!("Required attribute 'hostname' is missing"));
};
let commit = Commit::new().create(self.attributes);
commit_changes(&commit).await?;

if self.is_new() {
commit_changes(&Commit::new().create(self.server.attributes)).await?;
} else {
self.server.commit().await?;
}

let mut server = Query::builder()
.filter("hostname", hostname)
Expand All @@ -64,7 +76,7 @@ impl NewObject {
.await?
.one()?;

server.changes = self.changeset;
server.changes = self.deferred_changes;

Ok(server)
}
Expand All @@ -82,28 +94,28 @@ impl NewObject {
pub fn deferred<R>(&mut self, callback: impl FnOnce(&mut Server) -> R) -> R {
let mut server = Server {
object_id: 0,
attributes: self.attributes.clone(),
changes: std::mem::take(&mut self.changeset),
attributes: self.server.attributes.clone(),
changes: std::mem::take(&mut self.deferred_changes),
};

let output = callback(&mut server);

self.changeset = std::mem::take(&mut server.changes);
self.deferred_changes = std::mem::take(&mut server.changes);

output
}
}

impl Deref for NewObject {
type Target = Dataset;
type Target = Server;

fn deref(&self) -> &Self::Target {
&self.attributes
&self.server
}
}

impl DerefMut for NewObject {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.attributes
&mut self.server
}
}

0 comments on commit 83a7a50

Please sign in to comment.