Browse Source

Merge branch 'rc' into features/add_tooltip_option_to_show_stack_mode_total_value_on_timeseries_chart_widgets

pull/13373/head
Paolo Cristiani 1 year ago
committed by GitHub
parent
commit
c3fcc6dcc2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 146
      application/src/main/data/json/edge/instructions/install/centos/instructions.md
  2. 54
      application/src/main/data/json/edge/instructions/install/docker/instructions.md
  3. 104
      application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md
  4. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  5. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java
  6. 48
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  7. 21
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  8. 12
      application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java
  9. 9
      application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java
  10. 15
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  11. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java
  12. 25
      dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java

146
application/src/main/data/json/edge/instructions/install/centos/instructions.md

@ -4,15 +4,15 @@ Here is the list of commands, that can be used to quickly install ThingsBoard Ed
Before continue to installation execute the following commands in order to install necessary tools: Before continue to installation execute the following commands in order to install necessary tools:
```bash ```bash
sudo yum install -y nano wget sudo yum install -y nano wget && sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm {:copy-code}
``` ```
#### Install Java 17 (OpenJDK) #### Step 1. Install Java 17 (OpenJDK)
ThingsBoard service is running on Java 17. Follow these instructions to install OpenJDK 17: ThingsBoard service is running on Java 17. Follow these instructions to install OpenJDK 17:
```bash ```bash
sudo yum install java-17-openjdk sudo dnf install java-17-openjdk
{:copy-code} {:copy-code}
``` ```
@ -39,112 +39,94 @@ OpenJDK Runtime Environment (...)
OpenJDK 64-Bit Server VM (build ...) OpenJDK 64-Bit Server VM (build ...)
``` ```
#### Configure PostgreSQL #### Step 2. Configure ThingsBoard Database
ThingsBoard Edge supports SQL and hybrid database approaches.
In this guide we will use SQL only.
For hybrid details please follow official installation instructions from the ThingsBoard documentation site.
### PostgresSql
ThingsBoard Edge uses PostgreSQL database as a local storage. ThingsBoard Edge uses PostgreSQL database as a local storage.
Instructions listed below will help you to install PostgreSQL. To install PostgreSQL, follow the instructions below.
```bash ```bash
# Update your system # Update your system
sudo yum update sudo dnf update
{:copy-code} {:copy-code}
``` ```
**For CentOS 7:** Install the repository RPM:
**For CentOS/RHEL 8:**
```bash ```bash
# Install the repository RPM (for CentOS 7): # Install the repository RPM (For CentOS/RHEL 8):
sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# Install packages
sudo yum -y install epel-release yum-utils
sudo yum-config-manager --enable pgdg16
sudo yum install postgresql16-server postgresql16 postgresql16-contrib
# Initialize your PostgreSQL DB
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb
sudo systemctl start postgresql-16
# Optional: Configure PostgreSQL to start on boot
sudo systemctl enable --now postgresql-16
{:copy-code} {:copy-code}
``` ```
**For CentOS 8:** **For CentOS/RHEL 9:**
```bash ```bash
# Install the repository RPM (for CentOS 8): # Install the repository RPM (for CentOS 9):
sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# Install packages
sudo dnf -qy module disable postgresql
sudo dnf -y install postgresql16 postgresql16-server postgresql16-contrib
# Initialize your PostgreSQL DB
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb
sudo systemctl start postgresql-16
# Optional: Configure PostgreSQL to start on boot
sudo systemctl enable --now postgresql-16
{:copy-code} {:copy-code}
``` ```
Once PostgreSQL is installed you may want to create a new user or set the password for the main user. Install packages and initialize PostgreSQL. The PostgreSQL service will automatically start every time the system boots up.
The instructions below will help to set the password for main PostgreSQL user:
```text ```bash
sudo su - postgres sudo dnf -qy module disable postgresql && \
psql sudo dnf -y install postgresql16 postgresql16-server postgresql16-contrib && \
\password sudo /usr/pgsql-16/bin/postgresql-16-setup initdb && \
\q sudo systemctl enable --now postgresql-16
{:copy-code}
``` ```
Then, press "Ctrl+D" to return to main user console. Once PostgreSQL is installed, it is recommended to set the password for the PostgreSQL main user.
After configuring the password, edit the pg_hba.conf to use MD5 authentication with the postgres user. The following command will switch the current user to the PostgreSQL user and set the password directly in PostgreSQL.
Edit pg_hba.conf file:
```bash ```bash
sudo nano /var/lib/pgsql/16/data/pg_hba.conf sudo -u postgres psql -c "\password"
{:copy-code} {:copy-code}
``` ```
Locate the following lines: Then, enter and confirm the password.
```text
# IPv4 local connections:
host all all 127.0.0.1/32 ident
```
Replace `ident` with `md5`:
```text Since ThingsBoard Edge uses the PostgreSQL database for local storage, configuring MD5 authentication ensures that only authenticated users or
host all all 127.0.0.1/32 md5 applications can access the database, thus protecting your data. After configuring the password,
``` edit the pg_hba.conf file to use MD5 hashing for authentication instead of the default method (ident) for local IPv4 connections.
Finally, you should restart the PostgreSQL service to initialize the new configuration: To replace ident with md5, run the following command:
```bash ```bash
sudo systemctl restart postgresql-16.service sudo sed -i 's/^host\s\+all\s\+all\s\+127\.0\.0\.1\/32\s\+ident/host all all 127.0.0.1\/32 md5/' /var/lib/pgsql/16/data/pg_hba.conf
{:copy-code} {:copy-code}
``` ```
Connect to the database to create ThingsBoard Edge DB: Then run the command that will restart the PostgreSQL service to apply configuration changes, connect to the database as a postgres user,
and create the ThingsBoard Edge database (tb_edge). To connect to the PostgreSQL database, enter the PostgreSQL password.
```bash ```bash
psql -U postgres -d postgres -h 127.0.0.1 -W sudo systemctl restart postgresql-16.service && psql -U postgres -d postgres -h 127.0.0.1 -W -c "CREATE DATABASE tb_edge;"
{:copy-code} {:copy-code}
``` ```
Execute create database statement: #### Step 3. Choose Queue Service
```bash ThingsBoard Edge supports only Kafka or in-memory queue (since v4.0) for message storage and communication between ThingsBoard services.
CREATE DATABASE tb_edge; How to choose the right queue implementation?
\q
{:copy-code}
```
#### ThingsBoard Edge service installation In Memory queue implementation is built-in and default. It is useful for development(PoC) environments and is not suitable for production deployments or any sort of cluster deployments.
Kafka is recommended for production deployments. This queue is used on the most of ThingsBoard production environments now.
In Memory queue is built in and enabled by default. No additional configuration is required.
#### Step 4. ThingsBoard Edge Service Installation
Download installation package: Download installation package:
```bash ```bash
wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_TAG}/tb-edge-${TB_EDGE_TAG}.rpm wget wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_TAG}/tb-edge-${TB_EDGE_TAG}.rpm
{:copy-code} {:copy-code}
``` ```
@ -155,7 +137,7 @@ sudo rpm -Uvh tb-edge-${TB_EDGE_TAG}.rpm
{:copy-code} {:copy-code}
``` ```
#### Configure ThingsBoard Edge #### Step 5. Configure ThingsBoard Edge
To configure ThingsBoard Edge, you can use the following command to automatically update the configuration file with specific values: To configure ThingsBoard Edge, you can use the following command to automatically update the configuration file with specific values:
```bash ```bash
@ -169,25 +151,21 @@ EOL'
{:copy-code} {:copy-code}
``` ```
##### [Optional] Database Configuration ##### Configure PostgreSQL (Optional)
In case you changed default PostgreSQL datasource settings (**postgres**/**postgres**) please update the configuration file (**/etc/tb-edge/conf/tb-edge.conf**) with your actual values: If you changed PostgreSQL default datasource settings, use the following command:
```bash
sudo nano /etc/tb-edge/conf/tb-edge.conf
{:copy-code}
```
Please update the following lines in your configuration file. Make sure **to replace**:
- Replace 'postgres' with your actual PostgreSQL username;
- Replace 'PUT_YOUR_POSTGRESQL_PASSWORD_HERE' with your actual PostgreSQL password.
```bash ```bash
sudo sh -c 'cat <<EOL >> /etc/tb-edge/conf/tb-edge.conf
export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/tb_edge export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/tb_edge
export SPRING_DATASOURCE_USERNAME=postgres export SPRING_DATASOURCE_USERNAME=postgres
export SPRING_DATASOURCE_PASSWORD=PUT_YOUR_POSTGRESQL_PASSWORD_HERE export SPRING_DATASOURCE_PASSWORD=<PUT_YOUR_POSTGRESQL_PASSWORD_HERE>
EOL'
{:copy-code} {:copy-code}
``` ```
PUT_YOUR_POSTGRESQL_PASSWORD_HERE: Replace with your actual PostgreSQL user password.
##### [Optional] Update bind ports ##### [Optional] Update bind ports
If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update configuration parameters to avoid port collision between ThingsBoard server and ThingsBoard Edge. If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update configuration parameters to avoid port collision between ThingsBoard server and ThingsBoard Edge.
@ -206,7 +184,7 @@ EOL'
Make sure that ports above (18080, 11883, 15683) are not used by any other application. Make sure that ports above (18080, 11883, 15683) are not used by any other application.
#### Run installation script #### Step 6. Run installation Script
Once ThingsBoard Edge is installed and configured please execute the following install script: Once ThingsBoard Edge is installed and configured please execute the following install script:
```bash ```bash
@ -214,18 +192,18 @@ sudo /usr/share/tb-edge/bin/install/install.sh
{:copy-code} {:copy-code}
``` ```
#### Restart ThingsBoard Edge service #### Step 7. Restart ThingsBoard Edge Service
```bash ```bash
sudo service tb-edge restart sudo service tb-edge restart
{:copy-code} {:copy-code}
``` ```
#### Open ThingsBoard Edge UI #### Step 8. Open ThingsBoard Edge UI
Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080.
###### NOTE: Edge HTTP bind port update ###### NOTE: Edge HTTP bind port update
Use next **ThingsBoard Edge UI** link **http://localhost:18080** if you updated HTTP 8080 bind port to **18080**. If the Edge HTTP bind port was changed to 18080 during Edge installation, access the ThingsBoard Edge instance at http://localhost:18080.

