Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package eu.knowledge.engine.rest.api;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

import eu.knowledge.engine.rest.RestServerHelper;
import eu.knowledge.engine.test_utils.AsyncTester;
import eu.knowledge.engine.test_utils.HttpTester;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestAskAnswerIncludeMetaKIs {
private final RestServerHelper rsh = new RestServerHelper();
private static int PORT = 8280;

@BeforeAll
public void setUpServer() {
rsh.start(PORT);
}

@Test
public void testAskAnswer() throws IOException, InterruptedException {

// In this test there will be an Ask KB with a single AskKI and
// an AnswerKB with a single AnswerKI that answers only part of the Ask pattern.
// The test will execute the AskKI with knowledge gaps enabled.
// As a result, the set of knowledge gaps should contain a single gap.

CountDownLatch KBReady = new CountDownLatch(1);

URL url = new URL("http://localhost:" + PORT + "/rest");

// activate the answer SC, KB, KI in a separate thread
var otherSc = new AsyncTester(new Runnable() {
@Override
public void run() {
try {
// register the AnswerKB
HttpTester registerOtherKb = new HttpTester(new URL(url + "/sc"), "POST",
"{\"knowledgeBaseId\": \"https://www.tno.nl/example/otherKB\", \"knowledgeBaseName\": \"RelationProvider\", \"knowledgeBaseDescription\": \"A KB that provides relations between people\", \"reasonerLevel\" : 2}",
Map.of("Content-Type", "application/json", "Accept", "*/*"));
registerOtherKb.expectStatus(200);
KBReady.countDown();
} catch (MalformedURLException e) {
fail();
}
}
});
otherSc.start();

KBReady.await();

// register the AnswerKB
HttpTester registerAskKb = new HttpTester(new URL(url + "/sc"), "POST",
"{\"knowledgeBaseId\": \"https://www.tno.nl/example/metadataAsker\", \"knowledgeBaseName\": \"RelationProvider\", \"knowledgeBaseDescription\": \"A KB that provides relations between people\", \"reasonerLevel\" : 2}",
Map.of("Content-Type", "application/json", "Accept", "*/*"));
registerAskKb.expectStatus(200);

// register the AskKI with IncludeMetaKIs enabled
HttpTester registerAskKiWithIncludeMetaKIsEnabled = new HttpTester(new URL(url + "/sc/ki"), "POST",
"{\"knowledgeInteractionType\": \"AskKnowledgeInteraction\", \"knowledgeInteractionName\": \"askMetadataWithIncludeMetaKIs\", \"includeMetaKIs\": \"true\", \"graphPattern\": \"?kb <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/knowledge-engine/KnowledgeBase> .\"}",
Map.of("Knowledge-Base-Id", "https://www.tno.nl/example/metadataAsker", "Content-Type",
"application/json", "Accept", "*/*"));
registerAskKiWithIncludeMetaKIsEnabled.expectStatus(200);

// register the AskKI without IncludeMetaKIs (= disabled)
HttpTester registerAskKiWithoutIncludeMetaKIs = new HttpTester(new URL(url + "/sc/ki"), "POST",
"{\"knowledgeInteractionType\": \"AskKnowledgeInteraction\", \"knowledgeInteractionName\": \"askMetadataWithoutIncludeMetaKIs\", \"graphPattern\": \"?kb <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/knowledge-engine/KnowledgeBase> .\"}",
Map.of("Knowledge-Base-Id", "https://www.tno.nl/example/metadataAsker", "Content-Type",
"application/json", "Accept", "*/*"));
registerAskKiWithoutIncludeMetaKIs.expectStatus(200);

// register the AskKI with IncludeMetaKIs disabled
HttpTester registerAskKiWithIncludeMetaKIsDisabled = new HttpTester(new URL(url + "/sc/ki"), "POST",
"{\"knowledgeInteractionType\": \"AskKnowledgeInteraction\", \"knowledgeInteractionName\": \"askMetadataWithIncludeMetaKIsDisabled\", \"includeMetaKIs\": \"false\", \"graphPattern\": \"?kb <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/knowledge-engine/KnowledgeBase> .\"}",
Map.of("Knowledge-Base-Id", "https://www.tno.nl/example/metadataAsker", "Content-Type",
"application/json", "Accept", "*/*"));
registerAskKiWithIncludeMetaKIsDisabled.expectStatus(200);

// start asking

HttpTester askAskKiWithIncludeMetaKIs = new HttpTester(new URL(url + "/sc/ask"), "POST",
"{\"recipientSelector\": {\"knowledgeBases\": []}, \"bindingSet\": []}",
Map.of("Knowledge-Base-Id", "https://www.tno.nl/example/metadataAsker", "Knowledge-Interaction-Id",
"https://www.tno.nl/example/metadataAsker/interaction/askMetadataWithIncludeMetaKIs",
"Content-Type", "application/json", "Accept", "*/*"));
System.out.println("Result is:" + askAskKiWithIncludeMetaKIs.getBody());

JsonReader jsonReader = Json.createReader(new StringReader(askAskKiWithIncludeMetaKIs.getBody()));
JsonObject jsonRoot = jsonReader.readObject();
JsonArray jsonBindingSet = jsonRoot.getJsonArray("bindingSet");
assertEquals(1, jsonBindingSet.size());

HttpTester askAskKiWithoutIncludeMetaKIs = new HttpTester(new URL(url + "/sc/ask"), "POST",
"{\"recipientSelector\": {\"knowledgeBases\": []}, \"bindingSet\": []}",
Map.of("Knowledge-Base-Id", "https://www.tno.nl/example/metadataAsker", "Knowledge-Interaction-Id",
"https://www.tno.nl/example/metadataAsker/interaction/askMetadataWithoutIncludeMetaKIs",
"Content-Type", "application/json", "Accept", "*/*"));
System.out.println("Result is:" + askAskKiWithoutIncludeMetaKIs.getBody());
jsonReader = Json.createReader(new StringReader(askAskKiWithoutIncludeMetaKIs.getBody()));
jsonRoot = jsonReader.readObject();
jsonBindingSet = jsonRoot.getJsonArray("bindingSet");
assertEquals(0, jsonBindingSet.size());

HttpTester askAskKiWithIncludeMetaKIsDisabled = new HttpTester(new URL(url + "/sc/ask"), "POST",
"{\"recipientSelector\": {\"knowledgeBases\": []}, \"bindingSet\": []}",
Map.of("Knowledge-Base-Id", "https://www.tno.nl/example/metadataAsker", "Knowledge-Interaction-Id",
"https://www.tno.nl/example/metadataAsker/interaction/askMetadataWithIncludeMetaKIsDisabled",
"Content-Type", "application/json", "Accept", "*/*"));
System.out.println("Result is:" + askAskKiWithIncludeMetaKIsDisabled.getBody());
jsonReader = Json.createReader(new StringReader(askAskKiWithIncludeMetaKIsDisabled.getBody()));
jsonRoot = jsonReader.readObject();
jsonBindingSet = jsonRoot.getJsonArray("bindingSet");
assertEquals(0, jsonBindingSet.size());

}

@AfterAll
public void cleanUp() throws MalformedURLException {

TestUtil.unregisterAllKBs("http://localhost:" + PORT + "/rest");
rsh.cleanUp();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ public BindingSet react(ReactKnowledgeInteraction aRKI, ReactExchangeInfo aReact

private boolean suspended = false;

private Thread shutdownHook = new Thread(() -> {
this.stop();
});

public RestKnowledgeBase(eu.knowledge.engine.rest.model.SmartConnector scModel, final Runnable onReady) {
this.knowledgeBaseId = scModel.getKnowledgeBaseId();
this.knowledgeBaseName = scModel.getKnowledgeBaseName();
Expand Down Expand Up @@ -236,9 +240,7 @@ protected boolean removeEldestEntry(Map.Entry<Integer, HandleRequest> eldest) {
if (scModel.getReasonerLevel() != null)
this.sc.setReasonerLevel(scModel.getReasonerLevel());

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
this.stop();
}));
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}

protected void tryProcessHandleRequestElseEnqueue(HandleRequest handleRequest) {
Expand Down Expand Up @@ -383,7 +385,11 @@ public String register(KnowledgeInteractionBase ki) {
prefixMapping = new PrefixMappingZero();
}

// TODO both {@code knowledgeGapsEnabled} and {@code includeMetaKIs} are only
// used for proactive KIs, so maybe move them from the {@code
// KnowledgeInteractionBase} to the specific KI type classes.
boolean knowledgeGapsEnabled = ki.getKnowledgeGapsEnabled() == null ? false : ki.getKnowledgeGapsEnabled();
boolean includeMetaKIs = ki.getIncludeMetaKIs() == null ? false : ki.getIncludeMetaKIs();

String type = ki.getKnowledgeInteractionType();
URI kiId;
Expand All @@ -403,7 +409,7 @@ public String register(KnowledgeInteractionBase ki) {
}

var askKI = new AskKnowledgeInteraction(ca, new GraphPattern(prefixMapping, aki.getGraphPattern()),
ki.getKnowledgeInteractionName(), false, false, knowledgeGapsEnabled, strategy);
ki.getKnowledgeInteractionName(), false, includeMetaKIs, knowledgeGapsEnabled, strategy);

kiId = this.sc.register(askKI);
this.knowledgeInteractions.put(kiId, askKI);
Expand Down Expand Up @@ -439,7 +445,8 @@ public String register(KnowledgeInteractionBase ki) {
"At least one of argumentGraphPattern and resultGraphPattern must be given for POST knowledge interactions.");
}

