feat/orm
Laurin Weger 1 week ago
parent 82de4660f3
commit 35dc5e2c93
No known key found for this signature in database
GPG Key ID: 9B372BB0B792770F
  1. 31
      Cargo.toml
  2. 440
      nextgraph/src/tests/orm.rs
  3. 33
      ng-verifier/src/orm/add_remove_triples.rs
  4. 58
      ng-verifier/src/orm/mod.rs
  5. 4
      ng-verifier/src/orm/types.rs
  6. 65
      ng-verifier/src/orm/validation.rs

@ -23,10 +23,31 @@ authors = ["Niko PLP <niko@nextgraph.org>"]
repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs" repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs"
homepage = "https://nextgraph.org" homepage = "https://nextgraph.org"
keywords = [ keywords = [
"crdt","dapp","decentralized","e2ee","local-first","p2p","semantic-web","eventual-consistency","json-ld","markdown", "crdt",
"ocap","vc","offline-first","p2p-network","collaboration","privacy-protection","rdf","rich-text-editor","self-hosted", "dapp",
"sparql","byzantine-fault-tolerance", "decentralized",
"web3", "graph-database", "database","triplestore" "e2ee",
"local-first",
"p2p",
"semantic-web",
"eventual-consistency",
"json-ld",
"markdown",
"ocap",
"vc",
"offline-first",
"p2p-network",
"collaboration",
"privacy-protection",
"rdf",
"rich-text-editor",
"self-hosted",
"sparql",
"byzantine-fault-tolerance",
"web3",
"graph-database",
"database",
"triplestore",
] ]
documentation = "https://docs.nextgraph.org/" documentation = "https://docs.nextgraph.org/"
@ -35,7 +56,7 @@ lto = true
opt-level = 's' opt-level = 's'
[profile.dev] [profile.dev]
opt-level = 2 opt-level = 1
[patch.crates-io] [patch.crates-io]
# tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup"} # tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup"}