54
application/src/main/data/json/edge/instructions/install/docker/instructions.md

@ -4,9 +4,31 @@ Here is the list of commands, that can be used to quickly install ThingsBoard Ed
Install <a href="https://docs.docker.com/engine/install/" target="_blank"> Docker CE</a> and <a href="https://docs.docker.com/compose/install/" target="_blank"> Docker Compose</a>. Install <a href="https://docs.docker.com/engine/install/" target="_blank"> Docker CE</a> and <a href="https://docs.docker.com/compose/install/" target="_blank"> Docker Compose</a>.
#### Running ThingsBoard Edge as docker service #### Step 1. Running ThingsBoard Edge
Create docker compose file for ThingsBoard Edge service: Here you can find ThingsBoard Edge docker image:
<a href="https://hub.docker.com/r/thingsboard/tb-edge" target="_blank"> thingsboard/tb-edge</a>
#### Step 2. Choose Queue and/or Database Services
ThingsBoard Edge supports only Kafka or in-memory queue (since v4.0) for message storage and communication between ThingsBoard services.
ThingsBoard Edge supports SQL and hybrid database approaches.
In this guide we will use SQL only.
For hybrid details please follow official installation instructions from the ThingsBoard documentation site.
How to choose the right queue implementation?
In Memory queue implementation is built-in and default. It is useful for development(PoC) environments and is not suitable for production deployments or any sort of cluster deployments.
Kafka is recommended for production deployments. This queue is used on the most of ThingsBoard production environments now.
Hybrid implementation combines PostgreSQL and Cassandra databases with Kafka queue service. It is recommended if you plan to manage 1M+ devices in production or handle high data ingestion rate (more than 5000 msg/sec).
Create a docker compose file for the ThingsBoard Edge service:
##### In Memory
```bash ```bash
nano docker-compose.yml nano docker-compose.yml
@ -35,7 +57,6 @@ services:
volumes: volumes:
- tb-edge-data:/data - tb-edge-data:/data
- tb-edge-logs:/var/log/tb-edge - tb-edge-logs:/var/log/tb-edge
${EXTRA_HOSTS}
postgres: postgres:
restart: always restart: always
image: "postgres:16" image: "postgres:16"
@ -58,24 +79,20 @@ volumes:
``` ```
##### [Optional] Update bind ports ##### [Optional] Update bind ports
If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update docker compose port mapping to avoid port collision between ThingsBoard server and ThingsBoard Edge. If ThingsBoard Edge is set to run on the same machine where the ThingsBoard server is operating, you need to update port configuration to prevent port collision between the ThingsBoard server and ThingsBoard Edge.
Please update next lines of `docker-compose.yml` file: Ensure that the ports 18080, 11883, 15683-15688 are not used by any other application.
```text Then, update the port configuration in the docker-compose.yml file:
ports: ```bash
- "18080:8080" sed -i ‘s/8080:8080/18080:8080/; s/1883:1883/11883:1883/; s/5683-5688:5683-5688\/udp/15683-15688:5683-5688\/udp/’ docker-compose.yml
- "11883:1883" {:copy-code}
- "15683-15688:5683-5688/udp"
``` ```
Make sure that ports above (18080, 11883, 15683-15688) are not used by any other application.
#### Start ThingsBoard Edge #### Start ThingsBoard Edge
Set the terminal in the directory which contains the `docker-compose.yml` file and execute the following commands to up this docker compose directly: Set the terminal in the directory which contains the docker-compose.yml file and execute the following commands to up this docker compose directly:
```bash ```bash
docker compose up -d docker compose up -d && docker compose logs -f mytbedge
docker compose logs -f mytbedge
{:copy-code} {:copy-code}
``` ```
@ -90,11 +107,12 @@ docker-compose up -d
docker-compose logs -f mytbedge docker-compose logs -f mytbedge
``` ```
#### Open ThingsBoard Edge UI #### Step 3. Open ThingsBoard Edge UI
Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. Once the Edge service is started, open the Edge UI at http://localhost:8080.
###### NOTE: Edge HTTP bind port update ###### NOTE: Edge HTTP bind port update
Use next **ThingsBoard Edge UI** link **http://localhost:18080** if you updated HTTP 8080 bind port to **18080**. If the Edge HTTP bind port was changed to 18080 during Edge installation, access the ThingsBoard Edge instance at http://localhost:18080.
Please use your tenant credentials from local Server instance or ThingsBoard Live Demo to log in to the ThingsBoard Edge.

