From d5398fd7c0d0d80e095976c010b68a86dd6b0557 Mon Sep 17 00:00:00 2001 From: MT <12283268+thoniTUB@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:01:00 +0100 Subject: [PATCH 01/18] adds dependabot mangement for dockerfiles --- .github/dependabot.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f504904f16..bd5b04ddc8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,8 +3,7 @@ updates: - package-ecosystem: maven directory: "/" schedule: - interval: daily - time: "04:00" + interval: "weekly" open-pull-requests-limit: 10 assignees: - awildturtok @@ -16,8 +15,7 @@ updates: - package-ecosystem: npm directory: "/frontend" schedule: - interval: daily - time: "04:00" + interval: "weekly" open-pull-requests-limit: 0 assignees: - Kadrian @@ -34,3 +32,14 @@ updates: labels: - gh-actions - dependencies + - package-ecosystem: "docker" + # Should match both dockerfiles according to https://github.com/dependabot/dependabot-core/commit/4d797fd82bbc9e99c80c0deea72ab698ceb64320 + directory: "/" + schedule: + interval: "weekly" + assignees: + - awildturtok + - thoniTUB + labels: + - docker + - dependencies From 579982c82435cbb2ba4501c1efb666c9ee8895a0 Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:18:28 +0100 Subject: [PATCH 02/18] update matching-stats don't resolve unneeded objects --- .../specific/UpdateMatchingStatsMessage.java | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/UpdateMatchingStatsMessage.java b/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/UpdateMatchingStatsMessage.java index e2e2fadc73..3e9407b720 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/UpdateMatchingStatsMessage.java +++ b/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/UpdateMatchingStatsMessage.java @@ -19,6 +19,7 @@ import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; import com.bakdata.conquery.models.events.Bucket; import com.bakdata.conquery.models.events.CBlock; +import com.bakdata.conquery.models.identifiable.ids.specific.CBlockId; import com.bakdata.conquery.models.identifiable.ids.specific.ConceptElementId; import com.bakdata.conquery.models.identifiable.ids.specific.ConceptId; import com.bakdata.conquery.models.jobs.Job; @@ -73,13 +74,9 @@ public void execute() throws Exception { subJobs = concepts.stream() .collect(Collectors.toMap(Functions.identity(), - concept -> CompletableFuture.runAsync(() -> { - final Concept resolved = concept.resolve(); - final Map, MatchingStats.Entry> - matchingStats = - new HashMap<>(resolved.countElements()); + conceptId -> CompletableFuture.runAsync(() -> { - calculateConceptMatches(resolved, matchingStats, worker); + Map, MatchingStats.Entry> matchingStats = calculateConceptMatches(conceptId, worker); final WriteFuture writeFuture = worker.send(new UpdateElementMatchingStats(worker.getInfo().getId(), matchingStats)); @@ -127,18 +124,24 @@ public String getLabel() { return String.format("Calculate Matching Stats for %s", worker.getInfo().getDataset()); } - private static void calculateConceptMatches(Concept concept, Map, MatchingStats.Entry> results, Worker worker) { - log.debug("BEGIN calculating for `{}`", concept.getId()); + private static Map, MatchingStats.Entry> calculateConceptMatches(ConceptId conceptId, Worker worker) { - try(Stream allCBlocks = worker.getStorage().getAllCBlocks()) { + Concept concept = conceptId.resolve(); + + final Map, MatchingStats.Entry> matchingStats = new HashMap<>(concept.countElements()); + + log.debug("BEGIN calculating for `{}`", conceptId); + + try (Stream allCBlocks = worker.getStorage().getAllCBlockIds()) { - for (CBlock cBlock : allCBlocks.toList()) { + for (CBlockId cBlockId : allCBlocks.toList()) { - if (!cBlock.getConnector().getConcept().equals(concept.getId())) { + if (!cBlockId.getConnector().getConcept().equals(conceptId)) { continue; } try { + CBlock cBlock = cBlockId.resolve(); final Bucket bucket = cBlock.getBucket().resolve(); final Table table = bucket.getTable().resolve(); @@ -152,7 +155,7 @@ private static void calculateConceptMatches(Concept concept, Map new MatchingStats.Entry()).addEvent(table, bucket, event, entity); + matchingStats.computeIfAbsent(conceptId, (ignored) -> new MatchingStats.Entry()).addEvent(table, bucket, event, entity); continue; } @@ -163,8 +166,8 @@ private static void calculateConceptMatches(Concept concept, Map element = ((TreeConcept) concept).getElementByLocalIdPath(localIds); while (element != null) { - results.computeIfAbsent(element.getId(), (ignored) -> new MatchingStats.Entry()) - .addEvent(table, bucket, event, entity); + matchingStats.computeIfAbsent(element.getId(), (ignored) -> new MatchingStats.Entry()) + .addEvent(table, bucket, event, entity); element = element.getParent(); } } @@ -172,15 +175,15 @@ private static void calculateConceptMatches(Concept concept, Map Date: Mon, 15 Dec 2025 06:22:30 +0000 Subject: [PATCH 03/18] Bump actions/cache from 4 to 5 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release_backend.yml | 2 +- .github/workflows/run_autodoc.yml | 2 +- .github/workflows/test_backend.yml | 4 ++-- .github/workflows/test_cypress.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release_backend.yml b/.github/workflows/release_backend.yml index 4e564bc35b..aa8659dac2 100644 --- a/.github/workflows/release_backend.yml +++ b/.github/workflows/release_backend.yml @@ -25,7 +25,7 @@ jobs: java-version: '21' overwrite-settings: false - name: Cache local Maven repository - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/run_autodoc.yml b/.github/workflows/run_autodoc.yml index 3a36253689..ad9d703240 100644 --- a/.github/workflows/run_autodoc.yml +++ b/.github/workflows/run_autodoc.yml @@ -18,7 +18,7 @@ jobs: timeout-minutes: 10 steps: - name: Cache local Maven repository - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index bf6963daeb..9088a02d1d 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -20,7 +20,7 @@ jobs: with: submodules: true - name: Cache local Maven repository - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -56,7 +56,7 @@ jobs: with: submodules: true - name: Cache local Maven repository - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/test_cypress.yml b/.github/workflows/test_cypress.yml index 2c1d25be91..1e031c2ede 100644 --- a/.github/workflows/test_cypress.yml +++ b/.github/workflows/test_cypress.yml @@ -20,7 +20,7 @@ jobs: timeout-minutes: 10 steps: - name: Cache local Maven repository - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From ccc3ad937137466ce7c8d6c56a40487ca24f4453 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 06:28:27 +0000 Subject: [PATCH 04/18] Bump alpine/git from v2.49.1 to 2.52.0 Bumps alpine/git from v2.49.1 to 2.52.0. --- updated-dependencies: - dependency-name: alpine/git dependency-version: 2.52.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- backend.Dockerfile | 2 +- frontend.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend.Dockerfile b/backend.Dockerfile index a1ce622715..25a6f69eae 100644 --- a/backend.Dockerfile +++ b/backend.Dockerfile @@ -1,5 +1,5 @@ # Version Extractor -FROM alpine/git:v2.49.1 AS version-extractor +FROM alpine/git:2.52.0 AS version-extractor WORKDIR /app COPY .git . diff --git a/frontend.Dockerfile b/frontend.Dockerfile index ce9e5a50da..e5c3cc5b89 100644 --- a/frontend.Dockerfile +++ b/frontend.Dockerfile @@ -1,5 +1,5 @@ # Version Extractor -FROM alpine/git:v2.49.1 AS version-extractor +FROM alpine/git:2.52.0 AS version-extractor WORKDIR /app COPY .git . From 15770837867c15c825e4c85600ea5319c2d03dfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 13:35:54 +0000 Subject: [PATCH 05/18] Bump qs, express and body-parser in /frontend Bumps [qs](https://github.com/ljharb/qs) to 6.14.1 and updates ancestor dependencies [qs](https://github.com/ljharb/qs), [express](https://github.com/expressjs/express) and [body-parser](https://github.com/expressjs/body-parser). These dependencies need to be updated together. Updates `qs` from 6.13.0 to 6.14.1 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.1) Updates `express` from 4.21.2 to 4.22.1 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.2...v4.22.1) Updates `body-parser` from 1.20.3 to 1.20.4 - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.20.3...1.20.4) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.1 dependency-type: indirect - dependency-name: express dependency-version: 4.22.1 dependency-type: direct:development - dependency-name: body-parser dependency-version: 1.20.4 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 178 +++++++++++++++++++++++++++---------- frontend/package.json | 4 +- 2 files changed, 133 insertions(+), 49 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aa8e13c54d..1d85ab38d9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -105,11 +105,11 @@ "@types/redux": "^3.6.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", - "body-parser": "^1.20.2", + "body-parser": "^1.20.4", "cors": "^2.8.5", "eslint": "^8.53.0", "eslint-plugin-cypress": "^2.15.1", - "express": "^4.21.2", + "express": "^4.22.1", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "papaparse": "^5.4.1", @@ -173,6 +173,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -751,6 +752,7 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1334,6 +1336,7 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", "license": "MIT", + "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "6.7.2" }, @@ -2197,6 +2200,7 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -3232,6 +3236,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" @@ -3466,6 +3471,7 @@ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -3863,6 +3869,7 @@ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", "license": "MIT", + "peer": true, "dependencies": { "hoist-non-react-statics": "^3.3.0" }, @@ -4022,6 +4029,7 @@ "integrity": "sha512-m7wxXGpPpqxp2QDi/rpih5O772APRuBIa/6XiGqLNoM1txkjI8Sz1V4oSXJxQLTz/yP5mgy9z6UXEO6/lP70Gg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -4067,6 +4075,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.25.tgz", "integrity": "sha512-oSVZmGtDPmRZtVDqvdKUi/qgCsWp5IDY29wp8na8Bj4B3cc99hfNzvNhlMkVVxctkAOGUA3Km7MMpBHAnWfcIA==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4091,6 +4100,7 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4297,6 +4307,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -4636,6 +4647,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5201,23 +5213,23 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -5233,12 +5245,41 @@ "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -5285,6 +5326,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", @@ -5523,6 +5565,7 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", "license": "MIT", + "peer": true, "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -6208,7 +6251,6 @@ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", - "peer": true, "dependencies": { "dequal": "^2.0.0" }, @@ -6267,6 +6309,7 @@ "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", "license": "MIT", + "peer": true, "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", @@ -6489,6 +6532,7 @@ "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -6602,6 +6646,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -6867,39 +6912,40 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -7771,6 +7817,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.6" } @@ -8521,6 +8568,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -9617,6 +9665,7 @@ "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-19.0.3.tgz", "integrity": "sha512-mzCBxrzfl+vB551Q7MB+T9+40IHU4i0a6g1eTatzeEGrQMis5m/BqvPC3kxTsI+/LxHbB9XYQE3u9SlWKDHQCw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "base64-js": "^1.5.1", "js-sha256": "^0.9.0" @@ -11620,6 +11669,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11776,6 +11826,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11950,12 +12001,12 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -12001,16 +12052,45 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -12096,6 +12176,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -12136,6 +12217,7 @@ "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", "license": "MIT", + "peer": true, "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", @@ -12251,6 +12333,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -12668,6 +12751,7 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -13016,6 +13100,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -13514,6 +13599,7 @@ "integrity": "sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@storybook/core": "8.6.12" }, @@ -14222,6 +14308,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -14360,6 +14447,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14409,7 +14497,6 @@ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "license": "MIT", - "peer": true, "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", @@ -14428,15 +14515,13 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/unified/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/unist": "^3.0.0" }, @@ -14450,7 +14535,6 @@ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", - "peer": true, "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" @@ -14465,7 +14549,6 @@ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", - "peer": true, "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" @@ -14931,6 +15014,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/frontend/package.json b/frontend/package.json index 4824519085..b464251ba5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -119,11 +119,11 @@ "@types/redux": "^3.6.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", - "body-parser": "^1.20.2", + "body-parser": "^1.20.4", "cors": "^2.8.5", "eslint": "^8.53.0", "eslint-plugin-cypress": "^2.15.1", - "express": "^4.21.2", + "express": "^4.22.1", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "papaparse": "^5.4.1", From 3f9ef7b9a763680e4f2323e6160cc71cf3ec4e7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:13:31 +0000 Subject: [PATCH 06/18] Bump react-router and react-router-dom in /frontend Bumps [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) to 6.30.3 and updates ancestor dependency [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom). These dependencies need to be updated together. Updates `react-router` from 6.30.1 to 6.30.3 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@6.30.3/packages/react-router) Updates `react-router-dom` from 6.30.1 to 6.30.3 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.30.3/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router dependency-version: 6.30.3 dependency-type: indirect - dependency-name: react-router-dom dependency-version: 6.30.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 82 +++++++++++--------------------------- frontend/package.json | 2 +- 2 files changed, 24 insertions(+), 60 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1d85ab38d9..14e118d925 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -61,7 +61,7 @@ "react-number-format": "^5.4.2", "react-redux": "^8.1.3", "react-resizable-panels": "^0.0.55", - "react-router-dom": "^6.26.2", + "react-router-dom": "^6.30.3", "react-window": "^1.8.10", "redux": "^4.1.2", "redux-devtools-extension": "^2.13.9", @@ -173,7 +173,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -752,7 +751,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1336,7 +1334,6 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", "license": "MIT", - "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "6.7.2" }, @@ -2200,7 +2197,6 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -2281,9 +2277,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", - "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", "license": "MIT", "engines": { "node": ">=14.0.0" @@ -3233,10 +3229,9 @@ "version": "1.13.5", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" @@ -3276,7 +3271,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3293,7 +3287,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3310,7 +3303,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -3327,7 +3319,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3344,7 +3335,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3361,7 +3351,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3378,7 +3367,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3395,7 +3383,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3412,7 +3399,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3429,7 +3415,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3443,7 +3428,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@swc/helpers": { @@ -3459,7 +3444,7 @@ "version": "0.1.25", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -3471,7 +3456,6 @@ "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -3869,7 +3853,6 @@ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", "license": "MIT", - "peer": true, "dependencies": { "hoist-non-react-statics": "^3.3.0" }, @@ -4029,7 +4012,6 @@ "integrity": "sha512-m7wxXGpPpqxp2QDi/rpih5O772APRuBIa/6XiGqLNoM1txkjI8Sz1V4oSXJxQLTz/yP5mgy9z6UXEO6/lP70Gg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -4075,7 +4057,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.25.tgz", "integrity": "sha512-oSVZmGtDPmRZtVDqvdKUi/qgCsWp5IDY29wp8na8Bj4B3cc99hfNzvNhlMkVVxctkAOGUA3Km7MMpBHAnWfcIA==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4100,7 +4081,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4307,7 +4287,6 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -4647,7 +4626,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5326,7 +5304,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", @@ -5565,7 +5542,6 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", "license": "MIT", - "peer": true, "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -6251,6 +6227,7 @@ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", + "peer": true, "dependencies": { "dequal": "^2.0.0" }, @@ -6309,7 +6286,6 @@ "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", "license": "MIT", - "peer": true, "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", @@ -6532,7 +6508,6 @@ "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -6646,7 +6621,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -6916,7 +6890,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -7817,7 +7790,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.6" } @@ -8568,7 +8540,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -9665,7 +9636,6 @@ "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-19.0.3.tgz", "integrity": "sha512-mzCBxrzfl+vB551Q7MB+T9+40IHU4i0a6g1eTatzeEGrQMis5m/BqvPC3kxTsI+/LxHbB9XYQE3u9SlWKDHQCw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "base64-js": "^1.5.1", "js-sha256": "^0.9.0" @@ -11669,7 +11639,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11826,7 +11795,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -12176,7 +12144,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -12217,7 +12184,6 @@ "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", "license": "MIT", - "peer": true, "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", @@ -12333,7 +12299,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -12611,12 +12576,12 @@ } }, "node_modules/react-router": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", - "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.0" + "@remix-run/router": "1.23.2" }, "engines": { "node": ">=14.0.0" @@ -12626,13 +12591,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", - "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" }, "engines": { "node": ">=14.0.0" @@ -12751,7 +12716,6 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -13100,7 +13064,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -13599,7 +13562,6 @@ "integrity": "sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@storybook/core": "8.6.12" }, @@ -14308,7 +14270,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -14447,7 +14408,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14497,6 +14457,7 @@ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "license": "MIT", + "peer": true, "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", @@ -14515,13 +14476,15 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/unified/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/unist": "^3.0.0" }, @@ -14535,6 +14498,7 @@ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", + "peer": true, "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" @@ -14549,6 +14513,7 @@ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", + "peer": true, "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" @@ -15014,7 +14979,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/frontend/package.json b/frontend/package.json index b464251ba5..2a88eaed8a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -75,7 +75,7 @@ "react-number-format": "^5.4.2", "react-redux": "^8.1.3", "react-resizable-panels": "^0.0.55", - "react-router-dom": "^6.26.2", + "react-router-dom": "^6.30.3", "react-window": "^1.8.10", "redux": "^4.1.2", "redux-devtools-extension": "^2.13.9", From be99271ea531263304862a6912e49958e6b2c18b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 07:09:12 +0000 Subject: [PATCH 07/18] Bump qs and @cypress/request Bumps [qs](https://github.com/ljharb/qs) and [@cypress/request](https://github.com/cypress-io/request). These dependencies needed to be updated together. Updates `qs` from 6.14.0 to 6.14.1 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1) Updates `@cypress/request` from 3.0.9 to 3.0.10 - [Release notes](https://github.com/cypress-io/request/releases) - [Changelog](https://github.com/cypress-io/request/blob/master/CHANGELOG.md) - [Commits](https://github.com/cypress-io/request/compare/v3.0.9...v3.0.10) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.1 dependency-type: indirect - dependency-name: "@cypress/request" dependency-version: 3.0.10 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 393deb765e..72f5fc6e13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", - "integrity": "sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.10.tgz", + "integrity": "sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ==", "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", @@ -39,7 +39,7 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.14.0", + "qs": "~6.14.1", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", @@ -251,16 +251,6 @@ "version": "3.7.2", "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/buffer": { "version": "5.7.1", "funding": [ @@ -1384,9 +1374,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" From db070885e2e6ef1c9c92346ce7a6d9c38a5311e5 Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:47:26 +0100 Subject: [PATCH 08/18] adds listener to ShardResult submission to catch errors --- .../query/DistributedExecutionManager.java | 8 +-- .../conquery/models/query/QueryExecutor.java | 11 ++-- .../models/query/results/FormShardResult.java | 8 ++- .../models/query/results/ShardResult.java | 58 ++++++++++++++----- 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java b/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java index 5dd5fe134a..6819a6da84 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java @@ -86,7 +86,7 @@ public void doCancelQuery(ManagedExecutionId executionId) { public void handleQueryResult(R result, E execution) { - log.debug("Received Result[size={}] for Query[{}]", result.getResults().size(), result.getQueryId()); + log.debug("Received Result[size={}] for Query[{}]", result.getResults().size(), result.getExecutionId()); log.trace("Received Result\n{}", result.getResults()); if (execution == null) { @@ -114,8 +114,8 @@ public v return; } - if (result.getError().isPresent()) { - execution.fail(result.getError().get()); + if (result.getError() != null) { + execution.fail(result.getError()); } else { distributedInfo.addShardResult(result); @@ -139,7 +139,7 @@ public v /* This log is here to prevent an NPE which could occur when no strong reference to result.getResults() existed anymore after the query finished and immediately was reset */ - log.trace("Collected metrics for execution {}. Last result received: {}:", result.getQueryId(), result.getResults()); + log.trace("Collected metrics for execution {}. Last result received: {}:", result.getExecutionId(), result.getResults()); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java b/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java index f9e5ef4c72..c1bc602e09 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java @@ -71,7 +71,7 @@ public boolean execute(Query query, QueryExecutionContext executionContext, Shar try { // We log the QueryPlan once for debugging purposes. if (log.isDebugEnabled()) { - log.debug("QueryPlan for Query[{}] = `{}`", result.getQueryId(), plan.get()); + log.debug("QueryPlan for Query[{}] = `{}`", result.getExecutionId(), plan.get()); } final List>> futures = @@ -79,8 +79,11 @@ public boolean execute(Query query, QueryExecutionContext executionContext, Shar final CompletableFuture allDone = CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); - allDone.thenApply((ignored) -> futures.stream().map(CompletableFuture::join).flatMap(Optional::stream).collect(Collectors.toList())) - .whenComplete((results, exc) -> result.finish(Objects.requireNonNullElse(results, Collections.emptyList()), Optional.ofNullable(exc), worker)); + allDone.thenApply((ignored) -> futures.stream() + .map(CompletableFuture::join) + .flatMap(Optional::stream) + .collect(Collectors.toList())) + .whenComplete((results, exception) -> result.finish(Objects.requireNonNullElse(results, Collections.emptyList()), exception, worker)); return true; @@ -94,7 +97,7 @@ public boolean execute(Query query, QueryExecutionContext executionContext, Shar } public void sendFailureToManagerNode(ShardResult result, ConqueryError error, Worker worker) { - result.finish(Collections.emptyList(), Optional.of(error), worker); + result.finish(Collections.emptyList(), error, worker); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/results/FormShardResult.java b/backend/src/main/java/com/bakdata/conquery/models/query/results/FormShardResult.java index 7c407206f4..965c812f24 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/results/FormShardResult.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/results/FormShardResult.java @@ -1,5 +1,7 @@ package com.bakdata.conquery.models.query.results; +import java.util.Optional; + import com.bakdata.conquery.io.cps.CPSType; import com.bakdata.conquery.models.execution.ExecutionState; import com.bakdata.conquery.models.forms.managed.ManagedInternalForm; @@ -37,10 +39,10 @@ public FormShardResult(ManagedExecutionId formId, ManagedExecutionId subQueryId, @Override protected void addResult(DistributedExecutionManager executionManager) { final ManagedInternalForm managedInternalForm = (ManagedInternalForm) executionManager.getExecution(getFormId()); - final ManagedQuery subQuery = managedInternalForm.getSubQuery(getQueryId()); + final ManagedQuery subQuery = managedInternalForm.getSubQuery(getExecutionId()); if (subQuery == null) { - throw new IllegalStateException("Subquery %s did not belong to form %s. Known subqueries: %s".formatted(getQueryId(), + throw new IllegalStateException("Subquery %s did not belong to form %s. Known subqueries: %s".formatted(getExecutionId(), formId, managedInternalForm.getSubQueries() )); @@ -51,7 +53,7 @@ protected void addResult(DistributedExecutionManager executionManager) { // Fail the whole execution if a subquery fails if (ExecutionState.FAILED.equals(subQuery.getState())) { - managedInternalForm.fail(getError().orElseThrow(() -> new IllegalStateException(String.format("Query[%s] failed but no error was set.", subQuery.getId())))); + managedInternalForm.fail(Optional.ofNullable(getError()).orElseThrow(() -> new IllegalStateException(String.format("Query[%s] failed but no error was set.", subQuery.getId())))); } if (managedInternalForm.allSubQueriesDone()) { diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java index 5b6e1b10e9..b5d0462041 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java @@ -3,11 +3,12 @@ import java.time.Duration; import java.time.LocalDateTime; import java.util.List; -import java.util.Optional; +import javax.annotation.Nullable; import com.bakdata.conquery.io.cps.CPSBase; import com.bakdata.conquery.io.cps.CPSType; import com.bakdata.conquery.models.error.ConqueryError; +import com.bakdata.conquery.models.execution.ExecutionState; import com.bakdata.conquery.models.identifiable.ids.specific.ManagedExecutionId; import com.bakdata.conquery.models.identifiable.ids.specific.WorkerId; import com.bakdata.conquery.models.messages.namespaces.NamespaceMessage; @@ -24,6 +25,7 @@ import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import org.apache.mina.core.future.WriteFuture; @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type") @CPSBase @@ -37,7 +39,7 @@ public class ShardResult extends NamespaceMessage { @ToString.Include - private ManagedExecutionId queryId; + private ManagedExecutionId executionId; @ToString.Include private WorkerId workerId; @@ -50,41 +52,69 @@ public class ShardResult extends NamespaceMessage { @ToString.Include private LocalDateTime finishTime; - private Optional error = Optional.empty(); + private ConqueryError error; - public ShardResult(ManagedExecutionId queryId, WorkerId workerId) { - this.queryId = queryId; + public ShardResult(ManagedExecutionId executionId, WorkerId workerId) { + this.executionId = executionId; this.workerId = workerId; } - public synchronized void finish(@NonNull List results, Optional maybeError, Worker worker) { - if (worker.getQueryExecutor().isCancelled(getQueryId())) { + public synchronized void finish(@NonNull List results, @Nullable Throwable maybeError, Worker worker) { + if (worker.getQueryExecutor().isCancelled(getExecutionId())) { // Query is done so we no longer need the cancellation entry. - worker.getQueryExecutor().unsetQueryCancelled(getQueryId()); + worker.getQueryExecutor().unsetQueryCancelled(getExecutionId()); return; } finishTime = LocalDateTime.now(); - if (maybeError.isPresent()) { - log.warn("FAILED Query[{}] within {}", queryId, Duration.between(startTime, finishTime), maybeError.get()); + if (maybeError != null) { + log.warn("FAILED Query[{}] within {}", executionId, Duration.between(startTime, finishTime), maybeError); - setError(maybeError.map(ConqueryError::asConqueryError)); + setError(ConqueryError.asConqueryError(maybeError)); } else { - log.info("FINISHED Query[{}] with {} results within {}", queryId, results.size(), Duration.between(startTime, finishTime)); + log.info("FINISHED Query[{}] with {} results within {}", executionId, results.size(), Duration.between(startTime, finishTime)); } this.results = results; log.trace("Sending collected Results\n{}", results); - worker.send(this); + WriteFuture sendResult = worker.send(this); + + // Add listener for write completion + sendResult.addListener(resultWriteFurture -> { + WriteFuture wf = (WriteFuture) resultWriteFurture; + if (wf.isWritten()) { + log.trace("Successfully submitted shard result for execution {}", executionId); + return; + } + + // Write may fail because message was too large + Throwable exception = wf.getException(); + log.error( + "Failed to submit (otherwise fine) shard result for execution {}. Notifying manager", executionId, exception + ); + ShardResult failMessage = new ShardResult(executionId, workerId); + failMessage.setError(ConqueryError.asConqueryError(exception)); + + WriteFuture sendFailure = worker.send(failMessage); + sendFailure.addListener(failWriteFuture -> { + if (((WriteFuture)failWriteFuture).isWritten()) { + log.info("Successfully informed manager about failed shard result for execution {}", executionId); + return; + } + log.error("Could not notify manager about shard result submission failure for execution {}. " + + "Manager probably has this execution in a dangling {} state", + executionId, ExecutionState.RUNNING); + }); + }); } protected void addResult(DistributedExecutionManager executionManager) { - executionManager.handleQueryResult(this, ((ManagedQuery) executionManager.getExecution(queryId))); + executionManager.handleQueryResult(this, ((ManagedQuery) executionManager.getExecution(executionId))); } @Override From 542e86f0e923a1233fbbeb844b132abe271e913a Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Mon, 19 Jan 2026 14:20:30 +0100 Subject: [PATCH 09/18] truncate trace --- .../com/bakdata/conquery/models/query/results/ShardResult.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java index b5d0462041..7d68c9f463 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java @@ -25,6 +25,7 @@ import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.apache.mina.core.future.WriteFuture; @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type") @@ -80,7 +81,7 @@ public synchronized void finish(@NonNull List results, @Nullable T this.results = results; - log.trace("Sending collected Results\n{}", results); + log.trace("Sending collected Results for execution {}\n{}", executionId, StringUtils.truncate(results.toString(),500)); WriteFuture sendResult = worker.send(this); From 3c770fcc32e447f7603daf61908367dfc10da1c9 Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Tue, 20 Jan 2026 08:50:57 +0100 Subject: [PATCH 10/18] catch errors in mina filter chain --- .../mode/cluster/ClusterConnectionShard.java | 7 ++- .../query/DistributedExecutionManager.java | 25 ++++---- .../models/query/results/ShardResult.java | 60 ++++++++++++------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/mode/cluster/ClusterConnectionShard.java b/backend/src/main/java/com/bakdata/conquery/mode/cluster/ClusterConnectionShard.java index 6b92baef38..7bb3b557df 100644 --- a/backend/src/main/java/com/bakdata/conquery/mode/cluster/ClusterConnectionShard.java +++ b/backend/src/main/java/com/bakdata/conquery/mode/cluster/ClusterConnectionShard.java @@ -20,6 +20,7 @@ import com.bakdata.conquery.models.worker.Worker; import com.bakdata.conquery.models.worker.WorkerInformation; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Throwables; import io.dropwizard.core.setup.Environment; import io.dropwizard.lifecycle.Managed; import io.dropwizard.util.Duration; @@ -174,8 +175,10 @@ public void sessionIdle(IoSession session, IdleStatus status) { } @Override - public void exceptionCaught(IoSession session, Throwable cause) { - log.error("Exception caught", cause); + public void exceptionCaught(IoSession session, Throwable cause) throws Exception { + // Rethrow + Throwables.throwIfInstanceOf(cause, Exception.class); + throw new RuntimeException("Encountered problem in %s".formatted(session.toString()), cause); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java b/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java index 6819a6da84..de9a223b6b 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/DistributedExecutionManager.java @@ -85,15 +85,19 @@ public void doCancelQuery(ManagedExecutionId executionId) { @SneakyThrows public void handleQueryResult(R result, E execution) { - - log.debug("Received Result[size={}] for Query[{}]", result.getResults().size(), result.getExecutionId()); - log.trace("Received Result\n{}", result.getResults()); - if (execution == null) { log.debug("Ignoring result {} because the corresponding execution was 'null' (probably deleted)", result); return; } + if (result.getError() != null) { + execution.fail(result.getError()); + return; + } + + log.debug("Received Result[size={}] for Query[{}]", result.getResults().size(), result.getExecutionId()); + log.trace("Received Result\n{}", result.getResults()); + Optional optInfo = tryGetExecutionInfo(execution.getId()); if (optInfo.isEmpty()) { @@ -114,18 +118,13 @@ public v return; } - if (result.getError() != null) { - execution.fail(result.getError()); - } - else { - distributedInfo.addShardResult(result); + distributedInfo.addShardResult(result); - // If all known workers have returned a result, the query is DONE. - if (distributedInfo.allResultsArrived(getWorkerHandler(execution.getDataset()).getAllWorkerIds())) { + // If all known workers have returned a result, the query is DONE. + if (distributedInfo.allResultsArrived(getWorkerHandler(execution.getDataset()).getAllWorkerIds())) { - execution.finish(ExecutionState.DONE); + execution.finish(ExecutionState.DONE); - } } // State changed to DONE or FAILED diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java index 7d68c9f463..e1b4f327bb 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java @@ -83,34 +83,48 @@ public synchronized void finish(@NonNull List results, @Nullable T log.trace("Sending collected Results for execution {}\n{}", executionId, StringUtils.truncate(results.toString(),500)); - WriteFuture sendResult = worker.send(this); + // Wrap in try-catch to catch errors in the mina filter chain + try { + WriteFuture sendResult = worker.send(this); - // Add listener for write completion - sendResult.addListener(resultWriteFurture -> { - WriteFuture wf = (WriteFuture) resultWriteFurture; - if (wf.isWritten()) { - log.trace("Successfully submitted shard result for execution {}", executionId); - return; - } + // Add listener to handle errors that may arise after the filter chain + sendResult.addListener(resultWriteFurture -> { - // Write may fail because message was too large - Throwable exception = wf.getException(); - log.error( - "Failed to submit (otherwise fine) shard result for execution {}. Notifying manager", executionId, exception - ); - ShardResult failMessage = new ShardResult(executionId, workerId); - failMessage.setError(ConqueryError.asConqueryError(exception)); - - WriteFuture sendFailure = worker.send(failMessage); - sendFailure.addListener(failWriteFuture -> { - if (((WriteFuture)failWriteFuture).isWritten()) { - log.info("Successfully informed manager about failed shard result for execution {}", executionId); + WriteFuture wf = (WriteFuture) resultWriteFurture; + if (wf.isWritten()) { + log.trace("Successfully submitted shard result for execution {}", executionId); return; } - log.error("Could not notify manager about shard result submission failure for execution {}. " - + "Manager probably has this execution in a dangling {} state", - executionId, ExecutionState.RUNNING); + + Throwable exception = wf.getException(); + + handleTransmissionFailure(worker, exception); }); + } catch (Throwable throwable) { + // Throwable because we might get an OOM if a message was too large + handleTransmissionFailure(worker,throwable); + } + + + } + + private void handleTransmissionFailure(Worker worker, Throwable throwable) { + log.error( + "Failed to submit (otherwise fine) shard result for execution {}. Notifying manager", executionId, throwable + ); + + ShardResult failMessage = new ShardResult(executionId, workerId); + failMessage.setError(ConqueryError.asConqueryError(throwable)); + + WriteFuture sendFailure = worker.send(failMessage); + sendFailure.addListener(failWriteFuture -> { + if (((WriteFuture)failWriteFuture).isWritten()) { + log.info("Successfully informed manager about failed shard result for execution {}", executionId); + return; + } + log.error("Could not notify manager about shard result submission failure for execution {}. " + + "Manager probably has this execution in a dangling {} state", + executionId, ExecutionState.RUNNING); }); } From d227075ab5ed09f95db3c165f288c40040abe2c3 Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:37:56 +0100 Subject: [PATCH 11/18] clean up result and failure handlers --- .../conquery/models/error/ConqueryError.java | 13 +++ .../conquery/models/error/ErrorMessages.java | 4 + .../namespaces/specific/ExecuteForm.java | 5 +- .../namespaces/specific/ExecuteQuery.java | 5 +- .../conquery/models/query/QueryExecutor.java | 82 ++++++++++++++++--- .../models/query/results/ShardResult.java | 63 ++------------ 6 files changed, 99 insertions(+), 73 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java b/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java index a4cb02e4be..78b898f5cb 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java +++ b/backend/src/main/java/com/bakdata/conquery/models/error/ConqueryError.java @@ -246,6 +246,19 @@ public String getMessageTemplate(ErrorMessages errorMessages) { } } + /** + * Result size was too large + */ + @CPSType(base = ConqueryError.class, id = "CQ_EXECUTION_RESULT_SIZE") + @RequiredArgsConstructor(onConstructor_ = {@JsonCreator}) + public static class ExecutionProcessingResultSizeError extends ConqueryError { + + @Override + public String getMessageTemplate(ErrorMessages errorMessages) { + return errorMessages.resultSizeTooLarge(); + } + } + @CPSType(base = ConqueryError.class, id = "CQ_EXECUTION_NO_SECONDARY_ID") @RequiredArgsConstructor(onConstructor_ = {@JsonCreator}) public static class NoSecondaryIdSelectedError extends ConqueryError { diff --git a/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java b/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java index 4066b2c221..304b3dde1e 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java +++ b/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java @@ -53,6 +53,10 @@ public interface ErrorMessages { @De("Die Anfrage lief zu lange und wurde abgebrochen.") String executionTimeout(); + @En("Query result size was too large.") + @De("Das Anfrageergebnis ist zu groß.") + String resultSizeTooLarge(); + @En("No secondaryId could be selected.") @De("Die ausgewählte Analyseebenen konnte in keinem der ausgewählten Konzepten gefunden werden.") String noSecondaryIdSelected(); diff --git a/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteForm.java b/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteForm.java index 69f29287c6..60195ea1c0 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteForm.java +++ b/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteForm.java @@ -61,9 +61,8 @@ public void react(Worker worker) throws Exception { query.createQueryPlan(new QueryPlanContext(worker.getStorage(), queryExecutor.getSecondaryIdSubPlanLimit())); } catch (Exception e) { - ConqueryError err = asConqueryError(e); - log.warn("Failed to create query plans for {}.", formId, err); - queryExecutor.sendFailureToManagerNode(result, err, worker); + log.warn("Failed to create query plans for {}.", formId, e); + queryExecutor.sendFailureToManagerNode(e, formId); return; } diff --git a/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteQuery.java b/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteQuery.java index 7fbe815664..d915a05f59 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteQuery.java +++ b/backend/src/main/java/com/bakdata/conquery/models/messages/namespaces/specific/ExecuteQuery.java @@ -56,9 +56,8 @@ public void react(Worker worker) throws Exception { log.trace("Created query plan in {}", stopwatch); } catch (Exception e) { - ConqueryError err = asConqueryError(e); - log.warn("Failed to create query plans for {}.", id, err); - queryExecutor.sendFailureToManagerNode(result, err, worker); + log.warn("Failed to create query plans for {}.", id, e); + queryExecutor.sendFailureToManagerNode(e, id); return; } diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java b/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java index c1bc602e09..5c8690a78c 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/QueryExecutor.java @@ -9,15 +9,14 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadPoolExecutor; -import java.util.stream.Collectors; import com.bakdata.conquery.apiv1.query.Query; import com.bakdata.conquery.models.error.ConqueryError; +import com.bakdata.conquery.models.execution.ExecutionState; import com.bakdata.conquery.models.identifiable.ids.specific.ManagedExecutionId; import com.bakdata.conquery.models.query.entity.Entity; import com.bakdata.conquery.models.query.queryplan.QueryPlan; @@ -28,6 +27,10 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.mina.core.future.IoFuture; +import org.apache.mina.core.future.IoFutureListener; +import org.apache.mina.core.future.WriteFuture; +import org.jetbrains.annotations.NotNull; @Slf4j @Data @@ -79,25 +82,78 @@ public boolean execute(Query query, QueryExecutionContext executionContext, Shar final CompletableFuture allDone = CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); - allDone.thenApply((ignored) -> futures.stream() - .map(CompletableFuture::join) - .flatMap(Optional::stream) - .collect(Collectors.toList())) - .whenComplete((results, exception) -> result.finish(Objects.requireNonNullElse(results, Collections.emptyList()), exception, worker)); + // Wait for completion or an exception might be thrown from here + // We could also use get(timeout) here to check cancellation state of the query + allDone.join(); + // We are in the clear here, all futures/entities completed successfully + List entityResults = futures.stream().map(CompletableFuture::join).filter(Optional::isPresent).map(Optional::get).toList(); + + // Prepare message to manager + result.finish(entityResults, worker); + + sendResultToManagerNode(result); return true; } - catch (Exception e) { - ConqueryError err = asConqueryError(e); - log.warn("Error while executing {}", executionContext.getExecutionId(), err); - sendFailureToManagerNode(result, asConqueryError(err), worker); + catch (Throwable e) { + log.warn("Error while executing {}", executionContext.getExecutionId(), e); + sendFailureToManagerNode(asConqueryError(e),executionContext.getExecutionId()); return false; } } - public void sendFailureToManagerNode(ShardResult result, ConqueryError error, Worker worker) { - result.finish(Collections.emptyList(), error, worker); + /** + * Send the {@link ShardResult} back to the manager + */ + private void sendResultToManagerNode(ShardResult result) { + + // Wrap in try-catch to catch errors in the mina filter chain + try { + WriteFuture sendResult = worker.send(result); + + + // Add listener to handle errors that may arise after the filter chain + sendResult.addListener(resultWriteFuture -> { + + WriteFuture wf = (WriteFuture) resultWriteFuture; + if (wf.isWritten()) { + log.trace("Successfully submitted shard result for execution {}", result.getExecutionId()); + return; + } + + Throwable exception = wf.getException(); + + sendFailureToManagerNode(exception, result.getExecutionId()); + }); + } + catch (OutOfMemoryError oome) { + // Result was too large for serialization + throw new ConqueryError.ExecutionProcessingResultSizeError(); + } + } + + public void sendFailureToManagerNode(Throwable throwable, ManagedExecutionId executionId) { + ShardResult failMessage = new ShardResult(executionId, worker.getInfo().getId()); + failMessage.setError(ConqueryError.asConqueryError(throwable)); + failMessage.setResults(Collections.emptyList()); + + WriteFuture sendFailure = worker.send(failMessage); + sendFailure.addListener(logFailureTransmission(executionId)); + } + + @NotNull + private static IoFutureListener logFailureTransmission(ManagedExecutionId executionId) { + return failWriteFuture -> { + if (((WriteFuture) failWriteFuture).isWritten()) { + log.info("Successfully informed manager about failed shard result for execution {}", executionId); + return; + } + log.error("Could not notify manager about shard result submission failure for execution {}. " + + "Manager probably has this execution in a dangling {} state", + executionId, ExecutionState.RUNNING + ); + }; } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java index e1b4f327bb..64cdb4f386 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java @@ -3,12 +3,10 @@ import java.time.Duration; import java.time.LocalDateTime; import java.util.List; -import javax.annotation.Nullable; import com.bakdata.conquery.io.cps.CPSBase; import com.bakdata.conquery.io.cps.CPSType; import com.bakdata.conquery.models.error.ConqueryError; -import com.bakdata.conquery.models.execution.ExecutionState; import com.bakdata.conquery.models.identifiable.ids.specific.ManagedExecutionId; import com.bakdata.conquery.models.identifiable.ids.specific.WorkerId; import com.bakdata.conquery.models.messages.namespaces.NamespaceMessage; @@ -26,7 +24,6 @@ import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.mina.core.future.WriteFuture; @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type") @CPSBase @@ -61,7 +58,7 @@ public ShardResult(ManagedExecutionId executionId, WorkerId workerId) { this.workerId = workerId; } - public synchronized void finish(@NonNull List results, @Nullable Throwable maybeError, Worker worker) { + public synchronized void finish(@NonNull List results, Worker worker) { if (worker.getQueryExecutor().isCancelled(getExecutionId())) { // Query is done so we no longer need the cancellation entry. worker.getQueryExecutor().unsetQueryCancelled(getExecutionId()); @@ -70,62 +67,20 @@ public synchronized void finish(@NonNull List results, @Nullable T finishTime = LocalDateTime.now(); - if (maybeError != null) { - log.warn("FAILED Query[{}] within {}", executionId, Duration.between(startTime, finishTime), maybeError); - setError(ConqueryError.asConqueryError(maybeError)); - } - else { - log.info("FINISHED Query[{}] with {} results within {}", executionId, results.size(), Duration.between(startTime, finishTime)); - } + log.info("FINISHED Query[{}] with {} results within {}", executionId, results.size(), Duration.between(startTime, finishTime)); this.results = results; - log.trace("Sending collected Results for execution {}\n{}", executionId, StringUtils.truncate(results.toString(),500)); - - // Wrap in try-catch to catch errors in the mina filter chain - try { - WriteFuture sendResult = worker.send(this); - - // Add listener to handle errors that may arise after the filter chain - sendResult.addListener(resultWriteFurture -> { - - WriteFuture wf = (WriteFuture) resultWriteFurture; - if (wf.isWritten()) { - log.trace("Successfully submitted shard result for execution {}", executionId); - return; - } - - Throwable exception = wf.getException(); - - handleTransmissionFailure(worker, exception); - }); - } catch (Throwable throwable) { - // Throwable because we might get an OOM if a message was too large - handleTransmissionFailure(worker,throwable); - } - - + // Truncate here because too large logs will crash/lock the process + log.trace("Collected Results for execution {}\n{}", executionId, StringUtils.truncate(results.toString(), 1000) + " (...)"); } - private void handleTransmissionFailure(Worker worker, Throwable throwable) { - log.error( - "Failed to submit (otherwise fine) shard result for execution {}. Notifying manager", executionId, throwable - ); - - ShardResult failMessage = new ShardResult(executionId, workerId); - failMessage.setError(ConqueryError.asConqueryError(throwable)); - - WriteFuture sendFailure = worker.send(failMessage); - sendFailure.addListener(failWriteFuture -> { - if (((WriteFuture)failWriteFuture).isWritten()) { - log.info("Successfully informed manager about failed shard result for execution {}", executionId); - return; - } - log.error("Could not notify manager about shard result submission failure for execution {}. " - + "Manager probably has this execution in a dangling {} state", - executionId, ExecutionState.RUNNING); - }); + public void fail (Throwable error, Worker worker) { + finishTime = LocalDateTime.now(); + log.warn("FAILED Query[{}] within {}", executionId, Duration.between(startTime, finishTime), error); + setError(ConqueryError.asConqueryError(error)); + } protected void addResult(DistributedExecutionManager executionManager) { From f22f485a3a45d774cdf266c35e4bf1f90df3c11b Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:43:03 +0100 Subject: [PATCH 12/18] remove unused method --- .../conquery/models/query/results/ShardResult.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java index 64cdb4f386..815b75b552 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java +++ b/backend/src/main/java/com/bakdata/conquery/models/query/results/ShardResult.java @@ -75,14 +75,7 @@ public synchronized void finish(@NonNull List results, Worker work // Truncate here because too large logs will crash/lock the process log.trace("Collected Results for execution {}\n{}", executionId, StringUtils.truncate(results.toString(), 1000) + " (...)"); } - - public void fail (Throwable error, Worker worker) { - finishTime = LocalDateTime.now(); - log.warn("FAILED Query[{}] within {}", executionId, Duration.between(startTime, finishTime), error); - setError(ConqueryError.asConqueryError(error)); - - } - + protected void addResult(DistributedExecutionManager executionManager) { executionManager.handleQueryResult(this, ((ManagedQuery) executionManager.getExecution(executionId))); } From 94e9e52fd7a60edafe2767d5a5c36dc35b5a1667 Mon Sep 17 00:00:00 2001 From: MT <12283268+thoniTUB@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:57:12 +0100 Subject: [PATCH 13/18] Apply suggestions from code review Co-authored-by: awildturtok <1553491+awildturtok@users.noreply.github.com> --- .../java/com/bakdata/conquery/models/error/ErrorMessages.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java b/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java index 304b3dde1e..7684594aa9 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java +++ b/backend/src/main/java/com/bakdata/conquery/models/error/ErrorMessages.java @@ -53,7 +53,7 @@ public interface ErrorMessages { @De("Die Anfrage lief zu lange und wurde abgebrochen.") String executionTimeout(); - @En("Query result size was too large.") + @En("Query result was too large.") @De("Das Anfrageergebnis ist zu groß.") String resultSizeTooLarge(); From c843b97a743444bf3a1ef78c775f8730a513a0f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:27:46 +0000 Subject: [PATCH 14/18] Bump lodash from 4.17.21 to 4.17.23 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 72f5fc6e13..2c57a6fde4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1133,7 +1133,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, "node_modules/lodash.once": { From c261870f5f23e497d67702f758a1ccb128436e25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 06:59:26 +0000 Subject: [PATCH 15/18] Bump cypress-io/github-action from 6 to 7 Bumps [cypress-io/github-action](https://github.com/cypress-io/github-action) from 6 to 7. - [Release notes](https://github.com/cypress-io/github-action/releases) - [Changelog](https://github.com/cypress-io/github-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/cypress-io/github-action/compare/v6...v7) --- updated-dependencies: - dependency-name: cypress-io/github-action dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test_cypress.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_cypress.yml b/.github/workflows/test_cypress.yml index 1e031c2ede..e11e316abe 100644 --- a/.github/workflows/test_cypress.yml +++ b/.github/workflows/test_cypress.yml @@ -52,7 +52,7 @@ jobs: - name: Cypress run # This is a preconfigured action, maintained by cypress, to run e2e tests # https://github.com/cypress-io/github-action - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@v7 with: working-directory: . start: bash ./scripts/run_e2e_all.sh From 86b12471aef578d79a054d860f3e8b8c6fb38716 Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Mon, 2 Feb 2026 09:41:20 +0100 Subject: [PATCH 16/18] deduplicates loaded entities for entity-history Navigation (#3838) --- frontend/src/js/entity-history/saveAndLoad.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/js/entity-history/saveAndLoad.ts b/frontend/src/js/entity-history/saveAndLoad.ts index b1b4d6e6af..af0bced3e7 100644 --- a/frontend/src/js/entity-history/saveAndLoad.ts +++ b/frontend/src/js/entity-history/saveAndLoad.ts @@ -54,7 +54,7 @@ export const useLoadHistory = ({ return useCallback( ({ label, data }: { label: string; data: string[][] }) => { - const loadedEntityIds: EntityId[] = []; + const distinctEntityIds: Set = new Set(); const loadedEntityStatus: EntityIdsStatus = {}; const loadedEntityStatusOptionsRaw: string[] = []; @@ -67,7 +67,8 @@ export const useLoadHistory = ({ const [kind, id] = row; - loadedEntityIds.push({ kind, id }); + // Deduplication is necessary for SecondaryId Queries + distinctEntityIds.add({ kind, id }); if (row.length > 2) { loadedEntityStatus[id] = row @@ -85,7 +86,7 @@ export const useLoadHistory = ({ ...new Set(loadedEntityStatusOptionsRaw), ].map((item) => ({ label: item, value: item })); - if (loadedEntityIds.length === 0) { + if (distinctEntityIds.size === 0) { dispatch( setMessage({ message: t("history.load.error"), @@ -95,6 +96,8 @@ export const useLoadHistory = ({ return; } + const loadedEntityIds = [...distinctEntityIds]; + onLoadFromFile({ label, loadedEntityIds, From 87b3f0edee355fc89f1a7bcbee53091ccd71b67a Mon Sep 17 00:00:00 2001 From: awildturtok <1553491+awildturtok@users.noreply.github.com> Date: Wed, 4 Feb 2026 11:50:49 +0100 Subject: [PATCH 17/18] removes time-editor to use editorv2 by default (#3837) --- cypress.config.js | 20 +- cypress/e2e/frontend/test_1_runQuery.cy.js | 8 - frontend/mock-api/mockApi.ts | 26 +- frontend/package-lock.json | 23 +- frontend/package.json | 1 + frontend/src/js/api/api.ts | 6 +- frontend/src/js/api/apiHelper.ts | 51 +-- frontend/src/js/app/RightPane.tsx | 64 +-- frontend/src/js/app/actions.ts | 2 - frontend/src/js/app/reducers.ts | 11 - frontend/src/js/editor-v2/EditorV2.tsx | 1 + frontend/src/js/editor-v2/types.ts | 17 +- frontend/src/js/editor-v2/util.ts | 16 +- frontend/src/js/model/query.ts | 5 +- frontend/src/js/query-runner/actions.ts | 12 +- .../TimebasedCondition.tsx | 205 --------- .../TimebasedConditionDayRange.tsx | 65 --- .../timebased-query-editor/TimebasedNode.tsx | 207 --------- .../TimebasedQueryClearButton.tsx | 42 -- .../TimebasedQueryEditor.tsx | 137 ------ .../TimebasedQueryEditorDropzone.tsx | 57 --- .../TimebasedQueryEditorTab.tsx | 13 - .../TimebasedQueryRunner.tsx | 69 --- .../src/js/timebased-query-editor/actions.ts | 72 --- .../src/js/timebased-query-editor/helpers.ts | 16 - .../src/js/timebased-query-editor/reducer.ts | 428 ------------------ frontend/src/js/user/userSettings.ts | 2 - 27 files changed, 90 insertions(+), 1486 deletions(-) delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedCondition.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedConditionDayRange.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedNode.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedQueryClearButton.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedQueryEditor.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedQueryEditorDropzone.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedQueryEditorTab.tsx delete mode 100644 frontend/src/js/timebased-query-editor/TimebasedQueryRunner.tsx delete mode 100644 frontend/src/js/timebased-query-editor/actions.ts delete mode 100644 frontend/src/js/timebased-query-editor/helpers.ts delete mode 100644 frontend/src/js/timebased-query-editor/reducer.ts diff --git a/cypress.config.js b/cypress.config.js index 509ad63b51..c331d21cf5 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,13 +1,15 @@ -const { defineConfig } = require('cypress') +const {defineConfig} = require('cypress') module.exports = defineConfig({ - e2e: { - // We've imported your old cypress plugins here. - // You may want to clean this up later by importing these. - setupNodeEvents(on, config) { - return require('./cypress/plugins/index.js')(on, config) + e2e: { + // We've imported your old cypress plugins here. + // You may want to clean this up later by importing these. + setupNodeEvents(on, config) { + return require('./cypress/plugins/index.js')(on, config) + }, + baseUrl: 'http://localhost:8080', + experimentalRunAllSpecs: true, }, - baseUrl: 'http://localhost:8080', - experimentalRunAllSpecs: true, - }, + viewportWidth: 1440, + viewportHeight: 900, }) diff --git a/cypress/e2e/frontend/test_1_runQuery.cy.js b/cypress/e2e/frontend/test_1_runQuery.cy.js index 2c443cadd0..e110aacff9 100644 --- a/cypress/e2e/frontend/test_1_runQuery.cy.js +++ b/cypress/e2e/frontend/test_1_runQuery.cy.js @@ -5,10 +5,6 @@ const USER_TOKEN_WITH_PERMISSIONS = "user.user2"; describe("Run query", () => { beforeEach(() => { - // run these tests as if in a desktop - // browser with a 720p monitor - cy.viewport(1280, 720) - visitWithToken(USER_TOKEN_WITH_PERMISSIONS); }); @@ -89,10 +85,6 @@ describe("Run query", () => { describe("Reference list", () => { beforeEach(() => { - // run these tests as if in a desktop - // browser with a 720p monitor - cy.viewport(1280, 720) - visitWithToken(USER_TOKEN_WITH_PERMISSIONS); }); diff --git a/frontend/mock-api/mockApi.ts b/frontend/mock-api/mockApi.ts index 48ffa61c90..ca6a8fead7 100644 --- a/frontend/mock-api/mockApi.ts +++ b/frontend/mock-api/mockApi.ts @@ -364,19 +364,19 @@ export default function mockApi(app: Application) { const storedValues: string[] = countriesRequested ? require("./autocomplete/countries") : wordsRequested - ? require("./autocomplete/words") - : [ - "1008508208", - "1015841172", - "1011218302", - "1007680436", - "1017776144", - "1003780588", - "1000326535", - "1014150881", - "1017126347", - "1008445564", - ]; + ? require("./autocomplete/words") + : [ + "1008508208", + "1015841172", + "1011218302", + "1007680436", + "1017776144", + "1003780588", + "1000326535", + "1014150881", + "1017126347", + "1008445564", + ]; const suggestions = storedValues .map((v, id) => ({ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 14e118d925..eab30e9081 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -105,6 +105,7 @@ "@types/redux": "^3.6.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", + "baseline-browser-mapping": "^2.9.18", "body-parser": "^1.20.4", "cors": "^2.8.5", "eslint": "^8.53.0", @@ -3229,7 +3230,7 @@ "version": "1.13.5", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -3271,6 +3272,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3287,6 +3289,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3303,6 +3306,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -3319,6 +3323,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3335,6 +3340,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3351,6 +3357,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3367,6 +3374,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3383,6 +3391,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3399,6 +3408,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3415,6 +3425,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -3428,7 +3439,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/helpers": { @@ -3444,7 +3455,7 @@ "version": "0.1.25", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -5157,9 +5168,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", - "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" diff --git a/frontend/package.json b/frontend/package.json index 2a88eaed8a..459001ada8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -119,6 +119,7 @@ "@types/redux": "^3.6.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", + "baseline-browser-mapping": "^2.9.18", "body-parser": "^1.20.4", "cors": "^2.8.5", "eslint": "^8.53.0", diff --git a/frontend/src/js/api/api.ts b/frontend/src/js/api/api.ts index a5e779c2fb..1581ee35e2 100644 --- a/frontend/src/js/api/api.ts +++ b/frontend/src/js/api/api.ts @@ -9,7 +9,6 @@ import type { } from "../previous-queries/list/reducer"; import type { QueryToUploadT } from "../previous-queries/upload/CSVColumnPicker"; import { StandardQueryStateT } from "../standard-query-editor/queryReducer"; -import { ValidatedTimebasedQueryStateT } from "../timebased-query-editor/reducer"; import { AuthTokenContext } from "../authorization/AuthTokenProvider"; import { transformQueryToApi } from "./apiHelper"; @@ -101,10 +100,7 @@ export const usePostQueries = () => { return useCallback( ( datasetId: DatasetT["id"], - query: - | StandardQueryStateT - | ValidatedTimebasedQueryStateT - | EditorV2Query, + query: StandardQueryStateT | EditorV2Query, options: { queryType: string; selectedSecondaryId?: string | null }, ) => api({ diff --git a/frontend/src/js/api/apiHelper.ts b/frontend/src/js/api/apiHelper.ts index 54d18586f7..cf8c658c1d 100644 --- a/frontend/src/js/api/apiHelper.ts +++ b/frontend/src/js/api/apiHelper.ts @@ -18,10 +18,6 @@ import type { StandardQueryNodeT, TableWithFilterValueT, } from "../standard-query-editor/types"; -import type { - ValidatedTimebasedConditionT, - ValidatedTimebasedQueryStateT, -} from "../timebased-query-editor/reducer"; import { ConceptIdT, DateRangeT } from "./types"; @@ -169,47 +165,6 @@ const createQueryConcepts = (query: StandardQueryStateT) => { }); }; -// TODO: Use, once feature is complete -const getDays = (condition: ValidatedTimebasedConditionT) => { - switch (condition.operator) { - case "DAYS_BEFORE": - return { - days: { - min: condition.minDays, - max: condition.maxDays, - }, - }; - case "DAYS_OR_NO_EVENT_BEFORE": - return { - days: condition.minDaysOrNoEvent, - }; - default: - return {}; - } -}; - -const transformTimebasedQueryToApi = (query: ValidatedTimebasedQueryStateT) => - createConceptQuery( - createAnd( - query.conditions.map((condition: ValidatedTimebasedConditionT) => { - const days = getDays(condition); - - return { - type: condition.operator, - ...days, - preceding: { - sampler: condition.result0.timestamp, - child: createSavedQuery(condition.result0.id), - }, - index: { - sampler: condition.result1.timestamp, - child: createSavedQuery(condition.result1.id), - }, - }; - }), - ), - ); - const createTimeMode = (timeNode: TreeChildrenTime) => { switch (timeNode.operator) { case "AFTER": @@ -309,14 +264,10 @@ const transformEditorV2QueryToApi = (query: EditorV2Query) => { // But small additions are made (properties allowlisted), empty things filtered out // to make it compatible with the backend API export const transformQueryToApi = ( - query: StandardQueryStateT | ValidatedTimebasedQueryStateT | EditorV2Query, + query: StandardQueryStateT | EditorV2Query, options: { queryType: string; selectedSecondaryId?: string | null }, ) => { switch (options.queryType) { - case "timebased": - return transformTimebasedQueryToApi( - query as ValidatedTimebasedQueryStateT, - ); case "standard": return transformStandardQueryToApi( query as StandardQueryStateT, diff --git a/frontend/src/js/app/RightPane.tsx b/frontend/src/js/app/RightPane.tsx index d858d3918c..4aba0c9972 100644 --- a/frontend/src/js/app/RightPane.tsx +++ b/frontend/src/js/app/RightPane.tsx @@ -1,6 +1,5 @@ import styled from "@emotion/styled"; -import { useCallback, useMemo, useState } from "react"; -import { useHotkeys } from "react-hotkeys-hook"; +import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; @@ -10,8 +9,6 @@ import FormsTab from "../external-forms/FormsTab"; import Pane from "../pane/Pane"; import { TabNavigationTab } from "../pane/TabNavigation"; import StandardQueryEditorTab from "../standard-query-editor/StandardQueryEditorTab"; -import TimebasedQueryEditorTab from "../timebased-query-editor/TimebasedQueryEditorTab"; -import { getUserSettings, storeUserSettings } from "../user/userSettings"; import type { StateT } from "./reducers"; @@ -27,31 +24,12 @@ const SxPane = styled(Pane)` background-color: ${({ theme }) => theme.col.bgAlt}; `; -const useEditorV2 = () => { - const [showEditorV2, setShowEditorV2] = useState( - getUserSettings().showEditorV2, - ); - - const toggleEditorV2 = useCallback(() => { - setShowEditorV2(!showEditorV2); - storeUserSettings({ showEditorV2: !showEditorV2 }); - }, [showEditorV2]); - - useHotkeys("shift+alt+e", toggleEditorV2, [showEditorV2]); - - return { - showEditorV2, - }; -}; - const RightPane = () => { const { t } = useTranslation(); const activeTab = useSelector( (state) => state.panes.right.activeTab, ); - const { showEditorV2 } = useEditorV2(); - const tabs: TabNavigationTab[] = useMemo( () => [ { @@ -59,24 +37,18 @@ const RightPane = () => { label: t("rightPane.queryEditor"), tooltip: t("help.tabQueryEditor"), }, - showEditorV2 - ? { - key: "editorV2", - label: t("rightPane.editorV2"), - tooltip: t("help.tabEditorV2"), - } - : { - key: "timebasedQueryEditor", - label: t("rightPane.timebasedQueryEditor"), - tooltip: t("help.tabTimebasedEditor"), - }, + { + key: "editorV2", + label: t("rightPane.editorV2"), + tooltip: t("help.tabEditorV2"), + }, { key: "externalForms", label: t("rightPane.externalForms"), tooltip: t("help.tabFormEditor"), }, ], - [t, showEditorV2], + [t], ); return ( @@ -85,19 +57,15 @@ const RightPane = () => { - {showEditorV2 ? ( - - ) : ( - - )} + diff --git a/frontend/src/js/app/actions.ts b/frontend/src/js/app/actions.ts index e2e09dbca1..ca0104e42e 100644 --- a/frontend/src/js/app/actions.ts +++ b/frontend/src/js/app/actions.ts @@ -16,7 +16,6 @@ import type { QueryUploadConceptListModalActions } from "../query-upload-concept import type { SnackMessageActions } from "../snack-message/actions"; import type { StandardQueryEditorActions } from "../standard-query-editor/actions"; import type { StartupActions } from "../startup/actions"; -import type { TimebasedActions } from "../timebased-query-editor/actions"; import type { TooltipActions } from "../tooltip/actions"; import type { UploadConceptListModalActions } from "../upload-concept-list-modal/actions"; import type { UserActions } from "../user/actions"; @@ -35,7 +34,6 @@ export type Action = | UploadConceptListModalActions | SnackMessageActions | PreviousQueryListActions - | TimebasedActions | StartupActions | TooltipActions | ExternalFormActions diff --git a/frontend/src/js/app/reducers.ts b/frontend/src/js/app/reducers.ts index af19ae2b33..b860a64df8 100644 --- a/frontend/src/js/app/reducers.ts +++ b/frontend/src/js/app/reducers.ts @@ -39,9 +39,6 @@ import selectedSecondaryIdsReducer, { SelectedSecondaryIdStateT, } from "../standard-query-editor/selectedSecondaryIdReducer"; import startup, { StartupStateT } from "../startup/reducer"; -import timebasedQueryReducer, { - TimebasedQueryStateT, -} from "../timebased-query-editor/reducer"; import tooltip, { TooltipStateT } from "../tooltip/reducer"; import uploadConceptListModal, { UploadConceptListModalStateT, @@ -70,10 +67,6 @@ export type StateT = { selectedSecondaryId: SelectedSecondaryIdStateT; queryRunner: QueryRunnerStateT; }; - timebasedQueryEditor: { - timebasedQuery: TimebasedQueryStateT; - timebasedQueryRunner: QueryRunnerStateT; - }; externalForms: { activeForm: string | null; queryRunner: QueryRunnerStateT; @@ -108,10 +101,6 @@ const buildAppReducer = () => { selectedSecondaryId: selectedSecondaryIdsReducer, queryRunner: createQueryRunnerReducer("standard"), }), - timebasedQueryEditor: combineReducers({ - timebasedQuery: timebasedQueryReducer, - timebasedQueryRunner: createQueryRunnerReducer("timebased"), - }), externalForms: combineReducers({ activeForm: activeFormReducer, availableForms: availableFormsReducer, diff --git a/frontend/src/js/editor-v2/EditorV2.tsx b/frontend/src/js/editor-v2/EditorV2.tsx index 54e4451950..9a2e28087e 100644 --- a/frontend/src/js/editor-v2/EditorV2.tsx +++ b/frontend/src/js/editor-v2/EditorV2.tsx @@ -502,6 +502,7 @@ export function EditorV2({ )} + ); diff --git a/frontend/src/js/editor-v2/types.ts b/frontend/src/js/editor-v2/types.ts index 932165db5c..f3963fd2a2 100644 --- a/frontend/src/js/editor-v2/types.ts +++ b/frontend/src/js/editor-v2/types.ts @@ -31,8 +31,21 @@ export interface TreeChildrenOr extends TreeChildrenBase { connection: "or"; } -export type TimeTimestamp = "ALL" | "ANY" | "EARLIEST" | "LATEST"; -export type TimeOperator = "BEFORE" | "AFTER" | "WHILE"; +export const TIME_OPERATORS: ("BEFORE" | "AFTER" | "WHILE")[] = [ + "BEFORE", + "AFTER", + "WHILE", +] as const; +export const TIME_TIMESTAMPS: ("ALL" | "ANY" | "EARLIEST" | "LATEST")[] = [ + "ALL", + "ANY", + "EARLIEST", + "LATEST", +] as const; + +export type TimeTimestamp = (typeof TIME_TIMESTAMPS)[number]; +export type TimeOperator = (typeof TIME_OPERATORS)[number]; + export interface TreeChildrenTime extends TreeChildrenBase { connection: "time"; operator: TimeOperator; diff --git a/frontend/src/js/editor-v2/util.ts b/frontend/src/js/editor-v2/util.ts index 215322e99c..b6a1906bec 100644 --- a/frontend/src/js/editor-v2/util.ts +++ b/frontend/src/js/editor-v2/util.ts @@ -1,7 +1,13 @@ import { useCallback } from "react"; import { useTranslation } from "react-i18next"; -import { ConnectionKind, Tree, TreeChildrenTime } from "./types"; +import { + ConnectionKind, + TimeOperator, + TimeTimestamp, + Tree, + TreeChildrenTime, +} from "./types"; export const findNodeById = (tree: Tree, id: string): Tree | undefined => { if (tree.id === id) { @@ -65,7 +71,7 @@ export const useGetTranslatedTimestamp = () => { const { t } = useTranslation(); return useCallback( - (timestamp: "ALL" | "ANY" | "EARLIEST" | "LATEST") => { + (timestamp: TimeTimestamp) => { if (timestamp === "ALL") { return t("editorV2.ALL"); } else if (timestamp === "ANY") { @@ -74,17 +80,13 @@ export const useGetTranslatedTimestamp = () => { return t("editorV2.EARLIEST"); } else if (timestamp === "LATEST") { return t("editorV2.LATEST"); - } else { - return ""; } }, [t], ); }; -export const useTranslatedOperator = ( - operator: "BEFORE" | "AFTER" | "WHILE", -) => { +export const useTranslatedOperator = (operator: TimeOperator) => { const { t } = useTranslation(); if (operator === "BEFORE") { diff --git a/frontend/src/js/model/query.ts b/frontend/src/js/model/query.ts index a94bdefc18..541b8d0e3f 100644 --- a/frontend/src/js/model/query.ts +++ b/frontend/src/js/model/query.ts @@ -4,7 +4,8 @@ import type { PreviousQueryQueryNodeType, StandardQueryNodeT, } from "../standard-query-editor/types"; -import { TIMEBASED_OPERATOR_TYPES } from "../timebased-query-editor/reducer"; + +import { TIME_OPERATORS } from "../editor-v2/types"; import { nodeIsConceptQueryNode } from "./node"; @@ -13,7 +14,7 @@ function isTimebasedQuery(node: PreviousQueryQueryNodeType) { const queryString = JSON.stringify(node.query); - return TIMEBASED_OPERATOR_TYPES.some((op) => queryString.indexOf(op) !== -1); + return TIME_OPERATORS.some((op) => queryString.indexOf(op) !== -1); } // A little weird that it's nested so deeply, but well, you can't expand an external query diff --git a/frontend/src/js/query-runner/actions.ts b/frontend/src/js/query-runner/actions.ts index afe3446e85..9fd55a10ca 100644 --- a/frontend/src/js/query-runner/actions.ts +++ b/frontend/src/js/query-runner/actions.ts @@ -29,7 +29,6 @@ import { useLoadQueries, } from "../previous-queries/list/actions"; import type { StandardQueryStateT } from "../standard-query-editor/queryReducer"; -import type { ValidatedTimebasedQueryStateT } from "../timebased-query-editor/reducer"; import { updateQueryId } from "../preview/actions"; import { QUERY_AGAIN_TIMEOUT } from "./constants"; @@ -83,11 +82,7 @@ export const useStartQuery = (queryType: QueryTypeT) => { return ( datasetId: DatasetT["id"], - query: - | StandardQueryStateT - | EditorV2Query - | ValidatedTimebasedQueryStateT - | FormQueryPostPayload, + query: StandardQueryStateT | EditorV2Query | FormQueryPostPayload, { selectedSecondaryId, }: { @@ -102,10 +97,7 @@ export const useStartQuery = (queryType: QueryTypeT) => { : () => postQueries( datasetId, - query as - | StandardQueryStateT - | EditorV2Query - | ValidatedTimebasedQueryStateT, + query as StandardQueryStateT | EditorV2Query, { queryType, selectedSecondaryId, diff --git a/frontend/src/js/timebased-query-editor/TimebasedCondition.tsx b/frontend/src/js/timebased-query-editor/TimebasedCondition.tsx deleted file mode 100644 index bb71bffb47..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedCondition.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import styled from "@emotion/styled"; -import { faTimes } from "@fortawesome/free-solid-svg-icons"; -import { useTranslation } from "react-i18next"; - -import IconButton from "../button/IconButton"; -import { isEmpty } from "../common/helpers/commonHelper"; -import type { DragItemQuery } from "../standard-query-editor/types"; -import VerticalToggleButton from "../ui-components/VerticalToggleButton"; - -import TimebasedConditionDayRange from "./TimebasedConditionDayRange"; -import TimebasedNode from "./TimebasedNode"; -import TimebasedQueryEditorDropzone from "./TimebasedQueryEditorDropzone"; -import type { - TimebasedConditionT, - TimebasedOperatorType, - TimebasedResultType, -} from "./reducer"; - -const StyledIconButton = styled(IconButton)` - position: absolute; - top: 0; - right: 0; - z-index: 1; - display: inline; -`; - -const Root = styled("div")` - position: relative; - padding: 30px 10px 10px; - box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.12); - border-radius: ${({ theme }) => theme.borderRadius}; - border: 1px solid ${({ theme }) => theme.col.grayLight}; - background-color: ${({ theme }) => theme.col.bg}; - - &:hover { - border: 1px solid ${({ theme }) => theme.col.grayLight}; - } -`; - -const StyledVerticalToggleButton = styled(VerticalToggleButton)` - max-width: 180px; -`; - -const NodesContainer = styled("div")` - margin-bottom: 10px; - position: relative; -`; - -const Nodes = styled("div")` - position: relative; - width: 100%; - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; -`; - -const HorizontalLine = styled("div")` - position: absolute; - top: 50%; - right: 10%; - width: 80%; - border-bottom: 1px solid ${({ theme }) => theme.col.blueGray}; - margin-top: -0.5px; -`; - -const Operator = styled("div")` - margin: 0 10px; -`; - -type PropsType = { - condition: TimebasedConditionT; - conditionIdx: number; - // indexResult: number | string | null; - removable: boolean; - onRemove: () => void; - onSetOperator: (value: TimebasedOperatorType) => void; - onRemoveTimebasedNode: (idx: number, moved: boolean) => void; - onDropTimebasedNode: ( - resultIdx: number, - node: TimebasedResultType | DragItemQuery, - moved: boolean, - ) => void; - onSetTimebasedNodeTimestamp: (idx: number, timestamp: string) => void; - onSetTimebasedConditionMinDays: (value: number | null) => void; - onSetTimebasedConditionMaxDays: (value: number | null) => void; - onSetTimebasedConditionMinDaysOrNoEvent: (value: number | null) => void; -}; - -const TimebasedCondition = ({ - condition, - conditionIdx, - // indexResult, - removable, - onRemove, - onSetOperator, - onRemoveTimebasedNode, - onDropTimebasedNode, - onSetTimebasedNodeTimestamp, - onSetTimebasedConditionMinDays, - onSetTimebasedConditionMaxDays, - onSetTimebasedConditionMinDaysOrNoEvent, -}: PropsType) => { - const { t } = useTranslation(); - - const minDays = !isEmpty(condition.minDays) ? condition.minDays : ""; - const maxDays = !isEmpty(condition.maxDays) ? condition.maxDays : ""; - const minDaysOrNoEvent = !isEmpty(condition.minDaysOrNoEvent) - ? condition.minDaysOrNoEvent - : ""; - - const createTimebasedResult = (idx: 0 | 1) => { - const node = idx === 0 ? condition.result0 : condition.result1; - return node ? ( - onRemoveTimebasedNode(idx, false)} - onSetTimebasedNodeTimestamp={(timestamp) => { - onSetTimebasedNodeTimestamp(idx, timestamp); - }} - // onSetTimebasedIndexResult={() => { - // onSetTimebasedIndexResult(condition[`result${idx}`].id); - // }} - // isIndexResultDisabled={ - // idx === 0 && condition.operator === "DAYS_OR_NO_EVENT_BEFORE" - // } - /> - ) : ( - onDropTimebasedNode(idx, node, moved)} - /> - ); - }; - - const result0 = createTimebasedResult(0); - const result1 = createTimebasedResult(1); - - return ( - - {removable && } - - - - {result0} - - - onSetOperator(value as TimebasedOperatorType) - } - activeValue={condition.operator} - options={[ - { - label: t("timebasedQueryEditor.opBefore"), - value: "BEFORE", - }, - { - label: t("timebasedQueryEditor.opBeforeOrSame"), - value: "BEFORE_OR_SAME", - }, - { - label: t("timebasedQueryEditor.opDays"), - value: "DAYS_BEFORE", - }, - { - label: t("timebasedQueryEditor.opSame"), - value: "SAME", - }, - { - label: t("timebasedQueryEditor.opDaysOrNoEventBefore"), - value: "DAYS_OR_NO_EVENT_BEFORE", - }, - ]} - /> - - {result1} - - - {condition.operator === "DAYS_BEFORE" && ( - - )} - {condition.operator === "DAYS_OR_NO_EVENT_BEFORE" && ( - - )} - - ); -}; - -export default TimebasedCondition; diff --git a/frontend/src/js/timebased-query-editor/TimebasedConditionDayRange.tsx b/frontend/src/js/timebased-query-editor/TimebasedConditionDayRange.tsx deleted file mode 100644 index 7a9ba8d0c9..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedConditionDayRange.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import styled from "@emotion/styled"; -import { useTranslation } from "react-i18next"; - -import InputPlain from "../ui-components/InputPlain/InputPlain"; - -const Container = styled("div")` - width: 100%; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; -`; - -const SxInputPlain = styled(InputPlain)` - padding: 0 5px; -`; - -interface PropsType { - minDays?: number | string | null; - maxDays?: number | string | null; - onSetTimebasedConditionMinDays?: (value: number | null) => void; - onSetTimebasedConditionMaxDays?: (value: number | null) => void; -} - -const TimebasedConditionDayRange = ({ - minDays, - maxDays, - onSetTimebasedConditionMinDays, - onSetTimebasedConditionMaxDays, -}: PropsType) => { - const { t } = useTranslation(); - - return ( - - {minDays !== undefined && !!onSetTimebasedConditionMinDays && ( - { - onSetTimebasedConditionMinDays(value as number | null); - }} - inputProps={{ min: 1, pattern: "^(?!-)\\d*$" }} - placeholder={t("common.timeUnitDays")} - label={t("timebasedQueryEditor.minDaysLabel")} - tinyLabel - /> - )} - {maxDays !== undefined && !!onSetTimebasedConditionMaxDays && ( - { - onSetTimebasedConditionMaxDays(value as number | null); - }} - inputProps={{ min: 1, pattern: "^(?!-)\\d*$" }} - placeholder={t("common.timeUnitDays")} - label={t("timebasedQueryEditor.maxDaysLabel")} - tinyLabel - /> - )} - - ); -}; - -export default TimebasedConditionDayRange; diff --git a/frontend/src/js/timebased-query-editor/TimebasedNode.tsx b/frontend/src/js/timebased-query-editor/TimebasedNode.tsx deleted file mode 100644 index b005793b8c..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedNode.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import styled from "@emotion/styled"; -import { faTimes } from "@fortawesome/free-solid-svg-icons"; -import { FC, useRef } from "react"; -import { useDrag } from "react-dnd"; -import { useTranslation } from "react-i18next"; - -import { getWidthAndHeight } from "../app/DndProvider"; -import IconButton from "../button/IconButton"; -import { DNDType } from "../common/constants/dndTypes"; -import { DragItemQuery } from "../standard-query-editor/types"; -import VerticalToggleButton, { - Option, -} from "../ui-components/VerticalToggleButton"; - -import { TimebasedResultType } from "./reducer"; - -const TimebasedNodeContainer = styled("div")` - border: 1px solid ${({ theme }) => theme.col.blueGray}; - border-radius: ${({ theme }) => theme.borderRadius}; - transition: all ${({ theme }) => theme.transitionTime}; - overflow: hidden; - - &:hover { - border: 1px solid ${({ theme }) => theme.col.blueGrayDark}; - } -`; - -const TimebasedNodeContent = styled("div")` - display: table; - width: 100%; - background-color: white; -`; - -const TimebasedNodeDescription = styled("div")` - display: table-cell; - vertical-align: middle; - position: relative; - padding: 10px; -`; -const TimebasedNodeDescriptionText = styled("p")` - word-break: break-word; -`; - -const TimebasedNodeTimestamp = styled("div")` - width: 70px; - display: table-cell; - vertical-align: middle; - border-right: 1px solid ${({ theme }) => theme.col.blueGrayLight}; - text-align: right; -`; - -const TimebasedNodeTimestampTitle = styled("p")` - font-size: ${({ theme }) => theme.font.tiny}; - margin: 0 0 5px; - padding: 2px 5px 0; - text-transform: uppercase; - font-weight: 700; - color: ${({ theme }) => theme.col.gray}; -`; - -const StyledIconButton = styled(IconButton)` - position: absolute; - top: 0; - right: 0; - z-index: 1; -`; - -const Root = styled("div")` - margin: 0 5px; - width: 200px; - font-size: ${({ theme }) => theme.font.sm}; -`; - -const StyledVerticalToggleButton = styled(VerticalToggleButton)` - ${Option} { - border: 0; - - &:first-of-type, - &:last-of-type { - border-radius: 0; - } - } -`; - -interface PropsT { - node: TimebasedResultType; - position: "left" | "right"; - onRemove: () => void; - onSetTimebasedNodeTimestamp: (value: string) => void; - conditionIdx: number; - resultIdx: number; -} - -const TimebasedNode: FC = ({ - node, - onRemove, - onSetTimebasedNodeTimestamp, - conditionIdx, - resultIdx, -}) => { - const { t } = useTranslation(); - const ref = useRef(null); - const item: DragItemQuery = { - ...node, - type: DNDType.PREVIOUS_QUERY, - tags: [], - dragContext: { - width: 0, - height: 0, - movedFromAndIdx: conditionIdx, - movedFromOrIdx: resultIdx, - }, - }; - const [, drag] = useDrag({ - type: item.type, - item: () => ({ - ...item, - dragContext: { - ...item.dragContext, - ...getWidthAndHeight(ref), - }, - }), - }); - - const toggleButton = ( - - ); - - return ( - { - ref.current = instance; - drag(instance); - }} - > - - - - - {t("timebasedQueryEditor.timestamp")} - - {toggleButton} - - - - - {node.label || node.id} - - - - - - ); -}; - -// &__index-result-btn -// border: none -// border-top: 1px solid $col-blue-gray-light -// width: 100% -// color: $col-black -// padding: 6px 0 -// background-color: white -// transition: all $transition-time -// font-size: $font-xs - -// &:hover -// &:not(.timebased-node__index-result-btn--active) -// background-color: $col-gray-very-light - -// &--active -// background-color: $col-blue-gray-very-light -// border-top: 1px solid $col-blue-gray -// color: $col-black - -// &--disabled -// cursor: not-allowed -// opacity: 0.6 -// Button indexResult (to re-enable this soon) -// - -export default TimebasedNode; diff --git a/frontend/src/js/timebased-query-editor/TimebasedQueryClearButton.tsx b/frontend/src/js/timebased-query-editor/TimebasedQueryClearButton.tsx deleted file mode 100644 index e1b4be4db7..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedQueryClearButton.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import styled from "@emotion/styled"; -import { faTrash } from "@fortawesome/free-solid-svg-icons"; -import { useTranslation } from "react-i18next"; -import { useDispatch, useSelector } from "react-redux"; - -import type { StateT } from "../app/reducers"; -import IconButton from "../button/IconButton"; - -import { clearTimebasedQuery } from "./actions"; -import { anyConditionFilled } from "./helpers"; - -const Root = styled("div")` - margin-bottom: 20px; - padding: 8px 20px 0 10px; -`; - -const TimebasedQueryClearButton = () => { - const { t } = useTranslation(); - const isEnabled = useSelector( - (state) => - state.timebasedQueryEditor.timebasedQuery.conditions.length > 1 || - anyConditionFilled(state.timebasedQueryEditor.timebasedQuery), - ); - - const dispatch = useDispatch(); - const clearQuery = () => dispatch(clearTimebasedQuery()); - - return ( - - - {t("common.clear")} - - - ); -}; - -export default TimebasedQueryClearButton; diff --git a/frontend/src/js/timebased-query-editor/TimebasedQueryEditor.tsx b/frontend/src/js/timebased-query-editor/TimebasedQueryEditor.tsx deleted file mode 100644 index 34198c8a5b..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedQueryEditor.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import styled from "@emotion/styled"; -import { faPlus } from "@fortawesome/free-solid-svg-icons"; -import { useTranslation } from "react-i18next"; -import { useDispatch, useSelector } from "react-redux"; - -import type { StateT } from "../app/reducers"; -import IconButton from "../button/IconButton"; -import type { DragItemQuery } from "../standard-query-editor/types"; - -import TimebasedCondition from "./TimebasedCondition"; -import { - addTimebasedCondition, - dropTimebasedNode, - removeTimebasedCondition, - removeTimebasedNode, - setTimebasedConditionMaxDays, - setTimebasedConditionMinDays, - setTimebasedConditionMinDaysOrNoEvent, - setTimebasedConditionOperator, - setTimebasedNodeTimestamp, -} from "./actions"; -import type { - TimebasedOperatorType, - TimebasedQueryStateT, - TimebasedResultType, -} from "./reducer"; - -const Root = styled("div")` - flex-grow: 1; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - padding: 0 20px 0 10px; -`; -const Connector = styled("p")` - font-size: ${({ theme }) => theme.font.sm}; - color: ${({ theme }) => theme.col.gray}; - text-align: center; - margin: 5px auto; -`; -const AddBtn = styled(IconButton)` - margin: 0 auto; - display: block; -`; - -const TimebasedQueryEditor = () => { - const { t } = useTranslation(); - - const query = useSelector( - (state) => state.timebasedQueryEditor.timebasedQuery, - ); - - const dispatch = useDispatch(); - - const onAddTimebasedCondition = () => dispatch(addTimebasedCondition()); - const onRemoveTimebasedCondition = (conditionIdx: number) => - dispatch(removeTimebasedCondition({ conditionIdx })); - const onSetTimebasedConditionOperator = ( - conditionIdx: number, - operator: TimebasedOperatorType, - ) => dispatch(setTimebasedConditionOperator({ conditionIdx, operator })); - const onDropTimebasedNode = ( - conditionIdx: number, - resultIdx: number, - node: TimebasedResultType | DragItemQuery, - moved: boolean, - ) => dispatch(dropTimebasedNode({ conditionIdx, resultIdx, node, moved })); - const onSetTimebasedNodeTimestamp = ( - conditionIdx: number, - resultIdx: number, - timestamp: string, - ) => - dispatch(setTimebasedNodeTimestamp({ conditionIdx, resultIdx, timestamp })); - const onRemoveTimebasedNode = ( - conditionIdx: number, - resultIdx: number, - moved: boolean, - ) => dispatch(removeTimebasedNode({ conditionIdx, resultIdx, moved })); - // const onSetTimebasedIndexResult = (indexResult) => - // dispatch(setTimebasedIndexResult(indexResult)); - const onSetTimebasedConditionMinDays = ( - conditionIdx: number, - days: number | null, - ) => dispatch(setTimebasedConditionMinDays({ conditionIdx, days })); - const onSetTimebasedConditionMaxDays = ( - conditionIdx: number, - days: number | null, - ) => dispatch(setTimebasedConditionMaxDays({ conditionIdx, days })); - const onSetTimebasedConditionMinDaysOrNoEvent = ( - conditionIdx: number, - days: number | null, - ) => dispatch(setTimebasedConditionMinDaysOrNoEvent({ conditionIdx, days })); - - return ( - - {query.conditions.map((condition, idx) => ( -
- 1} - onRemove={() => onRemoveTimebasedCondition(idx)} - onRemoveTimebasedNode={(resultIdx, moved) => { - onRemoveTimebasedNode(idx, resultIdx, moved); - }} - onSetOperator={(value) => - onSetTimebasedConditionOperator(idx, value) - } - onDropTimebasedNode={(resultIdx, node, moved) => { - onDropTimebasedNode(idx, resultIdx, node, moved); - }} - onSetTimebasedNodeTimestamp={(resultIdx, timestamp) => { - onSetTimebasedNodeTimestamp(idx, resultIdx, timestamp); - }} - // onSetTimebasedIndexResult={onSetTimebasedIndexResult} - onSetTimebasedConditionMinDays={(days) => { - onSetTimebasedConditionMinDays(idx, days); - }} - onSetTimebasedConditionMaxDays={(days) => { - onSetTimebasedConditionMaxDays(idx, days); - }} - onSetTimebasedConditionMinDaysOrNoEvent={(days) => { - onSetTimebasedConditionMinDaysOrNoEvent(idx, days); - }} - /> - - {t("common.and")} -
- ))} - - {t("timebasedQueryEditor.addCondition")} - -
- ); -}; - -export default TimebasedQueryEditor; diff --git a/frontend/src/js/timebased-query-editor/TimebasedQueryEditorDropzone.tsx b/frontend/src/js/timebased-query-editor/TimebasedQueryEditorDropzone.tsx deleted file mode 100644 index 3d1b80283f..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedQueryEditorDropzone.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import styled from "@emotion/styled"; -import { useTranslation } from "react-i18next"; -import { useDispatch } from "react-redux"; - -import { DNDType } from "../common/constants/dndTypes"; -import { exists } from "../common/helpers/exists"; -import type { DragItemQuery } from "../standard-query-editor/types"; -import Dropzone from "../ui-components/Dropzone"; - -import { removeTimebasedNode } from "./actions"; -import { TimebasedResultType } from "./reducer"; - -interface PropsType { - onDropNode: ( - node: TimebasedResultType | DragItemQuery, - moved: boolean, - ) => void; -} - -const StyledDropzone = styled(Dropzone)` - width: 150px; - text-align: center; -`; - -const DROP_TYPES = [DNDType.PREVIOUS_QUERY]; - -const TimebasedQueryEditorDropzone = ({ onDropNode }: PropsType) => { - const { t } = useTranslation(); - const dispatch = useDispatch(); - const onRemoveTimebasedNode = ( - conditionIdx: number, - resultIdx: number, - moved: boolean, - ) => dispatch(removeTimebasedNode({ conditionIdx, resultIdx, moved })); - - const onDrop = (item: DragItemQuery) => { - const { movedFromAndIdx, movedFromOrIdx } = item.dragContext; - - if (exists(movedFromAndIdx) && exists(movedFromOrIdx)) { - onRemoveTimebasedNode(movedFromAndIdx, movedFromOrIdx, true); - onDropNode(item, true); - } else { - onDropNode(item, false); - } - }; - - return ( - >> */ - acceptedDropTypes={DROP_TYPES} - onDrop={(item) => onDrop(item as DragItemQuery)} - > - {() => t("dropzone.dragQuery")} - - ); -}; - -export default TimebasedQueryEditorDropzone; diff --git a/frontend/src/js/timebased-query-editor/TimebasedQueryEditorTab.tsx b/frontend/src/js/timebased-query-editor/TimebasedQueryEditorTab.tsx deleted file mode 100644 index 00d28e4703..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedQueryEditorTab.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import TimebasedQueryClearButton from "./TimebasedQueryClearButton"; -import TimebasedQueryEditor from "./TimebasedQueryEditor"; -import TimebasedQueryRunner from "./TimebasedQueryRunner"; - -const TimebasedQueryEditorTab = () => ( - <> - - - - -); - -export default TimebasedQueryEditorTab; diff --git a/frontend/src/js/timebased-query-editor/TimebasedQueryRunner.tsx b/frontend/src/js/timebased-query-editor/TimebasedQueryRunner.tsx deleted file mode 100644 index 92aa7f668f..0000000000 --- a/frontend/src/js/timebased-query-editor/TimebasedQueryRunner.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useSelector } from "react-redux"; - -import type { DatasetT, QueryIdT } from "../api/types"; -import type { StateT } from "../app/reducers"; -import QueryRunner from "../query-runner/QueryRunner"; -import { useStartQuery, useStopQuery } from "../query-runner/actions"; -import { QueryRunnerStateT } from "../query-runner/reducer"; - -import { allConditionsFilled } from "./helpers"; -import { TimebasedQueryStateT } from "./reducer"; - -const selectIsButtonEnabled = - (datasetId: DatasetT["id"] | null, queryRunner: QueryRunnerStateT | null) => - (state: StateT) => { - if (!queryRunner) return false; - - return !!( - datasetId !== null && - !queryRunner.startQuery.loading && - !queryRunner.stopQuery.loading && - allConditionsFilled(state.timebasedQueryEditor.timebasedQuery) - ); - }; - -const TimebasedQueryRunner = () => { - const datasetId = useSelector( - (state) => state.datasets.selectedDatasetId, - ); - const queryRunner = useSelector( - (state) => state.timebasedQueryEditor.timebasedQueryRunner, - ); - const isButtonEnabled = useSelector( - selectIsButtonEnabled(datasetId, queryRunner), - ); - const isQueryRunning = !!queryRunner.runningQuery; - // Following ones only needed in dispatch functions - const queryId = useSelector( - (state) => state.timebasedQueryEditor.timebasedQueryRunner.runningQuery, - ); - const query = useSelector( - (state) => state.timebasedQueryEditor.timebasedQuery, - ); - - const startTimebasedQuery = useStartQuery("timebased"); - const stopTimebasedQuery = useStopQuery("timebased"); - - const startQuery = () => { - if (datasetId && allConditionsFilled(query)) { - startTimebasedQuery(datasetId, query); - } - }; - const stopQuery = () => { - if (queryId) { - stopTimebasedQuery(queryId); - } - }; - - return ( - - ); -}; - -export default TimebasedQueryRunner; diff --git a/frontend/src/js/timebased-query-editor/actions.ts b/frontend/src/js/timebased-query-editor/actions.ts deleted file mode 100644 index 71ea5e12c4..0000000000 --- a/frontend/src/js/timebased-query-editor/actions.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { ActionType, createAction } from "typesafe-actions"; - -import type { DragItemQuery } from "../standard-query-editor/types"; - -import { TimebasedOperatorType, TimebasedResultType } from "./reducer"; - -export type TimebasedActions = ActionType< - | typeof dropTimebasedNode - | typeof removeTimebasedNode - | typeof setTimebasedNodeTimestamp - | typeof setTimebasedConditionOperator - | typeof setTimebasedConditionMaxDays - | typeof setTimebasedConditionMinDays - | typeof setTimebasedConditionMinDaysOrNoEvent - | typeof setTimebasedIndexResult - | typeof addTimebasedCondition - | typeof removeTimebasedCondition - | typeof clearTimebasedQuery ->; - -export const dropTimebasedNode = createAction( - "timebased-query-editor/DROP_TIMEBASED_NODE", -)<{ - conditionIdx: number; - resultIdx: number; - node: TimebasedResultType | DragItemQuery; - moved: boolean; -}>(); - -export const removeTimebasedNode = createAction( - "timebased-query-editor/REMOVE_TIMEBASED_NODE", -)<{ conditionIdx: number; resultIdx: number; moved: boolean }>(); - -export const setTimebasedNodeTimestamp = createAction( - "timebased-query-editor/SET_TIMEBASED_NODE_TIMESTAMP", -)<{ - conditionIdx: number; - resultIdx: number; - timestamp: string; -}>(); - -export const setTimebasedConditionOperator = createAction( - "timebased-query-editor/SET_TIMEBASED_CONDITION_OPERATOR", -)<{ conditionIdx: number; operator: TimebasedOperatorType }>(); - -export const setTimebasedConditionMaxDays = createAction( - "timebased-query-editor/SET_TIMEBASED_CONDITION_MAX_DAYS", -)<{ conditionIdx: number; days: number | null }>(); - -export const setTimebasedConditionMinDays = createAction( - "timebased-query-editor/SET_TIMEBASED_CONDITION_MIN_DAYS", -)<{ conditionIdx: number; days: number | null }>(); - -export const setTimebasedConditionMinDaysOrNoEvent = createAction( - "timebased-query-editor/SET_TIME_BASED_CONDITION_MIN_DAYS_OR_NO_EVENT", -)<{ conditionIdx: number; days: number | null }>(); - -export const setTimebasedIndexResult = createAction( - "timebased-query-editor/SET_TIMEBASED_INDEX_RESULT", -)<{ indexResult: string }>(); - -export const addTimebasedCondition = createAction( - "timebased-query-editor/ADD_TIMEBASED_CONDITION", -)(); - -export const removeTimebasedCondition = createAction( - "timebased-query-editor/REMOVE_TIMEBASED_CONDITION", -)<{ conditionIdx: number }>(); - -export const clearTimebasedQuery = createAction( - "timebased-query-editor/CLEAR_TIMEBASED_QUERY", -)(); diff --git a/frontend/src/js/timebased-query-editor/helpers.ts b/frontend/src/js/timebased-query-editor/helpers.ts deleted file mode 100644 index e1ac930ee7..0000000000 --- a/frontend/src/js/timebased-query-editor/helpers.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { - TimebasedQueryStateT, - ValidatedTimebasedQueryStateT, -} from "./reducer"; - -export const allConditionsFilled = ( - timebasedQuery: TimebasedQueryStateT, -): timebasedQuery is ValidatedTimebasedQueryStateT => - timebasedQuery.conditions.every( - (condition) => !!condition.result0 && !!condition.result1, - ); - -export const anyConditionFilled = (timebasedQuery: TimebasedQueryStateT) => - timebasedQuery.conditions.some( - (condition) => condition.result0 || condition.result1, - ); diff --git a/frontend/src/js/timebased-query-editor/reducer.ts b/frontend/src/js/timebased-query-editor/reducer.ts deleted file mode 100644 index 12c142aab0..0000000000 --- a/frontend/src/js/timebased-query-editor/reducer.ts +++ /dev/null @@ -1,428 +0,0 @@ -import { ActionType, getType } from "typesafe-actions"; - -import { Action } from "../app/actions"; -import type { DragItemQuery } from "../standard-query-editor/types"; - -import { - addTimebasedCondition, - clearTimebasedQuery, - dropTimebasedNode, - removeTimebasedCondition, - removeTimebasedNode, - setTimebasedConditionMaxDays, - setTimebasedConditionMinDays, - setTimebasedConditionMinDaysOrNoEvent, - setTimebasedConditionOperator, - setTimebasedIndexResult, - setTimebasedNodeTimestamp, -} from "./actions"; - -export type TimebasedTimestampType = "EARLIEST" | "LATEST" | "RANDOM"; - -export type TimebasedResultType = { - id: string; - label: string; - timestamp: TimebasedTimestampType; -}; - -export type TimebasedOperatorType = - | "BEFORE" - | "BEFORE_OR_SAME" - | "SAME" - | "DAYS_BEFORE" - | "DAYS_OR_NO_EVENT_BEFORE"; - -export const TIMEBASED_OPERATOR_TYPES: TimebasedOperatorType[] = [ - "BEFORE", - "BEFORE_OR_SAME", - "SAME", - "DAYS_BEFORE", - "DAYS_OR_NO_EVENT_BEFORE", -]; - -export interface TimebasedConditionT { - operator: TimebasedOperatorType; - result0: TimebasedResultType | null; - result1: TimebasedResultType | null; - minDays?: number | null; - maxDays?: number | null; - minDaysOrNoEvent?: number | null; -} - -export interface ValidatedTimebasedConditionT extends TimebasedConditionT { - result0: TimebasedResultType; - result1: TimebasedResultType; -} - -export interface TimebasedQueryStateT { - indexResult: string | null; - conditions: TimebasedConditionT[]; -} - -export interface ValidatedTimebasedQueryStateT extends TimebasedQueryStateT { - conditions: ValidatedTimebasedConditionT[]; -} - -const getEmptyNode = () => ({ - operator: "BEFORE" as const, - result0: null, - result1: null, -}); - -const setTimebasedConditionAttributes = ( - state: TimebasedQueryStateT, - conditionIdx: number, - attributes: Partial, -) => { - return { - ...state, - conditions: [ - ...state.conditions.slice(0, conditionIdx), - { - ...state.conditions[conditionIdx], - ...attributes, - }, - ...state.conditions.slice(conditionIdx + 1), - ], - }; -}; - -const setNode = ( - state: TimebasedQueryStateT, - resultIdx: number, - conditionIdx: number, - node: TimebasedResultType | DragItemQuery, -) => { - const attributes = { - [`result${resultIdx}`]: { - id: node.id, - label: node.label, - timestamp: ("timestamp" in node && node.timestamp) || "EARLIEST", - } as TimebasedResultType, - }; - - return setTimebasedConditionAttributes(state, conditionIdx, attributes); -}; - -const conditionResultsToArray = (conditions: TimebasedConditionT[]) => { - return conditions.reduce((results, c) => { - if (c.result0) results.push(c.result0); - if (c.result1) results.push(c.result1); - - return results; - }, []); -}; - -const getPossibleIndexResults = (conditions: TimebasedConditionT[]) => { - return conditions.reduce( - (possibleResults, condition) => { - if (condition.operator === "DAYS_OR_NO_EVENT_BEFORE" && condition.result1) - possibleResults.push(condition.result1); - - if (condition.operator !== "DAYS_OR_NO_EVENT_BEFORE" && condition.result0) - possibleResults.push(condition.result0); - - if (condition.operator !== "DAYS_OR_NO_EVENT_BEFORE" && condition.result1) - possibleResults.push(condition.result1); - - return possibleResults; - }, - [], - ); -}; - -const ensureIndexResult = (state: TimebasedQueryStateT) => { - // Return if there is already an indexResult - if (state.indexResult) return state; - - // Ok, so there is none, let's ensure it - const possibleResults = getPossibleIndexResults(state.conditions); - // allResults includes results on the left side - // of conditions with a DAYS_OR_NO_EVENT_BEFORE operator - const allResults = conditionResultsToArray(state.conditions); - - // Best case - if (possibleResults.length > 0) - return { ...state, indexResult: possibleResults[0].id }; - - // Bad but ok case - if (allResults.length > 0) return { ...state, indexResult: allResults[0].id }; - - // Well, couldn't find any result - return { ...state, indexResult: null }; -}; - -const onDropTimebasedNode = ( - state: TimebasedQueryStateT, - { - node, - resultIdx, - conditionIdx, - moved, - }: ActionType["payload"], -) => { - const stateWithNode = setNode(state, resultIdx, conditionIdx, node); - - return moved ? stateWithNode : ensureIndexResult(stateWithNode); -}; - -const onRemoveTimebasedNode = ( - state: TimebasedQueryStateT, - { - conditionIdx, - resultIdx, - moved, - }: ActionType["payload"], -) => { - const condition = state.conditions[conditionIdx]; - const node = resultIdx === 0 ? condition.result0 : condition.result1; - - if (!node) return state; - - const attributes = { - [`result${resultIdx}`]: null, - }; - - const stateWithoutNode = setTimebasedConditionAttributes( - state, - conditionIdx, - attributes, - ); - - if (moved) return stateWithoutNode; - - // If item has not been moved and was indexResult, remove indexResult - const nextState = - node.id === state.indexResult - ? { ...stateWithoutNode, indexResult: null } - : stateWithoutNode; - - return ensureIndexResult(nextState); -}; - -const onSetTimebasedNodeTimestamp = ( - state: TimebasedQueryStateT, - { - conditionIdx, - resultIdx, - timestamp, - }: ActionType["payload"], -) => { - const condition = state.conditions[conditionIdx]; - const node = resultIdx === 0 ? condition.result0 : condition.result1; - const attributes = { - [`result${resultIdx}`]: { - ...node, - timestamp, - }, - }; - - return setTimebasedConditionAttributes(state, conditionIdx, attributes); -}; - -const onSetTimebasedConditionOperator = ( - state: TimebasedQueryStateT, - { - conditionIdx, - operator, - }: ActionType["payload"], -) => { - // Check if we're not switching to DAYS_OR_NO_EVENT_BEFORE. Then we're good. - // But IF IN FACT we do, check if the indexResult is somewhere else than on the left result - // Then we're also good. - const nextState = setTimebasedConditionAttributes(state, conditionIdx, { - operator, - }); - - const { result0 } = state.conditions[conditionIdx]; - - if ( - operator !== "DAYS_OR_NO_EVENT_BEFORE" || - !result0 || - result0.id !== state.indexResult - ) - return nextState; - - // Now that this didn't work we're switching the operator to DAYS_OR_NO_EVENT_BEFORE - // and the indexResult points to the first of both results of this condition. - // This is not allowed with DAYS_OR_NO_EVENT_BEFORE. - - // Let's try to find a possible result to be the new indexResult - const possibleResults = getPossibleIndexResults(nextState.conditions); - - // Now let's hope we found a possible result - return possibleResults.length === 0 - ? // Too bad, couldn't find a possible result - nextState - : // Nice, take the first result - { - ...nextState, - indexResult: possibleResults[0].id, - }; -}; - -const onSetTimebasedConditionMinDays = ( - state: TimebasedQueryStateT, - { - days, - conditionIdx, - }: ActionType["payload"], -) => { - return setTimebasedConditionAttributes(state, conditionIdx, { - minDays: days, - }); -}; - -const onSetTimebasedConditionMaxDays = ( - state: TimebasedQueryStateT, - { - days, - conditionIdx, - }: ActionType["payload"], -) => { - return setTimebasedConditionAttributes(state, conditionIdx, { - maxDays: days, - }); -}; - -const onSetTimebasedConditionMinDaysOrNoEvent = ( - state: TimebasedQueryStateT, - { - days, - conditionIdx, - }: ActionType["payload"], -) => { - return setTimebasedConditionAttributes(state, conditionIdx, { - minDaysOrNoEvent: days, - }); -}; - -const onSetTimebasedIndexResult = ( - state: TimebasedQueryStateT, - { indexResult }: ActionType["payload"], -) => { - return { - ...state, - indexResult, - }; -}; - -const onAddTimebasedCondition = (state: TimebasedQueryStateT) => { - return { - ...state, - conditions: [...state.conditions, getEmptyNode()], - }; -}; - -const onRemoveTimebasedCondition = ( - state: TimebasedQueryStateT, - { conditionIdx }: ActionType["payload"], -) => { - const deletedCondition = state.conditions[conditionIdx]; - const nextState = { - ...state, - conditions: [ - ...state.conditions.slice(0, conditionIdx), - ...state.conditions.slice(conditionIdx + 1), - ], - }; - - // if no node was indexed - if ( - !( - deletedCondition.result0 && - deletedCondition.result0.id === state.indexResult - ) && - !( - deletedCondition.result1 && - deletedCondition.result1.id === state.indexResult - ) - ) - return nextState; - - return ensureIndexResult({ ...nextState, indexResult: null }); -}; - -const initialState = { - indexResult: null, - conditions: [getEmptyNode()], -}; - -// This state contains multiple and-conditions. -// Every and-condition sets exactly two previous queries in a time-based relation. -// To be more specific, the RESULTS of two previous queries are set in relation. -// (The relation is measured in days) -// -// The result of this timebased query will look like those from any other query: -// -// A number of results with -// id, -// first date of occurrence, -// last date of occurrence, -// random dateof occurrence -// -// These dates will have to be taken from a single previous query -// (result1 or result2 from within specific condition) the "indexResult". -// -// Example: -// -// { -// indexResult: 2523, -// conditions: [ -// { -// operator: 'BEFORE', -// // operator: 'BEFORE_OR_SAME', -// // operator: 'SAME', -// result0: { -// id: 2523, -// timestamp: 'FIRST', -// // timestamp: 'LAST', -// // timestamp: 'RANDOM', -// }, -// result1: { -// id: 2525, -// timestamp: 'LAST' -// }, -// minDays: 5, -// maxDays: 10 -// }, -// { -// operator: 'SAME', -// result0: { id: 6274, timestamp: "RANDOM" }, -// result1: { id: 5274, timestamp: "RANDOM" }, -// } -// ] -// } -const timebasedQuery = ( - state: TimebasedQueryStateT = initialState, - action: Action, -): TimebasedQueryStateT => { - switch (action.type) { - case getType(dropTimebasedNode): - return onDropTimebasedNode(state, action.payload); - case getType(removeTimebasedNode): - return onRemoveTimebasedNode(state, action.payload); - case getType(setTimebasedNodeTimestamp): - return onSetTimebasedNodeTimestamp(state, action.payload); - case getType(setTimebasedConditionOperator): - return onSetTimebasedConditionOperator(state, action.payload); - case getType(setTimebasedConditionMinDays): - return onSetTimebasedConditionMinDays(state, action.payload); - case getType(setTimebasedConditionMaxDays): - return onSetTimebasedConditionMaxDays(state, action.payload); - case getType(setTimebasedConditionMinDaysOrNoEvent): - return onSetTimebasedConditionMinDaysOrNoEvent(state, action.payload); - case getType(setTimebasedIndexResult): - return onSetTimebasedIndexResult(state, action.payload); - case getType(addTimebasedCondition): - return onAddTimebasedCondition(state); - case getType(removeTimebasedCondition): - return onRemoveTimebasedCondition(state, action.payload); - case getType(clearTimebasedQuery): - return initialState; - default: - return state; - } -}; - -export default timebasedQuery; diff --git a/frontend/src/js/user/userSettings.ts b/frontend/src/js/user/userSettings.ts index e9976531ac..302cd9a79c 100644 --- a/frontend/src/js/user/userSettings.ts +++ b/frontend/src/js/user/userSettings.ts @@ -2,14 +2,12 @@ const localStorage: Storage = window.localStorage; interface UserSettings { - showEditorV2: boolean; arePreviousQueriesFoldersOpen: boolean; preferredDownloadEnding?: string; // Usually CSV or XLSX preferredDownloadLabel?: string; // Label of the preferred Download format (e.g. "All files") } const initialState: UserSettings = { - showEditorV2: false, arePreviousQueriesFoldersOpen: false, preferredDownloadEnding: undefined, preferredDownloadLabel: undefined, From ede81eb938c6d147fb035cdb1b944c67b4c217ad Mon Sep 17 00:00:00 2001 From: Max Thonagel <12283268+thoniTUB@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:36:15 +0100 Subject: [PATCH 18/18] add token to silently failing action --- .github/workflows/prepare-merge-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/prepare-merge-release.yml b/.github/workflows/prepare-merge-release.yml index b1f4caa502..3ff3290a2b 100644 --- a/.github/workflows/prepare-merge-release.yml +++ b/.github/workflows/prepare-merge-release.yml @@ -20,6 +20,8 @@ jobs: id: check_pr_count run: > echo "pr_count=$(gh pr list --head release --limit 1 --base $MAIN_BRANCH_NAME --state open --json state --jq 'length')" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create pull-request if: ${{ steps.check_pr_count.outputs.pr_count == 0 }}