Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graph Store HTTP Protocol (GET, POST) back end #1668

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

Qup42
Copy link
Member

@Qup42 Qup42 commented Dec 8, 2024

Implement a function transformGraphStoreProtocol that does the back end of transforming a SPARQL Graph Store HTTP Protocol request to it's equivalent SPARQL Query or Update. The integration will be a separate step.

TODO:

  • execute TODOs in code
  • think about function definition in header

Additional smaller changes that were required or sensible and which could be extracted into separate PRs if needed:

  • move checkParameter from Server to ad_utility::url_parser
  • extract creation of test HTTP request into HttpRequestHelper
  • add methods to access Id from TripleComponent
  • added media types N-Triples and N-Quads

Copy link

codecov bot commented Dec 9, 2024

Codecov Report

Attention: Patch coverage is 97.87234% with 3 lines in your changes missing coverage. Please review.

Project coverage is 89.89%. Comparing base (a97905e) to head (ff36dfd).

Files with missing lines Patch % Lines
src/engine/Server.cpp 71.42% 2 Missing ⚠️
src/engine/GraphStoreProtocol.h 98.96% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1668      +/-   ##
==========================================
+ Coverage   89.86%   89.89%   +0.03%     
==========================================
  Files         389      393       +4     
  Lines       37189    37320     +131     
  Branches     4196     4213      +17     
==========================================
+ Hits        33419    33550     +131     
- Misses       2473     2474       +1     
+ Partials     1297     1296       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Qup42 Qup42 marked this pull request as ready for review December 16, 2024 11:29
@sparql-conformance
Copy link

Copy link
Member

@joka921 joka921 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A first round of reviews, this is already looking very promising.

Comment on lines +22 to +29
if (rawRequest.find(field::content_type) != rawRequest.end()) {
contentTypeString = rawRequest.at(field::content_type);
}
if (contentTypeString.empty()) {
// ContentType not set or empty; we don't try to guess -> 400 Bad Request
}
const auto contentType =
ad_utility::getMediaTypeFromAcceptHeader(contentTypeString);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract this as a function.
And there is an empty if (probably misssing TODO, do you want this to throw?

Comment on lines +30 to +46
std::vector<TurtleTriple> triples;
switch (contentType.value()) {
case ad_utility::MediaType::turtle:
case ad_utility::MediaType::ntriples: {
auto parser = Re2Parser();
parser.setInputStream(rawRequest.body());
triples = parser.parseAndReturnAllTriples();
break;
}
default: {
// Unsupported media type -> 415 Unsupported Media Type
throw std::runtime_error(absl::StrCat(
"Mediatype \"", ad_utility::toString(contentType.value()),
"\" is not supported for SPARQL Graph Store HTTP "
"Protocol in QLever."));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also be a small function (which can be defined in the .cpp file.
Also report in the error message, which types ARE supported, s.t. the user can fix this.
And for the handling of 415 you probably need a dedicated exception type.

Comment on lines +53 to +55
if (std::holds_alternative<GraphRef>(graph)) {
g = Iri(std::get<GraphRef>(graph).toStringRepresentation());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This IRI can be computed in advance. And it is somewhat wasteful to repeat the Graph IRI over and over (but maybe that is not super relevant, as the subject, predicate, and object are also strings here.

if (std::holds_alternative<GraphRef>(graph)) {
g = Iri(std::get<GraphRef>(graph).toStringRepresentation());
}
return SparqlTripleSimpleWithGraph(triple.subject_, triple.predicate_,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to std::move here (I hope that ad_utility:transform supports this, otherwise do something else or let's think about something.

Comment on lines +57 to +62
triple.object_, g);
};
updateClause::GraphUpdate up{
ad_utility::transform(triples, transformTurtleTriple), {}};
res._clause = parsedQuery::UpdateClause{up};
return res;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual triple parsing can also be a separate function. Everything that doesn't directly depend on the templated request can and should be extracted to the .cpp file.

Comment on lines +179 to +180
[[nodiscard]] const Id& getId() const { return std::get<Id>(_variant); }
[[nodiscard]] Id& getId() { return std::get<Id>(_variant); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use nodiscard by default.
Only when it is really semantically subtle (e.g. RAII types like Lock-Guard).
That clang-tidy rule of "every const method should be nodiscard" doesn't really add much value.

Comment on lines +14 to +38
inline auto MakeRequest(
const http::verb method = http::verb::get, const std::string& target = "/",
const ad_utility::HashMap<http::field, std::string>& headers = {},
const std::optional<std::string>& body = std::nullopt) {
// version 11 stands for HTTP/1.1
auto req = http::request<http::string_body>{method, target, 11};
for (const auto& [key, value] : headers) {
req.set(key, value);
}
if (body.has_value()) {
req.body() = body.value();
req.prepare_payload();
}
return req;
}

inline auto MakeGetRequest(const std::string& target) {
return MakeRequest(http::verb::get, target);
}

inline auto MakePostRequest(const std::string& target,
const std::string& contentType,
const std::string& body) {
return MakeRequest(http::verb::post, target,
{{http::field::content_type, contentType}}, body);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thos are functions, so their names should be lowercase.

Comment on lines +26 to +27
EXPECT_THAT(GraphStoreProtocol::extractTargetGraph({{"graph", {"foo"}}}),
t::iri("<foo>"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case {"graph", {"foo", "bar"}} can also be tested (multiple graphs throw in this domain.

Comment on lines +156 to +179
AD_EXPECT_THROW_WITH_MESSAGE(
GraphStoreProtocol::transformGraphStoreProtocol(
ad_utility::testing::MakeRequest(boost::beast::http::verb::put,
"/?default")),
testing::HasSubstr("PUT in the SPARQL Graph Store HTTP Protocol"));
AD_EXPECT_THROW_WITH_MESSAGE(
GraphStoreProtocol::transformGraphStoreProtocol(
ad_utility::testing::MakeRequest(boost::beast::http::verb::delete_,
"/?default")),
testing::HasSubstr("DELETE in the SPARQL Graph Store HTTP Protocol"));
AD_EXPECT_THROW_WITH_MESSAGE(
GraphStoreProtocol::transformGraphStoreProtocol(
ad_utility::testing::MakeRequest(boost::beast::http::verb::head,
"/?default")),
testing::HasSubstr("HEAD in the SPARQL Graph Store HTTP Protocol"));
AD_EXPECT_THROW_WITH_MESSAGE(
GraphStoreProtocol::transformGraphStoreProtocol(
ad_utility::testing::MakeRequest(boost::beast::http::verb::patch,
"/?default")),
testing::HasSubstr("PATCH in the SPARQL Graph Store HTTP Protocol"));
AD_EXPECT_THROW_WITH_MESSAGE(
GraphStoreProtocol::transformGraphStoreProtocol(
ad_utility::testing::MakeRequest(boost::beast::http::verb::connect,
"/?default")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of code duplication here, can be mitigated.

TC(Var{"?s"}), "?p", TC(Var{"?o"}))}))));
EXPECT_THAT(
GraphStoreProtocol::transformGraphStoreProtocol(
ad_utility::testing::MakePostRequest(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A using namespace ad_utiility::testing inside this function would make the code a little shorter (not too important though).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants