diff --git a/TODO.md b/TODO.md index 44be832..df9e6e9 100644 --- a/TODO.md +++ b/TODO.md @@ -5,6 +5,7 @@ * [ ] Make `KafkaSource` generic, align with `Notifier` trait * [ ] Think about handling "by application" limitation. * [ ] RBAC -* [ ] Allow a way to modify the thing, overriding a non-empty outbox +* [x] Allow a way to modify the thing, overriding a non-empty outbox + * [ ] Allow more fine-grained control over this * [ ] Implement WASM -* [ ] Ensure that reported state "last updated" changes when only the value changes (move logic to machine) +* [x] Ensure that reported state "last updated" changes when only the value changes (move logic to machine) diff --git a/core/src/machine/mod.rs b/core/src/machine/mod.rs index 694c1fe..a072745 100644 --- a/core/src/machine/mod.rs +++ b/core/src/machine/mod.rs @@ -189,14 +189,11 @@ impl Reconciler { } pub async fn run(mut self) -> Result { - // clear reconcile waker - self.new_thing.clear_wakeup(WakerReason::Reconcile); + // cleanup first + self.cleanup(); - // clear old logs first, otherwise logging of state will continuously grow - // FIXME: remove when we only send a view of the state to the reconcile code - for (_, v) in &mut self.new_thing.reconciliation.changed { - v.last_log.clear(); - } + // detect reported state changes + self.sync_reported_state(); // synthetics self.generate_synthetics().await?; @@ -215,6 +212,29 @@ impl Reconciler { }) } + fn cleanup(&mut self) { + // clear reconcile waker + self.new_thing.clear_wakeup(WakerReason::Reconcile); + + // clear old logs first, otherwise logging of state will continuously grow + // FIXME: remove when we only send a view of the state to the reconcile code + for (_, v) in &mut self.new_thing.reconciliation.changed { + v.last_log.clear(); + } + } + + fn sync_reported_state(&mut self) { + // we ensure that all reported values which changed from the previous value get an updated + // last_update timestamp + for (k, next) in &mut self.new_thing.reported_state { + if let Some(previous) = self.current_thing.reported_state.get(k) { + if previous.value != next.value { + next.last_update = Utc::now(); + } + } + } + } + async fn generate_synthetics(&mut self) -> Result<(), Error> { let now = Utc::now(); diff --git a/core/src/service/updater.rs b/core/src/service/updater.rs index 7186bbf..4969f2b 100644 --- a/core/src/service/updater.rs +++ b/core/src/service/updater.rs @@ -58,7 +58,8 @@ impl InfallibleUpdater for ReportedStateUpdater { let e = e.get_mut(); if e.value != value { e.value = value; - e.last_update = Utc::now(); + // don't need to update the last_updated, as the system will ensure + // that for us later on } } Entry::Vacant(e) => { diff --git a/examples/10_basic.adoc b/examples/10_basic.adoc index 02e75c6..edfb11d 100644 --- a/examples/10_basic.adoc +++ b/examples/10_basic.adoc @@ -37,11 +37,21 @@ http PUT localhost:8080/api/v1alpha1/things/default/things/foo/reportedStates te == Patch a thing +With a JSON patch: + [source,shell] ---- http PATCH localhost:8080/api/v1alpha1/things/default/things/foo content-type:application/json-patch+json '[0][op]=replace' '[0][path]=/reportedState/temperature/value' '[0][value]=43' ---- +Or with a strategic merge: + +[source,shell] +---- +http PATCH localhost:8080/api/v1alpha1/things/default/things/foo content-type:application/merge-patch+json 'reportedState[temperature][value]:=43' +---- + + === Remove an annotation [source,shell]