diff --git a/ng-broker/src/server_broker.rs b/ng-broker/src/server_broker.rs index f1ce5c2..acae160 100644 --- a/ng-broker/src/server_broker.rs +++ b/ng-broker/src/server_broker.rs @@ -457,14 +457,19 @@ impl IServerBroker for ServerBroker { } } } else { - let res = session - .verifier - .app_request(req) - .await - .map_err(|e| e.into()); + let res = session.verifier.app_request(req).await; + //log_debug!("GOT RES {:?}", res); + let app_message: AppMessage = match res { + Err(e) => { + log_debug!("AppRequest error NgError {e}"); + let server_err: ServerError = e.into(); + server_err.into() + } + Ok(app_res) => app_res.into(), + }; fsm.lock() .await - .send_in_reply_to(res.into(), request_id) + .send_in_reply_to(app_message.into(), request_id) .await?; } diff --git a/ng-net/src/app_protocol.rs b/ng-net/src/app_protocol.rs index dfd92a8..3bcc536 100644 --- a/ng-net/src/app_protocol.rs +++ b/ng-net/src/app_protocol.rs @@ -10,12 +10,14 @@ //! App Protocol (between LocalBroker and Verifier) use lazy_static::lazy_static; +use ng_repo::utils::decode_overlayid; use regex::Regex; use serde::{Deserialize, Serialize}; use ng_repo::errors::NgError; +use ng_repo::log::*; use ng_repo::types::*; -use ng_repo::utils::{decode_id, decode_key, decode_sym_key}; +use ng_repo::utils::{decode_digest, decode_key, decode_sym_key}; use crate::types::*; @@ -42,6 +44,7 @@ pub enum AppFetchContentV0 { //Invoke, ReadQuery, // more to be detailed WriteQuery, // more to be detailed + RdfDump, } impl AppFetchContentV0 { @@ -254,7 +257,7 @@ impl NuriV0 { let cap = c.unwrap(); let j = cap.get(1).unwrap().as_str(); let k = cap.get(2).unwrap().as_str(); - let id = decode_id(j)?; + let id = decode_digest(j)?; let key = decode_sym_key(k)?; Ok(Self { identity: None, @@ -276,16 +279,17 @@ impl NuriV0 { { let cap = c.unwrap(); let o = cap.get(1).unwrap().as_str(); + let v = cap.get(2).unwrap().as_str(); let repo_id = decode_key(o)?; - let overlay_id = decode_id(v)?; + let overlay_id = decode_overlayid(v)?; Ok(Self { identity: None, target: NuriTargetV0::Repo(repo_id), entire_store: false, object: None, branch: None, - overlay: Some(OverlayLink::Outer(overlay_id)), + overlay: Some(overlay_id.into()), access: vec![], topic: None, locator: vec![], @@ -303,7 +307,7 @@ impl NuriV0 { let v = cap.get(2).unwrap().as_str(); let b = cap.get(3).unwrap().as_str(); let repo_id = decode_key(o)?; - let overlay_id = decode_id(v)?; + let overlay_id = decode_overlayid(v)?; let branch_id = decode_key(b)?; Ok(Self { identity: None, @@ -311,7 +315,7 @@ impl NuriV0 { entire_store: false, object: None, branch: Some(TargetBranchV0::BranchId(branch_id)), - overlay: Some(OverlayLink::Outer(overlay_id)), + overlay: Some(overlay_id.into()), access: vec![], topic: None, locator: vec![], @@ -353,6 +357,9 @@ impl AppRequestCommandV0 { pub fn new_write_query() -> Self { AppRequestCommandV0::Fetch(AppFetchContentV0::WriteQuery) } + pub fn new_rdf_dump() -> Self { + AppRequestCommandV0::Fetch(AppFetchContentV0::RdfDump) + } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/ng-net/src/broker.rs b/ng-net/src/broker.rs index 4b9ca15..cb329c6 100644 --- a/ng-net/src/broker.rs +++ b/ng-net/src/broker.rs @@ -641,11 +641,11 @@ impl Broker { Some(Either::Right(remote_peer_id)) => { let res = join.next().await; - if res.is_some() - && res.as_ref().unwrap().as_ref().unwrap_left() == &NetError::Closing - { - return; - } + // if res.is_some() + // && res.as_ref().unwrap().as_ref().unwrap_left() == &NetError::Closing + // { + // return; + // } log_debug!("SOCKET IS CLOSED {:?} peer_id: {:?}", res, remote_peer_id); BROKER .write() diff --git a/ng-net/src/connection.rs b/ng-net/src/connection.rs index b78a5b3..990feae 100644 --- a/ng-net/src/connection.rs +++ b/ng-net/src/connection.rs @@ -974,7 +974,7 @@ impl NoiseFSM { FSMstate::AppHello2 => { if let Some(msg) = msg_opt { if msg.type_id() != TypeId::of::() { - return Err(ProtocolError::AccessDenied); + return Err(ProtocolError::InvalidState); } match msg.id() { Some(id) => { @@ -991,7 +991,7 @@ impl NoiseFSM { FSMstate::AuthResult | FSMstate::Local0 => { if let Some(msg) = msg_opt { if msg.type_id() != TypeId::of::() { - return Err(ProtocolError::AccessDenied); + return Err(ProtocolError::InvalidState); } match msg.id() { Some(id) => { diff --git a/ng-net/src/types.rs b/ng-net/src/types.rs index c7b33b3..c8f2e8f 100644 --- a/ng-net/src/types.rs +++ b/ng-net/src/types.rs @@ -1344,6 +1344,7 @@ pub enum OverlayLink { Inner(Digest), Inherit, Public(PubKey), + Global, } impl OverlayLink { @@ -1361,6 +1362,16 @@ impl OverlayLink { } } +impl From for OverlayLink { + fn from(id: OverlayId) -> Self { + match id { + OverlayId::Inner(i) => OverlayLink::Inner(Digest::from_slice(i)), + OverlayId::Outer(o) => OverlayLink::Outer(Digest::from_slice(o)), + OverlayId::Global => OverlayLink::Global, + } + } +} + /// Overlay session ID /// /// It is a pubkey used for signing all OverlayMessage sent by the peer. diff --git a/ng-oxigraph/LICENSE-APACHE b/ng-oxigraph/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/ng-oxigraph/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ng-oxigraph/LICENSE-MIT b/ng-oxigraph/LICENSE-MIT new file mode 100644 index 0000000..70d2651 --- /dev/null +++ b/ng-oxigraph/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018 Oxigraph developers +Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/ng-oxigraph/README.md b/ng-oxigraph/README.md index 0b98575..5636278 100644 --- a/ng-oxigraph/README.md +++ b/ng-oxigraph/README.md @@ -1,6 +1,7 @@ # Oxigraph Oxigraph is a graph database library implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard. +Its author is Thomas Pellissier Tanon thomas@pellissier-tanon.fr The official upstream project is here: https://oxigraph.org/ @@ -9,6 +10,7 @@ https://github.com/oxigraph/oxigraph/ https://crates.io/crates/oxigraph This package (ng-oxigraph) is a fork used internally by NextGraph.org project. +It mostly adds CRDTs to RDF/SPARQL (and also provides a RocksDB backend with encryption at rest, and OpenBSD support). If you are interested to know more about NextGraph: https://nextgraph.org @@ -16,6 +18,20 @@ https://git.nextgraph.org/NextGraph/nextgraph-rs https://crates.io/crates/nextgraph +## License + +Both OxiGraph and NextGraph are licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + at your option. + +`SPDX-License-Identifier: Apache-2.0 OR MIT` + +Copyright is attributed to "Copyright (c) 2018 Oxigraph developers" for all the code corresponding to the commit [427d675c9b4e7f55308825357d8628c612b82a91](https://github.com/oxigraph/oxigraph/commit/427d675c9b4e7f55308825357d8628c612b82a91) of the OxiGraph repository on date Mon Apr 8 09:11:04 2024 +0200. + +All the code added in subsequent commits have a copyright attributed to "Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers". + ## NextGraph > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. diff --git a/ng-oxigraph/src/oxigraph/sparql/algebra.rs b/ng-oxigraph/src/oxigraph/sparql/algebra.rs index 52af785..ce96c26 100644 --- a/ng-oxigraph/src/oxigraph/sparql/algebra.rs +++ b/ng-oxigraph/src/oxigraph/sparql/algebra.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + //! [SPARQL 1.1 Query Algebra](https://www.w3.org/TR/sparql11-query/#sparqlQuery) //! //! The root type for SPARQL queries is [`Query`] and the root type for updates is [`Update`]. @@ -251,6 +261,12 @@ impl QueryDataset { && self.named.is_none() } + pub fn has_no_default_dataset(&self) -> bool { + self.default + .as_ref() + .map_or(true, |t| t == &[GraphName::DefaultGraph] || t.is_empty()) + } + /// Returns the list of the store graphs that are available to the query as the default graph or `None` if the union of all graphs is used as the default graph /// This list is by default only the store default graph pub fn default_graph_graphs(&self) -> Option<&[GraphName]> { diff --git a/ng-oxigraph/src/oxigraph/sparql/dataset.rs b/ng-oxigraph/src/oxigraph/sparql/dataset.rs index e60a455..5987ff3 100644 --- a/ng-oxigraph/src/oxigraph/sparql/dataset.rs +++ b/ng-oxigraph/src/oxigraph/sparql/dataset.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + use crate::oxigraph::model::TermRef; use crate::oxigraph::sparql::algebra::QueryDataset; use crate::oxigraph::sparql::EvaluationError; @@ -6,6 +16,8 @@ use crate::oxigraph::storage::numeric_encoder::{ }; use crate::oxigraph::storage::{MatchBy, StorageError, StorageReader}; use crate::oxigraph::store::CorruptionError; +use crate::oxrdf::GraphName; +use crate::sparopt::algebra::NamedNode; use std::cell::RefCell; use std::collections::hash_map::Entry; @@ -29,11 +41,23 @@ impl Iterator for ErrorIterator { } impl DatasetView { - pub fn new(reader: StorageReader, dataset: &QueryDataset) -> Self { + pub fn new( + reader: StorageReader, + dataset: &QueryDataset, + default_graph: &Option, + ) -> Self { let dataset = EncodedDatasetSpec { - default: dataset - .default_graph_graphs() - .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::>()), + default: if dataset.has_no_default_dataset() && default_graph.is_some() { + Some(vec![GraphName::NamedNode(NamedNode::new_unchecked( + default_graph.to_owned().unwrap(), + )) + .as_ref() + .into()]) + } else { + dataset + .default_graph_graphs() + .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::>()) + }, named: dataset .available_named_graphs() .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::>()), diff --git a/ng-oxigraph/src/oxigraph/sparql/error.rs b/ng-oxigraph/src/oxigraph/sparql/error.rs index 39ba505..8b3f317 100644 --- a/ng-oxigraph/src/oxigraph/sparql/error.rs +++ b/ng-oxigraph/src/oxigraph/sparql/error.rs @@ -50,6 +50,8 @@ pub enum EvaluationError { /// The results are not a RDF graph #[error("The query results are not a RDF graph")] NotAGraph, + #[error("NextGraph cannot add triples to the default graph")] + NoDefaultGraph, } impl From for EvaluationError { @@ -78,7 +80,8 @@ impl From for io::Error { | EvaluationError::UnsupportedService(_) | EvaluationError::UnsupportedContentType(_) | EvaluationError::ServiceDoesNotReturnSolutions - | EvaluationError::NotAGraph => Self::new(io::ErrorKind::InvalidInput, error), + | EvaluationError::NotAGraph + | EvaluationError::NoDefaultGraph => Self::new(io::ErrorKind::InvalidInput, error), } } } diff --git a/ng-oxigraph/src/oxigraph/sparql/mod.rs b/ng-oxigraph/src/oxigraph/sparql/mod.rs index 3e5bbac..24f85fa 100644 --- a/ng-oxigraph/src/oxigraph/sparql/mod.rs +++ b/ng-oxigraph/src/oxigraph/sparql/mod.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + //! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation. //! //! Stores execute SPARQL. See [`Store`](crate::oxigraph::store::Store::query()) for an example. @@ -43,7 +53,7 @@ pub(crate) fn evaluate_query( run_stats: bool, ) -> Result<(Result, QueryExplanation), EvaluationError> { let query = query.try_into().map_err(Into::into)?; - let dataset = DatasetView::new(reader, &query.dataset); + let dataset = DatasetView::new(reader, &query.dataset, options.get_default_graph()); let start_planning = Timer::now(); let (results, plan_node_with_stats, planning_duration) = match query.inner { spargebra::Query::Select { @@ -162,6 +172,7 @@ pub struct QueryOptions { http_timeout: Option, http_redirection_limit: usize, without_optimizations: bool, + default_graph: Option, } pub(crate) type CustomFunctionRegistry = @@ -178,6 +189,14 @@ impl QueryOptions { self } + pub fn set_default_graph(&mut self, dg: Option) { + self.default_graph = dg; + } + + pub fn get_default_graph(&self) -> &Option { + &self.default_graph + } + /// Disables the `SERVICE` calls #[inline] #[must_use] @@ -276,6 +295,12 @@ impl From for UpdateOptions { } } +impl UpdateOptions { + pub fn set_default_graph(&mut self, dg: Option) { + self.query_options.set_default_graph(dg); + } +} + /// The explanation of a query. #[derive(Clone)] pub struct QueryExplanation { diff --git a/ng-oxigraph/src/oxigraph/sparql/update.rs b/ng-oxigraph/src/oxigraph/sparql/update.rs index 95143c0..996238a 100644 --- a/ng-oxigraph/src/oxigraph/sparql/update.rs +++ b/ng-oxigraph/src/oxigraph/sparql/update.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + use crate::oxigraph::io::{RdfFormat, RdfParser}; use crate::oxigraph::model::{GraphName as OxGraphName, GraphNameRef, Quad as OxQuad}; use crate::oxigraph::sparql::algebra::QueryDataset; @@ -7,6 +17,7 @@ use crate::oxigraph::sparql::http::Client; use crate::oxigraph::sparql::{EvaluationError, Update, UpdateOptions}; use crate::oxigraph::storage::numeric_encoder::{Decoder, EncodedTerm}; use crate::oxigraph::storage::CommitWriter; +use crate::oxrdf::NamedNodeRef; use crate::spargebra::algebra::{GraphPattern, GraphTarget}; use crate::spargebra::term::{ BlankNode, GraphName, GraphNamePattern, GroundQuad, GroundQuadPattern, GroundSubject, @@ -101,7 +112,8 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> { fn eval_insert_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> { let mut bnodes = HashMap::new(); for quad in data { - let quad = Self::convert_quad(quad, &mut bnodes); + let mut quad = Self::convert_quad(quad, &mut bnodes); + self.set_default_graph_if_needed(&mut quad); self.transaction.insert(quad.as_ref())?; } Ok(()) @@ -109,12 +121,23 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> { fn eval_delete_data(&mut self, data: &[GroundQuad]) -> Result<(), EvaluationError> { for quad in data { - let quad = Self::convert_ground_quad(quad); + let mut quad = Self::convert_ground_quad(quad); + self.set_default_graph_if_needed(&mut quad); self.transaction.remove(quad.as_ref())?; } Ok(()) } + fn set_default_graph_if_needed(&self, quad: &mut crate::oxrdf::Quad) { + if quad.graph_name.is_default_graph() { + if let Some(default_graph) = &self.options.query_options.default_graph { + quad.graph_name = crate::oxrdf::GraphName::NamedNode(NamedNode::new_unchecked( + default_graph.clone(), + )); + } + } + } + fn eval_delete_insert( &mut self, delete: &[GroundQuadPattern], @@ -122,7 +145,11 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> { using: &QueryDataset, algebra: &GraphPattern, ) -> Result<(), EvaluationError> { - let dataset = Rc::new(DatasetView::new(self.transaction.reader(), using)); + let dataset = Rc::new(DatasetView::new( + self.transaction.reader(), + using, + self.options.query_options.get_default_graph(), + )); let mut pattern = sparopt::algebra::GraphPattern::from(algebra); if !self.options.query_options.without_optimizations { pattern = Optimizer::optimize_graph_pattern(sparopt::algebra::GraphPattern::Reduced { @@ -143,16 +170,18 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> { eval(EncodedTuple::with_capacity(variables.len())).collect::, _>>()?; // TODO: would be much better to stream for tuple in tuples { for quad in delete { - if let Some(quad) = + if let Some(mut quad) = Self::convert_ground_quad_pattern(quad, &variables, &tuple, &dataset)? { + self.set_default_graph_if_needed(&mut quad); self.transaction.remove(quad.as_ref())?; } } for quad in insert { - if let Some(quad) = + if let Some(mut quad) = Self::convert_quad_pattern(quad, &variables, &tuple, &dataset, &mut bnodes)? { + self.set_default_graph_if_needed(&mut quad); self.transaction.insert(quad.as_ref())?; } } @@ -161,6 +190,16 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> { Ok(()) } + /*if quad.graph_name.is_default_graph() { + if let Some(default_graph) = &self.options.query_options.default_graph { + crate::oxrdf::GraphName::NamedNode(NamedNode::new_unchecked( + default_graph.clone(), + )).into() + } else { + return Err(EvaluationError); + } + } */ + fn eval_load(&mut self, from: &NamedNode, to: &GraphName) -> Result<(), EvaluationError> { let (content_type, body) = self .client @@ -173,7 +212,15 @@ impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> { .ok_or_else(|| EvaluationError::UnsupportedContentType(content_type))?; let to_graph_name = match to { GraphName::NamedNode(graph_name) => graph_name.into(), - GraphName::DefaultGraph => GraphNameRef::DefaultGraph, + GraphName::DefaultGraph => { + if let Some(default_graph) = &self.options.query_options.default_graph { + crate::oxrdf::GraphNameRef::NamedNode(NamedNodeRef::new_unchecked( + &default_graph, + )) + } else { + return Err(EvaluationError::NoDefaultGraph); + } + } }; let mut parser = RdfParser::from_format(format) .rename_blank_nodes() diff --git a/ng-oxigraph/src/oxigraph/storage/backend/fallback.rs b/ng-oxigraph/src/oxigraph/storage/backend/fallback.rs index 5863940..9f76951 100644 --- a/ng-oxigraph/src/oxigraph/storage/backend/fallback.rs +++ b/ng-oxigraph/src/oxigraph/storage/backend/fallback.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + //! TODO: This storage is dramatically naive. use super::super::numeric_encoder::StrHash; diff --git a/ng-oxigraph/src/oxigraph/storage/backend/oxi_rocksdb.rs b/ng-oxigraph/src/oxigraph/storage/backend/oxi_rocksdb.rs index 9908994..0f4c593 100644 --- a/ng-oxigraph/src/oxigraph/storage/backend/oxi_rocksdb.rs +++ b/ng-oxigraph/src/oxigraph/storage/backend/oxi_rocksdb.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + //! Code inspired by [Rust RocksDB](https://github.com/rust-rocksdb/rust-rocksdb) under Apache License 2.0. #![allow( diff --git a/ng-oxigraph/src/oxigraph/storage/binary_encoder.rs b/ng-oxigraph/src/oxigraph/storage/binary_encoder.rs index 2e7b0cb..3113c49 100644 --- a/ng-oxigraph/src/oxigraph/storage/binary_encoder.rs +++ b/ng-oxigraph/src/oxigraph/storage/binary_encoder.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + use crate::oxigraph::storage::error::{CorruptionError, StorageError}; use crate::oxigraph::storage::numeric_encoder::{EncodedQuad, EncodedTerm, EncodedTriple, StrHash}; use crate::oxigraph::storage::small_string::SmallString; diff --git a/ng-oxigraph/src/oxigraph/storage/mod.rs b/ng-oxigraph/src/oxigraph/storage/mod.rs index 78f4f04..dc4f979 100644 --- a/ng-oxigraph/src/oxigraph/storage/mod.rs +++ b/ng-oxigraph/src/oxigraph/storage/mod.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + #![allow(clippy::same_name_method)] use crate::oxigraph::model::Quad; use crate::oxigraph::model::{GraphNameRef, NamedOrBlankNodeRef, QuadRef, TermRef}; @@ -558,6 +568,7 @@ impl<'a> NgPastQuadIterator<'a> { }, f: Box::new(f), //TODO: avoid the copy of past (store a Vec instead of HashSet in cache) + // https://smallcultfollowing.com/babysteps/blog/2018/09/02/rust-pattern-iterating-an-over-a-rc-vec-t/ iter: past.as_ref().clone().into_iter(), } } @@ -653,6 +664,9 @@ impl NgCommitQuadIterator { } let (q, value) = self.iter.next().unwrap().unwrap(); if is_added(value) { + if self.current_is_added { + return Err(false); + } self.current_is_added = true; self.current_add_is_removed = None; return Ok(Some(Ok(EncodedQuad::new( @@ -787,7 +801,7 @@ impl StorageReader { .into()); } // TODO: check that all the commits are from the same branch - // TODO: if commits are exactly like current heads of branch, set at_current_heads = true + // TODO: if commits are exactly like current heads of branch, set at_current_heads = true (or if it is the main branch, use MatchBy::Repos) MatchBy::Commits { heads: HashSet::from_iter( commits @@ -1723,40 +1737,40 @@ impl StorageReader { } } -pub struct ChainedDecodingQuadIterator { - first: DecodingQuadIterator, - second: Option, -} - -impl ChainedDecodingQuadIterator { - fn new(first: DecodingQuadIterator) -> Self { - Self { - first, - second: None, - } - } - - fn pair(first: DecodingQuadIterator, second: DecodingQuadIterator) -> Self { - Self { - first, - second: Some(second), - } - } -} +// pub struct ChainedDecodingQuadIterator { +// first: DecodingQuadIterator, +// second: Option, +// } -impl Iterator for ChainedDecodingQuadIterator { - type Item = Result; +// impl ChainedDecodingQuadIterator { +// fn new(first: DecodingQuadIterator) -> Self { +// Self { +// first, +// second: None, +// } +// } + +// fn pair(first: DecodingQuadIterator, second: DecodingQuadIterator) -> Self { +// Self { +// first, +// second: Some(second), +// } +// } +// } - fn next(&mut self) -> Option { - if let Some(result) = self.first.next() { - Some(result) - } else if let Some(second) = self.second.as_mut() { - second.next() - } else { - None - } - } -} +// impl Iterator for ChainedDecodingQuadIterator { +// type Item = Result; + +// fn next(&mut self) -> Option { +// if let Some(result) = self.first.next() { +// Some(result) +// } else if let Some(second) = self.second.as_mut() { +// second.next() +// } else { +// None +// } +// } +// } pub struct DecodingQuadIterator { iter: Iter, diff --git a/ng-oxigraph/src/oxigraph/store.rs b/ng-oxigraph/src/oxigraph/store.rs index 86096ab..c70dac0 100644 --- a/ng-oxigraph/src/oxigraph/store.rs +++ b/ng-oxigraph/src/oxigraph/store.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + //! API to access an on-disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset). //! //! The entry point of the module is the [`Store`] struct. @@ -36,10 +46,8 @@ use super::sparql::{ use super::storage::numeric_encoder::{Decoder, EncodedQuad, EncodedTerm, StrHash}; #[cfg(all(not(target_family = "wasm"), not(docsrs)))] use super::storage::StorageBulkLoader; -use super::storage::{ - ChainedDecodingQuadIterator, DecodingGraphIterator, Storage, StorageReader, StorageWriter, -}; pub use super::storage::{CorruptionError, LoaderError, SerializerError, StorageError}; +use super::storage::{DecodingGraphIterator, Storage, StorageReader, StorageWriter}; use std::collections::HashSet; use std::error::Error; use std::io::{Read, Write}; @@ -192,8 +200,11 @@ impl Store { pub fn query( &self, query: impl TryInto>, + default_graph: Option, ) -> Result { - self.query_opt(query, QueryOptions::default()) + let mut opts = QueryOptions::default(); + opts.set_default_graph(default_graph); + self.query_opt(query, opts) } /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options. @@ -444,8 +455,11 @@ impl Store { pub fn ng_update( &self, update: impl TryInto>, + default_graph: Option, ) -> Result<(HashSet, HashSet), EvaluationError> { - self.ng_update_opt(update, UpdateOptions::default()) + let mut opts = UpdateOptions::default(); + opts.set_default_graph(default_graph); + self.ng_update_opt(update, opts) } /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options. diff --git a/ng-oxigraph/src/oxrdf/named_node.rs b/ng-oxigraph/src/oxrdf/named_node.rs index 09ec666..c96a080 100644 --- a/ng-oxigraph/src/oxrdf/named_node.rs +++ b/ng-oxigraph/src/oxrdf/named_node.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + use oxiri::{Iri, IriParseError}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; diff --git a/ng-oxigraph/src/spargebra/term.rs b/ng-oxigraph/src/spargebra/term.rs index b4ec630..f5032bd 100644 --- a/ng-oxigraph/src/spargebra/term.rs +++ b/ng-oxigraph/src/spargebra/term.rs @@ -1,3 +1,13 @@ +// partial Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// partial Copyright (c) 2018 Oxigraph developers +// All work licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice or not, may not be copied, modified, or distributed except +// according to those terms. + //! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples. pub use crate::oxrdf::{BlankNode, Literal, NamedNode, Subject, Term, Triple, Variable}; @@ -192,6 +202,12 @@ pub enum GraphName { } impl GraphName { + // pub(crate) fn is_default_graph(&self) -> bool { + // match self { + // GraphName::DefaultGraph => true, + // _ => false, + // } + // } /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html). pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result { match self { diff --git a/ng-repo/src/errors.rs b/ng-repo/src/errors.rs index 9aa30b1..9a6886d 100644 --- a/ng-repo/src/errors.rs +++ b/ng-repo/src/errors.rs @@ -81,6 +81,7 @@ pub enum NgError { LocalBrokerIsHeadless, LocalBrokerIsNotHeadless, InvalidNuri, + InvalidTarget, } impl Error for NgError {} @@ -259,6 +260,8 @@ pub enum ServerError { SessionNotFound, SessionDetached, OxiGraphError, + InvalidNuri, + InvalidTarget, } impl From for ServerError { @@ -285,6 +288,9 @@ impl From for ServerError { match e { NgError::InvalidSignature => ServerError::InvalidSignature, NgError::OxiGraphError(_) => ServerError::OxiGraphError, + NgError::InvalidNuri => ServerError::InvalidNuri, + NgError::InvalidTarget => ServerError::InvalidTarget, + _ => ServerError::OtherError, } } diff --git a/ng-repo/src/types.rs b/ng-repo/src/types.rs index c481492..ba18485 100644 --- a/ng-repo/src/types.rs +++ b/ng-repo/src/types.rs @@ -48,6 +48,11 @@ impl Digest { Self::Blake3Digest32(o) => o, } } + pub fn to_slice(self) -> [u8; 32] { + match self { + Self::Blake3Digest32(o) => o, + } + } } impl fmt::Display for Digest { @@ -531,8 +536,8 @@ pub type InnerOverlayId = Digest; /// except for Dialog Overlays where the Hash is computed from 2 secrets. #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub enum OverlayId { - Outer(OuterOverlayId), - Inner(InnerOverlayId), + Outer(Blake3Digest32), + Inner(Blake3Digest32), Global, } @@ -561,19 +566,20 @@ impl OverlayId { let key_hash = blake3::keyed_hash(&key, &store_id); store_overlay_branch_readcap_secret_ser.zeroize(); key.zeroize(); - OverlayId::Inner(Digest::from_slice(*key_hash.as_bytes())) + OverlayId::Inner(*key_hash.as_bytes()) } pub fn outer(store_id: &PubKey) -> OverlayId { let store_id = serde_bare::to_vec(store_id).unwrap(); - OverlayId::Outer((&store_id).into()) + let d: Digest = (&store_id).into(); + OverlayId::Outer(d.to_slice()) } #[cfg(any(test, feature = "testing"))] pub fn dummy() -> OverlayId { - OverlayId::Outer(Digest::dummy()) + OverlayId::Outer(Digest::dummy().to_slice()) } pub fn nil() -> OverlayId { - OverlayId::Outer(Digest::nil()) + OverlayId::Outer(Digest::nil().to_slice()) } pub fn is_inner(&self) -> bool { @@ -636,7 +642,7 @@ impl StoreOverlay { | StoreOverlay::V0(StoreOverlayV0::ProtectedStore(id)) | StoreOverlay::V0(StoreOverlayV0::PrivateStore(id)) | StoreOverlay::V0(StoreOverlayV0::Group(id)) => OverlayId::outer(id), - StoreOverlay::V0(StoreOverlayV0::Dialog(d)) => OverlayId::Inner(d.clone()), + StoreOverlay::V0(StoreOverlayV0::Dialog(d)) => OverlayId::Inner(d.clone().to_slice()), StoreOverlay::Own(_) => unimplemented!(), } } @@ -652,7 +658,7 @@ impl StoreOverlay { | StoreOverlay::V0(StoreOverlayV0::Group(id)) => { OverlayId::inner(id, &store_overlay_branch_readcap_secret) } - StoreOverlay::V0(StoreOverlayV0::Dialog(d)) => OverlayId::Inner(d.clone()), + StoreOverlay::V0(StoreOverlayV0::Dialog(d)) => OverlayId::Inner(d.clone().to_slice()), StoreOverlay::Own(_) => unimplemented!(), } } @@ -784,7 +790,7 @@ impl StoreRepo { | Self::V0(StoreRepoV0::ProtectedStore(_id)) | Self::V0(StoreRepoV0::Group(_id)) | Self::V0(StoreRepoV0::PrivateStore(_id)) => self.overlay_id_for_read_purpose(), - Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::Inner(d.1.clone()), + Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::Inner(d.1.clone().to_slice()), } } @@ -799,7 +805,7 @@ impl StoreRepo { | Self::V0(StoreRepoV0::PrivateStore(id)) => { OverlayId::inner(id, store_overlay_branch_readcap_secret) } - Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::Inner(d.1.clone()), + Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::Inner(d.1.clone().to_slice()), } } } diff --git a/ng-repo/src/utils.rs b/ng-repo/src/utils.rs index ebb35e0..cbcd0cd 100644 --- a/ng-repo/src/utils.rs +++ b/ng-repo/src/utils.rs @@ -70,11 +70,16 @@ pub fn decode_sym_key(key_string: &str) -> Result { Ok(serde_bare::from_slice(&vec).map_err(|_| NgError::InvalidKey)?) } -pub fn decode_id(key_string: &str) -> Result { +pub fn decode_digest(key_string: &str) -> Result { let vec = base64_url::decode(key_string).map_err(|_| NgError::InvalidKey)?; Ok(serde_bare::from_slice(&vec).map_err(|_| NgError::InvalidKey)?) } +pub fn decode_overlayid(id_string: &str) -> Result { + let vec = base64_url::decode(id_string).map_err(|_| NgError::InvalidKey)?; + Ok(serde_bare::from_slice(&vec).map_err(|_| NgError::InvalidKey)?) +} + pub fn ed_privkey_to_ed_pubkey(privkey: &PrivKey) -> PubKey { // SecretKey is zeroized on drop (3 lines below) se we are safe let sk = SecretKey::from_bytes(privkey.slice()).unwrap(); diff --git a/ng-sdk-js/app-node/index.js b/ng-sdk-js/app-node/index.js index dde8331..912d16e 100644 --- a/ng-sdk-js/app-node/index.js +++ b/ng-sdk-js/app-node/index.js @@ -24,18 +24,29 @@ ng.init_headless(config).then( async() => { //let user_id = await ng.admin_create_user(config); //console.log("user created: ",user_id); - let other_user_id = "AJQ5gCLoXXjalC9diTDCvxxWu5ZQUcYWEE821nhVRMcE"; + let other_user_id = "ACO9_6356BOZdCAwUr9mzTod8Wv-BM7p48sQzGNa2PwI"; let session = await ng.session_headless_start(other_user_id); session_id = session.session_id; console.log(session); + let dump = await ng.rdf_dump(session.session_id); + console.log(dump); + + let sparql_result = await ng.sparql_query(session.session_id, "SELECT ?s ?p ?o WHERE { ?s ?p ?o }"); + console.log(sparql_result); + for (const q of sparql_result.results.bindings) { + console.log(q); + } + + await ng.sparql_update(session.session_id, "DELETE DATA { }"); + //await ng.sparql_update(session.session_id, "INSERT DATA { }"); //await ng.sparql_update(session.session_id, "INSERT { ?s } WHERE { ?s } "); //await ng.sparql_update(session.session_id, "INSERT DATA { . }"); - let sparql_result = await ng.sparql_query(session.session_id, "SELECT ?a WHERE { ?a _:abc. _:abc }"); + sparql_result = await ng.sparql_query(session.session_id, "SELECT ?a WHERE { ?a _:abc. _:abc }"); console.log(sparql_result); for (const q of sparql_result.results.bindings) { console.log(q); @@ -52,25 +63,25 @@ ng.init_headless(config).then( async() => { console.log(q.subject.toString(), q.predicate.toString(), q.object.toString(), q.graph.toString()) } - let file_nuri = await ng.file_put_to_private_store(session.session_id,"LICENSE-MIT","text/plain"); - console.log(file_nuri); + // let file_nuri = await ng.file_put_to_private_store(session.session_id,"LICENSE-MIT","text/plain"); + // console.log(file_nuri); - //let file_nuri = "did:ng:j:AD_d4njVMAtIDEU1G-RDxfOLIOZyOrB_1Rb7B6XykIEJ:k:APV-_Xtk03PW_Mbl4OaYpLrmEkBDVtn81lpto8sxc_tb"; - var bufs = []; - let cancel = await ng.file_get_from_private_store(session.session_id, file_nuri, async (file) => { - if (file.V0.FileMeta) { - //skip - } else if (file.V0.FileBinary) { - if (file.V0.FileBinary.byteLength > 0) { - bufs.push(file.V0.FileBinary); - } - } else if (file.V0 == 'EndOfStream') { - //console.log("end of file"); - var buf = Buffer.concat(bufs); - // if the file contains some UTF8 text - console.log(buf.toString('utf8')); - } - }); + // //let file_nuri = "did:ng:j:AD_d4njVMAtIDEU1G-RDxfOLIOZyOrB_1Rb7B6XykIEJ:k:APV-_Xtk03PW_Mbl4OaYpLrmEkBDVtn81lpto8sxc_tb"; + // var bufs = []; + // let cancel = await ng.file_get_from_private_store(session.session_id, file_nuri, async (file) => { + // if (file.V0.FileMeta) { + // //skip + // } else if (file.V0.FileBinary) { + // if (file.V0.FileBinary.byteLength > 0) { + // bufs.push(file.V0.FileBinary); + // } + // } else if (file.V0 == 'EndOfStream') { + // //console.log("end of file"); + // var buf = Buffer.concat(bufs); + // // if the file contains some UTF8 text + // console.log(buf.toString('utf8')); + // } + // }); // the 2nd argument `false` means: do not `force_close` the dataset. // it will be detached, which means it stays in memory even when the session is stopped. diff --git a/ng-sdk-js/src/lib.rs b/ng-sdk-js/src/lib.rs index ca74c6d..e5cb233 100644 --- a/ng-sdk-js/src/lib.rs +++ b/ng-sdk-js/src/lib.rs @@ -251,7 +251,7 @@ pub async fn sparql_update(session_id: JsValue, sparql: String) -> Result<(), St let request = AppRequest::V0(AppRequestV0 { command: AppRequestCommandV0::new_write_query(), - nuri: NuriV0::new_entire_user_site(), + nuri: NuriV0::new_private_store_target(), payload: Some(AppRequestPayload::new_sparql_query(sparql)), session_id, }); @@ -263,6 +263,30 @@ pub async fn sparql_update(session_id: JsValue, sparql: String) -> Result<(), St Ok(()) } +#[cfg(wasmpack_target = "nodejs")] +#[wasm_bindgen] +pub async fn rdf_dump(session_id: JsValue) -> Result { + let session_id: u64 = serde_wasm_bindgen::from_value::(session_id) + .map_err(|_| "Invalid session_id".to_string())?; + + let request = AppRequest::V0(AppRequestV0 { + command: AppRequestCommandV0::new_rdf_dump(), + nuri: NuriV0::new_entire_user_site(), + payload: None, + session_id, + }); + + let res = nextgraph::local_broker::app_request(request) + .await + .map_err(|e: NgError| e.to_string())?; + + let AppResponse::V0(res) = res; + match res { + AppResponseV0::Text(s) => Ok(s), + _ => Err("invalid response".to_string()), + } +} + #[cfg(wasmpack_target = "nodejs")] #[wasm_bindgen] pub async fn admin_create_user(config: JsValue) -> Result { diff --git a/ng-verifier/src/commits/transaction.rs b/ng-verifier/src/commits/transaction.rs index b422a57..0a90e0c 100644 --- a/ng-verifier/src/commits/transaction.rs +++ b/ng-verifier/src/commits/transaction.rs @@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize}; use ng_net::app_protocol::{NuriV0, TargetBranchV0}; use ng_oxigraph::oxrdf::{GraphName, GraphNameRef, NamedNode, Quad, Triple, TripleRef}; use ng_repo::errors::VerifierError; +use ng_repo::log::*; use ng_repo::store::Store; use ng_repo::types::*; @@ -230,6 +231,7 @@ impl Verifier { match &quad.graph_name { GraphName::NamedNode(named_node) => { let graph_name = named_node.as_string(); + log_debug!("graph_name {graph_name}"); if let Some(branch_found) = nuri_branches.get(graph_name) { return Ok(branch_found.clone()); } @@ -237,8 +239,9 @@ impl Verifier { if !nuri.is_branch_identifier() { return Err(VerifierError::InvalidNamedGraph); } - let store = self - .get_store_by_overlay_id(&OverlayId::Outer(*nuri.overlay.unwrap().outer()))?; + let store = self.get_store_by_overlay_id(&OverlayId::Outer( + nuri.overlay.unwrap().outer().to_slice(), + ))?; let repo = self.get_repo(nuri.target.repo_id(), store.get_store_repo())?; let (branch_id, is_publisher, is_main, topic_id, token) = match nuri.branch { None => { @@ -529,12 +532,16 @@ impl Verifier { pub(crate) async fn process_sparql_update( &mut self, - _nuri: &NuriV0, + nuri: &NuriV0, query: &String, ) -> Result<(), String> { let store = self.graph_dataset.as_ref().unwrap(); - //TODO: use nuri to set some default dataset in oxigraph - let res = store.ng_update(query); + + let res = store.ng_update( + query, + self.resolve_target_for_sparql(&nuri.target, true) + .map_err(|e| e.to_string())?, + ); match res { Err(e) => Err(e.to_string()), Ok((inserts, removes)) => { diff --git a/ng-verifier/src/request_processor.rs b/ng-verifier/src/request_processor.rs index 4f7b318..b51f41f 100644 --- a/ng-verifier/src/request_processor.rs +++ b/ng-verifier/src/request_processor.rs @@ -117,7 +117,7 @@ impl Verifier { } fn resolve_target( - &mut self, + &self, target: &NuriTargetV0, ) -> Result<(RepoId, BranchId, StoreRepo), NgError> { match target { @@ -142,6 +142,36 @@ impl Verifier { } } + pub(crate) fn resolve_target_for_sparql( + &self, + target: &NuriTargetV0, + update: bool, + ) -> Result, NgError> { + match target { + NuriTargetV0::PrivateStore => { + let repo_id = self.config.private_store_id.unwrap(); + let repo = self.repos.get(&repo_id).ok_or(NgError::RepoNotFound)?; + let overlay_id = repo.store.overlay_id; + Ok(Some(NuriV0::repo_graph_name(&repo_id, &overlay_id))) + } + NuriTargetV0::Repo(repo_id) => { + let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?; + Ok(Some(NuriV0::repo_graph_name( + &repo_id, + &repo.store.overlay_id, + ))) + } + NuriTargetV0::UserSite | NuriTargetV0::None => { + if update { + return Err(NgError::InvalidTarget); + } else { + return Ok(None); + } + } + _ => unimplemented!(), + } + } + async fn open_for_target( &mut self, target: &NuriTargetV0, @@ -211,9 +241,12 @@ impl Verifier { return Ok(AppResponse::error(parsed.unwrap_err().to_string())); } let mut parsed = parsed.unwrap(); - parsed.dataset_mut().set_default_graph_as_union(); - - let results = store.query(parsed); + let dataset = parsed.dataset_mut(); + if dataset.has_no_default_dataset() { + dataset.set_default_graph_as_union(); + } + let results = store + .query(parsed, self.resolve_target_for_sparql(&nuri.target, false)?); return Ok(match results { Err(e) => AppResponse::error(e.to_string()), Ok(qr) => { @@ -244,6 +277,20 @@ impl Verifier { Err(NgError::InvalidPayload) }; } + AppFetchContentV0::RdfDump => { + let store = self.graph_dataset.as_ref().unwrap(); + + let results = store.iter(); + + let vec: Vec = results + .map(|q| match q { + Ok(o) => o.to_string(), + Err(e) => e.to_string(), + }) + .collect(); + + return Ok(AppResponse::V0(AppResponseV0::Text(vec.join("\n")))); + } _ => unimplemented!(), }, AppRequestCommandV0::FilePut => match payload {