@ -12,8 +12,8 @@ use crate::tests::create_or_open_wallet::create_or_open_wallet;
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use ng_net::app_protocol::{AppResponse, AppResponseV0, NuriV0}; use ng_net::app_protocol::{AppResponse, AppResponseV0, NuriV0};
use ng_net::orm::{ use ng_net::orm::{
BasicType, OrmSchemaDataType, OrmSchemaLiteralType, OrmSchemaPredicate, OrmSchemaShape, BasicType, OrmSchema, OrmSchemaDataType, OrmSchemaLiteralType, OrmSchemaPredicate,
OrmShapeType, OrmSchemaShape, OrmShapeType,
}; };
use ng_verifier::orm::utils::shape_type_to_sparql; use ng_verifier::orm::utils::shape_type_to_sparql;
@ -69,222 +69,7 @@ INSERT DATA {
.await .await
.expect("SPARQL update failed"); .expect("SPARQL update failed");
// Define the ORM schema let schema = create_big_schema();
let mut schema = HashMap::new();
schema.insert(
"http://example.org/TestObject||http://example.org/anotherObject".to_string(),
Arc::new(OrmSchemaShape {
iri: "http://example.org/TestObject||http://example.org/anotherObject".to_string(),
predicates: vec![
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::string,
literals: None,
shape: None,
}],
iri: "http://example.org/prop1".to_string(),
readablePredicate: "prop1".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/prop2".to_string(),
readablePredicate: "prop2".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
],
}),
);
schema.insert(
"http://example.org/TestObject".to_string(),
Arc::new(OrmSchemaShape {
iri: "http://example.org/TestObject".to_string(),
predicates: vec![
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::literal,
literals: Some(vec![BasicType::Str(
"http://example.org/TestObject".to_string(),
)]),
shape: None,
}],
iri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
readablePredicate: "type".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: Some(true),
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::string,
literals: None,
shape: None,
}],
iri: "http://example.org/stringValue".to_string(),
readablePredicate: "stringValue".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/numValue".to_string(),
readablePredicate: "numValue".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::boolean,
literals: None,
shape: None,
}],
iri: "http://example.org/boolValue".to_string(),
readablePredicate: "boolValue".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/arrayValue".to_string(),
readablePredicate: "arrayValue".to_string(),
maxCardinality: -1,
minCardinality: 0,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::shape,
literals: None,
shape: Some(
"http://example.org/TestObject||http://example.org/objectValue"
.to_string(),
),
}],
iri: "http://example.org/objectValue".to_string(),
readablePredicate: "objectValue".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::shape,
literals: None,
shape: Some(
"http://example.org/TestObject||http://example.org/anotherObject"
.to_string(),
),
}],
iri: "http://example.org/anotherObject".to_string(),
readablePredicate: "anotherObject".to_string(),
maxCardinality: -1,
minCardinality: 0,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![
OrmSchemaDataType {
valType: OrmSchemaLiteralType::string,
literals: None,
shape: None,
},
OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
},
],
iri: "http://example.org/numOrStr".to_string(),
readablePredicate: "numOrStr".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::literal,
literals: Some(vec![
BasicType::Str("lit1".to_string()),
BasicType::Str("lit2".to_string()),
]),
shape: None,
}],
iri: "http://example.org/lit1Or2".to_string(),
readablePredicate: "lit1Or2".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
],
}),
);
schema.insert(
"http://example.org/TestObject||http://example.org/objectValue".to_string(),
Arc::new(OrmSchemaShape {
iri: "http://example.org/TestObject||http://example.org/objectValue".to_string(),
predicates: vec![
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::string,
literals: None,
shape: None,
}],
iri: "http://example.org/nestedString".to_string(),
readablePredicate: "nestedString".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/nestedNum".to_string(),
readablePredicate: "nestedNum".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/nestedArray".to_string(),
readablePredicate: "nestedArray".to_string(),
maxCardinality: -1,
minCardinality: 0,
extra: None,
}),
],
}),
);
let shape_type = OrmShapeType { let shape_type = OrmShapeType {
schema, schema,
shape: "http://example.org/TestObject".to_string(), shape: "http://example.org/TestObject".to_string(),
@ -581,6 +366,29 @@ INSERT DATA {
ex:lit1Or2 "lit1" ; ex:lit1Or2 "lit1" ;
ex:unrelated "some value" ; ex:unrelated "some value" ;
ex:anotherUnrelated 4242 . ex:anotherUnrelated 4242 .
<urn:test:obj2> a ex:TestObject ;
ex:stringValue "hello world #2" ;
ex:numValue 422 ;
ex:boolValue false ;
ex:arrayValue 4,5,6 ;
ex:objectValue [
ex:nestedString "nested2" ;
ex:nestedNum 72 ;
ex:nestedArray 7,8,9
] ;
ex:anotherObject [
ex:prop1 "one2" ;
ex:prop2 12
], [
ex:prop1 "two2" ;
ex:prop2 22
] ;
ex:numOrStr 4 ;
ex:lit1Or2 "lit2" ;
ex:unrelated "some value2" ;
ex:anotherUnrelated 42422 .
} }
"# "#
.to_string(); .to_string();
@ -590,8 +398,47 @@ INSERT DATA {
.expect("SPARQL update failed"); .expect("SPARQL update failed");
// Define the ORM schema // Define the ORM schema
let mut schema = HashMap::new(); let schema = create_big_schema();
let shape_type = OrmShapeType {
schema,
shape: "http://example.org/TestObject".to_string(),
};
log_info!("starting orm test");
let nuri = NuriV0::new_from(&doc_nuri).expect("parse nuri");
let (mut receiver, cancel_fn) = orm_start(nuri, shape_type, session_id)
.await
.expect("orm_start");
log_info!("orm_start called");
// TODO: remove this call to cancel_fn()
cancel_fn();
// TODO : change the data with sparql_query
while let Some(app_response) = receiver.next().await {
let orm_json = match app_response {
AppResponse::V0(v) => match v {
AppResponseV0::OrmInitial(json) => Some(json),
_ => None,
},
}
.unwrap();
log_info!("ORM JSON arrived\n: {:?}", orm_json);
// TODO: after we got what we wanted, call cancel_fn, otherwise the test never ends.
}
//
}
fn create_big_schema() -> OrmSchema {
// Define the ORM schema
let mut schema: OrmSchema = HashMap::new();
// Base shape
schema.insert( schema.insert(
"http://example.org/TestObject".to_string(), "http://example.org/TestObject".to_string(),
Arc::new(OrmSchemaShape { Arc::new(OrmSchemaShape {
@ -659,6 +506,36 @@ INSERT DATA {
minCardinality: 0, minCardinality: 0,
extra: None, extra: None,
}), }),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::shape,
literals: None,
shape: Some(
"http://example.org/TestObject||http://example.org/objectValue"
.to_string(),
),
}],
iri: "http://example.org/objectValue".to_string(),
readablePredicate: "objectValue".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::shape,
literals: None,
shape: Some(
"http://example.org/TestObject||http://example.org/anotherObject"
.to_string(),
),
}],
iri: "http://example.org/anotherObject".to_string(),
readablePredicate: "anotherObject".to_string(),
maxCardinality: -1,
minCardinality: 0,
extra: None,
}),
Arc::new(OrmSchemaPredicate { Arc::new(OrmSchemaPredicate {
dataTypes: vec![ dataTypes: vec![
OrmSchemaDataType { OrmSchemaDataType {
@ -679,14 +556,18 @@ INSERT DATA {
extra: None, extra: None,
}), }),
Arc::new(OrmSchemaPredicate { Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType { dataTypes: vec![
OrmSchemaDataType {
valType: OrmSchemaLiteralType::literal, valType: OrmSchemaLiteralType::literal,
literals: Some(vec![ literals: Some(vec![BasicType::Str("lit1".to_string())]),
BasicType::Str("lit1".to_string()),
BasicType::Str("lit2".to_string()),
]),
shape: None, shape: None,
}], },
OrmSchemaDataType {
valType: OrmSchemaLiteralType::literal,
literals: Some(vec![BasicType::Str("lit2".to_string())]),
shape: None,
},
],
iri: "http://example.org/lit1Or2".to_string(), iri: "http://example.org/lit1Or2".to_string(),
readablePredicate: "lit1Or2".to_string(), readablePredicate: "lit1Or2".to_string(),
maxCardinality: 1, maxCardinality: 1,
@ -697,36 +578,85 @@ INSERT DATA {
}), }),
); );
let shape_type = OrmShapeType { // a nested shape (http://example.org/anotherObject)
schema, schema.insert(
shape: "http://example.org/TestObject".to_string(), "http://example.org/TestObject||http://example.org/anotherObject".to_string(),
}; Arc::new(OrmSchemaShape {
iri: "http://example.org/TestObject||http://example.org/anotherObject".to_string(),
log_info!("starting orm test"); predicates: vec![
let nuri = NuriV0::new_from(&doc_nuri).expect("parse nuri"); Arc::new(OrmSchemaPredicate {
let (mut receiver, cancel_fn) = orm_start(nuri, shape_type, session_id) dataTypes: vec![OrmSchemaDataType {
.await valType: OrmSchemaLiteralType::string,
.expect("orm_start"); literals: None,
shape: None,
log_info!("orm_start called"); }],
iri: "http://example.org/prop1".to_string(),
// TODO: remove this call to cancel_fn() readablePredicate: "prop1".to_string(),
cancel_fn(); maxCardinality: 1,
minCardinality: 1,
// TODO : change the data with sparql_query extra: None,
}),
while let Some(app_response) = receiver.next().await { Arc::new(OrmSchemaPredicate {
let orm_json = match app_response { dataTypes: vec![OrmSchemaDataType {
AppResponse::V0(v) => match v { valType: OrmSchemaLiteralType::number,
AppResponseV0::OrmInitial(json) => Some(json), literals: None,
_ => None, shape: None,
}, }],
} iri: "http://example.org/prop2".to_string(),
.unwrap(); readablePredicate: "prop2".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
],
}),
);
log_info!("ORM JSON arrived\n: {:?}", orm_json); // another nested shape (http://example.org/objectValue)
schema.insert(
"http://example.org/TestObject||http://example.org/objectValue".to_string(),
Arc::new(OrmSchemaShape {
iri: "http://example.org/TestObject||http://example.org/objectValue".to_string(),
predicates: vec![
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::string,
literals: None,
shape: None,
}],
iri: "http://example.org/nestedString".to_string(),
readablePredicate: "nestedString".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/nestedNum".to_string(),
readablePredicate: "nestedNum".to_string(),
maxCardinality: 1,
minCardinality: 1,
extra: None,
}),
Arc::new(OrmSchemaPredicate {
dataTypes: vec![OrmSchemaDataType {
valType: OrmSchemaLiteralType::number,
literals: None,
shape: None,
}],
iri: "http://example.org/nestedArray".to_string(),
readablePredicate: "nestedArray".to_string(),
maxCardinality: -1,
minCardinality: 0,
extra: None,
}),
],
}),
);
// TODO: after we got what we wanted, call cancel_fn, otherwise the test never ends. return schema;
}
//
} }