104
application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md

@ -1,51 +1,50 @@
Here is the list of commands, that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server. Here is the list of commands, that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server.
#### Install Java 17 (OpenJDK) #### Step 1. Install Java 17 (OpenJDK)
ThingsBoard service is running on Java 17. Follow these instructions to install OpenJDK 17: ThingsBoard service is running on Java 17. To install OpenJDK 17, follow these instructions:
```bash ```bash
sudo apt update sudo apt update && sudo apt install openjdk-17-jdk
sudo apt install openjdk-17-jdk
{:copy-code} {:copy-code}
``` ```
Please don't forget to configure your operating system to use OpenJDK 17 by default. Configure your operating system to use OpenJDK 17 by default. You can configure the default version by running the following command:
You can configure which version is the default using the following command:
```bash ```bash
sudo update-alternatives --config java sudo update-alternatives --config java
{:copy-code} {:copy-code}
``` ```
You can check the installation using the following command: To check the installed Java version on your system, use the following command:
```bash ```bash
java -version java -version
{:copy-code} {:copy-code}
``` ```
Expected command output is: The expected result is:
```text ```text
openjdk version "17.x.xx" openjdk version "17.x.xx"
OpenJDK Runtime Environment (...) OpenJDK Runtime Environment (...)
OpenJDK 64-Bit Server VM (build ...) OpenJDK 64-Bit Server VM (...)
``` ```
#### Configure PostgreSQL #### Step 2. Configure ThingsBoard Edge Database
ThingsBoard Edge uses PostgreSQL database as a local storage.
Instructions listed below will help you to install PostgreSQL.
```bash ThingsBoard Edge supports SQL and hybrid database approaches.
# install **wget** if not already installed: In this guide we will use SQL only.
sudo apt install -y wget For hybrid details please follow official installation instructions from the ThingsBoard documentation site.
# import the repository signing key: ### Configure PostgreSQL
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - ThingsBoard Edge uses PostgreSQL database as a local storage.
# add repository contents to your system: To install the PostgreSQL database, run these commands:
RELEASE=$(lsb_release -cs)
echo "deb http://apt.postgresql.org/pub/repos/apt/ ${RELEASE}"-pgdg main | sudo tee /etc/apt/sources.list.d/pgdg.list ```bash
# Automated repository configuration:
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
# install and launch the postgresql service: # install and launch the postgresql service:
sudo apt update sudo apt update
@ -54,25 +53,35 @@ sudo service postgresql start
{:copy-code} {:copy-code}
``` ```
Once PostgreSQL is installed you may want to create a new user or set the password for the main user. Once PostgreSQL is installed, it is recommended to set the password for the PostgreSQL main user.
The instructions below will help to set the password for main PostgreSQL user:
```text The following command will switch the current user to the PostgreSQL user and set the password directly in PostgreSQL.
sudo su - postgres
psql ```bash
\password sudo -u postgres psql -c "\password"
\q {:copy-code}
``` ```
Then, press “Ctrl+D” to return to main user console and connect to the database to create ThingsBoard Edge DB: Then, enter and confirm the password.
```text Finally, create a new PostgreSQL database named tb_edge by running the following command:
psql -U postgres -d postgres -h 127.0.0.1 -W
CREATE DATABASE tb_edge; ```bash
\q echo "CREATE DATABASE tb_edge;" | psql -U postgres -d postgres -h 127.0.0.1 -W
{:copy-code}
``` ```
#### Thingsboard Edge service installation #### Step 3. Choose Queue Service
ThingsBoard Edge supports only Kafka or in-memory queue (since v4.0) for message storage and communication between ThingsBoard services. Choose the appropriate queue implementation based on your specific business needs:
In Memory: The built-in and default queue implementation. It is useful for development or proof-of-concept (PoC) environments, but is not recommended for production or any type of clustered deployments due to limited scalability.
Kafka: Recommended for production deployments. This queue is used in the most of ThingsBoard production environments now.
In Memory queue is built in and enabled by default. No additional configuration is required.
#### Step 4. ThingsBoard Edge Service Installation
Download installation package: Download installation package:
```bash ```bash
@ -87,7 +96,7 @@ sudo dpkg -i tb-edge-${TB_EDGE_TAG}.deb
{:copy-code} {:copy-code}
``` ```
#### Configure ThingsBoard Edge #### Step 5. Configure ThingsBoard Edge
To configure ThingsBoard Edge, you can use the following command to automatically update the configuration file with specific values: To configure ThingsBoard Edge, you can use the following command to automatically update the configuration file with specific values:
```bash ```bash
@ -101,25 +110,20 @@ EOL'
{:copy-code} {:copy-code}
``` ```
##### [Optional] Database Configuration ##### [Optional] Configure PostgreSQL
In case you changed default PostgreSQL datasource settings (**postgres**/**postgres**) please update the configuration file (**/etc/tb-edge/conf/tb-edge.conf**) with your actual values: If you changed PostgreSQL default datasource settings, use the following command:
```bash
sudo nano /etc/tb-edge/conf/tb-edge.conf
{:copy-code}
```
Please update the following lines in your configuration file. Make sure **to replace**:
- Replace 'postgres' with your actual PostgreSQL username;
- Replace 'PUT_YOUR_POSTGRESQL_PASSWORD_HERE' with your actual PostgreSQL password.
```bash ```bash
sudo sh -c 'cat <<EOL >> /etc/tb-edge/conf/tb-edge.conf
export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/tb_edge export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/tb_edge
export SPRING_DATASOURCE_USERNAME=postgres export SPRING_DATASOURCE_USERNAME=postgres
export SPRING_DATASOURCE_PASSWORD=PUT_YOUR_POSTGRESQL_PASSWORD_HERE export SPRING_DATASOURCE_PASSWORD=<PUT_YOUR_POSTGRESQL_PASSWORD_HERE>
EOL'
{:copy-code} {:copy-code}
``` ```
PUT_YOUR_POSTGRESQL_PASSWORD_HERE: Replace with your actual PostgreSQL user password.
##### [Optional] Update bind ports ##### [Optional] Update bind ports
If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update configuration parameters to avoid port collision between ThingsBoard server and ThingsBoard Edge. If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update configuration parameters to avoid port collision between ThingsBoard server and ThingsBoard Edge.
@ -138,7 +142,7 @@ EOL'
Make sure that ports above (18080, 11883, 15683) are not used by any other application. Make sure that ports above (18080, 11883, 15683) are not used by any other application.
#### Run installation script #### Step 6. Run installation Script
Once ThingsBoard Edge is installed and configured please execute the following install script: Once ThingsBoard Edge is installed and configured please execute the following install script:
@ -147,14 +151,14 @@ sudo /usr/share/tb-edge/bin/install/install.sh
{:copy-code} {:copy-code}
``` ```
#### Restart ThingsBoard Edge service #### Step 7. Restart ThingsBoard Edge Service
```bash ```bash
sudo service tb-edge restart sudo service tb-edge restart
{:copy-code} {:copy-code}
``` ```
#### Open ThingsBoard Edge UI #### Step 8. Open ThingsBoard Edge UI
Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080.

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -712,7 +712,7 @@ public abstract class EdgeGrpcSession implements Closeable {
private long findStartSeqIdFromOldestEventIfAny() { private long findStartSeqIdFromOldestEventIfAny() {
long startSeqId = 0L; long startSeqId = 0L;
try { try {
TimePageLink pageLink = new TimePageLink(1, 0, null, new SortOrder("createdTime"), null, null); TimePageLink pageLink = new TimePageLink(1, 0, null, null, null, null);
PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), null, null, pageLink); PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), null, null, pageLink);
if (!edgeEvents.getData().isEmpty()) { if (!edgeEvents.getData().isEmpty()) {
startSeqId = edgeEvents.getData().get(0).getSeqId() - 1; startSeqId = edgeEvents.getData().get(0).getSeqId() - 1;

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java

@ -26,9 +26,13 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeEventService;
import java.util.concurrent.TimeUnit;
@AllArgsConstructor @AllArgsConstructor
@Slf4j @Slf4j
public class GeneralEdgeEventFetcher implements EdgeEventFetcher { public class GeneralEdgeEventFetcher implements EdgeEventFetcher {
// Subtract from queueStartTs to ensure no data is lost due to potential misordering of edge events by created_time.
private static final long MISORDERING_COMPENSATION_MILLIS = TimeUnit.SECONDS.toMillis(60);
private final Long queueStartTs; private final Long queueStartTs;
private Long seqIdStart; private Long seqIdStart;
@ -44,7 +48,7 @@ public class GeneralEdgeEventFetcher implements EdgeEventFetcher {
0, 0,
null, null,
null, null,
queueStartTs, queueStartTs > 0 ? queueStartTs - MISORDERING_COMPENSATION_MILLIS : 0,
System.currentTimeMillis()); System.currentTimeMillis());
} }

48
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -60,6 +60,7 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
@ -68,6 +69,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.common.data.security.model.JwtSettings;
import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
@ -107,6 +109,7 @@ import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.edge.AbstractEdgeTest.CONNECT_MESSAGE_COUNT;
@TestPropertySource(properties = { @TestPropertySource(properties = {
"edges.enabled=true", "edges.enabled=true",
@ -138,6 +141,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
public EdgeDao edgeDao(EdgeDao edgeDao) { public EdgeDao edgeDao(EdgeDao edgeDao) {
return Mockito.mock(EdgeDao.class, AdditionalAnswers.delegatesTo(edgeDao)); return Mockito.mock(EdgeDao.class, AdditionalAnswers.delegatesTo(edgeDao));
} }
} }
@Before @Before
@ -886,6 +890,8 @@ public class EdgeControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class); Device savedDevice = doPost("/api/device", device, Device.class);
// create public customer // create public customer
//1 message
// Customer
doPost("/api/customer/public/device/" + savedDevice.getId().getId(), Device.class); doPost("/api/customer/public/device/" + savedDevice.getId().getId(), Device.class);
doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class); doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class);
@ -897,13 +903,16 @@ public class EdgeControllerTest extends AbstractControllerTest {
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret()); EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class); edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class);
edgeImitator.ignoreType(OAuth2DomainUpdateMsg.class); edgeImitator.ignoreType(OAuth2DomainUpdateMsg.class);
edgeImitator.expectMessageAmount(27); // 17 connect message
// + 1 Customer
// + 5 fetchers messages (DeviceProfile, Device, DeviceCredentials, AssetProfile, Asset) in sync process
// + 5 queue messages the same
edgeImitator.expectMessageAmount(CONNECT_MESSAGE_COUNT + 11);
edgeImitator.connect(); edgeImitator.connect();
waitForMessages(edgeImitator); edgeImitator.waitForMessages();
verifyFetchersMsgs(edgeImitator, savedDevice); verifyFetchersMsgs(edgeImitator, savedDevice);
// verify queue msgs // verify queue msgs
@ -914,9 +923,12 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
printQueueMsgsIfNotEmpty(edgeImitator); printQueueMsgsIfNotEmpty(edgeImitator);
edgeImitator.expectMessageAmount(21); // 17 connect messages
// + 1 Customer
// + 5 fetchers messages (DeviceProfile, Device, DeviceCredentials, AssetProfile, Asset) in sync process
edgeImitator.expectMessageAmount(CONNECT_MESSAGE_COUNT + 6);
doPost("/api/edge/sync/" + edge.getId()).andExpect(status().isOk()); doPost("/api/edge/sync/" + edge.getId()).andExpect(status().isOk());
waitForMessages(edgeImitator); edgeImitator.waitForMessages();
verifyFetchersMsgs(edgeImitator, savedDevice); verifyFetchersMsgs(edgeImitator, savedDevice);
printQueueMsgsIfNotEmpty(edgeImitator); printQueueMsgsIfNotEmpty(edgeImitator);
@ -987,17 +999,6 @@ public class EdgeControllerTest extends AbstractControllerTest {
}); });
} }
private void waitForMessages(EdgeImitator edgeImitator) throws Exception {
boolean success = edgeImitator.waitForMessages();
if (!success) {
List<AbstractMessage> downlinkMsgs = edgeImitator.getDownlinkMsgs();
for (AbstractMessage downlinkMsg : downlinkMsgs) {
log.error("{}\n{}", downlinkMsg.getClass(), downlinkMsg);
}
Assert.fail("Await for messages was not successful!");
}
}
private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) { private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) {
Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
@ -1011,6 +1012,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgs(), currentUserId));
Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
@ -1156,6 +1158,20 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popUserCredentialsMsg(List<AbstractMessage> messages, UserId userId) {
for (AbstractMessage message : messages) {
if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) {
UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(userCredentials);
if (userId.equals(userCredentials.getUserId())) {
messages.remove(message);
return true;
}
}
}
return false;
}
private boolean popUserMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) { private boolean popUserMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof UserUpdateMsg userUpdateMsg) { if (message instanceof UserUpdateMsg userUpdateMsg) {

21
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -115,7 +115,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
}) })
@Slf4j @Slf4j
abstract public class AbstractEdgeTest extends AbstractControllerTest { abstract public class AbstractEdgeTest extends AbstractControllerTest {
public static final Integer CONNECT_MESSAGE_COUNT = 17;
public static final Integer INSTALLATION_MESSAGE_COUNT = 8;
public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT;
private static final String THERMOSTAT_DEVICE_PROFILE_NAME = "Thermostat"; private static final String THERMOSTAT_DEVICE_PROFILE_NAME = "Thermostat";
protected DeviceProfile thermostatDeviceProfile; protected DeviceProfile thermostatDeviceProfile;
@ -136,11 +138,12 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
doPost("/api/admin/jwtSettings", settings).andExpect(status().isOk()); doPost("/api/admin/jwtSettings", settings).andExpect(status().isOk());
loginTenantAdmin(); loginTenantAdmin();
//8 installation messages
installation(); installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.expectMessageAmount(25); // 17 connect messages + 8 installation messages
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT);
edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class); edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class);
edgeImitator.ignoreType(OAuth2DomainUpdateMsg.class); edgeImitator.ignoreType(OAuth2DomainUpdateMsg.class);
edgeImitator.connect(); edgeImitator.connect();
@ -164,22 +167,32 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
thermostatDeviceProfile = this.createDeviceProfile(THERMOSTAT_DEVICE_PROFILE_NAME, thermostatDeviceProfile = this.createDeviceProfile(THERMOSTAT_DEVICE_PROFILE_NAME,
createMqttDeviceProfileTransportConfiguration(new JsonTransportPayloadConfiguration(), false)); createMqttDeviceProfileTransportConfiguration(new JsonTransportPayloadConfiguration(), false));
extendDeviceProfileData(thermostatDeviceProfile); extendDeviceProfileData(thermostatDeviceProfile);
//2 messages DeviceProfile
thermostatDeviceProfile = doPost("/api/deviceProfile", thermostatDeviceProfile, DeviceProfile.class); thermostatDeviceProfile = doPost("/api/deviceProfile", thermostatDeviceProfile, DeviceProfile.class);
Device savedDevice = saveDevice("Edge Device 1", THERMOSTAT_DEVICE_PROFILE_NAME); Device savedDevice = saveDevice("Edge Device 1", THERMOSTAT_DEVICE_PROFILE_NAME);
// create public customer // create public customer
//1 message
// Customer
doPost("/api/customer/public/device/" + savedDevice.getId().getId(), Device.class); doPost("/api/customer/public/device/" + savedDevice.getId().getId(), Device.class);
doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class); doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class);
Asset savedAsset = saveAsset("Edge Asset 1");
Asset savedAsset = saveAsset("Edge Asset 1");
updateRootRuleChainMetadata(); updateRootRuleChainMetadata();
edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class); edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class);
//3 messages
// Device
// DeviceProfile
// DeviceCredentials
doPost("/api/edge/" + edge.getUuidId() doPost("/api/edge/" + edge.getUuidId()
+ "/device/" + savedDevice.getUuidId(), Device.class); + "/device/" + savedDevice.getUuidId(), Device.class);
//2 messages
// Asset
// AssetProfile
doPost("/api/edge/" + edge.getUuidId() doPost("/api/edge/" + edge.getUuidId()
+ "/asset/" + savedAsset.getUuidId(), Asset.class); + "/asset/" + savedAsset.getUuidId(), Asset.class);

