Skip to content

Commit

Permalink
Adapt RDFox adapter to implement cursor for query:
Browse files Browse the repository at this point in the history
Summary:

Added RequestBuilder to manage HTTP interactions with RDFox, improving the consistency of request handling across different features.
Simplified request setup for various operations, such as creating connections, cursors, and querying.
Implemented functionality to support cursor-based queries for efficient data pagination.
Added unit tests to validate the creation of connections with RDFox.
Enhanced the mock setup for the RDFox Adapter to avoid code duplication in tests.
Added comprehensive integration tests for RDFox Adapter functionalities, ensuring compatibility with multiple formats and scenarios.
Verified the correctness of SPARQL query results across different RDF formats (e.g., Turtle, JSON-LD, RDF/XML).
Extended the query functionality to support various content types such as Turtle, CSV, JSON-LD, RDF/XML, and N-Triples.
Created parameterised tests to validate data consistency across supported input and output formats.
Update related documentation.
Tests to run new features:

rdfox_adapter_unit_test
rdfox_adapter_integration_test
Signed-off-by: q632394 <[email protected]>
  • Loading branch information
q632394 committed Jan 29, 2025
1 parent 98e2e73 commit f378d3e
Show file tree
Hide file tree
Showing 17 changed files with 1,538 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ void TripleAssembler::initialize() {
}
if (!model_config_.shacl_shapes_files.empty()) {
for (const auto& file : model_config_.shacl_shapes_files) {
const std::string ttl_data = file_handler_.readFile(file);
if (ttl_data.empty() || !rdfox_adapter_.loadData(ttl_data)) {
const std::string data = file_handler_.readFile(file);
if (data.empty() || !rdfox_adapter_.loadData(data)) {
throw std::runtime_error(
"No SHACL shapes could be loaded. The triples cannot be generated.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ target_link_libraries(triple_assembler_unit_tests
rdf_writer
test_fixtures
)
target_include_directories(triple_assembler_unit_tests
PRIVATE
${PROJECT_ROOT_DIR}/symbolic-reasoner/test/utils
)

# Add unit and integration tests to CTest
add_test(NAME TripleWriterIntegrationTests COMMAND triple_writer_integration_tests)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

#include "data_types.h"
#include "mock_adapter.h"
#include "rdfox_adapter.h"
#include "triple_assembler.h"
#include "vin_utils.h"
Expand All @@ -19,15 +20,6 @@ struct TestParamsTransformMessageToRDFTriple {
std::string data_triple_result;
};

class MockRDFoxAdapter : public RDFoxAdapter {
public:
MockRDFoxAdapter() : RDFoxAdapter("localhost", "8080", "test_auth", "test_ds") {}

MOCK_METHOD(bool, checkDataStore, (), (override));
MOCK_METHOD1(loadData, bool(const std::string& ttl_data));
MOCK_METHOD(std::string, queryData, (const std::string& sparql_query), (override));
};

class MockFileHandler : public IFileHandler {
public:
MOCK_METHOD(std::string, readFile, (const std::string& file_path), (override));
Expand Down Expand Up @@ -58,7 +50,7 @@ class TripleAssemblerUnitTest : public ::testing::Test {
DataMessage message_feature_;

// Set up mock RDFoxAdapter
MockRDFoxAdapter mock_adapter_;
MockAdapter mock_adapter_{"localhost", "8080", "test_auth", "test_ds"};
MockFileHandler mock_file_handler_;
MockTripleWriter mock_triple_writer_;

Expand Down Expand Up @@ -148,11 +140,11 @@ SELECT some_data_query)";
.WillRepeatedly(::testing::Return(query_data));

// Mock querying data using the SHACL queries and returning predefined responses
EXPECT_CALL(mock_adapter_, queryData(query_object))
EXPECT_CALL(mock_adapter_, queryData(query_object, ::testing::StrEq("table/csv")))
.Times(times_executing_object_related_functions)
.WillRepeatedly(testing::Return(query_object_response));

EXPECT_CALL(mock_adapter_, queryData(query_data))
EXPECT_CALL(mock_adapter_, queryData(query_data, ::testing::StrEq("table/csv")))
.Times(times_executing_data_related_functions)
.WillRepeatedly(testing::Return(query_data_response));
}
Expand Down Expand Up @@ -183,8 +175,10 @@ TEST_F(TripleAssemblerUnitTest, InitializeSuccess) {
.WillOnce(testing::Return("data2"));

// Mock loading data into the adapter and returning success
EXPECT_CALL(mock_adapter_, loadData(testing::StrEq("data1"))).WillOnce(testing::Return(true));
EXPECT_CALL(mock_adapter_, loadData(testing::StrEq("data2"))).WillOnce(testing::Return(true));
EXPECT_CALL(mock_adapter_, loadData(testing::StrEq("data1"), ::testing::_))
.WillOnce(testing::Return(true));
EXPECT_CALL(mock_adapter_, loadData(testing::StrEq("data2"), ::testing::_))
.WillOnce(testing::Return(true));

// Assert that the initialization process does not throw any exceptions
EXPECT_NO_THROW(triple_assembler->initialize());
Expand Down Expand Up @@ -266,7 +260,7 @@ car:Observation20181116155027O0
.WillOnce(testing::Return(dummy_ttl));

// Mock logging the generated TTL triples to RDFox
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl)))
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl), ::testing::_))
.Times(1)
.WillOnce(testing::Return(true));

