-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into addAllDatasetFromQuery
- Loading branch information
Showing
29 changed files
with
1,227 additions
and
397 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright 2024, University of Freiburg | ||
// Chair of Algorithms and Data Structures | ||
// Authors: Julian Mundhahs <[email protected]> | ||
|
||
#include "engine/GraphStoreProtocol.h" | ||
|
||
#include "util/http/beast.h" | ||
|
||
// ____________________________________________________________________________ | ||
GraphOrDefault GraphStoreProtocol::extractTargetGraph( | ||
const ad_utility::url_parser::ParamValueMap& params) { | ||
const std::optional<std::string> graphIri = | ||
ad_utility::url_parser::checkParameter(params, "graph", std::nullopt); | ||
const bool isDefault = | ||
ad_utility::url_parser::checkParameter(params, "default", "").has_value(); | ||
if (graphIri.has_value() == isDefault) { | ||
throw std::runtime_error( | ||
"Exactly one of the query parameters default or graph must be set to " | ||
"identify the graph for the graph store protocol request."); | ||
} | ||
if (graphIri.has_value()) { | ||
return GraphRef::fromIrirefWithoutBrackets(graphIri.value()); | ||
} else { | ||
AD_CORRECTNESS_CHECK(isDefault); | ||
return DEFAULT{}; | ||
} | ||
} | ||
|
||
// ____________________________________________________________________________ | ||
void GraphStoreProtocol::throwUnsupportedMediatype( | ||
const string_view& mediatype) { | ||
throw UnsupportedMediatypeError(absl::StrCat( | ||
"Mediatype \"", mediatype, | ||
"\" is not supported for SPARQL Graph Store HTTP Protocol in QLever. " | ||
"Supported: ", | ||
toString(ad_utility::MediaType::turtle), ", ", | ||
toString(ad_utility::MediaType::ntriples), ".")); | ||
} | ||
|
||
// ____________________________________________________________________________ | ||
void GraphStoreProtocol::throwUnsupportedHTTPMethod( | ||
const std::string_view& method) { | ||
throw std::runtime_error(absl::StrCat( | ||
method, | ||
" in the SPARQL Graph Store HTTP Protocol is not yet implemented " | ||
"in QLever.")); | ||
} | ||
|
||
// ____________________________________________________________________________ | ||
std::vector<TurtleTriple> GraphStoreProtocol::parseTriples( | ||
const string& body, const ad_utility::MediaType contentType) { | ||
using Re2Parser = RdfStringParser<TurtleParser<Tokenizer>>; | ||
switch (contentType) { | ||
case ad_utility::MediaType::turtle: | ||
case ad_utility::MediaType::ntriples: { | ||
auto parser = Re2Parser(); | ||
parser.setInputStream(body); | ||
return parser.parseAndReturnAllTriples(); | ||
} | ||
default: { | ||
throwUnsupportedMediatype(toString(contentType)); | ||
} | ||
} | ||
} | ||
|
||
// ____________________________________________________________________________ | ||
std::vector<SparqlTripleSimpleWithGraph> GraphStoreProtocol::convertTriples( | ||
const GraphOrDefault& graph, std::vector<TurtleTriple> triples) { | ||
SparqlTripleSimpleWithGraph::Graph tripleGraph{std::monostate{}}; | ||
if (std::holds_alternative<GraphRef>(graph)) { | ||
tripleGraph = Iri(std::get<GraphRef>(graph).toStringRepresentation()); | ||
} | ||
auto transformTurtleTriple = [&tripleGraph](TurtleTriple&& triple) { | ||
AD_CORRECTNESS_CHECK(triple.graphIri_.isId() && | ||
triple.graphIri_.getId() == | ||
qlever::specialIds().at(DEFAULT_GRAPH_IRI)); | ||
|
||
return SparqlTripleSimpleWithGraph(std::move(triple.subject_), | ||
std::move(triple.predicate_), | ||
std::move(triple.object_), tripleGraph); | ||
}; | ||
return ad_utility::transform(std::move(triples), transformTurtleTriple); | ||
} | ||
|
||
// ____________________________________________________________________________ | ||
ParsedQuery GraphStoreProtocol::transformGet(const GraphOrDefault& graph) { | ||
ParsedQuery res; | ||
res._clause = parsedQuery::ConstructClause( | ||
{{Variable("?s"), Variable("?p"), Variable("?o")}}); | ||
res._rootGraphPattern = {}; | ||
parsedQuery::GraphPattern selectSPO; | ||
selectSPO._graphPatterns.emplace_back(parsedQuery::BasicGraphPattern{ | ||
{SparqlTriple(Variable("?s"), "?p", Variable("?o"))}}); | ||
if (const auto* iri = | ||
std::get_if<ad_utility::triple_component::Iri>(&graph)) { | ||
res.datasetClauses_ = | ||
parsedQuery::DatasetClauses::fromClauses({DatasetClause{*iri, false}}); | ||
} | ||
res._rootGraphPattern = std::move(selectSPO); | ||
return res; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright 2024, University of Freiburg | ||
// Chair of Algorithms and Data Structures | ||
// Authors: Julian Mundhahs <[email protected]> | ||
|
||
#pragma once | ||
|
||
#include <gtest/gtest_prod.h> | ||
|
||
#include "parser/ParsedQuery.h" | ||
#include "parser/RdfParser.h" | ||
#include "util/http/HttpUtils.h" | ||
#include "util/http/UrlParser.h" | ||
|
||
// The mediatype of a request could not be determined. | ||
class UnknownMediatypeError : public std::runtime_error { | ||
public: | ||
explicit UnknownMediatypeError(std::string_view msg) | ||
: std::runtime_error{std::string{msg}} {} | ||
}; | ||
|
||
// The mediatype of a request is not supported. | ||
class UnsupportedMediatypeError : public std::runtime_error { | ||
public: | ||
explicit UnsupportedMediatypeError(std::string_view msg) | ||
: std::runtime_error{std::string{msg}} {} | ||
}; | ||
|
||
// Transform SPARQL Graph Store Protocol requests to their equivalent | ||
// ParsedQuery (SPARQL Query or Update). | ||
class GraphStoreProtocol { | ||
private: | ||
// Extract the mediatype from a request. | ||
static ad_utility::MediaType extractMediatype( | ||
const ad_utility::httpUtils::HttpRequest auto& rawRequest) { | ||
using namespace boost::beast::http; | ||
|
||
std::string_view contentTypeString; | ||
if (rawRequest.find(field::content_type) != rawRequest.end()) { | ||
contentTypeString = rawRequest.at(field::content_type); | ||
} | ||
if (contentTypeString.empty()) { | ||
// If the mediatype is not given, return an error. | ||
// Note: The specs also allow to try to determine the media type from the | ||
// content. | ||
throw UnknownMediatypeError("Mediatype empty or not set."); | ||
} | ||
const auto mediatype = | ||
ad_utility::getMediaTypeFromAcceptHeader(contentTypeString); | ||
// A media type is set but not one of the supported ones as per the QLever | ||
// MediaType code. | ||
if (!mediatype.has_value()) { | ||
throwUnsupportedMediatype(rawRequest.at(field::content_type)); | ||
} | ||
return mediatype.value(); | ||
} | ||
FRIEND_TEST(GraphStoreProtocolTest, extractMediatype); | ||
|
||
// Throws the error if a mediatype is not supported. | ||
[[noreturn]] static void throwUnsupportedMediatype( | ||
const std::string_view& mediatype); | ||
|
||
// Throws the error if an HTTP method is not supported. | ||
[[noreturn]] static void throwUnsupportedHTTPMethod( | ||
const std::string_view& method); | ||
|
||
// Parse the triples from the request body according to the content type. | ||
static std::vector<TurtleTriple> parseTriples( | ||
const std::string& body, const ad_utility::MediaType contentType); | ||
FRIEND_TEST(GraphStoreProtocolTest, parseTriples); | ||
|
||
// Transforms the triples from `TurtleTriple` to `SparqlTripleSimpleWithGraph` | ||
// and sets the correct graph. | ||
static std::vector<SparqlTripleSimpleWithGraph> convertTriples( | ||
const GraphOrDefault& graph, std::vector<TurtleTriple> triples); | ||
FRIEND_TEST(GraphStoreProtocolTest, convertTriples); | ||
|
||
// Transform a SPARQL Graph Store Protocol POST to an equivalent ParsedQuery | ||
// which is an SPARQL Update. | ||
static ParsedQuery transformPost( | ||
const ad_utility::httpUtils::HttpRequest auto& rawRequest, | ||
const GraphOrDefault& graph) { | ||
auto triples = | ||
parseTriples(rawRequest.body(), extractMediatype(rawRequest)); | ||
auto convertedTriples = convertTriples(graph, std::move(triples)); | ||
updateClause::GraphUpdate up{std::move(convertedTriples), {}}; | ||
ParsedQuery res; | ||
res._clause = parsedQuery::UpdateClause{std::move(up)}; | ||
return res; | ||
} | ||
FRIEND_TEST(GraphStoreProtocolTest, transformPost); | ||
|
||
// Transform a SPARQL Graph Store Protocol GET to an equivalent ParsedQuery | ||
// which is an SPARQL Query. | ||
static ParsedQuery transformGet(const GraphOrDefault& graph); | ||
FRIEND_TEST(GraphStoreProtocolTest, transformGet); | ||
|
||
public: | ||
// Every Graph Store Protocol request has equivalent SPARQL Query or Update. | ||
// Transform the Graph Store Protocol request into it's equivalent Query or | ||
// Update. | ||
static ParsedQuery transformGraphStoreProtocol( | ||
const ad_utility::httpUtils::HttpRequest auto& rawRequest) { | ||
ad_utility::url_parser::ParsedUrl parsedUrl = | ||
ad_utility::url_parser::parseRequestTarget(rawRequest.target()); | ||
// We only support passing the target graph as a query parameter (`Indirect | ||
// Graph Identification`). `Direct Graph Identification` (the URL is the | ||
// graph) is not supported. See also | ||
// https://www.w3.org/TR/2013/REC-sparql11-http-rdf-update-20130321/#graph-identification. | ||
GraphOrDefault graph = extractTargetGraph(parsedUrl.parameters_); | ||
|
||
using enum boost::beast::http::verb; | ||
auto method = rawRequest.method(); | ||
if (method == get) { | ||
return transformGet(graph); | ||
} else if (method == put) { | ||
throwUnsupportedHTTPMethod("PUT"); | ||
} else if (method == delete_) { | ||
throwUnsupportedHTTPMethod("DELETE"); | ||
} else if (method == post) { | ||
return transformPost(rawRequest, graph); | ||
} else if (method == head) { | ||
throwUnsupportedHTTPMethod("HEAD"); | ||
} else if (method == patch) { | ||
throwUnsupportedHTTPMethod("PATCH"); | ||
} else { | ||
throw std::runtime_error( | ||
absl::StrCat("Unsupported HTTP method \"", | ||
std::string_view{rawRequest.method_string()}, | ||
"\" for the SPARQL Graph Store HTTP Protocol.")); | ||
} | ||
} | ||
|
||
private: | ||
// Extract the graph to be acted upon using from the URL query parameters | ||
// (`Indirect Graph Identification`). See | ||
// https://www.w3.org/TR/2013/REC-sparql11-http-rdf-update-20130321/#indirect-graph-identification | ||
static GraphOrDefault extractTargetGraph( | ||
const ad_utility::url_parser::ParamValueMap& params); | ||
FRIEND_TEST(GraphStoreProtocolTest, extractTargetGraph); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.