12
application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java

@ -128,15 +128,19 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest {
@Test @Test
public void testDeleteDeviceProfilesWhenEdgeIsOffline() throws Exception { public void testDeleteDeviceProfilesWhenEdgeIsOffline() throws Exception {
//2 message RuleChain and RuleChainMetadata
RuleChainId thermostatsRuleChainId = createEdgeRuleChainAndAssignToEdge("Thermostats Rule Chain"); RuleChainId thermostatsRuleChainId = createEdgeRuleChainAndAssignToEdge("Thermostats Rule Chain");
// create device profile // create device profile
DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null); DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null);
deviceProfile.setDefaultEdgeRuleChainId(thermostatsRuleChainId); deviceProfile.setDefaultEdgeRuleChainId(thermostatsRuleChainId);
extendDeviceProfileData(deviceProfile); extendDeviceProfileData(deviceProfile);
//1 message DeviceProfile
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg);
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage;
@ -150,9 +154,11 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest {
doDelete("/api/deviceProfile/" + deviceProfile.getUuidId()) doDelete("/api/deviceProfile/" + deviceProfile.getUuidId())
.andExpect(status().isOk()); .andExpect(status().isOk());
edgeImitator.connect(); edgeImitator.connect();
// 27 sync message
// + 1 delete message // 25 sync message
edgeImitator.expectMessageAmount(28); // + 2 RuleChain and RuleChainMetadata
// + 1 delete DeviceProfile
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT + 3);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage(); latestMessage = edgeImitator.getLatestMessage();