Expand Down Expand Up @@ -321,8 +315,8 @@ TEST_F(TripleAssemblerUnitTest,
.WillRepeatedly(::testing::Return("MOCK QUERY FOR DATA PROPERTY"));

// Mock the SHACL queries responses (first and second node)
EXPECT_CALL(mock_adapter_, queryData("MOCK QUERY FOR OBJECTS PROPERTY")).Times(3);
EXPECT_CALL(mock_adapter_, queryData("MOCK QUERY FOR DATA PROPERTY")).Times(2);
EXPECT_CALL(mock_adapter_, queryData("MOCK QUERY FOR OBJECTS PROPERTY", ::testing::_)).Times(3);
EXPECT_CALL(mock_adapter_, queryData("MOCK QUERY FOR DATA PROPERTY", ::testing::_)).Times(2);

// Mock the data added to the RDF triples (only first node)
EXPECT_CALL(mock_triple_writer_, addRDFObjectToTriple(::testing::_, ::testing::_)).Times(3);
Expand All @@ -346,7 +340,7 @@ TEST_F(TripleAssemblerUnitTest,
;

// Mock logging the generated TTL triples to RDFox
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl)))
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl), ::testing::_))
.Times(1)
.WillOnce(testing::Return(true));

Expand Down Expand Up @@ -405,7 +399,7 @@ TEST_F(TripleAssemblerUnitTest, InitializeFailIfShaclShapesContentIsEmpty) {
.WillOnce(testing::Return("")); // The content cannot be empty

// Ensure no data loading is attempted with empty content
EXPECT_CALL(mock_adapter_, loadData(testing::_)).Times(0);
EXPECT_CALL(mock_adapter_, loadData(testing::_, ::testing::_)).Times(0);

// Attempt to initialize the TripleAssembler and expect a runtime error
TripleAssembler assembler(model_config_, mock_adapter_, mock_file_handler_,
Expand All @@ -427,7 +421,7 @@ TEST_F(TripleAssemblerUnitTest, InitializeFailsIfLoadingDataFromShaclShapesGoesW
.WillOnce(testing::Return("data"));

// Simulate a failure in loading the data, returning false
EXPECT_CALL(mock_adapter_, loadData(testing::StrEq("data")))
EXPECT_CALL(mock_adapter_, loadData(testing::StrEq("data"), ::testing::_))
.WillOnce(testing::Return(false)); // Fail loading data

// Initialize TripleAssembler and expect a runtime error to be thrown
Expand All @@ -451,7 +445,7 @@ TEST_F(TripleAssemblerUnitTest, TransformMessageToRDFTripleFailsWhenAnyRDFDataSt
EXPECT_CALL(mock_triple_writer_, initiateTriple(::testing::_)).Times(0);
EXPECT_CALL(mock_file_handler_, readFile(::testing::_)).Times(0);
EXPECT_CALL(mock_file_handler_, writeFile(::testing::_, ::testing::_, ::testing::_)).Times(0);
EXPECT_CALL(mock_adapter_, queryData(::testing::_)).Times(0);
EXPECT_CALL(mock_adapter_, queryData(::testing::_, ::testing::_)).Times(0);
EXPECT_CALL(mock_triple_writer_, addRDFObjectToTriple(::testing::_, ::testing::_)).Times(0);
EXPECT_CALL(mock_triple_writer_, addRDFDataToTriple(::testing::_, ::testing::_, ::testing::_,
::testing::_, ::testing::_))
Expand Down Expand Up @@ -508,7 +502,7 @@ TEST_F(TripleAssemblerUnitTest, TransformMessageToRDFTripleWithCoordinatesSucces
.WillRepeatedly(testing::Return(dummy_ttl));

// Mock logging the generated TTL triples to RDFox
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl)))
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl), ::testing::_))
.Times(1)
.WillOnce(testing::Return(true));

