diff --git a/.github/ct.yml b/.github/ct.yml
new file mode 100644
index 00000000000..53c9c3d3f17
--- /dev/null
+++ b/.github/ct.yml
@@ -0,0 +1,20 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+
+# consider helm install to be failed after 10 minutes
+helm-extra-args: --timeout 10m
+check-version-increment: true
+debug: true
+chart-dirs:
+ - deployment/helm
+chart-repos:
+ - stable=https://charts.helm.sh/stable
+ - bitnami=https://charts.bitnami.com/bitnami
diff --git a/.github/kubeval.sh b/.github/kubeval.sh
new file mode 100755
index 00000000000..105cdece542
--- /dev/null
+++ b/.github/kubeval.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+
+#
+# use kubeval to validate helm generated kubernetes manifest
+#
+
+set -o errexit
+set -o pipefail
+
+CHART_DIR=deployment/helm/ditto
+KUBEVAL_VERSION="v0.16.1"
+SCHEMA_LOCATION="https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/"
+
+# install kubeval
+curl --silent --show-error --fail --location --output /tmp/kubeval.tar.gz https://github.com/instrumenta/kubeval/releases/download/"${KUBEVAL_VERSION}"/kubeval-linux-amd64.tar.gz
+sudo tar -C /usr/local/bin -xf /tmp/kubeval.tar.gz kubeval
+
+# add helm repos to resolve dependencies
+helm repo add stable https://charts.helm.sh/stable
+helm repo add bitnami https://charts.bitnami.com/bitnami
+
+# validate chart
+echo "helm dependency build..."
+helm dependency build "${CHART_DIR}"
+
+echo "kubeval(idating) ${CHART_DIR} chart..."
+helm template "${CHART_DIR}" | kubeval --strict --ignore-missing-schemas --kubernetes-version "${KUBERNETES_VERSION#v}" --schema-location "${SCHEMA_LOCATION}"
diff --git a/.github/workflows/docker-nightly.yml b/.github/workflows/docker-nightly.yml
index 49163666e37..66633f10181 100644
--- a/.github/workflows/docker-nightly.yml
+++ b/.github/workflows/docker-nightly.yml
@@ -17,6 +17,7 @@ on:
jobs:
build:
+ if: github.repository == 'eclipse-ditto/ditto'
runs-on: ubuntu-latest
steps:
-
@@ -129,7 +130,20 @@ jobs:
tags: |
eclipse/ditto-connectivity:${{ env.IMAGE_TAG }}
-
- name: Build and push ditto-ui
+ name: Use Node.js 18.x
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ -
+ name: Install npm dependencies
+ run: npm install
+ working-directory: ./ui
+ -
+ name: Build UI with node
+ run: npm run build
+ working-directory: ./ui
+ -
+ name: Build and push ditto-ui image
uses: docker/build-push-action@v3
with:
context: ./ui
diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index d507c2ecf97..0a6aff80868 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -30,6 +30,16 @@ jobs:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v3
+ - name: Use Node.js 18.x
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ - name: Install npm dependencies
+ run: npm install
+ working-directory: ./ui
+ - name: Build UI with node
+ run: npm run build
+ working-directory: ./ui
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
diff --git a/.github/workflows/helm-chart-release.yml b/.github/workflows/helm-chart-release.yml
new file mode 100644
index 00000000000..decdbf3f82e
--- /dev/null
+++ b/.github/workflows/helm-chart-release.yml
@@ -0,0 +1,56 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+name: Release and publish Helm chart
+
+env:
+ VERSION_HELM: "v3.11.2"
+on:
+ workflow_dispatch:
+ inputs:
+ chartVersion:
+ description: 'Helm chart version'
+ required: true
+ type: string
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Set up Helm
+ uses: azure/setup-helm@v3
+ with:
+ version: ${{ env.VERSION_HELM }}
+
+ - name: Helm | Login
+ shell: bash
+ run: echo ${{ secrets.DOCKER_HUB_TOKEN }} | helm registry login -u eclipsedittobot --password-stdin registry-1.docker.io
+
+ - name: Helm | Package
+ shell: bash
+ run: helm package deployment/helm/ditto --dependency-update --version ${{ inputs.chartVersion }}
+
+ - name: Helm | Push
+ shell: bash
+ run: helm push ditto-${{ inputs.chartVersion }}.tgz oci://registry-1.docker.io/eclipse
+
+ - name: Helm | Logout
+ shell: bash
+ run: helm registry logout registry-1.docker.io
+
+ - name: Helm | Output
+ id: output
+ shell: bash
+ run: echo "image=registry-1.docker.io/eclipse/ditto:${{ inputs.chartVersion }}" >> $GITHUB_OUTPUT
diff --git a/.github/workflows/helm-chart.yml b/.github/workflows/helm-chart.yml
new file mode 100644
index 00000000000..ef472e9597f
--- /dev/null
+++ b/.github/workflows/helm-chart.yml
@@ -0,0 +1,129 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+name: Lint and test Helm chart
+
+env:
+ CONFIG_OPTION_CHART_TESTING: "--config .github/ct.yml"
+ VERSION_CHART_TESTING: "v3.8.0"
+ VERSION_HELM: "v3.11.2"
+ VERSION_PYTHON: "3.9"
+on:
+ pull_request:
+ paths:
+ - 'deployment/helm/**'
+ - '.github/workflows/helm-chart.yml'
+ - '.github/ct.yml'
+ - '.github/kubeval.sh'
+
+jobs:
+ lint-chart:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: Set up Helm
+ uses: azure/setup-helm@v3
+ with:
+ version: ${{ env.VERSION_HELM }}
+ - uses: actions/setup-python@v4
+ with:
+ python-version: ${{ env.VERSION_PYTHON }}
+ check-latest: true
+ - name: Set up chart-testing
+ uses: helm/chart-testing-action@v2.4.0
+ with:
+ version: ${{ env.VERSION_CHART_TESTING }}
+ - name: Run chart-testing (list-changed)
+ id: list-changed
+ run: |
+ changed=$(ct list-changed ${{ env.CONFIG_OPTION_CHART_TESTING }} --target-branch ${{ github.event.repository.default_branch }})
+ if [[ -n "$changed" ]]; then
+ echo "changed=true" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Run chart-testing (lint)
+ if: steps.list-changed.outputs.changed == 'true'
+ run: ct lint ${{ env.CONFIG_OPTION_CHART_TESTING }} --target-branch ${{ github.event.repository.default_branch }}
+
+ kubeval-chart:
+ runs-on: ubuntu-latest
+ needs: lint-chart
+ strategy:
+ matrix:
+ # the versions supported by kubeval are the ones for
+ # which a folder exists at
+ # https://github.com/yannh/kubernetes-json-schema/
+ k8s:
+ - v1.25.2
+ - v1.26.4
+ - v1.27.1
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Fetch history for chart testing
+ run: git fetch --prune --unshallow
+ - name: Set up Helm
+ uses: azure/setup-helm@v3
+ with:
+ version: ${{ env.VERSION_HELM }}
+ - name: Run kubeval
+ env:
+ KUBERNETES_VERSION: ${{ matrix.k8s }}
+ run: .github/kubeval.sh
+
+ install-chart:
+ name: install-chart
+ runs-on: ubuntu-latest
+ needs:
+ - lint-chart
+ - kubeval-chart
+ strategy:
+ matrix:
+ # the versions supported by chart-testing are the tags
+ # available for the docker.io/kindest/node image
+ # https://hub.docker.com/r/kindest/node/tags
+ k8s:
+ - v1.25.2
+ - v1.26.4
+ - v1.27.1
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Fetch history for chart testing
+ run: git fetch --prune --unshallow
+ - name: Set up Helm
+ uses: azure/setup-helm@v3
+ with:
+ version: ${{ env.VERSION_HELM }}
+ - uses: actions/setup-python@v4
+ with:
+ python-version: ${{ env.VERSION_PYTHON }}
+ check-latest: true
+ - name: Set up chart-testing
+ uses: helm/chart-testing-action@v2.4.0
+ with:
+ version: ${{ env.VERSION_CHART_TESTING }}
+ - name: Run chart-testing (list-changed)
+ id: list-changed
+ run: |
+ changed=$(ct list-changed ${{ env.CONFIG_OPTION_CHART_TESTING }} --target-branch ${{ github.event.repository.default_branch }})
+ if [[ -n "$changed" ]]; then
+ echo "changed=true" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Create kind ${{ matrix.k8s }} cluster
+ if: steps.list-changed.outputs.changed == 'true'
+ uses: helm/kind-action@v1.4.0
+ with:
+ node_image: kindest/node:${{ matrix.k8s }}
+ - name: Run chart-testing (install)
+ if: steps.list-changed.outputs.changed == 'true'
+ run: ct install ${{ env.CONFIG_OPTION_CHART_TESTING }} --target-branch ${{ github.event.repository.default_branch }}
diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml
index a6e7224e006..1254d09eb1d 100644
--- a/.github/workflows/license-check.yml
+++ b/.github/workflows/license-check.yml
@@ -29,7 +29,7 @@ jobs:
- name: Ensure license year for added files is the file's creation year
shell: bash
run: |
- included_file_endings=".*\.(java|xml|yml)"
+ included_file_endings=".*\.(java|xml|yml|yaml)"
current_year=$(date +'%Y')
missing_counter=0
for file in ${{ steps.the-files.outputs.added }}; do
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 716c7e506d5..d51ab5d3516 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -18,6 +18,13 @@ on:
# Run build for any PR
pull_request:
+ paths-ignore:
+ - 'README.md'
+ - 'RELEASE.md'
+ - 'CONTRIBUTING.md'
+ - 'SECURITY.md'
+ - 'deployment/**'
+ - 'documentation/**'
jobs:
build:
diff --git a/.github/workflows/push-dockerhub-on-demand.yml b/.github/workflows/push-dockerhub-on-demand.yml
index 91c0f382aa1..173394fc299 100644
--- a/.github/workflows/push-dockerhub-on-demand.yml
+++ b/.github/workflows/push-dockerhub-on-demand.yml
@@ -170,6 +170,19 @@ jobs:
eclipse/ditto-connectivity:${{ env.IMAGE_MINOR_TAG }}
eclipse/ditto-connectivity:${{ env.IMAGE_MAJOR_TAG }}
eclipse/ditto-connectivity:latest
+ -
+ name: Use Node.js 18.x
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ -
+ name: Install npm dependencies
+ run: npm install
+ working-directory: ./ui
+ -
+ name: Build UI with node
+ run: npm run build
+ working-directory: ./ui
-
name: Build and push ditto-ui
if: env.MILESTONE_OR_RC_SUFFIX == env.IMAGE_TAG && inputs.dittoImage == 'ditto-ui'
diff --git a/.github/workflows/push-dockerhub.yml b/.github/workflows/push-dockerhub.yml
index 0b2930c89b7..47189cb50d6 100644
--- a/.github/workflows/push-dockerhub.yml
+++ b/.github/workflows/push-dockerhub.yml
@@ -155,6 +155,19 @@ jobs:
eclipse/ditto-connectivity:${{ env.IMAGE_MINOR_TAG }}
eclipse/ditto-connectivity:${{ env.IMAGE_MAJOR_TAG }}
eclipse/ditto-connectivity:latest
+ -
+ name: Use Node.js 18.x
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ -
+ name: Install npm dependencies
+ run: npm install
+ working-directory: ./ui
+ -
+ name: Build UI with node
+ run: npm run build
+ working-directory: ./ui
-
name: Build and push ditto-ui
if: env.MILESTONE_OR_RC_SUFFIX == env.IMAGE_TAG
diff --git a/.gitignore b/.gitignore
index e28468ceb25..bb0a6751374 100755
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,5 @@ deployment/helm/eclipse-ditto/requirements.lock
deployment/helm/eclipse-ditto/charts/*
.factorypath
ui/node_modules
-ui/package.json
-ui/package-lock.json
+ui/dist
diff --git a/.run/ConnectivityService.run.xml b/.run/ConnectivityService.run.xml
index fd0fa2ff98d..19697e5c888 100644
--- a/.run/ConnectivityService.run.xml
+++ b/.run/ConnectivityService.run.xml
@@ -1,31 +1,15 @@
-
+
+
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/.run/Ditto.run.xml b/.run/Ditto.run.xml
index ac5bcbb86b2..6aacf9255db 100644
--- a/.run/Ditto.run.xml
+++ b/.run/Ditto.run.xml
@@ -1,15 +1,3 @@
-
diff --git a/.run/GatewayService.run.xml b/.run/GatewayService.run.xml
index 7884b94f5bc..de83356697b 100644
--- a/.run/GatewayService.run.xml
+++ b/.run/GatewayService.run.xml
@@ -1,15 +1,3 @@
-
@@ -18,12 +6,6 @@
-
-
-
-
-
-
diff --git a/.run/PoliciesService.run.xml b/.run/PoliciesService.run.xml
index b4789fa5f43..f966193eb35 100644
--- a/.run/PoliciesService.run.xml
+++ b/.run/PoliciesService.run.xml
@@ -1,31 +1,16 @@
-
+
+
+
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/.run/SearchService.run.xml b/.run/SearchService.run.xml
index 98ee0fb7b41..25444065eaa 100644
--- a/.run/SearchService.run.xml
+++ b/.run/SearchService.run.xml
@@ -1,15 +1,3 @@
-
@@ -23,12 +11,6 @@
-
-
-
-
-
-
diff --git a/.run/ThingsService.run.xml b/.run/ThingsService.run.xml
index 3a9dafa3e4b..5716c61ad0a 100644
--- a/.run/ThingsService.run.xml
+++ b/.run/ThingsService.run.xml
@@ -1,31 +1,14 @@
-
+
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1cb0974a9f3..09328574e5c 100755
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -56,7 +56,7 @@ Please make sure any file you newly create contains a proper license header. Fin
Adjusted for Java classes:
```java
/*
- * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
@@ -72,7 +72,7 @@ Adjusted for Java classes:
Adjusted for XML files:
```xml
```
-### Important
-
-Please do not forget to add your name/organization to the [legal/NOTICE.md](legal/NOTICE.md) file's Copyright Holders
-section. If this is not the first contribution you make, then simply update the time period contained in the copyright
-entry to use the year of your first contribution as the lower boundary and the current year as the upper boundary, e.g.
-
- Copyright 2018-2019 ACME Corporation
-
## Submitting the Changes
Submit a pull request via the normal GitHub UI.
@@ -100,3 +92,128 @@ Submit a pull request via the normal GitHub UI.
## After Submitting
* Do not use your branch for any other development, otherwise further changes that you make will be visible in the PR.
+
+
+# OSS development process - rules
+
+As of 02/2023, the following additional "rules" regarding the open OSS development process were agreed on.
+
+## Addition of new features via feature toggles
+
+Goals:
+* Reduce the risk for other Ditto users that a new feature have an impact on existing functionality and stability
+* Whenever possible (and feasible), added functionality shall be added using a "feature toggle".
+
+Ditto already has a class `FeatureToggle.java` where feature toggles are contained and providing functionality to
+"secure" a feature with a method in there which throws an `UnsupportedSignalException` once a feature is used which
+is disabled via feature toggle.
+
+The toggles are then configured in `ditto-devops.conf` file and can be enabled/disabled via the contained environment variables.
+
+## Creating GitHub issues before starting to work on code
+
+Goals:
+* Improve transparency on what is currently happening
+* Openly discuss new features and whether they are a good fit for Ditto
+* Reduce "time waste"
+
+Whenever a new feature or a bugfix is being worked on, we want to create an issue in Ditto's GitHub project **beforehand**:
+https://github.com/eclipse-ditto/ditto/issues
+
+This provides the needed transparency for other contributors before much effort is put into a new topic in order to:
+* Get input on the background (e.g. use case behind / "the need") of the feature/bugfix
+* Provide feedback, maybe even suggesting alternatives instead
+* Provide suggestions of how to implement it the most efficient way
+* Maybe even find synergies when more than 1 contributing companies currently have the same topic to work on
+
+The following situation shall be prevented:
+* If no issue is created upfront, a contributing company e.g. invests 2 months of work in a new feature
+* Then a PR is created with this new functionality
+* Only then, a discussion with other contributors can start
+* At this point, when there e.g. is a big flaw in the architecture or security or API stability of the added functionality,
+ the invested 2 months could - in the worst case - be a complete waste of time
+* This could easily be resolved by discussing it beforehand
+
+## Create PullRequests early
+
+Goals:
+* Get early feedback on implementation details of new features / bugfixes
+* Prevent that an implementation goes "into the wrong direction" (e.g. performance or security wise)
+
+PullRequests should be created quite early and publicly on the Ditto project.
+If they are not yet "ready" to review/merge, they must be marked as "DRAFT" - once they are ready, they can be marked
+as such and a review can be performed.
+
+## Make use of GitHub "Projects" for showing current work for next release
+
+Goals:
+* Make transparent "who" currently works on "what"
+* Make transparent what the current agenda for the next Ditto release is
+
+The new "Projects" capabilities of GitHub look more than sufficient of what we want to achieve here:
+* https://github.com/orgs/eclipse-ditto/projects/1
+* https://github.com/orgs/eclipse-ditto/projects/1/views/2 (table view is especially useful, as grouping by "Milestone" is necessary)
+
+## Establish system-tests in the OpenSource codebase
+
+Goals:
+* Provide means to run automated tests for future enhancements to Ditto
+* Secure existing functionality, avoid breaking APIs and existing functionality when changes to the Ditto OSS codebase are done
+
+The system tests for Eclipse Ditto were initiated here:
+https://github.com/eclipse-ditto/ditto-testing
+
+The tests should be part of the validations done in a PR before a PR is approved and merged.
+In order to be able to do that, we want to clarify if the Eclipse Foundation can provide enough resources in order to
+run the system-tests in a stable way.
+
+Currently, that seems to be quite difficult, as projects only have very limited resources in order to run their builds.
+In addition, the CI runs in an OpenShift cluster with additional restrictions, e.g. regarding the kind of Docker images
+which can be run, exposing of the Docker socket, etc.
+
+## Regular community meetings
+
+Goals:
+
+* Discuss upcoming topics/problems in advance
+* Stay in touch via audio/video
+* Build up a (contributor and adopter) community who can help each other
+
+We want to re-establish regular community meetings/call, e.g. a meeting every 2 weeks for 1 hour.
+We can utilize the Zoom account from the Eclipse Foundation to have a "neutral" one .. or just use "Google Meet".
+
+## Chat for (internal) exchanges
+
+Goals:
+* Have a direct channel where to reach other Ditto committers and contributors
+* In order to get timely responses if e.g. a bugfix release has to be scheduled/done quickly
+
+We can use "Gitter.IM" communities to add different rooms of which some also can be private:
+https://gitter.im/EclipseDitto/community
+
+## Release strategy
+
+Goals:
+* Have rules of how often to do "planned feature releases"
+* Have options for contributing companies to prioritize a release (e.g. if urgent bugfix or urgent feature release)
+
+The suggestion would be to have approximately 4 planned minor releases per year, 1 each quarter (e.g. 03 / 06 / 09 / 12).
+If needed and all contributing companies agree minor releases can also happen earlier/more often.
+
+Bugfix releases should be done immediately if a critical bug was fixed and either the contributors or the community need a quick fix release.
+
+## Approving / merging PRs
+
+Goals:
+* PullRequests - once they are ready - shall not stay unmerged for a long time as this leads to the risk they are not
+ mergable or get outdated quickly
+
+Approach:
+
+* Before merging a PR at least 1 approval is required
+ * Approvals shall only be issued after a code review
+ * Preferably that would be 1 approval from an existing Ditto committer
+ * But could also be the approval of an active contributor who does not yet have committer status
+* If no approval is given for a PR within a duration of 4 weeks after declaring it "ready", a PR can also be merged without other approvals
+ * Before doing so, the reasons for not approving must be found out (e.g. via the Chat / community call)
+ * If the reason simply is "no time" and there are no objections against that PR, the PR can be merged without other approvals
diff --git a/NOTICE.md b/NOTICE.md
index 3e022ace3d9..d53c4ca9742 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -1,6 +1,6 @@
This content is produced and maintained by the Eclipse Ditto project.
-* Project home: https://www.eclipse.org/ditto
+* Project home: https://www.eclipse.dev/ditto
# Trademarks
@@ -23,7 +23,7 @@ SPDX-License-Identifier: EPL-2.0
# Source Code
-* https://github.com/eclipse/ditto
+* https://github.com/eclipse-ditto/ditto
# Third-party Content
diff --git a/README.md b/README.md
index 71564ff8b25..b5f5169f07d 100755
--- a/README.md
+++ b/README.md
@@ -7,20 +7,20 @@
# Eclipse Ditto™
[![Join the chat at https://gitter.im/eclipse/ditto](https://badges.gitter.im/eclipse/ditto.svg)](https://gitter.im/eclipse/ditto?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[![Build Status](https://github.com/eclipse/ditto/workflows/build/badge.svg)](https://github.com/eclipse/ditto/actions?query=workflow%3Abuild)
+[![Build Status](https://github.com/eclipse-ditto/ditto/workflows/build/badge.svg)](https://github.com/eclipse-ditto/ditto/actions?query=workflow%3Abuild)
[![Maven Central](https://img.shields.io/maven-central/v/org.eclipse.ditto/ditto?label=maven)](https://search.maven.org/search?q=g:org.eclipse.ditto)
[![Docker pulls](https://img.shields.io/docker/pulls/eclipse/ditto-things.svg)](https://hub.docker.com/search?q=eclipse%2Fditto&type=image)
[![License](https://img.shields.io/badge/License-EPL%202.0-green.svg)](https://opensource.org/licenses/EPL-2.0)
[![Lines of code](https://img.shields.io/badge/dynamic/xml.svg?label=Lines%20of%20code&url=https%3A%2F%2Fwww.openhub.net%2Fprojects%2Feclipse-ditto.xml%3Fapi_key%3D11ac3aa12a364fd87b461559a7eedcc53e18fb5a4cf1e43e02cb7a615f1f3d4f&query=%2Fresponse%2Fresult%2Fproject%2Fanalysis%2Ftotal_code_lines&colorB=lightgrey)](https://www.openhub.net/p/eclipse-ditto)
-[Eclipse Ditto](https://www.eclipse.org/ditto/)™ is a technology in the IoT implementing a software pattern called “digital twins”.
+[Eclipse Ditto](https://www.eclipse.dev/ditto/)™ is a technology in the IoT implementing a software pattern called “digital twins”.
A digital twin is a virtual, cloud based, representation of his real world counterpart (real world “Things”, e.g. devices like sensors, smart heating, connected cars, smart grids, EV charging stations, …).
An ever growing list of [adopters](https://iot.eclipse.org/adopters/?#iot.ditto) makes use of Ditto as part of their IoT platforms - if you're as well using it, it would be super nice to show your [adoption here](https://iot.eclipse.org/adopters/how-to-be-listed-as-an-adopter/).
## Documentation
-Find the documentation on the project site: [https://www.eclipse.org/ditto/](https://www.eclipse.org/ditto/)
+Find the documentation on the project site: [https://www.eclipse.dev/ditto/](https://www.eclipse.dev/ditto/)
## Eclipse Ditto™ explorer UI
@@ -30,7 +30,7 @@ You should be able to work with your locally running default using the `local_di
## Star History
-[![Star History Chart](https://api.star-history.com/svg?repos=eclipse-ditto/ditto&type=Date)](https://star-history.com/?secret=Z2hwXzJERUNBUmFRa09KM3BvdTFMUkJ1Y3VnY25FV3hxVjNBM3hEVQ==#eclipse-ditto/ditto&Date)
+[![Star History Chart](https://api.star-history.com/svg?repos=eclipse-ditto/ditto&type=Date)](https://star-history.com/#eclipse-ditto/ditto&Date)
## Getting started
@@ -41,7 +41,7 @@ In order to start up Ditto via *Docker Compose*, you'll need:
* at least 2 CPU cores which can be used by Docker
* at least 4 GB of RAM which can be used by Docker
-You also have other possibilities to run Ditto, please have a look [here](https://github.com/eclipse/ditto/tree/master/deployment) to explore them.
+You also have other possibilities to run Ditto, please have a look [here](https://github.com/eclipse-ditto/ditto/tree/master/deployment) to explore them.
### Start Ditto
@@ -58,7 +58,7 @@ docker-compose logs -f
```
Open following URL to get started: [http://localhost:8080](http://localhost:8080)
-Or have a look at the ["Hello World"](https://www.eclipse.org/ditto/intro-hello-world.html)
+Or have a look at the ["Hello World"](https://www.eclipse.dev/ditto/intro-hello-world.html)
Additional [deployment options](deployment/) are also available, if Docker Compose is not what you want to use.
diff --git a/RELEASE.md b/RELEASE.md
index b986c398bd4..cc93d5667b0 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -14,21 +14,21 @@ Ditto releases are tracked and planned here: https://projects.eclipse.org/projec
* First close the staging repo (after all artifacts are there, e.g. also the client artifacts)
* Then release the staging repo
* Then it will take a few hours until those changes are synced successfully to Maven central
-* Write Release notes, e.g. like for 3.1.0: https://www.eclipse.org/ditto/release_notes_310.html
+* Write Release notes, e.g. like for 3.1.0: https://www.eclipse.dev/ditto/release_notes_310.html
* New features, changes, bug fixes to last release / milestone release
* Add migration notes (if there are any)
-* Write a Blog post announcement, e.g. like for: https://www.eclipse.org/ditto/2022-12-16-release-announcement-310.html
-* Close GitHub milestone (and assign all Issues/PRs which were still included in that milestone): https://github.com/eclipse/ditto/milestones
-* Create a GitHub release: https://github.com/eclipse/ditto/releases (based on the Tags which was pushed during release job)
+* Write a Blog post announcement, e.g. like for: https://www.eclipse.dev/ditto/2022-12-16-release-announcement-310.html
+* Close GitHub milestone (and assign all Issues/PRs which were still included in that milestone): https://github.com/eclipse-ditto/ditto/milestones
+* Create a GitHub release: https://github.com/eclipse-ditto/ditto/releases (based on the Tags which was pushed during release job)
* Write a mail to the "ditto-dev" mailing list
* Tweet about it ;)
* Set binary compatibility check version to the new public release. Delete all exclusions and module-level deactivation of japi-cmp plugin except for *.internal packages.
-* Update https://github.com/eclipse/ditto/blob/master/SECURITY.md with the supported versions to receive security fixes
+* Update https://github.com/eclipse-ditto/ditto/blob/master/SECURITY.md with the supported versions to receive security fixes
* For major+minor versions:
* Create a "release" branch"release-" from the released git tag
* needed to build the documentation from
* required for bugfixes to build a bugfix release for the affected minor version
- * Add the new version to the documentation config: https://github.com/eclipse/ditto/blob/master/documentation/src/main/resources/_config.yml#L114
+ * Add the new version to the documentation config: https://github.com/eclipse-ditto/ditto/blob/master/documentation/src/main/resources/_config.yml#L114
* Adjust the "website" CI jobs to also build the newly added branch:
* https://ci.eclipse.org/ditto/view/Website/
* https://ci.eclipse.org/ditto/view/Website/job/website-build-and-deploy-fast/ will build the latest released minor version + "master" (development) version
diff --git a/SECURITY.md b/SECURITY.md
index db6acad65fa..14caa8332fe 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,14 +1,17 @@
# Security Policy
+Eclipse Ditto follows the [Eclipse Vulnerability Reporting Policy](https://www.eclipse.org/security/policy.php). Vulnerabilities are tracked by the Eclipse security team, in cooperation with the Ditto project leads. Fixing vulnerabilities is taken care of by the Ditto project committers, with assistance and guidance of the security team.
+
## Supported Versions
+Eclipse Ditto provides security updates for the two most recent minor versions.
These versions of Eclipse Ditto are currently being supported with security updates.
| Version | Supported |
|---------| ------------------ |
-| 3.1.x | :white_check_mark: |
-| 3.0.x | :white_check_mark: |
-| < 3.0.0 | :x: |
+| 3.3.x | :white_check_mark: |
+| 3.2.x | :white_check_mark: |
+| < 3.2.0 | :x: |
## Reporting a Vulnerability
diff --git a/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceResponseTest.java b/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceResponseTest.java
index b46a3584672..3e5406a7965 100644
--- a/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceResponseTest.java
+++ b/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceResponseTest.java
@@ -20,6 +20,7 @@
import org.eclipse.ditto.base.model.common.HttpStatus;
import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
import org.eclipse.ditto.base.model.entity.type.EntityType;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.signals.commands.CommandResponse;
@@ -33,7 +34,7 @@
*/
public final class CleanupPersistenceResponseTest {
- private static final EntityId ID = EntityId.of(EntityType.of("thing"), "eclipse:ditto");
+ private static final EntityId ID = NamespacedEntityId.of(EntityType.of("thing"), "eclipse:ditto");
private static final JsonObject KNOWN_JSON = JsonObject.newBuilder()
.set(CommandResponse.JsonFields.TYPE, CleanupPersistenceResponse.TYPE)
.set(CleanupCommandResponse.JsonFields.ENTITY_TYPE, ID.getEntityType().toString())
diff --git a/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceTest.java b/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceTest.java
index c1de951f4bd..43ba4297412 100644
--- a/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceTest.java
+++ b/base/api/src/test/java/org/eclipse/ditto/base/api/persistence/cleanup/CleanupPersistenceTest.java
@@ -18,6 +18,7 @@
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
import org.eclipse.ditto.base.model.entity.type.EntityType;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.signals.commands.Command;
@@ -31,7 +32,7 @@
*/
public class CleanupPersistenceTest {
- private static final EntityId ID = EntityId.of(EntityType.of("thing"), "eclipse:ditto");
+ private static final EntityId ID = NamespacedEntityId.of(EntityType.of("thing"), "eclipse:ditto");
private static final JsonObject KNOWN_JSON = JsonObject.newBuilder()
.set(Command.JsonFields.TYPE, CleanupPersistence.TYPE)
.set(CleanupCommand.JsonFields.ENTITY_TYPE, ID.getEntityType().toString())
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/acks/AcknowledgementLabelInvalidException.java b/base/model/src/main/java/org/eclipse/ditto/base/model/acks/AcknowledgementLabelInvalidException.java
index 8dbc94eed48..03f40f04390 100644
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/acks/AcknowledgementLabelInvalidException.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/acks/AcknowledgementLabelInvalidException.java
@@ -19,12 +19,12 @@
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
-import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.base.model.common.HttpStatus;
import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.json.JsonObject;
/**
* Thrown if an AcknowledgementLabel is not valid, for example because it did not comply to the AcknowledgmentLabel
@@ -48,7 +48,7 @@ public final class AcknowledgementLabelInvalidException extends DittoRuntimeExce
"An acknowledgement label must conform to the regular expression of Ditto documentation.";
private static final URI DEFAULT_HREF = URI.create(
- "https://www.eclipse.org/ditto/protocol-specification-topic.html#acknowledgement-criterion-actions");
+ "https://www.eclipse.dev/ditto/protocol-specification-topic.html#acknowledgement-criterion-actions");
private static final long serialVersionUID = -2385649293006205966L;
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/common/LikeHelper.java b/base/model/src/main/java/org/eclipse/ditto/base/model/common/LikeHelper.java
index 75a3d9bd566..e25db8a962b 100644
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/common/LikeHelper.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/common/LikeHelper.java
@@ -14,6 +14,8 @@
import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
/**
* A helper to create "like" patterns.
*
@@ -40,7 +42,8 @@ private LikeHelper() {
* @param expression The wildcard expression to convert.
* @return The regular expression, which can be compiled with {@link Pattern#compile(String)}.
*/
- public static String convertToRegexSyntax(final String expression) {
+ @Nullable
+ public static String convertToRegexSyntax(@Nullable final String expression) {
if (expression == null) {
return null;
}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/EntityId.java b/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/EntityId.java
index 32566d5fd02..47f71909a7a 100644
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/EntityId.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/EntityId.java
@@ -35,7 +35,13 @@ public interface EntityId extends CharSequence, Comparable {
*/
static EntityId of(final EntityType entityType, final CharSequence entityId) {
final EntityIds entityIds = EntityIds.getInstance();
- return entityIds.getEntityId(entityType, entityId);
+ try {
+ // most entity ids are namespaces, so try that first
+ return entityIds.getNamespacedEntityId(entityType, entityId);
+ } catch (final NamespacedEntityIdInvalidException namespacedEntityIdInvalidException) {
+ // only in the exceptional case, fall back to non-namespaced flavor:
+ return entityIds.getEntityId(entityType, entityId);
+ }
}
@Override
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/NamespacedEntityIdInvalidException.java b/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/NamespacedEntityIdInvalidException.java
index 4843ff97940..da4d16a030a 100644
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/NamespacedEntityIdInvalidException.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/entity/id/NamespacedEntityIdInvalidException.java
@@ -56,7 +56,7 @@ public final class NamespacedEntityIdInvalidException extends EntityIdInvalidExc
"length of 256 characters.";
private static final URI DEFAULT_HREF =
- URI.create("https://www.eclipse.org/ditto/basic-namespaces-and-names.html#namespaced-id");
+ URI.create("https://www.eclipse.dev/ditto/basic-namespaces-and-names.html#namespaced-id");
private static final long serialVersionUID = -8903476318490123234L;
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeaders.java b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeaders.java
index aaa6b176a0e..f7c32d086fe 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeaders.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeaders.java
@@ -313,6 +313,12 @@ public Optional getIfNoneMatch() {
.map(EntityTagMatchers::fromCommaSeparatedString);
}
+ @Override
+ public Optional getIfEqual() {
+ return getStringForDefinition(DittoHeaderDefinition.IF_EQUAL)
+ .flatMap(IfEqual::forOption);
+ }
+
@Override
public Optional getInboundPayloadMapper() {
return getStringForDefinition(DittoHeaderDefinition.INBOUND_PAYLOAD_MAPPER);
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeadersBuilder.java b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeadersBuilder.java
index 3dde80d8717..f91f8737923 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeadersBuilder.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/AbstractDittoHeadersBuilder.java
@@ -410,6 +410,12 @@ public S ifNoneMatch(final EntityTagMatchers entityTags) {
return myself;
}
+ @Override
+ public S ifEqual(final IfEqual ifEqual) {
+ putCharSequence(DittoHeaderDefinition.IF_EQUAL, ifEqual.toString());
+ return myself;
+ }
+
@Override
public S inboundPayloadMapper(@Nullable final String inboundPayloadMapperId) {
putCharSequence(DittoHeaderDefinition.INBOUND_PAYLOAD_MAPPER, inboundPayloadMapperId);
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaderDefinition.java b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaderDefinition.java
index b8f520c80a4..438418c524a 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaderDefinition.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaderDefinition.java
@@ -195,6 +195,21 @@ public enum DittoHeaderDefinition implements HeaderDefinition {
false,
HeaderValueValidators.getEntityTagMatchersValidator()),
+ /**
+ * Header definition for "If-Equal".
+ * Can hold one of the values: {@code update}, {@code skip}.
+ *
+ * Key: {@code "If-Equal"}, Java type: {@link String}.
+ *
+ * @since 3.3.0
+ */
+ IF_EQUAL("if-equal",
+ IfEqual.class,
+ String.class,
+ true,
+ false,
+ HeaderValueValidators.getEnumValidator(IfEqual.values())),
+
/**
* Header definition for the internal header "ditto-reply-target". This header is evaluated for responses to be
* published.
@@ -349,7 +364,7 @@ public enum DittoHeaderDefinition implements HeaderDefinition {
*
* @since 3.0.0
*/
- DITTO_METADATA("ditto-metadata", JsonObject.class, false, true, HeaderValueValidators.getNoOpValidator()),
+ DITTO_METADATA("ditto-metadata", JsonObject.class, false, true, HeaderValueValidators.getJsonObjectValidator()),
/**
* Header definition for allowing the policy lockout (i.e. a subject can create a policy without having WRITE
@@ -484,7 +499,43 @@ public enum DittoHeaderDefinition implements HeaderDefinition {
Boolean.class,
false,
true,
- HeaderValueValidators.getBooleanValidator());
+ HeaderValueValidators.getBooleanValidator()),
+
+ /**
+ * Header containing a specific historical revision to retrieve when retrieving a persisted entity
+ * (thing/policy/connection).
+ *
+ * @since 3.2.0
+ */
+ AT_HISTORICAL_REVISION("at-historical-revision",
+ Long.class,
+ true,
+ false,
+ HeaderValueValidators.getLongValidator()),
+
+ /**
+ * Header containing a specific historical timestamp to retrieve when retrieving a persisted entity
+ * (thing/policy/connection).
+ *
+ * @since 3.2.0
+ */
+ AT_HISTORICAL_TIMESTAMP("at-historical-timestamp",
+ String.class,
+ true,
+ false,
+ HeaderValueValidators.getNoOpValidator()),
+
+ /**
+ * Header containing retrieved historical headers to be returned for e.g. a historical retrieve command.
+ * Useful for audit-log information, e.g. which "originator" did a change to a thing/policy/connection.
+ *
+ * @since 3.2.0
+ */
+ HISTORICAL_HEADERS("historical-headers",
+ JsonObject.class,
+ false,
+ true,
+ HeaderValueValidators.getJsonObjectValidator());
/**
* Map to speed up lookup of header definition by key.
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaders.java b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaders.java
index 0e48413c85d..5bc4497cfad 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaders.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeaders.java
@@ -266,6 +266,14 @@ static DittoHeadersBuilder newBuilder(final JsonObject jsonObject) {
*/
Optional getIfNoneMatch();
+ /**
+ * Returns the "If-Equal" header defining whether to update a value if it was equal to the previous value or not.
+ *
+ * @return the if-equal header.
+ * @since 3.3.0
+ */
+ Optional getIfEqual();
+
/**
* Returns the inbound {@code MessageMapper} ID which mapped incoming arbitrary payload from external sources.
*
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeadersBuilder.java b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeadersBuilder.java
index 1ded962e980..6fac2c3c2c3 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeadersBuilder.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/DittoHeadersBuilder.java
@@ -189,6 +189,16 @@ default B randomCorrelationId() {
*/
B ifNoneMatch(EntityTagMatchers entityTags);
+ /**
+ * Sets the If-Equal value.
+ *
+ * @param ifEqual The if-equal value to set defining what to do with a value to update which is equal to
+ * its previous value.
+ * @return this builder for Method Chaining
+ * @since 3.3.0
+ */
+ B ifEqual(IfEqual ifEqual);
+
/**
* Sets the inbound {@code MessageMapper} ID value.
*
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/headers/IfEqual.java b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/IfEqual.java
new file mode 100644
index 00000000000..90e833a8a59
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/headers/IfEqual.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.headers;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * Possible options for Ditto's {@code if-equal} header.
+ *
+ * @since 3.3.0
+ */
+public enum IfEqual {
+
+ /**
+ * Option which updates a value, even if the value is the same (via {@code equal()}) than the value before.
+ * This is the default if omitted and for backwards compatibility.
+ */
+ UPDATE("update"),
+
+ /**
+ * Option which will skip the update of a twin if the new value is the same (via {@code equal()}) than the value
+ * before.
+ */
+ SKIP("skip");
+
+ private final String option;
+
+ IfEqual(final String option) {
+ this.option = option;
+ }
+
+ @Override
+ public String toString() {
+ return option;
+ }
+
+ /**
+ * Find an If-Equal option by a provided option string.
+ *
+ * @param option the option.
+ * @return the option with the given option string if any exists.
+ */
+ public static Optional forOption(final String option) {
+ return Arrays.stream(values())
+ .filter(strategy -> strategy.toString().equals(option))
+ .findAny();
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/FeatureToggle.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/FeatureToggle.java
index 07ee3bcc1d8..7bfc5bb5c9c 100644
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/FeatureToggle.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/FeatureToggle.java
@@ -33,6 +33,12 @@ public final class FeatureToggle {
*/
public static final String WOT_INTEGRATION_ENABLED = "ditto.devops.feature.wot-integration-enabled";
+ /**
+ * System property name of the property defining whether the historical API access is enabled.
+ * @since 3.2.0
+ */
+ public static final String HISTORICAL_APIS_ENABLED = "ditto.devops.feature.historical-apis-enabled";
+
/**
* Resolves the system property {@value MERGE_THINGS_ENABLED}.
*/
@@ -43,6 +49,11 @@ public final class FeatureToggle {
*/
private static final boolean IS_WOT_INTEGRATION_ENABLED = resolveProperty(WOT_INTEGRATION_ENABLED);
+ /**
+ * Resolves the system property {@value HISTORICAL_APIS_ENABLED}.
+ */
+ private static final boolean IS_HISTORICAL_APIS_ENABLED = resolveProperty(HISTORICAL_APIS_ENABLED);
+
private static boolean resolveProperty(final String propertyName) {
final String propertyValue = System.getProperty(propertyName, Boolean.TRUE.toString());
return !Boolean.FALSE.toString().equalsIgnoreCase(propertyValue);
@@ -101,4 +112,35 @@ public static DittoHeaders checkWotIntegrationFeatureEnabled(final String signal
public static boolean isWotIntegrationFeatureEnabled() {
return IS_WOT_INTEGRATION_ENABLED;
}
+
+ /**
+ * Checks if the historical API access feature is enabled based on the system property {@value HISTORICAL_APIS_ENABLED}.
+ *
+ * @param signal the name of the signal that was supposed to be processed
+ * @param dittoHeaders headers used to build exception
+ * @return the unmodified headers parameters
+ * @throws UnsupportedSignalException if the system property
+ * {@value HISTORICAL_APIS_ENABLED} resolves to {@code false}
+ * @since 3.2.0
+ */
+ public static DittoHeaders checkHistoricalApiAccessFeatureEnabled(final String signal, final DittoHeaders dittoHeaders) {
+ if (!isHistoricalApiAccessFeatureEnabled()) {
+ throw UnsupportedSignalException
+ .newBuilder(signal)
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+ return dittoHeaders;
+ }
+
+ /**
+ * Returns whether the historical API access feature is enabled based on the system property
+ * {@value HISTORICAL_APIS_ENABLED}.
+ *
+ * @return whether the historical API access feature is enabled or not.
+ * @since 3.2.0
+ */
+ public static boolean isHistoricalApiAccessFeatureEnabled() {
+ return IS_HISTORICAL_APIS_ENABLED;
+ }
}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/WithStreamingSubscriptionId.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/WithStreamingSubscriptionId.java
new file mode 100755
index 00000000000..a6810d5113a
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/WithStreamingSubscriptionId.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals;
+
+import org.eclipse.ditto.base.model.signals.commands.streaming.StreamingSubscriptionCommand;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+
+/**
+ * Interface of streaming commands/events addressing a particular session identified by a subscription ID.
+ *
+ * @since 3.2.0
+ */
+public interface WithStreamingSubscriptionId> extends
+ StreamingSubscriptionCommand {
+
+ /**
+ * Returns the subscriptionId identifying the session of this streaming signal.
+ *
+ * @return the subscriptionId.
+ */
+ String getSubscriptionId();
+
+ /**
+ * Json fields of this command.
+ */
+ final class JsonFields {
+
+ /**
+ * JSON field for the streaming subscription ID.
+ */
+ public static final JsonFieldDefinition SUBSCRIPTION_ID =
+ JsonFactory.newStringFieldDefinition("subscriptionId");
+
+ JsonFields() {
+ throw new AssertionError();
+ }
+
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/Command.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/Command.java
index 4afd8b0ff4b..c4277fa6960 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/Command.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/Command.java
@@ -159,7 +159,13 @@ enum Category {
* Category of commands that are neither of the above 3 (query, modify, delete) but perform an action on the
* entity.
*/
- ACTION;
+ ACTION,
+
+ /**
+ * Category of commands that stream e.g. historical events.
+ * @since 3.2.0
+ */
+ STREAM;
/**
* Determines whether the passed {@code category} effectively modifies the targeted entity.
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionNotFoundException.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionNotFoundException.java
new file mode 100755
index 00000000000..f0a4c9e3e03
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionNotFoundException.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.exceptions;
+
+import java.net.URI;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.eclipse.ditto.base.model.common.HttpStatus;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.base.model.exceptions.GeneralException;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.json.JsonObject;
+
+/**
+ * Error response for streaming subscription commands addressing a nonexistent subscription.
+ *
+ * @since 3.2.0
+ */
+@JsonParsableException(errorCode = StreamingSubscriptionNotFoundException.ERROR_CODE)
+public class StreamingSubscriptionNotFoundException extends DittoRuntimeException implements GeneralException {
+
+ /**
+ * Error code of this exception.
+ */
+ public static final String ERROR_CODE = ERROR_CODE_PREFIX + "streaming.subscription.not.found";
+
+ private static final HttpStatus STATUS_CODE = HttpStatus.NOT_FOUND;
+
+ private StreamingSubscriptionNotFoundException(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+
+ super(ERROR_CODE, STATUS_CODE, dittoHeaders, message, description, cause, href);
+ }
+
+ /**
+ * Create a {@code StreamingSubscriptionNotFoundException}.
+ *
+ * @param subscriptionId ID of the nonexistent subscription.
+ * @param dittoHeaders the Ditto headers.
+ * @return the exception.
+ */
+ public static StreamingSubscriptionNotFoundException of(final String subscriptionId, final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(String.format("No subscription with ID '%s' exists.", subscriptionId))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * Constructs a new {@code StreamingSubscriptionNotFoundException} object with the exception message extracted from the
+ * given JSON object.
+ *
+ * @param jsonObject the JSON to read the {@link DittoRuntimeException.JsonFields#MESSAGE} field from.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new StreamingSubscriptionNotFoundException.
+ * @throws NullPointerException if any argument is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionNotFoundException fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
+ }
+
+ @Override
+ public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(getMessage())
+ .description(getDescription().orElse(null))
+ .cause(getCause())
+ .href(getHref().orElse(null))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * A mutable builder with a fluent API for a {@link StreamingSubscriptionNotFoundException}.
+ */
+ @NotThreadSafe
+ public static final class Builder extends DittoRuntimeExceptionBuilder {
+
+ private Builder() {}
+
+ @Override
+ protected StreamingSubscriptionNotFoundException doBuild(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ return new StreamingSubscriptionNotFoundException(dittoHeaders, message, description, cause, href);
+ }
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionProtocolErrorException.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionProtocolErrorException.java
new file mode 100755
index 00000000000..5875e8660db
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionProtocolErrorException.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.exceptions;
+
+import java.net.URI;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.eclipse.ditto.base.model.common.HttpStatus;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.base.model.exceptions.GeneralException;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.json.JsonObject;
+
+/**
+ * Error response for subscriptions with no interaction for a long time.
+ *
+ * @since 3.2.0
+ */
+@JsonParsableException(errorCode = StreamingSubscriptionProtocolErrorException.ERROR_CODE)
+public class StreamingSubscriptionProtocolErrorException extends DittoRuntimeException implements GeneralException {
+
+ /**
+ * Error code of this exception.
+ */
+ public static final String ERROR_CODE = ERROR_CODE_PREFIX + "streaming.subscription.protocol.error";
+
+ private static final HttpStatus STATUS_CODE = HttpStatus.BAD_REQUEST;
+
+ private StreamingSubscriptionProtocolErrorException(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+
+ super(ERROR_CODE, STATUS_CODE, dittoHeaders, message, description, cause, href);
+ }
+
+ /**
+ * Create a {@code StreamingSubscriptionProtocolErrorException}.
+ *
+ * @param cause the actual protocol error.
+ * @param dittoHeaders the Ditto headers.
+ * @return the exception.
+ */
+ public static StreamingSubscriptionProtocolErrorException of(final Throwable cause, final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(cause.getMessage())
+ .cause(cause)
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * Create an empty builder for this exception.
+ *
+ * @return an empty builder.
+ */
+ public static DittoRuntimeExceptionBuilder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Constructs a new {@code StreamingSubscriptionProtocolErrorException} object with the exception message extracted from the
+ * given JSON object.
+ *
+ * @param jsonObject the JSON to read the {@link DittoRuntimeException.JsonFields#MESSAGE} field from.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new StreamingSubscriptionProtocolErrorException.
+ * @throws NullPointerException if any argument is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionProtocolErrorException fromJson(final JsonObject jsonObject,
+ final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
+ }
+
+ @Override
+ public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(getMessage())
+ .description(getDescription().orElse(null))
+ .cause(getCause())
+ .href(getHref().orElse(null))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * A mutable builder with a fluent API for a {@link StreamingSubscriptionProtocolErrorException}.
+ */
+ @NotThreadSafe
+ public static final class Builder extends DittoRuntimeExceptionBuilder {
+
+ private Builder() {}
+
+ @Override
+ protected StreamingSubscriptionProtocolErrorException doBuild(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ return new StreamingSubscriptionProtocolErrorException(dittoHeaders, message, description, cause, href);
+ }
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionTimeoutException.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionTimeoutException.java
new file mode 100755
index 00000000000..a55feff9a31
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/exceptions/StreamingSubscriptionTimeoutException.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.exceptions;
+
+import java.net.URI;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.eclipse.ditto.base.model.common.HttpStatus;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.base.model.exceptions.GeneralException;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.json.JsonObject;
+
+/**
+ * Error response for subscriptions with no interaction for a long time.
+ *
+ * @since 3.2.0
+ */
+@JsonParsableException(errorCode = StreamingSubscriptionTimeoutException.ERROR_CODE)
+public class StreamingSubscriptionTimeoutException extends DittoRuntimeException implements GeneralException {
+
+ /**
+ * Error code of this exception.
+ */
+ public static final String ERROR_CODE = ERROR_CODE_PREFIX + "streaming.subscription.timeout";
+
+ private static final HttpStatus STATUS_CODE = HttpStatus.REQUEST_TIMEOUT;
+
+ private StreamingSubscriptionTimeoutException(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+
+ super(ERROR_CODE, STATUS_CODE, dittoHeaders, message, description, cause, href);
+ }
+
+ /**
+ * Create a {@code StreamingSubscriptionTimeoutException}.
+ *
+ * @param subscriptionId ID of the nonexistent subscription.
+ * @param dittoHeaders the Ditto headers.
+ * @return the exception.
+ */
+ public static StreamingSubscriptionTimeoutException of(final String subscriptionId, final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(String.format("The subscription '%s' stopped due to a lack of interaction.", subscriptionId))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * Constructs a new {@code StreamingSubscriptionTimeoutException} object with the exception message extracted from the
+ * given JSON object.
+ *
+ * @param jsonObject the JSON to read the {@link DittoRuntimeException.JsonFields#MESSAGE} field from.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new StreamingSubscriptionTimeoutException.
+ * @throws NullPointerException if any argument is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionTimeoutException fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
+ }
+
+ @Override
+ public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(getMessage())
+ .description(getDescription().orElse(null))
+ .cause(getCause())
+ .href(getHref().orElse(null))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * A mutable builder with a fluent API for a {@link StreamingSubscriptionTimeoutException}.
+ */
+ @NotThreadSafe
+ public static final class Builder extends DittoRuntimeExceptionBuilder {
+
+ private Builder() {}
+
+ @Override
+ protected StreamingSubscriptionTimeoutException doBuild(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ return new StreamingSubscriptionTimeoutException(dittoHeaders, message, description, cause, href);
+ }
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/AbstractStreamingSubscriptionCommand.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/AbstractStreamingSubscriptionCommand.java
new file mode 100755
index 00000000000..1b8ff670875
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/AbstractStreamingSubscriptionCommand.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
+
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.entity.id.EntityIdJsonDeserializer;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.entity.type.EntityTypeJsonDeserializer;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
+import org.eclipse.ditto.base.model.signals.commands.AbstractCommand;
+import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * Abstract base class for streaming commands.
+ *
+ * @param the type of the AbstractStreamingSubscriptionCommand
+ * @since 3.2.0
+ */
+@Immutable
+abstract class AbstractStreamingSubscriptionCommand>
+ extends AbstractCommand
+ implements StreamingSubscriptionCommand {
+
+ protected final EntityId entityId;
+ protected final JsonPointer resourcePath;
+
+ protected AbstractStreamingSubscriptionCommand(final String type,
+ final EntityId entityId,
+ final JsonPointer resourcePath,
+ final DittoHeaders dittoHeaders) {
+
+ super(type, dittoHeaders);
+ this.entityId = checkNotNull(entityId, "entityId");
+ this.resourcePath = checkNotNull(resourcePath, "resourcePath");
+ }
+
+ protected static EntityId deserializeEntityId(final JsonObject jsonObject) {
+ return EntityIdJsonDeserializer.deserializeEntityId(jsonObject,
+ StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_ID,
+ EntityTypeJsonDeserializer.deserializeEntityType(jsonObject,
+ StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_TYPE));
+ }
+
+ @Override
+ public Category getCategory() {
+ return Category.STREAM;
+ }
+
+ @Override
+ public EntityId getEntityId() {
+ return entityId;
+ }
+
+ @Override
+ public EntityType getEntityType() {
+ return entityId.getEntityType();
+ }
+
+ @Override
+ public JsonPointer getResourcePath() {
+ return resourcePath;
+ }
+
+ @Override
+ public String getResourceType() {
+ return getEntityType().toString();
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder,
+ final JsonSchemaVersion schemaVersion,
+ final Predicate thePredicate) {
+
+ final Predicate predicate = schemaVersion.and(thePredicate);
+ jsonObjectBuilder.set(StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_TYPE,
+ entityId.getEntityType().toString(), predicate);
+ jsonObjectBuilder.set(StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_ID,
+ entityId.toString(), predicate);
+ jsonObjectBuilder.set(StreamingSubscriptionCommand.JsonFields.JSON_RESOURCE_PATH,
+ resourcePath.toString(), predicate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), entityId, resourcePath);
+ }
+
+ @SuppressWarnings({"squid:MethodCyclomaticComplexity", "squid:S1067", "OverlyComplexMethod"})
+ @Override
+ public boolean equals(@Nullable final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final AbstractStreamingSubscriptionCommand> other = (AbstractStreamingSubscriptionCommand>) obj;
+
+ return other.canEqual(this) &&
+ super.equals(other) &&
+ Objects.equals(entityId, other.entityId) &&
+ Objects.equals(resourcePath, other.resourcePath);
+ }
+
+ @Override
+ protected boolean canEqual(@Nullable final Object other) {
+ return other instanceof AbstractStreamingSubscriptionCommand;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() +
+ ", entityId=" + entityId +
+ ", entityType=" + getEntityType() +
+ ", resourcePath=" + resourcePath;
+ }
+
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/CancelStreamingSubscription.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/CancelStreamingSubscription.java
new file mode 100755
index 00000000000..8586c5cd5fe
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/CancelStreamingSubscription.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableCommand;
+import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.commands.CommandJsonDeserializer;
+import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * Command for cancelling a subscription of streaming results.
+ * Corresponds to the reactive-streams signal {@code Subscription#cancel()}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableCommand(typePrefix = StreamingSubscriptionCommand.TYPE_PREFIX, name = CancelStreamingSubscription.NAME)
+public final class CancelStreamingSubscription extends AbstractStreamingSubscriptionCommand
+ implements WithStreamingSubscriptionId {
+
+ /**
+ * Name of the command.
+ */
+ public static final String NAME = "cancel";
+
+ /**
+ * Type of this command.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private final String subscriptionId;
+
+ private CancelStreamingSubscription(final EntityId entityId,
+ final JsonPointer resourcePath,
+ final String subscriptionId,
+ final DittoHeaders dittoHeaders) {
+ super(TYPE, entityId, resourcePath, dittoHeaders);
+ this.subscriptionId = subscriptionId;
+ }
+
+ /**
+ * Returns a new instance of the command.
+ *
+ * @param entityId the entityId that should be streamed.
+ * @param resourcePath the resource path for which to stream.
+ * @param subscriptionId ID of the subscription to cancel.
+ * @param dittoHeaders the headers of the command.
+ * @return a new command to cancel a subscription.
+ * @throws NullPointerException if {@code dittoHeaders} is {@code null}.
+ */
+ public static CancelStreamingSubscription of(final EntityId entityId,
+ final JsonPointer resourcePath,
+ final String subscriptionId,
+ final DittoHeaders dittoHeaders) {
+ return new CancelStreamingSubscription(entityId, resourcePath, subscriptionId, dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code CancelStreamingSubscription} from a JSON object.
+ *
+ * @param jsonObject the JSON object of which the command is to be created.
+ * @param dittoHeaders the headers of the command.
+ * @return the command.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static CancelStreamingSubscription fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new CommandJsonDeserializer(TYPE, jsonObject).deserialize(() ->
+ new CancelStreamingSubscription(deserializeEntityId(jsonObject),
+ JsonPointer.of(
+ jsonObject.getValueOrThrow(StreamingSubscriptionCommand.JsonFields.JSON_RESOURCE_PATH)),
+ jsonObject.getValueOrThrow(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID),
+ dittoHeaders
+ )
+ );
+ }
+
+ @Override
+ public String getSubscriptionId() {
+ return subscriptionId;
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder, final JsonSchemaVersion schemaVersion,
+ final Predicate thePredicate) {
+
+ super.appendPayload(jsonObjectBuilder, schemaVersion, thePredicate);
+ jsonObjectBuilder.set(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID, subscriptionId);
+ }
+
+ @Override
+ public CancelStreamingSubscription setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new CancelStreamingSubscription(entityId, resourcePath, subscriptionId, dittoHeaders);
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CancelStreamingSubscription)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ final CancelStreamingSubscription that = (CancelStreamingSubscription) o;
+ return Objects.equals(subscriptionId, that.subscriptionId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), subscriptionId);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" +
+ super.toString() +
+ ", subscriptionId=" + subscriptionId +
+ ']';
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/RequestFromStreamingSubscription.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/RequestFromStreamingSubscription.java
new file mode 100755
index 00000000000..4a72cde4204
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/RequestFromStreamingSubscription.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableCommand;
+import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.commands.CommandJsonDeserializer;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * Command for requesting items from a subscription of streaming results.
+ * Corresponds to the reactive-streams signal {@code Subscription#request(long)}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableCommand(typePrefix = StreamingSubscriptionCommand.TYPE_PREFIX, name = RequestFromStreamingSubscription.NAME)
+public final class RequestFromStreamingSubscription extends AbstractStreamingSubscriptionCommand
+ implements WithStreamingSubscriptionId {
+
+ /**
+ * Name of the command.
+ */
+ public static final String NAME = "request";
+
+ /**
+ * Type of this command.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private final String subscriptionId;
+ private final long demand;
+
+ private RequestFromStreamingSubscription(final EntityId entityId,
+ final JsonPointer resourcePath,
+ final String subscriptionId,
+ final long demand,
+ final DittoHeaders dittoHeaders) {
+
+ super(TYPE, entityId, resourcePath, dittoHeaders);
+ this.subscriptionId = subscriptionId;
+ this.demand = demand;
+ }
+
+ /**
+ * Returns a new instance of the command.
+ *
+ * @param entityId the entityId that should be streamed.
+ * @param resourcePath the resource path for which to stream.
+ * @param subscriptionId ID of the subscription to request from.
+ * @param demand how many pages to request.
+ * @param dittoHeaders the headers of the command.
+ * @return a new command to request from a subscription.
+ * @throws NullPointerException if {@code dittoHeaders} is {@code null}.
+ */
+ public static RequestFromStreamingSubscription of(final EntityId entityId,
+ final JsonPointer resourcePath,
+ final String subscriptionId,
+ final long demand,
+ final DittoHeaders dittoHeaders) {
+ return new RequestFromStreamingSubscription(entityId, resourcePath, subscriptionId, demand, dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code RequestSubscription} from a JSON object.
+ *
+ * @param jsonObject the JSON object of which the command is to be created.
+ * @param dittoHeaders the headers of the command.
+ * @return the command.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static RequestFromStreamingSubscription fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new CommandJsonDeserializer(TYPE, jsonObject).deserialize(() ->
+ new RequestFromStreamingSubscription(deserializeEntityId(jsonObject),
+ JsonPointer.of(
+ jsonObject.getValueOrThrow(StreamingSubscriptionCommand.JsonFields.JSON_RESOURCE_PATH)),
+ jsonObject.getValueOrThrow(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID),
+ jsonObject.getValueOrThrow(JsonFields.DEMAND),
+ dittoHeaders
+ )
+ );
+ }
+
+
+ @Override
+ public String getSubscriptionId() {
+ return subscriptionId;
+ }
+
+
+ /**
+ * Returns the demand which is to be included in the JSON of the retrieved entity.
+ *
+ * @return the demand.
+ */
+ public long getDemand() {
+ return demand;
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder, final JsonSchemaVersion schemaVersion,
+ final Predicate thePredicate) {
+
+ super.appendPayload(jsonObjectBuilder, schemaVersion, thePredicate);
+ jsonObjectBuilder.set(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID, subscriptionId);
+ jsonObjectBuilder.set(JsonFields.DEMAND, demand);
+ }
+
+ @Override
+ public String getResourceType() {
+ return getEntityType().toString();
+ }
+
+ @Override
+ public RequestFromStreamingSubscription setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new RequestFromStreamingSubscription(entityId, resourcePath, subscriptionId, demand, dittoHeaders);
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof RequestFromStreamingSubscription)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ final RequestFromStreamingSubscription that = (RequestFromStreamingSubscription) o;
+ return Objects.equals(subscriptionId, that.subscriptionId) && demand == that.demand;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), subscriptionId, demand);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" +
+ super.toString() +
+ ", subscriptionId=" + subscriptionId +
+ ", demand=" + demand +
+ ']';
+ }
+
+ /**
+ * JSON fields of this command.
+ */
+ public static final class JsonFields {
+
+ /**
+ * JSON field for number of pages demanded by this command.
+ */
+ public static final JsonFieldDefinition DEMAND = JsonFactory.newLongFieldDefinition("demand");
+
+ JsonFields() {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/StreamingSubscriptionCommand.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/StreamingSubscriptionCommand.java
new file mode 100755
index 00000000000..f57d240d934
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/StreamingSubscriptionCommand.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import static org.eclipse.ditto.base.model.json.FieldType.REGULAR;
+import static org.eclipse.ditto.base.model.json.JsonSchemaVersion.V_2;
+
+import org.eclipse.ditto.base.model.entity.type.WithEntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.signals.SignalWithEntityId;
+import org.eclipse.ditto.base.model.signals.WithResource;
+import org.eclipse.ditto.base.model.signals.commands.Command;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+
+/**
+ * Aggregates all {@link Command}s which request a stream (e.g. a {@code SourceRef}) of
+ * {@link org.eclipse.ditto.base.model.signals.Signal}s to subscribe for.
+ *
+ * @param the type of the implementing class.
+ * @since 3.2.0
+ */
+public interface StreamingSubscriptionCommand> extends Command,
+ WithEntityType, SignalWithEntityId, WithResource {
+
+ /**
+ * Resource type of streaming subscription commands.
+ */
+ String RESOURCE_TYPE = "streaming.subscription";
+
+ /**
+ * Type Prefix of Streaming commands.
+ */
+ String TYPE_PREFIX = RESOURCE_TYPE + "." + TYPE_QUALIFIER + ":";
+
+ @Override
+ default String getTypePrefix() {
+ return TYPE_PREFIX;
+ }
+
+ @Override
+ T setDittoHeaders(DittoHeaders dittoHeaders);
+
+ /**
+ * This class contains definitions for all specific fields of this command's JSON representation.
+ */
+ final class JsonFields {
+
+ private JsonFields() {
+ throw new AssertionError();
+ }
+
+ public static final JsonFieldDefinition JSON_ENTITY_ID =
+ JsonFactory.newStringFieldDefinition("entityId", REGULAR, V_2);
+
+ public static final JsonFieldDefinition JSON_ENTITY_TYPE =
+ JsonFactory.newStringFieldDefinition("entityType", REGULAR, V_2);
+
+ public static final JsonFieldDefinition JSON_RESOURCE_PATH =
+ JsonFactory.newStringFieldDefinition("resourcePath", REGULAR, V_2);
+
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/SubscribeForPersistedEvents.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/SubscribeForPersistedEvents.java
new file mode 100755
index 00000000000..aeca3a7738f
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/SubscribeForPersistedEvents.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import static org.eclipse.ditto.base.model.json.FieldType.REGULAR;
+import static org.eclipse.ditto.base.model.json.JsonSchemaVersion.V_2;
+
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableCommand;
+import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * Command which starts a stream of journal entries as persisted events for a given EntityId.
+ * Corresponds to the reactive-streams signal {@code Publisher#subscribe(Subscriber)}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableCommand(typePrefix = StreamingSubscriptionCommand.TYPE_PREFIX, name = SubscribeForPersistedEvents.NAME)
+public final class SubscribeForPersistedEvents extends AbstractStreamingSubscriptionCommand
+ implements StreamingSubscriptionCommand {
+
+ /**
+ * The name of this streaming subscription command.
+ */
+ public static final String NAME = "subscribeForPersistedEvents";
+
+ /**
+ * Type of this command.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private final long fromHistoricalRevision;
+ private final long toHistoricalRevision;
+
+ @Nullable private final Instant fromHistoricalTimestamp;
+ @Nullable private final Instant toHistoricalTimestamp;
+ @Nullable private final String prefix;
+
+ private SubscribeForPersistedEvents(final EntityId entityId,
+ final JsonPointer resourcePath,
+ final long fromHistoricalRevision,
+ final long toHistoricalRevision,
+ @Nullable final Instant fromHistoricalTimestamp,
+ @Nullable final Instant toHistoricalTimestamp,
+ @Nullable final String prefix,
+ final DittoHeaders dittoHeaders) {
+
+ super(TYPE, entityId, resourcePath, dittoHeaders);
+ this.fromHistoricalRevision = fromHistoricalRevision;
+ this.toHistoricalRevision = toHistoricalRevision;
+ this.fromHistoricalTimestamp = fromHistoricalTimestamp;
+ this.toHistoricalTimestamp = toHistoricalTimestamp;
+ this.prefix = prefix;
+ }
+
+ /**
+ * Creates a new {@code SudoStreamSnapshots} command based on "from" and "to" {@code long} revisions.
+ *
+ * @param entityId the entityId that should be streamed.
+ * @param resourcePath the resource path for which to stream events.
+ * @param fromHistoricalRevision the revision to start the streaming from.
+ * @param toHistoricalRevision the revision to stop the streaming at.
+ * @param dittoHeaders the command headers of the request.
+ * @return the command.
+ * @throws NullPointerException if any non-nullable argument is {@code null}.
+ */
+ public static SubscribeForPersistedEvents of(final EntityId entityId,
+ final JsonPointer resourcePath,
+ final long fromHistoricalRevision,
+ final long toHistoricalRevision,
+ final DittoHeaders dittoHeaders) {
+
+ return new SubscribeForPersistedEvents(entityId,
+ resourcePath,
+ fromHistoricalRevision,
+ toHistoricalRevision,
+ null,
+ null,
+ null,
+ dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code SudoStreamSnapshots} command based on "from" and "to" {@code Instant} timestamps.
+ *
+ * @param entityId the entityId that should be streamed.
+ * @param resourcePath the resource path for which to stream events.
+ * @param fromHistoricalTimestamp the timestamp to start the streaming from.
+ * @param toHistoricalTimestamp the timestamp to stop the streaming at.
+ * @param dittoHeaders the command headers of the request.
+ * @return the command.
+ * @throws NullPointerException if any non-nullable argument is {@code null}.
+ */
+ public static SubscribeForPersistedEvents of(final EntityId entityId,
+ final JsonPointer resourcePath,
+ @Nullable final Instant fromHistoricalTimestamp,
+ @Nullable final Instant toHistoricalTimestamp,
+ final DittoHeaders dittoHeaders) {
+
+ return new SubscribeForPersistedEvents(entityId,
+ resourcePath,
+ 0L,
+ Long.MAX_VALUE,
+ fromHistoricalTimestamp,
+ toHistoricalTimestamp,
+ null,
+ dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code SudoStreamSnapshots} command based on "from" and "to" {@code Instant} timestamps.
+ *
+ * @param entityId the entityId that should be streamed.
+ * @param resourcePath the resource path for which to stream events.
+ * @param fromHistoricalRevision the revision to start the streaming from.
+ * @param toHistoricalRevision the revision to stop the streaming at.
+ * @param fromHistoricalTimestamp the timestamp to start the streaming from.
+ * @param toHistoricalTimestamp the timestamp to stop the streaming at.
+ * @param dittoHeaders the command headers of the request.
+ * @return the command.
+ * @throws NullPointerException if any non-nullable argument is {@code null}.
+ */
+ public static SubscribeForPersistedEvents of(final EntityId entityId,
+ final JsonPointer resourcePath,
+ @Nullable final Long fromHistoricalRevision,
+ @Nullable final Long toHistoricalRevision,
+ @Nullable final Instant fromHistoricalTimestamp,
+ @Nullable final Instant toHistoricalTimestamp,
+ final DittoHeaders dittoHeaders) {
+
+ return new SubscribeForPersistedEvents(entityId,
+ resourcePath,
+ null != fromHistoricalRevision ? fromHistoricalRevision : 0L,
+ null != toHistoricalRevision ? toHistoricalRevision : Long.MAX_VALUE,
+ fromHistoricalTimestamp,
+ toHistoricalTimestamp,
+ null,
+ dittoHeaders);
+ }
+
+ /**
+ * Deserializes a {@code SubscribeForPersistedEvents} from the specified {@link JsonObject} argument.
+ *
+ * @param jsonObject the JSON object to be deserialized.
+ * @return the deserialized {@code SubscribeForPersistedEvents}.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if {@code jsonObject} did not contain all required
+ * fields.
+ * @throws org.eclipse.ditto.json.JsonParseException if {@code jsonObject} was not in the expected format.
+ */
+ public static SubscribeForPersistedEvents fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new SubscribeForPersistedEvents(deserializeEntityId(jsonObject),
+ JsonPointer.of(jsonObject.getValueOrThrow(StreamingSubscriptionCommand.JsonFields.JSON_RESOURCE_PATH)),
+ jsonObject.getValueOrThrow(JsonFields.JSON_FROM_HISTORICAL_REVISION),
+ jsonObject.getValueOrThrow(JsonFields.JSON_TO_HISTORICAL_REVISION),
+ jsonObject.getValue(JsonFields.JSON_FROM_HISTORICAL_TIMESTAMP).map(Instant::parse).orElse(null),
+ jsonObject.getValue(JsonFields.JSON_TO_HISTORICAL_TIMESTAMP).map(Instant::parse).orElse(null),
+ jsonObject.getValue(JsonFields.PREFIX).orElse(null),
+ dittoHeaders
+ );
+ }
+
+ /**
+ * Create a copy of this command with prefix set. The prefix is used to identify a streaming subscription manager
+ * if multiple are deployed in the cluster.
+ *
+ * @param prefix the subscription ID prefix.
+ * @return the new command.
+ */
+ public SubscribeForPersistedEvents setPrefix(@Nullable final String prefix) {
+ return new SubscribeForPersistedEvents(entityId, resourcePath, fromHistoricalRevision, toHistoricalRevision,
+ fromHistoricalTimestamp, toHistoricalTimestamp, prefix, getDittoHeaders());
+ }
+
+ /**
+ * Returns the revision to start the streaming from.
+ *
+ * @return the revision to start the streaming from.
+ */
+ public long getFromHistoricalRevision() {
+ return fromHistoricalRevision;
+ }
+
+ /**
+ * Returns the timestamp to stop the streaming at.
+ *
+ * @return the timestamp to stop the streaming at.
+ */
+ public long getToHistoricalRevision() {
+ return toHistoricalRevision;
+ }
+
+ /**
+ * Returns the optional timestamp to start the streaming from.
+ *
+ * @return the optional timestamp to start the streaming from.
+ */
+ public Optional getFromHistoricalTimestamp() {
+ return Optional.ofNullable(fromHistoricalTimestamp);
+ }
+
+ /**
+ * Returns the optional timestamp to stop the streaming at.
+ *
+ * @return the optional timestamp to stop the streaming at.
+ */
+ public Optional getToHistoricalTimestamp() {
+ return Optional.ofNullable(toHistoricalTimestamp);
+ }
+
+ /**
+ * Get the prefix of subscription IDs. The prefix is used to identify a streaming subscription manager if multiple
+ * are deployed in the cluster.
+ *
+ * @return the subscription ID prefix.
+ */
+ public Optional getPrefix() {
+ return Optional.ofNullable(prefix);
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder,
+ final JsonSchemaVersion schemaVersion,
+ final Predicate thePredicate) {
+
+ super.appendPayload(jsonObjectBuilder, schemaVersion, thePredicate);
+
+ final Predicate predicate = schemaVersion.and(thePredicate);
+ jsonObjectBuilder.set(JsonFields.JSON_FROM_HISTORICAL_REVISION, fromHistoricalRevision, predicate);
+ jsonObjectBuilder.set(JsonFields.JSON_TO_HISTORICAL_REVISION, toHistoricalRevision, predicate);
+ jsonObjectBuilder.set(JsonFields.JSON_TO_HISTORICAL_REVISION, toHistoricalRevision, predicate);
+ if (null != fromHistoricalTimestamp) {
+ jsonObjectBuilder.set(JsonFields.JSON_FROM_HISTORICAL_TIMESTAMP, fromHistoricalTimestamp.toString(),
+ predicate);
+ }
+ if (null != toHistoricalTimestamp) {
+ jsonObjectBuilder.set(JsonFields.JSON_TO_HISTORICAL_TIMESTAMP, toHistoricalTimestamp.toString(), predicate);
+ }
+ getPrefix().ifPresent(thePrefix -> jsonObjectBuilder.set(JsonFields.PREFIX, thePrefix));
+ }
+
+ @Override
+ public String getTypePrefix() {
+ return TYPE_PREFIX;
+ }
+
+ @Override
+ public SubscribeForPersistedEvents setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new SubscribeForPersistedEvents(entityId, resourcePath, fromHistoricalRevision, toHistoricalRevision,
+ fromHistoricalTimestamp, toHistoricalTimestamp, prefix, dittoHeaders);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), entityId, resourcePath, fromHistoricalRevision, toHistoricalRevision,
+ fromHistoricalTimestamp, toHistoricalTimestamp, prefix);
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final SubscribeForPersistedEvents that = (SubscribeForPersistedEvents) obj;
+
+ return that.canEqual(this) && super.equals(that) &&
+ fromHistoricalRevision == that.fromHistoricalRevision &&
+ toHistoricalRevision == that.toHistoricalRevision &&
+ Objects.equals(fromHistoricalTimestamp, that.fromHistoricalTimestamp) &&
+ Objects.equals(toHistoricalTimestamp, that.toHistoricalTimestamp) &&
+ Objects.equals(prefix, that.prefix);
+ }
+
+ @Override
+ protected boolean canEqual(@Nullable final Object other) {
+ return other instanceof SubscribeForPersistedEvents;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + super.toString()
+ + ", fromHistoricalRevision=" + fromHistoricalRevision
+ + ", toHistoricalRevision=" + toHistoricalRevision
+ + ", fromHistoricalTimestamp=" + fromHistoricalTimestamp
+ + ", toHistoricalTimestamp=" + toHistoricalTimestamp
+ + ", prefix=" + prefix
+ + "]";
+ }
+
+ /**
+ * This class contains definitions for all specific fields of this command's JSON representation.
+ */
+ public static final class JsonFields {
+
+ private JsonFields() {
+ throw new AssertionError();
+ }
+
+ public static final JsonFieldDefinition JSON_FROM_HISTORICAL_REVISION =
+ JsonFactory.newLongFieldDefinition("fromHistoricalRevision", REGULAR, V_2);
+
+ public static final JsonFieldDefinition JSON_TO_HISTORICAL_REVISION =
+ JsonFactory.newLongFieldDefinition("toHistoricalRevision", REGULAR, V_2);
+
+ public static final JsonFieldDefinition JSON_FROM_HISTORICAL_TIMESTAMP =
+ JsonFactory.newStringFieldDefinition("fromHistoricalTimestamp", REGULAR, V_2);
+
+ public static final JsonFieldDefinition JSON_TO_HISTORICAL_TIMESTAMP =
+ JsonFactory.newStringFieldDefinition("toHistoricalTimestamp", REGULAR, V_2);
+
+ static final JsonFieldDefinition PREFIX =
+ JsonFactory.newStringFieldDefinition("prefix", REGULAR, V_2);
+ }
+
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/package-info.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/package-info.java
new file mode 100755
index 00000000000..565442a816b
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/commands/streaming/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+@org.eclipse.ditto.utils.jsr305.annotations.AllValuesAreNonnullByDefault
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEvent.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEvent.java
index 3f19feec46a..815c280930a 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEvent.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEvent.java
@@ -98,6 +98,7 @@ public String getManifest() {
@Override
public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate thePredicate) {
final Predicate predicate = schemaVersion.and(thePredicate);
+
final JsonObjectBuilder jsonObjectBuilder = JsonFactory.newObjectBuilder()
// TYPE is included unconditionally:
.set(JsonFields.TYPE, type)
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEventsourcedEvent.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEventsourcedEvent.java
index af20d1b37e7..0276fa4d5e0 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEventsourcedEvent.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/AbstractEventsourcedEvent.java
@@ -96,6 +96,7 @@ public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate<
// it shall not invoke super.toJson(...) because in that case "appendPayloadAndBuild" would be invoked twice
// and the order of the fields to appear in the JSON would not be controllable!
final Predicate predicate = schemaVersion.and(thePredicate);
+
final JsonObjectBuilder jsonObjectBuilder = JsonFactory.newObjectBuilder()
// TYPE + entityId is included unconditionally:
.set(Event.JsonFields.TYPE, getType())
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/EventJsonDeserializer.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/EventJsonDeserializer.java
index 782ea697881..e8704a716b9 100755
--- a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/EventJsonDeserializer.java
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/EventJsonDeserializer.java
@@ -22,13 +22,13 @@
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
+import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.exceptions.DittoJsonException;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonMissingFieldException;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonParseException;
import org.eclipse.ditto.json.JsonValue;
-import org.eclipse.ditto.base.model.entity.metadata.Metadata;
-import org.eclipse.ditto.base.model.exceptions.DittoJsonException;
/**
* This class helps to deserialize JSON to a sub-class of {@link Event}. Hereby this class extracts the values which
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/AbstractStreamingSubscriptionEvent.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/AbstractStreamingSubscriptionEvent.java
new file mode 100755
index 00000000000..ce0089bc51d
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/AbstractStreamingSubscriptionEvent.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
+
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.entity.id.EntityIdJsonDeserializer;
+import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.entity.type.EntityTypeJsonDeserializer;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.events.Event;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+
+/**
+ * Abstract base class of subscription events. Package-private. Not to be extended in user code.
+ *
+ * @param the type of the implementing class.
+ * @since 3.2.0
+ */
+@Immutable
+abstract class AbstractStreamingSubscriptionEvent> implements
+ StreamingSubscriptionEvent {
+
+ private final String type;
+ private final String subscriptionId;
+
+ private final EntityId entityId;
+ private final DittoHeaders dittoHeaders;
+
+ /**
+ * Constructs a new {@code AbstractStreamingSubscriptionEvent} object.
+ *
+ * @param type the type of this event.
+ * @param subscriptionId the subscription ID.
+ * @param entityId the entity ID of this streaming subscription event.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @throws NullPointerException if any argument but {@code timestamp} is {@code null}.
+ */
+ protected AbstractStreamingSubscriptionEvent(final String type,
+ final String subscriptionId,
+ final EntityId entityId,
+ final DittoHeaders dittoHeaders) {
+
+ this.type = checkNotNull(type, "type");
+ this.subscriptionId = checkNotNull(subscriptionId, "subscriptionId");
+ this.entityId = checkNotNull(entityId, "entityId");
+ this.dittoHeaders = checkNotNull(dittoHeaders, "dittoHeaders");
+ }
+
+ @Override
+ public String getSubscriptionId() {
+ return subscriptionId;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public EntityId getEntityId() {
+ return entityId;
+ }
+
+ @Override
+ public EntityType getEntityType() {
+ return entityId.getEntityType();
+ }
+
+ @Override
+ public Optional getTimestamp() {
+ // subscription events have no timestamp.
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getMetadata() {
+ return Optional.empty();
+ }
+
+ @Override
+ public DittoHeaders getDittoHeaders() {
+ return dittoHeaders;
+ }
+
+ @Nonnull
+ @Override
+ public String getManifest() {
+ return getType();
+ }
+
+ @Override
+ public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate thePredicate) {
+ final JsonObjectBuilder jsonObjectBuilder = JsonFactory.newObjectBuilder()
+ // TYPE is included unconditionally
+ .set(Event.JsonFields.TYPE, type)
+ .set(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID, subscriptionId)
+ .set(StreamingSubscriptionEvent.JsonFields.JSON_ENTITY_ID, entityId.toString())
+ .set(StreamingSubscriptionEvent.JsonFields.JSON_ENTITY_TYPE, entityId.getEntityType().toString());
+
+ appendPayload(jsonObjectBuilder);
+
+ return jsonObjectBuilder.build();
+ }
+
+ protected static EntityId deserializeEntityId(final JsonObject jsonObject) {
+ return EntityIdJsonDeserializer.deserializeEntityId(jsonObject,
+ StreamingSubscriptionEvent.JsonFields.JSON_ENTITY_ID,
+ EntityTypeJsonDeserializer.deserializeEntityType(jsonObject,
+ StreamingSubscriptionEvent.JsonFields.JSON_ENTITY_TYPE));
+ }
+
+ /**
+ * Appends the event specific custom payload to the passed {@code jsonObjectBuilder}.
+ *
+ * @param jsonObjectBuilder the JsonObjectBuilder to add the custom payload to.
+ */
+ protected abstract void appendPayload(final JsonObjectBuilder jsonObjectBuilder);
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (o != null && getClass() == o.getClass()) {
+ final AbstractStreamingSubscriptionEvent> that = (AbstractStreamingSubscriptionEvent>) o;
+ return Objects.equals(type, that.type) &&
+ Objects.equals(subscriptionId, that.subscriptionId) &&
+ Objects.equals(entityId, that.entityId) &&
+ Objects.equals(dittoHeaders, that.dittoHeaders);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, subscriptionId, entityId, dittoHeaders);
+ }
+
+ @Override
+ public String toString() {
+ return "type=" + type +
+ ", subscriptionId=" + subscriptionId +
+ ", entityId=" + entityId +
+ ", dittoHeaders=" + dittoHeaders;
+ }
+
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionComplete.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionComplete.java
new file mode 100755
index 00000000000..e6bd039cddd
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionComplete.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableEvent;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.events.EventJsonDeserializer;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * This event is emitted after all items of a subscription are sent.
+ * Corresponds to the reactive-streams signal {@code Subscriber#onComplete()}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableEvent(name = StreamingSubscriptionComplete.NAME, typePrefix = StreamingSubscriptionEvent.TYPE_PREFIX)
+public final class StreamingSubscriptionComplete
+ extends AbstractStreamingSubscriptionEvent {
+
+ /**
+ * Name of the event.
+ */
+ public static final String NAME = "complete";
+
+ /**
+ * Type of this event.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private StreamingSubscriptionComplete(final String subscriptionId, final EntityId entityId,
+ final DittoHeaders dittoHeaders) {
+ super(TYPE, subscriptionId, entityId, dittoHeaders);
+ }
+
+ /**
+ * Constructs a new {@code StreamingSubscriptionComplete} object.
+ *
+ * @param subscriptionId the subscription ID.
+ * @param entityId the entity ID of this streaming subscription event.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the StreamingSubscriptionComplete created.
+ * @throws NullPointerException if either argument is null.
+ */
+ public static StreamingSubscriptionComplete of(final String subscriptionId, final EntityId entityId,
+ final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionComplete(subscriptionId, entityId, dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code StreamingSubscriptionComplete} from a JSON object.
+ *
+ * @param jsonObject the JSON object from which a new StreamingSubscriptionComplete instance is to be created.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the {@code StreamingSubscriptionComplete} which was created from the given JSON object.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionComplete fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new EventJsonDeserializer(TYPE, jsonObject)
+ .deserialize((revision, timestamp, metadata) -> {
+ final String subscriptionId = jsonObject
+ .getValueOrThrow(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID);
+ final EntityId entityId = deserializeEntityId(jsonObject);
+ return new StreamingSubscriptionComplete(subscriptionId, entityId, dittoHeaders);
+ });
+ }
+
+ @Override
+ public JsonPointer getResourcePath() {
+ return JsonPointer.empty();
+ }
+
+ @Override
+ public StreamingSubscriptionComplete setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionComplete(getSubscriptionId(), getEntityId(), dittoHeaders);
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder) {
+ // nothing to add
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + super.toString() + "]";
+ }
+
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCreated.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCreated.java
new file mode 100755
index 00000000000..6c6ffc518e2
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCreated.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableEvent;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.events.EventJsonDeserializer;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * This event is emitted after a stream is established for items to be streamed in the back-end.
+ * Corresponds to the reactive-streams signal {@code Subscriber#onSubscribe(Subscription)}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableEvent(name = StreamingSubscriptionCreated.NAME, typePrefix = StreamingSubscriptionEvent.TYPE_PREFIX)
+public final class StreamingSubscriptionCreated
+ extends AbstractStreamingSubscriptionEvent {
+
+ /**
+ * Name of the event.
+ */
+ public static final String NAME = "created";
+
+ /**
+ * Type of this event.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private StreamingSubscriptionCreated(final String subscriptionId,
+ final EntityId entityId,
+ final DittoHeaders dittoHeaders) {
+ super(TYPE, subscriptionId, entityId, dittoHeaders);
+ }
+
+ /**
+ * Constructs a new {@code StreamingSubscriptionCreated} event.
+ *
+ * @param subscriptionId the subscription ID.
+ * @param entityId the entity ID of this streaming subscription event.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the event.
+ * @throws NullPointerException if either argument is null.
+ */
+ public static StreamingSubscriptionCreated of(final String subscriptionId,
+ final EntityId entityId,
+ final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionCreated(subscriptionId, entityId, dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code StreamingSubscriptionCreated} from a JSON object.
+ *
+ * @param jsonObject the JSON object from which a new StreamingSubscriptionCreated instance is to be created.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the {@code StreamingSubscriptionCreated} which was created from the given JSON object.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionCreated fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new EventJsonDeserializer(TYPE, jsonObject)
+ .deserialize((revision, timestamp, metadata) -> {
+ final String subscriptionId = jsonObject
+ .getValueOrThrow(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID);
+ final EntityId entityId = deserializeEntityId(jsonObject);
+ return new StreamingSubscriptionCreated(subscriptionId, entityId, dittoHeaders);
+ });
+ }
+
+ @Override
+ public JsonPointer getResourcePath() {
+ return JsonPointer.empty();
+ }
+
+ @Override
+ public StreamingSubscriptionCreated setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionCreated(getSubscriptionId(), getEntityId(), dittoHeaders);
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder) {
+ // nothing to add
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + super.toString() + "]";
+ }
+
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionEvent.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionEvent.java
new file mode 100755
index 00000000000..a009d0fded2
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionEvent.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import static org.eclipse.ditto.base.model.json.FieldType.REGULAR;
+import static org.eclipse.ditto.base.model.json.JsonSchemaVersion.V_2;
+
+import org.eclipse.ditto.base.model.entity.id.WithEntityId;
+import org.eclipse.ditto.base.model.entity.type.WithEntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.signals.events.Event;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+
+/**
+ * Interface for all outgoing messages related to a subscription for streaming something.
+ *
+ * @param the type of the implementing class.
+ * @since 3.2.0
+ */
+public interface StreamingSubscriptionEvent> extends Event,
+ WithEntityId, WithEntityType {
+
+ /**
+ * Resource type of streaming subscription events.
+ */
+ String RESOURCE_TYPE = "streaming.subscription";
+
+ /**
+ * Type Prefix of Streaming events.
+ */
+ String TYPE_PREFIX = RESOURCE_TYPE + "." + TYPE_QUALIFIER + ":";
+
+ /**
+ * Returns the subscriptionId identifying the session of this streaming signal.
+ *
+ * @return the subscriptionId.
+ */
+ String getSubscriptionId();
+
+ @Override
+ T setDittoHeaders(DittoHeaders dittoHeaders);
+
+ @Override
+ default String getResourceType() {
+ return RESOURCE_TYPE;
+ }
+
+ /**
+ * This class contains definitions for all specific fields of this event's JSON representation.
+ */
+ final class JsonFields {
+
+ private JsonFields() {
+ throw new AssertionError();
+ }
+
+ public static final JsonFieldDefinition JSON_ENTITY_ID =
+ JsonFactory.newStringFieldDefinition("entityId", REGULAR, V_2);
+
+ public static final JsonFieldDefinition JSON_ENTITY_TYPE =
+ JsonFactory.newStringFieldDefinition("entityType", REGULAR, V_2);
+
+ }
+
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionFailed.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionFailed.java
new file mode 100755
index 00000000000..4e4f878e34d
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionFailed.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import java.util.Objects;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableEvent;
+import org.eclipse.ditto.base.model.signals.GlobalErrorRegistry;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.events.EventJsonDeserializer;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+
+/**
+ * This event is emitted after a stream failed.
+ * Corresponds to the reactive-streams signal {@code Subscriber#onError()}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableEvent(name = StreamingSubscriptionFailed.NAME, typePrefix = StreamingSubscriptionEvent.TYPE_PREFIX)
+public final class StreamingSubscriptionFailed extends AbstractStreamingSubscriptionEvent {
+
+ /**
+ * Name of the event.
+ */
+ public static final String NAME = "failed";
+
+ /**
+ * Type of this event.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private final DittoRuntimeException error;
+
+ private StreamingSubscriptionFailed(final String subscriptionId,
+ final EntityId entityId,
+ final DittoRuntimeException error,
+ final DittoHeaders dittoHeaders) {
+ super(TYPE, subscriptionId, entityId, dittoHeaders);
+ this.error = error;
+ }
+
+ /**
+ * Constructs a new {@code StreamingSubscriptionFailed} object.
+ *
+ * @param subscriptionId the subscription ID.
+ * @param entityId the entity ID of this streaming subscription event.
+ * @param error the cause of the failure.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the StreamingSubscriptionFailed created.
+ * @throws NullPointerException if either argument is null.
+ */
+ public static StreamingSubscriptionFailed of(final String subscriptionId,
+ final EntityId entityId,
+ final DittoRuntimeException error,
+ final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionFailed(subscriptionId, entityId, error, dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code StreamingSubscriptionFailed} from a JSON object.
+ *
+ * @param jsonObject the JSON object from which a new StreamingSubscriptionFailed instance is to be created.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the {@code StreamingSubscriptionFailed} which was created from the given JSON object.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionFailed fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new EventJsonDeserializer(TYPE, jsonObject)
+ .deserialize((revision, timestamp, metadata) -> {
+ final String subscriptionId =
+ jsonObject.getValueOrThrow(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID);
+ final JsonObject errorJson = jsonObject.getValueOrThrow(JsonFields.ERROR);
+ final EntityId entityId = deserializeEntityId(jsonObject);
+ final DittoRuntimeException error =
+ GlobalErrorRegistry.getInstance().parse(errorJson, dittoHeaders);
+ return new StreamingSubscriptionFailed(subscriptionId, entityId, error, dittoHeaders);
+ });
+ }
+
+ /**
+ * Get the cause of the failure.
+ *
+ * @return the error in JSON format.
+ */
+ public DittoRuntimeException getError() {
+ return error;
+ }
+
+ /**
+ * Create a copy of this event with a new error.
+ *
+ * @param error the new error.
+ * @return the copied event with new error.
+ */
+ public StreamingSubscriptionFailed setError(final DittoRuntimeException error) {
+ return new StreamingSubscriptionFailed(getSubscriptionId(), getEntityId(), error, getDittoHeaders());
+ }
+
+ @Override
+ public JsonPointer getResourcePath() {
+ return JsonPointer.empty();
+ }
+
+ @Override
+ public StreamingSubscriptionFailed setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionFailed(getSubscriptionId(), getEntityId(), error, dittoHeaders);
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder) {
+ jsonObjectBuilder.set(JsonFields.ERROR, error.toJson());
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ return super.equals(o) && Objects.equals(error, ((StreamingSubscriptionFailed) o).error);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), error);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + super.toString() + ", error=" + error + "]";
+ }
+
+ /**
+ * Json fields of this event.
+ */
+ public static final class JsonFields {
+
+ /**
+ * Json fields for a JSON representation of the error.
+ */
+ public static final JsonFieldDefinition ERROR =
+ JsonFactory.newJsonObjectFieldDefinition("error");
+
+ JsonFields() {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionHasNext.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionHasNext.java
new file mode 100755
index 00000000000..c8539110fb9
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionHasNext.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import java.util.Objects;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableEvent;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
+import org.eclipse.ditto.base.model.signals.events.EventJsonDeserializer;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
+import org.eclipse.ditto.json.JsonPointer;
+import org.eclipse.ditto.json.JsonValue;
+
+/**
+ * This event is emitted after the next items to stream are ready.
+ * Corresponds to the reactive-streams signal {@code Subscriber#onNext(T)}.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableEvent(name = StreamingSubscriptionHasNext.NAME, typePrefix = StreamingSubscriptionEvent.TYPE_PREFIX)
+public final class StreamingSubscriptionHasNext
+ extends AbstractStreamingSubscriptionEvent {
+
+ /**
+ * Name of the event.
+ */
+ public static final String NAME = "next";
+
+ /**
+ * Type of this event.
+ */
+ public static final String TYPE = TYPE_PREFIX + NAME;
+
+ private final JsonValue item;
+
+ private StreamingSubscriptionHasNext(final String subscriptionId,
+ final EntityId entityId,
+ final JsonValue item,
+ final DittoHeaders dittoHeaders) {
+ super(TYPE, subscriptionId, entityId, dittoHeaders);
+ this.item = item;
+ }
+
+ /**
+ * Constructs a new {@code SubscriptionHasNext} object.
+ *
+ * @param subscriptionId the subscription ID.
+ * @param entityId the entity ID of this streaming subscription event.
+ * @param item the "next" item.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the SubscriptionHasNext created.
+ * @throws NullPointerException if either argument is null.
+ */
+ public static StreamingSubscriptionHasNext of(final String subscriptionId,
+ final EntityId entityId,
+ final JsonValue item,
+ final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionHasNext(subscriptionId, entityId, item, dittoHeaders);
+ }
+
+ /**
+ * Creates a new {@code SubscriptionHasNext} from a JSON object.
+ *
+ * @param jsonObject the JSON object from which a new SubscriptionHasNext instance is to be created.
+ * @param dittoHeaders the headers of the command which was the cause of this event.
+ * @return the {@code SubscriptionHasNext} which was created from the given JSON object.
+ * @throws NullPointerException if {@code jsonObject} is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static StreamingSubscriptionHasNext fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return new EventJsonDeserializer(TYPE, jsonObject)
+ .deserialize((revision, timestamp, metadata) -> {
+ final String subscriptionId =
+ jsonObject.getValueOrThrow(WithStreamingSubscriptionId.JsonFields.SUBSCRIPTION_ID);
+ final EntityId entityId = deserializeEntityId(jsonObject);
+ final JsonValue item = jsonObject.getValueOrThrow(JsonFields.ITEM);
+ return new StreamingSubscriptionHasNext(subscriptionId, entityId, item, dittoHeaders);
+ });
+ }
+
+ /**
+ * Get the "next" item.
+ *
+ * @return the next item.
+ */
+ public JsonValue getItem() {
+ return item;
+ }
+
+ /**
+ * Create a copy of this event with a new item.
+ *
+ * @param item the new item.
+ * @return the copied event with new item.
+ */
+ public StreamingSubscriptionHasNext setItem(final JsonValue item) {
+ return new StreamingSubscriptionHasNext(getSubscriptionId(), getEntityId(), item, getDittoHeaders());
+ }
+
+ @Override
+ public JsonPointer getResourcePath() {
+ return JsonPointer.empty();
+ }
+
+ @Override
+ public StreamingSubscriptionHasNext setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new StreamingSubscriptionHasNext(getSubscriptionId(), getEntityId(), item, dittoHeaders);
+ }
+
+ @Override
+ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder) {
+ jsonObjectBuilder.set(JsonFields.ITEM, item);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ // super.equals(o) guarantees getClass() == o.getClass()
+ return super.equals(o) && Objects.equals(item, ((StreamingSubscriptionHasNext) o).item);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), item);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + super.toString() + ", item=" + item + "]";
+ }
+
+ /**
+ * Json fields of this event.
+ */
+ public static final class JsonFields {
+
+ /**
+ * Json field for "next" item.
+ */
+ public static final JsonFieldDefinition ITEM =
+ JsonFactory.newJsonValueFieldDefinition("item");
+
+ JsonFields() {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/package-info.java b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/package-info.java
new file mode 100755
index 00000000000..7a13cf46a93
--- /dev/null
+++ b/base/model/src/main/java/org/eclipse/ditto/base/model/signals/events/streaming/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+@org.eclipse.ditto.utils.jsr305.annotations.AllValuesAreNonnullByDefault
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/common/LikeHelperTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/common/LikeHelperTest.java
index 41d134522dc..be15e71c7f9 100644
--- a/base/model/src/test/java/org/eclipse/ditto/base/model/common/LikeHelperTest.java
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/common/LikeHelperTest.java
@@ -21,6 +21,7 @@ public final class LikeHelperTest {
@Test
public void testWildcards() {
+ //case sensitive test cases
assertExpression("", "*", true);
assertExpression("foo", "*", true);
assertExpression("foo.bar", "foo.bar", true);
@@ -35,4 +36,23 @@ private static void assertExpression(final String value, final String expression
Pattern p = Pattern.compile(LikeHelper.convertToRegexSyntax(expression));
Assert.assertEquals(matches, p.matcher(value).matches());
}
+
+ @Test
+ public void testCaseInsensitiveWildcards() {
+ //case insensitive test cases
+ assertExpressionCaseInsensitive("", "*", true);
+ assertExpressionCaseInsensitive("foo", "*", true);
+ assertExpressionCaseInsensitive("foo.bar", "FOO.BAR", true);
+ assertExpressionCaseInsensitive("foo..bar", "foo.bar", false);
+ assertExpressionCaseInsensitive("foo..bar", "FOO*", true);
+ assertExpressionCaseInsensitive("foo..bar", "*Bar", true);
+ assertExpressionCaseInsensitive("foo.bar.baz", "bar", false);
+ assertExpressionCaseInsensitive("foo.bar.baz", "*bAr*", true);
+ }
+
+ private static void assertExpressionCaseInsensitive(final String value, final String expression, final boolean matches) {
+ Pattern p = Pattern.compile(LikeHelper.convertToRegexSyntax(expression), Pattern.CASE_INSENSITIVE);
+ Assert.assertEquals(matches, p.matcher(value).matches());
+ }
+
}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/headers/ImmutableDittoHeadersTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/headers/ImmutableDittoHeadersTest.java
index 5c53e597a8c..474b7ca73da 100755
--- a/base/model/src/test/java/org/eclipse/ditto/base/model/headers/ImmutableDittoHeadersTest.java
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/headers/ImmutableDittoHeadersTest.java
@@ -19,11 +19,13 @@
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
import java.time.Duration;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -77,6 +79,7 @@ public final class ImmutableDittoHeadersTest {
EntityTagMatchers.fromCommaSeparatedString("\"oneValue\",\"anotherValue\"");
private static final EntityTagMatchers KNOWN_IF_NONE_MATCH =
EntityTagMatchers.fromCommaSeparatedString("\"notOneValue\",\"notAnotherValue\"");
+ private static final IfEqual KNOWN_IF_EQUAL_OPTION = IfEqual.SKIP;
private static final EntityTag KNOWN_ETAG = EntityTag.fromString("\"-12124212\"");
private static final Collection KNOWN_READ_GRANTED_SUBJECTS =
Lists.list(AuthorizationModelFactory.newAuthSubject("knownGrantedSubject1"),
@@ -129,6 +132,12 @@ public final class ImmutableDittoHeadersTest {
.build())
.build())
.build();
+ private static final Long KNOWN_AT_HISTORICAL_REVISION = 42L;
+ private static final Instant KNOWN_AT_HISTORICAL_TIMESTAMP = Instant.now();
+
+ private static final JsonObject KNOWN_HISTORICAL_HEADERS = JsonObject.newBuilder()
+ .set(DittoHeaderDefinition.ORIGINATOR.getKey(), "foo:bar")
+ .build();
static {
@@ -164,6 +173,7 @@ public void settingAllKnownHeadersWorksAsExpected() {
.eTag(KNOWN_ETAG)
.ifMatch(KNOWN_IF_MATCH)
.ifNoneMatch(KNOWN_IF_NONE_MATCH)
+ .ifEqual(KNOWN_IF_EQUAL_OPTION)
.origin(KNOWN_ORIGIN)
.contentType(KNOWN_CONTENT_TYPE)
.replyTarget(Integer.valueOf(KNOWN_REPLY_TARGET))
@@ -199,6 +209,9 @@ public void settingAllKnownHeadersWorksAsExpected() {
.putHeader(DittoHeaderDefinition.GET_METADATA.getKey(), KNOWN_DITTO_GET_METADATA )
.putHeader(DittoHeaderDefinition.DELETE_METADATA.getKey(), KNOWN_DITTO_DELETE_METADATA )
.putHeader(DittoHeaderDefinition.DITTO_METADATA.getKey(), KNOWN_DITTO_METADATA.formatAsString())
+ .putHeader(DittoHeaderDefinition.AT_HISTORICAL_REVISION.getKey(), String.valueOf(KNOWN_AT_HISTORICAL_REVISION))
+ .putHeader(DittoHeaderDefinition.AT_HISTORICAL_TIMESTAMP.getKey(), String.valueOf(KNOWN_AT_HISTORICAL_TIMESTAMP))
+ .putHeader(DittoHeaderDefinition.HISTORICAL_HEADERS.getKey(), KNOWN_HISTORICAL_HEADERS.formatAsString())
.build();
assertThat(underTest).isEqualTo(expectedHeaderMap);
@@ -491,6 +504,7 @@ public void toJsonReturnsExpected() {
authorizationSubjectsToJsonArray(KNOWN_READ_REVOKED_SUBJECTS))
.set(DittoHeaderDefinition.IF_MATCH.getKey(), KNOWN_IF_MATCH.toString())
.set(DittoHeaderDefinition.IF_NONE_MATCH.getKey(), KNOWN_IF_NONE_MATCH.toString())
+ .set(DittoHeaderDefinition.IF_EQUAL.getKey(), KNOWN_IF_EQUAL_OPTION.toString())
.set(DittoHeaderDefinition.ETAG.getKey(), KNOWN_ETAG.toString())
.set(DittoHeaderDefinition.ORIGIN.getKey(), KNOWN_ORIGIN)
.set(DittoHeaderDefinition.CONTENT_TYPE.getKey(), KNOWN_CONTENT_TYPE)
@@ -526,6 +540,9 @@ public void toJsonReturnsExpected() {
.set(DittoHeaderDefinition.GET_METADATA.getKey(), KNOWN_DITTO_GET_METADATA)
.set(DittoHeaderDefinition.DELETE_METADATA.getKey(), KNOWN_DITTO_DELETE_METADATA)
.set(DittoHeaderDefinition.DITTO_METADATA.getKey(), KNOWN_DITTO_METADATA)
+ .set(DittoHeaderDefinition.AT_HISTORICAL_REVISION.getKey(), KNOWN_AT_HISTORICAL_REVISION)
+ .set(DittoHeaderDefinition.AT_HISTORICAL_TIMESTAMP.getKey(), KNOWN_AT_HISTORICAL_TIMESTAMP.toString())
+ .set(DittoHeaderDefinition.HISTORICAL_HEADERS.getKey(), KNOWN_HISTORICAL_HEADERS)
.build();
final Map allKnownHeaders = createMapContainingAllKnownHeaders();
@@ -710,7 +727,7 @@ public void preserveCapitalizationOfCorrelationId() {
}
private static Map createMapContainingAllKnownHeaders() {
- final Map result = new HashMap<>();
+ final Map result = new LinkedHashMap<>();
result.put(DittoHeaderDefinition.AUTHORIZATION_CONTEXT.getKey(), AUTH_CONTEXT.toJsonString());
result.put(DittoHeaderDefinition.CORRELATION_ID.getKey(), KNOWN_CORRELATION_ID);
result.put(DittoHeaderDefinition.SCHEMA_VERSION.getKey(), KNOWN_SCHEMA_VERSION.toString());
@@ -725,6 +742,7 @@ private static Map createMapContainingAllKnownHeaders() {
authorizationSubjectsToJsonArray(KNOWN_READ_REVOKED_SUBJECTS).toString());
result.put(DittoHeaderDefinition.IF_MATCH.getKey(), KNOWN_IF_MATCH.toString());
result.put(DittoHeaderDefinition.IF_NONE_MATCH.getKey(), KNOWN_IF_NONE_MATCH.toString());
+ result.put(DittoHeaderDefinition.IF_EQUAL.getKey(), KNOWN_IF_EQUAL_OPTION.toString());
result.put(DittoHeaderDefinition.ETAG.getKey(), KNOWN_ETAG.toString());
result.put(DittoHeaderDefinition.CONTENT_TYPE.getKey(), KNOWN_CONTENT_TYPE);
result.put(DittoHeaderDefinition.ACCEPT.getKey(), KNOWN_ACCEPT);
@@ -762,6 +780,9 @@ private static Map createMapContainingAllKnownHeaders() {
result.put(DittoHeaderDefinition.GET_METADATA.getKey(), KNOWN_DITTO_GET_METADATA);
result.put(DittoHeaderDefinition.DELETE_METADATA.getKey(), KNOWN_DITTO_DELETE_METADATA);
result.put(DittoHeaderDefinition.DITTO_METADATA.getKey(), KNOWN_DITTO_METADATA.formatAsString());
+ result.put(DittoHeaderDefinition.AT_HISTORICAL_REVISION.getKey(), String.valueOf(KNOWN_AT_HISTORICAL_REVISION));
+ result.put(DittoHeaderDefinition.AT_HISTORICAL_TIMESTAMP.getKey(), String.valueOf(KNOWN_AT_HISTORICAL_TIMESTAMP));
+ result.put(DittoHeaderDefinition.HISTORICAL_HEADERS.getKey(), KNOWN_HISTORICAL_HEADERS.formatAsString());
return result;
}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/ShardedMessageEnvelopeTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/ShardedMessageEnvelopeTest.java
index ccc35d53b85..b1e2ad591f2 100644
--- a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/ShardedMessageEnvelopeTest.java
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/ShardedMessageEnvelopeTest.java
@@ -14,11 +14,12 @@
import static org.assertj.core.api.Assertions.assertThat;
-import org.eclipse.ditto.json.JsonFactory;
-import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
import org.eclipse.ditto.base.model.entity.type.EntityType;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonObject;
import org.junit.Test;
import nl.jqno.equalsverifier.EqualsVerifier;
@@ -31,7 +32,7 @@ public final class ShardedMessageEnvelopeTest {
private static final DittoHeaders DITTO_HEADERS = DittoHeaders.empty();
private static final EntityId MESSAGE_ID =
- EntityId.of(EntityType.of("thing"), "org.eclipse.ditto.test:thingId");
+ NamespacedEntityId.of(EntityType.of("thing"), "org.eclipse.ditto.test:thingId");
private static final String TYPE = "message-type";
private static final JsonObject MESSAGE = JsonFactory.newObjectBuilder().set("hello", "world").build();
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/CancelStreamingSubscriptionTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/CancelStreamingSubscriptionTest.java
new file mode 100755
index 00000000000..520e43134e2
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/CancelStreamingSubscriptionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.util.UUID;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.json.JsonPointer;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link CancelStreamingSubscription}.
+ */
+public final class CancelStreamingSubscriptionTest {
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(CancelStreamingSubscription.class, areImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(CancelStreamingSubscription.class)
+ .withRedefinedSuperclass()
+ .verify();
+ }
+
+ @Test
+ public void serialization() {
+ final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().randomCorrelationId().build();
+ final CancelStreamingSubscription underTest = CancelStreamingSubscription.of(
+ NamespacedEntityId.of(EntityType.of("thing"), "foo:bar"),
+ JsonPointer.of("/"),
+ UUID.randomUUID().toString(), dittoHeaders);
+ final CancelStreamingSubscription deserialized = CancelStreamingSubscription.fromJson(underTest.toJson(), dittoHeaders);
+ assertThat(deserialized).isEqualTo(underTest);
+ }
+
+}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/RequestFromStreamingSubscriptionTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/RequestFromStreamingSubscriptionTest.java
new file mode 100755
index 00000000000..1c0c8f31988
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/RequestFromStreamingSubscriptionTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.util.UUID;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.json.JsonPointer;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link RequestFromStreamingSubscription}.
+ */
+public final class RequestFromStreamingSubscriptionTest {
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(RequestFromStreamingSubscription.class, areImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(RequestFromStreamingSubscription.class)
+ .withRedefinedSuperclass()
+ .verify();
+ }
+
+ @Test
+ public void serialization() {
+ final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().randomCorrelationId().build();
+ final RequestFromStreamingSubscription
+ underTest = RequestFromStreamingSubscription.of(NamespacedEntityId.of(EntityType.of("thing"), "foo:bar"),
+ JsonPointer.of("/"), UUID.randomUUID().toString(), 9L, dittoHeaders);
+ final RequestFromStreamingSubscription deserialized = RequestFromStreamingSubscription.fromJson(underTest.toJson(), dittoHeaders);
+ assertThat(deserialized).isEqualTo(underTest);
+ }
+
+}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/SubscribeForPersistedEventsTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/SubscribeForPersistedEventsTest.java
new file mode 100755
index 00000000000..c78c8dab609
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/commands/streaming/SubscribeForPersistedEventsTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.commands.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.AllowedReason.provided;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.time.Instant;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.signals.commands.Command;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonPointer;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link SubscribeForPersistedEvents}.
+ */
+public final class SubscribeForPersistedEventsTest {
+
+ private static final String KNOWN_ENTITY_ID_STR = "foo:bar";
+ private static final String KNOWN_ENTITY_TYPE_STR = "thing";
+ private static final String KNOWN_RESOURCE_PATH = "/";
+
+ private static final long KNOWN_FROM_REV = 23L;
+ private static final long KNOWN_TO_REV = 42L;
+ private static final String KNOWN_FROM_TS = "2022-10-25T14:00:00Z";
+ private static final String KNOWN_TO_TS = "2022-10-25T15:00:00Z";
+
+ private static final String JSON_ALL_FIELDS = JsonFactory.newObjectBuilder()
+ .set(Command.JsonFields.TYPE, SubscribeForPersistedEvents.TYPE)
+ .set(StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_TYPE, KNOWN_ENTITY_TYPE_STR)
+ .set(StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_ID, KNOWN_ENTITY_ID_STR)
+ .set(StreamingSubscriptionCommand.JsonFields.JSON_RESOURCE_PATH, KNOWN_RESOURCE_PATH)
+ .set(SubscribeForPersistedEvents.JsonFields.JSON_FROM_HISTORICAL_REVISION, KNOWN_FROM_REV)
+ .set(SubscribeForPersistedEvents.JsonFields.JSON_TO_HISTORICAL_REVISION, KNOWN_TO_REV)
+ .set(SubscribeForPersistedEvents.JsonFields.JSON_FROM_HISTORICAL_TIMESTAMP, KNOWN_FROM_TS)
+ .set(SubscribeForPersistedEvents.JsonFields.JSON_TO_HISTORICAL_TIMESTAMP, KNOWN_TO_TS)
+ .build()
+ .toString();
+
+ private static final String JSON_MINIMAL = JsonFactory.newObjectBuilder()
+ .set(Command.JsonFields.TYPE, SubscribeForPersistedEvents.TYPE)
+ .set(StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_TYPE, KNOWN_ENTITY_TYPE_STR)
+ .set(StreamingSubscriptionCommand.JsonFields.JSON_ENTITY_ID, KNOWN_ENTITY_ID_STR)
+ .set(StreamingSubscriptionCommand.JsonFields.JSON_RESOURCE_PATH, KNOWN_RESOURCE_PATH)
+ .set(SubscribeForPersistedEvents.JsonFields.JSON_FROM_HISTORICAL_REVISION, KNOWN_FROM_REV)
+ .set(SubscribeForPersistedEvents.JsonFields.JSON_TO_HISTORICAL_REVISION, KNOWN_TO_REV)
+ .build().toString();
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(SubscribeForPersistedEvents.class,
+ areImmutable(),
+ provided(Instant.class).isAlsoImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(SubscribeForPersistedEvents.class)
+ .withRedefinedSuperclass()
+ .verify();
+ }
+
+ @Test
+ public void toJsonWithAllFieldsSet() {
+ final SubscribeForPersistedEvents command = SubscribeForPersistedEvents.of(
+ NamespacedEntityId.of(EntityType.of(KNOWN_ENTITY_TYPE_STR), KNOWN_ENTITY_ID_STR),
+ JsonPointer.of(KNOWN_RESOURCE_PATH),
+ KNOWN_FROM_REV,
+ KNOWN_TO_REV,
+ Instant.parse(KNOWN_FROM_TS),
+ Instant.parse(KNOWN_TO_TS),
+ DittoHeaders.empty()
+ );
+
+ final String json = command.toJsonString();
+ assertThat(json).isEqualTo(JSON_ALL_FIELDS);
+ }
+
+ @Test
+ public void toJsonWithOnlyRequiredFieldsSet() {
+ final SubscribeForPersistedEvents command = SubscribeForPersistedEvents.of(
+ NamespacedEntityId.of(EntityType.of(KNOWN_ENTITY_TYPE_STR), KNOWN_ENTITY_ID_STR),
+ JsonPointer.of(KNOWN_RESOURCE_PATH),
+ KNOWN_FROM_REV,
+ KNOWN_TO_REV,
+ DittoHeaders.empty());
+ final String json = command.toJsonString();
+ assertThat(json).isEqualTo(JSON_MINIMAL);
+ }
+
+ @Test
+ public void fromJsonWithAllFieldsSet() {
+ final SubscribeForPersistedEvents command = SubscribeForPersistedEvents.of(
+ NamespacedEntityId.of(EntityType.of(KNOWN_ENTITY_TYPE_STR), KNOWN_ENTITY_ID_STR),
+ JsonPointer.of(KNOWN_RESOURCE_PATH),
+ KNOWN_FROM_REV,
+ KNOWN_TO_REV,
+ Instant.parse(KNOWN_FROM_TS),
+ Instant.parse(KNOWN_TO_TS),
+ DittoHeaders.empty()
+ );
+ assertThat(SubscribeForPersistedEvents.fromJson(JsonObject.of(JSON_ALL_FIELDS), DittoHeaders.empty()))
+ .isEqualTo(command);
+ }
+
+ @Test
+ public void fromJsonWithOnlyRequiredFieldsSet() {
+ assertThat(SubscribeForPersistedEvents.fromJson(JsonObject.of(JSON_MINIMAL), DittoHeaders.empty()))
+ .isEqualTo(SubscribeForPersistedEvents.of(
+ NamespacedEntityId.of(EntityType.of(KNOWN_ENTITY_TYPE_STR), KNOWN_ENTITY_ID_STR),
+ JsonPointer.of(KNOWN_RESOURCE_PATH),
+ KNOWN_FROM_REV,
+ KNOWN_TO_REV,
+ DittoHeaders.empty()));
+ }
+
+}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCompleteTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCompleteTest.java
new file mode 100644
index 00000000000..1f628cf250f
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCompleteTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.util.UUID;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link StreamingSubscriptionComplete}.
+ */
+public final class StreamingSubscriptionCompleteTest {
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(StreamingSubscriptionComplete.class, areImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(StreamingSubscriptionComplete.class).withRedefinedSuperclass().verify();
+ }
+
+ @Test
+ public void serialization() {
+ final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().randomCorrelationId().build();
+ final StreamingSubscriptionComplete underTest = StreamingSubscriptionComplete.of(UUID.randomUUID().toString(),
+ NamespacedEntityId.of(EntityType.of("thing"), "foo:bar"), dittoHeaders);
+ final StreamingSubscriptionComplete deserialized = StreamingSubscriptionComplete.fromJson(underTest.toJson(), dittoHeaders);
+ assertThat(deserialized).isEqualTo(underTest);
+ }
+}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCreatedTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCreatedTest.java
new file mode 100644
index 00000000000..cc97fc9cee3
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionCreatedTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.util.UUID;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link StreamingSubscriptionCreated}.
+ */
+public final class StreamingSubscriptionCreatedTest {
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(StreamingSubscriptionCreated.class, areImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(StreamingSubscriptionCreated.class).withRedefinedSuperclass().verify();
+ }
+
+ @Test
+ public void serialization() {
+ final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().randomCorrelationId().build();
+ final StreamingSubscriptionCreated underTest = StreamingSubscriptionCreated.of(UUID.randomUUID().toString(),
+ NamespacedEntityId.of(EntityType.of("thing"), "foo:bar"), dittoHeaders);
+ final StreamingSubscriptionCreated deserialized = StreamingSubscriptionCreated.fromJson(underTest.toJson(), dittoHeaders);
+ assertThat(deserialized).isEqualTo(underTest);
+ }
+}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionFailedTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionFailedTest.java
new file mode 100644
index 00000000000..03ccc64ab3b
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionFailedTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.AllowedReason.provided;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.util.UUID;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.InvalidRqlExpressionException;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.signals.GlobalErrorRegistry;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link StreamingSubscriptionFailed}.
+ */
+public final class StreamingSubscriptionFailedTest {
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(StreamingSubscriptionFailed.class, areImmutable(),
+ provided(DittoRuntimeException.class).isAlsoImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(StreamingSubscriptionFailed.class).withRedefinedSuperclass().verify();
+ }
+
+ @Test
+ public void serialization() {
+ final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().randomCorrelationId().build();
+ final DittoRuntimeException error = GlobalErrorRegistry.getInstance()
+ .parse(InvalidRqlExpressionException.newBuilder().build().toJson(), dittoHeaders);
+ final StreamingSubscriptionFailed underTest = StreamingSubscriptionFailed.of(UUID.randomUUID().toString(),
+ NamespacedEntityId.of(EntityType.of("thing"), "foo:bar"), error, dittoHeaders);
+ final StreamingSubscriptionFailed deserialized = StreamingSubscriptionFailed.fromJson(underTest.toJson(), dittoHeaders);
+ assertThat(deserialized).isEqualTo(underTest);
+ }
+}
diff --git a/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionHasNextTest.java b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionHasNextTest.java
new file mode 100644
index 00000000000..15c371b4567
--- /dev/null
+++ b/base/model/src/test/java/org/eclipse/ditto/base/model/signals/events/streaming/StreamingSubscriptionHasNextTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.base.model.signals.events.streaming;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mutabilitydetector.unittesting.AllowedReason.provided;
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.util.UUID;
+
+import org.eclipse.ditto.base.model.entity.id.NamespacedEntityId;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.json.JsonArray;
+import org.eclipse.ditto.json.JsonValue;
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Tests {@link StreamingSubscriptionHasNext}.
+ */
+public final class StreamingSubscriptionHasNextTest {
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(StreamingSubscriptionHasNext.class, areImmutable(), provided(JsonValue.class).isAlsoImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(StreamingSubscriptionHasNext.class).withRedefinedSuperclass().verify();
+ }
+
+ @Test
+ public void serialization() {
+ final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().randomCorrelationId().build();
+ final JsonArray items = JsonArray.of("[{\"x\":1},{\"x\":2}]");
+ final StreamingSubscriptionHasNext
+ underTest = StreamingSubscriptionHasNext.of(UUID.randomUUID().toString(),
+ NamespacedEntityId.of(EntityType.of("thing"), "foo:bar"), items, dittoHeaders);
+ final StreamingSubscriptionHasNext deserialized = StreamingSubscriptionHasNext.fromJson(underTest.toJson(), dittoHeaders);
+ assertThat(deserialized).isEqualTo(underTest);
+ }
+}
diff --git a/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/DefaultLimitsConfig.java b/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/DefaultLimitsConfig.java
index ff45d6567ad..567320531fe 100644
--- a/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/DefaultLimitsConfig.java
+++ b/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/DefaultLimitsConfig.java
@@ -69,6 +69,11 @@ public long getPoliciesMaxSize() {
return policiesMaxSize;
}
+ @Override
+ public int getPolicyImportsLimit() {
+ return policyImportsLimit;
+ }
+
@Override
public long getMessagesMaxSize() {
return messagesMaxSize;
@@ -92,11 +97,6 @@ public String getConfigPath() {
return CONFIG_PATH;
}
- @Override
- public int getPolicyImportsLimit() {
- return policyImportsLimit;
- }
-
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
diff --git a/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/LimitsConfig.java b/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/LimitsConfig.java
index 0978ebecb09..94343f286c8 100644
--- a/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/LimitsConfig.java
+++ b/base/service/src/main/java/org/eclipse/ditto/base/service/config/limits/LimitsConfig.java
@@ -80,6 +80,11 @@ enum LimitsConfigValue implements KnownConfigValue {
*/
POLICIES_MAX_SIZE("policies.max-size", Constants.DEFAULT_ENTITY_MAX_SIZE),
+ /**
+ * The number of imports that a policy may contain.
+ */
+ POLICY_IMPORTS_LIMIT("policies.imports-limit", 10),
+
/**
* The maximum possible size of "Messages" entities in bytes.
*/
@@ -93,12 +98,7 @@ enum LimitsConfigValue implements KnownConfigValue {
/**
* The maximum pagination size to apply when searching for "Things" via "things-search".
*/
- THINGS_SEARCH_MAX_PAGE_SIZE(Constants.THINGS_SEARCH_PATH + "." + "max-page-size", 200),
-
- /**
- * The number of imports that a policy may contain.
- */
- POLICY_IMPORTS_LIMIT("imports-limit", 10);
+ THINGS_SEARCH_MAX_PAGE_SIZE(Constants.THINGS_SEARCH_PATH + "." + "max-page-size", 200);
private final String path;
private final Object defaultValue;
diff --git a/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultLocalAskTimeoutConfig.java b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultLocalAskTimeoutConfig.java
new file mode 100644
index 00000000000..7df4ee807b8
--- /dev/null
+++ b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultLocalAskTimeoutConfig.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.eclipse.ditto.base.service.config.supervision;
+
+import java.time.Duration;
+import java.util.Objects;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.internal.utils.config.ConfigWithFallback;
+import org.eclipse.ditto.internal.utils.config.ScopedConfig;
+
+import com.typesafe.config.Config;
+
+/**
+ * This class is the default implementation of the local ACK timeout config.
+ */
+@Immutable
+public class DefaultLocalAskTimeoutConfig implements LocalAskTimeoutConfig {
+
+ private static final String CONFIG_PATH = "local-ask";
+ private final Duration askTimeout;
+
+ private DefaultLocalAskTimeoutConfig(final ScopedConfig config) {
+ askTimeout = config.getNonNegativeAndNonZeroDurationOrThrow(LocalAskTimeoutConfigValue.ASK_TIMEOUT);
+ }
+
+ /**
+ * Returns an instance of {@code DefaultLocalAskTimeoutConfig} based on the settings of the specified Config.
+ *
+ * @param config is supposed to provide the settings of the local ASK timeout config at {@value #CONFIG_PATH}.
+ * @return the instance.
+ * @throws org.eclipse.ditto.internal.utils.config.DittoConfigError if {@code config} is invalid.
+ */
+ public static DefaultLocalAskTimeoutConfig of(final Config config) {
+ return new DefaultLocalAskTimeoutConfig(ConfigWithFallback.newInstance(config, CONFIG_PATH,
+ LocalAskTimeoutConfig.LocalAskTimeoutConfigValue.values()));
+ }
+
+ @Override
+ public Duration getLocalAckTimeout() {
+ return askTimeout;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final DefaultLocalAskTimeoutConfig that = (DefaultLocalAskTimeoutConfig) o;
+ return Objects.equals(askTimeout, that.askTimeout);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(askTimeout);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" +
+ "askTimeout=" + askTimeout +
+ ']';
+ }
+}
diff --git a/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultSupervisorConfig.java b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultSupervisorConfig.java
index 8f50cde1691..dae28521c12 100644
--- a/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultSupervisorConfig.java
+++ b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/DefaultSupervisorConfig.java
@@ -30,9 +30,12 @@ public final class DefaultSupervisorConfig implements SupervisorConfig {
private static final String CONFIG_PATH = "supervisor";
private final ExponentialBackOffConfig exponentialBackOffConfig;
+ private final DefaultLocalAskTimeoutConfig localAskTimeoutConfig;
- private DefaultSupervisorConfig(final ExponentialBackOffConfig theExponentialBackOffConfig) {
+ private DefaultSupervisorConfig(final ExponentialBackOffConfig theExponentialBackOffConfig,
+ final DefaultLocalAskTimeoutConfig theLocalAskTimeoutConfig) {
exponentialBackOffConfig = theExponentialBackOffConfig;
+ localAskTimeoutConfig = theLocalAskTimeoutConfig;
}
/**
@@ -45,7 +48,8 @@ private DefaultSupervisorConfig(final ExponentialBackOffConfig theExponentialBac
public static DefaultSupervisorConfig of(final Config config) {
final ScopedConfig supervisorScopedConfig = DefaultScopedConfig.newInstance(config, CONFIG_PATH);
- return new DefaultSupervisorConfig(DefaultExponentialBackOffConfig.of(supervisorScopedConfig));
+ return new DefaultSupervisorConfig(DefaultExponentialBackOffConfig.of(supervisorScopedConfig),
+ DefaultLocalAskTimeoutConfig.of(supervisorScopedConfig));
}
@Override
@@ -53,6 +57,11 @@ public ExponentialBackOffConfig getExponentialBackOffConfig() {
return exponentialBackOffConfig;
}
+ @Override
+ public LocalAskTimeoutConfig getLocalAskTimeoutConfig() {
+ return localAskTimeoutConfig;
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
diff --git a/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/LocalAskTimeoutConfig.java b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/LocalAskTimeoutConfig.java
new file mode 100644
index 00000000000..c14a1b3f3d5
--- /dev/null
+++ b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/LocalAskTimeoutConfig.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.eclipse.ditto.base.service.config.supervision;
+
+import java.time.Duration;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.internal.utils.config.KnownConfigValue;
+
+/**
+ * Provides configuration settings for the local ACK timeout.
+ */
+@Immutable
+public interface LocalAskTimeoutConfig {
+
+ /**
+ * Timeout for local actor invocations - a small timeout should be more than sufficient as those are just method
+ * calls.
+ * @return the duration for a local ACK timeout calls.
+ */
+ Duration getLocalAckTimeout();
+
+
+ /**
+ * An enumeration of the known config path expressions and their associated default values for
+ * {@code LocalAskTimeoutConfigValue}.
+ */
+ enum LocalAskTimeoutConfigValue implements KnownConfigValue {
+
+ /**
+ * The local ACK timeout duration.
+ */
+ ASK_TIMEOUT("timeout", Duration.ofSeconds(5L));
+
+ private final String path;
+ private final Duration defaultValue;
+
+ LocalAskTimeoutConfigValue(final String thePath, final Duration theDefaultValue) {
+
+ this.path = thePath;
+ this.defaultValue = theDefaultValue;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return defaultValue;
+ }
+
+ @Override
+ public String getConfigPath() {
+ return path;
+ }
+ }
+}
diff --git a/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/SupervisorConfig.java b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/SupervisorConfig.java
index 9415b69a75f..10d8521a36d 100644
--- a/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/SupervisorConfig.java
+++ b/base/service/src/main/java/org/eclipse/ditto/base/service/config/supervision/SupervisorConfig.java
@@ -27,4 +27,9 @@ public interface SupervisorConfig {
*/
ExponentialBackOffConfig getExponentialBackOffConfig();
+ /** Returns the config for supervisor local ACK timeout calls.
+ * @return the config.
+ */
+ LocalAskTimeoutConfig getLocalAskTimeoutConfig();
+
}
diff --git a/base/service/src/test/java/org/eclipse/ditto/base/service/config/supervision/DefaultLocalAskTimeoutConfigTest.java b/base/service/src/test/java/org/eclipse/ditto/base/service/config/supervision/DefaultLocalAskTimeoutConfigTest.java
new file mode 100644
index 00000000000..10edd813f2a
--- /dev/null
+++ b/base/service/src/test/java/org/eclipse/ditto/base/service/config/supervision/DefaultLocalAskTimeoutConfigTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.eclipse.ditto.base.service.config.supervision;
+
+import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
+import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
+
+import java.time.Duration;
+
+import org.assertj.core.api.JUnitSoftAssertions;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+/**
+ * Unit test for {@link DefaultLocalAskTimeoutConfigTest}.
+ */
+public class DefaultLocalAskTimeoutConfigTest {
+
+ private static Config supervisorLocalAskTimeoutConfig;
+
+ @Rule
+ public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
+
+ @BeforeClass
+ public static void initTestFixture() {
+ supervisorLocalAskTimeoutConfig = ConfigFactory.load("local-ask-timout-test");
+ }
+
+ @Test
+ public void assertImmutability() {
+ assertInstancesOf(DefaultLocalAskTimeoutConfig.class,
+ areImmutable());
+ }
+
+ @Test
+ public void testHashCodeAndEquals() {
+ EqualsVerifier.forClass(DefaultLocalAskTimeoutConfig.class)
+ .usingGetClass()
+ .verify();
+ }
+
+ @Test
+ public void underTestReturnsDefaultValuesIfBaseConfigWasEmpty() {
+ final DefaultLocalAskTimeoutConfig underTest = DefaultLocalAskTimeoutConfig.of(ConfigFactory.empty());
+
+ softly.assertThat(underTest.getLocalAckTimeout())
+ .as(LocalAskTimeoutConfig.LocalAskTimeoutConfigValue.ASK_TIMEOUT.getConfigPath())
+ .isEqualTo(LocalAskTimeoutConfig.LocalAskTimeoutConfigValue.ASK_TIMEOUT.getDefaultValue());
+ }
+
+ @Test
+ public void underTestReturnsValuesOfConfigFile() {
+ final DefaultLocalAskTimeoutConfig underTest = DefaultLocalAskTimeoutConfig.of(supervisorLocalAskTimeoutConfig);
+
+ softly.assertThat(underTest.getLocalAckTimeout())
+ .as(LocalAskTimeoutConfig.LocalAskTimeoutConfigValue.ASK_TIMEOUT.getConfigPath())
+ .isEqualTo(Duration.ofSeconds(10L));
+ }
+}
\ No newline at end of file
diff --git a/base/service/src/test/resources/local-ask-timout-test.conf b/base/service/src/test/resources/local-ask-timout-test.conf
new file mode 100644
index 00000000000..110938ddc6f
--- /dev/null
+++ b/base/service/src/test/resources/local-ask-timout-test.conf
@@ -0,0 +1,3 @@
+local-ask {
+ timeout = 10s
+}
diff --git a/bom/pom.xml b/bom/pom.xml
index d7ff2b70b1b..6b59efa521b 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -34,7 +34,7 @@
0.9.5
- 2.13.4.20221013
+ 2.14.3
1.4.2
0.6.1
2.6.20
@@ -85,7 +85,7 @@
-->
3.2.0
- 3.1.1
+ 3.1.6
3.11
diff --git a/connectivity/model/pom.xml b/connectivity/model/pom.xml
index 9ac624708b7..01801773765 100644
--- a/connectivity/model/pom.xml
+++ b/connectivity/model/pom.xml
@@ -124,7 +124,7 @@
-
+ org.eclipse.ditto.connectivity.model.Connection#toJson(org.eclipse.ditto.base.model.json.JsonSchemaVersion,org.eclipse.ditto.json.JsonFieldSelector)
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnection.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnection.java
index eea09317d52..bb8dc0f8e31 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnection.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnection.java
@@ -15,6 +15,8 @@
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -31,6 +33,8 @@
import javax.annotation.Nullable;
+import org.eclipse.ditto.base.model.entity.id.EntityId;
+import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.json.JsonArray;
import org.eclipse.ditto.json.JsonCollectors;
@@ -56,6 +60,9 @@ abstract class AbstractConnection implements Connection {
@Nullable private final Credentials credentials;
@Nullable private final String trustedCertificates;
@Nullable private final ConnectionLifecycle lifecycle;
+ @Nullable private final ConnectionRevision revision;
+ @Nullable private final Instant modified;
+ @Nullable private final Instant created;
private final List sources;
private final List targets;
private final int clientCount;
@@ -85,12 +92,15 @@ abstract class AbstractConnection implements Connection {
payloadMappingDefinition = builder.payloadMappingDefinition;
tags = Collections.unmodifiableSet(new LinkedHashSet<>(builder.tags));
lifecycle = builder.lifecycle;
+ revision = builder.revision;
+ modified = builder.modified;
+ created = builder.created;
sshTunnel = builder.sshTunnel;
}
abstract ConnectionUri getConnectionUri(@Nullable String builderConnectionUri);
- static void buildFromJson (final JsonObject jsonObject, final AbstractConnectionBuilder builder) {
+ static void buildFromJson(final JsonObject jsonObject, final AbstractConnectionBuilder builder) {
final MappingContext mappingContext = jsonObject.getValue(JsonFields.MAPPING_CONTEXT)
.map(ConnectivityModelFactory::mappingContextFromJson)
.orElse(null);
@@ -112,13 +122,22 @@ static void buildFromJson (final JsonObject jsonObject, final AbstractConnection
jsonObject.getValue(JsonFields.LIFECYCLE)
.flatMap(ConnectionLifecycle::forName).ifPresent(builder::lifecycle);
- jsonObject.getValue(JsonFields.CREDENTIALS).ifPresent(builder::credentialsFromJson);
+ jsonObject.getValue(JsonFields.REVISION)
+ .map(ConnectionRevision::newInstance).ifPresent(builder::revision);
+ jsonObject.getValue(JsonFields.MODIFIED)
+ .map(AbstractConnection::tryToParseInstant).ifPresent(builder::modified);
+ jsonObject.getValue(JsonFields.CREATED)
+ .map(AbstractConnection::tryToParseInstant).ifPresent(builder::created);
+ jsonObject.getValue(JsonFields.CREDENTIALS)
+ .filter(f -> !f.isNull())
+ .ifPresent(builder::credentialsFromJson);
jsonObject.getValue(JsonFields.CLIENT_COUNT).ifPresent(builder::clientCount);
jsonObject.getValue(JsonFields.FAILOVER_ENABLED).ifPresent(builder::failoverEnabled);
jsonObject.getValue(JsonFields.VALIDATE_CERTIFICATES).ifPresent(builder::validateCertificate);
jsonObject.getValue(JsonFields.PROCESSOR_POOL_SIZE).ifPresent(builder::processorPoolSize);
jsonObject.getValue(JsonFields.TRUSTED_CERTIFICATES).ifPresent(builder::trustedCertificates);
jsonObject.getValue(JsonFields.SSH_TUNNEL)
+ .filter(f -> !f.isNull())
.ifPresent(jsonFields -> builder.sshTunnel(ImmutableSshTunnel.fromJson(jsonFields)));
}
@@ -159,6 +178,7 @@ private static List getTargets(final JsonObject jsonObject) {
private static Map getSpecificConfiguration(final JsonObject jsonObject) {
return jsonObject.getValue(JsonFields.SPECIFIC_CONFIG)
+ .filter(f -> !f.isNull())
.filter(JsonValue::isObject)
.map(JsonValue::asObject)
.map(JsonObject::stream)
@@ -169,6 +189,7 @@ private static Map getSpecificConfiguration(final JsonObject jso
private static Set getTags(final JsonObject jsonObject) {
return jsonObject.getValue(JsonFields.TAGS)
+ .filter(f -> !f.isNull())
.map(array -> array.stream()
.filter(JsonValue::isString)
.map(JsonValue::asString)
@@ -296,6 +317,36 @@ public Optional getLifecycle() {
return Optional.ofNullable(lifecycle);
}
+ @Override
+ public Optional extends EntityId> getEntityId() {
+ return Optional.of(id);
+ }
+
+ @Override
+ public Optional getRevision() {
+ return Optional.ofNullable(revision);
+ }
+
+ @Override
+ public Optional getModified() {
+ return Optional.ofNullable(modified);
+ }
+
+ @Override
+ public Optional getCreated() {
+ return Optional.ofNullable(created);
+ }
+
+ @Override
+ public Optional getMetadata() {
+ return Optional.empty(); // currently not metadata support for connections
+ }
+
+ @Override
+ public boolean isDeleted() {
+ return ConnectionLifecycle.DELETED.equals(lifecycle);
+ }
+
static ConnectionBuilder fromConnection(final Connection connection, final AbstractConnectionBuilder builder) {
checkNotNull(connection, "Connection");
@@ -316,7 +367,10 @@ static ConnectionBuilder fromConnection(final Connection connection, final Abstr
.name(connection.getName().orElse(null))
.sshTunnel(connection.getSshTunnel().orElse(null))
.tags(connection.getTags())
- .lifecycle(connection.getLifecycle().orElse(null));
+ .lifecycle(connection.getLifecycle().orElse(null))
+ .revision(connection.getRevision().orElse(null))
+ .modified(connection.getModified().orElse(null))
+ .created(connection.getCreated().orElse(null));
}
@Override
@@ -329,6 +383,15 @@ public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate<
}
jsonObjectBuilder.set(JsonFields.ID, String.valueOf(id), predicate);
jsonObjectBuilder.set(JsonFields.NAME, name, predicate);
+ if (null != revision) {
+ jsonObjectBuilder.set(JsonFields.REVISION, revision.toLong(), predicate);
+ }
+ if (null != modified) {
+ jsonObjectBuilder.set(JsonFields.MODIFIED, modified.toString(), predicate);
+ }
+ if (null != created) {
+ jsonObjectBuilder.set(JsonFields.CREATED, created.toString(), predicate);
+ }
jsonObjectBuilder.set(JsonFields.CONNECTION_TYPE, connectionType.getName(), predicate);
jsonObjectBuilder.set(JsonFields.CONNECTION_STATUS, connectionStatus.getName(), predicate);
jsonObjectBuilder.set(JsonFields.URI, uri.toString(), predicate);
@@ -368,6 +431,15 @@ public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate<
return jsonObjectBuilder.build();
}
+ private static Instant tryToParseInstant(final CharSequence dateTime) {
+ try {
+ return Instant.parse(dateTime);
+ } catch (final DateTimeParseException e) {
+ throw new JsonParseException("The JSON object's field '" + Connection.JsonFields.MODIFIED.getPointer() + "' " +
+ "is not in ISO-8601 format as expected");
+ }
+ }
+
@SuppressWarnings("OverlyComplexMethod")
@Override
public boolean equals(@Nullable final Object o) {
@@ -394,6 +466,9 @@ public boolean equals(@Nullable final Object o) {
Objects.equals(specificConfig, that.specificConfig) &&
Objects.equals(payloadMappingDefinition, that.payloadMappingDefinition) &&
Objects.equals(lifecycle, that.lifecycle) &&
+ Objects.equals(revision, that.revision) &&
+ Objects.equals(modified, that.modified) &&
+ Objects.equals(created, that.created) &&
Objects.equals(sshTunnel, that.sshTunnel) &&
Objects.equals(tags, that.tags);
}
@@ -402,7 +477,7 @@ public boolean equals(@Nullable final Object o) {
public int hashCode() {
return Objects.hash(id, name, connectionType, connectionStatus, sources, targets, clientCount, failOverEnabled,
credentials, trustedCertificates, uri, validateCertificate, processorPoolSize, specificConfig,
- payloadMappingDefinition, sshTunnel, tags, lifecycle);
+ payloadMappingDefinition, sshTunnel, tags, lifecycle, revision, modified, created);
}
@Override
@@ -426,6 +501,9 @@ public String toString() {
", payloadMappingDefinition=" + payloadMappingDefinition +
", tags=" + tags +
", lifecycle=" + lifecycle +
+ ", revision=" + revision +
+ ", modified=" + modified +
+ ", created=" + created +
"]";
}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnectionBuilder.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnectionBuilder.java
index 5bf00d4797a..6243c22352f 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnectionBuilder.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/AbstractConnectionBuilder.java
@@ -16,6 +16,7 @@
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -49,6 +50,9 @@ abstract class AbstractConnectionBuilder implements ConnectionBuilder {
@Nullable MappingContext mappingContext = null;
@Nullable String trustedCertificates;
@Nullable ConnectionLifecycle lifecycle = null;
+ @Nullable ConnectionRevision revision = null;
+ @Nullable Instant modified = null;
+ @Nullable Instant created = null;
@Nullable SshTunnel sshTunnel = null;
// optional with default:
@@ -192,6 +196,24 @@ public ConnectionBuilder lifecycle(@Nullable final ConnectionLifecycle lifecycle
return this;
}
+ @Override
+ public ConnectionBuilder revision(@Nullable final ConnectionRevision revision) {
+ this.revision = revision;
+ return this;
+ }
+
+ @Override
+ public ConnectionBuilder modified(@Nullable final Instant modified) {
+ this.modified = modified;
+ return this;
+ }
+
+ @Override
+ public ConnectionBuilder created(@Nullable final Instant created) {
+ this.created = created;
+ return this;
+ }
+
@Override
public ConnectionBuilder sshTunnel(@Nullable final SshTunnel sshTunnel) {
this.sshTunnel = sshTunnel;
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/Connection.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/Connection.java
index b9a99b60f1e..bcb51110a54 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/Connection.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/Connection.java
@@ -20,21 +20,19 @@
import javax.annotation.concurrent.Immutable;
+import org.eclipse.ditto.base.model.entity.Entity;
import org.eclipse.ditto.base.model.json.FieldType;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
-import org.eclipse.ditto.base.model.json.Jsonifiable;
import org.eclipse.ditto.json.JsonArray;
import org.eclipse.ditto.json.JsonFactory;
-import org.eclipse.ditto.json.JsonField;
import org.eclipse.ditto.json.JsonFieldDefinition;
-import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
/**
* Represents a connection within the Connectivity service.
*/
@Immutable
-public interface Connection extends Jsonifiable.WithFieldSelectorAndPredicate {
+public interface Connection extends Entity {
/**
* Returns the identifier of this {@code Connection}.
@@ -242,11 +240,6 @@ default JsonObject toJson() {
return toJson(FieldType.notHidden());
}
- @Override
- default JsonObject toJson(final JsonSchemaVersion schemaVersion, final JsonFieldSelector fieldSelector) {
- return toJson(schemaVersion, FieldType.notHidden()).get(fieldSelector);
- }
-
/**
* An enumeration of the known {@code JsonField}s of a {@code Connection}.
*/
@@ -261,6 +254,33 @@ final class JsonFields {
FieldType.HIDDEN,
JsonSchemaVersion.V_2);
+ /**
+ * JSON field containing the Connection's revision.
+ * @since 3.2.0
+ */
+ public static final JsonFieldDefinition REVISION = JsonFactory.newLongFieldDefinition("_revision",
+ FieldType.SPECIAL,
+ FieldType.HIDDEN,
+ JsonSchemaVersion.V_2);
+
+ /**
+ * JSON field containing the Connection's modified timestamp in ISO-8601 format.
+ * @since 3.2.0
+ */
+ public static final JsonFieldDefinition MODIFIED = JsonFactory.newStringFieldDefinition("_modified",
+ FieldType.SPECIAL,
+ FieldType.HIDDEN,
+ JsonSchemaVersion.V_2);
+
+ /**
+ * JSON field containing the Connection's created timestamp in ISO-8601 format.
+ * @since 3.2.0
+ */
+ public static final JsonFieldDefinition CREATED = JsonFactory.newStringFieldDefinition("_created",
+ FieldType.SPECIAL,
+ FieldType.HIDDEN,
+ JsonSchemaVersion.V_2);
+
/**
* JSON field containing the {@code Connection} identifier.
*/
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionBuilder.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionBuilder.java
index a124f64ccaa..56daf691b7b 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionBuilder.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionBuilder.java
@@ -12,6 +12,7 @@
*/
package org.eclipse.ditto.connectivity.model;
+import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -203,6 +204,44 @@ default ConnectionBuilder credentialsFromJson(final JsonObject jsonObject) {
*/
ConnectionBuilder lifecycle(@Nullable ConnectionLifecycle lifecycle);
+ /**
+ * Sets the given revision number to this builder.
+ *
+ * @param revisionNumber the revision number to be set.
+ * @return this builder to allow method chaining.
+ * @since 3.2.0
+ */
+ default ConnectionBuilder revision(final long revisionNumber) {
+ return revision(ConnectionRevision.newInstance(revisionNumber));
+ }
+
+ /**
+ * Sets the {@link ConnectionRevision} of the connection.
+ *
+ * @param revision the connection revision
+ * @return this builder
+ * @since 3.2.0
+ */
+ ConnectionBuilder revision(@Nullable ConnectionRevision revision);
+
+ /**
+ * Sets the given modified timestamp to this builder.
+ *
+ * @param modified the timestamp to be set.
+ * @return this builder to allow method chaining.
+ * @since 3.2.0
+ */
+ ConnectionBuilder modified(@Nullable Instant modified);
+
+ /**
+ * Sets the given created timestamp to this builder.
+ *
+ * @param created the created timestamp to be set.
+ * @return this builder to allow method chaining.
+ * @since 3.2.0
+ */
+ ConnectionBuilder created(@Nullable Instant created);
+
/**
* Sets the {@link SshTunnel} of the connection.
*
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionRevision.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionRevision.java
new file mode 100755
index 00000000000..2986f7a82d3
--- /dev/null
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectionRevision.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.model;
+
+import org.eclipse.ditto.base.model.entity.Revision;
+
+/**
+ * Represents the current revision of a Connection.
+ */
+public interface ConnectionRevision extends Revision {
+
+ /**
+ * Returns a new immutable {@code ConnectionRevision} which is initialised with the given revision number.
+ *
+ * @param revisionNumber the {@code long} value of the revision.
+ * @return the new immutable {@code ConnectionRevision}.
+ */
+ static ConnectionRevision newInstance(final long revisionNumber) {
+ return ConnectivityModelFactory.newConnectionRevision(revisionNumber);
+ }
+
+}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectivityModelFactory.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectivityModelFactory.java
index 61ca499af91..4ace7c5435f 100755
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectivityModelFactory.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ConnectivityModelFactory.java
@@ -991,4 +991,15 @@ public static LogEntryBuilder newLogEntryBuilder(final String correlationId,
return ImmutableLogEntry.getBuilder(correlationId, timestamp, logCategory, logType, logLevel, message);
}
+ /**
+ * Returns a new immutable {@link ConnectionRevision} which is initialised with the given revision number.
+ *
+ * @param revisionNumber the {@code long} value of the revision.
+ * @return the new immutable {@code ConnectionRevision}.
+ * @since 3.2.0
+ */
+ public static ConnectionRevision newConnectionRevision(final long revisionNumber) {
+ return ImmutableConnectionRevision.of(revisionNumber);
+ }
+
}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableConnectionRevision.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableConnectionRevision.java
new file mode 100755
index 00000000000..c9ec22b8000
--- /dev/null
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableConnectionRevision.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.model;
+
+import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
+
+import java.util.Objects;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * An immutable implementation of {@link ConnectionRevision}.
+ */
+@Immutable
+final class ImmutableConnectionRevision implements ConnectionRevision {
+
+ private final long value;
+
+ private ImmutableConnectionRevision(final long theValue) {
+ value = theValue;
+ }
+
+ /**
+ * Returns a new instance of {@code ConnectionRevision} with the given value.
+ *
+ * @param value the value of the new revision.
+ * @return a new Connection revision.
+ */
+ public static ImmutableConnectionRevision of(final long value) {
+ return new ImmutableConnectionRevision(value);
+ }
+
+ @Override
+ public boolean isGreaterThan(final ConnectionRevision other) {
+ return 0 < compareTo(other);
+ }
+
+ @Override
+ public boolean isGreaterThanOrEqualTo(final ConnectionRevision other) {
+ return 0 <= compareTo(other);
+ }
+
+ @Override
+ public boolean isLowerThan(final ConnectionRevision other) {
+ return 0 > compareTo(other);
+ }
+
+ @Override
+ public boolean isLowerThanOrEqualTo(final ConnectionRevision other) {
+ return 0 >= compareTo(other);
+ }
+
+ @Override
+ public ConnectionRevision increment() {
+ return of(value + 1);
+ }
+
+ @Override
+ public long toLong() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ImmutableConnectionRevision that = (ImmutableConnectionRevision) o;
+ return value == that.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
+
+ @Override
+ public int compareTo(final ConnectionRevision o) {
+ checkNotNull(o, "other revision to compare this revision with");
+ return Long.compare(value, o.toLong());
+ }
+
+}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableMappingContext.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableMappingContext.java
index 286063a76d9..5cac0bef77e 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableMappingContext.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableMappingContext.java
@@ -25,13 +25,13 @@
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
+import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.json.JsonCollectors;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonField;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonObjectBuilder;
import org.eclipse.ditto.json.JsonValue;
-import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
/**
* Immutable implementation of {@link MappingContext}.
@@ -69,14 +69,18 @@ public static MappingContext fromJson(final JsonObject jsonObject) {
final Builder builder = new Builder(mappingEngine, options);
builder.incomingConditions(
- jsonObject.getValue(JsonFields.INCOMING_CONDITIONS).orElse(JsonObject.empty()).stream()
+ jsonObject.getValue(JsonFields.INCOMING_CONDITIONS)
+ .filter(f -> !f.isNull())
+ .orElse(JsonObject.empty()).stream()
.collect(Collectors.toMap(
e -> e.getKey().toString(),
e -> e.getValue().isString() ? e.getValue().asString() : e.getValue().toString())
));
builder.outgoingConditions(
- jsonObject.getValue(JsonFields.OUTGOING_CONDITIONS).orElse(JsonObject.empty()).stream()
+ jsonObject.getValue(JsonFields.OUTGOING_CONDITIONS)
+ .filter(f -> !f.isNull())
+ .orElse(JsonObject.empty()).stream()
.collect(Collectors.toMap(
e -> e.getKey().toString(),
e -> e.getValue().isString() ? e.getValue().asString() : e.getValue().toString())
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableReplyTarget.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableReplyTarget.java
index a8ac462faad..e8c009505c7 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableReplyTarget.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableReplyTarget.java
@@ -121,9 +121,11 @@ static Optional fromJsonOptional(final JsonObject jsonObject) {
return jsonObject.getValue(JsonFields.ADDRESS).map(address -> new Builder()
.address(address)
.headerMapping(jsonObject.getValue(JsonFields.HEADER_MAPPING)
+ .filter(f -> !f.isNull())
.map(ConnectivityModelFactory::newHeaderMapping)
.orElse(null))
.expectedResponseTypes(jsonObject.getValue(JsonFields.EXPECTED_RESPONSE_TYPES)
+ .filter(f -> !f.isNull())
.map(jsonArray -> jsonArray.stream()
.map(JsonValue::asString)
.map(ResponseType::fromName)
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableSource.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableSource.java
index 1ef25b6783c..b1b3ef15d1c 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableSource.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableSource.java
@@ -253,18 +253,26 @@ public static Source fromJson(final JsonObject jsonObject, final int index) {
authorizationSubjects);
final Enforcement readEnforcement =
- jsonObject.getValue(JsonFields.ENFORCEMENT).map(ImmutableEnforcement::fromJson).orElse(null);
+ jsonObject.getValue(JsonFields.ENFORCEMENT)
+ .filter(f -> !f.isNull())
+ .map(ImmutableEnforcement::fromJson)
+ .orElse(null);
final FilteredAcknowledgementRequest readAcknowledgementRequests =
jsonObject.getValue(JsonFields.ACKNOWLEDGEMENT_REQUESTS)
+ .filter(f -> !f.isNull())
.map(FilteredAcknowledgementRequest::fromJson)
.orElse(null);
final HeaderMapping readHeaderMapping =
- jsonObject.getValue(JsonFields.HEADER_MAPPING).map(ImmutableHeaderMapping::fromJson).orElse(null);
+ jsonObject.getValue(JsonFields.HEADER_MAPPING)
+ .filter(f -> !f.isNull())
+ .map(ImmutableHeaderMapping::fromJson)
+ .orElse(null);
final PayloadMapping readPayloadMapping =
jsonObject.getValue(JsonFields.PAYLOAD_MAPPING)
+ .filter(f -> !f.isNull())
.map(ImmutablePayloadMapping::fromJson)
.orElse(ConnectivityModelFactory.emptyPayloadMapping());
@@ -273,6 +281,7 @@ public static Source fromJson(final JsonObject jsonObject, final int index) {
final ReplyTarget readReplyTarget =
jsonObject.getValue(JsonFields.REPLY_TARGET)
+ .filter(f -> !f.isNull())
.flatMap(ImmutableReplyTarget::fromJsonOptional)
.orElse(null);
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableTarget.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableTarget.java
index e81329e7a43..6f3be41d147 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableTarget.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/ImmutableTarget.java
@@ -193,11 +193,13 @@ public static Target fromJson(final JsonObject jsonObject) {
final HeaderMapping readHeaderMapping =
jsonObject.getValue(JsonFields.HEADER_MAPPING)
+ .filter(f -> !f.isNull())
.map(ImmutableHeaderMapping::fromJson)
.orElse(null);
final PayloadMapping readMapping =
jsonObject.getValue(JsonFields.PAYLOAD_MAPPING)
+ .filter(f -> !f.isNull())
.map(ImmutablePayloadMapping::fromJson)
.orElse(ConnectivityModelFactory.emptyPayloadMapping());
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/ConnectivityCommand.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/ConnectivityCommand.java
index 6c33196e410..53ead396749 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/ConnectivityCommand.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/ConnectivityCommand.java
@@ -12,14 +12,16 @@
*/
package org.eclipse.ditto.connectivity.model.signals.commands;
-import org.eclipse.ditto.json.JsonFactory;
-import org.eclipse.ditto.json.JsonFieldDefinition;
-import org.eclipse.ditto.json.JsonPointer;
+import org.eclipse.ditto.base.model.entity.type.EntityType;
+import org.eclipse.ditto.base.model.entity.type.WithEntityType;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.json.FieldType;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
-import org.eclipse.ditto.connectivity.model.ConnectivityConstants;
import org.eclipse.ditto.base.model.signals.commands.Command;
+import org.eclipse.ditto.connectivity.model.ConnectivityConstants;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonPointer;
/**
* Base interface for all commands which are understood by the Connectivity service. Implementations of this interface
@@ -27,7 +29,8 @@
*
* @param the type of the implementing class.
*/
-public interface ConnectivityCommand> extends Command {
+public interface ConnectivityCommand> extends Command,
+ WithEntityType {
/**
* Type Prefix of Connectivity commands.
@@ -57,6 +60,17 @@ default String getTypePrefix() {
@Override
T setDittoHeaders(DittoHeaders dittoHeaders);
+ /**
+ * Returns the entity type {@link ConnectivityConstants#ENTITY_TYPE}.
+ *
+ * @return the Connection entity type.
+ * @since 3.2.0
+ */
+ @Override
+ default EntityType getEntityType() {
+ return ConnectivityConstants.ENTITY_TYPE;
+ }
+
/**
* This class contains definitions for all specific fields of a {@code ConnectivityCommand}'s JSON representation.
*/
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionHistoryNotAccessibleException.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionHistoryNotAccessibleException.java
new file mode 100755
index 00000000000..e51a650b3ae
--- /dev/null
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionHistoryNotAccessibleException.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.model.signals.commands.exceptions;
+
+import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
+
+import java.net.URI;
+import java.text.MessageFormat;
+import java.time.Instant;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.eclipse.ditto.base.model.common.HttpStatus;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
+import org.eclipse.ditto.connectivity.model.ConnectivityException;
+import org.eclipse.ditto.json.JsonObject;
+
+/**
+ * Thrown if historical data of the Connection was either not present in Ditto at all or if the requester had insufficient
+ * permissions to access it.
+ *
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableException(errorCode = ConnectionHistoryNotAccessibleException.ERROR_CODE)
+public final class ConnectionHistoryNotAccessibleException extends DittoRuntimeException
+ implements ConnectivityException {
+
+ /**
+ * Error code of this exception.
+ */
+ public static final String ERROR_CODE = ERROR_CODE_PREFIX + "connection.history.notfound";
+
+ private static final String MESSAGE_TEMPLATE =
+ "The Connection with ID ''{0}'' at revision ''{1}'' could not be found or requester had insufficient " +
+ "permissions to access it.";
+
+ private static final String MESSAGE_TEMPLATE_TS =
+ "The Connection with ID ''{0}'' at timestamp ''{1}'' could not be found or requester had insufficient " +
+ "permissions to access it.";
+
+ private static final String DEFAULT_DESCRIPTION =
+ "Check if the ID of your requested Connection was correct, you have sufficient permissions and ensure that the " +
+ "asked for revision/timestamp does not exceed the history-retention-duration.";
+
+ private static final long serialVersionUID = -998877665544332221L;
+
+ private ConnectionHistoryNotAccessibleException(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ super(ERROR_CODE, HttpStatus.NOT_FOUND, dittoHeaders, message, description, cause, href);
+ }
+
+ private static String getMessage(final ConnectionId connectionId, final long revision) {
+ checkNotNull(connectionId, "connectionId");
+ return MessageFormat.format(MESSAGE_TEMPLATE, String.valueOf(connectionId), String.valueOf(revision));
+ }
+
+ private static String getMessage(final ConnectionId connectionId, final Instant timestamp) {
+ checkNotNull(connectionId, "connectionId");
+ checkNotNull(timestamp, "timestamp");
+ return MessageFormat.format(MESSAGE_TEMPLATE_TS, String.valueOf(connectionId), timestamp.toString());
+ }
+
+ /**
+ * A mutable builder for a {@code ConnectionHistoryNotAccessibleException}.
+ *
+ * @param connectionId the ID of the connection.
+ * @param revision the asked for revision of the connection.
+ * @return the builder.
+ * @throws NullPointerException if {@code connectionId} is {@code null}.
+ */
+ public static Builder newBuilder(final ConnectionId connectionId, final long revision) {
+ return new Builder(connectionId, revision);
+ }
+
+ /**
+ * A mutable builder for a {@code ConnectionHistoryNotAccessibleException}.
+ *
+ * @param connectionId the ID of the connection.
+ * @param timestamp the asked for timestamp of the connection.
+ * @return the builder.
+ * @throws NullPointerException if {@code connectionId} is {@code null}.
+ */
+ public static Builder newBuilder(final ConnectionId connectionId, final Instant timestamp) {
+ return new Builder(connectionId, timestamp);
+ }
+
+ /**
+ * Constructs a new {@code ConnectionHistoryNotAccessibleException} object with given message.
+ *
+ * @param message detail message. This message can be later retrieved by the {@link #getMessage()} method.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new ConnectionHistoryNotAccessibleException.
+ * @throws NullPointerException if {@code dittoHeaders} is {@code null}.
+ */
+ public static ConnectionHistoryNotAccessibleException fromMessage(@Nullable final String message,
+ final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromMessage(message, dittoHeaders, new Builder());
+ }
+
+ /**
+ * Constructs a new {@code ConnectionHistoryNotAccessibleException} object with the exception message extracted from the given
+ * JSON object.
+ *
+ * @param jsonObject the JSON to read the {@link org.eclipse.ditto.base.model.exceptions.DittoRuntimeException.JsonFields#MESSAGE} field from.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new ConnectionHistoryNotAccessibleException.
+ * @throws NullPointerException if any argument is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static ConnectionHistoryNotAccessibleException fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
+ }
+
+ @Override
+ public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(getMessage())
+ .description(getDescription().orElse(null))
+ .cause(getCause())
+ .href(getHref().orElse(null))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * A mutable builder with a fluent API for a {@link ConnectionHistoryNotAccessibleException}.
+ */
+ @NotThreadSafe
+ public static final class Builder extends DittoRuntimeExceptionBuilder {
+
+ private Builder() {
+ description(DEFAULT_DESCRIPTION);
+ }
+
+ private Builder(final ConnectionId connectionId, final long revision) {
+ this();
+ message(ConnectionHistoryNotAccessibleException.getMessage(connectionId, revision));
+ }
+
+ private Builder(final ConnectionId connectionId, final Instant timestamp) {
+ this();
+ message(ConnectionHistoryNotAccessibleException.getMessage(connectionId, timestamp));
+ }
+
+ @Override
+ protected ConnectionHistoryNotAccessibleException doBuild(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ return new ConnectionHistoryNotAccessibleException(dittoHeaders, message, description, cause, href);
+ }
+
+ }
+
+}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionPreconditionFailedException.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionPreconditionFailedException.java
new file mode 100644
index 00000000000..6746f57942c
--- /dev/null
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionPreconditionFailedException.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.model.signals.commands.exceptions;
+
+import java.net.URI;
+import java.text.MessageFormat;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.eclipse.ditto.base.model.common.HttpStatus;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.connectivity.model.ConnectivityException;
+import org.eclipse.ditto.json.JsonObject;
+
+/**
+ * Thrown when validating a precondition header fails on a Connection or one of its sub-entities.
+ * @since 3.2.0
+ */
+@Immutable
+@JsonParsableException(errorCode = ConnectionPreconditionFailedException.ERROR_CODE)
+public final class ConnectionPreconditionFailedException extends DittoRuntimeException implements
+ ConnectivityException {
+
+ /**
+ * Error code of this exception.
+ */
+ public static final String ERROR_CODE = ERROR_CODE_PREFIX + "precondition.failed";
+
+ private static final String MESSAGE_TEMPLATE =
+ "The comparison of precondition header ''{0}'' for the requested connection resource evaluated to false." +
+ " Header value: ''{1}'', actual entity-tag: ''{2}''.";
+
+ private static final String DEFAULT_DESCRIPTION = "The comparison of the provided precondition header with the " +
+ "current ETag value of the requested connection resource evaluated to false. Check the value of your " +
+ "conditional header value.";
+
+ private ConnectionPreconditionFailedException(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ super(ERROR_CODE, HttpStatus.PRECONDITION_FAILED, dittoHeaders, message, description, cause, href);
+ }
+
+ /**
+ * A mutable builder for a {@link ConnectionPreconditionFailedException}.
+ *
+ * @param conditionalHeaderName the name of the conditional header.
+ * @param expected the expected value.
+ * @param actual the actual ETag value.
+ * @return the builder.
+ */
+ public static Builder newBuilder(final String conditionalHeaderName, final String expected, final String actual) {
+ return new Builder(conditionalHeaderName, expected, actual);
+ }
+
+ /**
+ * Constructs a new {@link ConnectionPreconditionFailedException} object with the exception message extracted from
+ * the given JSON object.
+ *
+ * @param jsonObject the JSON to read the {@link org.eclipse.ditto.base.model.exceptions.DittoRuntimeException.JsonFields#MESSAGE} field from.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new {@link ConnectionPreconditionFailedException}.
+ * @throws NullPointerException if any argument is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static ConnectionPreconditionFailedException fromJson(final JsonObject jsonObject,
+ final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
+ }
+
+ @Override
+ public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(getMessage())
+ .description(getDescription().orElse(null))
+ .cause(getCause())
+ .href(getHref().orElse(null))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * A mutable builder with a fluent API for a {@link ConnectionPreconditionFailedException}.
+ */
+ @NotThreadSafe
+ public static final class Builder
+ extends DittoRuntimeExceptionBuilder {
+
+ private Builder() {
+ description(DEFAULT_DESCRIPTION);
+ }
+
+ private Builder(final String conditionalHeaderName, final String expected, final String actual) {
+ this();
+ message(MessageFormat.format(MESSAGE_TEMPLATE, conditionalHeaderName, expected, actual));
+ }
+
+ @Override
+ protected ConnectionPreconditionFailedException doBuild(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ return new ConnectionPreconditionFailedException(dittoHeaders, message, description, cause, href);
+ }
+
+ }
+}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionPreconditionNotModifiedException.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionPreconditionNotModifiedException.java
new file mode 100644
index 00000000000..dfaeaf5e828
--- /dev/null
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/exceptions/ConnectionPreconditionNotModifiedException.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.model.signals.commands.exceptions;
+
+import java.net.URI;
+import java.text.MessageFormat;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.eclipse.ditto.base.model.common.HttpStatus;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.JsonParsableException;
+import org.eclipse.ditto.connectivity.model.ConnectivityException;
+import org.eclipse.ditto.json.JsonObject;
+
+/**
+ * Thrown when validating a precondition header on a connection or one of its sub-entities leads to status
+ * {@link org.eclipse.ditto.base.model.common.HttpStatus#NOT_MODIFIED}.
+ */
+@Immutable
+@JsonParsableException(errorCode = ConnectionPreconditionNotModifiedException.ERROR_CODE)
+public final class ConnectionPreconditionNotModifiedException extends DittoRuntimeException
+ implements ConnectivityException {
+
+ /**
+ * Error code of this exception.
+ */
+ public static final String ERROR_CODE = ERROR_CODE_PREFIX + "precondition.notmodified";
+
+ private static final String MESSAGE_TEMPLATE =
+ "The comparison of precondition header ''if-none-match'' for the requested connection resource evaluated to " +
+ "false. Expected: ''{0}'' not to match actual: ''{1}''.";
+
+ private static final String DEFAULT_DESCRIPTION =
+ "The comparison of the provided precondition header ''if-none-match'' with the current ETag value of the " +
+ "requested connection resource evaluated to false. Check the value of your conditional header value.";
+
+ private ConnectionPreconditionNotModifiedException(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ super(ERROR_CODE, HttpStatus.NOT_MODIFIED, dittoHeaders, message, description, cause, href);
+ }
+
+ /**
+ * A mutable builder for a {@link ConnectionPreconditionNotModifiedException}.
+ *
+ * @return the builder.
+ * @since 3.3.0
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * A mutable builder for a {@link ConnectionPreconditionNotModifiedException}.
+ *
+ * @param expectedNotToMatch the value which was expected not to match {@code matched} value.
+ * @param matched the matched value.
+ * @return the builder.
+ */
+ public static Builder newBuilder(final String expectedNotToMatch, final String matched) {
+ return new Builder(expectedNotToMatch, matched);
+ }
+
+ /**
+ * Constructs a new {@link ConnectionPreconditionNotModifiedException} object with the exception message extracted from
+ * the given JSON object.
+ *
+ * @param jsonObject the JSON to read the
+ * {@link org.eclipse.ditto.base.model.exceptions.DittoRuntimeException.JsonFields#MESSAGE} field from.
+ * @param dittoHeaders the headers of the command which resulted in this exception.
+ * @return the new ConditionalHeadersNotModifiedException.
+ * @throws NullPointerException if any argument is {@code null}.
+ * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message.
+ * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the expected
+ * format.
+ */
+ public static ConnectionPreconditionNotModifiedException fromJson(final JsonObject jsonObject,
+ final DittoHeaders dittoHeaders) {
+ return DittoRuntimeException.fromJson(jsonObject, dittoHeaders, new Builder());
+ }
+
+ @Override
+ public DittoRuntimeException setDittoHeaders(final DittoHeaders dittoHeaders) {
+ return new Builder()
+ .message(getMessage())
+ .description(getDescription().orElse(null))
+ .cause(getCause())
+ .href(getHref().orElse(null))
+ .dittoHeaders(dittoHeaders)
+ .build();
+ }
+
+ /**
+ * A mutable builder with a fluent API for a {@link ConnectionPreconditionNotModifiedException}.
+ */
+ @NotThreadSafe
+ public static final class Builder extends DittoRuntimeExceptionBuilder {
+
+ private Builder() {
+ description(DEFAULT_DESCRIPTION);
+ }
+
+ private Builder(final String expectedNotToMatch, final String matched) {
+ this();
+ message(MessageFormat.format(MESSAGE_TEMPLATE, expectedNotToMatch, matched));
+ }
+
+ @Override
+ protected ConnectionPreconditionNotModifiedException doBuild(final DittoHeaders dittoHeaders,
+ @Nullable final String message,
+ @Nullable final String description,
+ @Nullable final Throwable cause,
+ @Nullable final URI href) {
+ return new ConnectionPreconditionNotModifiedException(dittoHeaders, message, description, cause, href);
+ }
+
+ }
+}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnection.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnection.java
index bd9e0c25df1..dfafac737c3 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnection.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnection.java
@@ -15,24 +15,29 @@
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
-import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
-import org.eclipse.ditto.json.JsonFactory;
-import org.eclipse.ditto.json.JsonField;
-import org.eclipse.ditto.json.JsonObject;
-import org.eclipse.ditto.json.JsonObjectBuilder;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.FieldType;
import org.eclipse.ditto.base.model.json.JsonParsableCommand;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
-import org.eclipse.ditto.connectivity.model.ConnectionId;
-import org.eclipse.ditto.connectivity.model.WithConnectionId;
import org.eclipse.ditto.base.model.signals.SignalWithEntityId;
import org.eclipse.ditto.base.model.signals.commands.AbstractCommand;
import org.eclipse.ditto.base.model.signals.commands.CommandJsonDeserializer;
+import org.eclipse.ditto.base.model.signals.commands.WithSelectedFields;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
+import org.eclipse.ditto.connectivity.model.WithConnectionId;
+import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
+import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonFieldSelector;
+import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
/**
* Command which retrieves a {@link org.eclipse.ditto.connectivity.model.Connection}.
@@ -40,7 +45,8 @@
@Immutable
@JsonParsableCommand(typePrefix = ConnectivityCommand.TYPE_PREFIX, name = RetrieveConnection.NAME)
public final class RetrieveConnection extends AbstractCommand
- implements ConnectivityQueryCommand, WithConnectionId, SignalWithEntityId {
+ implements ConnectivityQueryCommand, WithConnectionId, WithSelectedFields,
+ SignalWithEntityId {
/**
* Name of this command.
@@ -52,11 +58,19 @@ public final class RetrieveConnection extends AbstractCommand JSON_SELECTED_FIELDS =
+ JsonFactory.newStringFieldDefinition("selectedFields", FieldType.REGULAR,
+ JsonSchemaVersion.V_2);
+
private final ConnectionId connectionId;
+ @Nullable private final JsonFieldSelector selectedFields;
- private RetrieveConnection(final ConnectionId connectionId, final DittoHeaders dittoHeaders) {
+ private RetrieveConnection(final ConnectionId connectionId,
+ @Nullable final JsonFieldSelector selectedFields,
+ final DittoHeaders dittoHeaders) {
super(TYPE, dittoHeaders);
this.connectionId = connectionId;
+ this.selectedFields = selectedFields;
}
/**
@@ -69,7 +83,23 @@ private RetrieveConnection(final ConnectionId connectionId, final DittoHeaders d
*/
public static RetrieveConnection of(final ConnectionId connectionId, final DittoHeaders dittoHeaders) {
checkNotNull(connectionId, "Connection ID");
- return new RetrieveConnection(connectionId, dittoHeaders);
+ return new RetrieveConnection(connectionId, null, dittoHeaders);
+ }
+
+ /**
+ * Returns a new instance of {@code RetrieveConnection}.
+ *
+ * @param connectionId the identifier of the connection to be retrieved.
+ * @param selectedFields the fields of the JSON representation of the Connection to retrieve.
+ * @param dittoHeaders the headers of the request.
+ * @return a new RetrieveConnection command.
+ * @throws NullPointerException if any argument is {@code null}.
+ */
+ public static RetrieveConnection of(final ConnectionId connectionId,
+ @Nullable final JsonFieldSelector selectedFields,
+ final DittoHeaders dittoHeaders) {
+ checkNotNull(connectionId, "Connection ID");
+ return new RetrieveConnection(connectionId, selectedFields, dittoHeaders);
}
/**
@@ -101,8 +131,12 @@ public static RetrieveConnection fromJson(final JsonObject jsonObject, final Dit
return new CommandJsonDeserializer(TYPE, jsonObject).deserialize(() -> {
final String readConnectionId = jsonObject.getValueOrThrow(ConnectivityCommand.JsonFields.JSON_CONNECTION_ID);
final ConnectionId connectionId = ConnectionId.of(readConnectionId);
+ final Optional selectedFields = jsonObject.getValue(JSON_SELECTED_FIELDS)
+ .map(str -> JsonFactory.newFieldSelector(str, JsonFactory.newParseOptionsBuilder()
+ .withoutUrlDecoding()
+ .build()));
- return of(connectionId, dittoHeaders);
+ return of(connectionId, selectedFields.orElse(null), dittoHeaders);
});
}
@@ -113,6 +147,9 @@ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder, final Js
final Predicate predicate = schemaVersion.and(thePredicate);
jsonObjectBuilder.set(ConnectivityCommand.JsonFields.JSON_CONNECTION_ID, String.valueOf(connectionId),
predicate);
+ if (null != selectedFields) {
+ jsonObjectBuilder.set(JSON_SELECTED_FIELDS, selectedFields.toString(), predicate);
+ }
}
@Override
@@ -125,9 +162,14 @@ public Category getCategory() {
return Category.QUERY;
}
+ @Override
+ public Optional getSelectedFields() {
+ return Optional.ofNullable(selectedFields);
+ }
+
@Override
public RetrieveConnection setDittoHeaders(final DittoHeaders dittoHeaders) {
- return of(connectionId, dittoHeaders);
+ return of(connectionId, selectedFields, dittoHeaders);
}
@Override
@@ -147,12 +189,13 @@ public boolean equals(@Nullable final Object o) {
return false;
}
final RetrieveConnection that = (RetrieveConnection) o;
- return Objects.equals(connectionId, that.connectionId);
+ return Objects.equals(connectionId, that.connectionId) &&
+ Objects.equals(selectedFields, that.selectedFields);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), connectionId);
+ return Objects.hash(super.hashCode(), connectionId, selectedFields);
}
@Override
@@ -160,6 +203,7 @@ public String toString() {
return getClass().getSimpleName() + " [" +
super.toString() +
", connectionId=" + connectionId +
+ ", selectedFields=" + selectedFields +
"]";
}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnections.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnections.java
index 319d1edd04b..aab45f4854f 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnections.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnections.java
@@ -13,6 +13,7 @@
package org.eclipse.ditto.connectivity.model.signals.commands.query;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Predicate;
import javax.annotation.Nullable;
@@ -23,10 +24,12 @@
import org.eclipse.ditto.base.model.json.JsonParsableCommand;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.base.model.signals.commands.AbstractCommand;
+import org.eclipse.ditto.base.model.signals.commands.WithSelectedFields;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonField;
import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonObjectBuilder;
import org.eclipse.ditto.json.JsonPointer;
@@ -40,7 +43,7 @@
@Immutable
@JsonParsableCommand(typePrefix = ConnectivityCommand.TYPE_PREFIX, name = RetrieveConnections.NAME)
public final class RetrieveConnections extends AbstractCommand
- implements ConnectivityQueryCommand {
+ implements ConnectivityQueryCommand, WithSelectedFields {
/**
* Name of the "Retrieve Connections" command.
@@ -54,11 +57,19 @@ public final class RetrieveConnections extends AbstractCommand JSON_IDS_ONLY =
JsonFactory.newBooleanFieldDefinition("idsOnly", FieldType.REGULAR, JsonSchemaVersion.V_2);
+ static final JsonFieldDefinition JSON_SELECTED_FIELDS =
+ JsonFactory.newStringFieldDefinition("selectedFields", FieldType.REGULAR,
+ JsonSchemaVersion.V_2);
+
private final boolean idsOnly;
+ @Nullable private final JsonFieldSelector selectedFields;
- private RetrieveConnections(final boolean idsOnly, final DittoHeaders dittoHeaders) {
+ private RetrieveConnections(final boolean idsOnly,
+ @Nullable final JsonFieldSelector selectedFields,
+ final DittoHeaders dittoHeaders) {
super(TYPE, dittoHeaders);
this.idsOnly = idsOnly;
+ this.selectedFields = selectedFields;
}
/**
@@ -70,7 +81,22 @@ private RetrieveConnections(final boolean idsOnly, final DittoHeaders dittoHeade
*/
public static RetrieveConnections newInstance(final boolean idsOnly, final DittoHeaders dittoHeaders) {
- return new RetrieveConnections(idsOnly, dittoHeaders);
+ return new RetrieveConnections(idsOnly, null, dittoHeaders);
+ }
+
+ /**
+ * Returns a new instance of the retrieve connections command.
+ *
+ * @param dittoHeaders provide additional information regarding connections retrieval like a correlation ID.
+ * @param selectedFields the fields of the JSON representation of the Connection to retrieve.
+ * @return the instance.
+ * @throws NullPointerException if any argument is {@code null}.
+ */
+ public static RetrieveConnections newInstance(final boolean idsOnly,
+ @Nullable final JsonFieldSelector selectedFields,
+ final DittoHeaders dittoHeaders) {
+
+ return new RetrieveConnections(idsOnly, selectedFields, dittoHeaders);
}
/**
@@ -100,8 +126,13 @@ public static RetrieveConnections fromJson(final String jsonString, final DittoH
*/
public static RetrieveConnections fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
final boolean idsOnly = jsonObject.getValueOrThrow(JSON_IDS_ONLY);
+ final JsonFieldSelector extractedFieldSelector = jsonObject.getValue(JSON_SELECTED_FIELDS)
+ .map(str -> JsonFactory.newFieldSelector(str, JsonFactory.newParseOptionsBuilder()
+ .withoutUrlDecoding()
+ .build()))
+ .orElse(null);
- return new RetrieveConnections(idsOnly, dittoHeaders);
+ return new RetrieveConnections(idsOnly, extractedFieldSelector, dittoHeaders);
}
/**
@@ -113,6 +144,11 @@ public boolean getIdsOnly() {
return idsOnly;
}
+ @Override
+ public Optional getSelectedFields() {
+ return Optional.ofNullable(selectedFields);
+ }
+
@Override
public Category getCategory() {
return Category.QUERY;
@@ -120,7 +156,7 @@ public Category getCategory() {
@Override
public RetrieveConnections setDittoHeaders(final DittoHeaders dittoHeaders) {
- return new RetrieveConnections(idsOnly, dittoHeaders);
+ return new RetrieveConnections(idsOnly, selectedFields, dittoHeaders);
}
@Override
@@ -134,6 +170,9 @@ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder, final Js
final Predicate predicate = jsonSchemaVersion.and(thePredicate);
jsonObjectBuilder.set(JSON_IDS_ONLY, idsOnly, predicate);
+ if (null != selectedFields) {
+ jsonObjectBuilder.set(JSON_SELECTED_FIELDS, selectedFields.toString(), predicate);
+ }
}
@Override
@@ -148,12 +187,13 @@ public boolean equals(@Nullable final Object o) {
return false;
}
final RetrieveConnections that = (RetrieveConnections) o;
- return Objects.equals(idsOnly, that.idsOnly);
+ return Objects.equals(idsOnly, that.idsOnly) &&
+ Objects.equals(selectedFields, that.selectedFields);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), idsOnly);
+ return Objects.hash(super.hashCode(), idsOnly, selectedFields);
}
@Override
@@ -161,6 +201,7 @@ public String toString() {
return getClass().getSimpleName() + " [" +
super.toString() +
", idsOnly=" + idsOnly +
+ ", selectedFields=" + selectedFields +
"]";
}
diff --git a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnection.java b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnection.java
index f4ba9a53678..54441b71ece 100644
--- a/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnection.java
+++ b/connectivity/model/src/main/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnection.java
@@ -15,22 +15,27 @@
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.json.FieldType;
import org.eclipse.ditto.base.model.json.JsonParsableCommand;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.base.model.signals.SignalWithEntityId;
import org.eclipse.ditto.base.model.signals.commands.AbstractCommand;
import org.eclipse.ditto.base.model.signals.commands.CommandJsonDeserializer;
+import org.eclipse.ditto.base.model.signals.commands.WithSelectedFields;
import org.eclipse.ditto.connectivity.model.ConnectionId;
import org.eclipse.ditto.connectivity.model.WithConnectionId;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonField;
+import org.eclipse.ditto.json.JsonFieldDefinition;
+import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonObjectBuilder;
@@ -43,7 +48,8 @@
@Immutable
@JsonParsableCommand(typePrefix = ConnectivityCommand.TYPE_PREFIX, name = RetrieveResolvedHonoConnection.NAME)
public final class RetrieveResolvedHonoConnection extends AbstractCommand
- implements ConnectivityQueryCommand, WithConnectionId, SignalWithEntityId {
+ implements ConnectivityQueryCommand, WithConnectionId, WithSelectedFields,
+ SignalWithEntityId {
/**
* Name of this command.
@@ -55,11 +61,19 @@ public final class RetrieveResolvedHonoConnection extends AbstractCommand JSON_SELECTED_FIELDS =
+ JsonFactory.newStringFieldDefinition("selectedFields", FieldType.REGULAR,
+ JsonSchemaVersion.V_2);
+
private final ConnectionId connectionId;
+ @Nullable private final JsonFieldSelector selectedFields;
- private RetrieveResolvedHonoConnection(final ConnectionId connectionId, final DittoHeaders dittoHeaders) {
+ private RetrieveResolvedHonoConnection(final ConnectionId connectionId,
+ @Nullable final JsonFieldSelector selectedFields,
+ final DittoHeaders dittoHeaders) {
super(TYPE, dittoHeaders);
this.connectionId = connectionId;
+ this.selectedFields = selectedFields;
}
/**
@@ -72,7 +86,23 @@ private RetrieveResolvedHonoConnection(final ConnectionId connectionId, final Di
*/
public static RetrieveResolvedHonoConnection of(final ConnectionId connectionId, final DittoHeaders dittoHeaders) {
checkNotNull(connectionId, "connectionId");
- return new RetrieveResolvedHonoConnection(connectionId, dittoHeaders);
+ return new RetrieveResolvedHonoConnection(connectionId, null, dittoHeaders);
+ }
+
+ /**
+ * Returns a new instance of {@code RetrieveResolvedHonoConnection}.
+ *
+ * @param connectionId the identifier of the connection to be retrieved.
+ * @param selectedFields the fields of the JSON representation of the HonoConnection to retrieve.
+ * @param dittoHeaders the headers of the request.
+ * @return a new RetrieveResolvedHonoConnection command.
+ * @throws NullPointerException if any argument is {@code null}.
+ */
+ public static RetrieveResolvedHonoConnection of(final ConnectionId connectionId,
+ @Nullable final JsonFieldSelector selectedFields,
+ final DittoHeaders dittoHeaders) {
+ checkNotNull(connectionId, "Connection ID");
+ return new RetrieveResolvedHonoConnection(connectionId, selectedFields, dittoHeaders);
}
/**
@@ -104,8 +134,12 @@ public static RetrieveResolvedHonoConnection fromJson(final JsonObject jsonObjec
return new CommandJsonDeserializer(TYPE, jsonObject).deserialize(() -> {
final String readConnectionId = jsonObject.getValueOrThrow(ConnectivityCommand.JsonFields.JSON_CONNECTION_ID);
final ConnectionId connectionId = ConnectionId.of(readConnectionId);
+ final Optional selectedFields = jsonObject.getValue(JSON_SELECTED_FIELDS)
+ .map(str -> JsonFactory.newFieldSelector(str, JsonFactory.newParseOptionsBuilder()
+ .withoutUrlDecoding()
+ .build()));
- return of(connectionId, dittoHeaders);
+ return of(connectionId, selectedFields.orElse(null), dittoHeaders);
});
}
@@ -116,6 +150,9 @@ protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder, final Js
final Predicate predicate = schemaVersion.and(thePredicate);
jsonObjectBuilder.set(ConnectivityCommand.JsonFields.JSON_CONNECTION_ID, String.valueOf(connectionId),
predicate);
+ if (null != selectedFields) {
+ jsonObjectBuilder.set(JSON_SELECTED_FIELDS, selectedFields.toString(), predicate);
+ }
}
@Override
@@ -128,9 +165,14 @@ public Category getCategory() {
return Category.QUERY;
}
+ @Override
+ public Optional getSelectedFields() {
+ return Optional.ofNullable(selectedFields);
+ }
+
@Override
public RetrieveResolvedHonoConnection setDittoHeaders(final DittoHeaders dittoHeaders) {
- return of(connectionId, dittoHeaders);
+ return of(connectionId, selectedFields, dittoHeaders);
}
@Override
@@ -150,12 +192,13 @@ public boolean equals(@Nullable final Object o) {
return false;
}
final RetrieveResolvedHonoConnection that = (RetrieveResolvedHonoConnection) o;
- return Objects.equals(connectionId, that.connectionId);
+ return Objects.equals(connectionId, that.connectionId) &&
+ Objects.equals(selectedFields, that.selectedFields);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), connectionId);
+ return Objects.hash(super.hashCode(), connectionId, selectedFields);
}
@Override
@@ -163,6 +206,7 @@ public String toString() {
return getClass().getSimpleName() + " [" +
super.toString() +
", connectionId=" + connectionId +
+ ", selectedFields=" + selectedFields +
"]";
}
diff --git a/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionTest.java b/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionTest.java
index 1137132cc16..eca9da01802 100644
--- a/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionTest.java
+++ b/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionTest.java
@@ -24,6 +24,7 @@
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.connectivity.model.signals.commands.TestConstants;
import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonPointer;
import org.eclipse.ditto.json.assertions.DittoJsonAssertions;
@@ -52,7 +53,7 @@ public void testHashCodeAndEquals() {
public void assertImmutability() {
assertInstancesOf(RetrieveConnection.class,
areImmutable(),
- provided(ConnectionId.class).isAlsoImmutable());
+ provided(ConnectionId.class, JsonFieldSelector.class).isAlsoImmutable());
}
@Test
diff --git a/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionsTest.java b/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionsTest.java
index 375d243bf80..8d70bccc83f 100644
--- a/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionsTest.java
+++ b/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveConnectionsTest.java
@@ -15,6 +15,7 @@
import static org.eclipse.ditto.json.assertions.DittoJsonAssertions.assertThat;
import static org.mutabilitydetector.unittesting.AllowedReason.assumingFields;
+import static org.mutabilitydetector.unittesting.AllowedReason.provided;
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;
@@ -28,6 +29,7 @@
import org.eclipse.ditto.base.model.signals.commands.GlobalCommandRegistry;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonPointer;
import org.eclipse.ditto.json.assertions.DittoJsonAssertions;
@@ -72,7 +74,8 @@ public void setUp() {
public void assertImmutability() {
assertInstancesOf(RetrieveConnections.class,
areImmutable(),
- assumingFields("connectionIds").areSafelyCopiedUnmodifiableCollectionsWithImmutableElements());
+ assumingFields("connectionIds").areSafelyCopiedUnmodifiableCollectionsWithImmutableElements(),
+ provided(JsonFieldSelector.class).isAlsoImmutable());
}
@Test
diff --git a/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnectionTest.java b/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnectionTest.java
index 94bd12fb22a..5018d59d8af 100644
--- a/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnectionTest.java
+++ b/connectivity/model/src/test/java/org/eclipse/ditto/connectivity/model/signals/commands/query/RetrieveResolvedHonoConnectionTest.java
@@ -27,6 +27,7 @@
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.connectivity.model.signals.commands.TestConstants;
import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonPointer;
import org.eclipse.ditto.json.assertions.DittoJsonAssertions;
@@ -55,7 +56,7 @@ public void testHashCodeAndEquals() {
public void assertImmutability() {
assertInstancesOf(RetrieveResolvedHonoConnection.class,
areImmutable(),
- provided(ConnectionId.class).isAlsoImmutable());
+ provided(ConnectionId.class, JsonFieldSelector.class).isAlsoImmutable());
}
@Test
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/ConnectivityRootActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/ConnectivityRootActor.java
index 899a587fd66..9a3357892a9 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/ConnectivityRootActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/ConnectivityRootActor.java
@@ -83,8 +83,6 @@ private ConnectivityRootActor(final ConnectivityConfig connectivityConfig,
final var dittoExtensionsConfig = ScopedConfig.dittoExtension(actorSystem.settings().config());
final var enforcerActorPropsFactory =
ConnectionEnforcerActorPropsFactory.get(actorSystem, dittoExtensionsConfig);
- final var connectionSupervisorProps =
- ConnectionSupervisorActor.props(commandForwarder, pubSubMediator, enforcerActorPropsFactory);
// Create persistence streaming actor (with no cache) and make it known to pubSubMediator.
final ActorRef persistenceStreamingActor =
startChildActor(ConnectionPersistenceStreamingActorCreator.ACTOR_NAME,
@@ -100,6 +98,10 @@ private ConnectivityRootActor(final ConnectivityConfig connectivityConfig,
DittoProtocolSub.get(actorSystem);
final MongoReadJournal mongoReadJournal = MongoReadJournal.newInstance(actorSystem);
+
+ final var connectionSupervisorProps =
+ ConnectionSupervisorActor.props(commandForwarder, pubSubMediator, enforcerActorPropsFactory,
+ mongoReadJournal);
startClusterSingletonActor(
PersistencePingActor.props(
startConnectionShardRegion(actorSystem, connectionSupervisorProps, clusterConfig),
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/ConnectionConfig.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/ConnectionConfig.java
index 04c61b58f74..b23934471b1 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/ConnectionConfig.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/ConnectionConfig.java
@@ -20,15 +20,18 @@
import org.eclipse.ditto.base.service.config.supervision.WithSupervisorConfig;
import org.eclipse.ditto.edge.service.acknowledgements.AcknowledgementConfig;
import org.eclipse.ditto.internal.utils.config.KnownConfigValue;
+import org.eclipse.ditto.internal.utils.persistence.mongo.config.EventConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.SnapshotConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.WithActivityCheckConfig;
+import org.eclipse.ditto.internal.utils.persistence.mongo.config.WithSnapshotConfig;
import org.eclipse.ditto.internal.utils.persistentactors.cleanup.WithCleanupConfig;
/**
* Provides configuration settings for Connectivity service's connection behaviour.
*/
@Immutable
-public interface ConnectionConfig extends WithSupervisorConfig, WithActivityCheckConfig, WithCleanupConfig {
+public interface ConnectionConfig extends WithSupervisorConfig, WithActivityCheckConfig, WithCleanupConfig,
+ WithSnapshotConfig {
/**
* Returns the amount of time for how long the connection actor waits for response from client actors.
@@ -75,6 +78,13 @@ public interface ConnectionConfig extends WithSupervisorConfig, WithActivityChec
*/
SnapshotConfig getSnapshotConfig();
+ /**
+ * Returns the config of the connection event journal behaviour.
+ *
+ * @return the config.
+ */
+ EventConfig getEventConfig();
+
/**
* Returns the maximum number of Targets within a connection.
*
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/DefaultConnectionConfig.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/DefaultConnectionConfig.java
index b30128e9885..f06c6971097 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/DefaultConnectionConfig.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/config/DefaultConnectionConfig.java
@@ -25,7 +25,9 @@
import org.eclipse.ditto.internal.utils.config.ConfigWithFallback;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.ActivityCheckConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.DefaultActivityCheckConfig;
+import org.eclipse.ditto.internal.utils.persistence.mongo.config.DefaultEventConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.DefaultSnapshotConfig;
+import org.eclipse.ditto.internal.utils.persistence.mongo.config.EventConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.SnapshotConfig;
import org.eclipse.ditto.internal.utils.persistentactors.cleanup.CleanupConfig;
@@ -47,6 +49,7 @@ public final class DefaultConnectionConfig implements ConnectionConfig {
private final String blockedHostRegex;
private final SupervisorConfig supervisorConfig;
private final SnapshotConfig snapshotConfig;
+ private final EventConfig eventConfig;
private final DefaultAcknowledgementConfig acknowledgementConfig;
private final CleanupConfig cleanupConfig;
private final Amqp10Config amqp10Config;
@@ -74,6 +77,7 @@ private DefaultConnectionConfig(final ConfigWithFallback config) {
blockedHostRegex = config.getString(ConnectionConfigValue.BLOCKED_HOST_REGEX.getConfigPath());
supervisorConfig = DefaultSupervisorConfig.of(config);
snapshotConfig = DefaultSnapshotConfig.of(config);
+ eventConfig = DefaultEventConfig.of(config);
acknowledgementConfig = DefaultAcknowledgementConfig.of(config);
cleanupConfig = CleanupConfig.of(config);
amqp10Config = DefaultAmqp10Config.of(config);
@@ -153,6 +157,11 @@ public SnapshotConfig getSnapshotConfig() {
return snapshotConfig;
}
+ @Override
+ public EventConfig getEventConfig() {
+ return eventConfig;
+ }
+
@Override
public Integer getMaxNumberOfTargets() {
return maxNumberOfTargets;
@@ -246,6 +255,7 @@ public boolean equals(final Object o) {
Objects.equals(blockedHostRegex, that.blockedHostRegex) &&
Objects.equals(supervisorConfig, that.supervisorConfig) &&
Objects.equals(snapshotConfig, that.snapshotConfig) &&
+ Objects.equals(eventConfig, that.eventConfig) &&
Objects.equals(acknowledgementConfig, that.acknowledgementConfig) &&
Objects.equals(cleanupConfig, that.cleanupConfig) &&
Objects.equals(amqp10Config, that.amqp10Config) &&
@@ -266,7 +276,7 @@ public boolean equals(final Object o) {
@Override
public int hashCode() {
return Objects.hash(clientActorAskTimeout, clientActorRestartsBeforeEscalation, allowedHostnames,
- blockedHostnames, blockedSubnets, blockedHostRegex, supervisorConfig, snapshotConfig,
+ blockedHostnames, blockedSubnets, blockedHostRegex, supervisorConfig, snapshotConfig, eventConfig,
acknowledgementConfig, cleanupConfig, maxNumberOfTargets, maxNumberOfSources, activityCheckConfig,
fieldsEncryptionConfig, amqp10Config, amqp091Config, mqttConfig, kafkaConfig, httpPushConfig,
ackLabelDeclareInterval, priorityUpdateInterval, shutdownTimeout, allClientActorsOnOneNode);
@@ -283,6 +293,7 @@ public String toString() {
", blockedHostRegex=" + blockedHostRegex +
", supervisorConfig=" + supervisorConfig +
", snapshotConfig=" + snapshotConfig +
+ ", eventConfig=" + eventConfig +
", acknowledgementConfig=" + acknowledgementConfig +
", cleanUpConfig=" + cleanupConfig +
", amqp10Config=" + amqp10Config +
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/ConnectionExistenceChecker.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/ConnectionExistenceChecker.java
new file mode 100644
index 00000000000..24fa1f91095
--- /dev/null
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/ConnectionExistenceChecker.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.service.enforcement.pre;
+
+import java.util.concurrent.CompletionStage;
+
+import org.eclipse.ditto.connectivity.api.ConnectivityMessagingConstants;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
+import org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnection;
+import org.eclipse.ditto.internal.utils.cache.entry.Entry;
+import org.eclipse.ditto.internal.utils.cluster.ShardRegionProxyActorFactory;
+import org.eclipse.ditto.internal.utils.cluster.config.DefaultClusterConfig;
+import org.eclipse.ditto.internal.utils.config.DefaultScopedConfig;
+import org.eclipse.ditto.policies.enforcement.config.DefaultEnforcementConfig;
+import org.eclipse.ditto.policies.enforcement.config.EnforcementConfig;
+
+import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+
+/**
+ * checks for the existence of connections.
+ */
+final class ConnectionExistenceChecker {
+
+ public static final String ENFORCEMENT_CACHE_DISPATCHER = "enforcement-cache-dispatcher";
+
+ private final AsyncCacheLoader> connectionIdLoader;
+ private final ActorSystem actorSystem;
+
+ ConnectionExistenceChecker(final ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ final var enforcementConfig = DefaultEnforcementConfig.of(
+ DefaultScopedConfig.dittoScoped(actorSystem.settings().config()));
+ connectionIdLoader = getConnectionIdLoader(actorSystem, enforcementConfig);
+ }
+
+ private AsyncCacheLoader> getConnectionIdLoader(
+ final ActorSystem actorSystem,
+ final EnforcementConfig enforcementConfig) {
+
+ final var clusterConfig = DefaultClusterConfig.of(actorSystem.settings().config().getConfig("ditto.cluster"));
+ final ShardRegionProxyActorFactory shardRegionProxyActorFactory =
+ ShardRegionProxyActorFactory.newInstance(actorSystem, clusterConfig);
+
+ final ActorRef connectionShardRegion = shardRegionProxyActorFactory.getShardRegionProxyActor(
+ ConnectivityMessagingConstants.CLUSTER_ROLE, ConnectivityMessagingConstants.SHARD_REGION);
+ return new PreEnforcementConnectionIdCacheLoader(enforcementConfig.getAskWithRetryConfig(),
+ actorSystem.getScheduler(),
+ connectionShardRegion);
+ }
+
+ public CompletionStage checkExistence(final ModifyConnection signal) {
+ try {
+ return connectionIdLoader.asyncLoad(signal.getEntityId(),
+ actorSystem.dispatchers().lookup(ENFORCEMENT_CACHE_DISPATCHER))
+ .thenApply(Entry::exists);
+ } catch (final Exception e) {
+ throw new IllegalStateException("Could not load connection via connectionIdLoader", e);
+ }
+ }
+
+}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/ModifyToCreateConnectionTransformer.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/ModifyToCreateConnectionTransformer.java
new file mode 100644
index 00000000000..5d713fe5781
--- /dev/null
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/ModifyToCreateConnectionTransformer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.service.enforcement.pre;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+import org.eclipse.ditto.base.model.signals.Signal;
+import org.eclipse.ditto.base.service.signaltransformer.SignalTransformer;
+import org.eclipse.ditto.connectivity.model.signals.commands.modify.CreateConnection;
+import org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnection;
+
+import com.typesafe.config.Config;
+
+import akka.actor.ActorSystem;
+
+/**
+ * Transforms a ModifyConnection into a CreateConnection if the connection does not exist already.
+ */
+public final class ModifyToCreateConnectionTransformer implements SignalTransformer {
+
+ private final ConnectionExistenceChecker existenceChecker;
+
+ @SuppressWarnings("unused")
+ ModifyToCreateConnectionTransformer(final ActorSystem actorSystem, final Config config) {
+ this(new ConnectionExistenceChecker(actorSystem));
+ }
+
+ ModifyToCreateConnectionTransformer(final ConnectionExistenceChecker existenceChecker) {
+ this.existenceChecker = existenceChecker;
+ }
+
+ @Override
+ public CompletionStage> apply(final Signal> signal) {
+ if (signal instanceof ModifyConnection modifyConnection) {
+ return existenceChecker.checkExistence(modifyConnection)
+ .thenApply(exists -> {
+ if (Boolean.FALSE.equals(exists)) {
+ return CreateConnection.of(
+ modifyConnection.getConnection(),
+ modifyConnection.getDittoHeaders()
+ );
+ } else {
+ return modifyConnection;
+ }
+ });
+ } else {
+ return CompletableFuture.completedStage(signal);
+ }
+ }
+
+}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/PreEnforcementConnectionIdCacheLoader.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/PreEnforcementConnectionIdCacheLoader.java
new file mode 100644
index 00000000000..8968c77b109
--- /dev/null
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/PreEnforcementConnectionIdCacheLoader.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.service.enforcement.pre;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.signals.commands.Command;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
+import org.eclipse.ditto.connectivity.model.ConnectivityConstants;
+import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionNotAccessibleException;
+import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection;
+import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionResponse;
+import org.eclipse.ditto.internal.utils.cache.entry.Entry;
+import org.eclipse.ditto.internal.utils.cacheloaders.ActorAskCacheLoader;
+import org.eclipse.ditto.internal.utils.cacheloaders.config.AskWithRetryConfig;
+import org.eclipse.ditto.json.JsonFieldSelector;
+
+import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
+
+import akka.actor.ActorRef;
+import akka.actor.Scheduler;
+
+/**
+ * Cache loader used for Connection existence check in pre-enforcement.
+ */
+final class PreEnforcementConnectionIdCacheLoader implements
+ AsyncCacheLoader> {
+
+ private final ActorAskCacheLoader, ConnectionId> delegate;
+
+ /**
+ * Constructor.
+ *
+ * @param askWithRetryConfig the configuration for the "ask with retry" pattern applied for the cache loader.
+ * @param scheduler the scheduler to use for the "ask with retry" for retries.
+ * @param shardRegionProxy the shard-region-proxy.
+ */
+ public PreEnforcementConnectionIdCacheLoader(final AskWithRetryConfig askWithRetryConfig,
+ final Scheduler scheduler,
+ final ActorRef shardRegionProxy) {
+
+ delegate = ActorAskCacheLoader.forShard(askWithRetryConfig,
+ scheduler,
+ ConnectivityConstants.ENTITY_TYPE,
+ shardRegionProxy,
+ connectionId -> RetrieveConnection.of(connectionId, JsonFieldSelector.newInstance("id"), DittoHeaders.empty()),
+ PreEnforcementConnectionIdCacheLoader::handleRetrieveConnectionResponse);
+ }
+
+ @Override
+ public CompletableFuture> asyncLoad(final ConnectionId key, final Executor executor) {
+ return delegate.asyncLoad(key, executor);
+ }
+
+ private static Entry handleRetrieveConnectionResponse(final Object response) {
+
+ if (response instanceof RetrieveConnectionResponse retrieveConnectionResponse) {
+ final ConnectionId connectionId = retrieveConnectionResponse.getEntityId();
+ return Entry.of(-1, connectionId);
+ } else if (response instanceof ConnectionNotAccessibleException) {
+ return Entry.nonexistent();
+ } else {
+ throw new IllegalStateException("expect RetrieveConnectionResponse, got: " + response);
+ }
+ }
+
+}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/package-info.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/package-info.java
new file mode 100644
index 00000000000..35e7d14ecc6
--- /dev/null
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/enforcement/pre/package-info.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+@org.eclipse.ditto.utils.jsr305.annotations.AllParametersAndReturnValuesAreNonnullByDefault
+package org.eclipse.ditto.connectivity.service.enforcement.pre;
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/mapping/CloudEventsMapper.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/mapping/CloudEventsMapper.java
index b863c083fe7..1c5d913dddb 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/mapping/CloudEventsMapper.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/mapping/CloudEventsMapper.java
@@ -58,7 +58,7 @@ public final class CloudEventsMapper extends AbstractMessageMapper {
private static final String TYPE = "type";
private static final String OUTBOUNDTYPE = "org.eclipse.ditto.outbound";
private static final String OUTBOUNDSPECVERSION = "1.0";
- private static final String OUTBOUNDSOURCE = "https://github.com/eclipse/ditto";
+ private static final String OUTBOUNDSOURCE = "https://github.com/eclipse-ditto/ditto";
private static final String DATA = "data";
private static final String DATA_BASE64 = "data_base64";
private static final String OUTBOUND_DATA_CONTENT_TYPE = "datacontenttype";
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseClientActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseClientActor.java
index dddec5cbe04..0c026c0ced7 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseClientActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseClientActor.java
@@ -61,7 +61,9 @@
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
import org.eclipse.ditto.base.model.signals.Signal;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
import org.eclipse.ditto.base.model.signals.WithType;
+import org.eclipse.ditto.base.model.signals.commands.streaming.StreamingSubscriptionCommand;
import org.eclipse.ditto.connectivity.api.BaseClientState;
import org.eclipse.ditto.connectivity.api.InboundSignal;
import org.eclipse.ditto.connectivity.api.OutboundSignal;
@@ -114,6 +116,7 @@
import org.eclipse.ditto.connectivity.service.util.ConnectionPubSub;
import org.eclipse.ditto.connectivity.service.util.ConnectivityMdcEntryKey;
import org.eclipse.ditto.edge.service.headers.DittoHeadersValidator;
+import org.eclipse.ditto.edge.service.streaming.StreamingSubscriptionManager;
import org.eclipse.ditto.internal.models.signal.correlation.MatchingValidationResult;
import org.eclipse.ditto.internal.utils.akka.logging.DittoLoggerFactory;
import org.eclipse.ditto.internal.utils.akka.logging.ThreadSafeDittoLoggingAdapter;
@@ -178,8 +181,10 @@ public abstract class BaseClientActor extends AbstractFSMWithStash inboundMappingSink;
private ActorRef outboundDispatchingActor;
private ActorRef subscriptionManager;
+ private ActorRef streamingSubscriptionManager;
private ActorRef tunnelActor;
private int ackregatorCount = 0;
private boolean shuttingDown = false;
@@ -325,6 +331,7 @@ protected void init() {
inboundMappingSink = getInboundMappingSink(protocolAdapter, inboundDispatchingSink);
subscriptionManager =
startSubscriptionManager(commandForwarderActorSelection, connectivityConfig().getClientConfig());
+ streamingSubscriptionManager = startStreamingSubscriptionManager(commandForwarderActorSelection, connectivityConfig().getClientConfig());
if (connection.getSshTunnel().map(SshTunnel::isEnabled).orElse(false)) {
tunnelActor = childActorNanny.startChildActor(SshTunnelActor.ACTOR_NAME,
@@ -505,6 +512,7 @@ protected void cleanupFurtherResourcesOnConnectionTimeout(final BaseClientState
*/
protected FSMStateFunctionBuilder inAnyState() {
return matchEvent(RetrieveConnectionMetrics.class, (command, data) -> retrieveConnectionMetrics(command))
+ .event(StreamingSubscriptionCommand.class, this::forwardStreamingSubscriptionCommand)
.event(ThingSearchCommand.class, this::forwardThingSearchCommand)
.event(RetrieveConnectionStatus.class, this::retrieveConnectionStatus)
.event(ResetConnectionMetrics.class, this::resetConnectionMetrics)
@@ -1435,8 +1443,8 @@ private FSM.State retrieveConnectionStatus(fina
" Forwarding to consumers and publishers.", command.getEntityId(),
sender);
- // only one PublisherActor is started for all targets (if targets are present)
- final int numberOfProducers = connection.getTargets().isEmpty() ? 0 : 1;
+ // only one PublisherActor is started for all targets - and it always is started
+ final int numberOfProducers = 1;
final int numberOfConsumers = determineNumberOfConsumers();
int expectedNumberOfChildren = numberOfProducers + numberOfConsumers;
if (getSshTunnelState().isEnabled()) {
@@ -1716,6 +1724,8 @@ private FSM.State handleInboundSignal(final Inb
final Signal> signal = inboundSignal.getSignal();
if (signal instanceof WithSubscriptionId>) {
dispatchSearchCommand((WithSubscriptionId>) signal);
+ } else if (signal instanceof WithStreamingSubscriptionId>) {
+ dispatchStreamingSubscriptionCommand((WithStreamingSubscriptionId>) signal);
} else {
final var entityId = tryExtractEntityId(signal).orElseThrow();
connectionPubSub.publishSignal(inboundSignal.asDispatched(), connectionId(), entityId, getSender());
@@ -1746,6 +1756,24 @@ private void dispatchSearchCommand(final WithSubscriptionId> searchCommand) {
}
}
+ private void dispatchStreamingSubscriptionCommand(
+ final WithStreamingSubscriptionId> streamingSubscriptionCommand) {
+
+ final String subscriptionId = streamingSubscriptionCommand.getSubscriptionId();
+ if (subscriptionId.length() > subscriptionIdPrefixLength) {
+ final var prefix = subscriptionId.substring(0, subscriptionIdPrefixLength);
+ connectionPubSub.publishSignal(streamingSubscriptionCommand, connectionId(), prefix, ActorRef.noSender());
+ } else {
+ // command is invalid or outdated, dropping.
+ logger.withCorrelationId(streamingSubscriptionCommand)
+ .info("Dropping streaming subscription command with invalid subscription ID: <{}>",
+ streamingSubscriptionCommand);
+ connectionLogger.failure(InfoProviderFactory.forSignal(streamingSubscriptionCommand),
+ "Dropping streaming subscription command with invalid subscription ID: " +
+ streamingSubscriptionCommand.getSubscriptionId());
+ }
+ }
+
private Instant getInConnectionStatusSince() {
return stateData().getInConnectionStatusSince();
}
@@ -1891,6 +1919,19 @@ private ActorRef startSubscriptionManager(final ActorSelection proxyActor, final
return getContext().actorOf(props, SubscriptionManager.ACTOR_NAME);
}
+ /**
+ * Start the streaming subscription manager. Requires MessageMappingProcessorActor to be started to work.
+ * Creates an actor materializer.
+ *
+ * @return reference of the streaming subscription manager.
+ */
+ private ActorRef startStreamingSubscriptionManager(final ActorSelection proxyActor, final ClientConfig clientConfig) {
+ final var mat = Materializer.createMaterializer(this::getContext);
+ final var props = StreamingSubscriptionManager.props(clientConfig.getSubscriptionManagerTimeout(),
+ proxyActor, mat);
+ return getContext().actorOf(props, StreamingSubscriptionManager.ACTOR_NAME);
+ }
+
private FSM.State forwardThingSearchCommand(final WithDittoHeaders command,
final BaseClientData data) {
// Tell subscriptionManager to send search events to messageMappingProcessorActor.
@@ -1906,6 +1947,19 @@ private FSM.State forwardThingSearchCommand(fin
return stay();
}
+ private FSM.State forwardStreamingSubscriptionCommand(
+ final StreamingSubscriptionCommand> command,
+ final BaseClientData data) {
+ // Tell subscriptionManager to send streaming subscription events to messageMappingProcessorActor.
+ if (stateName() == CONNECTED) {
+ streamingSubscriptionManager.tell(command, outboundDispatchingActor);
+ } else {
+ logger.withCorrelationId(command)
+ .debug("Client state <{}> is not CONNECTED; dropping <{}>", stateName(), command);
+ }
+ return stay();
+ }
+
private FSM.State resubscribe(final Control trigger, final BaseClientData data) {
subscribeAndDeclareAcknowledgementLabels(dryRun, true);
startSubscriptionRefreshTimer();
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseConsumerActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseConsumerActor.java
index fe46ef752b7..bf45d23cc4c 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseConsumerActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BaseConsumerActor.java
@@ -186,18 +186,8 @@ private void prepareResponseHandler(final AcknowledgeableMessage acknowledgeable
} else {
// don't count this as "failure" in the "source consumed" metric as the consumption
// itself was successful
- final var dittoRuntimeException = DittoRuntimeException.asDittoRuntimeException(error, rootCause -> {
-
- // Redeliver and pray this unexpected error goes away
- log().debug("Rejecting [redeliver=true] due to error <{}>. " +
- "ResponseCollector=<{}>", rootCause, responseCollector);
- ackTimer.tag(getAckSuccessTag(false));
- ackTimer.tag(getAckRedeliverTag(true));
- ackTimer.stop();
- ackCounter.decrement();
- acknowledgeableMessage.reject(true);
- return null;
- });
+ final var dittoRuntimeException = DittoRuntimeException
+ .asDittoRuntimeException(error, rootCause -> null);
if (dittoRuntimeException != null) {
if (isConsideredSuccess(dittoRuntimeException)) {
ackTimer.tag(getAckSuccessTag(true));
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java
index 960583434c8..9583bd4e155 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java
@@ -48,6 +48,7 @@
import org.eclipse.ditto.base.model.signals.acks.Acknowledgement;
import org.eclipse.ditto.base.model.signals.acks.Acknowledgements;
import org.eclipse.ditto.base.model.signals.commands.CommandResponse;
+import org.eclipse.ditto.base.model.signals.events.streaming.StreamingSubscriptionEvent;
import org.eclipse.ditto.connectivity.api.ExternalMessage;
import org.eclipse.ditto.connectivity.api.OutboundSignal;
import org.eclipse.ditto.connectivity.model.Connection;
@@ -356,7 +357,7 @@ private Stream sendMappedOutboundSignal(final OutboundSignal.M
private Optional getSendingContext(final OutboundSignal.Mapped mappedOutboundSignal) {
final Optional result;
- if (isResponseOrErrorOrSearchEvent(mappedOutboundSignal)) {
+ if (isResponseOrErrorOrStreamingEvent(mappedOutboundSignal)) {
final Signal> source = mappedOutboundSignal.getSource();
final DittoHeaders dittoHeaders = source.getDittoHeaders();
result = dittoHeaders.getReplyTarget()
@@ -369,17 +370,19 @@ private Optional getSendingContext(final OutboundSignal.Mapped m
}
/**
- * Checks whether the passed in {@code outboundSignal} is a response or an error or a search event.
+ * Checks whether the passed in {@code outboundSignal} is a response or an error or a streaming event
+ * (including search events).
* Those messages are supposed to be published at the reply target of the source whence the original command came.
*
* @param outboundSignal the OutboundSignal to check.
* @return {@code true} if the OutboundSignal is a response or an error, {@code false} otherwise
*/
- private static boolean isResponseOrErrorOrSearchEvent(final OutboundSignal.Mapped outboundSignal) {
+ private static boolean isResponseOrErrorOrStreamingEvent(final OutboundSignal.Mapped outboundSignal) {
final ExternalMessage externalMessage = outboundSignal.getExternalMessage();
return externalMessage.isResponse() ||
externalMessage.isError() ||
- outboundSignal.getSource() instanceof SubscriptionEvent;
+ outboundSignal.getSource() instanceof SubscriptionEvent ||
+ outboundSignal.getSource() instanceof StreamingSubscriptionEvent>;
}
private Optional getReplyTargetByIndex(final int replyTargetIndex) {
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/ConnectionIdsRetrievalActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/ConnectionIdsRetrievalActor.java
index 7e7e133c9a7..9373a78fb35 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/ConnectionIdsRetrievalActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/ConnectionIdsRetrievalActor.java
@@ -40,6 +40,7 @@
import org.eclipse.ditto.internal.utils.akka.logging.DittoLoggerFactory;
import org.eclipse.ditto.internal.utils.cluster.DistPubSubAccess;
import org.eclipse.ditto.internal.utils.persistence.mongo.streaming.MongoReadJournal;
+import org.eclipse.ditto.internal.utils.persistentactors.EmptyEvent;
import akka.NotUsed;
import akka.actor.AbstractActor;
@@ -114,6 +115,12 @@ private static boolean isDeleted(final Document document) {
.orElse(true);
}
+ private static boolean isNotEmptyEvent(final Document document) {
+ return Optional.ofNullable(document.getString(MongoReadJournal.J_EVENT_MANIFEST))
+ .map(manifest -> !EmptyEvent.TYPE.equals(manifest))
+ .orElse(false);
+ }
+
private static boolean isNotDeleted(final Document document) {
return Optional.ofNullable(document.getString(MongoReadJournal.J_EVENT_MANIFEST))
.map(manifest -> !ConnectionDeleted.TYPE.equals(manifest))
@@ -166,6 +173,7 @@ private void getAllConnectionIDs(final WithDittoHeaders cmd) {
final Source idsFromSnapshots = getIdsFromSnapshotsSource();
final Source idsFromJournal = persistenceIdsFromJournalSourceSupplier.get()
.filter(ConnectionIdsRetrievalActor::isNotDeleted)
+ .filter(ConnectionIdsRetrievalActor::isNotEmptyEvent)
.map(document -> document.getString(MongoReadJournal.J_EVENT_PID));
final CompletionStage retrieveAllConnectionIdsResponse =
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/InboundDispatchingSink.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/InboundDispatchingSink.java
index afe0da9db33..53d2ae0f277 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/InboundDispatchingSink.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/InboundDispatchingSink.java
@@ -44,11 +44,13 @@
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
import org.eclipse.ditto.base.model.headers.translator.HeaderTranslator;
import org.eclipse.ditto.base.model.signals.Signal;
+import org.eclipse.ditto.base.model.signals.WithStreamingSubscriptionId;
import org.eclipse.ditto.base.model.signals.acks.Acknowledgement;
import org.eclipse.ditto.base.model.signals.acks.Acknowledgements;
import org.eclipse.ditto.base.model.signals.commands.Command;
import org.eclipse.ditto.base.model.signals.commands.CommandResponse;
import org.eclipse.ditto.base.model.signals.commands.ErrorResponse;
+import org.eclipse.ditto.base.model.signals.commands.streaming.SubscribeForPersistedEvents;
import org.eclipse.ditto.connectivity.api.ExternalMessage;
import org.eclipse.ditto.connectivity.api.InboundSignal;
import org.eclipse.ditto.connectivity.api.MappedInboundExternalMessage;
@@ -508,6 +510,8 @@ private PartialFunction, Stream> dispatchResponsesAndS
)
.match(CreateSubscription.class, cmd -> forwardToConnectionActor(cmd, sender))
.match(WithSubscriptionId.class, cmd -> forwardToClientActor(cmd, sender))
+ .match(SubscribeForPersistedEvents.class, cmd -> forwardToConnectionActor(cmd, sender))
+ .match(WithStreamingSubscriptionId.class, cmd -> forwardToClientActor(cmd, sender))
.matchAny(baseSignal -> ackregatorStarter.preprocess(baseSignal,
(signal, isAckRequesting) -> Stream.of(new IncomingSignal(signal,
getReturnAddress(sender, isAckRequesting, signal),
@@ -659,7 +663,7 @@ private Stream forwardToClientActor(final Signal> signal, @Nullable fin
return Stream.empty();
}
- private Stream forwardToConnectionActor(final CreateSubscription command, @Nullable final ActorRef sender) {
+ private Stream forwardToConnectionActor(final Command> command, @Nullable final ActorRef sender) {
connectionActor.tell(command, sender);
return Stream.empty();
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundDispatchingActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundDispatchingActor.java
index 630147a10f0..3e63e41d785 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundDispatchingActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundDispatchingActor.java
@@ -31,6 +31,7 @@
import org.eclipse.ditto.base.model.signals.acks.Acknowledgement;
import org.eclipse.ditto.base.model.signals.commands.Command;
import org.eclipse.ditto.base.model.signals.commands.CommandResponse;
+import org.eclipse.ditto.base.model.signals.events.streaming.StreamingSubscriptionEvent;
import org.eclipse.ditto.connectivity.api.InboundSignal;
import org.eclipse.ditto.connectivity.api.OutboundSignalFactory;
import org.eclipse.ditto.connectivity.model.Target;
@@ -79,6 +80,7 @@ public Receive createReceive() {
.match(InboundSignal.class, this::inboundSignal)
.match(CommandResponse.class, this::forwardWithoutCheck)
.match(SubscriptionEvent.class, this::forwardWithoutCheck)
+ .match(StreamingSubscriptionEvent.class, this::forwardWithoutCheck)
.match(DittoRuntimeException.class, this::forwardWithoutCheck)
.match(Signal.class, this::handleSignal)
.matchAny(message -> logger.warning("Unknown message: <{}>", message))
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActor.java
index 96ee126d233..869909f8b5b 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActor.java
@@ -349,13 +349,21 @@ protected OutboundSignalWithSender mapMessage(final OutboundSignal message) {
}
@Override
- protected void messageDiscarded(OutboundSignal message, QueueOfferResult result) {
+ protected void messageDiscarded(final OutboundSignal message, final QueueOfferResult result) {
+ final Set monitorsForOutboundSignal =
+ getMonitorsForOutboundSignal(message, MAPPED, LogType.MAPPED, responseMappedMonitor);
if (QueueOfferResult.dropped().equals(result)) {
- responseDispatchedMonitor.failure(message.getSource(), "Message is dropped as a result of backpressure strategy!");
+ monitorsForOutboundSignal.forEach(monitor ->
+ monitor.failure(message.getSource(), "Message is dropped as a result of backpressure strategy!")
+ );
} else if (result instanceof final QueueOfferResult.Failure failure) {
- responseDispatchedMonitor.failure(message.getSource(), "Enqueue failed! - failure: {}", failure.cause());
+ monitorsForOutboundSignal.forEach(monitor ->
+ monitor.failure(message.getSource(), "Enqueue failed! - failure: {}", failure.cause())
+ );
} else {
- responseDispatchedMonitor.failure(message.getSource(), "Enqueue failed without acknowledgement!");
+ monitorsForOutboundSignal.forEach(monitor ->
+ monitor.failure(message.getSource(), "Enqueue failed without acknowledgement!")
+ );
}
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactory.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactory.java
index 18c806b30fc..99043999f82 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactory.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactory.java
@@ -16,6 +16,8 @@
import java.text.MessageFormat;
import java.util.Set;
+import org.eclipse.ditto.connectivity.model.Connection;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
import org.eclipse.ditto.connectivity.model.HonoAddressAlias;
import org.eclipse.ditto.connectivity.model.UserPasswordCredentials;
import org.eclipse.ditto.connectivity.service.config.DefaultHonoConfig;
@@ -33,6 +35,8 @@ public final class DefaultHonoConnectionFactory extends HonoConnectionFactory {
private final HonoConfig honoConfig;
+ private ConnectionId connectionId;
+
/**
* Constructs a {@code DefaultHonoConnectionFactory} for the specified arguments.
*
@@ -44,6 +48,11 @@ public DefaultHonoConnectionFactory(final ActorSystem actorSystem, final Config
honoConfig = new DefaultHonoConfig(actorSystem);
}
+ @Override
+ protected void preConversion(final Connection honoConnection) {
+ connectionId = honoConnection.getId();
+ }
+
@Override
public URI getBaseUri() {
return honoConfig.getBaseUri();
@@ -76,12 +85,14 @@ protected UserPasswordCredentials getCredentials() {
@Override
protected String resolveSourceAddress(final HonoAddressAlias honoAddressAlias) {
- return MessageFormat.format("hono.{0}", honoAddressAlias.getAliasValue());
+ return MessageFormat.format("hono.{0}.{1}",
+ honoAddressAlias.getAliasValue(), connectionId);
}
@Override
protected String resolveTargetAddress(final HonoAddressAlias honoAddressAlias) {
- return MessageFormat.format("hono.{0}/'{{thing:id}}'", honoAddressAlias.getAliasValue());
+ return MessageFormat.format("hono.{0}.{1}/'{{thing:id}}'",
+ honoAddressAlias.getAliasValue(), connectionId);
}
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/HonoConnectionFactory.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/HonoConnectionFactory.java
index 3057a0ea4d9..8552aee4b5a 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/HonoConnectionFactory.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/hono/HonoConnectionFactory.java
@@ -16,6 +16,8 @@
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.net.URI;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -150,7 +152,8 @@ protected void preConversion(final Connection honoConnection) {
private String combineUriWithCredentials(final String uri, final UserPasswordCredentials credentials) {
return uri.replaceFirst("(\\S+://)(\\S+)",
- "$1" + credentials.getUsername() + ":" + credentials.getPassword() + "@$2");
+ "$1" + URLEncoder.encode(credentials.getUsername(), StandardCharsets.UTF_8) + ":" +
+ URLEncoder.encode(credentials.getPassword(), StandardCharsets.UTF_8) + "@$2");
}
private Map makeupSpecificConfig(final Connection connection) {
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/client/BaseGenericMqttSubscribingClient.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/client/BaseGenericMqttSubscribingClient.java
index 7d1f35158b6..4ec4fc9ad48 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/client/BaseGenericMqttSubscribingClient.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/client/BaseGenericMqttSubscribingClient.java
@@ -226,9 +226,13 @@ protected Single sendSubscribe(final Mqtt3RxClient mqtt3RxCli
@Override
Completable sendUnsubscribe(final Mqtt3RxClient mqtt3RxClient, final MqttTopicFilter... mqttTopicFilters) {
- final var unsubscribe =
- Mqtt3Unsubscribe.builder().addTopicFilters(mqttTopicFilters).build();
- return mqtt3RxClient.unsubscribe(unsubscribe);
+ if (mqttTopicFilters.length == 0) {
+ return Completable.complete();
+ } else {
+ final var unsubscribe =
+ Mqtt3Unsubscribe.builder().addTopicFilters(mqttTopicFilters).build();
+ return mqtt3RxClient.unsubscribe(unsubscribe);
+ }
}
@Override
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformer.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformer.java
index ebc5ee326ea..65f84b46b3b 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformer.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformer.java
@@ -236,6 +236,7 @@ private static Set getUserPropertiesOrEmptySet(
) {
return externalMessageHeaders.stream()
.filter(header -> !KNOWN_MQTT_HEADER_NAMES.contains(header.getKey()))
+ .filter(header -> header.getValue() != null && !header.getValue().isBlank())
.map(header -> {
final var headerKey = header.getKey();
final String headerValue;
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActor.java
index ebed209c9ef..a55e3fb1979 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActor.java
@@ -45,6 +45,7 @@
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.base.model.signals.SignalWithEntityId;
import org.eclipse.ditto.base.model.signals.commands.Command;
+import org.eclipse.ditto.base.model.signals.commands.streaming.SubscribeForPersistedEvents;
import org.eclipse.ditto.connectivity.api.BaseClientState;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionId;
@@ -55,6 +56,7 @@
import org.eclipse.ditto.connectivity.model.ConnectivityStatus;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommandInterceptor;
import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionFailedException;
+import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionHistoryNotAccessibleException;
import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionNotAccessibleException;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CheckConnectionLogsActive;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CloseConnection;
@@ -198,12 +200,13 @@ public final class ConnectionPersistenceActor
@Nullable private Instant recoveredAt;
ConnectionPersistenceActor(final ConnectionId connectionId,
+ final MongoReadJournal mongoReadJournal,
final ActorRef commandForwarderActor,
final ActorRef pubSubMediator,
final Trilean allClientActorsOnOneNode,
final Config connectivityConfigOverwrites) {
- super(connectionId);
+ super(connectionId, mongoReadJournal);
this.actorSystem = context().system();
cluster = Cluster.get(actorSystem);
final Config dittoExtensionConfig = ScopedConfig.dittoExtension(actorSystem.settings().config());
@@ -263,18 +266,21 @@ protected DittoDiagnosticLoggingAdapter createLogger() {
* Creates Akka configuration object for this actor.
*
* @param connectionId the connection ID.
- * @param commandForwarderActor the actor used to send signals into the ditto cluster..
+ * @param mongoReadJournal the ReadJournal used for gaining access to historical values of the connection.
+ * @param commandForwarderActor the actor used to send signals into the ditto cluster.
+ * @param pubSubMediator pub-sub-mediator for the shutdown behavior.
* @param pubSubMediator the pubSubMediator
* @param connectivityConfigOverwrites the overwrites for the connectivity config for the given connection.
* @return the Akka configuration Props object.
*/
public static Props props(final ConnectionId connectionId,
+ final MongoReadJournal mongoReadJournal,
final ActorRef commandForwarderActor,
final ActorRef pubSubMediator,
final Config connectivityConfigOverwrites
) {
- return Props.create(ConnectionPersistenceActor.class, connectionId,
- commandForwarderActor, pubSubMediator, Trilean.UNKNOWN, connectivityConfigOverwrites);
+ return Props.create(ConnectionPersistenceActor.class, connectionId, mongoReadJournal,
+ commandForwarderActor, pubSubMediator,Trilean.UNKNOWN, connectivityConfigOverwrites);
}
/**
@@ -350,6 +356,16 @@ protected DittoRuntimeExceptionBuilder> newNotAccessibleExceptionBuilder() {
return ConnectionNotAccessibleException.newBuilder(entityId);
}
+ @Override
+ protected DittoRuntimeExceptionBuilder> newHistoryNotAccessibleExceptionBuilder(final long revision) {
+ return ConnectionHistoryNotAccessibleException.newBuilder(entityId, revision);
+ }
+
+ @Override
+ protected DittoRuntimeExceptionBuilder> newHistoryNotAccessibleExceptionBuilder(final Instant timestamp) {
+ return ConnectionHistoryNotAccessibleException.newBuilder(entityId, timestamp);
+ }
+
@Override
protected void publishEvent(@Nullable final Connection previousEntity, final ConnectivityEvent> event) {
if (event instanceof ConnectionDeleted) {
@@ -564,6 +580,17 @@ public void onMutation(final Command> command, final ConnectivityEvent> even
}
}
+ @Override
+ public void onStagedMutation(final Command> command, final CompletionStage> event,
+ final CompletionStage response, final boolean becomeCreated,
+ final boolean becomeDeleted) {
+ if (command instanceof StagedCommand stagedCommand) {
+ interpretStagedCommand(stagedCommand.withSenderUnlessDefined(getSender()));
+ } else {
+ super.onStagedMutation(command, event, response, becomeCreated, becomeDeleted);
+ }
+ }
+
@Override
protected void checkForActivity(final CheckForActivity trigger) {
if (isDesiredStateOpen()) {
@@ -665,6 +692,9 @@ protected Receive matchAnyAfterInitialization() {
// CreateSubscription is a ThingSearchCommand, but it is created in InboundDispatchingSink from an
// adaptable and directly sent to this actor:
.match(CreateSubscription.class, this::startThingSearchSession)
+ // SubscribeForPersistedEvents is created in InboundDispatchingSink from an
+ // adaptable and directly sent to this actor:
+ .match(SubscribeForPersistedEvents.class, this::startStreamingSubscriptionSession)
.matchEquals(Control.CHECK_LOGGING_ACTIVE, this::checkLoggingEnabled)
.matchEquals(Control.TRIGGER_UPDATE_PRIORITY, this::triggerUpdatePriority)
.match(UpdatePriority.class, this::updatePriority)
@@ -725,6 +755,17 @@ private void startThingSearchSession(final CreateSubscription command) {
augmentWithPrefixAndForward(command, entity.getClientCount());
}
+ private void startStreamingSubscriptionSession(final SubscribeForPersistedEvents command) {
+ if (entity == null) {
+ logDroppedSignal(command, command.getType(), "No Connection configuration available.");
+ return;
+ }
+ log.debug("Forwarding <{}> to client actors.", command);
+ // compute the next prefix according to subscriptionCounter and the currently configured client actor count
+ // ignore any "prefix" field from the command
+ augmentWithPrefixAndForward(command, entity.getClientCount());
+ }
+
private void augmentWithPrefixAndForward(final CreateSubscription createSubscription, final int clientCount) {
subscriptionCounter = (subscriptionCounter + 1) % Math.max(1, clientCount);
final var prefix = getPrefix(getSubscriptionPrefixLength(clientCount), subscriptionCounter);
@@ -732,6 +773,14 @@ private void augmentWithPrefixAndForward(final CreateSubscription createSubscrip
connectionPubSub.publishSignal(commandWithPrefix, entityId, prefix, ActorRef.noSender());
}
+ private void augmentWithPrefixAndForward(final SubscribeForPersistedEvents subscribeForPersistedEvents,
+ final int clientCount) {
+ subscriptionCounter = (subscriptionCounter + 1) % Math.max(1, clientCount);
+ final var prefix = getPrefix(getSubscriptionPrefixLength(clientCount), subscriptionCounter);
+ final var commandWithPrefix = subscribeForPersistedEvents.setPrefix(prefix);
+ connectionPubSub.publishSignal(commandWithPrefix, entityId, prefix, ActorRef.noSender());
+ }
+
private static String getPrefix(final int prefixLength, final int subscriptionCounter) {
final var prefixPattern = MessageFormat.format("%0{0,number}X", prefixLength);
return String.format(prefixPattern, subscriptionCounter);
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionSupervisorActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionSupervisorActor.java
index 05d69b8560b..fc62e17cdc2 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionSupervisorActor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionSupervisorActor.java
@@ -31,6 +31,7 @@
import org.eclipse.ditto.base.model.signals.Signal;
import org.eclipse.ditto.base.service.actors.ShutdownBehaviour;
import org.eclipse.ditto.base.service.config.supervision.ExponentialBackOffConfig;
+import org.eclipse.ditto.base.service.config.supervision.LocalAskTimeoutConfig;
import org.eclipse.ditto.connectivity.model.ConnectionId;
import org.eclipse.ditto.connectivity.model.ConnectionType;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
@@ -41,6 +42,7 @@
import org.eclipse.ditto.connectivity.service.config.DittoConnectivityConfig;
import org.eclipse.ditto.connectivity.service.enforcement.ConnectionEnforcerActorPropsFactory;
import org.eclipse.ditto.internal.utils.config.DefaultScopedConfig;
+import org.eclipse.ditto.internal.utils.persistence.mongo.streaming.MongoReadJournal;
import org.eclipse.ditto.internal.utils.persistentactors.AbstractPersistenceSupervisor;
import com.typesafe.config.Config;
@@ -71,12 +73,6 @@ public final class ConnectionSupervisorActor
private static final Duration MAX_CONFIG_RETRIEVAL_DURATION = Duration.ofSeconds(5);
- /**
- * For connectivity, this local ask timeout has to be higher as e.g. "openConnection" commands performed in a
- * "staged" way will lead to quite some response times.
- */
- private static final Duration CONNECTIVITY_DEFAULT_LOCAL_ASK_TIMEOUT = Duration.ofSeconds(50);
-
private static final SupervisorStrategy SUPERVISOR_STRATEGY =
new OneForOneStrategy(true,
DeciderBuilder.match(JMSRuntimeException.class, e ->
@@ -95,10 +91,12 @@ public final class ConnectionSupervisorActor
private final ConnectionEnforcerActorPropsFactory enforcerActorPropsFactory;
@SuppressWarnings("unused")
- private ConnectionSupervisorActor(final ActorRef commandForwarderActor, final ActorRef pubSubMediator,
- final ConnectionEnforcerActorPropsFactory enforcerActorPropsFactory) {
+ private ConnectionSupervisorActor(final ActorRef commandForwarderActor,
+ final ActorRef pubSubMediator,
+ final ConnectionEnforcerActorPropsFactory enforcerActorPropsFactory,
+ final MongoReadJournal mongoReadJournal) {
- super(null, CONNECTIVITY_DEFAULT_LOCAL_ASK_TIMEOUT);
+ super(null, mongoReadJournal);
this.commandForwarderActor = commandForwarderActor;
this.pubSubMediator = pubSubMediator;
this.enforcerActorPropsFactory = enforcerActorPropsFactory;
@@ -114,14 +112,16 @@ private ConnectionSupervisorActor(final ActorRef commandForwarderActor, final Ac
* @param commandForwarder the actor used to send signals into the ditto cluster.
* @param pubSubMediator pub-sub-mediator for the shutdown behavior.
* @param enforcerActorPropsFactory used to create the enforcer actor.
+ * @param mongoReadJournal the ReadJournal used for gaining access to historical values of the connection.
* @return the {@link Props} to create this actor.
*/
public static Props props(final ActorRef commandForwarder,
final ActorRef pubSubMediator,
- final ConnectionEnforcerActorPropsFactory enforcerActorPropsFactory) {
+ final ConnectionEnforcerActorPropsFactory enforcerActorPropsFactory,
+ final MongoReadJournal mongoReadJournal) {
return Props.create(ConnectionSupervisorActor.class, commandForwarder, pubSubMediator,
- enforcerActorPropsFactory);
+ enforcerActorPropsFactory, mongoReadJournal);
}
@Override
@@ -183,7 +183,7 @@ protected void handleMessagesDuringStartup(final Object message) {
@Override
protected Props getPersistenceActorProps(final ConnectionId entityId) {
- return ConnectionPersistenceActor.props(entityId, commandForwarderActor, pubSubMediator,
+ return ConnectionPersistenceActor.props(entityId, mongoReadJournal, commandForwarderActor, pubSubMediator,
connectivityConfigOverwrites);
}
@@ -200,6 +200,14 @@ protected ExponentialBackOffConfig getExponentialBackOffConfig() {
return connectionConfig.getSupervisorConfig().getExponentialBackOffConfig();
}
+ @Override
+ protected LocalAskTimeoutConfig getLocalAskTimeoutConfig() {
+ return DittoConnectivityConfig.of(DefaultScopedConfig.dittoScoped(getContext().getSystem().settings().config()))
+ .getConnectionConfig()
+ .getSupervisorConfig()
+ .getLocalAskTimeoutConfig();
+ }
+
@Override
protected ShutdownBehaviour getShutdownBehaviour(final ConnectionId entityId) {
return ShutdownBehaviour.fromIdWithoutNamespace(entityId, pubSubMediator, getSelf());
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectivityMongoEventAdapter.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectivityMongoEventAdapter.java
index b9478f4fd4e..2d1f1465293 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectivityMongoEventAdapter.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectivityMongoEventAdapter.java
@@ -12,28 +12,31 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence;
+import java.util.HashMap;
+import java.util.Map;
+
import akka.actor.ExtendedActorSystem;
import org.eclipse.ditto.base.model.signals.JsonParsable;
+import org.eclipse.ditto.base.model.signals.events.Event;
import org.eclipse.ditto.base.model.signals.events.EventJsonDeserializer;
import org.eclipse.ditto.base.model.signals.events.EventRegistry;
import org.eclipse.ditto.base.model.signals.events.GlobalEventRegistry;
+import org.eclipse.ditto.base.service.config.DittoServiceConfig;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectivityConstants;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionCreated;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionModified;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
-import org.eclipse.ditto.connectivity.service.config.DittoConnectivityConfig;
-import org.eclipse.ditto.connectivity.service.config.FieldsEncryptionConfig;
+import org.eclipse.ditto.connectivity.service.config.DefaultConnectionConfig;
import org.eclipse.ditto.internal.utils.config.DefaultScopedConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.AbstractMongoEventAdapter;
+import org.eclipse.ditto.connectivity.service.config.DittoConnectivityConfig;
+import org.eclipse.ditto.connectivity.service.config.FieldsEncryptionConfig;
import org.eclipse.ditto.json.JsonObject;
+import org.eclipse.ditto.json.JsonObjectBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* EventAdapter for {@link ConnectivityEvent}s persisted into
* akka-persistence event-journal. Converts Events to MongoDB BSON objects and vice versa.
@@ -43,8 +46,10 @@ public final class ConnectivityMongoEventAdapter extends AbstractMongoEventAdapt
private final FieldsEncryptionConfig encryptionConfig;
private final Logger logger;
- public ConnectivityMongoEventAdapter(@Nullable final ExtendedActorSystem system) {
- super(system, createEventRegistry());
+ public ConnectivityMongoEventAdapter(final ExtendedActorSystem system) {
+ super(system, createEventRegistry(), DefaultConnectionConfig.of(
+ DittoServiceConfig.of(DefaultScopedConfig.dittoScoped(system.settings().config()), "connectivity"))
+ .getEventConfig());
logger = LoggerFactory.getLogger(ConnectivityMongoEventAdapter.class);
final DittoConnectivityConfig connectivityConfig = DittoConnectivityConfig.of(
DefaultScopedConfig.dittoScoped(system.settings().config()));
@@ -56,11 +61,14 @@ public ConnectivityMongoEventAdapter(@Nullable final ExtendedActorSystem system)
}
@Override
- protected JsonObject performToJournalMigration(final JsonObject jsonObject) {
+ protected JsonObjectBuilder performToJournalMigration(final Event> event, final JsonObject jsonObject) {
if (encryptionConfig.isEncryptionEnabled()) {
- return JsonFieldsEncryptor.encrypt(jsonObject, ConnectivityConstants.ENTITY_TYPE.toString(), encryptionConfig.getJsonPointers(), encryptionConfig.getSymmetricalKey());
+ final JsonObject superObject = super.performToJournalMigration(event, jsonObject).build();
+ return JsonFieldsEncryptor.encrypt(superObject, ConnectivityConstants.ENTITY_TYPE.toString(),
+ encryptionConfig.getJsonPointers(), encryptionConfig.getSymmetricalKey())
+ .toBuilder();
}
- return jsonObject;
+ return super.performToJournalMigration(event, jsonObject);
}
@Override
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/JsonFieldsEncryptor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/JsonFieldsEncryptor.java
index a1d8a9ac4f6..fc617f226e8 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/JsonFieldsEncryptor.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/JsonFieldsEncryptor.java
@@ -87,7 +87,7 @@ public static JsonObject decrypt(final JsonObject jsonObject, final String point
}
static String replaceUriPassword(final String uriStringRepresentation, final String patchedPassword) {
- final String userInfo = URI.create(uriStringRepresentation).getUserInfo();
+ final String userInfo = URI.create(uriStringRepresentation).getRawUserInfo();
final String newUserInfo = userInfo.substring(0, userInfo.indexOf(":") + 1) + patchedPassword;
final int startOfPwd = uriStringRepresentation.indexOf(userInfo);
final int endOfPassword = uriStringRepresentation.indexOf("@");
@@ -159,7 +159,7 @@ private static Optional getUriPassword(final String uriStringRepresentat
.message("Not a valid connection URI")
.build();
}
- final String userInfo = uri.getUserInfo();
+ final String userInfo = uri.getRawUserInfo();
if (userInfo == null) {
return Optional.empty();
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/stages/StagedCommand.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/stages/StagedCommand.java
index 96777760a5a..140dbb0501f 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/stages/StagedCommand.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/stages/StagedCommand.java
@@ -27,6 +27,8 @@
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
+import org.eclipse.ditto.connectivity.model.WithConnectionId;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
import org.eclipse.ditto.json.JsonField;
@@ -41,7 +43,8 @@
* It contains a sequence of actions. Some actions are asynchronous. The connection actor can thus schedule the next
* action as a staged command to self after an asynchronous action. Synchronous actions can be executed right away.
*/
-public final class StagedCommand implements ConnectivityCommand, Iterator {
+public final class StagedCommand implements ConnectivityCommand, Iterator,
+ WithConnectionId {
private final ConnectivityCommand> command;
@Nullable private final ConnectivityEvent> event;
@@ -82,6 +85,17 @@ public ConnectivityCommand> getCommand() {
return command;
}
+ @Override
+ public ConnectionId getEntityId() {
+ if (command instanceof WithConnectionId withConnectionId) {
+ return withConnectionId.getEntityId();
+ } else if (event != null) {
+ return event.getEntityId();
+ } else {
+ throw new IllegalStateException("Could not determine ConnectionId in StagedCommand");
+ }
+ }
+
/**
* @return the event to persist, apply or publish or dummy-event.
*/
@@ -210,4 +224,5 @@ public ConnectionAction nextAction() {
private Queue getActionsAsQueue() {
return new LinkedList<>(actions);
}
+
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/AbstractConnectivityCommandStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/AbstractConnectivityCommandStrategy.java
index 2d11f67ebbf..65608619c5e 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/AbstractConnectivityCommandStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/AbstractConnectivityCommandStrategy.java
@@ -29,7 +29,8 @@
import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionNotAccessibleException;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.internal.utils.persistentactors.commands.AbstractCommandStrategy;
+import org.eclipse.ditto.internal.utils.headers.conditional.ConditionalHeadersValidator;
+import org.eclipse.ditto.internal.utils.persistentactors.etags.AbstractConditionHeaderCheckingCommandStrategy;
/**
* Abstract base class for {@link org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand} strategies.
@@ -37,12 +38,20 @@
* @param the type of the handled command
*/
abstract class AbstractConnectivityCommandStrategy>
- extends AbstractCommandStrategy> {
+ extends AbstractConditionHeaderCheckingCommandStrategy> {
- AbstractConnectivityCommandStrategy(final Class> theMatchingClass) {
+ private static final ConditionalHeadersValidator VALIDATOR =
+ ConnectionsConditionalHeadersValidatorProvider.getInstance();
+
+ protected AbstractConnectivityCommandStrategy(final Class theMatchingClass) {
super(theMatchingClass);
}
+ @Override
+ protected ConditionalHeadersValidator getValidator() {
+ return VALIDATOR;
+ }
+
@Override
public boolean isDefined(final C command) {
return command instanceof ConnectivityCommand || command instanceof ConnectivitySudoCommand;
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CloseConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CloseConnectionStrategy.java
index 3b3bb0e322e..36db15991b5 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CloseConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CloseConnectionStrategy.java
@@ -16,20 +16,22 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CloseConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CloseConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionClosed;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
* This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.CloseConnection} command.
@@ -56,4 +58,15 @@ protected Result> doApply(final Context co
ConnectionAction.SEND_RESPONSE);
return newMutationResult(StagedCommand.of(command, event, response, actions), event, response);
}
+
+ @Override
+ public Optional previousEntityTag(final CloseConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.ofNullable(previousEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final CloseConnection command, @Nullable final Connection newEntity) {
+ return Optional.of(getEntityOrThrow(newEntity)).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionConflictStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionConflictStrategy.java
index 8387721afdc..9d6050e93fc 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionConflictStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionConflictStrategy.java
@@ -14,15 +14,18 @@
import static org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory.newErrorResult;
+import java.util.Optional;
+
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionConflictException;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CreateConnection;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
* This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.CreateConnection} command
@@ -49,4 +52,15 @@ protected Result> doApply(final Context co
.build();
return newErrorResult(conflictException, command);
}
+
+ @Override
+ public Optional previousEntityTag(final CreateConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.ofNullable(previousEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final CreateConnection command, @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionUninitializedStrategies.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionUninitializedStrategies.java
deleted file mode 100644
index f606c1e0215..00000000000
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionUninitializedStrategies.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2021 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
-
-import java.util.Optional;
-import java.util.function.Consumer;
-
-import javax.annotation.Nullable;
-
-import org.eclipse.ditto.base.model.entity.metadata.Metadata;
-import org.eclipse.ditto.base.model.signals.commands.Command;
-import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionNotAccessibleException;
-import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.internal.utils.persistentactors.commands.AbstractCommandStrategy;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
-import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
-
-/**
- * Strategies to handle signals as an uninitialized connection that stashes every command.
- */
-public final class ConnectionUninitializedStrategies
- extends AbstractCommandStrategy, Connection, ConnectionState, ConnectivityEvent>>
- implements ConnectivityCommandStrategies {
-
- private final Consumer> action;
-
- private ConnectionUninitializedStrategies(final Consumer> action) {
- super(Command.class);
- this.action = action;
- }
-
- /**
- * Return a new instance of this class.
- *
- * @param action what to do on connectivity commands.
- * @return the empty result.
- */
- public static ConnectionUninitializedStrategies of(final Consumer> action) {
- return new ConnectionUninitializedStrategies(action);
- }
-
- @Override
- public boolean isDefined(final Command> command) {
- return true;
- }
-
- @Override
- protected Optional calculateRelativeMetadata(@Nullable final Connection entity,
- final Command> command) {
- return Optional.empty();
- }
-
- @Override
- protected Result> doApply(final Context context,
- @Nullable final Connection entity,
- final long nextRevision, final Command> command, @Nullable final Metadata metadata) {
- action.accept(command);
- return Result.empty();
- }
-
- @Override
- public Result> unhandled(final Context context,
- @Nullable final Connection entity,
- final long nextRevision,
- final Command> command) {
-
- return ResultFactory.newErrorResult(ConnectionNotAccessibleException
- .newBuilder(context.getState().id())
- .dittoHeaders(command.getDittoHeaders())
- .build(), command);
- }
-
-}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionsConditionalHeadersValidatorProvider.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionsConditionalHeadersValidatorProvider.java
new file mode 100644
index 00000000000..f4dcca5b163
--- /dev/null
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ConnectionsConditionalHeadersValidatorProvider.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.eclipse.ditto.base.model.exceptions.DittoRuntimeExceptionBuilder;
+import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionPreconditionFailedException;
+import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionPreconditionNotModifiedException;
+import org.eclipse.ditto.internal.utils.headers.conditional.ConditionalHeadersValidator;
+
+/**
+ * Provides a {@link ConditionalHeadersValidator} which checks conditional (http) headers based on a given ETag on
+ * Connection resources.
+ */
+@Immutable
+final class ConnectionsConditionalHeadersValidatorProvider {
+
+ /**
+ * Settings for validating conditional headers on Connection resources.
+ */
+ private static class ConnectionsConditionalHeadersValidationSettings
+ implements ConditionalHeadersValidator.ValidationSettings {
+
+ /**
+ * Returns a builder for a {@link org.eclipse.ditto.things.model.signals.commands.exceptions.ThingPreconditionFailedException}.
+ *
+ * @param conditionalHeaderName the name of the conditional header.
+ * @param expected the expected value.
+ * @param actual the actual ETag value.
+ * @return the builder.
+ */
+ @Override
+ public DittoRuntimeExceptionBuilder> createPreconditionFailedExceptionBuilder(final String conditionalHeaderName,
+ final String expected, final String actual) {
+ return ConnectionPreconditionFailedException.newBuilder(conditionalHeaderName, expected, actual);
+ }
+
+ /**
+ * Returns a builder for a {@link ConnectionPreconditionNotModifiedException}.
+ *
+ * @param expectedNotToMatch the value which was expected not to match {@code matched} value.
+ * @param matched the matched value.
+ * @return the builder.
+ */
+ @Override
+ public DittoRuntimeExceptionBuilder> createPreconditionNotModifiedExceptionBuilder(
+ final String expectedNotToMatch, final String matched) {
+ return ConnectionPreconditionNotModifiedException.newBuilder(expectedNotToMatch, matched);
+ }
+
+ @Override
+ public DittoRuntimeExceptionBuilder> createPreconditionNotModifiedForEqualityExceptionBuilder() {
+ return ConnectionPreconditionNotModifiedException.newBuilder()
+ .message("The previous value was equal to the new value and the 'if-equal' header was set to 'skip'.")
+ .description("Your changes were not applied, which is probably the expected outcome.");
+ }
+ }
+
+ private static final ConditionalHeadersValidator INSTANCE = createInstance();
+
+ private ConnectionsConditionalHeadersValidatorProvider() {
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns the (singleton) instance of {@link org.eclipse.ditto.internal.utils.headers.conditional.ConditionalHeadersValidator} for Thing resources.
+ *
+ * @return the {@link org.eclipse.ditto.internal.utils.headers.conditional.ConditionalHeadersValidator}.
+ */
+ public static ConditionalHeadersValidator getInstance() {
+ return INSTANCE;
+ }
+
+ private static ConditionalHeadersValidator createInstance() {
+ return ConditionalHeadersValidator.of(new ConnectionsConditionalHeadersValidationSettings(),
+ checker -> false);
+ }
+
+}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CreateConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CreateConnectionStrategy.java
index e4978b5a5e0..0e640ab3cc0 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CreateConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/CreateConnectionStrategy.java
@@ -22,7 +22,9 @@
import static org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory.newErrorResult;
import static org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory.newMutationResult;
+import java.time.Instant;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -30,16 +32,17 @@
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectivityStatus;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CreateConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.CreateConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionCreated;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
* This strategy handles the {@link CreateConnection} command.
@@ -50,6 +53,21 @@ final class CreateConnectionStrategy extends AbstractConnectivityCommandStrategy
super(CreateConnection.class);
}
+ @Override
+ public boolean isDefined(final CreateConnection command) {
+ return true;
+ }
+
+ @Override
+ public boolean isDefined(final Context context, @Nullable final Connection connection,
+ final CreateConnection command) {
+ final boolean connectionExists = Optional.ofNullable(connection)
+ .map(t -> !t.isDeleted())
+ .orElse(false);
+
+ return !connectionExists && Objects.equals(context.getState().id(), command.getEntityId());
+ }
+
@Override
protected Result> doApply(final Context context,
@Nullable final Connection entity,
@@ -57,7 +75,12 @@ protected Result> doApply(final Context co
final CreateConnection command,
@Nullable final Metadata metadata) {
- final Connection connection = command.getConnection().toBuilder().lifecycle(ACTIVE).build();
+ final Instant timestamp = getEventTimestamp();
+ final Connection connection = command.getConnection().toBuilder().lifecycle(ACTIVE)
+ .revision(nextRevision)
+ .created(timestamp)
+ .modified(timestamp)
+ .build();
final ConnectivityEvent> event =
ConnectionCreated.of(connection, nextRevision, getEventTimestamp(), command.getDittoHeaders(),
metadata);
@@ -78,4 +101,15 @@ protected Result> doApply(final Context co
return newMutationResult(command, event, response, true, false);
}
}
+
+ @Override
+ public Optional previousEntityTag(final CreateConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final CreateConnection command, @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/DeleteConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/DeleteConnectionStrategy.java
index f27f0559c68..459aed74e2d 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/DeleteConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/DeleteConnectionStrategy.java
@@ -16,23 +16,25 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.DeleteConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.DeleteConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionDeleted;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.DeleteConnection}
+ * This strategy handles the {@link DeleteConnection}
* command.
*/
final class DeleteConnectionStrategy extends AbstractConnectivityCommandStrategy {
@@ -61,4 +63,14 @@ protected Result> doApply(final Context co
return newMutationResult(StagedCommand.of(command, event, response, actions), event, response);
}
+ @Override
+ public Optional previousEntityTag(final DeleteConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.ofNullable(previousEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final DeleteConnection command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/EnableConnectionLogsStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/EnableConnectionLogsStrategy.java
index b6047eaebbd..5f2c17b9fe2 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/EnableConnectionLogsStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/EnableConnectionLogsStrategy.java
@@ -14,16 +14,21 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.EnableConnectionLogs;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.EnableConnectionLogsResponse;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.EnableConnectionLogs} command.
+ * This strategy handles the {@link EnableConnectionLogs} command.
*/
final class EnableConnectionLogsStrategy extends AbstractEphemeralStrategy {
@@ -40,4 +45,15 @@ WithDittoHeaders getResponse(final ConnectionState state, final DittoHeaders hea
List getActions() {
return Arrays.asList(ConnectionAction.BROADCAST_TO_CLIENT_ACTORS_IF_STARTED, ConnectionAction.SEND_RESPONSE, ConnectionAction.ENABLE_LOGGING);
}
+
+ @Override
+ public Optional previousEntityTag(final EnableConnectionLogs command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final EnableConnectionLogs command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/LoggingExpiredStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/LoggingExpiredStrategy.java
index 81f09893640..e44ccc0b1f5 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/LoggingExpiredStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/LoggingExpiredStrategy.java
@@ -12,11 +12,17 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.LoggingExpired;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.LoggingExpired} command.
+ * This strategy handles the {@link LoggingExpired} command.
*/
final class LoggingExpiredStrategy extends AbstractSingleActionStrategy {
@@ -28,4 +34,15 @@ final class LoggingExpiredStrategy extends AbstractSingleActionStrategy previousEntityTag(final LoggingExpired command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final LoggingExpired command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ModifyConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ModifyConnectionStrategy.java
index d8252b31f1b..2c9813c8a49 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ModifyConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ModifyConnectionStrategy.java
@@ -16,6 +16,7 @@
import static org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory.newErrorResult;
import static org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory.newMutationResult;
+import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -25,21 +26,22 @@
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionConfigurationInvalidException;
import org.eclipse.ditto.connectivity.model.ConnectivityStatus;
+import org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnection;
+import org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnectionResponse;
+import org.eclipse.ditto.connectivity.model.signals.events.ConnectionModified;
+import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
-import org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnection;
-import org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnectionResponse;
-import org.eclipse.ditto.connectivity.model.signals.events.ConnectionModified;
-import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.ModifyConnection} command.
+ * This strategy handles the {@link ModifyConnection} command.
*/
final class ModifyConnectionStrategy extends AbstractConnectivityCommandStrategy {
@@ -54,7 +56,11 @@ protected Result> doApply(final Context co
final ModifyConnection command,
@Nullable final Metadata metadata) {
- final Connection connection = command.getConnection().toBuilder().lifecycle(ACTIVE).build();
+ final Instant eventTs = getEventTimestamp();
+ final Connection connection = command.getConnection().toBuilder().lifecycle(ACTIVE)
+ .revision(nextRevision)
+ .modified(eventTs)
+ .build();
if (entity != null && entity.getConnectionType() != connection.getConnectionType() &&
!command.getDittoHeaders().isSudo()) {
return ResultFactory.newErrorResult(
@@ -96,4 +102,15 @@ protected Result> doApply(final Context co
return newMutationResult(command, event, response);
}
}
+
+ @Override
+ public Optional previousEntityTag(final ModifyConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.ofNullable(previousEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final ModifyConnection command, @Nullable final Connection newEntity) {
+ return Optional.of(getEntityOrThrow(newEntity)).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/OpenConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/OpenConnectionStrategy.java
index f5978d4ec57..14048528575 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/OpenConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/OpenConnectionStrategy.java
@@ -29,15 +29,16 @@
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.OpenConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.OpenConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionOpened;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
* This strategy handles the {@link OpenConnection} command.
@@ -69,4 +70,15 @@ protected Result> doApply(final Context co
return newMutationResult(StagedCommand.of(command, event, response, actions), event, response);
}
}
+
+ @Override
+ public Optional previousEntityTag(final OpenConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.ofNullable(previousEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final OpenConnection command, @Nullable final Connection newEntity) {
+ return Optional.of(getEntityOrThrow(newEntity)).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionLogsStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionLogsStrategy.java
index aa80d590d8a..fc59fe58805 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionLogsStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionLogsStrategy.java
@@ -14,13 +14,18 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.ResetConnectionLogs;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.ResetConnectionLogsResponse;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
/**
* This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.ResetConnectionLogs}
@@ -41,4 +46,15 @@ WithDittoHeaders getResponse(final ConnectionState state, final DittoHeaders hea
List getActions() {
return Arrays.asList(ConnectionAction.BROADCAST_TO_CLIENT_ACTORS_IF_STARTED, ConnectionAction.SEND_RESPONSE);
}
+
+ @Override
+ public Optional previousEntityTag(final ResetConnectionLogs command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final ResetConnectionLogs command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionMetricsStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionMetricsStrategy.java
index 40db31d9de2..f7a3faf7625 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionMetricsStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/ResetConnectionMetricsStrategy.java
@@ -14,16 +14,21 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.ResetConnectionMetrics;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.ResetConnectionMetricsResponse;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.ResetConnectionMetrics}
+ * This strategy handles the {@link ResetConnectionMetrics}
* command.
*/
final class ResetConnectionMetricsStrategy extends AbstractEphemeralStrategy {
@@ -41,4 +46,16 @@ WithDittoHeaders getResponse(final ConnectionState state, final DittoHeaders hea
List getActions() {
return Arrays.asList(ConnectionAction.BROADCAST_TO_CLIENT_ACTORS_IF_STARTED, ConnectionAction.SEND_RESPONSE);
}
+
+ @Override
+ public Optional previousEntityTag(final ResetConnectionMetrics command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final ResetConnectionMetrics command,
+ @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionLogsStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionLogsStrategy.java
index c93539703cf..988b39526cc 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionLogsStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionLogsStrategy.java
@@ -12,11 +12,17 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionLogs;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionLogs}
+ * This strategy handles the {@link RetrieveConnectionLogs}
* command.
*/
final class RetrieveConnectionLogsStrategy extends AbstractSingleActionStrategy {
@@ -29,4 +35,16 @@ final class RetrieveConnectionLogsStrategy extends AbstractSingleActionStrategy<
ConnectionAction getAction() {
return ConnectionAction.RETRIEVE_CONNECTION_LOGS;
}
+
+ @Override
+ public Optional previousEntityTag(final RetrieveConnectionLogs command,
+ @Nullable final Connection previousEntity) {
+ return nextEntityTag(command, previousEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final RetrieveConnectionLogs command,
+ @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionMetricsStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionMetricsStrategy.java
index a6de954cfb6..c9f801253a1 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionMetricsStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionMetricsStrategy.java
@@ -12,11 +12,17 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionMetrics;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionMetrics}
+ * This strategy handles the {@link RetrieveConnectionMetrics}
* command.
*/
final class RetrieveConnectionMetricsStrategy extends AbstractSingleActionStrategy {
@@ -29,4 +35,16 @@ final class RetrieveConnectionMetricsStrategy extends AbstractSingleActionStrate
ConnectionAction getAction() {
return ConnectionAction.RETRIEVE_CONNECTION_METRICS;
}
+
+ @Override
+ public Optional previousEntityTag(final RetrieveConnectionMetrics command,
+ @Nullable final Connection previousEntity) {
+ return nextEntityTag(command, previousEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final RetrieveConnectionMetrics command,
+ @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStatusStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStatusStrategy.java
index 98bba007344..17d63c06cf3 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStatusStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStatusStrategy.java
@@ -12,11 +12,17 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
+import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionStatus;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionStatus}
+ * This strategy handles the {@link RetrieveConnectionStatus}
* command.
*/
final class RetrieveConnectionStatusStrategy extends AbstractSingleActionStrategy {
@@ -29,4 +35,16 @@ final class RetrieveConnectionStatusStrategy extends AbstractSingleActionStrateg
ConnectionAction getAction() {
return ConnectionAction.RETRIEVE_CONNECTION_STATUS;
}
+
+ @Override
+ public Optional previousEntityTag(final RetrieveConnectionStatus command,
+ @Nullable final Connection previousEntity) {
+ return nextEntityTag(command, previousEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final RetrieveConnectionStatus command,
+ @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStrategy.java
index 0efc54c8747..8afe2019b92 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveConnectionStrategy.java
@@ -12,19 +12,26 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
+import java.util.Optional;
+
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.DittoHeadersSettable;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
-import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
+import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionNotAccessibleException;
+import org.eclipse.ditto.connectivity.model.signals.commands.query.ConnectivityQueryCommand;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
+import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
+import org.eclipse.ditto.json.JsonObject;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnection} command.
+ * This strategy handles the {@link RetrieveConnection} command.
*/
final class RetrieveConnectionStrategy extends AbstractConnectivityCommandStrategy {
@@ -41,9 +48,40 @@ protected Result> doApply(final Context co
if (entity != null) {
return ResultFactory.newQueryResult(command,
- RetrieveConnectionResponse.of(entity.toJson(), command.getDittoHeaders()));
+ appendETagHeaderIfProvided(command, getRetrieveConnectionResponse(entity, command), entity)
+ );
} else {
return ResultFactory.newErrorResult(notAccessible(context, command), command);
}
}
+
+ @Override
+ public Optional previousEntityTag(final RetrieveConnection command,
+ @Nullable final Connection previousEntity) {
+ return nextEntityTag(command, previousEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final RetrieveConnection command, @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ private static DittoHeadersSettable> getRetrieveConnectionResponse(@Nullable final Connection connection,
+ final ConnectivityQueryCommand command) {
+ if (connection != null) {
+ return RetrieveConnectionResponse.of(getConnectionJson(connection, command),
+ command.getDittoHeaders());
+ } else {
+ return ConnectionNotAccessibleException.newBuilder(((RetrieveConnection) command).getEntityId())
+ .dittoHeaders(command.getDittoHeaders())
+ .build();
+ }
+ }
+
+ private static JsonObject getConnectionJson(final Connection connection,
+ final ConnectivityQueryCommand command) {
+ return ((RetrieveConnection) command).getSelectedFields()
+ .map(selectedFields -> connection.toJson(command.getImplementedSchemaVersion(), selectedFields))
+ .orElseGet(() -> connection.toJson(command.getImplementedSchemaVersion()));
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveResolvedHonoConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveResolvedHonoConnectionStrategy.java
index d997d23cb4f..aef990c5d4c 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveResolvedHonoConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/RetrieveResolvedHonoConnectionStrategy.java
@@ -12,11 +12,17 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
+import java.util.Optional;
+
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.DittoHeadersSettable;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionType;
+import org.eclipse.ditto.connectivity.model.signals.commands.exceptions.ConnectionNotAccessibleException;
+import org.eclipse.ditto.connectivity.model.signals.commands.query.ConnectivityQueryCommand;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.commands.query.RetrieveResolvedHonoConnection;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
@@ -24,6 +30,7 @@
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
+import org.eclipse.ditto.json.JsonObject;
import akka.actor.ActorSystem;
@@ -49,13 +56,45 @@ protected Result> doApply(final Context co
final Result> result;
if (entity != null && entity.getConnectionType() == ConnectionType.HONO) {
- final var json = honoConnectionFactory.getHonoConnection(entity).toJson();
-
- result = ResultFactory.newQueryResult(command,
- RetrieveConnectionResponse.of(json, command.getDittoHeaders()));
+ return ResultFactory.newQueryResult(command,
+ appendETagHeaderIfProvided(command, getRetrieveConnectionResponse(entity, command), entity)
+ );
} else {
result = ResultFactory.newErrorResult(notAccessible(context, command), command);
}
return result;
}
+
+ @Override
+ public Optional previousEntityTag(final RetrieveResolvedHonoConnection command,
+ @Nullable final Connection previousEntity) {
+ return nextEntityTag(command, previousEntity);
+ }
+
+ @Override
+ public Optional nextEntityTag(final RetrieveResolvedHonoConnection command,
+ @Nullable final Connection newEntity) {
+ return Optional.ofNullable(newEntity).flatMap(EntityTag::fromEntity);
+ }
+
+ private DittoHeadersSettable> getRetrieveConnectionResponse(@Nullable final Connection connection,
+ final ConnectivityQueryCommand command) {
+ if (connection != null) {
+ return RetrieveConnectionResponse.of(getConnectionJson(connection, command),
+ command.getDittoHeaders());
+ } else {
+ return ConnectionNotAccessibleException.newBuilder(((RetrieveResolvedHonoConnection) command).getEntityId())
+ .dittoHeaders(command.getDittoHeaders())
+ .build();
+ }
+ }
+
+ private JsonObject getConnectionJson(final Connection connection,
+ final ConnectivityQueryCommand command) {
+
+ final Connection honoConnection = honoConnectionFactory.getHonoConnection(connection);
+ return ((RetrieveResolvedHonoConnection) command).getSelectedFields()
+ .map(selectedFields -> honoConnection.toJson(command.getImplementedSchemaVersion(), selectedFields))
+ .orElseGet(() -> honoConnection.toJson(command.getImplementedSchemaVersion()));
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/StagedCommandStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/StagedCommandStrategy.java
index b35efef2929..50921897e83 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/StagedCommandStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/StagedCommandStrategy.java
@@ -13,17 +13,19 @@
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
import java.text.MessageFormat;
+import java.util.Optional;
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectivityInternalErrorException;
+import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
-import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
/**
* This strategy handles the {@link org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand}
@@ -50,4 +52,15 @@ protected Result> doApply(final Context co
.dittoHeaders(command.getDittoHeaders())
.build(), command));
}
+
+ @Override
+ public Optional previousEntityTag(final StagedCommand command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final StagedCommand command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoAddConnectionLogEntryStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoAddConnectionLogEntryStrategy.java
index 9f71bd93e8b..3c59a182321 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoAddConnectionLogEntryStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoAddConnectionLogEntryStrategy.java
@@ -12,9 +12,12 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
+import java.util.Optional;
+
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.api.commands.sudo.SudoAddConnectionLogEntry;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionId;
@@ -58,4 +61,16 @@ private ConnectionLogger getAppropriateLogger(final ConnectionLoggerRegistry con
logEntry.getLogType(),
logEntry.getAddress().orElse(null));
}
+
+ @Override
+ public Optional previousEntityTag(final SudoAddConnectionLogEntry command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final SudoAddConnectionLogEntry command,
+ @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoRetrieveConnectionTagsStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoRetrieveConnectionTagsStrategy.java
index 0b8b7a30fa9..f447b56f18e 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoRetrieveConnectionTagsStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/SudoRetrieveConnectionTagsStrategy.java
@@ -12,9 +12,12 @@
*/
package org.eclipse.ditto.connectivity.service.messaging.persistence.strategies.commands;
+import java.util.Optional;
+
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.api.commands.sudo.SudoRetrieveConnectionTags;
import org.eclipse.ditto.connectivity.api.commands.sudo.SudoRetrieveConnectionTagsResponse;
import org.eclipse.ditto.connectivity.model.Connection;
@@ -46,4 +49,16 @@ protected Result> doApply(final Context co
return ResultFactory.newErrorResult(notAccessible(context, command), command);
}
}
+
+ @Override
+ public Optional previousEntityTag(final SudoRetrieveConnectionTags command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final SudoRetrieveConnectionTags command,
+ @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionConflictStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionConflictStrategy.java
index 6837d04401d..7ce4217df36 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionConflictStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionConflictStrategy.java
@@ -14,15 +14,18 @@
import static org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory.newQueryResult;
+import java.util.Optional;
+
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.TestConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.TestConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
* This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.TestConnection} command
@@ -44,4 +47,15 @@ protected Result> doApply(final Context co
return newQueryResult(command,
TestConnectionResponse.alreadyCreated(context.getState().id(), command.getDittoHeaders()));
}
+
+ @Override
+ public Optional previousEntityTag(final TestConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final TestConnection command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionStrategy.java
index 42ae1cc3514..4001ceb4f75 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/commands/TestConnectionStrategy.java
@@ -18,21 +18,23 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
-import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
-import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.TestConnection;
import org.eclipse.ditto.connectivity.model.signals.commands.modify.TestConnectionResponse;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionCreated;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectivityEvent;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionAction;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.ConnectionState;
+import org.eclipse.ditto.connectivity.service.messaging.persistence.stages.StagedCommand;
+import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
/**
* This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.commands.modify.TestConnection} command.
@@ -43,6 +45,20 @@ final class TestConnectionStrategy extends AbstractConnectivityCommandStrategy context, @Nullable final Connection connection, final TestConnection command) {
+ final boolean connectionExists = Optional.ofNullable(connection)
+ .map(t -> !t.isDeleted())
+ .orElse(false);
+
+ return !connectionExists && Objects.equals(context.getState().id(), command.getEntityId());
+ }
+
@Override
protected Result> doApply(final Context context,
@Nullable final Connection entity,
@@ -67,4 +83,15 @@ protected Result> doApply(final Context co
TestConnectionResponse.alreadyCreated(context.getState().id(), command.getDittoHeaders()));
}
}
+
+ @Override
+ public Optional previousEntityTag(final TestConnection command,
+ @Nullable final Connection previousEntity) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nextEntityTag(final TestConnection command, @Nullable final Connection newEntity) {
+ return Optional.empty();
+ }
}
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionClosedStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionClosedStrategy.java
index f106c7b3c4d..cc65c9f2dfc 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionClosedStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionClosedStrategy.java
@@ -18,11 +18,11 @@
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectivityStatus;
-import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionClosed;
+import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.events.ConnectionClosed} event.
+ * This strategy handles the {@link ConnectionClosed} event.
*/
final class ConnectionClosedStrategy implements EventStrategy {
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionCreatedStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionCreatedStrategy.java
index 10fc23649c7..27cabadce17 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionCreatedStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionCreatedStrategy.java
@@ -15,11 +15,11 @@
import javax.annotation.Nullable;
import org.eclipse.ditto.connectivity.model.Connection;
-import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionCreated;
+import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.events.ConnectionCreated} event.
+ * This strategy handles the {@link ConnectionCreated} event.
*/
final class ConnectionCreatedStrategy implements EventStrategy {
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionDeletedStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionDeletedStrategy.java
index ed958633104..5709cda7baf 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionDeletedStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionDeletedStrategy.java
@@ -16,11 +16,11 @@
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionLifecycle;
-import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionDeleted;
+import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.events.ConnectionDeleted} event.
+ * This strategy handles the {@link ConnectionDeleted} event.
*/
final class ConnectionDeletedStrategy implements EventStrategy {
@@ -29,7 +29,9 @@ final class ConnectionDeletedStrategy implements EventStrategy {
diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionOpenedStrategy.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionOpenedStrategy.java
index 517b0986a45..884dbd10742 100644
--- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionOpenedStrategy.java
+++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/persistence/strategies/events/ConnectionOpenedStrategy.java
@@ -18,17 +18,20 @@
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectivityStatus;
-import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionOpened;
+import org.eclipse.ditto.internal.utils.persistentactors.events.EventStrategy;
/**
- * This strategy handles the {@link org.eclipse.ditto.connectivity.model.signals.events.ConnectionOpened} event.
+ * This strategy handles the {@link ConnectionOpened} event.
*/
final class ConnectionOpenedStrategy implements EventStrategy {
@Override
public Connection handle(final ConnectionOpened event, @Nullable final Connection connection,
final long revision) {
- return checkNotNull(connection, "connection").toBuilder().connectionStatus(ConnectivityStatus.OPEN).build();
+ return checkNotNull(connection, "connection")
+ .toBuilder()
+ .connectionStatus(ConnectivityStatus.OPEN)
+ .build();
}
}
diff --git a/connectivity/service/src/main/resources/connectivity-extension.conf b/connectivity/service/src/main/resources/connectivity-extension.conf
index e69de29bb2d..0529d13a7a2 100644
--- a/connectivity/service/src/main/resources/connectivity-extension.conf
+++ b/connectivity/service/src/main/resources/connectivity-extension.conf
@@ -0,0 +1,5 @@
+ditto.extensions {
+ signal-transformers-provider.extension-config.signal-transformers = [
+ "org.eclipse.ditto.connectivity.service.enforcement.pre.ModifyToCreateConnectionTransformer", // always keep this as first transformer in order to guarantee that all following transformers know that the command is creating a connection instead of modifying it
+ ] ${ditto.extensions.signal-transformers-provider.extension-config.signal-transformers}
+}
diff --git a/connectivity/service/src/main/resources/connectivity.conf b/connectivity/service/src/main/resources/connectivity.conf
index 6c58dc8e0ee..5191d6e0982 100644
--- a/connectivity/service/src/main/resources/connectivity.conf
+++ b/connectivity/service/src/main/resources/connectivity.conf
@@ -205,11 +205,33 @@ ditto {
random-factor = 1.0
corrupted-receive-timeout = 600s
}
+
+ # For connectivity, this local ask timeout has to be higher as e.g. "openConnection" commands performed in a
+ # "staged" way will lead to quite some response times.
+ local-ask {
+ timeout = 50s
+ timeout = ${?THINGS_SUPERVISOR_LOCAL_ASK_TIMEOUT}
+ }
}
snapshot {
- threshold = 10
+ # the interval when to do snapshot for a Connection which had changes to it
interval = 15m
+ interval = ${?CONNECTION_SNAPSHOT_INTERVAL} # may be overridden with this environment variable
+
+ # the threshold after how many changes to a Connection to do a snapshot
+ threshold = 10
+ threshold = ${?CONNECTION_SNAPSHOT_THRESHOLD} # may be overridden with this environment variable
+ }
+
+ event {
+ # define the DittoHeaders to persist when persisting events to the journal
+ # those can e.g. be retrieved as additional "audit log" information when accessing a historical connection revision
+ historical-headers-to-persist = [
+ #"ditto-originator" # who (user-subject/connection-pre-auth-subject) issued the event
+ #"correlation-id"
+ ]
+ historical-headers-to-persist = ${?CONNECTION_EVENT_HISTORICAL_HEADERS_TO_PERSIST}
}
activity-check {
@@ -677,27 +699,62 @@ ditto {
}
cleanup {
+ # enabled configures whether background cleanup is enabled or not
+ # If enabled, stale "snapshot" and "journal" entries will be cleaned up from the MongoDB by a background process
enabled = true
enabled = ${?CLEANUP_ENABLED}
+ # history-retention-duration configures the duration of how long to "keep" events and snapshots before being
+ # allowed to remove them in scope of cleanup.
+ # If this e.g. is set to 30d - then effectively an event history of 30 days would be available via the read
+ # journal.
+ history-retention-duration = 30d
+ history-retention-duration = ${?CLEANUP_HISTORY_RETENTION_DURATION}
+
+ # quiet-period defines how long to stay in a state where the background cleanup is not yet started
+ # Applies after:
+ # - starting the service
+ # - each "completed" background cleanup run (all entities were cleaned up)
quiet-period = 5m
quiet-period = ${?CLEANUP_QUIET_PERIOD}
+ # interval configures how often a "credit decision" is made.
+ # The background cleanup works with a credit system and does only generate new "cleanup credits" if the MongoDB
+ # currently has capacity to do cleanups.
interval = 60s
interval = ${?CLEANUP_INTERVAL}
+ # timer-threshold configures the maximum database latency to give out credit for cleanup actions.
+ # If write operations to the MongoDB within the last `interval` had a `max` value greater to the configured
+ # threshold, no new cleanup credits will be issued for the next `interval`.
+ # Which throttles cleanup when MongoDB is currently under heavy (write) load.
timer-threshold = 150ms
timer-threshold = ${?CLEANUP_TIMER_THRESHOLD}
+ # credits-per-batch configures how many "cleanup credits" should be generated per `interval` as long as the
+ # write operations to the MongoDB are less than the configured `timer-threshold`.
+ # Limits the rate of cleanup actions to this many per credit decision interval.
+ # One credit means that the "journal" and "snapshot" entries of one entity are cleaned up each `interval`.
credits-per-batch = 1
credits-per-batch = ${?CLEANUP_CREDITS_PER_BATCH}
+ # reads-per-query configures the number of snapshots to scan per MongoDB query.
+ # Configuring this to high values will reduce the need to query MongoDB too often - it should however be aligned
+ # with the amount of "cleanup credits" issued per `interval` - in order to avoid long running queries.
reads-per-query = 100
reads-per-query = ${?CLEANUP_READS_PER_QUERY}
+ # writes-per-credit configures the number of documents to delete for each credit.
+ # If for example one entity would have 1000 journal entries to cleanup, a `writes-per-credit` of 100 would lead
+ # to 10 delete operations performed against MongoDB.
writes-per-credit = 100
writes-per-credit = ${?CLEANUP_WRITES_PER_CREDIT}
+ # delete-final-deleted-snapshot configures whether for a deleted entity, the final snapshot (containing the
+ # "deleted" information) should be deleted or not.
+ # If the final snapshot is not deleted, re-creating the entity will cause that the recreated entity starts with
+ # a revision number 1 higher than the previously deleted entity. If the final snapshot is deleted as well,
+ # recreation of an entity with the same ID will lead to revisionNumber=1 after its recreation.
delete-final-deleted-snapshot = false
delete-final-deleted-snapshot = ${?CLEANUP_DELETE_FINAL_DELETED_SNAPSHOT}
}
@@ -1113,9 +1170,31 @@ akka-contrib-mongodb-persistence-connection-journal {
}
}
+akka-contrib-mongodb-persistence-connection-journal-read {
+ class = "akka.contrib.persistence.mongodb.MongoReadJournal"
+ plugin-dispatcher = "connection-persistence-dispatcher"
+
+ overrides {
+ journal-collection = "connection_journal"
+ journal-index = "connection_journal_index"
+ realtime-collection = "connection_realtime"
+ metadata-collection = "connection_metadata"
+ }
+}
+
akka-contrib-mongodb-persistence-connection-snapshots {
class = "akka.contrib.persistence.mongodb.MongoSnapshots"
plugin-dispatcher = "connection-persistence-dispatcher"
+
+ circuit-breaker {
+ max-failures = 5 # if an exception during persisting an event/snapshot occurs this often -- a successful write resets the counter
+ max-failures = ${?SNAPSHOT_BREAKER_MAXTRIES}
+ call-timeout = 10s # MongoDB Timeouts causing the circuitBreaker to open
+ call-timeout = ${?SNAPSHOT_BREAKER_TIMEOUT}
+ reset-timeout = 6s # after this time in "Open" state, the cicuitBreaker is "Half-opened" again
+ reset-timeout = ${?SNAPSHOT_BREAKER_RESET}
+ }
+
overrides {
snaps-collection = "connection_snaps"
snaps-index = "connection_snaps_index"
diff --git a/connectivity/service/src/main/resources/javascript/incoming-mapping.js b/connectivity/service/src/main/resources/javascript/incoming-mapping.js
index d85f3c61cbf..b9f6beeda01 100644
--- a/connectivity/service/src/main/resources/javascript/incoming-mapping.js
+++ b/connectivity/service/src/main/resources/javascript/incoming-mapping.js
@@ -19,7 +19,7 @@ function mapToDittoProtocolMsg(
// ### Insert/adapt your mapping logic here.
// Use helper function Ditto.buildDittoProtocolMsg to build Ditto protocol message
// based on incoming payload.
- // See https://www.eclipse.org/ditto/connectivity-mapping.html#helper-functions for details.
+ // See https://www.eclipse.dev/ditto/connectivity-mapping.html#helper-functions for details.
// ### example code assuming the Ditto protocol content type for incoming messages.
if (contentType === 'application/vnd.eclipse.ditto+json') {
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalCommandRegistryTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalCommandRegistryTest.java
index 8feb00887e4..827d15af927 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalCommandRegistryTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalCommandRegistryTest.java
@@ -17,6 +17,7 @@
import org.eclipse.ditto.base.api.devops.signals.commands.ExecutePiggybackCommand;
import org.eclipse.ditto.base.api.persistence.cleanup.CleanupPersistence;
import org.eclipse.ditto.base.model.namespaces.signals.commands.PurgeNamespace;
+import org.eclipse.ditto.base.model.signals.commands.streaming.SubscribeForPersistedEvents;
import org.eclipse.ditto.base.service.cluster.ModifySplitBrainResolver;
import org.eclipse.ditto.connectivity.api.commands.sudo.SudoAddConnectionLogEntry;
import org.eclipse.ditto.connectivity.api.commands.sudo.SudoRetrieveConnectionIdsByTag;
@@ -66,7 +67,8 @@ public ConnectivityServiceGlobalCommandRegistryTest() {
PurgeEntities.class,
ModifySplitBrainResolver.class,
PublishSignal.class,
- SudoAddConnectionLogEntry.class
+ SudoAddConnectionLogEntry.class,
+ SubscribeForPersistedEvents.class
);
}
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalEventRegistryTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalEventRegistryTest.java
index 4df61bcefb2..ae6b9142a42 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalEventRegistryTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/ConnectivityServiceGlobalEventRegistryTest.java
@@ -12,6 +12,7 @@
*/
package org.eclipse.ditto.connectivity.service;
+import org.eclipse.ditto.base.model.signals.events.streaming.StreamingSubscriptionComplete;
import org.eclipse.ditto.connectivity.model.signals.events.ConnectionCreated;
import org.eclipse.ditto.internal.utils.persistentactors.EmptyEvent;
import org.eclipse.ditto.internal.utils.test.GlobalEventRegistryTestCases;
@@ -31,7 +32,8 @@ public ConnectivityServiceGlobalEventRegistryTest() {
SubscriptionCreated.class,
ThingsOutOfSync.class,
ThingSnapshotTaken.class,
- EmptyEvent.class
+ EmptyEvent.class,
+ StreamingSubscriptionComplete.class
);
}
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/ErrorHandlingActorTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/ErrorHandlingActorTest.java
index 7391eb710c3..d40dd99cad9 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/ErrorHandlingActorTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/ErrorHandlingActorTest.java
@@ -16,6 +16,7 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.assertj.core.api.Assertions;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionId;
@@ -91,7 +92,12 @@ public void tryCreateConnectionExpectSuccessResponseIndependentOfConnectionStatu
// create connection
final ConnectivityModifyCommand> command = CreateConnection.of(connection, DittoHeaders.empty());
underTest.tell(command, getRef());
- expectMsg(CreateConnectionResponse.of(connection, DittoHeaders.empty()));
+ final CreateConnectionResponse resp =
+ expectMsgClass(dilated(CONNECT_TIMEOUT), CreateConnectionResponse.class);
+ Assertions.assertThat(resp.getConnection())
+ .usingRecursiveComparison()
+ .ignoringFields("revision", "modified", "created")
+ .isEqualTo(connection);
}};
tearDown();
}
@@ -121,9 +127,12 @@ public void tryDeleteConnectionExpectErrorResponse() {
// create connection
final CreateConnection createConnection = CreateConnection.of(connection, DittoHeaders.empty());
underTest.tell(createConnection, getRef());
- final CreateConnectionResponse createConnectionResponse =
- CreateConnectionResponse.of(connection, DittoHeaders.empty());
- expectMsg(dilated(CONNECT_TIMEOUT), createConnectionResponse);
+ final CreateConnectionResponse resp =
+ expectMsgClass(dilated(CONNECT_TIMEOUT), CreateConnectionResponse.class);
+ Assertions.assertThat(resp.getConnection())
+ .usingRecursiveComparison()
+ .ignoringFields("revision", "modified", "created")
+ .isEqualTo(connection);
// delete connection
final ConnectivityModifyCommand> command = DeleteConnection.of(connectionId, DittoHeaders.empty());
@@ -147,9 +156,12 @@ private void tryModifyConnectionExpectErrorResponse(final String action) {
// create connection
final CreateConnection createConnection = CreateConnection.of(connection, DittoHeaders.empty());
underTest.tell(createConnection, getRef());
- final CreateConnectionResponse createConnectionResponse =
- CreateConnectionResponse.of(connection, DittoHeaders.empty());
- expectMsg(createConnectionResponse);
+ final CreateConnectionResponse resp =
+ expectMsgClass(dilated(CONNECT_TIMEOUT), CreateConnectionResponse.class);
+ Assertions.assertThat(resp.getConnection())
+ .usingRecursiveComparison()
+ .ignoringFields("revision", "modified", "created")
+ .isEqualTo(connection);
// modify connection
final ConnectivityModifyCommand> command;
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActorTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActorTest.java
index 7009ed6ff62..3ec16166e1c 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActorTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/OutboundMappingProcessorActorTest.java
@@ -63,11 +63,11 @@
import akka.testkit.TestProbe;
import akka.testkit.javadsl.TestKit;
-@FixMethodOrder(MethodSorters.DEFAULT)
/**
* Tests in addition to {@link MessageMappingProcessorActorTest}
* for {@link OutboundMappingProcessorActor} only.
*/
+@FixMethodOrder(MethodSorters.DEFAULT)
public final class OutboundMappingProcessorActorTest {
@ClassRule
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/TestConstants.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/TestConstants.java
index b0a787054d4..da6dcfb5ea6 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/TestConstants.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/TestConstants.java
@@ -110,6 +110,7 @@
import org.eclipse.ditto.internal.utils.cluster.DistPubSubAccess;
import org.eclipse.ditto.internal.utils.config.DefaultScopedConfig;
import org.eclipse.ditto.internal.utils.config.ScopedConfig;
+import org.eclipse.ditto.internal.utils.persistence.mongo.streaming.MongoReadJournal;
import org.eclipse.ditto.internal.utils.persistentactors.config.PingConfig;
import org.eclipse.ditto.internal.utils.protocol.config.ProtocolConfig;
import org.eclipse.ditto.internal.utils.pubsub.StreamingType;
@@ -126,9 +127,11 @@
import org.eclipse.ditto.protocol.TopicPath;
import org.eclipse.ditto.protocol.adapter.DittoProtocolAdapter;
import org.eclipse.ditto.things.model.Attributes;
+import org.eclipse.ditto.things.model.FeatureProperties;
import org.eclipse.ditto.things.model.Thing;
import org.eclipse.ditto.things.model.ThingId;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyThing;
+import org.eclipse.ditto.things.model.signals.events.FeatureDesiredPropertiesModified;
import org.eclipse.ditto.things.model.signals.events.ThingModified;
import org.eclipse.ditto.things.model.signals.events.ThingModifiedEvent;
import org.mockito.Mockito;
@@ -325,6 +328,14 @@ public static final class Things {
}
+ public static final class Feature {
+
+ public static final String FEATURE_ID = "Feature";
+ public static final FeatureProperties FEATURE_DESIRED_PROPERTIES = FeatureProperties.newBuilder()
+ .set("property", "test").build();
+
+ }
+
public static final class Authorization {
public static final String SUBJECT_ID = "some:subject";
@@ -924,7 +935,8 @@ public static ActorRef createConnectionSupervisorActor(final ConnectionId connec
final var enforcerActorPropsFactory =
ConnectionEnforcerActorPropsFactory.get(actorSystem, dittoExtensionsConfig);
final Props props =
- ConnectionSupervisorActor.props(commandForwarderActor, pubSubMediator, enforcerActorPropsFactory);
+ ConnectionSupervisorActor.props(commandForwarderActor, pubSubMediator, enforcerActorPropsFactory,
+ Mockito.mock(MongoReadJournal.class));
final Props shardRegionMockProps = Props.create(ShardRegionMockActor.class, props, connectionId.toString());
@@ -984,6 +996,12 @@ public static ThingModifiedEvent> thingModified(final Collection featureDesiredPropertiesModified(Collection readSubjects) {
+ DittoHeaders dittoHeaders = DittoHeaders.newBuilder().readGrantedSubjects(readSubjects).build();
+ return FeatureDesiredPropertiesModified.of(Things.THING_ID, Feature.FEATURE_ID,
+ Feature.FEATURE_DESIRED_PROPERTIES, 1, null, dittoHeaders, null);
+ }
+
public static MessageCommand, ?> sendThingMessage(final Collection readSubjects) {
final DittoHeaders dittoHeaders = DittoHeaders.newBuilder()
.readGrantedSubjects(readSubjects)
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactoryTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactoryTest.java
index 6a72dd50171..d2b7344b440 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactoryTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/hono/DefaultHonoConnectionFactoryTest.java
@@ -19,6 +19,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -28,6 +30,7 @@
import org.assertj.core.api.Assertions;
import org.eclipse.ditto.connectivity.model.Connection;
+import org.eclipse.ditto.connectivity.model.ConnectionId;
import org.eclipse.ditto.connectivity.model.ConnectivityModelFactory;
import org.eclipse.ditto.connectivity.model.HonoAddressAlias;
import org.eclipse.ditto.connectivity.model.ReplyTarget;
@@ -118,10 +121,11 @@ private Connection getExpectedHonoConnection(final Connection originalConnection
"correlation-id", "{{ header:correlation-id }}",
"subject", "{{ header:subject | fn:default(topic:action-subject) }}"
);
+ final var connectionId = originalConnection.getId();
return ConnectivityModelFactory.newConnectionBuilder(originalConnection)
.uri(honoConfig.getBaseUri().toString().replaceFirst("(\\S+://)(\\S+)",
- "$1" + honoConfig.getUserPasswordCredentials().getUsername()
- + ":" + honoConfig.getUserPasswordCredentials().getPassword()
+ "$1" + URLEncoder.encode(honoConfig.getUserPasswordCredentials().getUsername(), StandardCharsets.UTF_8)
+ + ":" + URLEncoder.encode(honoConfig.getUserPasswordCredentials().getPassword(), StandardCharsets.UTF_8)
+ "@$2"))
.validateCertificate(honoConfig.isValidateCertificates())
.specificConfig(Map.of(
@@ -131,22 +135,22 @@ private Connection getExpectedHonoConnection(final Connection originalConnection
)
.setSources(List.of(
ConnectivityModelFactory.newSourceBuilder(sourcesByAddress.get(TELEMETRY.getAliasValue()))
- .addresses(Set.of(getExpectedResolvedSourceAddress(TELEMETRY)))
+ .addresses(Set.of(getExpectedResolvedSourceAddress(TELEMETRY, connectionId)))
.replyTarget(ReplyTarget.newBuilder()
- .address(getExpectedResolvedCommandTargetAddress())
+ .address(getExpectedResolvedCommandTargetAddress(connectionId))
.headerMapping(commandReplyTargetHeaderMapping)
.build())
.build(),
ConnectivityModelFactory.newSourceBuilder(sourcesByAddress.get(EVENT.getAliasValue()))
- .addresses(Set.of(getExpectedResolvedSourceAddress(EVENT)))
+ .addresses(Set.of(getExpectedResolvedSourceAddress(EVENT, connectionId)))
.replyTarget(ReplyTarget.newBuilder()
- .address(getExpectedResolvedCommandTargetAddress())
+ .address(getExpectedResolvedCommandTargetAddress(connectionId))
.headerMapping(commandReplyTargetHeaderMapping)
.build())
.build(),
ConnectivityModelFactory.newSourceBuilder(
sourcesByAddress.get(COMMAND_RESPONSE.getAliasValue()))
- .addresses(Set.of(getExpectedResolvedSourceAddress(COMMAND_RESPONSE)))
+ .addresses(Set.of(getExpectedResolvedSourceAddress(COMMAND_RESPONSE, connectionId)))
.headerMapping(ConnectivityModelFactory.newHeaderMapping(Map.of(
"correlation-id", "{{ header:correlation-id }}",
"status", "{{ header:status }}"
@@ -155,8 +159,8 @@ private Connection getExpectedHonoConnection(final Connection originalConnection
))
.setTargets(List.of(
ConnectivityModelFactory.newTargetBuilder(targets.get(0))
- .address(getExpectedResolvedCommandTargetAddress())
- .originalAddress(getExpectedResolvedCommandTargetAddress())
+ .address(getExpectedResolvedCommandTargetAddress(connectionId))
+ .originalAddress(getExpectedResolvedCommandTargetAddress(connectionId))
.headerMapping(ConnectivityModelFactory.newHeaderMapping(
Stream.concat(
basicAdditionalTargetHeaderMappingEntries.entrySet().stream(),
@@ -166,8 +170,8 @@ private Connection getExpectedHonoConnection(final Connection originalConnection
))
.build(),
ConnectivityModelFactory.newTargetBuilder(targets.get(1))
- .address(getExpectedResolvedCommandTargetAddress())
- .originalAddress(getExpectedResolvedCommandTargetAddress())
+ .address(getExpectedResolvedCommandTargetAddress(connectionId))
+ .originalAddress(getExpectedResolvedCommandTargetAddress(connectionId))
.headerMapping(ConnectivityModelFactory.newHeaderMapping(
basicAdditionalTargetHeaderMappingEntries
))
@@ -182,12 +186,12 @@ private static Map getSourcesByAddress(final Iterable so
return result;
}
- private static String getExpectedResolvedSourceAddress(final HonoAddressAlias honoAddressAlias) {
- return "hono." + honoAddressAlias.getAliasValue();
+ private static String getExpectedResolvedSourceAddress(final HonoAddressAlias honoAddressAlias, final ConnectionId connectionId) {
+ return "hono." + honoAddressAlias.getAliasValue() + "." + connectionId;
}
- private static String getExpectedResolvedCommandTargetAddress() {
- return "hono." + HonoAddressAlias.COMMAND.getAliasValue() + "/{{thing:id}}";
+ private static String getExpectedResolvedCommandTargetAddress(final ConnectionId connectionId) {
+ return "hono." + HonoAddressAlias.COMMAND.getAliasValue() + "." + connectionId + "/{{thing:id}}";
}
}
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformerTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformerTest.java
index 7b10149aef7..b6c4ff97a77 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformerTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/mqtt/hivemq/publishing/ExternalMessageToMqttPublishTransformerTest.java
@@ -276,4 +276,32 @@ public void transformExternalMessageWithInvalidRetainValueYieldsTransformationFa
.hasCauseInstanceOf(InvalidHeaderValueException.class);
}
+ @Test
+ public void transformFullyFledgedExternalMessageWithBlankHeaderReturnsExpectedTransformationSuccessResult() {
+ final var correlationId = testNameCorrelationId.getCorrelationId();
+ final var genericMqttPublish = GenericMqttPublish.builder(MQTT_TOPIC, MQTT_QOS)
+ .retain(RETAIN)
+ .payload(PAYLOAD)
+ .correlationData(ByteBufferUtils.fromUtf8String(correlationId.toString()))
+ .contentType(CONTENT_TYPE.getValue())
+ .responseTopic(REPLY_TO_TOPIC)
+ .userProperties(USER_PROPERTIES)
+ .build();
+ Mockito.when(externalMessage.getHeaders())
+ .thenReturn(DittoHeaders.newBuilder()
+ .putHeader(MqttHeader.MQTT_TOPIC.getName(), MQTT_TOPIC.toString())
+ .putHeader(MqttHeader.MQTT_QOS.getName(), String.valueOf(MQTT_QOS.getCode()))
+ .putHeader(MqttHeader.MQTT_RETAIN.getName(), String.valueOf(genericMqttPublish.isRetain()))
+ .putHeader("ablankheader", "")
+ .correlationId(correlationId)
+ .putHeader(ExternalMessage.REPLY_TO_HEADER, REPLY_TO_TOPIC.toString())
+ .contentType(CONTENT_TYPE)
+ .putHeaders(USER_PROPERTIES.stream()
+ .collect(Collectors.toMap(UserProperty::name, UserProperty::value)))
+ .build());
+ Mockito.when(externalMessage.getBytePayload()).thenReturn(genericMqttPublish.getPayload());
+
+ assertThat(ExternalMessageToMqttPublishTransformer.transform(externalMessage, mqttPublishTarget))
+ .isEqualTo(TransformationSuccess.of(externalMessage, genericMqttPublish));
+ }
}
\ No newline at end of file
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActorTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActorTest.java
index e268f81ff06..5fc675a4de2 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActorTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceActorTest.java
@@ -19,7 +19,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
-import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
@@ -28,12 +27,16 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+
+import org.assertj.core.api.Assertions;
import org.awaitility.Awaitility;
import org.eclipse.ditto.base.api.persistence.cleanup.CleanupPersistence;
import org.eclipse.ditto.base.api.persistence.cleanup.CleanupPersistenceResponse;
import org.eclipse.ditto.base.model.correlationid.TestNameCorrelationId;
import org.eclipse.ditto.base.model.headers.DittoHeaderDefinition;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
+import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.connectivity.api.BaseClientState;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionConfigurationInvalidException;
@@ -82,15 +85,19 @@
import org.eclipse.ditto.internal.utils.akka.ActorSystemResource;
import org.eclipse.ditto.internal.utils.akka.PingCommand;
import org.eclipse.ditto.internal.utils.akka.controlflow.WithSender;
+import org.eclipse.ditto.internal.utils.persistence.mongo.streaming.MongoReadJournal;
import org.eclipse.ditto.internal.utils.persistentactors.AbstractPersistenceSupervisor;
import org.eclipse.ditto.internal.utils.test.Retry;
import org.eclipse.ditto.internal.utils.tracing.DittoTracingInitResource;
import org.eclipse.ditto.json.JsonFactory;
+import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.thingsearch.model.signals.commands.subscription.CreateSubscription;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.Mockito;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
@@ -210,8 +217,14 @@ public void testConnection() {
@Test
public void testConnectionTypeHono() throws IOException {
//GIVEN
- final var honoConnection = generateConnectionObjectFromJsonFile("hono-connection-custom-test.json");
- final var expectedHonoConnection = generateConnectionObjectFromJsonFile("hono-connection-custom-expected.json");
+ final var honoConnection = generateConnectionObjectFromJsonFile("hono-connection-custom-test.json", null)
+ .toBuilder()
+ .id(connectionId)
+ .build();
+ final var expectedHonoConnection = generateConnectionObjectFromJsonFile("hono-connection-custom-expected.json", connectionId)
+ .toBuilder()
+ .id(connectionId)
+ .build();
final var testConnection = TestConnection.of(honoConnection, dittoHeadersWithCorrelationId);
final var testProbe = actorSystemResource1.newTestProbe();
final var connectionSupervisorActor = createSupervisor();
@@ -234,7 +247,7 @@ public void testConnectionTypeHono() throws IOException {
@Test
public void testRestartByConnectionType() throws IOException {
// GIVEN
- final var honoConnection = generateConnectionObjectFromJsonFile("hono-connection-custom-test.json");
+ final var honoConnection = generateConnectionObjectFromJsonFile("hono-connection-custom-test.json", null);
mockClientActorProbe.setAutoPilot(new TestActor.AutoPilot() {
@Override
public TestActor.AutoPilot run(final ActorRef sender, final Object msg) {
@@ -248,13 +261,17 @@ public TestActor.AutoPilot run(final ActorRef sender, final Object msg) {
final var connectionActorProps = Props.create(ConnectionPersistenceActor.class,
() -> new ConnectionPersistenceActor(connectionId,
+ Mockito.mock(MongoReadJournal.class),
commandForwarderActor,
pubSubMediatorProbe.ref(),
Trilean.TRUE,
ConfigFactory.empty()));
final var underTest = actorSystemResource1.newActor(connectionActorProps, connectionId.toString());
- underTest.tell(createConnection(honoConnection), testProbe.ref());
+ final CreateConnection createConnection = createConnection(
+ honoConnection.toBuilder().id(connectionId).build()
+ );
+ underTest.tell(createConnection, testProbe.ref());
testProbe.expectMsgClass(FiniteDuration.apply(20, "s"), CreateConnectionResponse.class);
Arrays.stream(ConnectionType.values()).forEach(connectionType -> {
@@ -270,13 +287,19 @@ public TestActor.AutoPilot run(final ActorRef sender, final Object msg) {
});
}
- private static Connection generateConnectionObjectFromJsonFile(final String fileName) throws IOException {
+ private static Connection generateConnectionObjectFromJsonFile(final String fileName,
+ @Nullable ConnectionId connectionId) throws IOException {
final var testClassLoader = DefaultHonoConnectionFactoryTest.class.getClassLoader();
try (final var connectionJsonFileStreamReader = new InputStreamReader(
testClassLoader.getResourceAsStream(fileName)
)) {
- return ConnectivityModelFactory.connectionFromJson(
- JsonFactory.readFrom(connectionJsonFileStreamReader).asObject());
+ JsonObject jsonObject = JsonFactory.readFrom(connectionJsonFileStreamReader).asObject();
+ var connId = jsonObject.getValue("id");
+ if (connectionId != null && connId.isPresent()) {
+ var jsonString = jsonObject.formatAsString().replace(connId.get().asString(), connectionId);
+ jsonObject = JsonFactory.readFrom(jsonString).asObject();
+ }
+ return ConnectivityModelFactory.connectionFromJson(jsonObject);
}
}
@@ -350,7 +373,7 @@ public void manageConnection() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// close connection
final CloseConnection closeConnection = CloseConnection.of(connectionId, dittoHeadersWithCorrelationId);
@@ -375,7 +398,7 @@ public void deleteConnectionUpdatesSubscriptionsAndClosesConnection() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// delete connection
underTest.tell(DeleteConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -465,7 +488,7 @@ public void createConnectionAfterDeleted() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// delete connection
underTest.tell(DeleteConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -476,7 +499,7 @@ public void createConnectionAfterDeleted() {
// create connection again (while ConnectionActor is in deleted state)
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
}
@Test
@@ -488,7 +511,7 @@ public void openConnectionAfterDeletedFails() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// delete connection
underTest.tell(DeleteConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -509,7 +532,7 @@ public void createConnectionInClosedState() {
// create connection
underTest.tell(createConnection(closedConnection), testProbe.ref());
- testProbe.expectMsg(createConnectionResponse(closedConnection));
+ expectCreateConnectionResponse(testProbe, closedConnection);
// assert that client actor is not called for closed connection
mockClientActorProbe.expectNoMessage();
@@ -674,7 +697,7 @@ public void modifyConnectionInClosedState() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// close connection
underTest.tell(CloseConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -697,7 +720,7 @@ public void retrieveMetricsInClosedStateDoesNotStartClientActor() {
// create connection
underTest.tell(createConnection(closedConnection), testProbe.ref());
- testProbe.expectMsg(createConnectionResponse(closedConnection));
+ expectCreateConnectionResponse(testProbe, closedConnection);
mockClientActorProbe.expectNoMessage();
// retrieve metrics
@@ -721,7 +744,7 @@ public void modifyConnectionClosesAndRestartsClientActor() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// modify connection | Implicitly validates the restart by waiting for pubsub subscribe from client actor.
underTest.tell(ModifyConnection.of(connection, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -746,7 +769,7 @@ public void recoverOpenConnection() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// stop actor
actorSystemResource1.stopActor(underTest);
@@ -797,7 +820,7 @@ public void recoverModifiedConnection() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// modify connection
final var modifiedConnection = ConnectivityModelFactory.newConnectionBuilder(connection)
@@ -819,7 +842,11 @@ public void recoverModifiedConnection() {
// retrieve connection status
underTest.tell(RetrieveConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
- testProbe.expectMsg(RetrieveConnectionResponse.of(modifiedConnection.toJson(), dittoHeadersWithCorrelationId));
+ testProbe.expectMsg(RetrieveConnectionResponse.of(modifiedConnection.toJson(),
+ dittoHeadersWithCorrelationId.toBuilder()
+ .eTag(EntityTag.fromString("\"rev:2\""))
+ .build()
+ ));
}
@Test
@@ -831,7 +858,7 @@ public void recoverClosedConnection() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// close connection
underTest.tell(CloseConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -873,7 +900,7 @@ public void recoverDeletedConnection() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// delete connection
underTest.tell(DeleteConnection.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -893,14 +920,16 @@ public void recoverDeletedConnection() {
@Test
public void exceptionDuringClientActorPropsCreation() {
- final var connectionActorProps = ConnectionPersistenceActor.props(
- TestConstants.createRandomConnectionId(), commandForwarderActor, pubSubMediatorProbe.ref(),
- ConfigFactory.empty()
- );
-
final var testProbe = actorSystemResource1.newTestProbe();
final var supervisor = actorSystemResource1.newTestProbe();
- final var connectionActorRef = supervisor.childActorOf(connectionActorProps);
+
+ final var connectionActorProps = ConnectionPersistenceActor.props(
+ connectionId,
+ Mockito.mock(MongoReadJournal.class),
+ commandForwarderActor,
+ pubSubMediatorProbe.ref(),
+ ConfigFactory.empty());
+ final var connectionActorRef = supervisor.childActorOf(connectionActorProps, "connection");
// create connection
final CreateConnection createConnection = createConnection();
@@ -920,7 +949,8 @@ public void exceptionDuringClientActorPropsCreation() {
@Test
public void exceptionDueToCustomValidator() {
- final var connectionActorProps = ConnectionPersistenceActor.props(TestConstants.createRandomConnectionId(),
+ final var connectionActorProps = ConnectionPersistenceActor.props(connectionId,
+ Mockito.mock(MongoReadJournal.class),
commandForwarderActor,
pubSubMediatorProbe.ref(),
ConfigFactory.empty());
@@ -954,7 +984,7 @@ public void testResetConnectionMetrics() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// reset metrics
final var resetConnectionMetrics = ResetConnectionMetrics.of(connectionId, dittoHeadersWithCorrelationId);
@@ -973,7 +1003,7 @@ public void testConnectionActorRespondsToCleanupCommand() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// send cleanup command
underTest.tell(CleanupPersistence.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -989,7 +1019,7 @@ public void enableConnectionLogs() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
//Close logging which are automatically enabled via create connection
underTest.tell(LoggingExpired.of(connectionId, dittoHeadersWithCorrelationId), testProbe.ref());
@@ -1011,7 +1041,7 @@ public void retrieveLogsInClosedStateDoesNotStartClientActor() {
// create connection
underTest.tell(createConnection(closedConnection), testProbe.ref());
- testProbe.expectMsg(createConnectionResponse(closedConnection));
+ expectCreateConnectionResponse(testProbe, closedConnection);
clientActorProbe.expectNoMessage();
// retrieve logs
@@ -1042,7 +1072,7 @@ public void retrieveLogsIsAggregated() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// retrieve logs
final var retrieveConnectionLogs = RetrieveConnectionLogs.of(connectionId, dittoHeadersWithCorrelationId);
@@ -1069,7 +1099,7 @@ public void resetConnectionLogs() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// reset logs
underTest.tell(resetConnectionLogs, testProbe.ref());
@@ -1086,7 +1116,7 @@ public void enabledConnectionLogsAreEnabledAgainAfterModify() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
// Wait until connection is established
// enable connection logs
@@ -1115,7 +1145,7 @@ public void disabledConnectionLogsAreNotEnabledAfterModify() {
// create connection
underTest.tell(createConnection(), testProbe.ref());
simulateSuccessfulOpenConnectionInClientActor();
- testProbe.expectMsg(createConnectionResponse());
+ expectCreateConnectionResponse(testProbe, connection);
//Close logging which are automatically enabled via create connection
underTest.tell(LoggingExpired.of(connectionId, DittoHeaders.empty()), testProbe.ref());
@@ -1155,6 +1185,7 @@ public TestActor.AutoPilot run(final ActorRef sender, final Object msg) {
final var testProbe = actorSystemResource1.newTestProbe();
final var connectionActorProps = Props.create(ConnectionPersistenceActor.class,
() -> new ConnectionPersistenceActor(connectionId,
+ Mockito.mock(MongoReadJournal.class),
commandForwarderActor,
pubSubMediatorProbe.ref(),
Trilean.TRUE,
@@ -1211,12 +1242,14 @@ private static boolean isMessageSenderInstanceOf(final Object message, final Cla
}
@Test
+ @Ignore("TODO unignore and stabilize flaky test")
public void retriesStartingClientActor() {
final var parent = actorSystemResource1.newTestProbe();
final var underTest = parent.childActorOf(
Props.create(
ConnectionPersistenceActor.class,
() -> new ConnectionPersistenceActor(connectionId,
+ Mockito.mock(MongoReadJournal.class),
commandForwarderActor,
pubSubMediatorProbe.ref(),
Trilean.FALSE,
@@ -1230,9 +1263,13 @@ public void retriesStartingClientActor() {
final DittoHeaders headersIndicatingFailingInstantiation = createConnection.getDittoHeaders().toBuilder()
.putHeader("number-of-instantiation-failures",
String.valueOf(TestConstants.CONNECTION_CONFIG.getClientActorRestartsBeforeEscalation()))
+ .responseRequired(false)
.build();
underTest.tell(createConnection.setDittoHeaders(headersIndicatingFailingInstantiation), testProbe.ref());
- testProbe.expectMsg(createConnectionResponse().setDittoHeaders(headersIndicatingFailingInstantiation));
+ final CreateConnectionResponse resp =
+ expectCreateConnectionResponse(testProbe, connection);
+ assertThat(resp.getDittoHeaders())
+ .isEqualTo(headersIndicatingFailingInstantiation);
assertThat(underTest.isTerminated()).isFalse();
}
@@ -1244,6 +1281,7 @@ public void escalatesWhenClientActorFailsTooOften() {
Props.create(
ConnectionPersistenceActor.class,
() -> new ConnectionPersistenceActor(connectionId,
+ Mockito.mock(MongoReadJournal.class),
commandForwarderActor,
pubSubMediatorProbe.ref(),
Trilean.FALSE,
@@ -1257,9 +1295,13 @@ public void escalatesWhenClientActorFailsTooOften() {
final DittoHeaders headersIndicatingFailingInstantiation = createConnection.getDittoHeaders().toBuilder()
.putHeader("number-of-instantiation-failures",
String.valueOf(TestConstants.CONNECTION_CONFIG.getClientActorRestartsBeforeEscalation() + 1))
+ .responseRequired(false)
.build();
underTest.tell(createConnection.setDittoHeaders(headersIndicatingFailingInstantiation), testProbe.ref());
- testProbe.expectMsg(createConnectionResponse().setDittoHeaders(headersIndicatingFailingInstantiation));
+ final CreateConnectionResponse resp =
+ expectCreateConnectionResponse(testProbe, connection);
+ assertThat(resp.getDittoHeaders())
+ .isEqualTo(headersIndicatingFailingInstantiation);
testProbe.expectTerminated(underTest, FiniteDuration.apply(3, TimeUnit.SECONDS));
}
@@ -1272,7 +1314,7 @@ public void deleteConnectionCommandEmitsEvent() {
// create connection
underTest.tell(createConnection(closedConnection), testProbe.ref());
- testProbe.expectMsg(createConnectionResponse(closedConnection));
+ expectCreateConnectionResponse(testProbe, closedConnection);
pubSubMediatorProbe.expectMsgClass(DistributedPubSubMediator.Subscribe.class);
// delete connection
@@ -1301,14 +1343,17 @@ private CreateConnection createConnection(final Connection connection) {
return CreateConnection.of(connection, dittoHeadersWithCorrelationId);
}
- private CreateConnectionResponse createConnectionResponse() {
- return createConnectionResponse(connection);
- }
-
- private CreateConnectionResponse createConnectionResponse(final Connection connection) {
- return CreateConnectionResponse.of(connection, dittoHeadersWithCorrelationId);
+ private CreateConnectionResponse expectCreateConnectionResponse(final TestProbe probe,
+ final Connection theConnection) {
+ final CreateConnectionResponse resp =
+ probe.expectMsgClass(CreateConnectionResponse.class);
+ Assertions.assertThat(resp.getConnection())
+ .usingRecursiveComparison()
+ .ignoringFields("revision", "modified", "created")
+ .isEqualTo(theConnection);
+ return resp;
}
-
+
private void simulateSuccessfulOpenConnectionInClientActor() {
expectMockClientActorMessage(EnableConnectionLogs.of(connectionId, DittoHeaders.empty()));
expectMockClientActorMessage(OpenConnection.of(connectionId, dittoHeadersWithCorrelationId));
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceOperationsActorIT.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceOperationsActorIT.java
index 883749f2701..af6b7f9171e 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceOperationsActorIT.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/ConnectionPersistenceOperationsActorIT.java
@@ -34,10 +34,12 @@
import org.eclipse.ditto.connectivity.service.enforcement.ConnectionEnforcerActorPropsFactory;
import org.eclipse.ditto.internal.utils.config.ScopedConfig;
import org.eclipse.ditto.internal.utils.persistence.mongo.ops.eventsource.MongoEventSourceITAssertions;
+import org.eclipse.ditto.internal.utils.persistence.mongo.streaming.MongoReadJournal;
import org.eclipse.ditto.internal.utils.tracing.DittoTracingInitResource;
import org.eclipse.ditto.utils.jsr305.annotations.AllValuesAreNonnullByDefault;
import org.junit.ClassRule;
import org.junit.Test;
+import org.mockito.Mockito;
import com.typesafe.config.Config;
@@ -134,7 +136,8 @@ protected ActorRef startEntityActor(final ActorSystem system, final ActorRef pub
final var dittoExtensionsConfig = ScopedConfig.dittoExtension(system.settings().config());
final var enforcerActorPropsFactory = ConnectionEnforcerActorPropsFactory.get(system, dittoExtensionsConfig);
final Props props =
- ConnectionSupervisorActor.props(proxyActorProbe.ref(), pubSubMediator, enforcerActorPropsFactory);
+ ConnectionSupervisorActor.props(proxyActorProbe.ref(), pubSubMediator, enforcerActorPropsFactory,
+ Mockito.mock(MongoReadJournal.class));
return system.actorOf(props, String.valueOf(id));
}
diff --git a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/SignalFilterWithFilterTest.java b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/SignalFilterWithFilterTest.java
index dee549fae0d..41cbdbac662 100644
--- a/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/SignalFilterWithFilterTest.java
+++ b/connectivity/service/src/test/java/org/eclipse/ditto/connectivity/service/messaging/persistence/SignalFilterWithFilterTest.java
@@ -23,6 +23,8 @@
import java.util.Collections;
import java.util.List;
+import org.assertj.core.api.Assertions;
+import org.eclipse.ditto.base.model.signals.Signal;
import org.eclipse.ditto.connectivity.service.messaging.monitoring.ConnectionMonitorRegistry;
import org.eclipse.ditto.json.JsonPointer;
import org.eclipse.ditto.json.JsonValue;
@@ -253,4 +255,21 @@ public void applySignalFilterWithNamespacesAndRqlFilter() {
targetD); // THEN: only targetA and targetD should be in the filtered targets
}
+ /**
+ * Test that target filtering works also for desired properties events. Issue #1599
+ */
+ @Test
+ public void applySignalFilterOnFeatureDesiredPropertiesModified() {
+ Target target = ConnectivityModelFactory.newTargetBuilder().address("address")
+ .authorizationContext(newAuthContext(DittoAuthorizationContextType.UNSPECIFIED, AUTHORIZED))
+ .topics(ConnectivityModelFactory.newFilteredTopicBuilder(TWIN_EVENTS)
+ .withFilter("like(resource:path,'/features/" + TestConstants.Feature.FEATURE_ID + "*')")
+ .build()).build();
+ Connection connection = TestConstants.createConnection(CONNECTION_ID, target);
+ SignalFilter signalFilter = new SignalFilter(connection, connectionMonitorRegistry);
+ Signal> signal = TestConstants.featureDesiredPropertiesModified(Collections.singletonList(AUTHORIZED));
+
+ List filteredTargets = signalFilter.filter(signal);
+ Assertions.assertThat(filteredTargets).hasSize(1).contains(target);
+ }
}
diff --git a/connectivity/service/src/test/resources/hono-connection-custom-expected.json b/connectivity/service/src/test/resources/hono-connection-custom-expected.json
index c55d5c52168..6ccab3c12a8 100644
--- a/connectivity/service/src/test/resources/hono-connection-custom-expected.json
+++ b/connectivity/service/src/test/resources/hono-connection-custom-expected.json
@@ -3,11 +3,11 @@
"name": "Things-Hono Test 1",
"connectionType": "hono",
"connectionStatus": "open",
- "uri": "tcp://test_username:test_password@localhost:9922",
+ "uri": "tcp://test_username:test_password_w%2Fspecial_char@localhost:9922",
"sources": [
{
"addresses": [
- "hono.telemetry"
+ "hono.telemetry.test-connection-id"
],
"consumerCount": 1,
"qos": 0,
@@ -32,7 +32,7 @@
"implicitStandaloneThingCreation"
],
"replyTarget": {
- "address": "hono.command/{{thing:id}}",
+ "address": "hono.command.test-connection-id/{{thing:id}}",
"headerMapping": {
"device_id": "custom_value1",
"user_key1": "user_value1",
@@ -48,7 +48,7 @@
},
{
"addresses": [
- "hono.event"
+ "hono.event.test-connection-id"
],
"consumerCount": 1,
"qos": 1,
@@ -72,7 +72,7 @@
"implicitStandaloneThingCreation"
],
"replyTarget": {
- "address": "hono.command/{{thing:id}}",
+ "address": "hono.command.test-connection-id/{{thing:id}}",
"headerMapping": {
"device_id": "{{ thing:id }}",
"subject": "custom_value2",
@@ -88,7 +88,7 @@
},
{
"addresses": [
- "hono.command_response"
+ "hono.command_response.test-connection-id"
],
"consumerCount": 1,
"qos": 0,
@@ -120,7 +120,7 @@
],
"targets": [
{
- "address": "hono.command/{{thing:id}}",
+ "address": "hono.command.test-connection-id/{{thing:id}}",
"topics": [
"_/_/things/live/messages",
"_/_/things/live/commands"
@@ -137,7 +137,7 @@
}
},
{
- "address": "hono.command/{{thing:id}}",
+ "address": "hono.command.test-connection-id/{{thing:id}}",
"topics": [
"_/_/things/twin/events",
"_/_/things/live/events"
diff --git a/connectivity/service/src/test/resources/test.conf b/connectivity/service/src/test/resources/test.conf
index b4ec24e9b1c..a4676ce04ff 100644
--- a/connectivity/service/src/test/resources/test.conf
+++ b/connectivity/service/src/test/resources/test.conf
@@ -103,7 +103,7 @@ ditto {
sasl-mechanism = PLAIN
bootstrap-servers = "tcp://server1:port1,tcp://server2:port2,tcp://server3:port3"
username = test_username
- password = test_password
+ password = test_password_w/special_char
}
connection {
diff --git a/deployment/README.md b/deployment/README.md
index 92fdf572fdf..944fa99dc7a 100644
--- a/deployment/README.md
+++ b/deployment/README.md
@@ -2,11 +2,11 @@
In order to deploy/start Ditto, you have the following options:
+- [starting via Kubernetes and Helm](helm/README.md) (* preferred way for productive setup)
- [starting via Docker and docker-compose](docker/README.md)
- [starting via Kubernetes with k3s](kubernetes/k3s/README.md)
- [starting via Kubernetes with minikube](kubernetes/minikube/README.md)
- [starting via OpenShift](openshift/README.md)
-- [starting via Kubernetes and Helm](helm/README.md)
- [starting via Microsoft Azure](azure/README.md)
### Resource requirements
diff --git a/deployment/docker/README.md b/deployment/docker/README.md
index 87631e977a9..ffc4874e221 100755
--- a/deployment/docker/README.md
+++ b/deployment/docker/README.md
@@ -49,7 +49,7 @@ curl http://devops:foobar@localhost:8080/devops/config/gateway/?path=ditto
```
Or by going through the configuration files in this repository, all available configuration files are
-[linked here](https://www.eclipse.org/ditto/installation-operating.html#ditto-configuration).
+[linked here](https://www.eclipse.dev/ditto/installation-operating.html#ditto-configuration).
## Start Eclipse Ditto
diff --git a/deployment/docker/nginx.conf b/deployment/docker/nginx.conf
index 54586d8f8b1..1efa8b9a1f6 100755
--- a/deployment/docker/nginx.conf
+++ b/deployment/docker/nginx.conf
@@ -1,6 +1,8 @@
-worker_processes 1;
+worker_processes auto;
-events {worker_connections 1024;}
+events {
+ worker_connections 1024;
+}
http {
include /etc/nginx/mime.types;
diff --git a/deployment/docker/sandbox/docker-compose.yml b/deployment/docker/sandbox/docker-compose.yml
index aa070a754c1..8a8a2320a07 100644
--- a/deployment/docker/sandbox/docker-compose.yml
+++ b/deployment/docker/sandbox/docker-compose.yml
@@ -135,7 +135,7 @@ services:
- TZ=Europe/Berlin
- BIND_HOSTNAME=0.0.0.0
- ENABLE_PRE_AUTHENTICATION=true
- - DEVOPS_SECURE_STATUS=false
+ - DEVOPS_STATUS_SECURED=false
- DITTO_DEVOPS_FEATURE_WOT_INTEGRATION_ENABLED=true
- |
JAVA_TOOL_OPTIONS=
diff --git a/deployment/docker/sandbox/html/index.html b/deployment/docker/sandbox/html/index.html
index 4bcc6313894..cd904d960fe 100644
--- a/deployment/docker/sandbox/html/index.html
+++ b/deployment/docker/sandbox/html/index.html
@@ -105,7 +105,7 @@ -
API. The Swagger UI for exploring the API will require authentication.
- Visit the Eclipse Ditto documentation in
+ Visit the Eclipse Ditto documentation in
order to learn more about the project.
diff --git a/deployment/docker/sandbox/nginx.conf b/deployment/docker/sandbox/nginx.conf
index 66a854e8050..2cc9ce73cf8 100644
--- a/deployment/docker/sandbox/nginx.conf
+++ b/deployment/docker/sandbox/nginx.conf
@@ -1,6 +1,8 @@
-worker_processes 1;
+worker_processes auto;
-events {worker_connections 1024;}
+events {
+ worker_connections 1024;
+}
http {
charset utf-8;
diff --git a/deployment/helm/README.md b/deployment/helm/README.md
index 7a15958ac0f..85020b4a66a 100644
--- a/deployment/helm/README.md
+++ b/deployment/helm/README.md
@@ -1,21 +1,20 @@
# Eclipse Ditto :: Helm
-The Ditto Helm chart is managed at the [Eclipse IoT Packages](https://github.com/eclipse/packages/tree/master/charts/ditto) project.
+The official Ditto Helm chart is managed here, in folder [ditto](ditto/).
+It is deployed as "OCI artifact" to Docker Hub at: https://hub.docker.com/r/eclipse/ditto
## Install Ditto via Helm Chart
To install the chart with the release name eclipse-ditto, run the following commands:
```shell script
-helm repo add eclipse-iot https://www.eclipse.org/packages/charts/
-helm repo update
-helm install eclipse-ditto eclipse-iot/ditto
+helm install -n ditto my-ditto oci://registry-1.docker.io/eclipse/ditto --version --wait
```
# Uninstall the Helm Chart
-To uninstall/delete the eclipse-ditto deployment:
+To uninstall/delete the `my-ditto` deployment:
```shell script
-helm delete eclipse-ditto
+helm uninstall my-ditto
```
diff --git a/deployment/helm/ditto/.gitignore b/deployment/helm/ditto/.gitignore
new file mode 100644
index 00000000000..f791801bcae
--- /dev/null
+++ b/deployment/helm/ditto/.gitignore
@@ -0,0 +1,2 @@
+charts/
+Chart.lock
diff --git a/deployment/helm/ditto/.helmignore b/deployment/helm/ditto/.helmignore
new file mode 100644
index 00000000000..d8882204716
--- /dev/null
+++ b/deployment/helm/ditto/.helmignore
@@ -0,0 +1,19 @@
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/deployment/helm/ditto/Chart.yaml b/deployment/helm/ditto/Chart.yaml
new file mode 100644
index 00000000000..69ec7769588
--- /dev/null
+++ b/deployment/helm/ditto/Chart.yaml
@@ -0,0 +1,36 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+---
+apiVersion: v2
+name: ditto
+description: |
+ Eclipse Ditto™ is a technology in the IoT implementing a software pattern called “digital twins”.
+ A digital twin is a virtual, cloud based, representation of his real world counterpart
+ (real world “Things”, e.g. devices like sensors, smart heating, connected cars, smart grids, EV charging stations etc).
+type: application
+version: 3.3.4 # chart version is effectively set by release-job
+appVersion: 3.3.4
+keywords:
+ - iot-chart
+ - digital-twin
+ - IoT
+home: https://www.eclipse.dev/ditto
+sources:
+ - https://github.com/eclipse-ditto/ditto
+icon: https://www.eclipse.dev/ditto/images/ditto.svg
+maintainers:
+ - name: thjaeckle
+ email: thomas.jaeckle@beyonnex.io
+dependencies:
+ - name: mongodb
+ repository: https://charts.bitnami.com/bitnami
+ version: ^12.x
+ condition: mongodb.enabled
diff --git a/deployment/helm/ditto/README.md b/deployment/helm/ditto/README.md
new file mode 100644
index 00000000000..631852cb0bb
--- /dev/null
+++ b/deployment/helm/ditto/README.md
@@ -0,0 +1,137 @@
+# Eclipse Ditto
+
+## Introduction
+
+[Eclipse Ditto™](https://www.eclipse.dev/ditto/) is a technology in the IoT implementing a software pattern
+called “digital twins”. A digital twin is a virtual, cloud based, representation of his real world counterpart
+(real world “Things”, e.g. devices like sensors, smart heating, connected cars, smart grids, EV charging stations, …).
+
+This chart uses `eclipse/ditto-XXX` containers to run Ditto inside Kubernetes.
+
+## Prerequisites
+
+Installing Ditto using the chart requires the Helm tool to be installed as described on the
+[IoT Packages chart repository prerequisites](https://www.eclipse.org/packages/prereqs/) page.
+
+TL;DR:
+
+* have a correctly configured [`kubectl`](https://kubernetes.io/docs/tasks/tools/#kubectl) (either against a local or remote k8s cluster)
+* have [Helm installed](https://helm.sh/docs/intro/)
+
+The Helm chart is being tested to successfully install on the most recent Kubernetes versions.
+
+## Installing the Chart
+
+The instructions below illustrate how Ditto can be installed to the `ditto` namespace in a Kubernetes cluster using
+release name `eclipse-ditto`.
+The commands can easily be adapted to use a different namespace or release name.
+
+The target namespace in Kubernetes only needs to be created if it doesn't exist yet:
+
+```bash
+kubectl create namespace ditto
+```
+
+The chart can then be installed to namespace `ditto` using release name `my-ditto`:
+
+```bash
+helm install --dependency-update -n ditto my-ditto oci://registry-1.docker.io/eclipse/ditto --version --wait
+```
+
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-ditto` release:
+
+```bash
+helm uninstall -n ditto my-ditto
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+## Configuration
+
+Please view the `values.yaml` for the list of possible configuration values with its documentation.
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example:
+
+```bash
+helm install -n ditto my-ditto oci://registry-1.docker.io/eclipse/ditto --version --set swaggerui.enabled=false
+```
+
+Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart.
+
+## Chart configuration options
+
+Please consult the [values.yaml](https://github.com/eclipse-ditto/ditto/blob/master/deployment/helm/ditto/values.yaml)
+for all available configuration options of the Ditto Helm chart.
+
+### Scaling options
+
+Please note the defaults the chart comes with:
+* the default deploy 1 instance per Ditto service
+* each Ditto service is configured to require:
+ * 0.5 CPUs
+ * 1024 MiB of memory
+
+Adjust this to your requirements, e.g. scale horizontally by configuring a higher `replicaCount` or vertically by
+configuring more resources.
+
+## Advanced configuration options
+
+Even more configuration options, not exposed to the `values.yaml`, can be configured using either environment variables
+or Java "System properties".
+To inspect all available configuration options, please inspect Ditto's service configurations:
+
+* [policies.conf](https://github.com/eclipse-ditto/ditto/blob/master/policies/service/src/main/resources/policies.conf)
+* [things.conf](https://github.com/eclipse-ditto/ditto/blob/master/things/service/src/main/resources/things.conf)
+* [things-search.conf](https://github.com/eclipse-ditto/ditto/blob/master/thingsearch/service/src/main/resources/search.conf)
+* [connectivity.conf](https://github.com/eclipse-ditto/ditto/blob/master/connectivity/service/src/main/resources/connectivity.conf)
+* [gateway.conf](https://github.com/eclipse-ditto/ditto/blob/master/gateway/service/src/main/resources/gateway.conf)
+
+
+### Configuration via environment variables
+
+In order to provide an environment variable config overwrite, simply put the environment variable in the `extraEnv`
+of the Ditto service you want to specify the configuration for.
+
+E.g. if you want to configure the `LOG_INCOMING_MESSAGES` for the `things` service to be disabled:
+```yaml
+things:
+ # ...
+ extraEnv:
+ - name: LOG_INCOMING_MESSAGES
+ value: "false"
+```
+
+### Configuration via system properties
+
+Not all Ditto/Akka configuration options have an environment variable overwrite defined in the configuration.
+For configurations without such an environment variable overwrite, the option can be configured via Java system property.
+The documentation on how this works can be found in the
+[HOCON documentation](https://github.com/lightbend/config/blob/main/HOCON.md#conventional-override-by-system-properties),
+which is the configuration format Ditto uses.
+
+E.g. if you want to adjust the `journal-collection` name which the `things` service uses to write its
+journal entries to MongoDB (which can be found [here](https://github.com/eclipse-ditto/ditto/blob/33a38bc04b47d0167ba0e99fe76d96a54aa3d162/things/service/src/main/resources/things.conf#L268)),
+simply configure:
+
+
+```yaml
+things:
+ # ...
+ systemProps:
+ - "-Dakka-contrib-mongodb-persistence-things-journal.overrides.journal-collection=another_fancy_name"
+```
+
+
+## Troubleshooting
+
+If you experience high resource consumption (either CPU or RAM or both), you can limit the resource usage by
+specifying resource limits.
+This can be done individually for each single component.
+Here is an example how to limit CPU to 0.25 Cores and RAM to 512 MiB for the `connectivity` service:
+
+```bash
+helm upgrade -n ditto eclipse-ditto . --install --set connectivity.resources.limits.cpu=0.25 --set connectivity.resources.limits.memory=512Mi
+```
diff --git a/deployment/helm/ditto/ci/ci-workflow-values.yaml b/deployment/helm/ditto/ci/ci-workflow-values.yaml
new file mode 100644
index 00000000000..f47e2e3a19c
--- /dev/null
+++ b/deployment/helm/ditto/ci/ci-workflow-values.yaml
@@ -0,0 +1,123 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+---
+
+# profile for installing Ditto
+# - with Ingress enabled and low cpu requests
+
+## ingress configuration
+ingress:
+ enabled: true
+ host: ditto.example.com
+ annotations:
+ # kubernetes.io/tls-acme: "true"
+ tls:
+ - secretName: ditto-tls
+ hosts:
+ - ditto.example.com
+
+## ----------------------------------------------------------------------------
+## policies configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-policies.html
+policies:
+ resources:
+ cpu: 0.15
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+
+## ----------------------------------------------------------------------------
+## things configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-things.html
+things:
+ resources:
+ cpu: 0.15
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+
+## ----------------------------------------------------------------------------
+## things-search configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-things-search.html
+thingsSearch:
+ resources:
+ cpu: 0.15
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+
+## ----------------------------------------------------------------------------
+## connectivity configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-connectivity.html
+connectivity:
+ resources:
+ cpu: 0.15
+ memoryMi: 768
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 55
+
+## ----------------------------------------------------------------------------
+## gateway configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-gateway.html
+gateway:
+ resources:
+ cpu: 0.15
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+
+## ----------------------------------------------------------------------------
+## nginx configuration
+nginx:
+ resources:
+ cpu: 0.05
+ memoryMi: 64
+ initContainers:
+ waitForGateway:
+ enabled: false
+
+## ----------------------------------------------------------------------------
+## swaggerui configuration
+swaggerui:
+ resources:
+ cpu: 0.05
+
+## ----------------------------------------------------------------------------
+## dittoui configuration
+dittoui:
+ resources:
+ cpu: 0.05
+
+## ----------------------------------------------------------------------------
+## mongodb dependency chart configuration
+mongodb:
+ enabled: true
+ resources:
+ limits:
+ cpu: 100m
+ memory: 256Mi
+ requests:
+ cpu: 100m
+ memory: 256Mi
+ readinessProbe:
+ enabled: false
+ livenessProbe:
+ enabled: false
+ auth:
+ enabled: false
+ persistence:
+ enabled: false
diff --git a/deployment/helm/ditto/dittoui-config/nginx.conf b/deployment/helm/ditto/dittoui-config/nginx.conf
new file mode 100644
index 00000000000..05e57d72a68
--- /dev/null
+++ b/deployment/helm/ditto/dittoui-config/nginx.conf
@@ -0,0 +1,36 @@
+worker_processes 1;
+
+error_log /var/log/nginx/error.log notice;
+pid /tmp/nginx.pid;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ proxy_temp_path /tmp/proxy_temp;
+ client_body_temp_path /tmp/client_temp;
+ fastcgi_temp_path /tmp/fastcgi_temp;
+ uwsgi_temp_path /tmp/uwsgi_temp;
+ scgi_temp_path /tmp/scgi_temp;
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ server_tokens off; # Hide Nginx version
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ include /etc/nginx/conf.d/*.conf;
+}
diff --git a/deployment/helm/ditto/local-values.yaml b/deployment/helm/ditto/local-values.yaml
new file mode 100644
index 00000000000..faeb79351f3
--- /dev/null
+++ b/deployment/helm/ditto/local-values.yaml
@@ -0,0 +1,166 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+---
+# Default values for ditto.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+global:
+ jwtOnly: false
+ basicAuthUsers:
+ ditto:
+ user: ditto
+ password: ditto
+ logging:
+ customConfigFile:
+ enabled: true
+
+## ----------------------------------------------------------------------------
+## policies configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-policies.html
+policies:
+ resources:
+ cpu: 0.2
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+ config:
+ persistence:
+ events:
+ historicalHeadersToPersist:
+ - "ditto-originator"
+ - "ditto-origin"
+ - "correlation-id"
+ entityCreation:
+ grants:
+ - namespaces:
+ - "org.eclipse.ditto.room"
+ authSubjects:
+ - "connection:some"
+
+## ----------------------------------------------------------------------------
+## things configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-things.html
+things:
+ resources:
+ cpu: 0.2
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+ config:
+ persistence:
+ events:
+ historicalHeadersToPersist:
+ - "ditto-originator"
+ - "ditto-origin"
+ - "correlation-id"
+ entityCreation:
+ grants:
+ - namespaces:
+ - "org.eclipse.ditto.room"
+ authSubjects:
+ - "connection:some"
+
+## ----------------------------------------------------------------------------
+## things-search configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-things-search.html
+thingsSearch:
+ resources:
+ cpu: 0.2
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+
+## ----------------------------------------------------------------------------
+## connectivity configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-connectivity.html
+connectivity:
+ resources:
+ cpu: 0.2
+ memoryMi: 768
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 55
+
+## ----------------------------------------------------------------------------
+## gateway configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-gateway.html
+gateway:
+ resources:
+ cpu: 0.2
+ memoryMi: 512
+ jvm:
+ activeProcessorCount: 2
+ heapRamPercentage: 50
+ config:
+ authentication:
+ enablePreAuthentication: true
+ oauth:
+ openidConnectIssuers:
+ example:
+ issuer: "example.com"
+ authSubjects:
+ - "{{ jwt:sub }}"
+ - "{{ jwt:groups }}"
+ devops:
+ ## this controls whether /devops resource is secured or not
+ secured: true
+ authMethod: "basic"
+ oauth:
+ # configure the amount of clock skew in seconds to tolerate when verifying the local time against the exp and nbf claims
+ allowedClockSkew: 20s
+ openidConnectIssuers:
+ example-ops:
+ issuer: "example.com"
+ authSubjects:
+ - "{{ jwt:sub }}"
+ - "{{ jwt:groups }}"
+ oauthSubjects:
+ - "example-ops:devops-admin"
+ ## this controls whether /status resource is secured or not
+ statusSecured: true
+ statusAuthMethod: "basic"
+ # array of strings for subjects authorized to use "/status" API
+ statusOauthSubjects:
+ - "example-ops:devops-admin"
+
+## ----------------------------------------------------------------------------
+## nginx configuration
+nginx:
+ resources:
+ cpu: 0.1
+ memoryMi: 64
+ initContainers:
+ waitForGateway:
+ enabled: false
+
+## ----------------------------------------------------------------------------
+## mongodb dependency chart configuration
+mongodb:
+ enabled: false
+ resources:
+ limits:
+ cpu: 100m
+ memory: 256Mi
+ requests:
+ cpu: 100m
+ memory: 256Mi
+ readinessProbe:
+ enabled: false
+ livenessProbe:
+ enabled: false
+ auth:
+ enabled: false
+ persistence:
+ enabled: false
diff --git a/deployment/helm/ditto/logback-config/connectivity.xml b/deployment/helm/ditto/logback-config/connectivity.xml
new file mode 100644
index 00000000000..0d22766ad5c
--- /dev/null
+++ b/deployment/helm/ditto/logback-config/connectivity.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+ sourceActorSystem
+ akkaUid
+ akkaTimestamp
+ x-correlation-id=correlation-id
+ connection-id=ditto-connection-id
+ connection-type=ditto-connection-type
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/logback-config/gateway.xml b/deployment/helm/ditto/logback-config/gateway.xml
new file mode 100755
index 00000000000..348e3a63875
--- /dev/null
+++ b/deployment/helm/ditto/logback-config/gateway.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ sourceActorSystem
+ akkaUid
+ akkaTimestamp
+ x-correlation-id=correlation-id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/logback-config/policies.xml b/deployment/helm/ditto/logback-config/policies.xml
new file mode 100755
index 00000000000..348e3a63875
--- /dev/null
+++ b/deployment/helm/ditto/logback-config/policies.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ sourceActorSystem
+ akkaUid
+ akkaTimestamp
+ x-correlation-id=correlation-id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/logback-config/things.xml b/deployment/helm/ditto/logback-config/things.xml
new file mode 100755
index 00000000000..348e3a63875
--- /dev/null
+++ b/deployment/helm/ditto/logback-config/things.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ sourceActorSystem
+ akkaUid
+ akkaTimestamp
+ x-correlation-id=correlation-id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/logback-config/thingssearch.xml b/deployment/helm/ditto/logback-config/thingssearch.xml
new file mode 100755
index 00000000000..348e3a63875
--- /dev/null
+++ b/deployment/helm/ditto/logback-config/thingssearch.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ sourceActorSystem
+ akkaUid
+ akkaTimestamp
+ x-correlation-id=correlation-id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/nginx-config/ditto-down.svg b/deployment/helm/ditto/nginx-config/ditto-down.svg
new file mode 100644
index 00000000000..679cf25b967
--- /dev/null
+++ b/deployment/helm/ditto/nginx-config/ditto-down.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/nginx-config/ditto-up.svg b/deployment/helm/ditto/nginx-config/ditto-up.svg
new file mode 100644
index 00000000000..33b074ba859
--- /dev/null
+++ b/deployment/helm/ditto/nginx-config/ditto-up.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/nginx-config/index.html b/deployment/helm/ditto/nginx-config/index.html
new file mode 100644
index 00000000000..abed686cd52
--- /dev/null
+++ b/deployment/helm/ditto/nginx-config/index.html
@@ -0,0 +1,174 @@
+
+
+
+ Eclipse Ditto
+
+
+
+
+
+
+
+
+ Eclipse Ditto
+
+
From here, you can:
+
+
+ Health
+
+
+ Statistics
+
+
+ persisted Things
+
+
+ currently "hot" Things (loaded in memory)
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/nginx-config/nginx-cors.conf b/deployment/helm/ditto/nginx-config/nginx-cors.conf
new file mode 100644
index 00000000000..f9ed9330776
--- /dev/null
+++ b/deployment/helm/ditto/nginx-config/nginx-cors.conf
@@ -0,0 +1,39 @@
+#
+# CORS header support
+#
+# As of Nginx 1.7.5, add_header supports an "always" parameter which
+# allows CORS to work if the backend returns 4xx or 5xx status code.
+#
+# For more information on CORS, please see: http://enable-cors.org/
+# From this Gist: https://gist.github.com/Stanback/7145487
+# And this: https://gist.github.com/pauloricardomg/7084524
+#
+
+set $cors '1';
+
+# OPTIONS indicates a CORS pre-flight request
+if ($request_method = 'OPTIONS') {
+ set $cors "${cors}o";
+}
+
+if ($cors = '1') {
+ add_header 'Access-Control-Allow-Origin' '$http_origin' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
+ add_header 'Access-Control-Allow-Credentials' 'true' always;
+ add_header 'Access-Control-Allow-Headers' '$http_access_control_request_headers' always;
+ add_header 'Access-Control-Expose-Headers' '*' always;
+}
+
+# OPTIONS (pre-flight) request from allowed CORS domain. return response directly
+if ($cors = '1o') {
+ # Tell client that this pre-flight info is valid for 20 days
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Access-Control-Allow-Origin' '$http_origin' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
+ add_header 'Access-Control-Allow-Credentials' 'true' always;
+ add_header 'Access-Control-Allow-Headers' '$http_access_control_request_headers' always;
+ add_header 'Access-Control-Expose-Headers' '*' always;
+ add_header 'Content-Type' 'text/plain charset=UTF-8';
+ add_header 'Content-Length' 0;
+ return 200;
+}
diff --git a/deployment/helm/ditto/swaggerui-config/index.html b/deployment/helm/ditto/swaggerui-config/index.html
new file mode 100644
index 00000000000..460d0fe7bc6
--- /dev/null
+++ b/deployment/helm/ditto/swaggerui-config/index.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+ Eclipse Ditto - HTTP API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment/helm/ditto/templates/NOTES.txt b/deployment/helm/ditto/templates/NOTES.txt
new file mode 100644
index 00000000000..1b2fcbce4be
--- /dev/null
+++ b/deployment/helm/ditto/templates/NOTES.txt
@@ -0,0 +1,33 @@
+Eclipse Ditto successfully installed!
+
+{{- if ( not .Values.openshift.routes.enabled ) }}
+Access Ditto in your browser (http://localhost:8080) by running:
+
+ kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "ditto.fullname" . }}-nginx 8080:8080
+
+The /status resource can be accessed by:
+
+ export STATUS_PWD=$(kubectl --namespace {{ .Release.Namespace }} get secret {{ include "ditto.fullname" . }}-gateway-secret -o jsonpath="{.data.status-password}" | base64 --decode)
+ curl -i -X GET "http://devops:${STATUS_PWD}@localhost:8080/status"
+
+The /devops resource can be accessed by:
+
+ export DEVOPS_PWD=$(kubectl --namespace {{ .Release.Namespace }} get secret {{ include "ditto.fullname" . }}-gateway-secret -o jsonpath="{.data.devops-password}" | base64 --decode)
+ curl -i -X GET "http://devops:${DEVOPS_PWD}@localhost:8080/devops"
+{{- else -}}
+Access Ditto in your browser, get the URL with:
+
+ echo https://$(kubectl --namespace {{ .Release.Namespace }} get route {{ include "ditto.fullname" . }} -o jsonpath="{.status.ingress[0].host}")
+
+The /status resource can be accessed by:
+
+ export STATUS_PWD=$(kubectl --namespace {{ .Release.Namespace }} get secret {{ include "ditto.fullname" . }}-gateway-secret -o jsonpath="{.data.status-password}" | base64 --decode)
+ export URL=https://devops:${STATUS_PWD}@$(kubectl --namespace {{ .Release.Namespace }} get route {{ include "ditto.fullname" . }} -o jsonpath="{.status.ingress[0].host}")
+ curl -i -X GET "$URL/status"
+
+The /devops resource can be accessed by:
+
+ export DEVOPS_PWD=$(kubectl --namespace {{ .Release.Namespace }} get secret {{ include "ditto.fullname" . }}-gateway-secret -o jsonpath="{.data.devops-password}" | base64 --decode)
+ export URL=https://devops:${DEVOPS_PWD}@$(kubectl --namespace {{ .Release.Namespace }} get route {{ include "ditto.fullname" . }} -o jsonpath="{.status.ingress[0].host}")
+ curl -i -X GET "$URL/devops"
+{{- end }}
diff --git a/deployment/helm/ditto/templates/_helpers.tpl b/deployment/helm/ditto/templates/_helpers.tpl
new file mode 100644
index 00000000000..8ba1d57081f
--- /dev/null
+++ b/deployment/helm/ditto/templates/_helpers.tpl
@@ -0,0 +1,72 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "ditto.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "ditto.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "ditto.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Common labels
+*/}}
+{{- define "ditto.labels" -}}
+helm.sh/chart: {{ include "ditto.chart" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "ditto.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create -}}
+ {{ default (include "ditto.fullname" .) .Values.serviceAccount.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccount.name }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a fully qualified MongoDB server name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "ditto.mongodb.fullname" -}}
+{{- if .Values.mongodb.fullnameOverride -}}
+{{- .Values.mongodb.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default "mongodb" .Values.mongodb.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/connectivity-deployment.yaml b/deployment/helm/ditto/templates/connectivity-deployment.yaml
new file mode 100644
index 00000000000..b644969b478
--- /dev/null
+++ b/deployment/helm/ditto/templates/connectivity-deployment.yaml
@@ -0,0 +1,323 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.connectivity.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-connectivity
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.connectivity.replicaCount }}
+ strategy:
+ {{- with .Values.connectivity.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ minReadySeconds: {{ .Values.connectivity.minReadySeconds }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ actorSystemName: {{ .Values.akka.actorSystemName }}
+ {{- with .Values.connectivity.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- if .Values.global.prometheus.enabled }}
+ prometheus.io/scrape: "true"
+ prometheus.io/path: "{{ .Values.global.prometheus.path }}"
+ prometheus.io/port: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ checksum/mongodb-config: {{ include (print $.Template.BasePath "/mongodb-secret.yaml") . | sha256sum }}
+ {{- with .Values.connectivity.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values.rbac.enabled }}
+ serviceAccountName: {{ template "ditto.serviceAccountName" . }}
+ {{- end }}
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: change-volume-owner
+ image: busybox
+ securityContext:
+ runAsUser: 0
+ command: [ "sh", "-c", "chown -R 1000:1000 /var/log/ditto && echo 'changed ownership of /var/log/ditto to 1000:1000'" ]
+ volumeMounts:
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-connectivity
+ image: {{ printf "%s:%s" .Values.connectivity.image.repository ( default .Chart.AppVersion ( default .Values.dittoTag .Values.connectivity.image.tag ) ) }}
+ imagePullPolicy: {{ .Values.connectivity.image.pullPolicy }}
+ env:
+ {{- if not .Values.global.logging.customConfigFile.enabled }}
+ - name: DITTO_LOGGING_DISABLE_SYSOUT_LOG
+ value: "{{ if .Values.global.logging.sysout.enabled }}false{{ else }}true{{ end }}"
+ - name: DITTO_LOGGING_FILE_APPENDER
+ value: "{{ if .Values.global.logging.logFiles.enabled }}true{{ else }}false{{ end }}"
+ {{- end }}
+ - name: DITTO_TRACING_ENABLED
+ value: "{{ .Values.global.tracing.enabled }}"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: "{{ .Values.global.tracing.otelExporterOtlpEndpoint }}"
+ - name: DITTO_TRACING_SAMPLER
+ value: "{{ .Values.global.tracing.sampler }}"
+ - name: DITTO_TRACING_RANDOM_SAMPLER_PROBABILITY
+ value: "{{ .Values.global.tracing.randomSampler.probability }}"
+ - name: DITTO_TRACING_ADAPTIVE_SAMPLER_THROUGHPUT
+ value: "{{ .Values.global.tracing.adaptiveSampler.throughput }}"
+ {{- if .Values.global.logging.logstash.enabled }}
+ - name: DITTO_LOGGING_LOGSTASH_SERVER
+ value: "{{ .Values.global.logging.logstash.endpoint }}"
+ {{- end }}
+ - name: POD_LABEL_SELECTOR
+ value: "app.kubernetes.io/name=%s"
+ - name: POD_NAMESPACE
+ value: {{ .Release.Namespace }}
+ - name: INSTANCE_INDEX
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: HOSTNAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ - name: DISCOVERY_METHOD
+ value: "kubernetes-api"
+ - name: TZ
+ value: "{{ .Values.global.timezone }}"
+ - name: JAVA_TOOL_OPTIONS
+ value: >
+ {{ .Values.global.jvmOptions }}
+ -XX:ActiveProcessorCount={{ .Values.connectivity.jvm.activeProcessorCount }}
+ -XX:MaxRAMPercentage={{ .Values.connectivity.jvm.heapRamPercentage }}
+ -XX:InitialRAMPercentage={{ .Values.connectivity.jvm.heapRamPercentage }}
+ -XX:MaxGCPauseMillis={{ .Values.connectivity.jvm.maxGcPauseMillis }}
+ {{ .Values.connectivity.additionalJvmOptions }}
+ {{- .Values.global.akkaOptions }}
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ -Dlogback.configurationFile=/opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- range $index, $header := .Values.connectivity.config.persistence.events.historicalHeadersToPersist }}
+ "{{ printf "%s%d=%s" "-Dditto.connectivity.connection.event.historical-headers-to-persist." $index $header }}"
+ {{- end }}
+ {{ join " " .Values.connectivity.systemProps }}
+ - name: MONGO_DB_SSL_ENABLED
+ value: "{{ if .Values.dbconfig.connectivity.ssl }}true{{ else }}false{{ end }}"
+ - name: MONGO_DB_URI
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.dbconfig.uriSecret | default ( printf "%s-mongodb-secret" ( include "ditto.fullname" . )) }}
+ key: connectivity-uri
+ - name: MONGO_DB_CONNECTION_MIN_POOL_SIZE
+ value: "{{ .Values.connectivity.config.mongodb.minPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_SIZE
+ value: "{{ .Values.connectivity.config.mongodb.maxPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_IDLE_TIME
+ value: "{{ .Values.connectivity.config.mongodb.maxPoolIdleTime }}"
+ - name: CLUSTER_BS_REQUIRED_CONTACTS
+ value: "{{ .Values.global.cluster.requiredContactPoints }}"
+ - name: DITTO_DDATA_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.ddata.numberOfShards }}"
+ - name: DITTO_DDATA_MAX_DELTA_ELEMENTS
+ value: "{{ .Values.global.cluster.ddata.maxDeltaElements }}"
+ - name: CLUSTER_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.numberOfShards }}"
+ - name: CLUSTER_DOWNING_STABLE_AFTER
+ value: "{{ .Values.global.cluster.downingStableAfter }}"
+ - name: CLUSTER_DOWNING_DOWN_ALL_WHEN_UNSTABLE
+ value: "{{ .Values.global.cluster.downAllWhenUnstable }}"
+ {{- if .Values.global.prometheus.enabled }}
+ - name: PROMETHEUS_PORT
+ value: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ - name: AKKA_PERSISTENCE_MONGO_JOURNAL_WRITE_CONCERN
+ value: "{{ .Values.connectivity.config.mongodb.journalWriteConcern }}"
+ - name: AKKA_PERSISTENCE_MONGO_SNAPS_WRITE_CONCERN
+ value: "{{ .Values.connectivity.config.mongodb.snapsWriteConcern }}"
+ - name: BREAKER_MAXTRIES
+ value: "{{ .Values.connectivity.config.mongodb.journalCircuitBreaker.maxTries }}"
+ - name: BREAKER_TIMEOUT
+ value: "{{ .Values.connectivity.config.mongodb.journalCircuitBreaker.timeout }}"
+ - name: BREAKER_RESET
+ value: "{{ .Values.connectivity.config.mongodb.journalCircuitBreaker.reset }}"
+ - name: SNAPSHOT_BREAKER_MAXTRIES
+ value: "{{ .Values.connectivity.config.mongodb.snapsCircuitBreaker.maxTries }}"
+ - name: SNAPSHOT_BREAKER_TIMEOUT
+ value: "{{ .Values.connectivity.config.mongodb.snapsCircuitBreaker.timeout }}"
+ - name: SNAPSHOT_BREAKER_RESET
+ value: "{{ .Values.connectivity.config.mongodb.snapsCircuitBreaker.reset }}"
+ - name: CONNECTION_ACTIVITY_CHECK_INTERVAL
+ value: "{{ .Values.connectivity.config.persistence.activityCheckInterval }}"
+ - name: HEALTH_CHECK_METRICS_REPORTER_RESOLUTION
+ value: "{{ .Values.connectivity.config.cleanup.metricsReporter.resolution }}"
+ - name: HEALTH_CHECK_METRICS_REPORTER_HISTORY
+ value: "{{ .Values.connectivity.config.cleanup.metricsReporter.history }}"
+ - name: CLEANUP_ENABLED
+ value: "{{ .Values.connectivity.config.cleanup.enabled }}"
+ - name: CLEANUP_QUIET_PERIOD
+ value: "{{ .Values.connectivity.config.cleanup.quietPeriod }}"
+ - name: CLEANUP_HISTORY_RETENTION_DURATION
+ value: "{{ .Values.connectivity.config.cleanup.history.retentionDuration }}"
+ - name: CLEANUP_INTERVAL
+ value: "{{ .Values.connectivity.config.cleanup.interval }}"
+ - name: CLEANUP_TIMER_THRESHOLD
+ value: "{{ .Values.connectivity.config.cleanup.timerThreshold }}"
+ - name: CLEANUP_CREDITS_PER_BATCH
+ value: "{{ .Values.connectivity.config.cleanup.creditsPerBatch }}"
+ - name: CONNECTION_SNAPSHOT_INTERVAL
+ value: "{{ .Values.connectivity.config.persistence.snapshots.interval }}"
+ - name: CONNECTION_SNAPSHOT_THRESHOLD
+ value: "{{ .Values.connectivity.config.persistence.snapshots.threshold }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_ENABLED
+ value: "{{ .Values.connectivity.config.policiesEnforcer.cache.enabled }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_MAX_SIZE
+ value: "{{ .Values.connectivity.config.policiesEnforcer.cache.maxSize }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_EXPIRE_AFTER_WRITE
+ value: "{{ .Values.connectivity.config.policiesEnforcer.cache.expireAfterWrite }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_EXPIRE_AFTER_ACCESS
+ value: "{{ .Values.connectivity.config.policiesEnforcer.cache.expireAfterAccess }}"
+ - name: RECONNECT_RATE_FREQUENCY
+ value: "{{ .Values.connectivity.config.connections.reconnect.rate.frequency }}"
+ - name: RECONNECT_RATE_ENTITIES
+ value: "{{ .Values.connectivity.config.connections.reconnect.rate.entities }}"
+ - name: CONNECTIVITY_CONNECTION_ALLOWED_HOSTNAMES
+ value: "{{ .Values.connectivity.config.connections.allowedHostnames }}"
+ - name: CONNECTIVITY_CONNECTION_BLOCKED_HOSTNAMES
+ value: "{{ .Values.connectivity.config.connections.blockedHostnames }}"
+ - name: CONNECTIVITY_CONNECTION_BLOCKED_SUBNETS
+ value: "{{ .Values.connectivity.config.connections.blockedSubnets }}"
+ - name: CONNECTIVITY_CONNECTION_BLOCKED_HOST_REGEX
+ value: "{{ .Values.connectivity.config.connections.blockedHostRegex }}"
+ - name: CONNECTION_SOURCE_NUMBER
+ value: "{{ .Values.connectivity.config.connections.limits.maxSources }}"
+ - name: CONNECTION_TARGET_NUMBER
+ value: "{{ .Values.connectivity.config.connections.limits.maxTargets }}"
+ - name: CONNECTIVITY_SIGNAL_ENRICHMENT_BUFFER_SIZE
+ value: "{{ .Values.connectivity.config.connections.enrichment.bufferSize }}"
+ - name: KAFKA_CONSUMER_THROTTLING_ENABLED
+ value: "{{ .Values.connectivity.config.connections.kafka.consumer.throttling.enabled }}"
+ - name: KAFKA_CONSUMER_THROTTLING_INTERVAL
+ value: "{{ .Values.connectivity.config.connections.kafka.consumer.throttling.interval }}"
+ - name: KAFKA_CONSUMER_THROTTLING_LIMIT
+ value: "{{ .Values.connectivity.config.connections.kafka.consumer.throttling.limit }}"
+ - name: KAFKA_CONSUMER_THROTTLING_MAX_IN_FLIGHT_FACTOR
+ value: "{{ .Values.connectivity.config.connections.kafka.consumer.throttling.maxInflightFactor }}"
+ - name: KAFKA_PRODUCER_QUEUE_SIZE
+ value: "{{ .Values.connectivity.config.connections.kafka.producer.queueSize }}"
+ - name: KAFKA_PRODUCER_PARALLELISM
+ value: "{{ .Values.connectivity.config.connections.kafka.producer.parallelism }}"
+ {{- if .Values.connectivity.extraEnv }}
+ {{- toYaml .Values.connectivity.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: remoting
+ containerPort: {{ .Values.akka.remoting.port }}
+ protocol: TCP
+ - name: management
+ containerPort: {{ .Values.akka.mgmthttp.port }}
+ protocol: TCP
+ {{- if .Values.global.prometheus.enabled }}
+ - name: prometheus
+ protocol: TCP
+ containerPort: {{ .Values.global.prometheus.port }}
+ {{- end }}
+ readinessProbe:
+ httpGet:
+ port: management
+ path: /ready
+ initialDelaySeconds: {{ .Values.connectivity.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.connectivity.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.connectivity.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.connectivity.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.connectivity.readinessProbe.failureThreshold }}
+ livenessProbe:
+ httpGet:
+ port: management
+ path: /alive
+ initialDelaySeconds: {{ .Values.connectivity.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.connectivity.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.connectivity.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.connectivity.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.connectivity.livenessProbe.failureThreshold }}
+ volumeMounts:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ mountPath: /opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ subPath: {{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ resources:
+ requests:
+ cpu: {{ mulf .Values.connectivity.resources.cpu 1000 }}m
+ memory: {{ .Values.connectivity.resources.memoryMi }}Mi
+ limits:
+ # ## no cpu limit to avoid CFS scheduler limits
+ # ref: https://doc.akka.io/docs/akka/snapshot/additional/deploy.html#in-kubernetes
+ # cpu: ""
+ memory: {{ .Values.connectivity.resources.memoryMi }}Mi
+ {{- if .Values.openshift.enabled }}
+ {{- with .Values.openshift.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- else }}
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ runAsGroup: 1000
+ allowPrivilegeEscalation: false
+ {{- end }}
+ {{- with .Values.connectivity.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.connectivity.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.connectivity.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ volumes:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ configMap:
+ name: {{ .Release.Name }}-logback-config-connectivity-xml
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ hostPath:
+ path: /var/log/ditto
+ type: DirectoryOrCreate
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/connectivity-pdb.yaml b/deployment/helm/ditto/templates/connectivity-pdb.yaml
new file mode 100644
index 00000000000..c33ebbdee52
--- /dev/null
+++ b/deployment/helm/ditto/templates/connectivity-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.connectivity.podDisruptionBudget.enabled (gt .Values.connectivity.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-connectivity
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.connectivity.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/connectivity-podmonitor.yaml b/deployment/helm/ditto/templates/connectivity-podmonitor.yaml
new file mode 100644
index 00000000000..d133f138215
--- /dev/null
+++ b/deployment/helm/ditto/templates/connectivity-podmonitor.yaml
@@ -0,0 +1,38 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.connectivity.podMonitor.enabled .Values.global.prometheus.port -}}
+{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" -}}
+---
+kind: PodMonitor
+apiVersion: monitoring.coreos.com/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}-connectivity
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ podMetricsEndpoints:
+ - targetPort: {{ .Values.global.prometheus.port }}
+ path: "/"
+ {{- if .Values.connectivity.podMonitor.interval }}
+ interval: {{ .Values.connectivity.podMonitor.interval }}
+ {{- end }}
+ {{- if .Values.connectivity.podMonitor.scrapeTimeout }}
+ scrapeTimeout: {{ .Values.connectivity.podMonitor.scrapeTimeout }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-connectivity
+ namespaceSelector:
+ matchNames:
+ - {{ $.Release.Namespace | quote }}
+{{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/dittoui-config.yaml b/deployment/helm/ditto/templates/dittoui-config.yaml
new file mode 100644
index 00000000000..bfba14c9b74
--- /dev/null
+++ b/deployment/helm/ditto/templates/dittoui-config.yaml
@@ -0,0 +1,30 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.dittoui.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $name := include "ditto.name" . -}}
+{{- $labels := include "ditto.labels" . -}}
+{{ $root := . }}
+{{ range $path, $bytes := .Files.Glob "dittoui-config/**" }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ $releaseName }}-{{ $path | replace "/" "-" | replace "." "-" }}
+ labels:
+ app.kubernetes.io/name: {{ $name }}-dittoui-config
+{{ $labels | indent 4 }}
+data:
+ {{ $path | replace "dittoui-config/" ""}}: |-
+{{ $root.Files.Get $path | indent 4 }}
+---
+{{- end -}}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/dittoui-deployment.yaml b/deployment/helm/ditto/templates/dittoui-deployment.yaml
new file mode 100644
index 00000000000..3ba80f3927e
--- /dev/null
+++ b/deployment/helm/ditto/templates/dittoui-deployment.yaml
@@ -0,0 +1,74 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.dittoui.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-dittoui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.dittoui.replicaCount }}
+ strategy:
+ {{- with .Values.dittoui.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ {{- with .Values.dittoui.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- with .Values.dittoui.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-dittoui
+ image: {{ printf "%s:%s" .Values.dittoui.image.repository ( default .Chart.AppVersion ( default .Values.dittoTag .Values.dittoui.image.tag ) ) }}
+ imagePullPolicy: {{ .Values.dittoui.image.pullPolicy }}
+ env:
+ {{- if .Values.dittoui.extraEnv }}
+ {{- toYaml .Values.dittoui.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: 8080
+ protocol: TCP
+ resources:
+ requests:
+ cpu: {{ mulf .Values.dittoui.resources.cpu 1000 }}m
+ memory: {{ .Values.dittoui.resources.memoryMi }}Mi
+ limits:
+ # cpu: ""
+ memory: {{ .Values.dittoui.resources.memoryMi }}Mi
+ volumeMounts:
+ - name: dittoui-nginx-conf
+ mountPath: /etc/nginx/nginx.conf
+ subPath: nginx.conf
+ volumes:
+ - name: dittoui-nginx-conf
+ configMap:
+ name: {{ .Release.Name }}-dittoui-config-nginx-conf
+{{- end }}
diff --git a/deployment/helm/ditto/templates/dittoui-pdb.yaml b/deployment/helm/ditto/templates/dittoui-pdb.yaml
new file mode 100644
index 00000000000..e3a5a6e52bf
--- /dev/null
+++ b/deployment/helm/ditto/templates/dittoui-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.dittoui.podDisruptionBudget.enabled (gt .Values.dittoui.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-dittoui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.dittoui.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/dittoui-service.yaml b/deployment/helm/ditto/templates/dittoui-service.yaml
new file mode 100644
index 00000000000..f640c4a8c15
--- /dev/null
+++ b/deployment/helm/ditto/templates/dittoui-service.yaml
@@ -0,0 +1,33 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.dittoui.enabled -}}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "ditto.fullname" . }}-dittoui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+{{ include "ditto.labels" . | indent 4 }}
+ {{- with .Values.dittoui.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ports:
+ - port: {{ .Values.dittoui.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-dittoui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/gateway-deployment.yaml b/deployment/helm/ditto/templates/gateway-deployment.yaml
new file mode 100644
index 00000000000..51f6536124d
--- /dev/null
+++ b/deployment/helm/ditto/templates/gateway-deployment.yaml
@@ -0,0 +1,293 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.gateway.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-gateway
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.gateway.replicaCount }}
+ strategy:
+ {{- with .Values.gateway.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ minReadySeconds: {{ .Values.gateway.minReadySeconds }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ actorSystemName: {{ .Values.akka.actorSystemName }}
+ {{- with .Values.gateway.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- if .Values.global.prometheus.enabled }}
+ prometheus.io/scrape: "true"
+ prometheus.io/path: "{{ .Values.global.prometheus.path }}"
+ prometheus.io/port: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ checksum/password-config: {{ include (print $.Template.BasePath "/gateway-secret.yaml") . | sha256sum }}
+ checksum/mongodb-config: {{ include (print $.Template.BasePath "/mongodb-secret.yaml") . | sha256sum }}
+ {{- with .Values.gateway.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values.rbac.enabled }}
+ serviceAccountName: {{ template "ditto.serviceAccountName" . }}
+ {{- end }}
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: change-volume-owner
+ image: busybox
+ securityContext:
+ runAsUser: 0
+ command: [ "sh", "-c", "chown -R 1000:1000 /var/log/ditto && echo 'changed ownership of /var/log/ditto to 1000:1000'" ]
+ volumeMounts:
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-gateway
+ image: {{ printf "%s:%s" .Values.gateway.image.repository ( default .Chart.AppVersion ( default .Values.dittoTag .Values.gateway.image.tag ) ) }}
+ imagePullPolicy: {{ .Values.gateway.image.pullPolicy }}
+ env:
+ {{- if not .Values.global.logging.customConfigFile.enabled }}
+ - name: DITTO_LOGGING_DISABLE_SYSOUT_LOG
+ value: "{{ if .Values.global.logging.sysout.enabled }}false{{ else }}true{{ end }}"
+ - name: DITTO_LOGGING_FILE_APPENDER
+ value: "{{ if .Values.global.logging.logFiles.enabled }}true{{ else }}false{{ end }}"
+ {{- end }}
+ - name: DITTO_TRACING_ENABLED
+ value: "{{ .Values.global.tracing.enabled }}"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: "{{ .Values.global.tracing.otelExporterOtlpEndpoint }}"
+ - name: DITTO_TRACING_SAMPLER
+ value: "{{ .Values.global.tracing.sampler }}"
+ - name: DITTO_TRACING_RANDOM_SAMPLER_PROBABILITY
+ value: "{{ .Values.global.tracing.randomSampler.probability }}"
+ - name: DITTO_TRACING_ADAPTIVE_SAMPLER_THROUGHPUT
+ value: "{{ .Values.global.tracing.adaptiveSampler.throughput }}"
+ {{- if .Values.global.logging.logstash.enabled }}
+ - name: DITTO_LOGGING_LOGSTASH_SERVER
+ value: "{{ .Values.global.logging.logstash.endpoint }}"
+ {{- end }}
+ - name: POD_LABEL_SELECTOR
+ value: "app.kubernetes.io/name=%s"
+ - name: POD_NAMESPACE
+ value: {{ .Release.Namespace }}
+ - name: INSTANCE_INDEX
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: HOSTNAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ - name: DISCOVERY_METHOD
+ value: "kubernetes-api"
+ - name: TZ
+ value: "{{ .Values.global.timezone }}"
+ - name: JAVA_TOOL_OPTIONS
+ value: >
+ {{ .Values.global.jvmOptions }}
+ -XX:ActiveProcessorCount={{ .Values.gateway.jvm.activeProcessorCount }}
+ -XX:MaxRAMPercentage={{ .Values.gateway.jvm.heapRamPercentage }}
+ -XX:InitialRAMPercentage={{ .Values.gateway.jvm.heapRamPercentage }}
+ -XX:MaxGCPauseMillis={{ .Values.gateway.jvm.maxGcPauseMillis }}
+ {{ .Values.gateway.additionalJvmOptions }}
+ {{- .Values.global.akkaOptions }}
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ -Dlogback.configurationFile=/opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- range $key, $value := .Values.gateway.config.authentication.oauth.openidConnectIssuers }}
+ "{{ printf "%s%s%s=%s" "-Dditto.gateway.authentication.oauth.openid-connect-issuers." $key ".issuer" $value.issuer }}"
+ {{- range $index, $subject := $value.authSubjects }}
+ "{{ printf "%s%s%s%d=%s" "-Dditto.gateway.authentication.oauth.openid-connect-issuers." $key ".auth-subjects." $index $subject }}"
+ {{- end }}
+ {{- end }}
+ {{- range $key, $value := .Values.gateway.config.authentication.devops.oauth.openidConnectIssuers }}
+ "{{ printf "%s%s%s=%s" "-Dditto.gateway.authentication.devops.oauth.openid-connect-issuers." $key ".issuer" $value.issuer }}"
+ {{- range $index, $subject := $value.authSubjects }}
+ "{{ printf "%s%s%s%d=%s" "-Dditto.gateway.authentication.devops.oauth.openid-connect-issuers." $key ".auth-subjects." $index $subject }}"
+ {{- end }}
+ {{- end }}
+ {{ join " " .Values.gateway.systemProps }}
+ - name: CLUSTER_BS_REQUIRED_CONTACTS
+ value: "{{ .Values.global.cluster.requiredContactPoints }}"
+ - name: DITTO_DDATA_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.ddata.numberOfShards }}"
+ - name: DITTO_DDATA_MAX_DELTA_ELEMENTS
+ value: "{{ .Values.global.cluster.ddata.maxDeltaElements }}"
+ - name: CLUSTER_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.numberOfShards }}"
+ - name: CLUSTER_DOWNING_STABLE_AFTER
+ value: "{{ .Values.global.cluster.downingStableAfter }}"
+ - name: CLUSTER_DOWNING_DOWN_ALL_WHEN_UNSTABLE
+ value: "{{ .Values.global.cluster.downAllWhenUnstable }}"
+ {{- if .Values.global.prometheus.enabled }}
+ - name: PROMETHEUS_PORT
+ value: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ - name: ENABLE_PRE_AUTHENTICATION
+ value: "{{ .Values.gateway.config.authentication.enablePreAuthentication }}"
+ - name: DEVOPS_SECURED
+ value: "{{ .Values.gateway.config.authentication.devops.secured }}"
+ - name: DEVOPS_AUTHENTICATION_METHOD
+ value: "{{ .Values.gateway.config.authentication.devops.authMethod }}"
+ - name: DEVOPS_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.gateway.config.authentication.devops.existingSecret | default ( printf "%s-gateway-secret" ( include "ditto.fullname" . )) }}
+ key: devops-password
+ {{- range $index, $oauthSubject := .Values.gateway.config.authentication.devops.oauthSubjects }}
+ - name: DEVOPS_OAUTH2_SUBJECTS.{{ $index }}
+ value: "{{ $oauthSubject }}"
+ {{- end }}
+ - name: DEVOPS_STATUS_SECURED
+ value: "{{ .Values.gateway.config.authentication.devops.statusSecured }}"
+ - name: STATUS_AUTHENTICATION_METHOD
+ value: "{{ .Values.gateway.config.authentication.devops.statusAuthMethod }}"
+ - name: STATUS_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.gateway.config.authentication.devops.existingSecret | default ( printf "%s-gateway-secret" ( include "ditto.fullname" . )) }}
+ key: status-password
+ {{- range $index, $oauthSubject := .Values.gateway.config.authentication.devops.statusOauthSubjects }}
+ - name: STATUS_OAUTH2_SUBJECTS.{{ $index }}
+ value: "{{ $oauthSubject }}"
+ {{- end }}
+ - name: WS_SUBSCRIBER_BACKPRESSURE
+ value: "{{ .Values.gateway.config.websocket.subscriber.backpressureQueueSize }}"
+ - name: WS_PUBLISHER_BACKPRESSURE
+ value: "{{ .Values.gateway.config.websocket.publisher.backpressureBufferSize }}"
+ - name: GATEWAY_WEBSOCKET_THROTTLING_ENABLED
+ value: "{{ .Values.gateway.config.websocket.throttling.enabled }}"
+ - name: GATEWAY_WEBSOCKET_THROTTLING_INTERVAL
+ value: "{{ .Values.gateway.config.websocket.throttling.interval }}"
+ - name: GATEWAY_WEBSOCKET_THROTTLING_LIMIT
+ value: "{{ .Values.gateway.config.websocket.throttling.limit }}"
+ - name: GATEWAY_SSE_THROTTLING_ENABLED
+ value: "{{ .Values.gateway.config.sse.throttling.enabled }}"
+ - name: GATEWAY_SSE_THROTTLING_INTERVAL
+ value: "{{ .Values.gateway.config.sse.throttling.interval }}"
+ - name: GATEWAY_SSE_THROTTLING_LIMIT
+ value: "{{ .Values.gateway.config.sse.throttling.limit }}"
+ - name: OAUTH_ALLOWED_CLOCK_SKEW
+ value: "{{ .Values.gateway.config.authentication.oauth.allowedClockSkew }}"
+ {{- if .Values.gateway.extraEnv }}
+ {{- toYaml .Values.gateway.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: {{ .Values.gateway.service.port }}
+ protocol: TCP
+ - name: remoting
+ containerPort: {{ .Values.akka.remoting.port }}
+ protocol: TCP
+ - name: management
+ containerPort: {{ .Values.akka.mgmthttp.port }}
+ protocol: TCP
+ {{- if .Values.global.prometheus.enabled }}
+ - name: prometheus
+ protocol: TCP
+ containerPort: {{ .Values.global.prometheus.port }}
+ {{- end }}
+ readinessProbe:
+ httpGet:
+ port: management
+ path: /ready
+ initialDelaySeconds: {{ .Values.gateway.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.gateway.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.gateway.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.gateway.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.gateway.readinessProbe.failureThreshold }}
+ livenessProbe:
+ httpGet:
+ port: management
+ path: /alive
+ initialDelaySeconds: {{ .Values.gateway.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.gateway.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.gateway.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.gateway.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.gateway.livenessProbe.failureThreshold }}
+ volumeMounts:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ mountPath: /opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ subPath: {{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ resources:
+ requests:
+ cpu: {{ mulf .Values.gateway.resources.cpu 1000 }}m
+ memory: {{ .Values.gateway.resources.memoryMi }}Mi
+ limits:
+ # ## no cpu limit to avoid CFS scheduler limits
+ # ref: https://doc.akka.io/docs/akka/snapshot/additional/deploy.html#in-kubernetes
+ # cpu: ""
+ memory: {{ .Values.gateway.resources.memoryMi }}Mi
+ {{- if .Values.openshift.enabled }}
+ {{- with .Values.openshift.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- else }}
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ runAsGroup: 1000
+ allowPrivilegeEscalation: false
+ {{- end }}
+ {{- with .Values.gateway.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.gateway.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.gateway.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ volumes:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ configMap:
+ name: {{ .Release.Name }}-logback-config-gateway-xml
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ hostPath:
+ path: /var/log/ditto
+ type: DirectoryOrCreate
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/gateway-pdb.yaml b/deployment/helm/ditto/templates/gateway-pdb.yaml
new file mode 100644
index 00000000000..1be894d9e5f
--- /dev/null
+++ b/deployment/helm/ditto/templates/gateway-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.gateway.podDisruptionBudget.enabled (gt .Values.gateway.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-gateway
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.gateway.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/gateway-podmonitor.yaml b/deployment/helm/ditto/templates/gateway-podmonitor.yaml
new file mode 100644
index 00000000000..b57a9b65de4
--- /dev/null
+++ b/deployment/helm/ditto/templates/gateway-podmonitor.yaml
@@ -0,0 +1,38 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.gateway.podMonitor.enabled .Values.global.prometheus.port -}}
+{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" -}}
+---
+kind: PodMonitor
+apiVersion: monitoring.coreos.com/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}-gateway
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ podMetricsEndpoints:
+ - targetPort: {{ .Values.global.prometheus.port }}
+ path: "/"
+ {{- if .Values.gateway.podMonitor.interval }}
+ interval: {{ .Values.gateway.podMonitor.interval }}
+ {{- end }}
+ {{- if .Values.gateway.podMonitor.scrapeTimeout }}
+ scrapeTimeout: {{ .Values.gateway.podMonitor.scrapeTimeout }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+ namespaceSelector:
+ matchNames:
+ - {{ $.Release.Namespace | quote }}
+{{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/gateway-secret.yaml b/deployment/helm/ditto/templates/gateway-secret.yaml
new file mode 100644
index 00000000000..45e9a42c6af
--- /dev/null
+++ b/deployment/helm/ditto/templates/gateway-secret.yaml
@@ -0,0 +1,32 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if not .Values.gateway.config.authentication.devops.existingSecret }}
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ include "ditto.fullname" . }}-gateway-secret
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway-secret
+{{ include "ditto.labels" . | indent 4 }}
+type: Opaque
+data:
+ {{- if .Values.gateway.config.authentication.devops.devopsPassword }}
+ devops-password: {{ .Values.gateway.config.authentication.devops.devopsPassword | b64enc | quote }}
+ {{- else }}
+ devops-password: {{ randAlphaNum 12 | b64enc | quote }}
+ {{- end }}
+ {{- if .Values.gateway.config.authentication.devops.statusPassword }}
+ status-password: {{ .Values.gateway.config.authentication.devops.statusPassword | b64enc | quote }}
+ {{- else }}
+ status-password: {{ randAlphaNum 12 | b64enc | quote }}
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/gateway-service.yaml b/deployment/helm/ditto/templates/gateway-service.yaml
new file mode 100644
index 00000000000..c4549ee7c00
--- /dev/null
+++ b/deployment/helm/ditto/templates/gateway-service.yaml
@@ -0,0 +1,33 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.gateway.enabled -}}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "ditto.fullname" . }}-gateway
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+{{ include "ditto.labels" . | indent 4 }}
+ {{- with .Values.gateway.service.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ports:
+ - port: {{ .Values.gateway.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-gateway
+ app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/logback-config.yaml b/deployment/helm/ditto/templates/logback-config.yaml
new file mode 100644
index 00000000000..029cf98467a
--- /dev/null
+++ b/deployment/helm/ditto/templates/logback-config.yaml
@@ -0,0 +1,31 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.global.logging.customConfigFile.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $name := include "ditto.name" . -}}
+{{- $labels := include "ditto.labels" . -}}
+{{- $logbackFileName := .Values.global.logging.customConfigFile.fileName -}}
+{{ $root := . }}
+{{ range $path, $bytes := .Files.Glob "logback-config/**" }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ $releaseName }}-{{ $path | replace "/" "-" | replace "." "-" }}
+ labels:
+ app.kubernetes.io/name: {{ $name }}-logback-config
+{{ $labels | indent 4 }}
+data:
+ {{ $logbackFileName }}: |-
+{{ $root.Files.Get $path | indent 4 }}
+---
+{{- end -}}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/mongodb-secret.yaml b/deployment/helm/ditto/templates/mongodb-secret.yaml
new file mode 100644
index 00000000000..06584e605e5
--- /dev/null
+++ b/deployment/helm/ditto/templates/mongodb-secret.yaml
@@ -0,0 +1,27 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if not .Values.dbconfig.uriSecret -}}
+{{- $mongoName := include "ditto.mongodb.fullname" . -}}
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ include "ditto.fullname" . }}-mongodb-secret
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-mongodb-secret
+{{ include "ditto.labels" . | indent 4 }}
+type: Opaque
+data:
+ connectivity-uri: {{ .Values.dbconfig.connectivity.uri | replace "#{PLACEHOLDER_MONGODB_HOSTNAME}#" $mongoName | b64enc | quote}}
+ things-uri: {{ .Values.dbconfig.things.uri | replace "#{PLACEHOLDER_MONGODB_HOSTNAME}#" $mongoName | b64enc | quote}}
+ thingsSearch-uri: {{ .Values.dbconfig.thingsSearch.uri | replace "#{PLACEHOLDER_MONGODB_HOSTNAME}#" $mongoName | b64enc | quote}}
+ policies-uri: {{ .Values.dbconfig.policies.uri | replace "#{PLACEHOLDER_MONGODB_HOSTNAME}#" $mongoName | b64enc | quote}}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-auth.yaml b/deployment/helm/ditto/templates/nginx-auth.yaml
new file mode 100644
index 00000000000..582cf89e8c5
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-auth.yaml
@@ -0,0 +1,36 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.nginx.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $name := include "ditto.name" . -}}
+{{- $labels := include "ditto.labels" . -}}
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ $releaseName }}-nginx-config-nginx-htpasswd
+ labels:
+ app.kubernetes.io/name: {{ $name }}-nginx-config
+{{ $labels | indent 4 }}
+type: Opaque
+stringData:
+ nginx.htpasswd: |-
+{{- if .Values.global.hashedBasicAuthUsers }}
+{{ range .Values.global.hashedBasicAuthUsers }}
+{{- . | indent 4 }}
+{{ end }}
+{{- else }}
+{{ range $key, $value := .Values.global.basicAuthUsers }}
+{{- (htpasswd $value.user $value.password) | indent 4 }}
+{{ end }}
+{{ end }}
+---
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-config.yaml b/deployment/helm/ditto/templates/nginx-config.yaml
new file mode 100644
index 00000000000..0d6e51a47f4
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-config.yaml
@@ -0,0 +1,30 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.nginx.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $name := include "ditto.name" . -}}
+{{- $labels := include "ditto.labels" . -}}
+{{ $root := . }}
+{{ range $path, $bytes := .Files.Glob "nginx-config/**" }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ $releaseName }}-{{ $path | replace "/" "-" | replace "." "-" }}
+ labels:
+ app.kubernetes.io/name: {{ $name }}-nginx-config
+{{ $labels | indent 4 }}
+data:
+ {{ $path | replace "nginx-config/" ""}}: |-
+{{ $root.Files.Get $path | indent 4 }}
+---
+{{- end -}}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/nginx-configmap.yaml b/deployment/helm/ditto/templates/nginx-configmap.yaml
new file mode 100644
index 00000000000..b9667b661ed
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-configmap.yaml
@@ -0,0 +1,242 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.nginx.enabled -}}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ .Release.Name }}-nginx-conf
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx-conf
+{{ include "ditto.labels" . | indent 4 }}
+data:
+ nginx.conf: |-
+ worker_processes {{ .Values.nginx.config.workerProcesses }};
+
+ events {
+ worker_connections {{ .Values.nginx.config.workerConnections }};
+ }
+
+ http {
+ charset utf-8;
+ default_type application/json;
+ include mime.types;
+
+ # timeouts are configured slightly higher than ditto-eclipse-ditto-gateway read-timeout of 60 seconds
+ proxy_connect_timeout 70; # seconds, default: 60
+ proxy_send_timeout 70; # seconds, default: 60
+ proxy_read_timeout 70; # seconds, default: 60
+ # will try another upstream if an error or timeout occurred during the connection
+ # or if the upstream returns 502 response
+ proxy_next_upstream error timeout http_502;
+ # will retry up to 3 times to find another upstream to connect to
+ proxy_next_upstream_tries 3;
+ # will try for max. 20s to find another upstream to connect to
+ proxy_next_upstream_timeout 20;
+
+ send_timeout 70; # seconds, default: 60
+
+ client_header_buffer_size 8k; # allow longer URIs + headers (default: 1k)
+ large_client_header_buffers 4 16k;
+
+ merge_slashes off; # allow multiple slashes for CRS Authentication
+
+ map $http_authorization $authentication {
+ default "Authentication required";
+ "~Bearer" "off";
+ # the above means: if we get a request containing an "Authorization: Bearer ..." header, set "off" to $authentication
+ }
+
+ map $http_authorization $nginx_auth_user {
+ default "nginx:${remote_user}";
+ "~Bearer" "";
+ }
+
+ upstream {{ include "ditto.fullname" . }}-gateway {
+ server {{ include "ditto.fullname" . }}-gateway:8080;
+ }
+
+ {{ if .Values.dittoui.enabled -}}
+ upstream {{ include "ditto.fullname" . }}-dittoui {
+ server {{ include "ditto.fullname" . }}-dittoui:8080;
+ }
+ {{- end }}
+
+ {{ if .Values.swaggerui.enabled -}}
+ upstream {{ include "ditto.fullname" . }}-swaggerui {
+ server {{ include "ditto.fullname" . }}-swaggerui:8080;
+ }
+ {{- end }}
+
+ log_format jsonlog escape=json '{'
+ '"@timestamp":"$time_iso8601",'
+ '"remote_addr":"$remote_addr",'
+ '"remote_user":"$remote_user",'
+ '"request":"$request",'
+ '"status": "$status",'
+ '"body_bytes_sent":"$body_bytes_sent",'
+ '"request_time":"$request_time",'
+ '"upstream_response_time":"$upstream_response_time",'
+ '"http_referrer":"$http_referer",'
+ '"http_user_agent":"$http_user_agent",'
+ '"correlation-id":"$http_correlation_id"'
+ '}';
+ access_log /var/log/nginx/access.log jsonlog;
+
+ server {
+ listen 8080;
+ server_name localhost;
+
+ location / {
+ index index.html;
+ }
+
+ # api
+ location /api {
+ include nginx-cors.conf;
+
+ {{ if .Values.global.jwtOnly -}}
+ proxy_pass_request_headers on;
+ proxy_set_header Authorization $http_authorization;
+ {{ else }}
+ auth_basic $authentication;
+ auth_basic_user_file nginx.htpasswd;
+ proxy_set_header X-Forwarded-User $remote_user;
+ proxy_set_header x-ditto-pre-authenticated $nginx_auth_user;
+ {{- end }}
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ proxy_set_header Connection '';
+ chunked_transfer_encoding off;
+ proxy_buffering off;
+ proxy_cache off;
+ }
+
+ # ws
+ location /ws {
+
+ {{ if .Values.global.jwtOnly -}}
+ proxy_pass_request_headers on;
+ proxy_set_header Authorization $http_authorization;
+ {{ else }}
+ auth_basic $authentication;
+ auth_basic_user_file nginx.htpasswd;
+ proxy_set_header X-Forwarded-User $remote_user;
+ proxy_set_header x-ditto-pre-authenticated $nginx_auth_user;
+ {{- end }}
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 1d;
+ proxy_send_timeout 1d;
+ }
+
+ # health
+ location /health {
+ include nginx-cors.conf;
+
+ # exclude health checks from being logged in access log:
+ access_log off;
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway/health;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-User $remote_user;
+ }
+
+ # status
+ location /status {
+ include nginx-cors.conf;
+
+ # exclude status access from being logged in access log:
+ access_log off;
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway/overall/status;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-User $remote_user;
+ }
+
+ # stats
+ location /stats {
+ include nginx-cors.conf;
+
+ # exclude stats access from being logged in access log:
+ access_log off;
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway/stats;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-User $remote_user;
+ }
+
+ # devops
+ location /devops {
+ include nginx-cors.conf;
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway/devops;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-User $remote_user;
+ }
+
+ # connections api using devops user configured in Ditto
+ location /api/2/connections {
+ include nginx-cors.conf;
+
+ proxy_pass http://{{ include "ditto.fullname" . }}-gateway;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-User $remote_user;
+ }
+
+ {{ if .Values.dittoui.enabled -}}
+ location /ui/ {
+ proxy_pass http://{{ include "ditto.fullname" . }}-dittoui/;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ }
+ {{- end }}
+
+ {{ if .Values.swaggerui.enabled -}}
+ # swagger
+ # access API doc on: /apidoc/
+ location /apidoc/ {
+ proxy_pass http://{{ include "ditto.fullname" . }}-swaggerui/;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ }
+ {{- end }}
+ }
+ }
+
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-deployment.yaml b/deployment/helm/ditto/templates/nginx-deployment.yaml
new file mode 100644
index 00000000000..3fa88faec5a
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-deployment.yaml
@@ -0,0 +1,149 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.nginx.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-nginx
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.nginx.replicaCount }}
+ strategy:
+ {{- with .Values.nginx.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ {{- with .Values.nginx.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ checksum/nginx-conf: {{ include (print $.Template.BasePath "/nginx-configmap.yaml") . | sha256sum }}
+ checksum/nginx-config: {{ include (print $.Template.BasePath "/nginx-config.yaml") . | sha256sum }}
+ checksum/nginx-auth: {{ include (print $.Template.BasePath "/nginx-auth.yaml") . | sha256sum }}
+ {{- with .Values.nginx.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- if .Values.nginx.initContainers.waitForGateway.enabled }}
+ initContainers:
+ - name: {{ .Values.nginx.initContainers.waitForGateway.name }}
+ image: {{ .Values.nginx.initContainers.waitForGateway.image }}
+ args:
+ - /bin/sh
+ - -c
+ - >
+ set -x;
+ while [[ "$(curl -sL -w "%{http_code}\n" http://{{ include "ditto.fullname" . }}-gateway:8080/health -o /dev/null)" != "200" ]]; do
+ echo '.'
+ sleep 1;
+ done
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-nginx
+ image: "{{ .Values.nginx.image.repository }}:{{ .Values.nginx.image.tag }}"
+ imagePullPolicy: {{ .Values.nginx.image.pullPolicy }}
+ env:
+ {{- if .Values.nginx.extraEnv }}
+ {{- toYaml .Values.nginx.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: 8080
+ protocol: TCP
+ {{- if .Values.nginx.readinessProbe }}
+ readinessProbe:
+ {{- toYaml .Values.nginx.readinessProbe | nindent 12 }}
+ {{- end }}
+ {{- if .Values.nginx.livenessProbe }}
+ livenessProbe:
+ {{- toYaml .Values.nginx.livenessProbe | nindent 12 }}
+ {{- end }}
+ resources:
+ requests:
+ cpu: {{ mulf .Values.nginx.resources.cpu 1000 }}m
+ memory: {{ .Values.nginx.resources.memoryMi }}Mi
+ limits:
+ # cpu: ""
+ memory: {{ .Values.nginx.resources.memoryMi }}Mi
+ volumeMounts:
+ - name: nginx-conf
+ mountPath: /etc/nginx/nginx.conf
+ subPath: nginx.conf
+ - name: nginx-htpasswd
+ mountPath: /etc/nginx/nginx.htpasswd
+ subPath: nginx.htpasswd
+ - name: nginx-cors
+ mountPath: /etc/nginx/nginx-cors.conf
+ subPath: nginx-cors.conf
+ - name: nginx-index
+ mountPath: /etc/nginx/html/index.html
+ subPath: index.html
+ - name: nginx-ditto-down
+ mountPath: /etc/nginx/html/ditto-down.svg
+ subPath: ditto-down.svg
+ - name: nginx-ditto-up
+ mountPath: /etc/nginx/html/ditto-up.svg
+ subPath: ditto-up.svg
+ - name: nginx-cache
+ mountPath: /var/cache/nginx
+ - name: nginx-run
+ mountPath: /run/nginx
+ volumes:
+ - name: nginx-conf
+ configMap:
+ name: {{ .Release.Name }}-nginx-conf
+ - name: nginx-htpasswd
+ secret:
+ secretName: {{ .Release.Name }}-nginx-config-nginx-htpasswd
+ - name: nginx-cors
+ configMap:
+ name: {{ .Release.Name }}-nginx-config-nginx-cors-conf
+ - name: nginx-index
+ configMap:
+ name: {{ .Release.Name }}-nginx-config-index-html
+ - name: nginx-ditto-down
+ configMap:
+ name: {{ .Release.Name }}-nginx-config-ditto-down-svg
+ - name: nginx-ditto-up
+ configMap:
+ name: {{ .Release.Name }}-nginx-config-ditto-up-svg
+ - name: nginx-cache
+ emptyDir: {}
+ - name: nginx-run
+ emptyDir: {}
+ {{- with .Values.nginx.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.nginx.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.nginx.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-ingress-api.yaml b/deployment/helm/ditto/templates/nginx-ingress-api.yaml
new file mode 100644
index 00000000000..d1900b7ab8f
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-ingress-api.yaml
@@ -0,0 +1,62 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "ditto.fullname" . -}}
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ $fullName }}-api
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+ annotations:
+ {{- with .Values.ingress.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.ingress.api.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ingressClassName: {{ .Values.ingress.className }}
+ defaultBackend:
+ service:
+ name: {{ $fullName }}-{{ .Values.ingress.defaultBackendSuffix }}
+ port:
+ name: http
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ - host: {{ .Values.ingress.host | quote }}
+ http:
+ paths:
+ {{- range .Values.ingress.api.paths }}
+ - path: {{ .path }}
+ {{- if .pathType }}
+ pathType: {{ .pathType }}
+ {{- else }}
+ pathType: Prefix
+ {{- end }}
+ backend:
+ service:
+ name: {{ $fullName }}-{{ .backendSuffix }}
+ port:
+ name: http
+ {{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/deployment/helm/ditto/templates/nginx-ingress-root.yaml b/deployment/helm/ditto/templates/nginx-ingress-root.yaml
new file mode 100644
index 00000000000..655af5a6160
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-ingress-root.yaml
@@ -0,0 +1,62 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "ditto.fullname" . -}}
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ $fullName }}-root
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+ annotations:
+ {{- with .Values.ingress.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.ingress.root.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ingressClassName: {{ .Values.ingress.className }}
+ defaultBackend:
+ service:
+ name: {{ $fullName }}-{{ .Values.ingress.defaultBackendSuffix }}
+ port:
+ name: http
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ - host: {{ .Values.ingress.host | quote }}
+ http:
+ paths:
+ {{- range .Values.ingress.root.paths }}
+ - path: {{ .path }}
+ {{- if .pathType }}
+ pathType: {{ .pathType }}
+ {{- else }}
+ pathType: Prefix
+ {{- end }}
+ backend:
+ service:
+ name: {{ $fullName }}-{{ .backendSuffix }}
+ port:
+ name: http
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-ingress-ui.yaml b/deployment/helm/ditto/templates/nginx-ingress-ui.yaml
new file mode 100644
index 00000000000..b16d6b324a2
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-ingress-ui.yaml
@@ -0,0 +1,62 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "ditto.fullname" . -}}
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ $fullName }}-ui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+ annotations:
+ {{- with .Values.ingress.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.ingress.ui.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ingressClassName: {{ .Values.ingress.className }}
+ defaultBackend:
+ service:
+ name: {{ $fullName }}-{{ .Values.ingress.defaultBackendSuffix }}
+ port:
+ name: http
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ - host: {{ .Values.ingress.host | quote }}
+ http:
+ paths:
+ {{- range .Values.ingress.ui.paths }}
+ - path: {{ .path }}
+ {{- if .pathType }}
+ pathType: {{ .pathType }}
+ {{- else }}
+ pathType: Prefix
+ {{- end }}
+ backend:
+ service:
+ name: {{ $fullName }}-{{ .backendSuffix }}
+ port:
+ name: http
+ {{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/deployment/helm/ditto/templates/nginx-ingress-ws.yaml b/deployment/helm/ditto/templates/nginx-ingress-ws.yaml
new file mode 100644
index 00000000000..d0a425919ef
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-ingress-ws.yaml
@@ -0,0 +1,62 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "ditto.fullname" . -}}
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ $fullName }}-ws
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+ annotations:
+ {{- with .Values.ingress.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.ingress.ws.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ingressClassName: {{ .Values.ingress.className }}
+ defaultBackend:
+ service:
+ name: {{ $fullName }}-{{ .Values.ingress.defaultBackendSuffix }}
+ port:
+ name: http
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ - host: {{ .Values.ingress.host | quote }}
+ http:
+ paths:
+ {{- range .Values.ingress.ws.paths }}
+ - path: {{ .path }}
+ {{- if .pathType }}
+ pathType: {{ .pathType }}
+ {{- else }}
+ pathType: Prefix
+ {{- end }}
+ backend:
+ service:
+ name: {{ $fullName }}-{{ .backendSuffix }}
+ port:
+ name: http
+ {{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/deployment/helm/ditto/templates/nginx-ingress.yaml b/deployment/helm/ditto/templates/nginx-ingress.yaml
new file mode 100644
index 00000000000..75c2f5abd60
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-ingress.yaml
@@ -0,0 +1,843 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.ingress.controller.enabled -}}
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: "{{ .Values.ingress.controller.namespace }}"
+ labels:
+ name: "{{ .Values.ingress.controller.namespace }}"
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-node-health-check-conf
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+data:
+ nginx-node-health-check.conf: |
+ # config for health check container running together with the ingress controller
+ worker_processes 1;
+
+ events {
+ worker_connections 1024;
+ }
+
+ http {
+ charset utf-8;
+
+ server {
+ listen 8080;
+ server_name localhost;
+
+ location /healthz {
+ add_header Content-Type text-plain;
+ return 200 'ok';
+ }
+
+ }
+ }
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.namespace }}"
+ name: "{{ .Values.ingress.controller.namespace }}"
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+spec:
+ selector:
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ ports:
+ - name: tcp
+ port: 80
+ protocol: TCP
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxIngressVersion }}"
+ name: nginx-ingress-controller-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+spec:
+ ports:
+ - appProtocol: https
+ name: https-webhook
+ port: 443
+ targetPort: webhook
+ selector:
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ type: ClusterIP
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-configuration
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+data:
+ worker-processes: "auto"
+ max-worker-connections: "0" # 0 will use the value of max-worker-open-files
+ max-worker-open-files: "0" # the default of 0 means "max open files (system's limit) / worker-processes - 1024"
+ server-tokens: "False"
+ use-gzip: "True"
+ gzip-level: "6"
+ gzip-types: "text/plain text/css text/js text/xml text/javascript application/javascript application/x-javascript application/json application/xml application/xml+rss"
+ location-snippet: |
+ more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains";
+ more_set_headers 'X-Content-Type-Options: "nosniff"';
+ more_set_headers 'X-Frame-Options: "SAMEORIGIN"';
+ more_set_headers 'X-XSS-Protection: "1; mode=block"';
+ default_type application/octet-stream;
+ gzip_disable "msie6";
+ gzip_min_length 1100;
+ gzip_buffers 16 8k;
+ gzip_proxied any;
+ gunzip on;
+ gzip_static always;
+ gzip_vary on;
+
+ tcp_nopush on;
+
+ # timeouts are configured slightly higher than gateway read-timeout of 60 seconds
+ send_timeout 70; # seconds, default: 60
+
+ # ignore X-Original-URI in the request
+ proxy_hide_header X-Original-URI;
+ proxy_set_header X-Original-URI $request_uri;
+
+ # set ditto-specific forwarded headers
+
+ proxy-connect-timeout: "10" # seconds, default: 60
+ # timeouts are configured slightly higher than gateway read-timeout of 60 seconds
+ proxy-send-timeout: "70" # seconds, default: 60
+ proxy-read-timeout: "70" # seconds, default: 60
+ # will try another upstream if an error or timeout occurred during the connection
+ # or if the upstream returns 502 response
+ proxy-next-upstream: "error timeout http_502"
+ # will retry up to 4 times to find another upstream to connect to
+ proxy-next-upstream-tries: "4"
+ # will try for max. 50s to find another upstream to connect to
+ proxy-next-upstream-timeout: "50"
+ client-header-buffer-size: "8k" # allow longer URIs + headers (default: 1k)
+ large-client-header-buffers: "4 16k"
+ keep-alive: "75" #seconds, default: 75
+ log-format-upstream: '$remote_addr - "$remote_user" [$time_local] "$host" "$request" $status $bytes_sent "$upstream_addr" "$http_referer" "$http_user_agent" "$http_origin" "$http_content_type"'
+ use-forwarded-headers: "True"
+ http-snippet: |
+ charset utf-8;
+ sendfile on;
+
+ # timeouts are configured slightly higher than gateway read-timeout of 60 seconds
+ send_timeout 70; # seconds, default: 60
+
+ merge_slashes off; # allow multiple slashes for CRS Authentication
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: tcp-services
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: udp-services
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-serviceaccount
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-role
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - namespaces
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - pods
+ - secrets
+ - endpoints
+ - services
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingresses/status
+ verbs:
+ - update
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingressclasses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resourceNames:
+ - ingress-controller-leader
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - update
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - create
+ - apiGroups:
+ - coordination.k8s.io
+ resourceNames:
+ - ingress-controller-leader
+ resources:
+ - leases
+ verbs:
+ - get
+ - update
+ - apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - create
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+ - apiGroups:
+ - discovery.k8s.io
+ resources:
+ - endpointslices
+ verbs:
+ - list
+ - watch
+ - get
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - create
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: "{{ .Values.ingress.controller.namespace }}-clusterrole"
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - endpoints
+ - nodes
+ - pods
+ - secrets
+ - namespaces
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - nodes
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - services
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingresses/status
+ verbs:
+ - update
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - ingressclasses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - discovery.k8s.io
+ resources:
+ - endpointslices
+ verbs:
+ - list
+ - watch
+ - get
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission
+rules:
+ - apiGroups:
+ - admissionregistration.k8s.io
+ resources:
+ - validatingwebhookconfigurations
+ verbs:
+ - get
+ - update
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-role-binding
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: nginx-ingress-role
+subjects:
+- kind: ServiceAccount
+ name: nginx-ingress-serviceaccount
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: nginx-ingress-admission
+subjects:
+ - kind: ServiceAccount
+ name: nginx-ingress-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: "{{ .Values.ingress.controller.namespace }}-clusterrole-binding"
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: "{{ .Values.ingress.controller.namespace }}-clusterrole"
+subjects:
+- kind: ServiceAccount
+ name: nginx-ingress-serviceaccount
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: nginx-ingress-admission
+subjects:
+ - kind: ServiceAccount
+ name: nginx-ingress-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+
+---
+apiVersion: scheduling.k8s.io/v1
+kind: PriorityClass
+metadata:
+ name: high-priority
+value: 1000000
+globalDefault: false
+description: "This priority class is used for the things and gateway service pods only."
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-controller
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+ minReadySeconds: 10
+ revisionHistoryLimit: 5
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ annotations:
+ prometheus.io/port: "10254"
+ prometheus.io/scrape: "true"
+ spec:
+ priorityClassName: high-priority
+ serviceAccountName: nginx-ingress-serviceaccount
+ dnsPolicy: ClusterFirst
+ nodeSelector:
+ ingress.node: "master"
+ terminationGracePeriodSeconds: 100
+ imagePullSecrets:
+ - name: acr-secret
+ securityContext:
+ fsGroup: 101
+ supplementalGroups: [101]
+ seccompProfile:
+ type: RuntimeDefault
+ containers:
+ - name: nginx-node-health-check
+ image: docker.io/library/nginx:{{ .Values.ingress.controller.nginxVersion }}
+ imagePullPolicy: Always
+ ports:
+ - name: healthz-port
+ containerPort: 8080
+ hostPort: 31005
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8080
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ timeoutSeconds: 6
+ successThreshold: 1
+ failureThreshold: 4
+ readinessProbe:
+ httpGet:
+ path: /healthz
+ port: 8080
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ timeoutSeconds: 2
+ failureThreshold: 3
+ successThreshold: 1
+ volumeMounts:
+ - name: nginx-node-health-check-conf
+ mountPath: /etc/nginx/nginx.conf
+ subPath: nginx-node-health-check.conf
+ - name: nginx-ingress-controller
+ image: registry.k8s.io/ingress-nginx/controller:{{ .Values.ingress.controller.nginxIngressVersion }}
+ imagePullPolicy: IfNotPresent
+ args:
+ - /nginx-ingress-controller
+ - --publish-service=$(POD_NAMESPACE)/{{ .Values.ingress.controller.namespace }}
+ - --election-id=ingress-controller-leader
+ - --controller-class=k8s.io/{{ .Values.ingress.controller.namespace }}
+ - --ingress-class={{ .Values.ingress.className }}
+ - --configmap=$(POD_NAMESPACE)/nginx-configuration
+ - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
+ - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
+ - --shutdown-grace-period=65
+ - --validating-webhook=:8443
+ - --validating-webhook-certificate=/usr/local/certificates/cert
+ - --validating-webhook-key=/usr/local/certificates/key
+ securityContext:
+ capabilities:
+ drop:
+ - ALL
+ add:
+ - NET_BIND_SERVICE
+ # www-data -> 101
+ runAsUser: 101
+ runAsGroup: 101
+ runAsNonRoot: true
+ allowPrivilegeEscalation: true
+ seccompProfile:
+ type: RuntimeDefault
+ lifecycle:
+ preStop:
+ exec:
+ command: ["sleep", "95"]
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: LD_PRELOAD
+ value: /usr/local/lib/libmimalloc.so
+ ports:
+ - name: http
+ containerPort: 80
+ hostPort: 30005
+ - name: https
+ containerPort: 443
+ protocol: TCP
+ - name: health
+ containerPort: 10254
+ - name: webhook
+ containerPort: 8443
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ timeoutSeconds: 6
+ successThreshold: 1
+ failureThreshold: 4
+ readinessProbe:
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ timeoutSeconds: 2
+ failureThreshold: 3
+ successThreshold: 1
+ resources:
+ requests:
+ cpu: "0.75"
+ memory: "1024Mi"
+ volumeMounts:
+ - mountPath: /usr/local/certificates/
+ name: webhook-cert
+ readOnly: true
+ volumes:
+ - name: nginx-node-health-check-conf
+ configMap:
+ name: nginx-node-health-check-conf
+ - name: webhook-cert
+ secret:
+ secretName: nginx-ingress-admission
+---
+apiVersion: batch/v1
+kind: Job
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission-create
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+spec:
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission-create
+ spec:
+ containers:
+ - args:
+ - create
+ - --host=nginx-ingress-controller-admission.$(POD_NAMESPACE).svc
+ - --namespace=$(POD_NAMESPACE)
+ - --secret-name=nginx-ingress-admission
+ env:
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343@sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f
+ imagePullPolicy: IfNotPresent
+ name: create
+ securityContext:
+ allowPrivilegeEscalation: false
+ nodeSelector:
+ kubernetes.io/os: linux
+ restartPolicy: OnFailure
+ securityContext:
+ fsGroup: 2000
+ runAsNonRoot: true
+ runAsUser: 2000
+ serviceAccountName: nginx-ingress-admission
+
+---
+apiVersion: batch/v1
+kind: Job
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission-patch
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+spec:
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission-patch
+ spec:
+ containers:
+ - args:
+ - patch
+ - --webhook-name=nginx-ingress-admission-{{ .Values.ingress.controller.namespace }}
+ - --namespace=$(POD_NAMESPACE)
+ - --patch-mutating=false
+ - --secret-name=nginx-ingress-admission
+ - --patch-failure-policy=Fail
+ env:
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343@sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f
+ imagePullPolicy: IfNotPresent
+ name: patch
+ securityContext:
+ allowPrivilegeEscalation: false
+ nodeSelector:
+ kubernetes.io/os: linux
+ restartPolicy: OnFailure
+ securityContext:
+ fsGroup: 2000
+ runAsNonRoot: true
+ runAsUser: 2000
+ serviceAccountName: nginx-ingress-admission
+
+---
+apiVersion: networking.k8s.io/v1
+kind: IngressClass
+metadata:
+ labels:
+ app.kubernetes.io/component: controller
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: "{{ .Values.ingress.className }}"
+spec:
+ controller: k8s.io/{{ .Values.ingress.controller.namespace }}
+
+---
+apiVersion: admissionregistration.k8s.io/v1
+kind: ValidatingWebhookConfiguration
+metadata:
+ labels:
+ app.kubernetes.io/component: admission-webhook
+ app.kubernetes.io/instance: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/name: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/part-of: "{{ .Values.ingress.controller.namespace }}"
+ app.kubernetes.io/version: "{{ .Values.ingress.controller.nginxVersion }}"
+ name: nginx-ingress-admission-{{ .Values.ingress.controller.namespace }}
+webhooks:
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: nginx-ingress-controller-admission
+ namespace: "{{ .Values.ingress.controller.namespace }}"
+ path: /networking/v1/ingresses
+ failurePolicy: Fail
+ matchPolicy: Equivalent
+ name: validate.nginx.ingress.kubernetes.io
+ rules:
+ - apiGroups:
+ - networking.k8s.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - ingresses
+ sideEffects: None
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-route.yaml b/deployment/helm/ditto/templates/nginx-route.yaml
new file mode 100644
index 00000000000..dff36831c6b
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-route.yaml
@@ -0,0 +1,35 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.openshift.routes.enabled -}}
+---
+kind: Route
+apiVersion: route.openshift.io/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+ {{- with .Values.openshift.routes.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ host: {{ .Values.openshift.routes.host | default "" }}
+ to:
+ kind: Service
+ name: {{ include "ditto.fullname" . }}-nginx
+ weight: 100
+ port:
+ targetPort: {{ .Values.openshift.routes.targetPort }}
+ tls:
+ termination: {{ .Values.openshift.routes.tlsTermination | default "edge" }}
+ insecureEdgeTerminationPolicy: {{ .Values.openshift.routes.tlsInsecurePolicy | default "Redirect" }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/nginx-service.yaml b/deployment/helm/ditto/templates/nginx-service.yaml
new file mode 100644
index 00000000000..7e711056315
--- /dev/null
+++ b/deployment/helm/ditto/templates/nginx-service.yaml
@@ -0,0 +1,37 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.nginx.enabled -}}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "ditto.fullname" . }}-nginx
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+{{ include "ditto.labels" . | indent 4 }}
+ {{- with .Values.nginx.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ type: {{ .Values.nginx.service.type }}
+ ports:
+ - port: {{ .Values.nginx.service.port }}
+ {{- if (and (eq .Values.nginx.service.type "NodePort") (not (empty .Values.nginx.service.nodePort))) }}
+ nodePort: {{ .Values.nginx.service.nodePort }}
+ {{- end }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-nginx
+ app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/policies-deployment.yaml b/deployment/helm/ditto/templates/policies-deployment.yaml
new file mode 100644
index 00000000000..ea372d7915e
--- /dev/null
+++ b/deployment/helm/ditto/templates/policies-deployment.yaml
@@ -0,0 +1,310 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.policies.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-policies
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.policies.replicaCount }}
+ strategy:
+ {{- with .Values.policies.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ minReadySeconds: {{ .Values.policies.minReadySeconds }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ actorSystemName: {{ .Values.akka.actorSystemName }}
+ {{- with .Values.policies.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- if .Values.global.prometheus.enabled }}
+ prometheus.io/scrape: "true"
+ prometheus.io/path: "{{ .Values.global.prometheus.path }}"
+ prometheus.io/port: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ checksum/mongodb-config: {{ include (print $.Template.BasePath "/mongodb-secret.yaml") . | sha256sum }}
+ {{- with .Values.policies.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values.rbac.enabled }}
+ serviceAccountName: {{ template "ditto.serviceAccountName" . }}
+ {{- end }}
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: change-volume-owner
+ image: busybox
+ securityContext:
+ runAsUser: 0
+ command: [ "sh", "-c", "chown -R 1000:1000 /var/log/ditto && echo 'changed ownership of /var/log/ditto to 1000:1000'" ]
+ volumeMounts:
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-policies
+ image: {{ printf "%s:%s" .Values.policies.image.repository ( default .Chart.AppVersion ( default .Values.dittoTag .Values.policies.image.tag ) ) }}
+ imagePullPolicy: {{ .Values.policies.image.pullPolicy }}
+ env:
+ {{- if not .Values.global.logging.customConfigFile.enabled }}
+ - name: DITTO_LOGGING_DISABLE_SYSOUT_LOG
+ value: "{{ if .Values.global.logging.sysout.enabled }}false{{ else }}true{{ end }}"
+ - name: DITTO_LOGGING_FILE_APPENDER
+ value: "{{ if .Values.global.logging.logFiles.enabled }}true{{ else }}false{{ end }}"
+ {{- end }}
+ - name: DITTO_TRACING_ENABLED
+ value: "{{ .Values.global.tracing.enabled }}"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: "{{ .Values.global.tracing.otelExporterOtlpEndpoint }}"
+ - name: DITTO_TRACING_SAMPLER
+ value: "{{ .Values.global.tracing.sampler }}"
+ - name: DITTO_TRACING_RANDOM_SAMPLER_PROBABILITY
+ value: "{{ .Values.global.tracing.randomSampler.probability }}"
+ - name: DITTO_TRACING_ADAPTIVE_SAMPLER_THROUGHPUT
+ value: "{{ .Values.global.tracing.adaptiveSampler.throughput }}"
+ {{- if .Values.global.logging.logstash.enabled }}
+ - name: DITTO_LOGGING_LOGSTASH_SERVER
+ value: "{{ .Values.global.logging.logstash.endpoint }}"
+ {{- end }}
+ - name: POD_LABEL_SELECTOR
+ value: "app.kubernetes.io/name=%s"
+ - name: POD_NAMESPACE
+ value: {{.Release.Namespace}}
+ - name: INSTANCE_INDEX
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: HOSTNAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ - name: DISCOVERY_METHOD
+ value: "kubernetes-api"
+ - name: TZ
+ value: "{{ .Values.global.timezone }}"
+ - name: JAVA_TOOL_OPTIONS
+ value: >
+ {{ .Values.global.jvmOptions }}
+ -XX:ActiveProcessorCount={{ .Values.policies.jvm.activeProcessorCount }}
+ -XX:MaxRAMPercentage={{ .Values.policies.jvm.heapRamPercentage }}
+ -XX:InitialRAMPercentage={{ .Values.policies.jvm.heapRamPercentage }}
+ -XX:MaxGCPauseMillis={{ .Values.policies.jvm.maxGcPauseMillis }}
+ {{ .Values.policies.additionalJvmOptions }}
+ {{- .Values.global.akkaOptions }}
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ -Dlogback.configurationFile=/opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- range $index, $header := .Values.policies.config.persistence.events.historicalHeadersToPersist }}
+ "{{ printf "%s%d=%s" "-Dditto.policies.policy.event.historical-headers-to-persist." $index $header }}"
+ {{- end }}
+ {{- range $grantIdx, $grant := .Values.policies.config.entityCreation.grants }}
+ "{{ printf "%s%d%s=%s" "-Dditto.entity-creation.grant." $grantIdx ".resource-types.0" "policy" }}"
+ {{- range $namespaceIdx, $namespace := $grant.namespaces }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.grant." $grantIdx ".namespaces." $namespaceIdx $namespace }}"
+ {{- end }}
+ {{- range $subjectIdx, $subject := $grant.authSubjects }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.grant." $grantIdx ".auth-subjects." $subjectIdx $subject }}"
+ {{- end }}
+ {{- end }}
+ {{- range $revokeIdx, $revoke := .Values.policies.config.entityCreation.revokes }}
+ "{{ printf "%s%d%s=%s" "-Dditto.entity-creation.revoke." $revokeIdx ".resource-types.0" "policy" }}"
+ {{- range $namespaceIdx, $namespace := $revoke.namespaces }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.revoke." $revokeIdx ".namespaces." $namespaceIdx $namespace }}"
+ {{- end }}
+ {{- range $subjectIdx, $subject := $revoke.authSubjects }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.revoke." $revokeIdx ".auth-subjects." $subjectIdx $subject }}"
+ {{- end }}
+ {{- end }}
+ {{ join " " .Values.policies.systemProps }}
+ - name: MONGO_DB_SSL_ENABLED
+ value: "{{ if .Values.dbconfig.policies.ssl }}true{{ else }}false{{ end }}"
+ - name: MONGO_DB_URI
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.dbconfig.uriSecret | default ( printf "%s-mongodb-secret" ( include "ditto.fullname" . )) }}
+ key: policies-uri
+ - name: MONGO_DB_CONNECTION_MIN_POOL_SIZE
+ value: "{{ .Values.policies.config.mongodb.minPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_SIZE
+ value: "{{ .Values.policies.config.mongodb.maxPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_IDLE_TIME
+ value: "{{ .Values.policies.config.mongodb.maxPoolIdleTime }}"
+ {{- if .Values.global.prometheus.enabled }}
+ - name: PROMETHEUS_PORT
+ value: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ - name: CLUSTER_BS_REQUIRED_CONTACTS
+ value: "{{ .Values.global.cluster.requiredContactPoints }}"
+ - name: DITTO_DDATA_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.ddata.numberOfShards }}"
+ - name: DITTO_DDATA_MAX_DELTA_ELEMENTS
+ value: "{{ .Values.global.cluster.ddata.maxDeltaElements }}"
+ - name: CLUSTER_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.numberOfShards }}"
+ - name: CLUSTER_DOWNING_STABLE_AFTER
+ value: "{{ .Values.global.cluster.downingStableAfter }}"
+ - name: CLUSTER_DOWNING_DOWN_ALL_WHEN_UNSTABLE
+ value: "{{ .Values.global.cluster.downAllWhenUnstable }}"
+ - name: AKKA_PERSISTENCE_MONGO_JOURNAL_WRITE_CONCERN
+ value: "{{ .Values.policies.config.mongodb.journalWriteConcern }}"
+ - name: AKKA_PERSISTENCE_MONGO_SNAPS_WRITE_CONCERN
+ value: "{{ .Values.policies.config.mongodb.snapsWriteConcern }}"
+ - name: BREAKER_MAXTRIES
+ value: "{{ .Values.policies.config.mongodb.journalCircuitBreaker.maxTries }}"
+ - name: BREAKER_TIMEOUT
+ value: "{{ .Values.policies.config.mongodb.journalCircuitBreaker.timeout }}"
+ - name: BREAKER_RESET
+ value: "{{ .Values.policies.config.mongodb.journalCircuitBreaker.reset }}"
+ - name: SNAPSHOT_BREAKER_MAXTRIES
+ value: "{{ .Values.policies.config.mongodb.snapsCircuitBreaker.maxTries }}"
+ - name: SNAPSHOT_BREAKER_TIMEOUT
+ value: "{{ .Values.policies.config.mongodb.snapsCircuitBreaker.timeout }}"
+ - name: SNAPSHOT_BREAKER_RESET
+ value: "{{ .Values.policies.config.mongodb.snapsCircuitBreaker.reset }}"
+ - name: POLICY_ACTIVITY_CHECK_INTERVAL
+ value: "{{ .Values.policies.config.persistence.activityCheckInterval }}"
+ - name: HEALTH_CHECK_METRICS_REPORTER_RESOLUTION
+ value: "{{ .Values.policies.config.cleanup.metricsReporter.resolution }}"
+ - name: HEALTH_CHECK_METRICS_REPORTER_HISTORY
+ value: "{{ .Values.policies.config.cleanup.metricsReporter.history }}"
+ - name: CLEANUP_ENABLED
+ value: "{{ .Values.policies.config.cleanup.enabled }}"
+ - name: CLEANUP_QUIET_PERIOD
+ value: "{{ .Values.policies.config.cleanup.quietPeriod }}"
+ - name: CLEANUP_HISTORY_RETENTION_DURATION
+ value: "{{ .Values.policies.config.cleanup.history.retentionDuration }}"
+ - name: CLEANUP_INTERVAL
+ value: "{{ .Values.policies.config.cleanup.interval }}"
+ - name: CLEANUP_TIMER_THRESHOLD
+ value: "{{ .Values.policies.config.cleanup.timerThreshold }}"
+ - name: CLEANUP_CREDITS_PER_BATCH
+ value: "{{ .Values.policies.config.cleanup.creditsPerBatch }}"
+ - name: POLICIES_PERSISTENCE_PING_RATE_FREQUENCY
+ value: "{{ .Values.policies.config.persistence.pingRate.frequency }}"
+ - name: POLICIES_PERSISTENCE_PING_RATE_ENTITIES
+ value: "{{ .Values.policies.config.persistence.pingRate.entities }}"
+ - name: POLICY_SNAPSHOT_INTERVAL
+ value: "{{ .Values.policies.config.persistence.snapshots.interval }}"
+ - name: POLICY_SNAPSHOT_THRESHOLD
+ value: "{{ .Values.policies.config.persistence.snapshots.threshold }}"
+ {{- if .Values.policies.extraEnv }}
+ {{- toYaml .Values.policies.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: 8080
+ protocol: TCP
+ - name: remoting
+ containerPort: {{ .Values.akka.remoting.port }}
+ protocol: TCP
+ - name: management
+ containerPort: {{ .Values.akka.mgmthttp.port }}
+ protocol: TCP
+ {{- if .Values.global.prometheus.enabled }}
+ - name: prometheus
+ protocol: TCP
+ containerPort: {{ .Values.global.prometheus.port }}
+ {{- end }}
+ readinessProbe:
+ httpGet:
+ port: management
+ path: /ready
+ initialDelaySeconds: {{ .Values.policies.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.policies.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.policies.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.policies.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.policies.readinessProbe.failureThreshold }}
+ livenessProbe:
+ httpGet:
+ port: management
+ path: /alive
+ initialDelaySeconds: {{ .Values.policies.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.policies.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.policies.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.policies.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.policies.livenessProbe.failureThreshold }}
+ volumeMounts:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ mountPath: /opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ subPath: {{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ resources:
+ requests:
+ cpu: {{ mulf .Values.policies.resources.cpu 1000 }}m
+ memory: {{ .Values.policies.resources.memoryMi }}Mi
+ limits:
+ # ## no cpu limit to avoid CFS scheduler limits
+ # ref: https://doc.akka.io/docs/akka/snapshot/additional/deploy.html#in-kubernetes
+ # cpu: ""
+ memory: {{ .Values.policies.resources.memoryMi }}Mi
+ {{- if .Values.openshift.enabled }}
+ {{- with .Values.openshift.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- else }}
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ runAsGroup: 1000
+ allowPrivilegeEscalation: false
+ {{- end }}
+ {{- with .Values.policies.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.policies.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.policies.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ volumes:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ configMap:
+ name: {{ .Release.Name }}-logback-config-policies-xml
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ hostPath:
+ path: /var/log/ditto
+ type: DirectoryOrCreate
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/policies-pdb.yaml b/deployment/helm/ditto/templates/policies-pdb.yaml
new file mode 100644
index 00000000000..e15dd6bd5aa
--- /dev/null
+++ b/deployment/helm/ditto/templates/policies-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.policies.podDisruptionBudget.enabled (gt .Values.policies.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-policies
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.policies.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/policies-podmonitor.yaml b/deployment/helm/ditto/templates/policies-podmonitor.yaml
new file mode 100644
index 00000000000..0069a617310
--- /dev/null
+++ b/deployment/helm/ditto/templates/policies-podmonitor.yaml
@@ -0,0 +1,38 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.policies.podMonitor.enabled .Values.global.prometheus.port -}}
+{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" -}}
+---
+kind: PodMonitor
+apiVersion: monitoring.coreos.com/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}-policies
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ podMetricsEndpoints:
+ - targetPort: {{ .Values.global.prometheus.port }}
+ path: "/"
+ {{- if .Values.policies.podMonitor.interval }}
+ interval: {{ .Values.policies.podMonitor.interval }}
+ {{- end }}
+ {{- if .Values.policies.podMonitor.scrapeTimeout }}
+ scrapeTimeout: {{ .Values.policies.podMonitor.scrapeTimeout }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-policies
+ namespaceSelector:
+ matchNames:
+ - {{ $.Release.Namespace | quote }}
+{{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/role.yaml b/deployment/helm/ditto/templates/role.yaml
new file mode 100644
index 00000000000..623ea685b72
--- /dev/null
+++ b/deployment/helm/ditto/templates/role.yaml
@@ -0,0 +1,24 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.rbac.enabled -}}
+---
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}
+{{ include "ditto.labels" . | indent 4 }}
+rules:
+- apiGroups: [""]
+ resources: ["pods"]
+ verbs: ["get", "watch", "list"]
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/rolebinding.yaml b/deployment/helm/ditto/templates/rolebinding.yaml
new file mode 100644
index 00000000000..3fdece1eb9f
--- /dev/null
+++ b/deployment/helm/ditto/templates/rolebinding.yaml
@@ -0,0 +1,27 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.rbac.enabled -}}
+---
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}
+{{ include "ditto.labels" . | indent 4 }}
+roleRef:
+ kind: Role
+ name: {{ include "ditto.fullname" . }}
+ apiGroup: rbac.authorization.k8s.io
+subjects:
+- kind: ServiceAccount
+ name: {{ template "ditto.serviceAccountName" . }}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/serviceaccount.yaml b/deployment/helm/ditto/templates/serviceaccount.yaml
new file mode 100644
index 00000000000..04f60c2146a
--- /dev/null
+++ b/deployment/helm/ditto/templates/serviceaccount.yaml
@@ -0,0 +1,20 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.serviceAccount.create -}}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ template "ditto.serviceAccountName" . }}
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}
+{{ include "ditto.labels" . | indent 4 }}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/swaggerui-config.yaml b/deployment/helm/ditto/templates/swaggerui-config.yaml
new file mode 100644
index 00000000000..3f035ae4a7a
--- /dev/null
+++ b/deployment/helm/ditto/templates/swaggerui-config.yaml
@@ -0,0 +1,30 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.swaggerui.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $name := include "ditto.name" . -}}
+{{- $labels := include "ditto.labels" . -}}
+{{ $root := . }}
+{{ range $path, $bytes := .Files.Glob "swaggerui-config/**" }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ $releaseName }}-{{ $path | replace "/" "-" | replace "." "-" }}
+ labels:
+ app.kubernetes.io/name: {{ $name }}-swaggerui-config
+{{ $labels | indent 4 }}
+data:
+ {{ $path | replace "swaggerui-config/" ""}}: |-
+{{ $root.Files.Get $path | indent 4 }}
+---
+{{- end -}}
+{{- end -}}
diff --git a/deployment/helm/ditto/templates/swaggerui-deployment.yaml b/deployment/helm/ditto/templates/swaggerui-deployment.yaml
new file mode 100644
index 00000000000..e6854da2dfa
--- /dev/null
+++ b/deployment/helm/ditto/templates/swaggerui-deployment.yaml
@@ -0,0 +1,114 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.swaggerui.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-swaggerui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.swaggerui.replicaCount }}
+ strategy:
+ {{- with .Values.swaggerui.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ {{- with .Values.swaggerui.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- with .Values.swaggerui.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ initContainers:
+ - name: {{ .Chart.Name }}-swaggerui-init
+ image: "docker.io/boky/alpine-bootstrap:latest"
+ imagePullPolicy: {{ .Values.swaggerui.image.pullPolicy }}
+ command:
+ - sh
+ - -ec
+ - |
+ mkdir -p /usr/share/nginx/html/openapi
+ curl -sL https://raw.githubusercontent.com/eclipse/ditto/{{ .Chart.AppVersion }}/documentation/src/main/resources/openapi/ditto-api-2.yml -o /usr/share/nginx/html/openapi/ditto-api-2.yml
+ sed --in-place "\=- url: 'https://ditto.eclipseprojects.io/api/2'=d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ sed --in-place "/description: online Ditto Sandbox/d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ echo "removing Google auth from ditto-api-2.yml"
+ sed --in-place "/- Google:/,+1d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ sed --in-place "/ Google:/,+9d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ {{- if not .Values.gateway.config.authentication.enablePreAuthentication }}
+ echo "removing NginxBasic auth from ditto-api-2.yml"
+ sed --in-place "/- NginxBasic: \[]/d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ sed --in-place "/ NginxBasic:/,+3d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ {{- end }}
+ {{- if eq .Values.gateway.config.authentication.devops.authMethod "oauth2" }}
+ echo "removing DevOpsBasic auth from ditto-api-2.yml"
+ sed --in-place "/- DevOpsBasic: \[]/d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ sed --in-place "/ DevOpsBasic:/,+3d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ {{- else }}
+ echo "removing DevOpsBearer auth from ditto-api-2.yml"
+ sed --in-place "/- DevOpsBearer: \[]/d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ sed --in-place "/ DevOpsBearer:/,+4d" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ {{- end }}
+ sed --in-place "s=- url: /api/2=- url: {{ .Values.global.proxyPart }}/api/2=g" /usr/share/nginx/html/openapi/ditto-api-2.yml
+ cp -rv /usr/share/nginx/html/openapi/. /init-config/
+ volumeMounts:
+ - name: swagger-ui-init-config
+ mountPath: /init-config
+ containers:
+ - name: {{ .Chart.Name }}-swaggerui
+ image: "{{ .Values.swaggerui.image.repository }}:{{ .Values.swaggerui.image.tag }}"
+ imagePullPolicy: {{ .Values.swaggerui.image.pullPolicy }}
+ env:
+ - name: QUERY_CONFIG_ENABLED
+ value: "true"
+ {{- if .Values.swaggerui.extraEnv }}
+ {{- toYaml .Values.swaggerui.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: 8080
+ protocol: TCP
+ resources:
+ requests:
+ cpu: {{ mulf .Values.swaggerui.resources.cpu 1000 }}m
+ memory: {{ .Values.swaggerui.resources.memoryMi }}Mi
+ limits:
+ # cpu: ""
+ memory: {{ .Values.swaggerui.resources.memoryMi }}Mi
+ volumeMounts:
+ - name: swagger-ui-init-config
+ mountPath: /usr/share/nginx/html/openapi
+ - name: swaggerui-index
+ mountPath: /usr/share/nginx/html/index.html
+ subPath: index.html
+ volumes:
+ - name: swagger-ui-init-config
+ emptyDir: {}
+ - name: swaggerui-index
+ configMap:
+ name: {{ .Release.Name }}-swaggerui-config-index-html
+{{- end }}
diff --git a/deployment/helm/ditto/templates/swaggerui-pdb.yaml b/deployment/helm/ditto/templates/swaggerui-pdb.yaml
new file mode 100644
index 00000000000..43abca8abc2
--- /dev/null
+++ b/deployment/helm/ditto/templates/swaggerui-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.swaggerui.podDisruptionBudget.enabled (gt .Values.swaggerui.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-swaggerui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.swaggerui.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/swaggerui-service.yaml b/deployment/helm/ditto/templates/swaggerui-service.yaml
new file mode 100644
index 00000000000..bb330b57059
--- /dev/null
+++ b/deployment/helm/ditto/templates/swaggerui-service.yaml
@@ -0,0 +1,33 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.swaggerui.enabled -}}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "ditto.fullname" . }}-swaggerui
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+{{ include "ditto.labels" . | indent 4 }}
+ {{- with .Values.swaggerui.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ ports:
+ - port: {{ .Values.swaggerui.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-swaggerui
+ app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/things-deployment.yaml b/deployment/helm/ditto/templates/things-deployment.yaml
new file mode 100644
index 00000000000..a5355985557
--- /dev/null
+++ b/deployment/helm/ditto/templates/things-deployment.yaml
@@ -0,0 +1,314 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.things.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-things
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.things.replicaCount }}
+ strategy:
+ {{- with .Values.things.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ minReadySeconds: {{ .Values.things.minReadySeconds }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ actorSystemName: {{ .Values.akka.actorSystemName }}
+ {{- with .Values.things.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- if .Values.global.prometheus.enabled }}
+ prometheus.io/scrape: "true"
+ prometheus.io/path: "{{ .Values.global.prometheus.path }}"
+ prometheus.io/port: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ checksum/mongodb-config: {{ include (print $.Template.BasePath "/mongodb-secret.yaml") . | sha256sum }}
+ {{- with .Values.things.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values.rbac.enabled }}
+ serviceAccountName: {{ template "ditto.serviceAccountName" . }}
+ {{- end }}
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: change-volume-owner
+ image: busybox
+ securityContext:
+ runAsUser: 0
+ command: [ "sh", "-c", "chown -R 1000:1000 /var/log/ditto && echo 'changed ownership of /var/log/ditto to 1000:1000'" ]
+ volumeMounts:
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-things
+ image: {{ printf "%s:%s" .Values.things.image.repository ( default .Chart.AppVersion ( default .Values.dittoTag .Values.things.image.tag ) ) }}
+ imagePullPolicy: {{ .Values.things.image.pullPolicy }}
+ env:
+ {{- if not .Values.global.logging.customConfigFile.enabled }}
+ - name: DITTO_LOGGING_DISABLE_SYSOUT_LOG
+ value: "{{ if .Values.global.logging.sysout.enabled }}false{{ else }}true{{ end }}"
+ - name: DITTO_LOGGING_FILE_APPENDER
+ value: "{{ if .Values.global.logging.logFiles.enabled }}true{{ else }}false{{ end }}"
+ {{- end }}
+ - name: DITTO_TRACING_ENABLED
+ value: "{{ .Values.global.tracing.enabled }}"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: "{{ .Values.global.tracing.otelExporterOtlpEndpoint }}"
+ - name: DITTO_TRACING_SAMPLER
+ value: "{{ .Values.global.tracing.sampler }}"
+ - name: DITTO_TRACING_RANDOM_SAMPLER_PROBABILITY
+ value: "{{ .Values.global.tracing.randomSampler.probability }}"
+ - name: DITTO_TRACING_ADAPTIVE_SAMPLER_THROUGHPUT
+ value: "{{ .Values.global.tracing.adaptiveSampler.throughput }}"
+ {{- if .Values.global.logging.logstash.enabled }}
+ - name: DITTO_LOGGING_LOGSTASH_SERVER
+ value: "{{ .Values.global.logging.logstash.endpoint }}"
+ {{- end }}
+ - name: POD_LABEL_SELECTOR
+ value: "app.kubernetes.io/name=%s"
+ - name: POD_NAMESPACE
+ value: {{.Release.Namespace}}
+ - name: INSTANCE_INDEX
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: HOSTNAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ - name: DISCOVERY_METHOD
+ value: "kubernetes-api"
+ - name: TZ
+ value: "{{ .Values.global.timezone }}"
+ - name: JAVA_TOOL_OPTIONS
+ value: >
+ {{ .Values.global.jvmOptions }}
+ -XX:ActiveProcessorCount={{ .Values.things.jvm.activeProcessorCount }}
+ -XX:MaxRAMPercentage={{ .Values.things.jvm.heapRamPercentage }}
+ -XX:InitialRAMPercentage={{ .Values.things.jvm.heapRamPercentage }}
+ -XX:MaxGCPauseMillis={{ .Values.things.jvm.maxGcPauseMillis }}
+ {{ .Values.things.additionalJvmOptions }}
+ {{- .Values.global.akkaOptions }}
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ -Dlogback.configurationFile=/opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- range $index, $header := .Values.things.config.persistence.events.historicalHeadersToPersist }}
+ "{{ printf "%s%d=%s" "-Dditto.things.thing.event.historical-headers-to-persist." $index $header }}"
+ {{- end }}
+ {{- range $grantIdx, $grant := .Values.things.config.entityCreation.grants }}
+ "{{ printf "%s%d%s=%s" "-Dditto.entity-creation.grant." $grantIdx ".resource-types.0" "thing" }}"
+ {{- range $namespaceIdx, $namespace := $grant.namespaces }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.grant." $grantIdx ".namespaces." $namespaceIdx $namespace }}"
+ {{- end }}
+ {{- range $subjectIdx, $subject := $grant.authSubjects }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.grant." $grantIdx ".auth-subjects." $subjectIdx $subject }}"
+ {{- end }}
+ {{- end }}
+ {{- range $revokeIdx, $revoke := .Values.things.config.entityCreation.revokes }}
+ "{{ printf "%s%d%s=%s" "-Dditto.entity-creation.revoke." $revokeIdx ".resource-types.0" "thing" }}"
+ {{- range $namespaceIdx, $namespace := $revoke.namespaces }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.revoke." $revokeIdx ".namespaces." $namespaceIdx $namespace }}"
+ {{- end }}
+ {{- range $subjectIdx, $subject := $revoke.authSubjects }}
+ "{{ printf "%s%d%s%d=%s" "-Dditto.entity-creation.revoke." $revokeIdx ".auth-subjects." $subjectIdx $subject }}"
+ {{- end }}
+ {{- end }}
+ '-Dditto.things.wot.to-thing-description.json-template={{ .Values.things.config.wot.tdJsonTemplate | replace "\n" "" | replace "\\\"" "\"" }}'
+ {{ join " " .Values.things.systemProps }}
+ - name: MONGO_DB_SSL_ENABLED
+ value: "{{ if .Values.dbconfig.things.ssl }}true{{ else }}false{{ end }}"
+ - name: MONGO_DB_URI
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.dbconfig.uriSecret | default ( printf "%s-mongodb-secret" ( include "ditto.fullname" . )) }}
+ key: things-uri
+ - name: MONGO_DB_CONNECTION_MIN_POOL_SIZE
+ value: "{{ .Values.things.config.mongodb.minPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_SIZE
+ value: "{{ .Values.things.config.mongodb.maxPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_IDLE_TIME
+ value: "{{ .Values.things.config.mongodb.maxPoolIdleTime }}"
+ {{- if .Values.global.prometheus.enabled }}
+ - name: PROMETHEUS_PORT
+ value: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ - name: CLUSTER_BS_REQUIRED_CONTACTS
+ value: "{{ .Values.global.cluster.requiredContactPoints }}"
+ - name: DITTO_DDATA_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.ddata.numberOfShards }}"
+ - name: DITTO_DDATA_MAX_DELTA_ELEMENTS
+ value: "{{ .Values.global.cluster.ddata.maxDeltaElements }}"
+ - name: CLUSTER_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.numberOfShards }}"
+ - name: CLUSTER_DOWNING_STABLE_AFTER
+ value: "{{ .Values.global.cluster.downingStableAfter }}"
+ - name: CLUSTER_DOWNING_DOWN_ALL_WHEN_UNSTABLE
+ value: "{{ .Values.global.cluster.downAllWhenUnstable }}"
+ - name: AKKA_PERSISTENCE_MONGO_JOURNAL_WRITE_CONCERN
+ value: "{{ .Values.things.config.mongodb.journalWriteConcern }}"
+ - name: AKKA_PERSISTENCE_MONGO_SNAPS_WRITE_CONCERN
+ value: "{{ .Values.things.config.mongodb.snapsWriteConcern }}"
+ - name: BREAKER_MAXTRIES
+ value: "{{ .Values.things.config.mongodb.journalCircuitBreaker.maxTries }}"
+ - name: BREAKER_TIMEOUT
+ value: "{{ .Values.things.config.mongodb.journalCircuitBreaker.timeout }}"
+ - name: BREAKER_RESET
+ value: "{{ .Values.things.config.mongodb.journalCircuitBreaker.reset }}"
+ - name: SNAPSHOT_BREAKER_MAXTRIES
+ value: "{{ .Values.things.config.mongodb.snapsCircuitBreaker.maxTries }}"
+ - name: SNAPSHOT_BREAKER_TIMEOUT
+ value: "{{ .Values.things.config.mongodb.snapsCircuitBreaker.timeout }}"
+ - name: SNAPSHOT_BREAKER_RESET
+ value: "{{ .Values.things.config.mongodb.snapsCircuitBreaker.reset }}"
+ - name: THING_ACTIVITY_CHECK_INTERVAL
+ value: "{{ .Values.things.config.persistence.activityCheckInterval }}"
+ - name: HEALTH_CHECK_METRICS_REPORTER_RESOLUTION
+ value: "{{ .Values.things.config.cleanup.metricsReporter.resolution }}"
+ - name: HEALTH_CHECK_METRICS_REPORTER_HISTORY
+ value: "{{ .Values.things.config.cleanup.metricsReporter.history }}"
+ - name: CLEANUP_ENABLED
+ value: "{{ .Values.things.config.cleanup.enabled }}"
+ - name: CLEANUP_QUIET_PERIOD
+ value: "{{ .Values.things.config.cleanup.quietPeriod }}"
+ - name: CLEANUP_HISTORY_RETENTION_DURATION
+ value: "{{ .Values.things.config.cleanup.history.retentionDuration }}"
+ - name: CLEANUP_INTERVAL
+ value: "{{ .Values.things.config.cleanup.interval }}"
+ - name: CLEANUP_TIMER_THRESHOLD
+ value: "{{ .Values.things.config.cleanup.timerThreshold }}"
+ - name: CLEANUP_CREDITS_PER_BATCH
+ value: "{{ .Values.things.config.cleanup.creditsPerBatch }}"
+ - name: THING_SNAPSHOT_INTERVAL
+ value: "{{ .Values.things.config.persistence.snapshots.interval }}"
+ - name: THING_SNAPSHOT_THRESHOLD
+ value: "{{ .Values.things.config.persistence.snapshots.threshold }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_ENABLED
+ value: "{{ .Values.things.config.policiesEnforcer.cache.enabled }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_MAX_SIZE
+ value: "{{ .Values.things.config.policiesEnforcer.cache.maxSize }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_EXPIRE_AFTER_WRITE
+ value: "{{ .Values.things.config.policiesEnforcer.cache.expireAfterWrite }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_EXPIRE_AFTER_ACCESS
+ value: "{{ .Values.things.config.policiesEnforcer.cache.expireAfterAccess }}"
+ - name: THINGS_WOT_TO_THING_DESCRIPTION_BASE_PREFIX
+ value: "{{ .Values.things.config.wot.tdBasePrefix }}"
+ {{- if .Values.things.extraEnv }}
+ {{- toYaml .Values.things.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: remoting
+ containerPort: {{ .Values.akka.remoting.port }}
+ protocol: TCP
+ - name: management
+ containerPort: {{ .Values.akka.mgmthttp.port }}
+ protocol: TCP
+ {{- if .Values.global.prometheus.enabled }}
+ - name: prometheus
+ protocol: TCP
+ containerPort: {{ .Values.global.prometheus.port }}
+ {{- end }}
+ readinessProbe:
+ httpGet:
+ port: management
+ path: /ready
+ initialDelaySeconds: {{ .Values.things.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.things.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.things.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.things.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.things.readinessProbe.failureThreshold }}
+ livenessProbe:
+ httpGet:
+ port: management
+ path: /alive
+ initialDelaySeconds: {{ .Values.things.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.things.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.things.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.things.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.things.livenessProbe.failureThreshold }}
+ volumeMounts:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ mountPath: /opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ subPath: {{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ resources:
+ requests:
+ cpu: {{ mulf .Values.things.resources.cpu 1000 }}m
+ memory: {{ .Values.things.resources.memoryMi }}Mi
+ limits:
+ # ## no cpu limit to avoid CFS scheduler limits
+ # ref: https://doc.akka.io/docs/akka/snapshot/additional/deploy.html#in-kubernetes
+ # cpu: ""
+ memory: {{ .Values.things.resources.memoryMi }}Mi
+ {{- if .Values.openshift.enabled }}
+ {{- with .Values.openshift.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- else }}
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ runAsGroup: 1000
+ allowPrivilegeEscalation: false
+ {{- end }}
+ {{- with .Values.things.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.things.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.things.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ volumes:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ configMap:
+ name: {{ .Release.Name }}-logback-config-things-xml
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ hostPath:
+ path: /var/log/ditto
+ type: DirectoryOrCreate
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/things-pdb.yaml b/deployment/helm/ditto/templates/things-pdb.yaml
new file mode 100644
index 00000000000..8f73693f60d
--- /dev/null
+++ b/deployment/helm/ditto/templates/things-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.things.podDisruptionBudget.enabled (gt .Values.things.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-things
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.things.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/things-podmonitor.yaml b/deployment/helm/ditto/templates/things-podmonitor.yaml
new file mode 100644
index 00000000000..e9816d72801
--- /dev/null
+++ b/deployment/helm/ditto/templates/things-podmonitor.yaml
@@ -0,0 +1,38 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.things.podMonitor.enabled .Values.global.prometheus.port -}}
+{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" -}}
+---
+kind: PodMonitor
+apiVersion: monitoring.coreos.com/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}-things
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ podMetricsEndpoints:
+ - targetPort: {{ .Values.global.prometheus.port }}
+ path: "/"
+ {{- if .Values.things.podMonitor.interval }}
+ interval: {{ .Values.things.podMonitor.interval }}
+ {{- end }}
+ {{- if .Values.things.podMonitor.scrapeTimeout }}
+ scrapeTimeout: {{ .Values.things.podMonitor.scrapeTimeout }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-things
+ namespaceSelector:
+ matchNames:
+ - {{ $.Release.Namespace | quote }}
+{{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/thingssearch-deployment.yaml b/deployment/helm/ditto/templates/thingssearch-deployment.yaml
new file mode 100644
index 00000000000..7c32d457ebf
--- /dev/null
+++ b/deployment/helm/ditto/templates/thingssearch-deployment.yaml
@@ -0,0 +1,294 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if .Values.thingsSearch.enabled -}}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ditto.fullname" . }}-thingssearch
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ replicas: {{ .Values.thingsSearch.replicaCount }}
+ strategy:
+ {{- with .Values.thingsSearch.updateStrategy }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ minReadySeconds: {{ .Values.thingsSearch.minReadySeconds }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ actorSystemName: {{ .Values.akka.actorSystemName }}
+ {{- with .Values.thingsSearch.additionalLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ annotations:
+ {{- if .Values.global.prometheus.enabled }}
+ prometheus.io/scrape: "true"
+ prometheus.io/path: "{{ .Values.global.prometheus.path }}"
+ prometheus.io/port: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ checksum/mongodb-config: {{ include (print $.Template.BasePath "/mongodb-secret.yaml") . | sha256sum }}
+ {{- with .Values.thingsSearch.additionalAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values.rbac.enabled }}
+ serviceAccountName: {{ template "ditto.serviceAccountName" . }}
+ {{- end }}
+ {{- with .Values.global.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ securityContext:
+ fsGroup: 1000
+ initContainers:
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: change-volume-owner
+ image: busybox
+ securityContext:
+ runAsUser: 0
+ command: [ "sh", "-c", "chown -R 1000:1000 /var/log/ditto && echo 'changed ownership of /var/log/ditto to 1000:1000'" ]
+ volumeMounts:
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}-thingssearch
+ image: {{ printf "%s:%s" .Values.thingsSearch.image.repository ( default .Chart.AppVersion ( default .Values.dittoTag .Values.thingsSearch.image.tag ) ) }}
+ imagePullPolicy: {{ .Values.thingsSearch.image.pullPolicy }}
+ env:
+ {{- if not .Values.global.logging.customConfigFile.enabled }}
+ - name: DITTO_LOGGING_DISABLE_SYSOUT_LOG
+ value: "{{ if .Values.global.logging.sysout.enabled }}false{{ else }}true{{ end }}"
+ - name: DITTO_LOGGING_FILE_APPENDER
+ value: "{{ if .Values.global.logging.logFiles.enabled }}true{{ else }}false{{ end }}"
+ {{- end }}
+ - name: DITTO_TRACING_ENABLED
+ value: "{{ .Values.global.tracing.enabled }}"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: "{{ .Values.global.tracing.otelExporterOtlpEndpoint }}"
+ - name: DITTO_TRACING_SAMPLER
+ value: "{{ .Values.global.tracing.sampler }}"
+ - name: DITTO_TRACING_RANDOM_SAMPLER_PROBABILITY
+ value: "{{ .Values.global.tracing.randomSampler.probability }}"
+ - name: DITTO_TRACING_ADAPTIVE_SAMPLER_THROUGHPUT
+ value: "{{ .Values.global.tracing.adaptiveSampler.throughput }}"
+ {{- if .Values.global.logging.logstash.enabled }}
+ - name: DITTO_LOGGING_LOGSTASH_SERVER
+ value: "{{ .Values.global.logging.logstash.endpoint }}"
+ {{- end }}
+ - name: POD_LABEL_SELECTOR
+ value: "app.kubernetes.io/name=%s"
+ - name: POD_NAMESPACE
+ value: {{.Release.Namespace}}
+ - name: INSTANCE_INDEX
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: HOSTNAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ - name: DISCOVERY_METHOD
+ value: "kubernetes-api"
+ - name: TZ
+ value: "{{ .Values.global.timezone }}"
+ - name: JAVA_TOOL_OPTIONS
+ value: >
+ {{ .Values.global.jvmOptions }}
+ -XX:ActiveProcessorCount={{ .Values.thingsSearch.jvm.activeProcessorCount }}
+ -XX:MaxRAMPercentage={{ .Values.thingsSearch.jvm.heapRamPercentage }}
+ -XX:InitialRAMPercentage={{ .Values.thingsSearch.jvm.heapRamPercentage }}
+ -XX:MaxGCPauseMillis={{ .Values.thingsSearch.jvm.maxGcPauseMillis }}
+ {{ .Values.thingsSearch.additionalJvmOptions }}
+ {{- .Values.global.akkaOptions }}
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ -Dlogback.configurationFile=/opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{ join " " .Values.thingsSearch.systemProps }}
+ - name: MONGO_DB_SSL_ENABLED
+ value: "{{ if .Values.dbconfig.thingsSearch.ssl }}true{{ else }}false{{ end }}"
+ - name: MONGO_DB_URI
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.dbconfig.uriSecret | default ( printf "%s-mongodb-secret" ( include "ditto.fullname" . )) }}
+ key: thingsSearch-uri
+ - name: MONGO_DB_CONNECTION_MIN_POOL_SIZE
+ value: "{{ .Values.thingsSearch.config.mongodb.minPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_SIZE
+ value: "{{ .Values.thingsSearch.config.mongodb.maxPoolSize }}"
+ - name: MONGO_DB_CONNECTION_POOL_IDLE_TIME
+ value: "{{ .Values.thingsSearch.config.mongodb.maxPoolIdleTime }}"
+ {{- if .Values.global.prometheus.enabled }}
+ - name: PROMETHEUS_PORT
+ value: "{{ .Values.global.prometheus.port }}"
+ {{- end }}
+ - name: CLUSTER_BS_REQUIRED_CONTACTS
+ value: "{{ .Values.global.cluster.requiredContactPoints }}"
+ - name: DITTO_DDATA_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.ddata.numberOfShards }}"
+ - name: DITTO_DDATA_MAX_DELTA_ELEMENTS
+ value: "{{ .Values.global.cluster.ddata.maxDeltaElements }}"
+ - name: CLUSTER_NUMBER_OF_SHARDS
+ value: "{{ .Values.global.cluster.numberOfShards }}"
+ - name: CLUSTER_DOWNING_STABLE_AFTER
+ value: "{{ .Values.global.cluster.downingStableAfter }}"
+ - name: CLUSTER_DOWNING_DOWN_ALL_WHEN_UNSTABLE
+ value: "{{ .Values.global.cluster.downAllWhenUnstable }}"
+ - name: MONGO_DB_READ_PREFERENCE
+ value: "{{ .Values.thingsSearch.config.mongodb.searchReadPreference }}"
+ - name: QUERY_PERSISTENCE_MONGO_DB_READ_CONCERN
+ value: "{{ .Values.thingsSearch.config.mongodb.queryReadConcern }}"
+ - name: MONGO_DB_WRITE_CONCERN
+ value: "{{ .Values.thingsSearch.config.mongodb.searchWriteConcern }}"
+ - name: UPDATER_PERSISTENCE_MONGO_DB_READ_CONCERN
+ value: "{{ .Values.thingsSearch.config.mongodb.updaterPersistenceReadConcern }}"
+ - name: UPDATER_PERSISTENCE_MONGO_DB_READ_PREFERENCE
+ value: "{{ .Values.thingsSearch.config.mongodb.updaterPersistenceReadPreference }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_PERSISTENCE_WITH_ACKS_WRITE_CONCERN
+ value: "{{ .Values.thingsSearch.config.mongodb.searchWithAcksWriteConcern }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_POLICY_CACHE_SIZE
+ value: "{{ .Values.thingsSearch.config.updater.stream.policiesEnforcer.cache.maxSize }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_POLICY_CACHE_EXPIRY
+ value: "{{ .Values.thingsSearch.config.updater.stream.policiesEnforcer.cache.expireAfterWrite }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_POLICY_CACHE_EXPIRY_AFTER_ACCESS
+ value: "{{ .Values.thingsSearch.config.updater.stream.policiesEnforcer.cache.expireAfterAccess }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_THING_CACHE_SIZE
+ value: "{{ .Values.thingsSearch.config.updater.stream.thingCache.maxSize }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_THING_CACHE_EXPIRY
+ value: "{{ .Values.thingsSearch.config.updater.stream.thingCache.expireAfterWrite }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_THING_CACHE_EXPIRY_AFTER_ACCESS
+ value: "{{ .Values.thingsSearch.config.updater.stream.thingCache.expireAfterAccess }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_RETRIEVAL_PARALLELISM
+ value: "{{ .Values.thingsSearch.config.updater.stream.retrievalParallelism }}"
+ - name: THINGS_SEARCH_UPDATER_STREAM_PERSISTENCE_PARALLELISM
+ value: "{{ .Values.thingsSearch.config.updater.stream.persistence.parallelism }}"
+ - name: ACTIVITY_CHECK_INTERVAL
+ value: "{{ .Values.thingsSearch.config.updater.activityCheckInterval }}"
+ - name: BACKGROUND_SYNC_ENABLED
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.enabled }}"
+ - name: BACKGROUND_SYNC_QUIET_PERIOD
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.quietPeriod }}"
+ - name: BACKGROUND_SYNC_IDLE_TIMEOUT
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.idleTimeout }}"
+ - name: BACKGROUND_SYNC_TOLERANCE_WINDOW
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.toleranceWindow }}"
+ - name: BACKGROUND_SYNC_KEEP_EVENTS
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.keepEvents }}"
+ - name: BACKGROUND_SYNC_THROTTLE_THROUGHPUT
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.throttle.throughput }}"
+ - name: BACKGROUND_SYCN_THROTTLE_PERIOD
+ value: "{{ .Values.thingsSearch.config.updater.backgroundSync.throttle.period }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_MAX_SIZE
+ value: "{{ .Values.thingsSearch.config.updater.stream.policiesEnforcer.cache.maxSize }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_EXPIRE_AFTER_WRITE
+ value: "{{ .Values.thingsSearch.config.updater.stream.policiesEnforcer.cache.expireAfterWrite }}"
+ - name: DITTO_POLICIES_ENFORCER_CACHE_EXPIRE_AFTER_ACCESS
+ value: "{{ .Values.thingsSearch.config.updater.stream.policiesEnforcer.cache.expireAfterAccess }}"
+ {{- if .Values.thingsSearch.extraEnv }}
+ {{- toYaml .Values.thingsSearch.extraEnv | nindent 12 }}
+ {{- end }}
+ ports:
+ - name: remoting
+ containerPort: {{ .Values.akka.remoting.port }}
+ protocol: TCP
+ - name: management
+ containerPort: {{ .Values.akka.mgmthttp.port }}
+ protocol: TCP
+ {{- if .Values.global.prometheus.enabled }}
+ - name: prometheus
+ protocol: TCP
+ containerPort: {{ .Values.global.prometheus.port }}
+ {{- end }}
+ readinessProbe:
+ httpGet:
+ port: management
+ path: /ready
+ initialDelaySeconds: {{ .Values.thingsSearch.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.thingsSearch.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.thingsSearch.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.thingsSearch.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.thingsSearch.readinessProbe.failureThreshold }}
+ livenessProbe:
+ httpGet:
+ port: management
+ path: /alive
+ initialDelaySeconds: {{ .Values.thingsSearch.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.thingsSearch.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.thingsSearch.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.thingsSearch.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.thingsSearch.livenessProbe.failureThreshold }}
+ volumeMounts:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ mountPath: /opt/ditto/{{ .Values.global.logging.customConfigFile.fileName }}
+ subPath: {{ .Values.global.logging.customConfigFile.fileName }}
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ mountPath: /var/log/ditto
+ {{- end }}
+ resources:
+ requests:
+ cpu: {{ mulf .Values.thingsSearch.resources.cpu 1000 }}m
+ memory: {{ .Values.thingsSearch.resources.memoryMi }}Mi
+ limits:
+ # ## no cpu limit to avoid CFS scheduler limits
+ # ref: https://doc.akka.io/docs/akka/snapshot/additional/deploy.html#in-kubernetes
+ # cpu: ""
+ memory: {{ .Values.thingsSearch.resources.memoryMi }}Mi
+ {{- if .Values.openshift.enabled }}
+ {{- with .Values.openshift.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- else }}
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ runAsGroup: 1000
+ allowPrivilegeEscalation: false
+ {{- end }}
+ {{- with .Values.thingsSearch.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.thingsSearch.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.thingsSearch.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ volumes:
+ {{- if .Values.global.logging.customConfigFile.enabled }}
+ - name: ditto-custom-log-config
+ configMap:
+ name: {{ .Release.Name }}-logback-config-thingssearch-xml
+ {{- end }}
+ {{- if .Values.global.logging.logFiles.enabled }}
+ - name: ditto-log-files-directory
+ hostPath:
+ path: /var/log/ditto
+ type: DirectoryOrCreate
+ {{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/thingssearch-pdb.yaml b/deployment/helm/ditto/templates/thingssearch-pdb.yaml
new file mode 100644
index 00000000000..c7651415a06
--- /dev/null
+++ b/deployment/helm/ditto/templates/thingssearch-pdb.yaml
@@ -0,0 +1,26 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.things.podDisruptionBudget.enabled (gt .Values.things.replicaCount 1.0) -}}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "ditto.fullname" . }}-thingssearch
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+ app.kubernetes.io/instance: {{ .Release.Name }}
+ minAvailable: {{ .Values.things.podDisruptionBudget.minAvailable }}
+{{- end }}
diff --git a/deployment/helm/ditto/templates/thingssearch-podmonitor.yaml b/deployment/helm/ditto/templates/thingssearch-podmonitor.yaml
new file mode 100644
index 00000000000..7f83efb6a58
--- /dev/null
+++ b/deployment/helm/ditto/templates/thingssearch-podmonitor.yaml
@@ -0,0 +1,38 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+{{- if and .Values.thingsSearch.podMonitor.enabled .Values.global.prometheus.port -}}
+{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" -}}
+---
+kind: PodMonitor
+apiVersion: monitoring.coreos.com/v1
+metadata:
+ name: {{ include "ditto.fullname" . }}-thingssearch
+ labels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+{{ include "ditto.labels" . | indent 4 }}
+spec:
+ podMetricsEndpoints:
+ - targetPort: {{ .Values.global.prometheus.port }}
+ path: "/"
+ {{- if .Values.thingsSearch.podMonitor.interval }}
+ interval: {{ .Values.thingsSearch.podMonitor.interval }}
+ {{- end }}
+ {{- if .Values.thingsSearch.podMonitor.scrapeTimeout }}
+ scrapeTimeout: {{ .Values.thingsSearch.podMonitor.scrapeTimeout }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: {{ include "ditto.name" . }}-thingssearch
+ namespaceSelector:
+ matchNames:
+ - {{ $.Release.Namespace | quote }}
+{{- end }}
+{{- end }}
diff --git a/deployment/helm/ditto/values.yaml b/deployment/helm/ditto/values.yaml
new file mode 100644
index 00000000000..2f6649605e2
--- /dev/null
+++ b/deployment/helm/ditto/values.yaml
@@ -0,0 +1,1586 @@
+# Copyright (c) 2023 Contributors to the Eclipse Foundation
+#
+# See the NOTICE file(s) distributed with this work for additional
+# information regarding copyright ownership.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0
+#
+# SPDX-License-Identifier: EPL-2.0
+---
+# Default values for ditto.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+serviceAccount:
+ # create controls whether a service account should be created
+ create: true
+ # name is the name of the service account to use
+ # If not set and create is true, a name is generated using the fullname template
+ name:
+
+rbac:
+ # enabled controls whether RBAC resources will be created
+ enabled: true
+
+nameOverride: ""
+fullnameOverride: ""
+
+## ----------------------------------------------------------------------------
+## global configuration shared by all components
+global:
+ # cluster holds the configuration for the Ditto/Akka cluster
+ cluster:
+ # requiredContactPoints defines the total amount of replicas in the Ditto cluster
+ # only if this amount is "seen" during cluster formation, the cluster can form itself
+ requiredContactPoints: 5
+ # ddata holds the "Distributed Data" configuration:
+ ddata:
+ # numberOfShards defines whether ddata structures should be shared (if >1)
+ # this is needed in case a lot of event subscribers (thousands) are connected simultaneously
+ numberOfShards: 1
+ # maxDeltaElements defines how many elements should be synced with a single cluster message
+ # if numberOfShards is > 1, it makes sense to keep maxDeltaElements lower
+ # so that the message size for remoting is not exceeding the configured max message size
+ maxDeltaElements: 1
+ # numberOfShards configures the sharding applied for things/policies/connections based on their ID
+ # as a rule of thumb: should be factor ten of the amount of cluster replicas for an entity
+ numberOfShards: 50
+ # downingStableAfter is a configuration of the Akka SBR (split brain resolver)
+ # how to find the right value: https://doc.akka.io/docs/akka/current/split-brain-resolver.html
+ downingStableAfter: 15s
+ # downAllWhenUnstable is a configuration of the Akka SBR (split brain resolver)
+ downAllWhenUnstable: "on"
+ # basicAuthUsers configures (as a map) several user/password combinations which the nginx of the Ditto chart will authenticate
+ basicAuthUsers: {}
+ # ditto:
+ # user: ditto
+ # password: ditto
+ # hashedBasicAuthUsers configures a list of hashed .htpasswd username/password entries
+ hashedBasicAuthUsers: []
+ # jwtOnly controls whether only OpenID-Connect authentication is supported
+ # if false, both OpenID-Connect and basicAuth via nginx (see above "basicAuthUsers" and "hashedBasicAuthUsers") is used
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#openid-connect
+ jwtOnly: false
+ # jvmOptions defines the JVM options applied to all Ditto services running in the JVM, it is put in JAVA_TOOL_OPTIONS
+ jvmOptions: >
+ -XX:+ExitOnOutOfMemoryError
+ -XX:+UseContainerSupport
+ -XX:+UseStringDeduplication
+ -Xss512k
+ -XX:MaxMetaspaceSize=256m
+ -XX:+UseG1GC
+ -Djava.net.preferIPv4Stack=true
+ akkaOptions: >
+ -Dakka.management.cluster.bootstrap.contact-point-discovery.port-name=management
+ -Dakka.cluster.failure-detector.threshold=15.0
+ -Dakka.cluster.failure-detector.expected-response-after=3s
+ -Dakka.cluster.failure-detector.acceptable-heartbeat-pause=7s
+ -Dakka.persistence.journal-plugin-fallback.recovery-event-timeout=30s
+ -Dakka.persistence.max-concurrent-recoveries=100
+ -Dakka.cluster.sharding.updating-state-timeout=20s
+ -Dakka.cluster.shutdown-after-unsuccessful-join-seed-nodes=120s
+ # timezone defines the timezone to configure the JVM with
+ timezone: Europe/Berlin
+ # imagePullSecrets will be added to every deployment
+ imagePullSecrets: []
+ # proxyPart configures a reverse proxy part to be added in front of the Ditto API endpoints:
+ proxyPart: ""
+ # prometheus holds the Prometheus specific configuration
+ prometheus:
+ # enabled controls whether scrape config annotation will be added to pod templates
+ enabled: true
+ # path where prometheus metric will be provided
+ path: "/"
+ # port where prometheus metrics will be provided
+ port: 9095
+ # logging the logging configuration for Ditto
+ logging:
+ # sysout holds the logging to SYSOUT config
+ sysout:
+ # enabled defines whether to log to SYSOUT
+ enabled: true
+ # logstash configures if logs should be pushed to a logstash endpoint
+ logstash:
+ # enabled defines whether to log to logstash
+ enabled: false
+ # endpoint configures the logstash endpoint to send logs to
+ endpoint: ""
+ # logFiles defines logging to log files config
+ logFiles:
+ # enabled whether to write logs to log files
+ # log files can be found on the host under /var/log/ditto
+ enabled: false
+ # customConfigFile configures that a custom "Logback" config file should be used instead of the one bundled
+ # with Ditto on the classpath
+ customConfigFile:
+ # enabled if enabled, a custom logback.xml file added to the Ditto containers will be used for logging configuration
+ enabled: true
+ # fileName passed as Java system property "-Dlogback.configurationFile"
+ fileName: logback.xml
+ # tracing configuration for Ditto
+ tracing:
+ # enabled whether tracing (via OpenTelemetry) is enabled
+ enabled: false
+ # otelExporterOtlpEndpoint the OTLP endpoint to report traces to
+ otelExporterOtlpEndpoint: "http://localhost:4317"
+ # sampler the tracing sampler to use
+ # can be one of:
+ # - always: report all traces.
+ # - never: don't report any trace.
+ # - random: randomly decide using the probability defined in the random-sampler.probability setting.
+ # - adaptive: keeps dynamic samplers for each operation while trying to achieve a set throughput goal.
+ sampler: never
+ # randomSampler configures the 'random' sampler
+ randomSampler:
+ # probability configures the probability of a span being sampled, must be a value between 0 and 1
+ probability: 0.01
+ # adaptiveSampler configures the 'adaptive' sampler
+ adaptiveSampler:
+ # throughput the throughput goal trying to achieve with the adaptive sampler
+ throughput: 600
+
+## ----------------------------------------------------------------------------
+## dbconfig for mongodb connections
+## will be handled as k8s secret as connection uri might contain auth credentials
+dbconfig:
+ # policies the MongoDB configuration for Ditto "policies" service
+ policies:
+ uri: mongodb://#{PLACEHOLDER_MONGODB_HOSTNAME}#:27017/ditto
+ ssl: false
+ # things the MongoDB configuration for Ditto "things" service
+ things:
+ uri: mongodb://#{PLACEHOLDER_MONGODB_HOSTNAME}#:27017/ditto
+ ssl: false
+ # connectivity the MongoDB configuration for Ditto "connectivity" service
+ connectivity:
+ uri: mongodb://#{PLACEHOLDER_MONGODB_HOSTNAME}#:27017/ditto
+ ssl: false
+ # thingsSearch the MongoDB configuration for Ditto "things-search" service
+ thingsSearch:
+ uri: mongodb://#{PLACEHOLDER_MONGODB_HOSTNAME}#:27017/ditto
+ ssl: false
+ ## If following property is set, an existing secret will be used to retrieve the mongodb connectionUris from.
+ # uriSecret: my-uri-secret
+
+## ----------------------------------------------------------------------------
+## ingress configures the Ingress
+ingress:
+ # enabled whether Ingress should be enabled as alternative to the contained nginx
+ enabled: false
+ # className is the 'ingressClassName' to configure in the Ingress spec
+ className: nginx
+ # host the hostname of the Ingress shared for all: api, ws and ui
+ host: localhost
+ # defaultBackendSuffix the suffix to add to the internal fullname to use as Ingress "defaultBackend"
+ defaultBackendSuffix: nginx
+ # annotations common annotations for all 3 Ingresses of Ditto
+ controller:
+ # enabled whether Ingress controller should be enabled
+ enabled: false
+ # namespace for ingress controller, managed by helm, should not be created manually
+ namespace: ingress-nginx
+ # Ingress-NGINX version. Check Supported Versions table from https://github.com/kubernetes/ingress-nginx to match k8s version.
+ nginxIngressVersion: "v1.8.0"
+ # Nginx Version. Check Supported Versions table from https://github.com/kubernetes/ingress-nginx to match k8s version.
+ nginxVersion: "1.21.6"
+ annotations:
+ nginx.ingress.kubernetes.io/service-upstream: "true"
+ nginx.ingress.kubernetes.io/server-snippet: |
+ charset utf-8;
+ default_type application/json;
+ chunked_transfer_encoding off;
+
+ send_timeout 70; # seconds, default: 60
+ client_header_buffer_size 8k; # allow longer URIs + headers (default: 1k)
+ large_client_header_buffers 4 16k;
+
+ proxy_http_version 1.1;
+ proxy_set_header X-Forwarded-Host $http_host;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header Host $host;
+ # api the /api, /devops, /status, /overall and /health Ingress configuration
+ api:
+ # paths configures ingress paths
+ paths:
+ - path: /api
+ backendSuffix: gateway
+ - path: /devops
+ backendSuffix: gateway
+ - path: /status
+ backendSuffix: gateway
+ - path: /stats
+ backendSuffix: gateway
+ - path: /overall
+ backendSuffix: gateway
+ - path: /health
+ backendSuffix: gateway
+ # annotations defines k8s annotations to add to the Ingress
+ annotations:
+ nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
+ nginx.ingress.kubernetes.io/proxy-send-timeout: "70"
+ nginx.ingress.kubernetes.io/proxy-read-timeout: "70"
+ nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout http_502"
+ nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "4"
+ nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "50"
+ nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
+ nginx.ingress.kubernetes.io/configuration-snippet: |
+ set $cors '1';
+
+ if ($request_method = 'OPTIONS') {
+ set $cors "${cors}o";
+ }
+
+ if ($cors = '1') {
+ add_header 'Access-Control-Allow-Origin' '$http_origin' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
+ add_header 'Access-Control-Allow-Credentials' 'true' always;
+ add_header 'Access-Control-Allow-Headers' '$http_access_control_request_headers' always;
+ add_header 'Access-Control-Expose-Headers' '*' always;
+ }
+
+ if ($cors = '1o') {
+ # Tell client that this pre-flight info is valid for 20 days
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Access-Control-Allow-Origin' '$http_origin' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
+ add_header 'Access-Control-Allow-Credentials' 'true' always;
+ add_header 'Access-Control-Allow-Headers' '$http_access_control_request_headers' always;
+ add_header 'Access-Control-Expose-Headers' '*' always;
+ add_header 'Content-Type' 'text/plain charset=UTF-8';
+ add_header 'Content-Length' 0;
+ return 200;
+ }
+
+ if ($request_method = 'OPTIONS') {
+ add_header 'Content-Type' 'text/plain charset=UTF-8';
+ return 405 "Origin $http_origin is not in CORS allow-list, contact your admin to get it added";
+ }
+
+ # security relevant headers:
+ add_header "Content-Security-Policy" "default-src 'none'; frame-ancestors 'none'" always;
+ add_header "Strict-Transport-Security" "max-age=63072000; includeSubdomains;" always;
+ add_header "Cache-Control" "no-cache" always;
+ add_header "X-Content-Type-Options" "nosniff" always;
+ add_header "X-Frame-Options" "SAMEORIGIN" always;
+ add_header "X-XSS-Protection" "1; mode=block" always;
+ # ws the /ws (WebSocket) Ingress configuration
+ ws:
+ # paths configures ingress paths
+ paths:
+ - path: /ws
+ backendSuffix: gateway
+ # annotations defines k8s annotations to add to the Ingress
+ annotations:
+ nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
+ nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
+ nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout http_502"
+ nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "4"
+ nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "50"
+ nginx.ingress.kubernetes.io/proxy-buffering: "off"
+ # the / Ingress configuration for serving the landing page and static resources
+ root:
+ # paths configures ingress paths
+ paths:
+ - path: /
+ pathType: Exact
+ backendSuffix: nginx
+ - path: /index.html
+ pathType: Exact
+ backendSuffix: nginx
+ - path: /ditto-up.svg
+ pathType: Exact
+ backendSuffix: nginx
+ - path: /ditto-down.svg
+ pathType: Exact
+ backendSuffix: nginx
+ # annotations defines k8s annotations to add to the Ingress
+ annotations:
+ nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
+ nginx.ingress.kubernetes.io/configuration-snippet: |
+ # security relevant headers:
+ add_header "Content-Security-Policy" "default-src 'self'; script-src-elem 'self' 'sha256-Kq9eqc/CtX2tgHPLJUEf8vDO9eNiGaRBrwAYYXTroVc=' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; worker-src 'self' blob:; object-src 'none';" always;
+ add_header "Strict-Transport-Security" "max-age=63072000; includeSubdomains;" always;
+ add_header "Cache-Control" "no-cache" always;
+ add_header "X-Content-Type-Options" "nosniff" always;
+ add_header "X-Frame-Options" "SAMEORIGIN" always;
+ add_header "X-XSS-Protection" "1; mode=block" always;
+ # ui the /ui and /apidoc Ingress configuration
+ ui:
+ # paths configures ingress paths
+ paths:
+ - path: /
+ pathType: Exact
+ backendSuffix: nginx
+ - path: /apidoc(/|$)(.*)
+ backendSuffix: swaggerui
+ - path: /ui(/|$)(.*)
+ backendSuffix: dittoui
+ # annotations defines k8s annotations to add to the Ingress
+ annotations:
+ nginx.ingress.kubernetes.io/use-regex: "true"
+ nginx.ingress.kubernetes.io/rewrite-target: /$2
+ nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
+ nginx.ingress.kubernetes.io/configuration-snippet: |
+ # security relevant headers:
+ add_header "Content-Security-Policy" "default-src 'self'; script-src-elem 'self' 'sha256-Ve/Ec/6YDEeTc+9y+QCJ+e9OhyGWAj3bYxCzNGfOn6U=' 'sha256-Kq9eqc/CtX2tgHPLJUEf8vDO9eNiGaRBrwAYYXTroVc=' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; img-src 'self' data: https://raw.githubusercontent.com; font-src 'self' https://cdnjs.cloudflare.com; worker-src 'self' blob:; connect-src 'self' localhost http://localhost:8080; object-src 'none';" always;
+ add_header "Strict-Transport-Security" "max-age=63072000; includeSubdomains;" always;
+ add_header "Cache-Control" "no-cache" always;
+ add_header "X-Content-Type-Options" "nosniff" always;
+ add_header "X-Frame-Options" "SAMEORIGIN" always;
+ add_header "X-XSS-Protection" "1; mode=block" always;
+ # tls configures the TLS for ingress
+ tls: []
+ # - secretName: ditto-tls
+ # hosts:
+ # - localhost
+
+
+## ----------------------------------------------------------------------------
+## openshift configures the OpenShift deployment
+openshift:
+ # enabled whether to deploy to OpenShift
+ enabled: false
+ # routes the OpenShift Routes
+ routes:
+ # enabled whether OpenShift routes are enabled
+ enabled: false
+ # annotations define k8s annotations to apply for the routes
+ annotations: {}
+ # host: ""
+ # targetPort configures the target port
+ targetPort: http
+ # tlsTermination: "edge"
+ # tlsInsecurePolicy: "Redirect"
+ # securityContext the security context for OpenShift
+ securityContext: {}
+
+## ----------------------------------------------------------------------------
+## akka holds the Akka actor configuration
+## ref: https://doc.akka.io/docs/akka/current/typed/index.html
+akka:
+ # actorSystemName defines the actor/cluster name of the Ditto cluster
+ actorSystemName: ditto-cluster
+ # remoting holds configuration for the Akka cluster remoting
+ remoting:
+ # port defines the Port to use for remoting
+ port: 2551
+ # mgmthttp holds configuration for the Akka cluster management
+ mgmthttp:
+ # port defines the Port to use for akka http management
+ port: 8558
+
+# Set "dittoTag" in order to specify another Ditto version to use for all Ditto services:
+# you may also use "1" (for latest Ditto 1.x.x) or "1.5" (for latest Ditto 1.5.x)
+# dittoTag: 3.3.0
+
+
+## ----------------------------------------------------------------------------
+## policies configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-policies.html
+policies:
+ # enabled controls whether policies related resources should be created
+ enabled: true
+ # replicaCount configuration for policies
+ replicaCount: 1
+ # updateStrategy configuration for policies
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # minReadySeconds configures the minimum number of seconds for which a newly created Pod should be ready without any
+ # of its containers crashing, for it to be considered available
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#min-ready-seconds
+ minReadySeconds: 10
+ # additionalLabels configuration for policies
+ additionalLabels: {}
+ # additionalAnnotations configuration for policies
+ additionalAnnotations: {}
+ image:
+ # repository for the policies docker image
+ repository: docker.io/eclipse/ditto-policies
+ # tag for the policies docker image - overwrite to specify something else than Chart.AppVersion
+ # tag: 3.3.0
+ # pullPolicy for the policies docker image
+ pullPolicy: IfNotPresent
+ # additionalJvmOptions JVM options to put into JAVA_TOOL_OPTIONS
+ additionalJvmOptions: ""
+ # systemProps used to define arbitrary system properties for policies service
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#configuration
+ systemProps:
+ # extraEnv to add arbitrary environment variable to policies container
+ extraEnv:
+ # - name: LOG_LEVEL_APPLICATION
+ # value: "DEBUG"
+ # resources configures the resources available/to use for the policies service
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.5
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 1024
+ # jvm contains JVM specific scaling/tuning configuration of e.g. processors and garbage collector settings
+ jvm:
+ # activeProcessorCount defines how many processors the JVM should be configured to use
+ # this is e.g. relevant for the GC which calculates the amount of asynchronous threads for GC based on the processor count
+ activeProcessorCount: 2
+ # heapRamPercentage defines how much memory of the configured "resources.memoryMi" can be used by the JVM heap space
+ # be aware that the JVM also requires memory for "off heap" (and also stack) space + the container needs memory as well
+ heapRamPercentage: 60
+ # maxGcPauseMillis configures the used G1 GC "target for the maximum GC pause time"
+ # default (by JVM if not set): 200
+ maxGcPauseMillis: 150
+ # readinessProbe configuration for policies
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ readinessProbe:
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 3
+ successThreshold: 1
+ failureThreshold: 3
+ # livenessProbe configuration for policies
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ livenessProbe:
+ initialDelaySeconds: 160
+ periodSeconds: 10
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 4
+ # podDisruptionBudget configuration for policies
+ # ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether policies related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # nodeSelector configuration for policies
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ # tolerations configuration for policies
+ # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ tolerations: []
+ # affinity configuration for policies
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ # podMonitor configuration for policies
+ podMonitor:
+ # enabled configures whether Pod Monitor is enabled, then a resource to scrape policies metrics will be created
+ enabled: false
+ # interval: 30s
+ # scrapeTimeout: 15s
+ # config holds policies specific configuration
+ config:
+ # mongodb holds mongodb specific configuration of policies
+ mongodb:
+ # minPoolSize configures the minimum number of connections in the connection pool
+ minPoolSize: 10
+ # maxPoolSize configures the minimum number of connections in the connection pool
+ maxPoolSize: 200
+ # maxPoolIdleTime configures the maximum amount of time a pooled connection is allowed to idle before closing the connection
+ maxPoolIdleTime: 10m
+ # journalWriteConcern the MongoDB write concern to apply for writing operations on the event journal
+ # one of: Unacknowledged | Acknowledged | Journaled | ReplicaAcknowledged
+ journalWriteConcern: "Journaled"
+ # snapsWriteConcern the MongoDB write concern to apply for writing operations on the snapshots persistence
+ # one of: Unacknowledged | Acknowledged | Journaled | ReplicaAcknowledged
+ snapsWriteConcern: "Journaled"
+ # journalCircuitBreaker configures the circuit breaker for MongoDB operations on the event journal
+ journalCircuitBreaker:
+ # maxTries opens the circuit breaker if an exception during persisting an event occurs this often
+ # a successful write resets the counter
+ maxTries: 10
+ # timeout configures the MongoDB write timeouts also causing the circuit breaker to open
+ timeout: 10s
+ # reset after this time in "Open" state, the circuit breaker is "Half-opened" again
+ reset: 5s
+ # snapsCircuitBreaker configures the circuit breaker for MongoDB operations on the snapshots persistence
+ snapsCircuitBreaker:
+ # maxTries opens the circuit breaker if an exception during persisting a snapshot occurs this often
+ # a successful write resets the counter
+ maxTries: 10
+ # timeout configures the MongoDB write timeouts also causing the circuit breaker to open
+ timeout: 20s
+ # reset after this time in "Open" state, the circuit breaker is "Half-opened" again
+ reset: 8s
+ # cleanup contains the configuration for the background cleanup of stale snapshots and events
+ cleanup:
+ # enabled configures whether background cleanup is enabled or not
+ # if enabled, stale "snapshot" and "journal" entries will be cleaned up from the MongoDB by a background process:
+ enabled: false
+ # quietPeriod defines how long to stay in a state where the background cleanup is not yet started
+ quietPeriod: 5m
+ # history contains configuration regarding the event history
+ history:
+ # retentionDuration configures the duration of how long to "keep" events and snapshots before being allowed to remove them in scope of cleanup
+ retentionDuration: 30d
+ # metricsReporter config of MongoMetricsReporter which is used by policies in order to report current persistence
+ # roundtrip times in order to determine credits to cleanup stale data (journal entries, snapshots)
+ metricsReporter:
+ # resolution configures how far apart each measurement should be done
+ resolution: 1s
+ # history configures how many historical items to keep
+ history: 5
+ # interval configures how often a "credit decision" is made
+ interval: 1s
+ # timerThreshold configures the maximum database latency to give out credit for cleanup actions
+ timerThreshold: 100ms
+ # creditsPerBatch configures how many "cleanup credits" should be generated per "interval" as long as the
+ creditsPerBatch: 5
+ # persistence holds configuration regarding (akka) persistence of policies (event journal and snapshots)
+ persistence:
+ # activityCheckInterval configures to keep policies for that amount of time in memory when no other use did happen:
+ activityCheckInterval: 2d
+ # pingRate used to throttle pinging of PolicyPersistenceActors, so that not all PolicyPersistenceActors are recovered at the same time:
+ pingRate:
+ # frequency the frequency of sent "pings" to PolicyPersistenceActors
+ frequency: 1s
+ # entities the amount of entities to wake up per "frequency" interval
+ entities: 50
+ # events contains event journal specific configuration
+ events:
+ # historicalHeadersToPersist define the DittoHeaders to persist when persisting events to the journal
+ # those can e.g. be retrieved as additional "audit log" information when accessing a historical Policy revision
+ historicalHeadersToPersist:
+ # - "ditto-originator"
+ # - "ditto-origin"
+ # - "correlation-id"
+ # snapshots contains snapshots persistence specific configuration
+ snapshots:
+ # interval configures the interval when to do snapshot for a Policy which had changes to it
+ interval: 15m
+ # threshold configures the threshold after how many changes to a Policy to do a snapshot
+ threshold: 5
+ # entityCreation by default, Ditto allows anyone to create a new entity (policy in this case) in any namespace.
+ # However, this behavior can be customized, and the ability to create new entities can be restricted:
+ entityCreation:
+ # grants contains the list of creation config entries which would allow the creation of entities
+ # An empty list would *not* allow any entity to be created.
+ # You must have at least one entry, even if it is without restrictions.
+ grants:
+ - # namespaces holds the list of namespaces this entry applies to. An empty list would match any.
+ # Wildcards `*` (Matching any number of any character) and `?` (Matches any single character) are supported in entries of this list.
+ namespaces: []
+ # authSubjects holds list of authentication subjects this entry applies to. An empty list would match any.
+ # Wildcards `*` (Matching any number of any character) and `?` (Matches any single character) are supported in entries of this list.
+ authSubjects: []
+ # revokes contains the list of creation config entries which would reject the creation of entities
+ revokes: []
+ # - namespaces: []
+ # authSubjects: []
+
+## ----------------------------------------------------------------------------
+## things configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-things.html
+things:
+ # enabled controls whether things related resources should be created
+ enabled: true
+ # replicaCount configuration for things
+ replicaCount: 1
+ # updateStrategy configuration for things
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # minReadySeconds configures the minimum number of seconds for which a newly created Pod should be ready without any
+ # of its containers crashing, for it to be considered available
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#min-ready-seconds
+ minReadySeconds: 10
+ # additionalLabels configuration for things
+ additionalLabels: {}
+ # additionalAnnotations configuration for things
+ additionalAnnotations: {}
+ image:
+ # repository for the things docker image
+ repository: docker.io/eclipse/ditto-things
+ # tag for the things docker image - overwrite to specify something else than Chart.AppVersion
+ # tag: 3.3.0
+ # pullPolicy for the things docker image
+ pullPolicy: IfNotPresent
+ # additionalJvmOptions JVM options to put into JAVA_TOOL_OPTIONS
+ additionalJvmOptions: ""
+ # systemProps used to define arbitrary system properties for things service
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#configuration
+ systemProps:
+ # extraEnv to add arbitrary environment variable to things container
+ extraEnv:
+ # - name: LOG_LEVEL_APPLICATION
+ # value: "DEBUG"
+ # resources configures the resources available/to use for the things service
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.5
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 1024
+ # jvm contains JVM specific scaling/tuning configuration of e.g. processors and garbage collector settings
+ jvm:
+ # activeProcessorCount defines how many processors the JVM should be configured to use
+ # this is e.g. relevant for the GC which calculates the amount of asynchronous threads for GC based on the processor count
+ activeProcessorCount: 2
+ # heapRamPercentage defines how much memory of the configured "resources.memoryMi" can be used by the JVM heap space
+ # be aware that the JVM also requires memory for "off heap" (and also stack) space + the container needs memory as well
+ heapRamPercentage: 60
+ # maxGcPauseMillis configures the used G1 GC "target for the maximum GC pause time"
+ # default (by JVM if not set): 200
+ maxGcPauseMillis: 150
+ # readinessProbe configuration for things
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ readinessProbe:
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 3
+ successThreshold: 1
+ failureThreshold: 3
+ # livenessProbe configuration for things
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ livenessProbe:
+ initialDelaySeconds: 160
+ periodSeconds: 10
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 4
+ # podDisruptionBudget configuration for things
+ # ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether things related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # nodeSelector configuration for things
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ # tolerations configuration for things
+ # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ tolerations: []
+ # affinity configuration for things
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ # podMonitor configuration for things
+ podMonitor:
+ # enabled configures whether Pod Monitor is enabled, then a resource to scrape things metrics will be created
+ enabled: false
+ # interval: 30s
+ # scrapeTimeout: 15s
+ # config holds things specific configuration
+ config:
+ # mongodb holds mongodb specific configuration of things
+ mongodb:
+ # minPoolSize configures the minimum number of connections in the connection pool
+ minPoolSize: 10
+ # maxPoolSize configures the minimum number of connections in the connection pool
+ maxPoolSize: 200
+ # maxPoolIdleTime configures the maximum amount of time a pooled connection is allowed to idle before closing the connection
+ maxPoolIdleTime: 10m
+ # journalWriteConcern the MongoDB write concern to apply for writing operations on the event journal
+ # one of: Unacknowledged | Acknowledged | Journaled | ReplicaAcknowledged
+ journalWriteConcern: "Acknowledged"
+ # snapsWriteConcern the MongoDB write concern to apply for writing operations on the snapshots persistence
+ # one of: Unacknowledged | Acknowledged | Journaled | ReplicaAcknowledged
+ snapsWriteConcern: "Acknowledged"
+ # journalCircuitBreaker configures the circuit breaker for MongoDB operations on the event journal
+ journalCircuitBreaker:
+ # maxTries opens the circuit breaker if an exception during persisting an event occurs this often
+ # a successful write resets the counter
+ maxTries: 10
+ # timeout configures the MongoDB write timeouts also causing the circuit breaker to open
+ timeout: 10s
+ # reset after this time in "Open" state, the circuit breaker is "Half-opened" again
+ reset: 5s
+ # snapsCircuitBreaker configures the circuit breaker for MongoDB operations on the snapshots persistence
+ snapsCircuitBreaker:
+ # maxTries opens the circuit breaker if an exception during persisting a snapshot occurs this often
+ # a successful write resets the counter
+ maxTries: 10
+ # timeout configures the MongoDB write timeouts also causing the circuit breaker to open
+ timeout: 20s
+ # reset after this time in "Open" state, the circuit breaker is "Half-opened" again
+ reset: 8s
+ # cleanup contains the configuration for the background cleanup of stale snapshots and events
+ cleanup:
+ # enabled configures whether background cleanup is enabled or not
+ # if enabled, stale "snapshot" and "journal" entries will be cleaned up from the MongoDB by a background process:
+ enabled: true
+ # quietPeriod defines how long to stay in a state where the background cleanup is not yet started
+ quietPeriod: 5m
+ # history contains configuration regarding the event history
+ history:
+ # retentionDuration configures the duration of how long to "keep" events and snapshots before being allowed to remove them in scope of cleanup
+ retentionDuration: 30d
+ # metricsReporter config of MongoMetricsReporter which is used by policies in order to report current persistence
+ # roundtrip times in order to determine credits to cleanup stale data (journal entries, snapshots)
+ metricsReporter:
+ # resolution configures how far apart each measurement should be done
+ resolution: 1s
+ # history configures how many historical items to keep
+ history: 5
+ # interval configures how often a "credit decision" is made
+ interval: 1s
+ # timerThreshold configures the maximum database latency to give out credit for cleanup actions
+ timerThreshold: 100ms
+ # creditsPerBatch configures how many "cleanup credits" should be generated per "interval" as long as the
+ creditsPerBatch: 5
+ # persistence holds configuration regarding (akka) persistence of things (event journal and snapshots)
+ persistence:
+ # activityCheckInterval configures to keep things for that amount of time in memory when no other use did happen
+ activityCheckInterval: 2d
+ # events contains event journal specific configuration
+ events:
+ # historicalHeadersToPersist define the DittoHeaders to persist when persisting events to the journal
+ # those can e.g. be retrieved as additional "audit log" information when accessing a historical Thing revision
+ historicalHeadersToPersist:
+ # - "ditto-originator"
+ # - "ditto-origin"
+ # - "correlation-id"
+ # snapshots contains snapshots persistence specific configuration
+ snapshots:
+ # the interval when to do snapshot for a Thing which had changes to it
+ interval: 15m
+ # the threshold after how many changes to a Thing to do a snapshot
+ threshold: 50
+ # entityCreation by default, Ditto allows anyone to create a new entity (thing in this case) in any namespace.
+ # However, this behavior can be customized, and the ability to create new entities can be restricted:
+ entityCreation:
+ # grants contains the list of creation config entries which would allow the creation of entities
+ # An empty list would *not* allow any entity to be created.
+ # You must have at least one entry, even if it is without restrictions.
+ grants:
+ - # namespaces holds the list of namespaces this entry applies to. An empty list would match any.
+ # Wildcards `*` (Matching any number of any character) and `?` (Matches any single character) are supported in entries of this list.
+ namespaces: []
+ # authSubjects holds list of authentication subjects this entry applies to. An empty list would match any.
+ # Wildcards `*` (Matching any number of any character) and `?` (Matches any single character) are supported in entries of this list.
+ authSubjects: []
+ # revokes contains the list of creation config entries which would reject the creation of entities
+ revokes: []
+ # - namespaces: []
+ # authSubjects: []
+ # policiesEnforcer contains configuration for Ditto "Policy Enforcers", e.g. regarding caching
+ policiesEnforcer:
+ # cache holds the configuration of policy enforcer caching
+ cache:
+ # enabled whether caching of policy enforcers should be enabled
+ enabled: true
+ # maxSize the maximum size of policy enforcers to keep in the cache
+ maxSize: 50000
+ # expireAfterWrite the maximum duration of inconsistency after losing a cache invalidation
+ expireAfterWrite: 8h
+ # expireAfterAccess prolonged on each cache access by that duration
+ expireAfterAccess: 4h
+ # wot contains Web of Things (WoT) specific configuration
+ wot:
+ # tdBasePrefix is the base to use where the Ditto endpoint is located in order to be injected into TDs:
+ tdBasePrefix: "http://localhost:8080"
+ # tdJsonTemplate contains a json template added to generated TDs, e.g. containing security information:
+ tdJsonTemplate: >-
+ {
+ "securityDefinitions": {
+ "basic_sc": {
+ "scheme": "basic",
+ "in": "header"
+ }
+ },
+ "security": "basic_sc",
+ "support": "https://www.eclipse.dev/ditto/"
+ }
+
+## ----------------------------------------------------------------------------
+## things-search configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-things-search.html
+thingsSearch:
+ # enabled controls whether things-search related resources should be created
+ enabled: true
+ # replicaCount configuration for things-search
+ replicaCount: 1
+ # updateStrategy configuration for things-search
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # minReadySeconds configures the minimum number of seconds for which a newly created Pod should be ready without any
+ # of its containers crashing, for it to be considered available
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#min-ready-seconds
+ minReadySeconds: 10
+ # additionalLabels configuration for things-search
+ additionalLabels: {}
+ # additionalAnnotations configuration for things-search
+ additionalAnnotations: {}
+ image:
+ # repository for the things-search docker image
+ repository: docker.io/eclipse/ditto-things-search
+ # tag for the things-search docker image - overwrite to specify something else than Chart.AppVersion
+ # tag: 3.3.0
+ # pullPolicy for the things-search docker image
+ pullPolicy: IfNotPresent
+ # additional JVM options to put into JAVA_TOOL_OPTIONS
+ additionalJvmOptions: ""
+ # systemProps used to define arbitrary system properties for things-search service
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#configuration
+ systemProps:
+ # extraEnv to add arbitrary environment variable to things-search container
+ extraEnv:
+ # - name: LOG_LEVEL_APPLICATION
+ # value: "DEBUG"
+ # resources configures the resources available/to use for the things search service
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.5
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 1024
+ # jvm contains JVM specific scaling/tuning configuration of e.g. processors and garbage collector settings
+ jvm:
+ # activeProcessorCount defines how many processors the JVM should be configured to use
+ # this is e.g. relevant for the GC which calculates the amount of asynchronous threads for GC based on the processor count
+ activeProcessorCount: 2
+ # heapRamPercentage defines how much memory of the configured "resources.memoryMi" can be used by the JVM heap space
+ # be aware that the JVM also requires memory for "off heap" (and also stack) space + the container needs memory as well
+ heapRamPercentage: 60
+ # maxGcPauseMillis configures the used G1 GC "target for the maximum GC pause time"
+ # default (by JVM if not set): 200
+ maxGcPauseMillis: 150
+ # readinessProbe configuration for things-search
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ readinessProbe:
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 3
+ successThreshold: 1
+ failureThreshold: 3
+ # livenessProbe configuration for things-search
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ livenessProbe:
+ initialDelaySeconds: 160
+ periodSeconds: 10
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 4
+ # podDisruptionBudget configuration for things-search
+ # ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether things-search related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # nodeSelector configuration for things-search
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ # tolerations configuration for things-search
+ # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ tolerations: []
+ # affinity configuration for things-search
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ # podMonitor configuration for things-search
+ podMonitor:
+ # enabled configures whether Pod Monitor is enabled, then a resource to scrape things search metrics will be created
+ enabled: false
+ # interval: 30s
+ # scrapeTimeout: 15s
+ # config holds things-search specific configuration
+ config:
+ # mongodb holds mongodb specific configuration of things-search
+ mongodb:
+ # minPoolSize configures the minimum number of connections in the connection pool
+ minPoolSize: 10
+ # maxPoolSize configures the minimum number of connections in the connection pool
+ maxPoolSize: 100
+ # maxPoolIdleTime configures the maximum amount of time a pooled connection is allowed to idle before closing the connection
+ maxPoolIdleTime: 10m
+ # searchReadPreference configures the overall MongoDB read preference
+ # one of: primary | primaryPreferred | secondary | secondaryPreferred | nearest
+ searchReadPreference: "primary"
+ # searchWriteConcern configures the overall MongoDB write concern
+ # one of: unacknowledged | acknowledged | majority | journaled | w1 | w2 | w3
+ searchWriteConcern: "acknowledged"
+ # searchWithAcksWriteConcern configures the MongoDB write concern for commands sent with "search-persisted" ACK
+ # ref: https://www.eclipse.dev/ditto/basic-acknowledgements.html#built-in-acknowledgement-labels
+ # one of: unacknowledged | acknowledged | majority | journaled | w1 | w2 | w3
+ searchWithAcksWriteConcern: "majority"
+ # queryReadConcern configures the MongoDB read concern for doing queries / performing searches
+ # only if this is "linearizable" in combination with the searchWithAcksWriteConcern: "majority" a strong consistency
+ # if used in a replicated MongoDB setup, this should be changed to `queryReadConcern: "linearizable"`
+ # for commands using the "search-persisted" requested ACK is guaranteed
+ # one of: default | local | majority | linearizable | snapshot | available
+ queryReadConcern: "local"
+ # updaterPersistenceReadConcern configures the MongoDB read concern for the "ThingUpdater"
+ # one of: default | local | majority | linearizable | snapshot | available
+ updaterPersistenceReadConcern: "local"
+ # updaterPersistenceReadPreference configures the MongoDB read preference for the "ThingUpdater"
+ updaterPersistenceReadPreference: "primaryPreferred"
+ # updater contains configuration for the "Things Updater" of things-search service
+ updater:
+ # activityCheckInterval configures to keep thing updaters for that amount of time in memory when no update did happen:
+ activityCheckInterval: 2h
+ # stream contains streaming configuration settings of the things-search service
+ stream:
+ # retrievalParallelism configures the upper bound of parallel SudoRetrieveThing commands
+ # (by extension, parallel loads of policy enforcer cache)
+ retrievalParallelism: 64
+ persistence:
+ # parallelism configures how much bulk writes to request in parallel - must be a power of 2
+ parallelism: 16
+ # policiesEnforcer contains configuration for Ditto "Policy Enforcers", e.g. regarding caching
+ policiesEnforcer:
+ # cache holds the configuration of policy enforcer caching
+ cache:
+ # maxSize the maximum size of policy enforcers to keep in the cache
+ maxSize: 30000
+ # expireAfterWrite the maximum duration of inconsistency after losing a cache invalidation
+ expireAfterWrite: 12h
+ # expireAfterAccess prolonged on each cache access by that duration
+ expireAfterAccess: 6h
+ # thingCache configures the cache configuration for caching of things in things-search
+ thingCache:
+ # maxSize defines how many things to cache
+ maxSize: 30000
+ # expireAfterWrite defines how long at most to keep things in the cache after loading them into the cache
+ expireAfterWrite: 12h
+ # expireAfterWrite defines how long at most to keep things in the cache after last accessing them from the cache
+ expireAfterAccess: 6h
+ # backgroundSync contains the configuration for the "background sync" responsible for continuously streaming
+ # over snapshot entries of things to ensure the eventual consistency of the search index
+ backgroundSync:
+ # enabled whether background sync is turned on
+ enabled: true
+ # quietPeriod the duration between service start-up and the beginning of background sync
+ quietPeriod: 5m
+ # idleTimeout how soon to close the remote stream if no element passed through it
+ idleTimeout: 5m
+ # toleranceWindow how long to wait before reacting to out-of-date search index entries
+ toleranceWindow: 20m
+ # keepEvents how many events to keep in the actor state
+ keepEvents: 2
+ # throttle contains the background sync throttling configuration
+ throttle:
+ # throughput how many things to update per throttle period
+ throughput: 100
+ # period the throttle period
+ period: 30s
+
+
+## ----------------------------------------------------------------------------
+## connectivity configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-connectivity.html
+connectivity:
+ # enabled controls whether connectivity related resources should be created
+ enabled: true
+ # replicaCount configuration for connectivity
+ replicaCount: 1
+ # updateStrategy configuration for connectivity
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # minReadySeconds configures the minimum number of seconds for which a newly created Pod should be ready without any
+ # of its containers crashing, for it to be considered available
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#min-ready-seconds
+ minReadySeconds: 10
+ # additionalLabels configuration for connectivity
+ additionalLabels: {}
+ # additionalAnnotations configuration for connectivity
+ additionalAnnotations: {}
+ image:
+ # repository for the connectivity docker image
+ repository: docker.io/eclipse/ditto-connectivity
+ # tag for the connectivity docker image - overwrite to specify something else than Chart.AppVersion
+ # tag: 3.3.0
+ # pullPolicy for the connectivity docker image
+ pullPolicy: IfNotPresent
+ # additional JVM options to put into JAVA_TOOL_OPTIONS
+ additionalJvmOptions: ""
+ # systemProps used to define arbitrary system properties for connectivity service
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#configuration
+ systemProps:
+ # extraEnv to add arbitrary environment variable to connectivity container
+ extraEnv:
+ # - name: LOG_LEVEL_APPLICATION
+ # value: "DEBUG"
+ # resources configures the resources available/to use for the connectivity service
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.5
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 1024
+ # jvm contains JVM specific scaling/tuning configuration of e.g. processors and garbage collector settings
+ jvm:
+ # activeProcessorCount defines how many processors the JVM should be configured to use
+ # this is e.g. relevant for the GC which calculates the amount of asynchronous threads for GC based on the processor count
+ activeProcessorCount: 2
+ # heapRamPercentage defines how much memory of the configured "resources.memoryMi" can be used by the JVM heap space
+ # be aware that the JVM also requires memory for "off heap" (and also stack) space + the container needs memory as well
+ heapRamPercentage: 60
+ # maxGcPauseMillis configures the used G1 GC "target for the maximum GC pause time"
+ # default (by JVM if not set): 200
+ maxGcPauseMillis: 150
+ # readinessProbe configuration for connectivity
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ readinessProbe:
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 3
+ successThreshold: 1
+ failureThreshold: 3
+ # livenessProbe configuration for connectivity
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ livenessProbe:
+ initialDelaySeconds: 160
+ periodSeconds: 10
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 4
+ # podDisruptionBudget configuration for connectivity
+ # ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether connectivity related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # nodeSelector configuration for connectivity
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ # tolerations configuration for connectivity
+ # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ tolerations: []
+ # affinity configuration for connectivity
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ # podMonitor configuration for connectivity
+ podMonitor:
+ # enabled configures whether Pod Monitor is enabled, then a resource to scrape connectivity metrics will be created
+ enabled: false
+ # interval: 30s
+ # scrapeTimeout: 15s
+ # config holds connectivity specific configuration
+ config:
+ # mongodb holds mongodb specific configuration of connectivity
+ mongodb:
+ # minPoolSize configures the minimum number of connections in the connection pool
+ minPoolSize: 10
+ # maxPoolSize configures the minimum number of connections in the connection pool
+ maxPoolSize: 50
+ # maxPoolIdleTime configures the maximum amount of time a pooled connection is allowed to idle before closing the connection
+ maxPoolIdleTime: 10m
+ # journalWriteConcern the MongoDB write concern to apply for writing operations on the event journal
+ # one of: Unacknowledged | Acknowledged | Journaled | ReplicaAcknowledged
+ journalWriteConcern: "Journaled"
+ # snapsWriteConcern the MongoDB write concern to apply for writing operations on the snapshots persistence
+ # one of: Unacknowledged | Acknowledged | Journaled | ReplicaAcknowledged
+ snapsWriteConcern: "Journaled"
+ # journalCircuitBreaker configures the circuit breaker for MongoDB operations on the event journal
+ journalCircuitBreaker:
+ # maxTries opens the circuit breaker if an exception during persisting an event occurs this often
+ # a successful write resets the counter
+ maxTries: 10
+ # timeout configures the MongoDB write timeouts also causing the circuit breaker to open
+ timeout: 10s
+ # reset after this time in "Open" state, the circuit breaker is "Half-opened" again
+ reset: 5s
+ # snapsCircuitBreaker configures the circuit breaker for MongoDB operations on the snapshots persistence
+ snapsCircuitBreaker:
+ # maxTries opens the circuit breaker if an exception during persisting a snapshot occurs this often
+ # a successful write resets the counter
+ maxTries: 10
+ # timeout configures the MongoDB write timeouts also causing the circuit breaker to open
+ timeout: 20s
+ # reset after this time in "Open" state, the circuit breaker is "Half-opened" again
+ reset: 8s
+ # policiesEnforcer contains configuration for Ditto "Policy Enforcers", e.g. regarding caching
+ policiesEnforcer:
+ # cache holds the configuration of policy enforcer caching
+ cache:
+ # enabled whether caching of policy enforcers should be enabled
+ enabled: true
+ # maxSize the maximum size of policy enforcers to keep in the cache
+ maxSize: 1000
+ # expireAfterWrite the maximum duration of inconsistency after losing a cache invalidation
+ expireAfterWrite: 8h
+ # expireAfterAccess prolonged on each cache access by that duration
+ expireAfterAccess: 4h
+ # cleanup contains the configuration for the background cleanup of stale snapshots and events
+ cleanup:
+ # enabled configures whether background cleanup is enabled or not
+ # if enabled, stale "snapshot" and "journal" entries will be cleaned up from the MongoDB by a background process:
+ enabled: false
+ # quietPeriod defines how long to stay in a state where the background cleanup is not yet started
+ quietPeriod: 5m
+ # history contains configuration regarding the event history
+ history:
+ # retentionDuration configures the duration of how long to "keep" events and snapshots before being allowed to remove them in scope of cleanup
+ retentionDuration: 30d
+ # metricsReporter config of MongoMetricsReporter which is used by policies in order to report current persistence
+ # roundtrip times in order to determine credits to cleanup stale data (journal entries, snapshots)
+ metricsReporter:
+ # resolution configures how far apart each measurement should be done
+ resolution: 1s
+ # history configures how many historical items to keep
+ history: 5
+ # interval configures how often a "credit decision" is made
+ interval: 10s
+ # timerThreshold configures the maximum database latency to give out credit for cleanup actions
+ timerThreshold: 100ms
+ # creditsPerBatch configures how many "cleanup credits" should be generated per "interval" as long as the
+ creditsPerBatch: 5
+ # persistence holds configuration regarding (akka) persistence of connections (event journal and snapshots)
+ persistence:
+ # keep closed, inactive connections for that amount of time in memory when no other use did happen:
+ activityCheckInterval: 45m
+ # events contains event journal specific configuration
+ events:
+ # historicalHeadersToPersist define the DittoHeaders to persist when persisting events to the journal
+ # those can e.g. be retrieved as additional "audit log" information when accessing a historical Connection revision
+ historicalHeadersToPersist:
+ # - "ditto-originator"
+ # - "ditto-origin"
+ # - "correlation-id"
+ # snapshots contains snapshots persistence specific configuration
+ snapshots:
+ # interval the interval when to do snapshot for a Connection which had changes to it
+ interval: 15m
+ # threshold the threshold after how many changes to a Connection to do a snapshot
+ threshold: 5
+ # connections holds configuration regarding connections
+ connections:
+ # reconnect configures pinging of connections, so that not all connections are recovered at the same time
+ reconnect:
+ # rate configures the rate in which frequency to ping/wake up how many entities (connections)
+ rate:
+ # frequency the frequency of how often to wake up connections after restart
+ frequency: 1s
+ # entities the amount of entities to wake up per "frequency" interval
+ entities: 10
+ # allowedHostnames contains a comma separated list of explicitly allowed hostnames
+ allowedHostnames: ""
+ # blockedHostnames contains a comma separated list of blocked hostnames
+ blockedHostnames: ""
+ # blockedSubnets holds a comma separated string of blocked subnets
+ # specify subnets to block in CIDR format e.g. "11.1.0.0/16"
+ blockedSubnets: ""
+ # blockedHostRegex contains the regex for blocked hostnames
+ blockedHostRegex: ""
+ limits:
+ # maxSources contains the max number of sources per connection
+ maxSources: 5
+ # maxTargets contains the max number of targets per connection
+ maxTargets: 5
+ enrichment:
+ # the buffer size used for the queue in the message mapping processor actor
+ bufferSize: 200
+ # kafka contains the configuration specific to Ditto connections to Apache Kafka
+ kafka:
+ # consumer contains configuration for consuming messages from Kafka
+ consumer:
+ # throttling contains configuration for applying throttling when consuming from a single Kafka connection
+ throttling:
+ # enabled defines whether throttling should be applied when consuming messages from a Kafka source
+ enabled: true
+ # interval the interval at which the consumer is throttled - must be > 0s
+ interval: 1s
+ # limit defines the maximum number of messages the consumer is allowed to receive within the configured
+ # throttling "interval" e.g. 100 msgs/s - must be > 0
+ limit: 500
+ # maxInflightFactor configures how many unacknowledged messages are allowed at any time as factor of
+ # ${limit} - must be >= 1.0
+ # This limit couples latency with throughput (long latency before ack -> lower throughput)
+ maxInflightFactor: 2.0
+ # producer contains configuration for publishing messages to Kafka
+ producer:
+ # If a message can't be published it is put in a queue. Further messages are dropped when the queue is full.
+ queueSize: 1000
+ # Messages to publish in parallel per Kafka-Publisher (one per connectivity client)
+ parallelism: 10
+
+## ----------------------------------------------------------------------------
+## gateway configuration
+## ref: https://www.eclipse.dev/ditto/architecture-services-gateway.html
+gateway:
+ # enabled controls whether gateway related resources should be created
+ enabled: true
+ # replicaCount configuration for gateway
+ replicaCount: 1
+ # updateStrategy configuration for gateway
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # minReadySeconds configures the minimum number of seconds for which a newly created Pod should be ready without any
+ # of its containers crashing, for it to be considered available
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#min-ready-seconds
+ minReadySeconds: 10
+ # additionalLabels configuration for gateway
+ additionalLabels: {}
+ # additionalAnnotations configuration for gateway
+ additionalAnnotations: {}
+ # additional JVM options to put into JAVA_TOOL_OPTIONS
+ additionalJvmOptions: ""
+ image:
+ # repository for the gateway docker image
+ repository: docker.io/eclipse/ditto-gateway
+ # tag for the gateway docker image - overwrite to specify something else than Chart.AppVersion
+ # tag: 3.3.0
+ # pullPolicy for the gateway docker image
+ pullPolicy: IfNotPresent
+ # systemProps used to define arbitrary system properties configuration for gateway
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#configuration
+ systemProps:
+ - "-Dditto.protocol.blocklist.0=raw-request-uri"
+ - "-Dditto.protocol.blocklist.1=cache-control"
+ - "-Dditto.protocol.blocklist.2=connection"
+ - "-Dditto.protocol.blocklist.3=timeout-access"
+ - "-Dditto.protocol.blocklist.4=accept-encoding"
+ - "-Dditto.protocol.blocklist.5=x-forwarded-scheme"
+ - "-Dditto.protocol.blocklist.6=x-forwarded-port"
+ - "-Dditto.protocol.blocklist.7=x-forwarded-for"
+ - "-Dditto.protocol.blocklist.8=forwarded=for"
+ - "-Dditto.protocol.blocklist.9=sec-fetch-mode"
+ - "-Dditto.protocol.blocklist.10=sec-fetch-site"
+ - "-Dditto.protocol.blocklist.11=authorization"
+ - "-Dditto.protocol.blocklist.12=accept-language"
+ - "-Dditto.protocol.blocklist.13=host"
+ - "-Dditto.protocol.blocklist.14=via"
+ - "-Dditto.protocol.blocklist.15=sec-ch-ua"
+ - "-Dditto.protocol.blocklist.16=sec-ch-ua-mobile"
+ - "-Dditto.protocol.blocklist.17=sec-ch-ua-platform"
+ - "-Dditto.protocol.blocklist.18=sec-fetch-dest"
+ - "-Dditto.protocol.blocklist.19=user-agent"
+ # extraEnv to add arbitrary environment variables to gateway container
+ extraEnv:
+ # - name: LOG_LEVEL_APPLICATION
+ # value: "DEBUG"
+ # resources configures the resources available/to use for the gateway service
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.5
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 1024
+ # jvm contains JVM specific scaling/tuning configuration of e.g. processors and garbage collector settings
+ jvm:
+ # activeProcessorCount defines how many processors the JVM should be configured to use
+ # this is e.g. relevant for the GC which calculates the amount of asynchronous threads for GC based on the processor count
+ activeProcessorCount: 2
+ # heapRamPercentage defines how much memory of the configured "resources.memoryMi" can be used by the JVM heap space
+ # be aware that the JVM also requires memory for "off heap" (and also stack) space + the container needs memory as well
+ heapRamPercentage: 60
+ # maxGcPauseMillis configures the used G1 GC "target for the maximum GC pause time"
+ # default (by JVM if not set): 200
+ maxGcPauseMillis: 150
+ # readinessProbe configuration for gateway
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ readinessProbe:
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 3
+ successThreshold: 1
+ failureThreshold: 3
+ # livenessProbe configuration for gateway
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ livenessProbe:
+ initialDelaySeconds: 160
+ periodSeconds: 10
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 4
+ # service configuration of the k8s service of the gateway
+ service:
+ # port number configuration for gateway
+ port: 8080
+ # annotations to add arbitrary annotations to nginx service
+ annotations: {}
+ # podDisruptionBudget configuration for gateway
+ # ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether gateway related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # nodeSelector configuration for gateway
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ # tolerations configuration for gateway
+ # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ tolerations: []
+ # affinity configuration for gateway
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ # podMonitor configuration for gateway
+ podMonitor:
+ # enabled configures whether Pod Monitor is enabled, then a resource to scrape gateway metrics will be created
+ enabled: false
+ # interval: 30s
+ # scrapeTimeout: 15s
+ # config holds gateway specific configuration
+ config:
+ # authentication contains the settings regarding authentication against the gateway
+ authentication:
+ # enablePreAuthentication whether Ditto "pre-authentication" should be enabled
+ # ref: https://www.eclipse.dev/ditto/installation-operating.html#pre-authentication
+ enablePreAuthentication: false
+ # oauth contains the OAuth2.0 / OpenID Connect related configuration
+ oauth:
+ # allowedClockSkew configures the amount of clock skew in seconds to tolerate when verifying the local time against the exp and nbf claims
+ allowedClockSkew: 20s
+ # openidConnectIssuers holds a map of issuer-prefixes as key (e.g. "example")
+ # and OAuth "issuer" and "authSubjects" list containing which claims to extract from a JWT issued by the issuer
+ openidConnectIssuers:
+ # example:
+ # issuer: "example.com"
+ # authSubjects:
+ # - "{{ jwt:sub }}"
+ # - "{{ jwt:groups }}"
+ # devops contains the configuration of the gateway's "/devops" API, e.g. access to it
+ devops:
+ # secured this controls whether "/devops" and "/api/2/connections" resources are secured or not
+ secured: true
+ # authMethod declares the authentication method to apply for authenticating the "/devops" and "/api/2/connections" resources
+ # one of: "basic" | "oauth2"
+ authMethod: "basic"
+ # oauth contains the OAuth2.0 / OpenID Connect related configuration applied when "authMethod" above is "oauth2"
+ oauth:
+ # allowedClockSkew configures the amount of clock skew in seconds to tolerate when verifying the local time against the exp and nbf claims
+ allowedClockSkew: 20s
+ # openidConnectIssuers holds a map of issuer-prefixes as key (e.g. "example")
+ # and OAuth "issuer" and "authSubjects" list containing which claims to extract from a JWT issued by the issuer
+ openidConnectIssuers:
+ # example-ops:
+ # issuer: "example.com"
+ # authSubjects:
+ # - "{{ jwt:sub }}"
+ # - "{{ jwt:groups }}"
+ # oauthSubjects contains list of subjects authorized to use "/devops" and "/api/2/connections" resources
+ oauthSubjects:
+ # - "example-ops:devops-admin"
+ # statusSecured controls whether the "/status" and "/status/health" resources are secured or not
+ statusSecured: true
+ # statusAuthMethod declares the authentication method to apply for authenticating the "/status" and "/status/health" resources
+ # one of: "basic" | "oauth2"
+ statusAuthMethod: "basic"
+ # statusOauthSubjects contains list of subjects authorized to use "/status" API
+ statusOauthSubjects:
+ # - "example-ops:devops-admin"
+ # existingSecret contains the name of existing secret containing status and devops passwords
+ # if not set then default secret is created - useful for managing secrets with external secrets manager
+ existingSecret:
+ # devopsPassword the password to use for accessing "/devops" and "/api/2/connections" resources
+ # when "authMethod": "basic" (with username: devops)
+ # if not set a random password will be generated and used
+ devopsPassword:
+ # statusPassword will be used for accessing "/status" and "/status/health" resources
+ # when "statusAuthMethod": "basic" (with username: devops)
+ # if not set a random password will be set
+ statusPassword:
+ # websocket contains the gateway websocket configuration
+ websocket:
+ # subscriber contains the configuration for receiving data via the websocket
+ subscriber:
+ # backpressureQueueSize is the max queue size of how many inflight commands a single websocket client can have
+ backpressureQueueSize: 100
+ # publisher contains the configuration for sending/publishing data via the websocket
+ publisher:
+ # backpressureBufferSize is the max buffer size of how many outstanding CommandResponses and Events a single
+ # websocket client can have - additional CommandResponses and Events are dropped if this size is reached
+ backpressureBufferSize: 200
+ # throttling contains the throttling configuration of a single websocket session
+ throttling:
+ # enabled whether throttling message consumption via a single websocket session is enabled
+ enabled: true
+ # interval is the interval at which a single websocket session is rate-limited - must be > 0s
+ interval: 1s
+ # limit is the maximum number of messages the websocket session is allowed to receive within the configured
+ # throttling interval e.g. 100 msgs/s
+ limit: 100
+ # websocket contains the gateway SSE (server sent events) configuration
+ sse:
+ # throttling contains the throttling configuration of a single SSE session (only applies for search via SSE)
+ throttling:
+ # enabled whether throttling message publishing via a single websocket session is enabled
+ enabled: true
+ # interval is the interval at which a single SSE session is rate-limited - must be > 0s
+ interval: 1s
+ # limit is the maximum number of messages the SSE session is allowed to receive within the configured
+ # throttling interval e.g. 100 msgs/s
+ limit: 100
+
+## ----------------------------------------------------------------------------
+## nginx configuration
+nginx:
+ # enabled controls whether nginx related resources should be created
+ enabled: true
+ # replicaCount for nginx
+ replicaCount: 1
+ # updateStrategy for nginx
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # additionalLabels on nginx pods
+ additionalLabels: {}
+ # additionalAnnotations on nginx pods
+ additionalAnnotations: {}
+ image:
+ # repository for the nginx docker image
+ repository: docker.io/nginx
+ # tag for the nginx docker image
+ tag: 1.25
+ # pullPolicy for the nginx docker image
+ pullPolicy: IfNotPresent
+ # extraEnv to add arbitrary environment variables to nginx container
+ extraEnv: []
+ # resources configures the resources available/to use for nginx
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.2
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 64
+ # readinessProbe configuration for nginx
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ readinessProbe: {}
+ # livenessProbe configuration for nginx
+ # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes
+ livenessProbe: {}
+ # service configuration of the k8s service of the nginx
+ service:
+ # type of the nginx service
+ type: ClusterIP
+ # port of the nginx service
+ port: 8080
+ # in case of NodePort the may additionally be set
+ # type: NodePort
+ # nodePort: 30080
+ # annotations to add arbitrary annotations to nginx service
+ annotations: {}
+ # nodeSelector configuration for nginx
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ nodeSelector: {}
+ # tolerations configuration for nginx
+ # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ tolerations: []
+ # affinity configuration for nginx
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ affinity: {}
+ # init containers for nginx
+ initContainers:
+ waitForGateway:
+ enabled: true
+ name: wait-for-gateway
+ image: rancher/curlimages-curl:7.73.0
+ # config holds nginx specific configuration
+ config:
+ # workerProcesses the 'worker_processes' option for nginx to use - can also be set to 'auto' in order to let nginx
+ # determine the worker processes based on the CPU count
+ workerProcesses: 4
+ # workerProcesses the 'events' 'worker_connections' option for nginx to use
+ workerConnections: 1024
+
+## ----------------------------------------------------------------------------
+## Ditto UI configuration
+dittoui:
+ enabled: true
+ # replicaCount for Ditto UI service
+ replicaCount: 1
+ # updateStrategy for Ditto UI service
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # additionalLabels on Ditto UI pods
+ additionalLabels: {}
+ # additionalAnnotations on Ditto UI pods
+ additionalAnnotations: {}
+ image:
+ # repository for the Ditto UI docker image
+ repository: docker.io/eclipse/ditto-ui
+ # tag for the Ditto UI image - overwrite to specify something else than Chart.AppVersion
+ # tag: 3.3.0
+ # pullPolicy for the Ditto UI docker image
+ pullPolicy: IfNotPresent
+ # extraEnv to add arbitrary environment variable to Ditto UI container
+ extraEnv: []
+ # resources configures the resources available/to use for the Ditto UI container
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.1
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 64
+ # podDisruptionBudget ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether Ditto UI related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # service configuration of the k8s service of the Ditto UI
+ service:
+ # port of the Ditto UI service
+ port: 8080
+ # annotations to add arbitrary annotations to Ditto UI service
+ annotations: {}
+
+## ----------------------------------------------------------------------------
+## swaggerui configuration
+swaggerui:
+ # enabled controls whether swagger ui related resources should be created
+ enabled: true
+ # replicaCount for swagger ui service
+ replicaCount: 1
+ # updateStrategy for swagger ui service
+ # ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ # additionalLabels on swagger ui pods
+ additionalLabels: {}
+ # additionalAnnotations on swagger ui pods
+ additionalAnnotations: {}
+ image:
+ # repository for the swagger ui docker image
+ repository: docker.io/swaggerapi/swagger-ui
+ # tag for the swagger ui docker image
+ tag: v4.19.1
+ # pullPolicy for the swagger ui docker image
+ pullPolicy: IfNotPresent
+ # extraEnv to add arbitrary environment variable to swagger ui container
+ extraEnv: []
+ # resources configures the resources available/to use for the swagger ui container
+ resources:
+ # cpu defines the "required" CPU of a node so that the service is placed there
+ cpu: 0.1
+ # memoryMi defines the memory in mebibyte (MiB) used as "required" and "limit" in k8s
+ memoryMi: 64
+ # podDisruptionBudget ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/
+ podDisruptionBudget:
+ # enabled controls whether swagger ui related PodDisruptionBudget should be created
+ enabled: true
+ # minAvailable number of replicas during voluntary disruptions
+ minAvailable: 1
+ # service configuration of the k8s service of the swagger ui
+ service:
+ # port of the swagger ui service
+ port: 8080
+ # annotations to add arbitrary annotations to swagger ui service
+ annotations: {}
+
+## ----------------------------------------------------------------------------
+## mongodb dependency chart configuration
+mongodb:
+ # enabled controls whether mongodb should be started as part of the Helm chart or not
+ enabled: true
+ # fullnameOverride: ditto-mongodb
+ auth:
+ enabled: false
+ securityContext:
+ enabled: false
+ persistence:
+ enabled: false
diff --git a/deployment/kubernetes/README.md b/deployment/kubernetes/README.md
index 4f34d10311f..351e404874f 100644
--- a/deployment/kubernetes/README.md
+++ b/deployment/kubernetes/README.md
@@ -53,7 +53,7 @@ In case you already have a MongoDB in the cloud or elsewhere it is possible to c
This can be done by setting the MongoDB URI via env variable "MONGO_DB_URI" in the
`deployment/kubernetes/deploymentFiles/ditto/ditto-cluster.yml` for all services except the `gateway`.
Other MongoDB settings can be set via env variables and are documented in
-[Operating Ditto](https://www.eclipse.org/ditto/installation-operating.html) section.
+[Operating Ditto](https://www.eclipse.dev/ditto/installation-operating.html) section.
In case your "MONGO_DB_URI" contains sensitive information like username and password it is recommended to use
a kubernetes secret.
diff --git a/deployment/openshift/nginx/index.html b/deployment/openshift/nginx/index.html
index d836b023218..d2c8839941d 100644
--- a/deployment/openshift/nginx/index.html
+++ b/deployment/openshift/nginx/index.html
@@ -41,7 +41,7 @@
You have started Eclipse Ditto
Thank you for trying out Eclipse Ditto!
-
In order to get started quickly, you can now have a look at the documentation
+
In order to get started quickly, you can now have a look at the documentation
To authenticate at the HTTP APIs use the following username "ditto" and password "ditto" when asked for by your browser.
diff --git a/deployment/openshift/nginx/nginx.conf b/deployment/openshift/nginx/nginx.conf
index c33bc6b3a8b..8bd6cc45b01 100644
--- a/deployment/openshift/nginx/nginx.conf
+++ b/deployment/openshift/nginx/nginx.conf
@@ -1,6 +1,8 @@
-worker_processes 1;
+worker_processes auto;
-events {worker_connections 1024;}
+events {
+ worker_connections 1024;
+}
http {
charset utf-8;
diff --git a/deployment/operations/grafana-dashboards/Akka.json b/deployment/operations/grafana-dashboards/Akka.json
index 75d5b19244b..32d6e089eda 100644
--- a/deployment/operations/grafana-dashboards/Akka.json
+++ b/deployment/operations/grafana-dashboards/Akka.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -29,10 +26,7 @@
"liveNow": false,
"panels": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -130,10 +124,7 @@
"text": "None",
"value": ""
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(actor_mailbox_size, service)",
"hide": 0,
"includeAll": false,
@@ -159,10 +150,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(actor_mailbox_size{service=~\"$service\"}, instance)",
"hide": 0,
"includeAll": true,
diff --git a/deployment/operations/grafana-dashboards/Akka_Dispatcher_Metrics.json b/deployment/operations/grafana-dashboards/Akka_Dispatcher_Metrics.json
index f2dff2f4b2e..9b36113fb79 100644
--- a/deployment/operations/grafana-dashboards/Akka_Dispatcher_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Akka_Dispatcher_Metrics.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "grafana",
- "uid": "-- Grafana --"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -34,10 +31,7 @@
{
"collapse": false,
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -52,10 +46,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Tracks the number of threads in use.",
"fieldConfig": {
"defaults": {
@@ -131,10 +122,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "rate(executor_threads_total_sum{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])\n/\nrate(executor_threads_total_count{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])",
"format": "time_series",
@@ -144,10 +132,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "rate(executor_threads_active_sum{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])\n/\nrate(executor_threads_active_count{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])",
"format": "time_series",
@@ -166,10 +151,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -211,10 +193,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "rate(executor_tasks_submitted_total{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])",
"format": "time_series",
@@ -225,10 +204,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "rate(executor_tasks_completed_total{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])",
"format": "time_series",
@@ -239,10 +215,7 @@
"refId": "B"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "max_over_time( ( rate( executor_tasks_completed_total{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval] ) )[$__interval:30s] )",
"format": "time_series",
@@ -284,10 +257,7 @@
}
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -366,10 +336,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"expr": "rate(executor_queue_size_sum{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])\n/\nrate(executor_queue_size_count{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])",
"format": "time_series",
"hide": false,
@@ -378,10 +345,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "histogram_quantile(0.95, sum(rate(executor_queue_size_bucket{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\",le=\"+Inf\"}[$Interval])) by (job,instance,name,le))",
"format": "time_series",
@@ -396,10 +360,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Tracks minimum/maximum number of Threads of the executors.",
"fieldConfig": {
"defaults": {
@@ -479,10 +440,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "executor_threads_min{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}",
"format": "time_series",
@@ -492,10 +450,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "executor_threads_max{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}",
"format": "time_series",
@@ -514,10 +469,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Tracks executor parallelism.",
"fill": 1,
"fillGradient": 0,
@@ -560,10 +512,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "executor_parallelism{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}",
"format": "time_series",
@@ -604,10 +553,7 @@
}
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Tracks the time that tasks spend on the executor service's queue",
"fieldConfig": {
"defaults": {
@@ -687,10 +633,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "rate(executor_time_in_queue_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])\n/\nrate(executor_time_in_queue_seconds_count{job=~\"$Application\",instance=~\"$Instance\",name=~\"$Dispatcher\",type=~\"$Type\"}[$Interval])",
"format": "time_series",
@@ -723,10 +666,7 @@
"$__all"
]
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values({__name__=~\"jvm.*\"}, job)",
"hide": 0,
"includeAll": true,
@@ -757,10 +697,7 @@
"$__all"
]
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(executor_threads_min{job=~\"$Application\"},instance)",
"hide": 0,
"includeAll": true,
@@ -791,10 +728,7 @@
"$__all"
]
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(executor_threads_min{job=~\"$Application\",instance=~\"$Instance\"},name)",
"hide": 0,
"includeAll": true,
@@ -880,10 +814,7 @@
"$__all"
]
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(executor_threads_min{job=~\"$Application\",instance=~\"$Instance\"},type)",
"hide": 0,
"includeAll": true,
diff --git a/deployment/operations/grafana-dashboards/Cache_Metrics.json b/deployment/operations/grafana-dashboards/Cache_Metrics.json
index 4cae8324023..5eb25da45df 100644
--- a/deployment/operations/grafana-dashboards/Cache_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Cache_Metrics.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Cluster_traffic.json b/deployment/operations/grafana-dashboards/Cluster_traffic.json
index a837a2ad0b1..8723f3e16b0 100644
--- a/deployment/operations/grafana-dashboards/Cluster_traffic.json
+++ b/deployment/operations/grafana-dashboards/Cluster_traffic.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -680,10 +680,7 @@
},
{
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -696,10 +693,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -779,10 +773,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "sum(irate(enforcement_seconds_count{segment=\"overall\",channel!=\"live\"}[5m]) > 0) by (category, resource, outcome)",
"format": "time_series",
@@ -792,10 +783,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "sum(irate(enforcement_seconds_count{segment=\"overall\",channel=\"live\"}[5m]) > 0) by (category, resource, outcome)",
"format": "time_series",
@@ -810,10 +798,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -890,10 +875,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "avg((idelta(enforcement_seconds_sum{segment=\"overall\",channel!=\"live\"}[5m]) / idelta(enforcement_seconds_count{segment=\"overall\",channel!=\"live\"}[5m])) > 0) by (category, resource, outcome)",
"format": "time_series",
@@ -903,10 +885,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "avg((idelta(enforcement_seconds_sum{segment=\"overall\",channel=\"live\"}[5m]) / idelta(enforcement_seconds_count{segment=\"overall\",channel=\"live\"}[5m])) > 0) by (category, resource, outcome)",
"format": "time_series",
diff --git a/deployment/operations/grafana-dashboards/Connectivity_ACKS.json b/deployment/operations/grafana-dashboards/Connectivity_ACKS.json
index b47c75f32a2..8ac2860b141 100644
--- a/deployment/operations/grafana-dashboards/Connectivity_ACKS.json
+++ b/deployment/operations/grafana-dashboards/Connectivity_ACKS.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Connectivity_Metrics.json b/deployment/operations/grafana-dashboards/Connectivity_Metrics.json
index 33822617a26..2a769a70426 100644
--- a/deployment/operations/grafana-dashboards/Connectivity_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Connectivity_Metrics.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -30,10 +27,7 @@
"panels": [
{
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -50,10 +44,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -102,10 +93,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"expr": "sum(connection_client{instance=~\"$Host\", type=~\"$ConnectionType\", id=~\"$Connection\"}) by (type)",
"format": "time_series",
"instant": false,
@@ -149,10 +137,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -201,10 +186,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"expr": "sum(connection_client{instance=~\"$Host\", type=~\"$ConnectionType\", id=~\"$Connection\"}) by (instance, type)",
"format": "time_series",
"instant": false,
@@ -248,10 +230,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Connections which delivered metrics in the last 7 days",
"fieldConfig": {
"defaults": {
@@ -301,10 +280,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"expr": "count by (ditto_connection_type) (count by (instance, ditto_connection_id, ditto_connection_type) (increase(connectivity_message_mapping_seconds_count{instance=~\"$Host\", ditto_connection_type=~\"$ConnectionType\", ditto_connection_id=~\"$Connection\"}[5m]) > 0))",
"format": "time_series",
"hide": false,
@@ -349,10 +325,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Connections which delivered metrics in the last 7 days",
"fieldConfig": {
"defaults": {
@@ -402,10 +375,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"expr": "count by (instance, ditto_connection_type) (count by (instance, ditto_connection_id, ditto_connection_type) (increase (connectivity_message_mapping_seconds_count{instance=~\"$Host\", ditto_connection_type=~\"$ConnectionType\", ditto_connection_id=~\"$Connection\"}[5m]) > 0))",
"format": "time_series",
"hide": false,
@@ -447,10 +417,7 @@
},
{
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -467,10 +434,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -566,10 +530,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "This number reflects the number of messages that resulted out of payload mappings per second.",
"fieldConfig": {
"defaults": {
@@ -666,10 +627,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -766,10 +724,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -871,10 +826,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -1006,10 +958,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -1056,10 +1005,7 @@
"steppedLine": false,
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": true,
"expr": "count(rate(connection_messages_total{instance=~\"$Host\", id=~\"$Connection\", type=~\"$ConnectionType\", category=\"throttled\", direction=\"inbound\"}[5m])>0) by (type, id)",
@@ -1071,10 +1017,7 @@
"refId": "B"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"exemplar": true,
"expr": "count(rate(connection_messages_total{category=\"throttled\", direction=\"inbound\"}[5m]) > 0) by (id)",
"hide": true,
@@ -1131,10 +1074,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -1228,10 +1168,7 @@
},
{
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -1248,10 +1185,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -1348,10 +1282,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -1447,10 +1378,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "This number reflects the number of messages that resulted out of payload mappings per second.",
"fieldConfig": {
"defaults": {
@@ -1547,10 +1475,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -1646,10 +1571,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -1745,10 +1667,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": [],
@@ -1844,10 +1763,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -1962,10 +1878,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -2074,10 +1987,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "",
"hide": 0,
"includeAll": true,
@@ -2108,10 +2018,7 @@
"$__all"
]
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(connectivity_message_mapping_seconds_count,ditto_connection_id)",
"hide": 0,
"includeAll": true,
@@ -2138,10 +2045,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(connectivity_message_mapping_seconds_count,ditto_connection_type)",
"hide": 0,
"includeAll": true,
diff --git a/deployment/operations/grafana-dashboards/Connectivity_live_status.json b/deployment/operations/grafana-dashboards/Connectivity_live_status.json
index 52796004638..3b3f266c3d9 100644
--- a/deployment/operations/grafana-dashboards/Connectivity_live_status.json
+++ b/deployment/operations/grafana-dashboards/Connectivity_live_status.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Container_Metrics.json b/deployment/operations/grafana-dashboards/Container_Metrics.json
index a3949c79f01..832cd2d6a82 100644
--- a/deployment/operations/grafana-dashboards/Container_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Container_Metrics.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/External_Metrics.json b/deployment/operations/grafana-dashboards/External_Metrics.json
index a21553f3643..67b79c3f72d 100644
--- a/deployment/operations/grafana-dashboards/External_Metrics.json
+++ b/deployment/operations/grafana-dashboards/External_Metrics.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Gateway_Traces.json b/deployment/operations/grafana-dashboards/Gateway_Traces.json
index 3de7a4cb297..a9350acf75d 100644
--- a/deployment/operations/grafana-dashboards/Gateway_Traces.json
+++ b/deployment/operations/grafana-dashboards/Gateway_Traces.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/JVM_Metrics.json b/deployment/operations/grafana-dashboards/JVM_Metrics.json
index 2c7fdfe5920..21a526f700e 100644
--- a/deployment/operations/grafana-dashboards/JVM_Metrics.json
+++ b/deployment/operations/grafana-dashboards/JVM_Metrics.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Kafka_Consumer_Metrics.json b/deployment/operations/grafana-dashboards/Kafka_Consumer_Metrics.json
index ef6a63d89da..2d468fbf34d 100644
--- a/deployment/operations/grafana-dashboards/Kafka_Consumer_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Kafka_Consumer_Metrics.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Kubernetes_Metrics.json b/deployment/operations/grafana-dashboards/Kubernetes_Metrics.json
index 72c17b5d1af..ddecf3ff22d 100644
--- a/deployment/operations/grafana-dashboards/Kubernetes_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Kubernetes_Metrics.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Load_Test.json b/deployment/operations/grafana-dashboards/Load_Test.json
index 514a65c2095..6c0544e7301 100644
--- a/deployment/operations/grafana-dashboards/Load_Test.json
+++ b/deployment/operations/grafana-dashboards/Load_Test.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -29,10 +26,7 @@
"panels": [
{
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -45,10 +39,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -132,10 +123,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -210,10 +198,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "sum(irate(enforcement_seconds_count{segment=\"overall\",channel!=\"live\"}[5m]) > 0) by (category, resource, outcome)",
"format": "time_series",
@@ -223,10 +208,7 @@
"refId": "A"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "sum(irate(enforcement_seconds_count{segment=\"overall\",channel=\"live\"}[5m]) > 0) by (category, resource, outcome)",
"format": "time_series",
@@ -242,10 +224,7 @@
},
{
"collapsed": false,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"gridPos": {
"h": 1,
"w": 24,
@@ -258,10 +237,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -354,10 +330,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -444,10 +417,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Positive: inbound messages\nNegative: outbound messages",
"fieldConfig": {
"defaults": {
diff --git a/deployment/operations/grafana-dashboards/Persistence_Entities.json b/deployment/operations/grafana-dashboards/Persistence_Entities.json
index a89f0319c5f..f69715730da 100644
--- a/deployment/operations/grafana-dashboards/Persistence_Entities.json
+++ b/deployment/operations/grafana-dashboards/Persistence_Entities.json
@@ -3,7 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": "-- Grafana --",
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
diff --git a/deployment/operations/grafana-dashboards/Pub_Sub.json b/deployment/operations/grafana-dashboards/Pub_Sub.json
index 49135cb536c..a88ae765a2e 100644
--- a/deployment/operations/grafana-dashboards/Pub_Sub.json
+++ b/deployment/operations/grafana-dashboards/Pub_Sub.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -33,10 +30,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -120,10 +114,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -222,10 +213,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -305,10 +293,7 @@
}
},
{
- "datasource": {
- "type": "elasticsearch",
- "uid": "PAE1B8C8635429669"
- },
+ "datasource": "elasticsearch",
"fieldConfig": {
"defaults": {
"color": {
@@ -555,10 +540,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -645,10 +627,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fill": 1,
"fillGradient": 0,
@@ -770,10 +749,7 @@
"noDataState": "no_data",
"notifications": []
},
- "datasource": {
- "type": "elasticsearch",
- "uid": "PAE1B8C8635429669"
- },
+ "datasource": "elasticsearch",
"fieldConfig": {
"defaults": {
"color": {
@@ -882,10 +858,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -981,10 +954,7 @@
"templating": {
"list": [
{
- "datasource": {
- "type": "elasticsearch",
- "uid": "PAE1B8C8635429669"
- },
+ "datasource": "elasticsearch",
"filters": [],
"hide": 0,
"name": "Filters",
diff --git a/deployment/operations/grafana-dashboards/Signal_processing.json b/deployment/operations/grafana-dashboards/Signal_processing.json
index 3a4e8f9ae20..d02ef2c1fe2 100644
--- a/deployment/operations/grafana-dashboards/Signal_processing.json
+++ b/deployment/operations/grafana-dashboards/Signal_processing.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "grafana",
- "uid": "-- Grafana --"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -42,10 +39,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -135,10 +129,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "sum(irate(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",segment=\"overall\",outcome=~\"$Outcome\",channel!=\"live\"}[$__rate_interval])) by (resource, category, outcome)",
@@ -152,10 +143,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -248,10 +236,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "sum(irate(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"overall\",channel=\"live\"}[$__rate_interval])) by (resource, category, outcome)",
@@ -266,10 +251,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -341,10 +323,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"overall\",channel!=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"overall\",channel!=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -358,10 +337,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -433,10 +409,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"overall\",channel=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",segment=\"overall\",channel=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -464,10 +437,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -539,10 +509,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(pre_enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",channel!=\"live\"}[$__rate_interval]) / idelta(pre_enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",channel!=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -556,10 +523,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -631,10 +595,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(pre_enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",channel=\"live\"}[$__rate_interval]) / idelta(pre_enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",channel=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -662,10 +623,7 @@
"type": "row"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -737,10 +695,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"enf\",channel!=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"enf\",channel!=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -754,10 +709,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -829,10 +781,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",segment=\"enf\",channel=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",segment=\"enf\",channel=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -857,10 +806,7 @@
"id": 17,
"panels": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -971,10 +917,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"process\",channel!=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"process\",channel!=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -988,10 +931,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -1063,10 +1003,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"process\",channel=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"process\",channel=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -1095,10 +1032,7 @@
"id": 21,
"panels": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -1170,10 +1104,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"resp_filter\",channel!=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",segment=\"resp_filter\",channel!=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -1187,10 +1118,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "",
"fieldConfig": {
"defaults": {
@@ -1262,10 +1190,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "avg((idelta(enforcement_seconds_sum{job=~\"$Application\",instance=~\"$Instance\",outcome=~\"$Outcome\",segment=\"resp_filter\",channel=\"live\"}[$__rate_interval]) / idelta(enforcement_seconds_count{job=~\"$Application\",instance=~\"$Instance\",segment=\"resp_filter\",channel=\"live\"}[$__rate_interval])) > 0) by (resource, category, outcome)",
@@ -1297,10 +1222,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(enforcement_seconds_count, job)",
"hide": 0,
"includeAll": true,
@@ -1324,10 +1246,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(enforcement_seconds_count{job=\"$Application\"}, instance)",
"hide": 0,
"includeAll": true,
@@ -1351,10 +1270,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(enforcement_seconds_count, outcome)",
"hide": 0,
"includeAll": true,
diff --git a/deployment/operations/grafana-dashboards/Sudo_command_count.json b/deployment/operations/grafana-dashboards/Sudo_command_count.json
index a25d5992242..587476ea225 100644
--- a/deployment/operations/grafana-dashboards/Sudo_command_count.json
+++ b/deployment/operations/grafana-dashboards/Sudo_command_count.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "grafana",
- "uid": "-- Grafana --"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -29,10 +26,7 @@
"liveNow": false,
"panels": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -105,10 +99,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"expr": "sum(irate(sudo_commands_total{job=~\"$Application\",instance=~\"$Instance\"}[5m])) by (type)",
"legendFormat": "{{type}}",
@@ -120,10 +111,7 @@
"type": "timeseries"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -189,10 +177,7 @@
"pluginVersion": "8.5.6",
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "sum(sudo_commands_total{job=~\"$Application\",instance=~\"$Instance\"}) by (type)",
@@ -241,10 +226,7 @@
"type": "table"
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -290,10 +272,7 @@
},
"targets": [
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editorMode": "code",
"exemplar": false,
"expr": "sum(sudo_commands_total{job=~\"$Application\",instance=~\"$Instance\"}) by (type)",
@@ -334,10 +313,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(sudo_commands_total, job)",
"hide": 0,
"includeAll": true,
@@ -361,10 +337,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "label_values(sudo_commands_total, instance)",
"hide": 0,
"includeAll": true,
diff --git a/deployment/operations/grafana-dashboards/Things-Wildcard-Search_Performance_Metrics.json b/deployment/operations/grafana-dashboards/Things-Wildcard-Search_Performance_Metrics.json
index 29353fd8d7a..6637c5084a0 100644
--- a/deployment/operations/grafana-dashboards/Things-Wildcard-Search_Performance_Metrics.json
+++ b/deployment/operations/grafana-dashboards/Things-Wildcard-Search_Performance_Metrics.json
@@ -3,10 +3,7 @@
"list": [
{
"builtIn": 1,
- "datasource": {
- "type": "datasource",
- "uid": "grafana"
- },
+ "datasource": "grafana",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@@ -35,10 +32,7 @@
"cacheTimeout": "0",
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Positive Y axis: stacked search queries\nNegative Y axis: stacked search counts",
"editable": true,
"error": false,
@@ -143,10 +137,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editable": true,
"error": false,
"fieldConfig": {
@@ -242,10 +233,7 @@
"cacheTimeout": "0",
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"editable": true,
"error": false,
"fieldConfig": {
@@ -343,10 +331,7 @@
"cacheTimeout": "0",
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"description": "Percentiles are capped to max. 10s",
"editable": true,
"error": false,
@@ -475,10 +460,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -568,10 +550,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -675,10 +654,7 @@
"bars": true,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fill": 1,
"fillGradient": 0,
"gridPos": {
@@ -764,10 +740,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -857,10 +830,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -990,10 +960,7 @@
}
},
{
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"color": {
@@ -1087,10 +1054,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -1180,10 +1144,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -1273,10 +1234,7 @@
"bars": false,
"dashLength": 10,
"dashes": false,
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"fieldConfig": {
"defaults": {
"links": []
@@ -1383,10 +1341,7 @@
"text": "All",
"value": "$__all"
},
- "datasource": {
- "type": "prometheus",
- "uid": "P1809F7CD0C75ACF3"
- },
+ "datasource": "prometheus",
"definition": "",
"hide": 0,
"includeAll": true,
diff --git a/deployment/operations/grafana-datasources/datasource.yaml b/deployment/operations/grafana-datasources/datasource.yaml
new file mode 100644
index 00000000000..14f5b356dd7
--- /dev/null
+++ b/deployment/operations/grafana-datasources/datasource.yaml
@@ -0,0 +1,39 @@
+# config file version
+apiVersion: 1
+
+# list of datasources to insert/update depending
+# whats available in the database
+datasources:
+ #
+ # Prometheus datasource
+ #
+- name: prometheus
+ #
datasource type. Required
+ type: prometheus
+ # access mode. direct or proxy. Required
+ access: proxy
+ # org id. will default to orgId 1 if not specified
+ orgId: 1
+ # url
+ url: http://prometheus:9090
+ # allow users to edit datasources from the UI.
+ editable: false
+
+- name: elasticsearch
+ # datasource type. Required
+ type: elasticsearch
+ # access mode. direct or proxy. Required
+ access: proxy
+ # org id. will default to orgId 1 if not specified
+ orgId: 1
+ # url
+ url: http://elasticsearch:9200
+ # allow users to edit datasources from the UI.
+ editable: false
+ database: "[logstash-]YYYY.MM.DD"
+ # fields that will be converted to json and stored in json_data
+ jsonData:
+ interval: Daily
+ timeField: "@timestamp"
+ esVersion: '8.0.0'
+ logMessageField: message
diff --git a/deployment/operations/prometheus/prometheus.yml b/deployment/operations/prometheus/prometheus.yml
new file mode 100644
index 00000000000..a5a50a5676f
--- /dev/null
+++ b/deployment/operations/prometheus/prometheus.yml
@@ -0,0 +1,42 @@
+global:
+ scrape_interval: 30s
+ scrape_timeout: 10s
+ evaluation_interval: 30s
+
+scrape_configs:
+ # Scrape prometheus itself.
+ - job_name: 'prometheus'
+ static_configs:
+ - targets: ['localhost:9090']
+
+ # Scrape grafana.
+ - job_name: 'grafana'
+ static_configs:
+ - targets: ['grafana:3000']
+
+ # Scrape Ditto.
+ - job_name: 'ditto'
+ kubernetes_sd_configs:
+ - role: pod
+
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
+ action: keep
+ regex: true
+ - source_labels: [ __meta_kubernetes_pod_annotation_prometheus_io_path ]
+ action: replace
+ target_label: __metrics_path__
+ regex: (.+)
+ - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
+ action: replace
+ regex: ([^:]+)(?::\d+)?;(\d+)
+ replacement: ${1}:${2}
+ target_label: __address__
+ - action: labelmap
+ regex: __meta_kubernetes_pod_label_(.+)
+ - source_labels: [__meta_kubernetes_pod_name]
+ action: replace
+ target_label: instance
+ - source_labels: [__meta_kubernetes_pod_container_name]
+ action: replace
+ target_label: job
diff --git a/documentation/README.md b/documentation/README.md
index 706f130e04b..eb0cc54181d 100755
--- a/documentation/README.md
+++ b/documentation/README.md
@@ -1,6 +1,6 @@
## Eclipse Ditto :: Documentation
-This folder contains the documentation and [static website of Eclipse Ditto](https://www.eclipse.org/ditto/).
+This folder contains the documentation and [static website of Eclipse Ditto](https://www.eclipse.dev/ditto/).
The documentation is based on [Jekyll](https://jekyllrb.com) and the fabulous [Jekyll Documentation Theme 6.0](http://idratherbewriting.com/documentation-theme-jekyll/).
@@ -49,7 +49,7 @@ bundle exec jekyll serve --verbose --unpublished
Validate that the HTML does not contain dead links, etc.:
```bash
-htmlproofer --assume-extension --allow-hash-href --disable-external --enforce-https=false --ignore-urls "/http-api-doc.html.*/" src/main/resources/_site/
+htmlproofer --assume-extension --allow-hash-href --disable-external --ignore-urls "/http-api-doc.html.*/","http://localhost:4000/feed.xml","http://www.ontology-of-units-of-measure.org/page/om-2" src/main/resources/_site/
```
#### Alternative 2: use Maven (UNIX)
diff --git a/documentation/src/main/resources/Gemfile b/documentation/src/main/resources/Gemfile
index 05068a53e5c..957b8ed6e5f 100644
--- a/documentation/src/main/resources/Gemfile
+++ b/documentation/src/main/resources/Gemfile
@@ -8,7 +8,7 @@ source "https://rubygems.org"
#
# This will help ensure the proper Jekyll version is running.
# Happy Jekylling!
-gem "jekyll", "~> 4.2.2"
+gem "jekyll", "~> 4.3.2"
# This is the default theme for new Jekyll sites. You may change this to anything you like.
diff --git a/documentation/src/main/resources/Gemfile.lock b/documentation/src/main/resources/Gemfile.lock
index 2a43b377843..d4a9af012af 100644
--- a/documentation/src/main/resources/Gemfile.lock
+++ b/documentation/src/main/resources/Gemfile.lock
@@ -1,36 +1,38 @@
GEM
remote: https://rubygems.org/
specs:
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0)
- concurrent-ruby (1.1.10)
+ concurrent-ruby (1.2.0)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.15.5)
forwardable-extended (2.6.0)
+ google-protobuf (3.22.0-arm64-darwin)
http_parser.rb (0.8.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
- jekyll (4.2.2)
+ jekyll (4.3.2)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
- jekyll-sass-converter (~> 2.0)
+ jekyll-sass-converter (>= 2.0, < 4.0)
jekyll-watch (~> 2.0)
- kramdown (~> 2.3)
+ kramdown (~> 2.3, >= 2.3.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
- mercenary (~> 0.4.0)
+ mercenary (>= 0.3.6, < 0.5)
pathutil (~> 0.9)
- rouge (~> 3.0)
+ rouge (>= 3.0, < 5.0)
safe_yaml (~> 1.0)
- terminal-table (~> 2.0)
- jekyll-sass-converter (2.2.0)
- sassc (> 2.0.1, < 3.0)
+ terminal-table (>= 1.8, < 4.0)
+ webrick (~> 1.7)
+ jekyll-sass-converter (3.0.0)
+ sass-embedded (~> 1.54)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-watch (2.2.1)
@@ -39,32 +41,33 @@ GEM
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
- liquid (4.0.3)
- listen (3.7.1)
+ liquid (4.0.4)
+ listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
- public_suffix (4.0.7)
- rb-fsevent (0.11.1)
+ public_suffix (5.0.1)
+ rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
- rouge (3.29.0)
+ rouge (4.1.0)
safe_yaml (1.0.5)
- sassc (2.4.0)
- ffi (~> 1.9)
- terminal-table (2.0.0)
- unicode-display_width (~> 1.1, >= 1.1.1)
- unicode-display_width (1.8.0)
+ sass-embedded (1.58.3-arm64-darwin)
+ google-protobuf (~> 3.21)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ unicode-display_width (2.4.2)
webrick (1.7.0)
PLATFORMS
arm64-darwin-21
+ arm64-darwin-22
DEPENDENCIES
- jekyll (~> 4.2.2)
+ jekyll (~> 4.3.2)
jekyll-sitemap (~> 1.4.0)
tzinfo-data
webrick (~> 1.7.0)
diff --git a/documentation/src/main/resources/_config.yml b/documentation/src/main/resources/_config.yml
index 517bf8d7893..8d6b64a8c08 100644
--- a/documentation/src/main/resources/_config.yml
+++ b/documentation/src/main/resources/_config.yml
@@ -114,17 +114,13 @@ plugins:
docVersions:
- label: "development"
basePath: ""
+ - label: "3.3"
+ basePath: "3.3"
+ - label: "3.2"
+ basePath: "3.2"
- label: "3.1"
basePath: "3.1"
- label: "3.0"
basePath: "3.0"
- label: "2.4"
basePath: "2.4"
- - label: "2.3"
- basePath: "2.3"
- - label: "2.2"
- basePath: "2.2"
- - label: "2.1"
- basePath: "2.1"
- - label: "2.0"
- basePath: "2.0"
diff --git a/documentation/src/main/resources/_data/authors.yml b/documentation/src/main/resources/_data/authors.yml
index 93333c8d446..1e286541d99 100644
--- a/documentation/src/main/resources/_data/authors.yml
+++ b/documentation/src/main/resources/_data/authors.yml
@@ -1,6 +1,6 @@
thomas_jaeckle:
name: Thomas Jäckle
- email: thomas.jaeckle@bosch.io
+ email: thomas.jaeckle@beyonnex.io
web: https://github.com/thjaeckle
florian_fendt:
@@ -10,7 +10,7 @@ florian_fendt:
juergen_fickel:
name: Jürgen Fickel
- email: juergen.fickel@bosch.io
+ email: eclipse-foundation@retujo.de
web: https://github.com/jufickel-b
philipp_michalski:
diff --git a/documentation/src/main/resources/_data/sidebars/ditto_sidebar.yml b/documentation/src/main/resources/_data/sidebars/ditto_sidebar.yml
index 4ac16530a28..48c7c3de88d 100644
--- a/documentation/src/main/resources/_data/sidebars/ditto_sidebar.yml
+++ b/documentation/src/main/resources/_data/sidebars/ditto_sidebar.yml
@@ -23,29 +23,50 @@ entries:
- title: Release Notes
output: web
folderitems:
- - title: 3.1.1
- url: /release_notes_311.html
+ - title: 3.3.4
+ url: /release_notes_334.html
output: web
- - title: 3.1.0
- url: /release_notes_310.html
+ - title: 3.3.3
+ url: /release_notes_333.html
output: web
- - title: 3.0.0
- url: /release_notes_300.html
+ - title: 3.3.2
+ url: /release_notes_332.html
output: web
- - title: 2.4.2
- url: /release_notes_242.html
+ - title: 3.3.0
+ url: /release_notes_330.html
output: web
- - title: 2.4.1
- url: /release_notes_241.html
+ - title: 3.2.1
+ url: /release_notes_321.html
output: web
- - title: 2.4.0
- url: /release_notes_240.html
+ - title: 3.2.0
+ url: /release_notes_320.html
output: web
subfolders:
- title: Archive
output: web
subfolderitems:
+ - title: 3.1.2
+ url: /release_notes_312.html
+ output: web
+ - title: 3.1.1
+ url: /release_notes_311.html
+ output: web
+ - title: 3.1.0
+ url: /release_notes_310.html
+ output: web
+ - title: 3.0.0
+ url: /release_notes_300.html
+ output: web
+ - title: 2.4.2
+ url: /release_notes_242.html
+ output: web
+ - title: 2.4.1
+ url: /release_notes_241.html
+ output: web
+ - title: 2.4.0
+ url: /release_notes_240.html
+ output: web
- title: 2.3.2
url: /release_notes_232.html
output: web
@@ -263,6 +284,9 @@ entries:
- title: Search
url: /basic-search.html
output: web
+ - title: History capabilities
+ url: /basic-history.html
+ output: web
- title: Acknowledgements / QoS
url: /basic-acknowledgements.html
output: web
@@ -492,6 +516,10 @@ entries:
url: /protocol-specification-connections-announcement.html
output: web
+ - title: Streaming subscriptions (history)
+ url: /protocol-specification-streaming-subscription.html
+ output: web
+
- title: Bindings
url: /protocol-bindings.html
output: web
diff --git a/documentation/src/main/resources/_data/tags.yml b/documentation/src/main/resources/_data/tags.yml
index 84db28d555c..72d45c03e79 100644
--- a/documentation/src/main/resources/_data/tags.yml
+++ b/documentation/src/main/resources/_data/tags.yml
@@ -11,6 +11,7 @@ allowed-tags:
- model
- signal
- http
+ - history
- search
- protocol
- connectivity
diff --git a/documentation/src/main/resources/_data/topnav.yml b/documentation/src/main/resources/_data/topnav.yml
index 4bf938138ec..7a473874854 100644
--- a/documentation/src/main/resources/_data/topnav.yml
+++ b/documentation/src/main/resources/_data/topnav.yml
@@ -13,15 +13,15 @@ topnav:
- title:
image: GitHub-Mark-Light-32px.png
alt: Sources at GitHub
- external_url: https://github.com/eclipse/ditto
+ external_url: https://github.com/eclipse-ditto/ditto
- title: SDKs
image: GitHub-Mark-Light-32px.png
alt: SDK sources at GitHub
- external_url: https://github.com/eclipse/ditto-clients
+ external_url: https://github.com/eclipse-ditto/ditto-clients
- title: examples
image: GitHub-Mark-Light-32px.png
alt: Example sources at GitHub
- external_url: https://github.com/eclipse/ditto-examples
+ external_url: https://github.com/eclipse-ditto/ditto-examples
#Topnav dropdowns
topnav_dropdowns:
diff --git a/documentation/src/main/resources/_includes/head.html b/documentation/src/main/resources/_includes/head.html
index 8e92a1e37f9..f85c61837f4 100644
--- a/documentation/src/main/resources/_includes/head.html
+++ b/documentation/src/main/resources/_includes/head.html
@@ -6,17 +6,17 @@
{% if page.title %} {% if page.layout == "post" %} {{ page.title }} • {{ site.topnav_title }}{% else %} {{ page.title }} • {{ site.short_title }}{% endif %}{% else %}{{ site.site_title }}{% endif %}
-
+
-
+
-
-
-
+
+
+
@@ -24,8 +24,8 @@
{
"@context": "http://schema.org",
"@type": "Organization",
- "url": "https://www.eclipse.org/ditto/",
- "logo": "https://www.eclipse.org/ditto/images/ditto.svg"
+ "url": "https://www.eclipse.dev/ditto/",
+ "logo": "https://www.eclipse.dev/ditto/images/ditto.svg"
}
@@ -36,5 +36,5 @@
-
-
+
+
diff --git a/documentation/src/main/resources/_includes/topnav.html b/documentation/src/main/resources/_includes/topnav.html
index b97d3cfb6a4..d17e798b338 100644
--- a/documentation/src/main/resources/_includes/topnav.html
+++ b/documentation/src/main/resources/_includes/topnav.html
@@ -72,7 +72,7 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+