9
application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java

@ -78,11 +78,14 @@ public class TenantProfileEdgeTest extends AbstractEdgeTest {
TenantProfileQueueConfiguration mainQueueConfiguration = createQueueConfig(DataConstants.MAIN_QUEUE_NAME, DataConstants.MAIN_QUEUE_TOPIC); TenantProfileQueueConfiguration mainQueueConfiguration = createQueueConfig(DataConstants.MAIN_QUEUE_NAME, DataConstants.MAIN_QUEUE_TOPIC);
TenantProfileQueueConfiguration isolatedQueueConfiguration = createQueueConfig("IsolatedHighPriority", "tb_rule_engine.isolated_hp"); TenantProfileQueueConfiguration isolatedQueueConfiguration = createQueueConfig("IsolatedHighPriority", "tb_rule_engine.isolated_hp");
edgeTenantProfile.getProfileData().setQueueConfiguration(List.of(mainQueueConfiguration, isolatedQueueConfiguration)); edgeTenantProfile.getProfileData().setQueueConfiguration(List.of(mainQueueConfiguration, isolatedQueueConfiguration));
// + 1 TenantProfile
// + 1 Queue main
// + 1 Queue isolated
edgeImitator.expectMessageAmount(3); edgeImitator.expectMessageAmount(3);
edgeTenantProfile = doPost("/api/tenantProfile", edgeTenantProfile, TenantProfile.class); edgeTenantProfile = doPost("/api/tenantProfile", edgeTenantProfile, TenantProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
Optional<TenantProfileUpdateMsg> tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); Optional<TenantProfileUpdateMsg> tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class);
Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent());
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get();
TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true);
@ -96,7 +99,9 @@ public class TenantProfileEdgeTest extends AbstractEdgeTest {
loginTenantAdmin(); loginTenantAdmin();
edgeImitator.expectMessageAmount(21); // 25 sync message
// +1 isolated Queue
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT + 1);
doPost("/api/edge/sync/" + edge.getId()); doPost("/api/edge/sync/" + edge.getId());
assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue(); assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue();