Expand Down Expand Up @@ -558,7 +552,7 @@ TEST_F(TripleAssemblerUnitTest, TransformMessageToRDFTripleFailsWhenCoordinatesA
.WillRepeatedly(testing::Return(dummy_ttl));

// Mock logging the generated TTL triples to RDFox
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl)))
EXPECT_CALL(mock_adapter_, loadData(::testing::StrEq(dummy_ttl), ::testing::_))
.Times(1)
.WillOnce(testing::Return(true));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ target_link_libraries(websocket_client_integration_tests
websocket_client
test_fixtures
)
target_include_directories(websocket_client_integration_tests
PRIVATE
${PROJECT_ROOT_DIR}/connector/websocket-client/tests/utils
)

# Add unit and integration tests to CTest
add_test(NAME WebsocketClientIntegrationTests COMMAND websocket_client_integration_tests)
Expand Down
2 changes: 2 additions & 0 deletions cdsp/knowledge-layer/symbolic-reasoner/rdfox/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Define the rdfox library
add_library(rdfox
src/rdfox_adapter.cpp
../utils/request_builder.cpp
)

# Include directories
target_include_directories(rdfox
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS}
${PROJECT_ROOT_DIR}/symbolic-reasoner/utils
)

# Link dependencies
Expand Down
4 changes: 2 additions & 2 deletions cdsp/knowledge-layer/symbolic-reasoner/rdfox/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# RDFox Integration

This folder contains the necessary files to interact with RDFox, a high-performance knowledge graph and reasoning engine used in this project.
This folder contains the necessary files to interact with RDFox using the [RDFox adapter](./src/README.md), a high-performance knowledge graph and reasoning engine used in this project.

## How to Use RDFox