@ -27,27 +27,6 @@ pub fn add_remove_triples(
orm_subscription: &mut OrmSubscription, orm_subscription: &mut OrmSubscription,
subject_changes: &mut OrmTrackedSubjectChange, subject_changes: &mut OrmTrackedSubjectChange,
) -> Result<(), VerifierError> { ) -> Result<(), VerifierError> {
// fn get_tracked_predicate<'a>(
// subject_iri: &str,
// shape: &Arc<OrmSchemaShape>,
// predicate_schema_iri: &String,
// tracked_subjects: &'a mut HashMap<String, HashMap<String, Arc<RwLock<OrmTrackedSubject>>>>,
// ) -> Result<Arc<RwLock<OrmTrackedPredicate>>, VerifierError> {
// let tracked_shapes_for_subject = tracked_subjects
// .get_mut(&subject_iri.to_string())
// .ok_or(VerifierError::OrmSubjectNotFound)?;
// let subject = tracked_shapes_for_subject
// .get_mut(&shape.iri)
// .ok_or(VerifierError::OrmSubjectNotFound)?;
// Ok(subject
// .read()
// .unwrap()
// .tracked_predicates
// .get(predicate_schema_iri)
// .ok_or(VerifierError::OrmPredicateNotFound)?
// .clone())
// }
// Helper to get/create tracked subjects // Helper to get/create tracked subjects
fn get_or_create_tracked_subject<'a>( fn get_or_create_tracked_subject<'a>(
subject_iri: &str, subject_iri: &str,
@ -91,8 +70,8 @@ pub fn add_remove_triples(
let tracked_subject_lock = let tracked_subject_lock =
get_or_create_tracked_subject(subject_iri, &shape, tracked_subjects); get_or_create_tracked_subject(subject_iri, &shape, tracked_subjects);
let mut tracked_subject = tracked_subject_lock.write().unwrap(); let mut tracked_subject = tracked_subject_lock.write().unwrap();
log_debug!("lock acquired on tracked_subject"); // log_debug!("lock acquired on tracked_subject");
// Add tracked predicate or increase cardinality // Add get tracked predicate.
let tracked_predicate_lock = tracked_subject let tracked_predicate_lock = tracked_subject
.tracked_predicates .tracked_predicates
.entry(predicate_schema.iri.clone()) .entry(predicate_schema.iri.clone())
@ -106,7 +85,7 @@ pub fn add_remove_triples(
}); });
{ {
let mut tracked_predicate = tracked_predicate_lock.write().unwrap(); let mut tracked_predicate = tracked_predicate_lock.write().unwrap();
log_debug!("lock acquired on tracked_predicate"); // log_debug!("lock acquired on tracked_predicate");
tracked_predicate.current_cardinality += 1; tracked_predicate.current_cardinality += 1;
// Keep track of the changed values too. // Keep track of the changed values too.
@ -216,7 +195,7 @@ pub fn add_remove_triples(
// Remove obj_val from current_literals in-place // Remove obj_val from current_literals in-place
current_literals.retain(|val| *val != val_removed); current_literals.retain(|val| *val != val_removed);
} else { } else {
tracked_predicate.current_literals = Some(vec![val_removed]); panic!("tracked_predicate.current_literals must not be None.");
} }
} else if tracked_predicate } else if tracked_predicate
.schema .schema
@ -244,7 +223,7 @@ pub fn add_remove_triples(
// Remove link to children // Remove link to children
tracked_predicate tracked_predicate
.tracked_children .tracked_children
.retain(|c| *obj_iri != c.read().unwrap().subject_iri); .retain(|ts| *obj_iri != ts.read().unwrap().subject_iri);
for shape_iri in shapes_to_process { for shape_iri in shapes_to_process {
// Get or create object's tracked subject struct. // Get or create object's tracked subject struct.
@ -255,7 +234,7 @@ pub fn add_remove_triples(
.write() .write()
.unwrap() .unwrap()
.parents .parents
.remove(obj_iri); .remove(subject_iri);
} }
} }
} }

