diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..44d89841 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 569221e5..2b7a0b33 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/docker-deployment.yml b/.github/workflows/docker-deployment.yml index b688de41..41a1b066 100644 --- a/.github/workflows/docker-deployment.yml +++ b/.github/workflows/docker-deployment.yml @@ -11,15 +11,15 @@ jobs: STARDOG_PASSWORD: ${{ secrets.STARDOG_PASSWORD }} steps: - name: Docker Login - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build the Docker images and Push to Dockerhub run: bash -c ./service_config/build_images.sh - name: Init update - uses: MindMaster98/docker-service-updater@v0.1.5 + uses: WSE-research/docker-service-updater@v0.2.1 with: api_key: ${{ secrets.API_KEY }} updater_host: ${{ secrets.UPDATER_HOST }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 74c342fe..89ee6e0a 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -15,10 +15,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 + distribution: 'adopt' - name: Build with Maven run: mvn -B package --file pom.xml diff --git a/qald-evaluator/pom.xml b/qald-evaluator/pom.xml index 667d8409..0b9e5aa9 100644 --- a/qald-evaluator/pom.xml +++ b/qald-evaluator/pom.xml @@ -3,7 +3,7 @@ 4.0.0 eu.wdaqua.qanary qald.evaluator - 1.0.2 + 1.0.4 org.springframework.boot @@ -65,7 +65,7 @@ net.minidev json-smart - 2.4.8 + 2.4.9 org.slf4j @@ -91,7 +91,7 @@ org.json json - 20200518 + 20230227 org.apache.jena diff --git a/qanary-configuration-frontend/package-lock.json b/qanary-configuration-frontend/package-lock.json index 29dfc1d8..d92a74b7 100644 --- a/qanary-configuration-frontend/package-lock.json +++ b/qanary-configuration-frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "qanary-configuration-frontend", "version": "1.0.3", "dependencies": { + "@babel/core": "7.20.12", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^7.2.1", @@ -17,14 +18,14 @@ "react-scripts": "^5.0.1" }, "devDependencies": { - "@babel/core": "^7.20.12", + "@babel/core": "7.20.12", "axios": "^0.21.2" } }, "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==" }, "node_modules/@ampproject/remapping": { "version": "2.1.1", @@ -4106,9 +4107,9 @@ } }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "bin": { "acorn": "bin/acorn" }, @@ -6426,9 +6427,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", - "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -10997,11 +10998,6 @@ "node": ">=4" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -15628,9 +15624,9 @@ } }, "node_modules/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -15656,33 +15652,33 @@ } }, "node_modules/webpack": { - "version": "5.69.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.0.tgz", - "integrity": "sha512-E5Fqu89Gu8fR6vejRqu26h8ld/k6/dCVbeGUcuZjc+goQHDfCPU9rER71JmdtBYGmci7Ec2aFEATQ2IVXKy2wg==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.0", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { @@ -16106,9 +16102,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "engines": { "node": ">=0.10.0" } @@ -16569,9 +16565,9 @@ }, "dependencies": { "@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==" }, "@ampproject/remapping": { "version": "2.1.1", @@ -19446,9 +19442,9 @@ } }, "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, "acorn-globals": { "version": "6.0.0", @@ -21148,9 +21144,9 @@ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "enhanced-resolve": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", - "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -24399,11 +24395,6 @@ "jsesc": { "version": "2.5.2" }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -27633,9 +27624,9 @@ } }, "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -27655,33 +27646,33 @@ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" }, "webpack": { - "version": "5.69.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.0.tgz", - "integrity": "sha512-E5Fqu89Gu8fR6vejRqu26h8ld/k6/dCVbeGUcuZjc+goQHDfCPU9rER71JmdtBYGmci7Ec2aFEATQ2IVXKy2wg==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", + "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.0", + "enhanced-resolve": "^5.10.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "dependencies": { @@ -27971,9 +27962,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==" }, "workbox-background-sync": { "version": "6.4.2", diff --git a/qanary_commons/pom.xml b/qanary_commons/pom.xml index 3b74371d..eafeef03 100644 --- a/qanary_commons/pom.xml +++ b/qanary_commons/pom.xml @@ -2,18 +2,20 @@ 4.0.0 eu.wdaqua.qanary qa.commons - 3.7.1 + 3.8.3 org.springframework.boot spring-boot-starter-parent 2.6.7 + + 11 - 2.6.7 + 2.7.10 ${java.version} ${java.version} - 4.4.0 + 4.7.0 [8.0.0,9.0.0) 4.4.0 @@ -71,7 +73,7 @@ org.json json - 20190722 + 20230227 com.vaadin.external.google @@ -242,13 +244,13 @@ com.google.code.gson gson - 2.10 + 2.10.1 compile org.glassfish.jaxb jaxb-runtime - 4.0.0 + 4.0.2 jakarta.xml.bind @@ -297,6 +299,19 @@ + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + - \ No newline at end of file + diff --git a/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/QanaryQuestion.java b/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/QanaryQuestion.java index 76945ddf..196dd35f 100644 --- a/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/QanaryQuestion.java +++ b/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/QanaryQuestion.java @@ -9,11 +9,12 @@ import org.apache.jena.atlas.json.JSON; import org.apache.jena.atlas.json.JsonObject; import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.QuerySolutionMap; import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.ResourceFactory; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -40,7 +41,7 @@ public class QanaryQuestion { private static final Logger logger = LoggerFactory.getLogger(QanaryQuestion.class); private QanaryMessage qanaryMessage; - private QanaryUtils qanaryUtil; + private QanaryUtils qanaryUtils; private T raw; // note: uri not final due to different sources for the value (some from // triplestore, some pre-set) @@ -54,6 +55,9 @@ public class QanaryQuestion { private QanaryConfigurator myQanaryConfigurator; + private String FILENAME_SELECT_TRANSLATION_ANNOTATION = "/queries/select_all_AnnotationOfQuestionTranslation.rq"; + private String FILENAME_SELECT_URI_TEXTUAL_REPRESENTATION = "/queries/select_uri_textual_representation.rq"; + /** * init the graph in the triplestore (c.f., application.properties), a new graph * is constructed @@ -169,7 +173,7 @@ public QanaryQuestion(QanaryMessage qanaryMessage, final QanaryConfigurator myQa public QanaryQuestion(QanaryMessage qanaryMessage, final QanaryTripleStoreConnector myQanaryTripleStoreConnector) { this.qanaryMessage = qanaryMessage; this.myQanaryTripleStoreConnector = myQanaryTripleStoreConnector; - this.qanaryUtil = new QanaryUtils(qanaryMessage, myQanaryTripleStoreConnector); + this.qanaryUtils = new QanaryUtils(qanaryMessage, myQanaryTripleStoreConnector); // save where the answer is stored this.namedGraph = qanaryMessage.getInGraph(); } @@ -201,7 +205,7 @@ public QanaryTripleStoreConnector getQanaryTripleStoreConnector() { */ private void initFromTriplestore(final QanaryConfigurator myQanaryConfigurator) throws URISyntaxException { this.qanaryMessage = new QanaryMessage(myQanaryConfigurator.getEndpoint(), namedGraph); - this.qanaryUtil = new QanaryUtils(this.qanaryMessage, this.getQanaryTripleStoreConnector()); + this.qanaryUtils = new QanaryUtils(this.qanaryMessage, this.getQanaryTripleStoreConnector()); } /** @@ -376,22 +380,17 @@ public T getRawData() throws Exception { */ public URI getUriTextualRepresentation() throws Exception { if (this.uriTextualRepresentation == null) { - String sparql = "" // - + "PREFIX qa: " // - + "PREFIX oa: " // - + "PREFIX xsd: " // - + "SELECT ?uri " // - + "FROM <" + this.getInGraph() + "> { " // - + " ?a a qa:AnnotationOfTextRepresentation . " // - + " ?a oa:hasBody ?uri " // - + "}"; // - - ResultSet resultset = this.getQanaryTripleStoreConnector().select(sparql); + QuerySolutionMap bindingsForSelect = new QuerySolutionMap(); + bindingsForSelect.add("graph", ResourceFactory.createResource(this.getOutGraph().toASCIIString())); + String sparql = QanaryTripleStoreConnector.readFileFromResourcesWithMap(FILENAME_SELECT_URI_TEXTUAL_REPRESENTATION, bindingsForSelect); + logger.info("SPARQL query: {}", sparql); + ResultSet resultSet = this.qanaryUtils.getQanaryTripleStoreConnector().select(sparql); + int i = 0; String uriTextRepresentation = null; - while (resultset.hasNext()) { - uriTextRepresentation = resultset.next().get("uri").asResource().getURI(); + while (resultSet.hasNext()) { + uriTextRepresentation = resultSet.next().get("uri").asResource().getURI(); logger.debug("{}: qa#Question = {}", i++, uriTextRepresentation); } if (i > 1) { @@ -420,6 +419,47 @@ public String getTextualRepresentation() throws Exception { return responseRaw.getBody(); } + public String getTextualRepresentation(String language) throws Exception { + // attempt to get original question language, if annotated by LD component + String questionLanguage = ""; + try { + questionLanguage = this.getLanguage(); + logger.info("Language of original text representation: {}", questionLanguage); + if (questionLanguage.equals(language)) { + // if language alrady matches, return getTextualRepresentation + return this.getTextualRepresentation(); + } + } catch (Exception e) { + logger.warn("Language of original text representation is not known!\n{}", e.getMessage()); + } if (language.trim().length() == 2) { + // look for annotation of translation with matching language + return this.getTranslatedTextualRepresentation(language); + } else { + throw new Exception("parameter `language` is invalid: " + language); + } + } + + public String getTranslatedTextualRepresentation(String language) throws Exception { + QuerySolutionMap bindingsForSelect = new QuerySolutionMap(); + bindingsForSelect.add("graph", ResourceFactory.createResource(this.getOutGraph().toASCIIString())); + bindingsForSelect.add("targetQuestion", ResourceFactory.createResource(this.getUri().toASCIIString())); + bindingsForSelect.add("language", ResourceFactory.createStringLiteral(language)); + + String sparql = QanaryTripleStoreConnector.readFileFromResourcesWithMap(FILENAME_SELECT_TRANSLATION_ANNOTATION, bindingsForSelect); + logger.info("SPARQL query: {}", sparql); + ResultSet resultSet = this.qanaryUtils.getQanaryTripleStoreConnector().select(sparql); + + while (resultSet.hasNext()) { + QuerySolution result = resultSet.next(); + String translatedQuestionString = result.get("translation").asLiteral().getString(); + // take the first best result + return translatedQuestionString; + } + // if nothing was found + throw new Exception("No uriTextRepresentation available in graph " + this.getInGraph() + + " for language " + language + " at " + this.getEndpoint()); + } + /** * get the uri of the audio representation of the question */ @@ -518,7 +558,7 @@ public void addAnnotations(Collection selectors) throws Ex + " ] ; " // + resourceSparql // + scoreSparql // - + " oa:annotatedBy <" + qanaryUtil.getComponentUri() + "> ; " // + + " oa:annotatedBy <" + qanaryUtils.getComponentUri() + "> ; " // + " oa:annotatedAt ?time . " // + "}} WHERE { " // + " BIND (IRI(str(RAND())) AS ?a) ." // @@ -545,14 +585,16 @@ public List getSparqlResults() throws SparqlQueryFailed { + " } \n" // + " OPTIONAL { ?a qa:score ?confidence . } \n" // + " OPTIONAL { ?a qa:overKnowledgeGraph ?kb . } \n" // - + " ?a oa:annotatedAt ?time1 . \n" // - + " { \n" // - + " SELECT ?time1 { \n" // - + " ?a a qa:AnnotationOfAnswerSPARQL . \n" // - + " ?a oa:annotatedAt ?time1 . \n" // - + " } \n" // - + " ORDER BY DESC(?time1) \n" // - + " LIMIT 1 \n" // + + " OPTIONAL { \n" // + + " ?a oa:annotatedAt ?time1 . \n" // + + " { \n" // + + " SELECT ?time1 { \n" // + + " ?a a qa:AnnotationOfAnswerSPARQL . \n" // + + " ?a oa:annotatedAt ?time1 . \n" // + + " } \n" // + + " ORDER BY DESC(?time1) \n" // + + " LIMIT 1 \n" // + + " } \n" // + " } \n" // + "} \n" // + "ORDER BY DESC(?score) \n"; @@ -582,11 +624,11 @@ public class SparqlAnnotation { } public String getSparqlResult() throws SparqlQueryFailed { - // added try and catch to prevent indexOutOfBoundsException, returns empty String if no Query was found + // added try and catch to prevent indexOutOfBoundsException and NullPointerException, returns empty String if no Query was found String sparqlResult = ""; try { sparqlResult = this.getSparqlResults().get(0).query; - } catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException | NullPointerException e) { logger.warn("No SPARQL Query found, index out of bounds"); } return sparqlResult; @@ -603,21 +645,23 @@ public String getJsonResult() throws SparqlQueryFailed { + " ?a a qa:AnnotationOfAnswerJson . " // + " ?a oa:hasBody ?answer . " // + " ?answer rdf:value ?json . " // -// + " ?a a qa:AnnotationOfAnswerJSON . " // -// + " ?a oa:hasBody ?json " // -// TODO: this should be body of AnswerJson with rdf:value answer + "}"; logger.debug("getJsonResult: SELECT using:\n{}", sparql); ResultSet resultset = this.getQanaryTripleStoreConnector().select(sparql); - // the default value has to be null to distinguish missing values from empty values + // OLD: the default value has to be null to distinguish missing values from empty values + // NEW: returning null would result in "null" being part of the response JSON, + // return an empty string instead. + // This way, a distinction is still possible: + // missing values: "" + // empty values: [] String sparqlAnnotation = null; while (resultset.hasNext()) { sparqlAnnotation = resultset.next().get("json").asLiteral().toString(); } if (sparqlAnnotation == null) { - return sparqlAnnotation; + return ""; } else { return sparqlAnnotation.replace("\\\"", "\""); } @@ -627,7 +671,7 @@ public void putTextRepresentation(String text) throws Exception { RestTemplate restTemplate = new RestTemplate(); MultiValueMap map = new LinkedMultiValueMap(); map.add("question", text); - String r = restTemplate.postForObject(qanaryUtil.getHostUri() + "/question", map, String.class); + String r = restTemplate.postForObject(qanaryUtils.getHostUri() + "/question", map, String.class); logger.info("DEBUG {}", r); JSONObject obj = new JSONObject(r); String uriTextRepresention = obj.get("questionURI").toString(); @@ -641,7 +685,7 @@ public void putTextRepresentation(String text) throws Exception { + " ?a a qa:AnnotationOfTextualRepresentation . " // + " ?a oa:hasTarget <" + this.getUri() + "> . " // + " ?a oa:hasBody <" + uriTextRepresention + "> ;" // - + " oa:annotatedBy <" + qanaryUtil.getComponentUri() + "> ; " // + + " oa:annotatedBy <" + qanaryUtils.getComponentUri() + "> ; " // + " oa:AnnotatedAt ?time " // + "}} " // + "WHERE { " // diff --git a/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/triplestoreconnectors/QanaryTripleStoreConnector.java b/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/triplestoreconnectors/QanaryTripleStoreConnector.java index 64a9a19c..29cd3138 100644 --- a/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/triplestoreconnectors/QanaryTripleStoreConnector.java +++ b/qanary_commons/src/main/java/eu/wdaqua/qanary/commons/triplestoreconnectors/QanaryTripleStoreConnector.java @@ -148,6 +148,18 @@ public static String getHighestScoreAnnotationOfAnswerInGraph(URI graph) throws return query.toString(); } + /** + * add AnnotationAnswer and AnnotationOfAnswerType to allow annotating typed literals + * as it may be required by Qanary QueryBuilder components + * + * @param bindings + * @return + * @throws IOException + */ + public static String insertAnnotationOfTypedLiteral(QuerySolutionMap bindings) throws IOException { + return readFileFromResourcesWithMap("/queries/insert_one_AnnotationOfTypedLiteral.rq", bindings); + } + /** * add AnnotationOfAnswerSPARQL as it is done typically in Qanary QueryBuilder * components diff --git a/qanary_commons/src/main/resources/queries/insert_one_AnnotationOfTypedLiteral.rq b/qanary_commons/src/main/resources/queries/insert_one_AnnotationOfTypedLiteral.rq new file mode 100644 index 00000000..8649deae --- /dev/null +++ b/qanary_commons/src/main/resources/queries/insert_one_AnnotationOfTypedLiteral.rq @@ -0,0 +1,36 @@ +PREFIX dbr: +PREFIX oa: +PREFIX qa: +PREFIX rdf: +PREFIX xsd: + +INSERT { +GRAPH ?graph { + ?newAnnotation rdf:type qa:AnnotationAnswer . + ?newAnnotation oa:hasTarget ?targetQuestion . + ?newAnnotation oa:hasBody ?answer . + ?newAnnotation qa:score ?confidence . + ?newAnnotation oa:annotatedAt ?time . + ?newAnnotation oa:annotatedBy ?application . + + ?answer rdf:type qa:Answer . + ?answer rdf:value ?answerValue . + + ?newTypeAnnotation rdf:type qa:AnnotationOfAnswerType . + ?newTypeAnnotation oa:hasTarget ?targetQuestion . + ?newTypeAnnotation oa:hasBody ?annotationOfAnswerType . + ?newTypeAnnotation qa:score ?confidence . + ?newTypeAnnotation oa:annotatedAt ?time . + ?newTypeAnnotation oa:annotatedBy ?application . + + ?answerType rdf:type qa:AnswerType . + ?answerType rdf:value ?answerDataType . + } +} +WHERE { + BIND (IRI(str(RAND())) AS ?newAnnotation) . + BIND (IRI(str(RAND())) AS ?answer) . + BIND (IRI(str(RAND())) AS ?newTypeAnnotation) . + BIND (IRI(str(RAND())) AS ?answerType) . + BIND (now() as ?time) . +} diff --git a/qanary_commons/src/main/resources/queries/select_all_AnnotationOfQuestionTranslation.rq b/qanary_commons/src/main/resources/queries/select_all_AnnotationOfQuestionTranslation.rq new file mode 100644 index 00000000..8774205e --- /dev/null +++ b/qanary_commons/src/main/resources/queries/select_all_AnnotationOfQuestionTranslation.rq @@ -0,0 +1,11 @@ +PREFIX qa: +PREFIX oa: + +SELECT ?a ?translation ?time +FROM ?graph +WHERE { + ?a a qa:AnnotationOfQuestionTranslation . + ?a oa:hasTarget ?targetQuestion . + ?a oa:hasBody ?translation . + FILTER (lang(?translation) = ?language). +} diff --git a/qanary_commons/src/main/resources/queries/select_uri_textual_representation.rq b/qanary_commons/src/main/resources/queries/select_uri_textual_representation.rq new file mode 100644 index 00000000..cdccb359 --- /dev/null +++ b/qanary_commons/src/main/resources/queries/select_uri_textual_representation.rq @@ -0,0 +1,9 @@ +PREFIX qa: +PREFIX oa: +SELECT ?uri +FROM ?graph { + ?q a qa:Question . + ?a a qa:AnnotationOfTextRepresentation . + ?a oa:hasTarget ?q . + ?a oa:hasBody ?uri . +} diff --git a/qanary_commons/src/test/java/qa/commons/QanaryCacheTest.java b/qanary_commons/src/test/java/qa/commons/QanaryCacheTest.java new file mode 100644 index 00000000..8e013d38 --- /dev/null +++ b/qanary_commons/src/test/java/qa/commons/QanaryCacheTest.java @@ -0,0 +1,99 @@ +package qa.commons; + +import eu.wdaqua.qanary.communications.CacheOfRestTemplateResponse; +import eu.wdaqua.qanary.communications.RestTemplateWithCaching; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +public class QanaryCacheTest { + private static final Logger LOGGER = LoggerFactory.getLogger(QanaryCacheTest.class); + + private final int testPort; + // time span for caching, tests wait this time span during the test runs + private final int maxTimeSpanSeconds; + private final RestTemplateWithCaching myRestTemplate; + private final CacheOfRestTemplateResponse myCacheOfResponse; + + public QanaryCacheTest( + int testPort, // + int maxTimeSpanSeconds, // + @Autowired RestTemplateWithCaching myRestTemplate, // + @Autowired CacheOfRestTemplateResponse myCacheOfResponse // + ) { + this.testPort = testPort; + this.maxTimeSpanSeconds = maxTimeSpanSeconds; + this.myRestTemplate = myRestTemplate; + this.myCacheOfResponse = myCacheOfResponse; + } + + /** + * @throws InterruptedException + * @throws URISyntaxException + */ + public void givenRestTemplate_whenRequested_thenLogAndModifyResponse() throws InterruptedException, URISyntaxException { + + assertNotNull(this.myRestTemplate); + assertNotNull(this.myCacheOfResponse); + + URI testServiceURL0 = new URI("http://localhost:" + this.testPort + "/"); + URI testServiceURL1 = new URI("http://localhost:" + this.testPort + "/component-description"); + + long initialNumberOfRequests = this.myCacheOfResponse.getNumberOfExecutedRequests(); + + callRestTemplateWithCaching(testServiceURL0, Cache.NOT_CACHED); // cache miss + callRestTemplateWithCaching(testServiceURL0, Cache.CACHED); // cache hit + callRestTemplateWithCaching(testServiceURL0, Cache.CACHED); // cache hit + + TimeUnit.SECONDS.sleep(this.maxTimeSpanSeconds + 5); // wait until it is too late for caching + + callRestTemplateWithCaching(testServiceURL0, Cache.NOT_CACHED); // cache miss: too long ago + callRestTemplateWithCaching(testServiceURL0, Cache.CACHED); // cache hit + callRestTemplateWithCaching(testServiceURL1, Cache.NOT_CACHED); // cache miss: different URI + callRestTemplateWithCaching(testServiceURL0, Cache.CACHED); // cache hit + callRestTemplateWithCaching(testServiceURL1, Cache.CACHED); // cache hit + + assertEquals(initialNumberOfRequests + 3, this.myCacheOfResponse.getNumberOfExecutedRequests()); + + } + + /** + * @param uri + * @param cacheStatus + * @throws URISyntaxException + */ + private void callRestTemplateWithCaching(URI uri, Cache cacheStatus) throws URISyntaxException { + long numberOfNewlyExecutedRequests = this.myCacheOfResponse.getNumberOfExecutedRequests(); + ResponseEntity responseEntity = this.myRestTemplate.getForEntity(uri, String.class); + numberOfNewlyExecutedRequests = this.myCacheOfResponse.getNumberOfExecutedRequests() - numberOfNewlyExecutedRequests; + this.LOGGER.info("numberOfExecutedRequest since last request: new={}, count={}, teststatus={}", // + numberOfNewlyExecutedRequests, this.myCacheOfResponse.getNumberOfExecutedRequests(), cacheStatus); + + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + + switch (cacheStatus) { + case NOT_CACHED: + assertEquals(1, numberOfNewlyExecutedRequests); + break; + case CACHED: + assertEquals(0, numberOfNewlyExecutedRequests); + break; + default: + fail("Test case misconfigured"); + break; + } + } + + private enum Cache { + CACHED, NOT_CACHED + } +} + diff --git a/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTest.java b/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTest.java deleted file mode 100644 index f2bc1a93..00000000 --- a/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTest.java +++ /dev/null @@ -1,144 +0,0 @@ -package qa.commons; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import static org.junit.jupiter.api.Assertions.fail; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.context.web.AnnotationConfigWebContextLoader; - -import eu.wdaqua.qanary.commons.config.CacheConfig; -import eu.wdaqua.qanary.communications.CacheOfRestTemplateResponse; -import eu.wdaqua.qanary.communications.RestTemplateWithCaching; - -/** - * Test is validating the correct functionality of RestTemplateWithCaching - * (i.e., repeated requests to a Web service are cached) - */ -@ExtendWith(SpringExtension.class) -@SpringBootTest(classes=RestTemplateCacheLiveTest.class) -@ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = { CacheConfig.class }) -@Import(RestTemplateCacheLiveTestConfiguration.class) -class RestTemplateCacheLiveTest { - // time span for caching, tests wait this time span during the test runs - protected final static int MAX_TIME_SPAN_SECONDS = 5; - - private Logger logger = LoggerFactory.getLogger(RestTemplateCacheLiveTest.class); - - private enum Cache { - CACHED, NOTCACHED - } - - @Autowired - RestTemplateWithCaching restTemplate; - - @Autowired - CacheOfRestTemplateResponse myCacheOfResponse; - - @Test - void givenRestTemplate_whenRequested_thenLogAndModifyResponse() - throws InterruptedException, URISyntaxException { - - // myCacheOfResponse = new CacheOfRestTemplateResponse(); - // restTemplate = new RestTemplateWithCaching(myCacheOfResponse); - assertNotNull(restTemplate); - assertNotNull(myCacheOfResponse); - - LoginForm loginForm0 = new LoginForm("userName", "password"); - LoginForm loginForm1 = new LoginForm("userName2", "password2"); - - assertEquals(0, myCacheOfResponse.getNumberOfExecutedRequests()); - - callRestTemplateWithCaching(loginForm0, Cache.NOTCACHED); // cache miss - callRestTemplateWithCaching(loginForm0, Cache.CACHED); // cache hit - callRestTemplateWithCaching(loginForm0, Cache.CACHED); // cache hit - TimeUnit.SECONDS.sleep(MAX_TIME_SPAN_SECONDS + 1); // wait until it is too late for caching - callRestTemplateWithCaching(loginForm0, Cache.NOTCACHED); // cache miss: too long ago - callRestTemplateWithCaching(loginForm0, Cache.CACHED); // cache hit - callRestTemplateWithCaching(loginForm1, Cache.NOTCACHED); // cache miss: different body - callRestTemplateWithCaching(loginForm0, Cache.CACHED); // cache hit - callRestTemplateWithCaching(loginForm1, Cache.CACHED); // cache hit - - assertEquals(3, myCacheOfResponse.getNumberOfExecutedRequests()); - - } - - private void callRestTemplateWithCaching(LoginForm loginForm, Cache cacheStatus) throws URISyntaxException { - URI TESTSERVICEURL = new URI("http://httpbin.org/post"); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity requestEntity = new HttpEntity(loginForm, headers); - - long numberOfNewlyExecutedRequests = myCacheOfResponse.getNumberOfExecutedRequests(); - ResponseEntity responseEntity = restTemplate.postForEntity(TESTSERVICEURL, requestEntity, String.class); - numberOfNewlyExecutedRequests = myCacheOfResponse.getNumberOfExecutedRequests() - - numberOfNewlyExecutedRequests; - logger.info("numberOfExecutedRequest since last request: new={}, count={}, teststatus={}", // - numberOfNewlyExecutedRequests, myCacheOfResponse.getNumberOfExecutedRequests(), cacheStatus); - - assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); - - switch (cacheStatus) { - case NOTCACHED: - assertEquals(1, numberOfNewlyExecutedRequests); - break; - case CACHED: - assertEquals(0, numberOfNewlyExecutedRequests); - break; - default: - fail("Test case misconfigured"); - break; - } - } - - public class LoginForm { - private String username; - private String password; - - public LoginForm() { - } - - public LoginForm(String username, String password) { - super(); - this.username = username; - this.password = password; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - } -} diff --git a/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTestConfiguration.java b/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTestConfiguration.java index dfbd9ac5..24efcc74 100644 --- a/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTestConfiguration.java +++ b/qanary_commons/src/test/java/qa/commons/RestTemplateCacheLiveTestConfiguration.java @@ -9,11 +9,6 @@ @TestConfiguration public class RestTemplateCacheLiveTestConfiguration { - // define here the current CaffeineCacheManager configuration - static { - System.setProperty("qanary.webservicecalls.cache.specs", - "maximumSize=1000,expireAfterAccess=" + RestTemplateCacheLiveTest.MAX_TIME_SPAN_SECONDS + "s"); - } @Bean @Primary diff --git a/qanary_component-template/pom.xml b/qanary_component-template/pom.xml index 4e85b1f5..6c584b32 100644 --- a/qanary_component-template/pom.xml +++ b/qanary_component-template/pom.xml @@ -14,7 +14,7 @@ 11 - 2.6.7 + 2.7.10 diff --git a/qanary_pipeline-template/pom.xml b/qanary_pipeline-template/pom.xml index 13a11e0f..7c0a1e94 100644 --- a/qanary_pipeline-template/pom.xml +++ b/qanary_pipeline-template/pom.xml @@ -5,7 +5,7 @@ 4.0.0 qa.pipeline eu.wdaqua.qanary - 3.7.5 + 3.7.6 org.springframework.boot spring-boot-starter-parent @@ -86,11 +86,6 @@ org.springframework spring-webmvc - - org.json - json - 20090211 - net.sf.json-lib json-lib-ext-spring diff --git a/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/QanaryGerbilController.java b/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/QanaryGerbilController.java index 08e7a145..fe1b70a9 100644 --- a/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/QanaryGerbilController.java +++ b/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/QanaryGerbilController.java @@ -198,7 +198,7 @@ public ResponseEntity gerbil( } String questionText = myQanaryQuestion.getTextualRepresentation(); String sparqlQueryString = myQanaryQuestion.getSparqlResult(); // returns empty String if no Query was found - String jsonAnswerString = "{}"; // TODO: myQanaryQuestion.getJsonResult(); // returns empty String if no answer was found + String jsonAnswerString = myQanaryQuestion.getJsonResult(); GerbilExecuteResponse obj = new GerbilExecuteResponse(Jackson2ObjectMapperBuilder.json().build(), // questionText, language, sparqlQueryString, jsonAnswerString); diff --git a/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/messages/GerbilExecuteResponse.java b/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/messages/GerbilExecuteResponse.java index a6af1481..00735e1e 100644 --- a/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/messages/GerbilExecuteResponse.java +++ b/qanary_pipeline-template/src/main/java/eu/wdaqua/qanary/web/messages/GerbilExecuteResponse.java @@ -88,12 +88,11 @@ public GerbilExecuteResponse(ObjectMapper objectMapper, String questionText, Str QueryObj queryObj = new QueryObj(sparqlQueryString); - JsonNode answersObj2 = null; + List answersArray = new LinkedList<>(); if (jsonAnswerString != null && jsonAnswerString.length() > 0) { - answersObj2 = objectMapper.readTree(jsonAnswerString); + JsonNode answersObj2 = objectMapper.readTree(jsonAnswerString); + answersArray.add(answersObj2); } - List answersArray = new LinkedList<>(); - answersArray.add(answersObj2); LinkedList questionsArray = new LinkedList<>(); Question question = new Question(queryObj, answersArray, questionDataArray); diff --git a/service_config/files/pipeline b/service_config/files/pipeline index 1e5e1d75..1983fcd2 100644 --- a/service_config/files/pipeline +++ b/service_config/files/pipeline @@ -1,7 +1,7 @@ QANARY_PROCESS_ALLOW-ADDITIONAL-TRIPLES=false SERVER_HOST=http://demos.swe.htwk-leipzig.de -STARDOG_URL=http://demos.swe.htwk-leipzig.de:40102/ +STARDOG_URL=https://sd-174feb1e.stardog.cloud:5820 STARDOG_PASSWORD=SECRETS_STARDOG_PASSWORD -STARDOG_USERNAME=admin +STARDOG_USERNAME=readwrite MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=* -SERVER_PORT=40111 \ No newline at end of file +SERVER_PORT=40111