15
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -24,6 +24,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Assert;
import org.thingsboard.edge.rpc.EdgeGrpcClient; import org.thingsboard.edge.rpc.EdgeGrpcClient;
import org.thingsboard.edge.rpc.EdgeRpcClient; import org.thingsboard.edge.rpc.EdgeRpcClient;
import org.thingsboard.server.controller.AbstractWebTest; import org.thingsboard.server.controller.AbstractWebTest;
@ -386,7 +387,19 @@ public class EdgeImitator {
} }
public boolean waitForMessages() throws InterruptedException { public boolean waitForMessages() throws InterruptedException {
return waitForMessages(AbstractWebTest.TIMEOUT); boolean success = waitForMessages(AbstractWebTest.TIMEOUT);
if (!success) {
List<AbstractMessage> downlinkMsgs = getDownlinkMsgs();
for (AbstractMessage downlinkMsg : downlinkMsgs) {
log.error("{}\n{}", downlinkMsg.getClass(), downlinkMsg);
}
log.error("message count: {}", downlinkMsgs.size());
Assert.fail("Await for messages was not successful!");
}
return true;
} }
public boolean waitForMessages(int timeoutInSeconds) throws InterruptedException { public boolean waitForMessages(int timeoutInSeconds) throws InterruptedException {

10
dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java

@ -44,7 +44,7 @@ import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
import java.util.ArrayList; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -58,6 +58,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class JpaBaseEdgeEventDao extends JpaPartitionedAbstractDao<EdgeEventEntity, EdgeEvent> implements EdgeEventDao { public class JpaBaseEdgeEventDao extends JpaPartitionedAbstractDao<EdgeEventEntity, EdgeEvent> implements EdgeEventDao {
private static final List<SortOrder> SORT_ORDERS = Collections.singletonList(new SortOrder("seqId"));
private final UUID systemTenantId = NULL_UUID; private final UUID systemTenantId = NULL_UUID;
@ -175,11 +176,6 @@ public class JpaBaseEdgeEventDao extends JpaPartitionedAbstractDao<EdgeEventEnti
@Override @Override
public PageData<EdgeEvent> findEdgeEvents(UUID tenantId, EdgeId edgeId, Long seqIdStart, Long seqIdEnd, TimePageLink pageLink) { public PageData<EdgeEvent> findEdgeEvents(UUID tenantId, EdgeId edgeId, Long seqIdStart, Long seqIdEnd, TimePageLink pageLink) {
List<SortOrder> sortOrders = new ArrayList<>();
if (pageLink.getSortOrder() != null) {
sortOrders.add(pageLink.getSortOrder());
}
sortOrders.add(new SortOrder("seqId"));
return DaoUtil.toPageData( return DaoUtil.toPageData(
edgeEventRepository edgeEventRepository
.findEdgeEventsByTenantIdAndEdgeId( .findEdgeEventsByTenantIdAndEdgeId(
@ -190,7 +186,7 @@ public class JpaBaseEdgeEventDao extends JpaPartitionedAbstractDao<EdgeEventEnti
pageLink.getEndTime(), pageLink.getEndTime(),
seqIdStart, seqIdStart,
seqIdEnd, seqIdEnd,
DaoUtil.toPageable(pageLink, sortOrders))); DaoUtil.toPageable(pageLink, SORT_ORDERS)));
} }
@Override @Override

25
dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java

@ -16,7 +16,6 @@
package org.thingsboard.server.dao.service; package org.thingsboard.server.dao.service;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -38,8 +37,6 @@ import org.thingsboard.server.dao.edge.EdgeEventService;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.time.DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT; import static org.apache.commons.lang3.time.DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT;
@ -103,26 +100,23 @@ public class EdgeEventServiceTest extends AbstractServiceTest {
} }
@Test @Test
public void findEdgeEventsByTimeDescOrder() throws Exception { public void findEdgeEventsBySeqIdOrder_createdTimeOrderIgnored() throws Exception {
EdgeId edgeId = new EdgeId(Uuids.timeBased()); EdgeId edgeId = new EdgeId(Uuids.timeBased());
DeviceId deviceId = new DeviceId(Uuids.timeBased()); DeviceId deviceId = new DeviceId(Uuids.timeBased());
List<ListenableFuture<Void>> futures = new ArrayList<>(); saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId).get();
futures.add(saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId)); saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId).get();
futures.add(saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId)); saveEdgeEventWithProvidedTime(eventTime + 2, edgeId, deviceId, tenantId).get();
futures.add(saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId)); saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId).get();
futures.add(saveEdgeEventWithProvidedTime(eventTime + 2, edgeId, deviceId, tenantId)); saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId).get();
futures.add(saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId));
Futures.allAsList(futures).get();
TimePageLink pageLink = new TimePageLink(2, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), startTime, endTime); TimePageLink pageLink = new TimePageLink(2, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), startTime, endTime);
PageData<EdgeEvent> edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, pageLink); PageData<EdgeEvent> edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, pageLink);
Assert.assertNotNull(edgeEvents.getData()); Assert.assertNotNull(edgeEvents.getData());
Assert.assertEquals(2, edgeEvents.getData().size()); Assert.assertEquals(2, edgeEvents.getData().size());
Assert.assertEquals(Uuids.startOf(eventTime + 2), edgeEvents.getData().get(0).getUuidId()); Assert.assertEquals(Uuids.startOf(eventTime), edgeEvents.getData().get(0).getUuidId());
Assert.assertEquals(Uuids.startOf(eventTime + 1), edgeEvents.getData().get(1).getUuidId()); Assert.assertEquals(Uuids.startOf(eventTime + 2), edgeEvents.getData().get(1).getUuidId());
Assert.assertTrue(edgeEvents.hasNext()); Assert.assertTrue(edgeEvents.hasNext());
Assert.assertNotNull(pageLink.nextPageLink()); Assert.assertNotNull(pageLink.nextPageLink());
@ -130,7 +124,7 @@ public class EdgeEventServiceTest extends AbstractServiceTest {
Assert.assertNotNull(edgeEvents.getData()); Assert.assertNotNull(edgeEvents.getData());
Assert.assertEquals(1, edgeEvents.getData().size()); Assert.assertEquals(1, edgeEvents.getData().size());
Assert.assertEquals(Uuids.startOf(eventTime), edgeEvents.getData().get(0).getUuidId()); Assert.assertEquals(Uuids.startOf(eventTime + 1), edgeEvents.getData().get(0).getUuidId());
Assert.assertFalse(edgeEvents.hasNext()); Assert.assertFalse(edgeEvents.hasNext());
edgeEventDao.cleanupEvents(1); edgeEventDao.cleanupEvents(1);
@ -141,4 +135,5 @@ public class EdgeEventServiceTest extends AbstractServiceTest {
edgeEvent.setId(new EdgeEventId(Uuids.startOf(time))); edgeEvent.setId(new EdgeEventId(Uuids.startOf(time)));
return edgeEventService.saveAsync(edgeEvent); return edgeEventService.saveAsync(edgeEvent);
} }
} }
Loading…
Cancel
Save