@ -58,7 +58,7 @@ impl Verifier {
//let base = NuriV0::repo_id(&repo.id); //let base = NuriV0::repo_id(&repo.id);
let nuri_str = nuri.as_ref().map(|s| s.as_str()); let nuri_str = nuri.as_ref().map(|s| s.as_str());
log_debug!("querying construct\n{}\n{}\n\n", nuri_str.unwrap(), query); log_debug!("querying construct\n{}\n{}\n", nuri_str.unwrap(), query);
let parsed = let parsed =
Query::parse(&query, nuri_str).map_err(|e| NgError::OxiGraphError(e.to_string()))?; Query::parse(&query, nuri_str).map_err(|e| NgError::OxiGraphError(e.to_string()))?;
@ -67,17 +67,19 @@ impl Verifier {
.map_err(|e| NgError::OxiGraphError(e.to_string()))?; .map_err(|e| NgError::OxiGraphError(e.to_string()))?;
match results { match results {
QueryResults::Graph(triples) => { QueryResults::Graph(triples) => {
let mut results = vec![]; let mut result_triples: Vec<Triple> = vec![];
for t in triples { for t in triples {
match t { match t {
Err(e) => { Err(e) => {
log_err!("{}", e.to_string()); log_err!("{}", e.to_string());
return Err(NgError::SparqlError(e.to_string())); return Err(NgError::SparqlError(e.to_string()));
} }
Ok(triple) => results.push(triple), Ok(triple) => {
result_triples.push(triple);
} }
} }
Ok(results) }
Ok(result_triples)
} }
_ => return Err(NgError::InvalidResponse), _ => return Err(NgError::InvalidResponse),
} }
@ -138,21 +140,18 @@ impl Verifier {
// First in, last out stack to keep track of objects to validate (nested objects first). Strings are object IRIs. // First in, last out stack to keep track of objects to validate (nested objects first). Strings are object IRIs.
let mut shape_validation_queue: Vec<(Arc<OrmSchemaShape>, Vec<String>)> = vec![]; let mut shape_validation_queue: Vec<(Arc<OrmSchemaShape>, Vec<String>)> = vec![];
// Add root shape for first validation run. // Add root shape for first validation run.
let root_shape_iri = root_shape.iri.clone();
shape_validation_queue.push((root_shape, vec![])); shape_validation_queue.push((root_shape, vec![]));
// Process queue of shapes and subjects to validate. // Process queue of shapes and subjects to validate.
// For a given shape, we evaluate every subject against that shape. // For a given shape, we evaluate every subject against that shape.
while let Some((shape, objects_to_validate)) = shape_validation_queue.pop() { while let Some((shape, objects_to_validate)) = shape_validation_queue.pop() {
// Collect triples relevant for validation. // Collect triples relevant for validation.
log_debug!(
"process_changes_for_shape_and_session triples_added: {:?}",
triples_added
);
let added_triples_by_subject = let added_triples_by_subject =
group_by_subject_for_shape(&shape, triples_added, &objects_to_validate); group_by_subject_for_shape(&shape, triples_added, &objects_to_validate);
let removed_triples_by_subject = let removed_triples_by_subject =
group_by_subject_for_shape(&shape, triples_removed, &objects_to_validate); group_by_subject_for_shape(&shape, triples_removed, &objects_to_validate);
let all_modified_subjects: HashSet<&SubjectIri> = added_triples_by_subject let modified_subject_iris: HashSet<&SubjectIri> = added_triples_by_subject
.keys() .keys()
.chain(removed_triples_by_subject.keys()) .chain(removed_triples_by_subject.keys())
.collect(); .collect();
@ -162,7 +161,7 @@ impl Verifier {
.get_mut(nuri) .get_mut(nuri)
.unwrap() .unwrap()
.iter_mut() .iter_mut()
.find(|sub| sub.session_id == session_id && sub.shape_type.shape == shape.iri) .find(|sub| sub.session_id == session_id && sub.shape_type.shape == root_shape_iri)
.unwrap(); .unwrap();
// Variable to collect nested objects that need validation. // Variable to collect nested objects that need validation.
@ -170,9 +169,9 @@ impl Verifier {
HashMap::new(); HashMap::new();
// For each subject, add/remove triples and validate. // For each subject, add/remove triples and validate.
log_debug!("all_modified_subjects: {:?}", all_modified_subjects); log_debug!("all_modified_subjects: {:?}", modified_subject_iris);
for subject_iri in all_modified_subjects { for subject_iri in modified_subject_iris {
let triples_added_for_subj = added_triples_by_subject let triples_added_for_subj = added_triples_by_subject
.get(subject_iri) .get(subject_iri)
.map(|v| v.as_slice()) .map(|v| v.as_slice())
@ -190,12 +189,17 @@ impl Verifier {
.or_insert_with(|| OrmTrackedSubjectChange { .or_insert_with(|| OrmTrackedSubjectChange {
subject_iri: subject_iri.clone(), subject_iri: subject_iri.clone(),
predicates: HashMap::new(), predicates: HashMap::new(),
data_applied: false,
}); });
// Apply all triples for that subject to the tracked (shape, subject) pair. // Apply all triples for that subject to the tracked (shape, subject) pair.
// Record the changes. // Record the changes.
{ {
log_debug!("add_remove_triples for subject {subject_iri}"); if !change.data_applied {
log_debug!(
"Adding triples to change tracker for subject {}",
subject_iri
);
if let Err(e) = add_remove_triples( if let Err(e) = add_remove_triples(
shape.clone(), shape.clone(),
subject_iri, subject_iri,
@ -207,6 +211,10 @@ impl Verifier {
log_err!("apply_changes_from_triples add/remove error: {:?}", e); log_err!("apply_changes_from_triples add/remove error: {:?}", e);
panic!(); panic!();
} }
change.data_applied = true;
} else {
log_debug!("not applying triples again for subject {subject_iri}");
}
let validity = { let validity = {
let tracked_subject_opt = orm_subscription let tracked_subject_opt = orm_subscription
@ -240,8 +248,11 @@ impl Verifier {
// Now, we queue all non-evaluated objects // Now, we queue all non-evaluated objects
for (shape_iri, objects_to_eval) in &nested_objects_to_eval { for (shape_iri, objects_to_eval) in &nested_objects_to_eval {
let orm_subscription = let orm_subscription = self.get_first_orm_subscription_for(
self.get_first_orm_subscription_for(nuri, Some(&shape.iri), Some(&session_id)); nuri,
Some(&root_shape_iri),
Some(&session_id),
);
// Extract schema and shape Arc before mutable borrow // Extract schema and shape Arc before mutable borrow
let schema = orm_subscription.shape_type.schema.clone(); let schema = orm_subscription.shape_type.schema.clone();
let shape_arc = schema.get(shape_iri).unwrap().clone(); let shape_arc = schema.get(shape_iri).unwrap().clone();
@ -526,16 +537,18 @@ impl Verifier {
let mut return_vals: Value = Value::Array(vec![]); let mut return_vals: Value = Value::Array(vec![]);
let return_val_vec = return_vals.as_array_mut().unwrap(); let return_val_vec = return_vals.as_array_mut().unwrap();
log_debug!( // log_debug!(
"Tracked subjects:\n{:?}\n", // "Tracked subjects:\n{:?}\n",
orm_subscription.tracked_subjects, // orm_subscription.tracked_subjects,
); // );
// For each valid change struct, we build an orm object. // For each valid change struct, we build an orm object.
// The way we get the changes from the tracked subjects is a bit hacky, sorry. // The way we get the changes from the tracked subjects is a bit hacky, sorry.
for (subject_iri, tracked_subjects_by_shape) in &orm_subscription.tracked_subjects { for (subject_iri, tracked_subjects_by_shape) in &orm_subscription.tracked_subjects {
if let Some(tracked_subject) = tracked_subjects_by_shape.get(&shape_type.shape) { if let Some(tracked_subject) = tracked_subjects_by_shape.get(&shape_type.shape) {
log_info!("changes for : {:?}\n{:?}", tracked_subject, changes); let ts = tracked_subject.read().unwrap();
if tracked_subject.read().unwrap().valid == OrmTrackedSubjectValidity::Valid { log_info!("changes for: {:?} valid: {:?}\n", ts.subject_iri, ts.valid);
if ts.valid == OrmTrackedSubjectValidity::Valid {
if let Some(change) = changes if let Some(change) = changes
.get(&shape_type.shape) .get(&shape_type.shape)
.and_then(|subject_iri_to_ts| subject_iri_to_ts.get(subject_iri).clone()) .and_then(|subject_iri_to_ts| subject_iri_to_ts.get(subject_iri).clone())
@ -546,7 +559,8 @@ impl Verifier {
root_shape, root_shape,
&orm_subscription.tracked_subjects, &orm_subscription.tracked_subjects,
); );
log_debug!("Materialized change:\n{:?}\ninto:\n{:?}", change, new_val); // TODO: For some reason, this log statement causes a panic.
// log_debug!("Materialized change:\n{:?}\ninto:\n{:?}", change, new_val);
return_val_vec.push(new_val); return_val_vec.push(new_val);
} }
} }

@ -57,6 +57,10 @@ pub struct OrmTrackedSubjectChange {
pub subject_iri: String, pub subject_iri: String,
/// Predicates that were changed. /// Predicates that were changed.
pub predicates: HashMap<String, OrmTrackedPredicateChanges>, pub predicates: HashMap<String, OrmTrackedPredicateChanges>,
/// If the new triples have been added to the tracked predicates
/// (values_added / values_removed) already. This is to prevent
/// double-application.
pub data_applied: bool,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct OrmTrackedPredicateChanges { pub struct OrmTrackedPredicateChanges {

@ -13,6 +13,7 @@ use std::collections::HashSet;
use crate::orm::types::*; use crate::orm::types::*;
use crate::verifier::*; use crate::verifier::*;
use ng_net::orm::*; use ng_net::orm::*;
use ng_repo::log::*;
impl Verifier { impl Verifier {
/// Check the validity of a subject and update affecting tracked subjects' validity. /// Check the validity of a subject and update affecting tracked subjects' validity.
@ -54,9 +55,8 @@ impl Verifier {
{ {
// We need to fetch the subject's current state: // We need to fetch the subject's current state:
// We have new parents but were previously not recording changes. // We have new parents but were previously not recording changes.
// Return the subject_iri with `needs_fetch` set to true.
return vec![(s_change.subject_iri.clone(), shape.iri.clone(), true)]; return vec![(s_change.subject_iri.clone(), shape.iri.clone(), true)];
// TODO
} }
} }
@ -97,6 +97,14 @@ impl Verifier {
// Check 4.1) Cardinality // Check 4.1) Cardinality
if count < p_schema.minCardinality { if count < p_schema.minCardinality {
log_debug!(
"[VALIDATION] Invalid: minCardinality not met | predicate: {:?} | count: {} | min: {} | schema: {:?} | changed: {:?}",
p_schema.iri,
count,
p_schema.minCardinality,
shape.iri,
p_change
);
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid); set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
if count <= 0 { if count <= 0 {
// If cardinality is 0, we can remove the tracked predicate. // If cardinality is 0, we can remove the tracked predicate.
@ -108,6 +116,14 @@ impl Verifier {
&& p_schema.maxCardinality != -1 && p_schema.maxCardinality != -1
&& p_schema.extra != Some(true) && p_schema.extra != Some(true)
{ {
log_debug!(
"[VALIDATION] Invalid: maxCardinality exceeded | predicate: {:?} | count: {} | max: {} | schema: {:?} | changed: {:?}",
p_schema.iri,
count,
p_schema.maxCardinality,
shape.iri,
p_change
);
// If cardinality is too high and no extra allowed, invalid. // If cardinality is too high and no extra allowed, invalid.
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid); set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
break; break;
@ -146,6 +162,12 @@ impl Verifier {
}, },
); );
if !some_valid { if !some_valid {
log_debug!(
"[VALIDATION] Invalid: required literals missing | predicate: {:?} | schema: {:?} | changed: {:?}",
p_schema.iri,
shape.iri,
p_change
);
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid); set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
} }
// Check 4.4) Nested shape correct. // Check 4.4) Nested shape correct.
@ -184,11 +206,36 @@ impl Verifier {
}); });
if counts.1 > 0 && p_schema.extra != Some(true) { if counts.1 > 0 && p_schema.extra != Some(true) {
// If we have at least one invalid nested object and no extra allowed, invalid. log_debug!(
"[VALIDATION] Invalid: nested invalid child | predicate: {:?} | schema: {:?} | changed: {:?}",
p_schema.iri,
shape.iri,
p_change
);
// If we have at least one invalid nested object
// and no extra (in this case this means invalid) allowed, invalid.
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid); set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
break; break;
} else if counts.0 < p_schema.minCardinality { } else if counts.0 > p_schema.maxCardinality && p_schema.maxCardinality != -1 {
// If we have not enough valid nested objects, invalid. log_debug!(
"[VALIDATION] Too many valid children: | predicate: {:?} | schema: {:?} | changed: {:?}",
p_schema.iri,
shape.iri,
p_change
);
// If there are more valid children than what's allowed, break.
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
break;
} else if counts.0 + counts.2 + counts.3 < p_schema.minCardinality {
log_debug!(
"[VALIDATION] Invalid: not enough nested children | predicate: {:?} | valid_count: {} | min: {} | schema: {:?} | changed: {:?}",
p_schema.iri,
counts.0,
p_schema.minCardinality,
shape.iri,
p_change
);
// If we don't have enough nested objects, invalid.
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid); set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
break; break;
} else if counts.3 > 0 { } else if counts.3 > 0 {
@ -251,6 +298,14 @@ impl Verifier {
}), }),
}; };
if !matches { if !matches {
log_debug!(
"[VALIDATION] Invalid: value type mismatch | predicate: {:?} | value: {:?} | allowed_types: {:?} | schema: {:?} | changed: {:?}",
p_schema.iri,
val_added,
allowed_types,
shape.iri,
p_change
);
set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid); set_validity(&mut new_validity, OrmTrackedSubjectValidity::Invalid);
break; break;
} }

Loading…
Cancel
Save