var postKI = new PostKnowledgeInteraction(ca, argGP, resGP, ki.getKnowledgeInteractionName());
var postKI = new PostKnowledgeInteraction(ca, argGP, resGP, ki.getKnowledgeInteractionName(), false,
includeMetaKIs, null /* default strategy */);
kiId = this.sc.register(postKI);

this.knowledgeInteractions.put(kiId, postKI);
Expand Down Expand Up @@ -746,6 +753,8 @@ public void stop() {
this.cancelInactivityTimeout();
try {
this.sc.stop().get();
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);

} catch (InterruptedException | ExecutionException e) {
LOG.error("An error occurred while stopping SC of KB '{}'.", this.knowledgeBaseId);
}
Expand Down
12 changes: 12 additions & 0 deletions smart-connector-rest-server/src/main/resources/openapi-sc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ paths:
provide a set of knowledge gaps as part of the result. If this set is empty, the binding set of the
result contains the answer. If the set of knowledge gaps is not empty, this contains one or more
knowledge gaps that need to be fixed for the knowledge interaction to produce an answer.

The body of a KI can also contain the option "includeMetaKIs" (see example 'ASK with meta KIs included').
This options is disabled by defauilt and when enabled will take meta knowledge interactions of other
Smart Connector into account. This means that meta data of the knowledge network can be requested
such as KB id, names, descriptions, knowledge interactions, etc. For more information see https://www.knowledge-engine.eu/ontology
content:
application/json; charset=UTF-8:
schema:
Expand Down Expand Up @@ -224,6 +229,11 @@ paths:
knowledgeInteractionType: AskKnowledgeInteraction
graphPattern: "?a <http://example.org/isRelatedTo> ?b ."
knowledgeGapsEnabled: "true"
ASK with meta KIs included:
value:
knowledgeInteractionType: AskKnowledgeInteraction
graphPattern: "?a <http://example.org/isRelatedTo> ?b ."
includeMetaKIs: true
responses:
'200':
description: If the Knowledge Interaction is successfully registered, it returns the KnowledgeInteractionId object.
Expand Down Expand Up @@ -670,6 +680,8 @@ components:
pattern: '^[a-zA-Z0-9-]*$'
knowledgeGapsEnabled:
type: boolean
includeMetaKIs:
type: boolean
communicativeAct:
$ref: '#/components/schemas/CommunicativeAct'
prefixes:
Expand Down