Working modify internal and migrations

This commit is contained in:
William Brown 2019-01-28 13:54:17 +10:00
parent 764695db4a
commit 3b3f5dc6d5
9 changed files with 460 additions and 57 deletions

View file

@ -156,7 +156,7 @@ impl Drop for BackendTransaction {
impl BackendTransaction {
pub fn new(conn: r2d2::PooledConnection<SqliteConnectionManager>) -> Self {
// Start the transaction
println!("Starting txn ...");
println!("Starting RO txn ...");
// TODO: Way to flag that this will be a read?
conn.execute("BEGIN TRANSACTION", NO_PARAMS).unwrap();
BackendTransaction {
@ -196,7 +196,7 @@ impl BackendReadTransaction for BackendWriteTransaction {
impl BackendWriteTransaction {
pub fn new(conn: r2d2::PooledConnection<SqliteConnectionManager>) -> Self {
// Start the transaction
println!("Starting txn ...");
println!("Starting WR txn ...");
// TODO: Way to flag that this will be a write?
conn.execute("BEGIN TRANSACTION", NO_PARAMS).unwrap();
BackendWriteTransaction {
@ -273,8 +273,51 @@ impl BackendWriteTransaction {
})
}
pub fn modify() -> Result<(), BackendError> {
unimplemented!()
pub fn modify(&self, au: &mut AuditScope, entries: &Vec<Entry>) -> Result<(), BackendError> {
if entries.is_empty() {
// TODO: Better error
return Err(BackendError::EmptyRequest);
}
// Assert the Id's exist on the entry, and serialise them.
let ser_entries: Vec<IdEntry> = entries
.iter()
.filter_map(|e| {
match e.id {
Some(id) => {
Some(IdEntry {
id: id,
// TODO: Should we do better than unwrap?
data: serde_json::to_string(&e).unwrap(),
})
}
None => None
}
})
.collect();
audit_log!(au, "serialising: {:?}", ser_entries);
// Simple: If the list of id's is not the same as the input list, we are missing id's
// TODO: This check won't be needed once I rebuild the entry state types.
if entries.len() != ser_entries.len() {
return Err(BackendError::EntryMissingId);
}
// Now, given the list of id's, update them
{
// TODO: ACTUALLY HANDLE THIS ERROR WILLIAM YOU LAZY SHIT.
let mut stmt = self.conn.prepare("UPDATE id2entry SET data = :data WHERE id = :id").unwrap();
ser_entries.iter().for_each(|ser_ent| {
stmt.execute_named(&[
(":id", &ser_ent.id),
(":data", &ser_ent.data),
]).unwrap();
});
}
Ok(())
}
pub fn delete(&self, au: &mut AuditScope, entries: &Vec<Entry>) -> Result<(), BackendError> {
@ -282,7 +325,6 @@ impl BackendWriteTransaction {
if entries.is_empty() {
// TODO: Better error
// End the timer
return Err(BackendError::EmptyRequest);
}
@ -294,12 +336,11 @@ impl BackendWriteTransaction {
.collect();
// Simple: If the list of id's is not the same as the input list, we are missing id's
// TODO: This check won't be needed once I rebuild the entry state types.
if entries.len() != id_list.len() {
return Err(BackendError::EntryMissingId);
}
// Now, given the list of id's, delete them.
{
// SQL doesn't say if the thing "does or does not exist anymore". As a result,
@ -521,6 +562,19 @@ mod tests {
}}
}
macro_rules! entry_attr_pres {
($audit:expr, $be:expr, $ent:expr, $attr:expr) => {{
let filt = $ent.filter_from_attrs(&vec![String::from("userid")]).unwrap();
let entries = $be.search($audit, &filt).unwrap();
match entries.first() {
Some(ent) => {
ent.attribute_pres($attr)
}
None => false
}
}}
}
#[test]
fn test_simple_create() {
run_test!(|audit: &mut AuditScope, be: &BackendWriteTransaction| {
@ -553,6 +607,44 @@ mod tests {
fn test_simple_modify() {
run_test!(|audit: &mut AuditScope, be: &BackendWriteTransaction| {
audit_log!(audit, "Simple Modify");
// First create some entries (3?)
let mut e1: Entry = Entry::new();
e1.add_ava(String::from("userid"), String::from("william"));
let mut e2: Entry = Entry::new();
e2.add_ava(String::from("userid"), String::from("alice"));
assert!(be.create(audit, &vec![e1.clone(), e2.clone()]).is_ok());
assert!(entry_exists!(audit, be, e1));
assert!(entry_exists!(audit, be, e2));
// You need to now retrieve the entries back out to get the entry id's
let mut results = be.search(audit, &Filter::Pres(String::from("userid"))).unwrap();
// Get these out to usable entries.
let mut r1 = results.remove(0);
let mut r2 = results.remove(0);
// Modify no id (err)
assert!(be.modify(audit, &vec![e1.clone()]).is_err());
// Modify none
assert!(be.modify(audit, &vec![]).is_err());
// Make some changes to r1, r2.
r1.add_ava(String::from("desc"), String::from("modified"));
r2.add_ava(String::from("desc"), String::from("modified"));
// Modify single
assert!(be.modify(audit, &vec![r1.clone()]).is_ok());
// Assert no other changes
assert!(entry_attr_pres!(audit, be, r1, "desc"));
assert!(! entry_attr_pres!(audit, be, r2, "desc"));
// Modify both
assert!(be.modify(audit, &vec![r1.clone(), r2.clone()]).is_ok());
assert!(entry_attr_pres!(audit, be, r1, "desc"));
assert!(entry_attr_pres!(audit, be, r2, "desc"));
});
}

View file

@ -4,6 +4,8 @@ use filter::Filter;
use std::collections::btree_map::{Iter as BTreeIter, IterMut as BTreeIterMut};
use std::collections::BTreeMap;
use std::slice::Iter as SliceIter;
use modify::{Modify, ModifyList};
use schema::SchemaReadTransaction;
// make a trait entry for everything to adhere to?
// * How to get indexs out?
@ -94,9 +96,23 @@ impl<'a> Iterator for EntryAvasMut<'a> {
}
// This is a BE concept, so move it there!
// Entry should have a lifecycle of types. THis is Raw (modifiable) and Entry (verified).
// This way, we can move between them, but only certain actions are possible on either
// This means modifications happen on Raw, but to move to Entry, you schema normalise.
// Vice versa, you can for free, move to Raw, but you lose the validation.
// Because this is type system it's "free" in the end, and means we force validation
// at the correct and required points of the entries life.
// This is specifically important for the commit to the backend, as we only want to
// commit validated types.
#[derive(Serialize, Deserialize, Debug)]
pub struct Entry {
pub id: Option<i64>,
// Flag if we have been schema checked or not.
// pub schema_validated: bool,
attrs: BTreeMap<String, Vec<String>>,
}
@ -105,6 +121,9 @@ impl Entry {
Entry {
// This means NEVER COMMITED
id: None,
// TODO: Make this only on cfg(test/debug_assertions) builds.
// ALTERNATE: Convert to a different entry type on validate/normalise?
// CONVERT TO A TYPE
attrs: BTreeMap::new(),
}
}
@ -123,6 +142,7 @@ impl Entry {
// Here we need to actually do a check/binary search ...
// FIXME: Because map_err is lazy, this won't do anything on release
match v.binary_search(&value) {
// It already exists, done!
Ok(_) => {}
Err(idx) => {
// This cloning is to fix a borrow issue with the or_insert below.
@ -134,6 +154,29 @@ impl Entry {
.or_insert(vec![value]);
}
pub fn remove_ava(&mut self, attr: &String, value: &String) {
self.attrs
// TODO: Fix this clone ...
.entry(attr.clone())
.and_modify(|v| {
// Here we need to actually do a check/binary search ...
// FIXME: Because map_err is lazy, this won't do anything on release
match v.binary_search(&value) {
// It exists, rm it.
Ok(idx) => {
v.remove(idx);
}
// It does not exist, move on.
Err(_) => {
}
}
});
}
pub fn purge_ava(&mut self, attr: &String) {
self.attrs.remove(attr);
}
// FIXME: Should this collect from iter instead?
/// Overwrite the existing avas.
pub fn set_avas(&mut self, attr: String, values: Vec<String>) {
@ -250,6 +293,72 @@ impl Entry {
attrs: self.attrs.clone(),
}
}
pub fn gen_modlist_assert(&self, schema: &SchemaReadTransaction) -> Result<ModifyList, ()>
{
// Create a modlist from this entry. We make this assuming we want the entry
// to have this one as a subset of values. This means if we have single
// values, we'll replace, if they are multivalue, we present them.
//
// We assume the schema validaty of the entry is already checked, and
// normalisation performed.
let mut mods = ModifyList::new();
for (k, vs) in self.attrs.iter() {
// Get the schema attribute type out.
match schema.is_multivalue(k) {
Ok(r) => {
if !r {
// As this is single value, purge then present to maintain this
// invariant
mods.push_mod(Modify::Purged(k.clone()));
}
}
Err(e) => {
return Err(())
}
}
for v in vs {
mods.push_mod(Modify::Present(k.clone(), v.clone()));
}
}
Ok(mods)
}
// Should this be schemaless, relying on checks of the modlist, and the entry validate after?
pub fn apply_modlist(&self, modlist: &ModifyList) -> Result<Entry, ()> {
// Apply a modlist, generating a new entry that conforms to the changes.
// This is effectively clone-and-transform
// clone the entry
let mut ne = self.clone();
// mutate
for modify in modlist.mods.iter() {
match modify {
Modify::Present(a, v) => {
ne.add_ava(a.clone(), v.clone())
}
Modify::Removed(a, v) => {
ne.remove_ava(a, v)
}
Modify::Purged(a) => {
ne.purge_ava(a)
}
}
}
// return it
Ok(ne)
}
pub fn clone_no_attrs(&self) -> Entry {
Entry {
id: self.id,
attrs: BTreeMap::new(),
}
}
}
impl Clone for Entry {
@ -318,6 +427,7 @@ struct User {
#[cfg(test)]
mod tests {
use super::{Entry, User};
use modify::{Modify, ModifyList};
use serde_json;
#[test]
@ -349,7 +459,6 @@ mod tests {
#[test]
fn test_entry_pres() {
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"));
assert!(e.attribute_pres("userid"));
@ -367,4 +476,24 @@ mod tests {
assert!(!e.attribute_equality("nonexist", "william"));
}
#[test]
fn test_entry_apply_modlist() {
// Test application of changes to an entry.
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"));
let mods = ModifyList::new_list(vec![
Modify::Present(String::from("attr"), String::from("value")),
]);
let ne = e.apply_modlist(&mods).unwrap();
// Assert the changes are there
assert!(ne.attribute_equality("attr", "value"));
// Assert present for multivalue
// Assert purge on single/multi/empty value
// Assert removed on value that exists and doesn't exist
}
}

View file

@ -14,6 +14,7 @@ pub enum SchemaError {
pub enum OperationError {
EmptyRequest,
Backend,
NoMatchingEntries,
SchemaViolation,
Plugin,
FilterGeneration,

View file

@ -4,6 +4,7 @@ use super::proto_v1::{CreateRequest, Response, SearchRequest, SearchResponse};
use actix::prelude::*;
use entry::Entry;
use error::OperationError;
use modify::ModifyList;
// Should the event Result have the log items?
// FIXME: Remove seralising here - each type should
@ -70,6 +71,14 @@ impl SearchEvent {
class: (),
}
}
pub fn new_internal(filter: Filter) -> Self {
SearchEvent {
internal: true,
filter: filter,
class: (),
}
}
}
// Represents the decoded entries from the protocol -> internal entry representation
@ -156,3 +165,24 @@ impl DeleteEvent {
}
}
#[derive(Debug)]
pub struct ModifyEvent {
pub filter: Filter,
pub modlist: ModifyList,
pub internal: bool,
}
impl Message for ModifyEvent {
type Result = Result<OpResult, OperationError>;
}
impl ModifyEvent {
pub fn new_internal(filter: Filter, modlist: ModifyList) -> Self {
ModifyEvent {
filter: filter,
modlist: modlist,
internal: true,
}
}
}

View file

@ -20,6 +20,10 @@ pub enum Filter {
Not(Box<Filter>),
}
// Change this so you have RawFilter and Filter. RawFilter is the "builder", and then
// given a "schema" you can emit a Filter. For us internally, we can create Filter
// directly still ...
impl Filter {
// Does this need mut self? Aren't we returning
// a new copied filter?

View file

@ -42,6 +42,7 @@ mod identity;
mod plugins;
mod schema;
mod server;
mod modify;
pub mod config;
pub mod core;

View file

@ -20,7 +20,7 @@ pub struct Base {}
impl Plugin for Base {
fn id() -> &'static str {
"Base"
"plugin_base"
}
// Need to be given the backend(for testing ease)
// audit

View file

@ -9,13 +9,12 @@ use regex::Regex;
use std::convert::TryFrom;
use std::str::FromStr;
use uuid::Uuid;
use modify::ModifyList;
use concread::cowcell::{CowCell, CowCellReadTxn, CowCellWriteTxn};
// representations of schema that confines object types, classes
// and attributes. This ties in deeply with "Entry".
// This only defines the types, and how they are represented. For
// application and validation of the schema, see "Entry".
//
// In the future this will parse/read it's schema from the db
// but we have to bootstrap with some core types.
@ -32,6 +31,7 @@ use concread::cowcell::{CowCell, CowCellReadTxn, CowCellWriteTxn};
// probably just protection from delete and modify, except systemmay/systemmust/index?
// TODO: Schema types -> Entry conversion
// TODO: Entry -> Schema given class. This is for loading from the db.
// TODO: prefix on all schema types that are system?
@ -300,6 +300,34 @@ pub struct SchemaInner {
attributes: HashMap<String, SchemaAttribute>,
}
pub trait SchemaReadTransaction {
fn get_inner(&self) -> &SchemaInner;
fn validate(&self, audit: &mut AuditScope) -> Result<(), ()> {
self.get_inner().validate(audit)
}
fn validate_entry(&self, entry: &Entry) -> Result<(), SchemaError> {
self.get_inner().validate_entry(entry)
}
fn validate_filter(&self, filt: &Filter) -> Result<(), SchemaError> {
self.get_inner().validate_filter(filt)
}
fn normalise_entry(&self, entry: &Entry) -> Entry {
self.get_inner().normalise_entry(entry)
}
fn normalise_modlist(&self, modlist: &ModifyList) -> ModifyList {
unimplemented!()
}
fn is_multivalue(&self, attr: &str) -> Result<bool, SchemaError> {
self.get_inner().is_multivalue(attr)
}
}
impl SchemaInner {
pub fn new(audit: &mut AuditScope) -> Result<Self, ()> {
let mut au = AuditScope::new("schema_new");
@ -941,13 +969,13 @@ impl SchemaInner {
Ok(())
}
// pub fn normalise_entry(&mut self, entry: &mut Entry) -> Result<(), SchemaError> {
// TODO: Restructure this when we change entry lifecycle types.
pub fn normalise_entry(&self, entry: &Entry) -> Entry {
// We duplicate the entry here, because we can't
// modify what we got on the protocol level. It also
// lets us extend and change things.
let mut entry_new: Entry = Entry::new();
let mut entry_new: Entry = entry.clone_no_attrs();
// Better hope we have the attribute type ...
let schema_attr_name = self.attributes.get("name").unwrap();
// For each ava
@ -972,7 +1000,10 @@ impl SchemaInner {
// now push those to the new entry.
entry_new.set_avas(attr_name_normal, avas_normal);
}
// Mark it is valid
// entry_new.schema_validated = true;
// Done!
// TODO: Convert the entry type here to a validated type.
entry_new
}
@ -1035,6 +1066,17 @@ impl SchemaInner {
pub fn normalise_filter(&mut self) {
unimplemented!()
}
fn is_multivalue(&self, attr_name: &str) -> Result<bool, SchemaError> {
match self.attributes.get(attr_name) {
Some(a_schema) => {
Ok(a_schema.multivalue)
}
None => {
return Err(SchemaError::InvalidAttribute);
}
}
}
}
// type Schema = CowCell<SchemaInner>;
@ -1052,14 +1094,6 @@ impl<'a> SchemaWriteTransaction<'a> {
self.inner.bootstrap_core(audit)
}
pub fn validate_entry(&self, entry: &Entry) -> Result<(), SchemaError> {
self.inner.validate_entry(entry)
}
pub fn normalise_entry(&self, entry: &Entry) -> Entry {
self.inner.normalise_entry(entry)
}
// TODO: Schema probably needs to be part of the backend, so that commits are wholly atomic
// but in the current design, we need to open be first, then schema, but we have to commit be
// first, then schema to ensure that the be content matches our schema. Saying this, if your
@ -1068,13 +1102,12 @@ impl<'a> SchemaWriteTransaction<'a> {
pub fn commit(self) {
self.inner.commit();
}
}
pub fn validate_filter(&self, filt: &Filter) -> Result<(), SchemaError> {
self.inner.validate_filter(filt)
}
pub fn validate(&self, audit: &mut AuditScope) -> Result<(), ()> {
self.inner.validate(audit)
impl<'a> SchemaReadTransaction for SchemaWriteTransaction<'a> {
fn get_inner(&self) -> &SchemaInner {
// Does this deref the CowCell for us?
&self.inner
}
}
@ -1082,17 +1115,10 @@ pub struct SchemaTransaction {
inner: CowCellReadTxn<SchemaInner>,
}
impl SchemaTransaction {
pub fn validate(&self, audit: &mut AuditScope) -> Result<(), ()> {
self.inner.validate(audit)
}
pub fn validate_entry(&self, entry: &Entry) -> Result<(), SchemaError> {
self.inner.validate_entry(entry)
}
pub fn validate_filter(&self, filt: &Filter) -> Result<(), SchemaError> {
self.inner.validate_filter(filt)
impl SchemaReadTransaction for SchemaTransaction {
fn get_inner(&self) -> &SchemaInner {
// Does this deref the CowCell for us?
&self.inner
}
}
@ -1124,6 +1150,7 @@ mod tests {
use super::super::error::SchemaError;
use super::super::filter::Filter;
use super::{IndexType, Schema, SchemaAttribute, SchemaClass, SyntaxType};
use schema::SchemaReadTransaction;
use serde_json;
use std::convert::TryFrom;
use uuid::Uuid;
@ -1373,10 +1400,11 @@ mod tests {
)
.unwrap();
assert_eq!(
schema.validate_entry(&e_attr_invalid),
Err(SchemaError::MissingMustAttribute(String::from("system")))
);
let res = schema.validate_entry(&e_attr_invalid);
assert!(match res {
Err(SchemaError::MissingMustAttribute(_)) => true,
_ => false,
});
let e_attr_invalid_may: Entry = serde_json::from_str(
r#"{

View file

@ -12,11 +12,13 @@ use be::{
use constants::{JSON_ANONYMOUS_V1, JSON_SYSTEM_INFO_V1};
use entry::Entry;
use error::OperationError;
use event::{CreateEvent, ExistsEvent, OpResult, SearchEvent, SearchResult, DeleteEvent};
use event::{CreateEvent, ExistsEvent, OpResult, SearchEvent, SearchResult, DeleteEvent, ModifyEvent};
use filter::Filter;
use log::EventLog;
use plugins::Plugins;
use schema::{Schema, SchemaTransaction, SchemaWriteTransaction};
use schema::{Schema, SchemaTransaction, SchemaReadTransaction, SchemaWriteTransaction};
use modify::ModifyList;
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
let mut audit = AuditScope::new("server_start");
@ -139,8 +141,13 @@ pub trait QueryServerReadTransaction {
res
}
fn internal_search(&self, _au: &mut AuditScope, _filter: Filter) -> Result<Vec<Entry>, OperationError> {
unimplemented!()
fn internal_search(&self, audit: &mut AuditScope, filter: Filter) -> Result<Vec<Entry>, OperationError> {
let mut audit_int = AuditScope::new("internal_search");
let se = SearchEvent::new_internal(filter);
let res = self.search(&mut audit_int, &se);
audit.append_scope(audit_int);
res
}
}
@ -313,6 +320,90 @@ impl<'a> QueryServerWriteTransaction<'a> {
unimplemented!()
}
pub fn modify(&self, au: &mut AuditScope, me: &ModifyEvent) -> Result<(), OperationError> {
// Get the candidates.
// Modify applies a modlist to a filter, so we need to internal search
// then apply.
// WARNING! Check access controls here!!!!
// How can we do the search with the permissions of the caller?
// TODO: Fix this filter clone ....
// Likely this will be fixed if search takes &filter, and then clone
// to normalise, instead of attempting to mut the filter on norm.
let pre_candidates = match self.internal_search(au, me.filter.clone()) {
Ok(results) => results,
Err(e) => return Err(e),
};
if pre_candidates.len() == 0 {
return Err(OperationError::NoMatchingEntries)
};
// Clone a set of writeables.
// Apply the modlist -> Remember, we have a set of origs
// and the new modified ents.
let mut candidates: Vec<Entry> = pre_candidates.iter()
.map(|er| {
er.apply_modlist(&me.modlist).unwrap()
})
.collect();
audit_log!(au, "modify: candidates -> {:?}", candidates);
// Pre mod plugins
// Schema validate
let r = candidates.iter().fold(Ok(()), |acc, e| {
if acc.is_ok() {
self.schema
.validate_entry(e)
.map_err(|err| {
audit_log!(au, "Schema Violation: {:?}", err);
OperationError::SchemaViolation
})
} else {
acc
}
});
if r.is_err() {
audit_log!(au, "Modify operation failed (schema), {:?}", r);
return r;
}
// Normalise all the data now it's validated.
// FIXME: This normalisation COPIES everything, which may be
// slow.
let norm_cand: Vec<Entry> = candidates
.iter()
.map(|e| self.schema.normalise_entry(&e))
.collect();
// Backend Modify
let mut audit_be = AuditScope::new("backend_modify");
let res = self
.be_txn
.modify(&mut audit_be, &norm_cand)
.map(|_| ())
.map_err(|e| match e {
BackendError::EmptyRequest => OperationError::EmptyRequest,
BackendError::EntryMissingId => OperationError::InvalidRequestState,
});
au.append_scope(audit_be);
if res.is_err() {
// be_txn is dropped, ie aborted here.
audit_log!(au, "Modify operation failed (backend), {:?}", r);
return res;
}
// Post Plugins
// return
audit_log!(au, "Modify operation success");
res
}
// These are where searches and other actions are actually implemented. This
// is the "internal" version, where we define the event as being internal
// only, allowing certain plugin by passes etc.
@ -343,6 +434,19 @@ impl<'a> QueryServerWriteTransaction<'a> {
res
}
pub fn internal_modify(
&self,
audit: &mut AuditScope,
filter: Filter,
modlist: ModifyList
) -> Result<(), OperationError> {
let mut audit_int = AuditScope::new("internal_modify");
let me = ModifyEvent::new_internal(filter, modlist);
let res = self.modify(&mut audit_int, &me);
audit.append_scope(audit_int);
res
}
// internal server operation types.
// These just wrap the fn create/search etc, but they allow
// creating the needed create event with the correct internal flags
@ -370,20 +474,32 @@ impl<'a> QueryServerWriteTransaction<'a> {
// few attributes.
//
// This will extra classes an attributes alone!
let filt = match e.filter_from_attrs(&vec![String::from("name"), String::from("uuid")]) {
let filt = match e.filter_from_attrs(&vec![String::from("uuid")]) {
Some(f) => f,
None => return Err(OperationError::FilterGeneration),
};
// Does it exist? (TODO: Should be search, not exists ...)
match self.internal_exists(audit, filt) {
Ok(true) => {
// it exists. We need to ensure the content now.
unimplemented!()
}
Ok(false) => {
match self.internal_search(audit, filt.clone()) {
Ok(results) => {
if results.len() == 0 {
// It does not exist. Create it.
self.internal_create(audit, vec![e])
} else if results.len() == 1 {
// If the thing is subset, pass
match e.gen_modlist_assert(&self.schema) {
Ok(modlist) => {
// Apply to &results[0]
audit_log!(audit, "Generated modlist -> {:?}", modlist);
self.internal_modify(audit, filt, modlist)
}
Err(e) => {
unimplemented!()
// No action required.
}
}
} else {
Err(OperationError::InvalidDBState)
}
}
Err(e) => {
// An error occured. pass it back up.
@ -403,7 +519,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
// attributes and classes.
// Create a filter from the entry for assertion.
let filt = match e.filter_from_attrs(&vec![String::from("name"), String::from("uuid")]) {
let filt = match e.filter_from_attrs(&vec![String::from("uuid")]) {
Some(f) => f,
None => return Err(OperationError::FilterGeneration),
};
@ -441,6 +557,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
let e: Entry = serde_json::from_str(JSON_SYSTEM_INFO_V1).unwrap();
self.internal_assert_or_create(audit, e)
});
audit_log!(audit_si, "start_system_info -> result {:?}", res);
audit.append_scope(audit_si);
assert!(res.is_ok());
if res.is_err() {
@ -453,6 +570,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
let e: Entry = serde_json::from_str(JSON_ANONYMOUS_V1).unwrap();
self.internal_migrate_or_create(audit, e)
});
audit_log!(audit_an, "start_anonymous -> result {:?}", res);
audit.append_scope(audit_an);
assert!(res.is_ok());
if res.is_err() {