Expand All @@ -13,7 +13,7 @@ See how to interact with the RDFox server using the [RDF assembler](/cdsp/knowle

### Getting Started

This project includes a small C++ application to verify that the RDFox service has been configured and started correctly. After compiling the project, you should be able to run the application from [`./rdfox-service-test/rdfox_test_main.cpp`](./rdfox-service-test/rdfox_test_main.cpp). The RDFox Test executable will be generated in the `/cdsp/knowledge-layer/build/bin/tests/` directory. You can run it with the following command:
This project includes a small C++ application to verify that the RDFox service has been configured and started correctly. After compiling the project, you should be able to run the application from [`./tests/rdfox-service-test/rdfox_test_main.cpp`](./tests/rdfox-service-test/rdfox_test_main.cpp). The RDFox Test executable will be generated in the `/cdsp/knowledge-layer/build/bin/tests/` directory. You can run it with the following command:

```bash
$ ./rdfox_test
Expand Down
101 changes: 90 additions & 11 deletions cdsp/knowledge-layer/symbolic-reasoner/rdfox/src/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,106 @@
# RDFoxAdapter

`RDFoxAdapter` is responsible for communicating with the RDFox server over HTTP. It checks if the datastore is active, loads RDF data, and queries the RDF data using [SPARQL](https://www.w3.org/TR/sparql11-query/).
`RDFoxAdapter` is responsible for communicating with the RDFox server using RESTful APIs. This adapter allows users to perform various operations such as creating connections, loading data, querying data (using [SPARQL](https://www.w3.org/TR/sparql11-query/)), and managing cursors efficiently.

> [!NOTE] Data store
> When the RDFoxAdapter initializes creates (if is does not exists) a datastore called `vehicle_ds` in the RDFox server.
>
> ## Features
- **Initialize a Datastore:** Checks if a specified datastore exists on the RDFox server. If not, it creates the datastore.
- **Load Data:** Loads Turtle data into a specified datastore.
- **Query Data:** Executes SPARQL queries against the RDFox datastore and retrieves results.
- **Delete Datastore:** Removes a specified datastore from the RDFox server.
## Features

**Example Usage**

- **Data Store Management**:
- Initialize and ensure the existence of the datastore.
- Load data into the datastore in various formats (e.g., Turtle, JSON-LD, RDF/XML).
- Delete the datastore.

- **Data Querying**:
- Query data using SPARQL queries.
- Support for multiple response content types such as `table/csv`, `application/sparql-results+json`, etc.

- **Connection Management**:
- Create and manage connections to the RDFox datastore.
- Validate existing connections.

- **Cursor Management**:
- Create cursors for large query results.
- Advance or open cursors for efficient pagination.
- Delete cursors after usage.

### Example Usage

**Initializing the Adapter**

```cpp
RDFoxAdapter adapter("<rdf_host>", "<port>", "<auth>", "<datastore>");
if (adapter.checkDataStore()) {
adapter.loadData("<your_ttl_data>");
std::string result = adapter.queryData("SELECT ?s ?p ?o WHERE { ?s ?p ?o }");
#include "rdfox_adapter.h"

int main() {
RDFoxAdapter adapter("<rdf_host>", "<port>", "<auth>", "<datastore>");

try {
adapter.initialize();
std::cout << "Datastore initialized successfully!" << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error initializing datastore: " << e.what() << std::endl;
}

return 0;
}
```

**Loading Data**

```cpp
std::string turtle_data = R"(
@prefix ex: <http://example.com/> .
ex:subject a ex:Object .
)";
adapter.loadData(turtle_data, "text/turtle");
```

**Querying Data**

```cpp
std::string sparql_query = "SELECT ?s ?p ?o WHERE { ?s ?p ?o }";
std::string result = adapter.queryData(sparql_query, "application/sparql-results+json");
std::cout << "Query Result: " << result << std::endl;
```

**Managing Cursors**
```cpp
std::pair<std::string, std::string> connection = adapter.createConnection();
std::string cursor_id = adapter.createCursor(connection.first, connection.second, sparql_query);

std::string cursor_result;
adapter.advanceCursor(connection.first, connection.second, cursor_id,
"application/sparql-results+json", "open", 100, &cursor_result);

std::cout << "Cursor Result: " << cursor_result << std::endl;

adapter.deleteCursor(connection.first, cursor_id);
```

> [!NOTE] Allowed Operations for Advancing the Cursor
> The `RDFoxAdapter::advanceCursor` method allows two operations:
> - **`open`:** Opens the cursor for the first time and retrieves data starting from the beginning.
> - **`advance`:** Advances the cursor from its current position to the next set of results.
## Supported Data Formats

### For Loading Data

- text/turtle
- application/ld+json
- application/n-triples
- application/n-quads

### For Query Responses

- table/csv
- text/plain
- application/sparql-results+json
- application/sparql-results+xml

# Testing

Unit and Integration [tests](../tests/) are provided to ensure functionality. Tests cover the main components (`RDFoxAdapter`) to verify RDF data transfer, and error handling.
Loading

0 comments on commit f378d3e

Please sign in to comment.