From 7401ece634712da3f983f7e2ca54fdb6d8e806d0 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 17 Nov 2023 11:56:29 +0200 Subject: [PATCH 1/3] testSyncEdge - improved edge activation. added loggin in case test failure --- .../server/controller/EdgeControllerTest.java | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index f0c47bb53a..0a25d76dd9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -23,6 +23,8 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.AbstractMessage; +import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -78,6 +80,8 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -92,6 +96,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; }) @ContextConfiguration(classes = {EdgeControllerTest.Config.class}) @DaoSqlTest +@Slf4j public class EdgeControllerTest extends AbstractControllerTest { public static final String EDGE_HOST = "localhost"; @@ -863,10 +868,7 @@ public class EdgeControllerTest extends AbstractControllerTest { Edge edge = doPost("/api/edge", constructEdge("Test Sync Edge", "test"), Edge.class); - // simulate edge activation - ObjectNode attributes = JacksonUtil.newObjectNode(); - attributes.put("active", true); - doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + simulateEdgeActivation(edge); doPost("/api/edge/" + edge.getId().getId().toString() + "/device/" + savedDevice.getId().getId().toString(), Device.class); @@ -878,7 +880,7 @@ public class EdgeControllerTest extends AbstractControllerTest { edgeImitator.expectMessageAmount(24); edgeImitator.connect(); - assertThat(edgeImitator.waitForMessages()).as("await for messages on first connect").isTrue(); + waitForMessages(edgeImitator); verifyFetchersMsgs(edgeImitator); // verify queue msgs @@ -890,7 +892,7 @@ public class EdgeControllerTest extends AbstractControllerTest { edgeImitator.expectMessageAmount(20); doPost("/api/edge/sync/" + edge.getId()); - assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue(); + waitForMessages(edgeImitator); verifyFetchersMsgs(edgeImitator); Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty()); @@ -909,6 +911,35 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + private void simulateEdgeActivation(Edge edge) throws Exception { + ObjectNode attributes = JacksonUtil.newObjectNode(); + attributes.put("active", true); + doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + Awaitility.await() + .atMost(30, TimeUnit.SECONDS) + .until(() -> { + List> values = doGetAsyncTyped("/api/plugins/telemetry/EDGE/" + edge.getId() + + "/values/attributes/SERVER_SCOPE", new TypeReference<>() {}); + Optional> activeAttrOpt = values.stream().filter(att -> att.get("key").equals("active")).findFirst(); + if (activeAttrOpt.isEmpty()) { + return false; + } + Map activeAttr = activeAttrOpt.get(); + return "true".equals(activeAttr.get("value").toString()); + }); + } + + private void waitForMessages(EdgeImitator edgeImitator) throws Exception { + boolean success = edgeImitator.waitForMessages(); + if (!success) { + List 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) { 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")); From 0eaa0d44e66ff23b13d2c7a5d38c293c3ec9763d Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 17 Nov 2023 11:59:00 +0200 Subject: [PATCH 2/3] Organize imports --- .../org/thingsboard/server/controller/EdgeControllerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index 0a25d76dd9..379dafef94 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -85,7 +85,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; From b6b501c3a3507906f2de27239f72bf020ed3cb26 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 17 Nov 2023 18:53:40 +0200 Subject: [PATCH 3/3] UI: Fix scroll grid. --- .../home/components/grid/scroll-grid.component.html | 4 ++-- .../modules/home/components/grid/scroll-grid.component.ts | 8 ++++++++ ui-ngx/src/app/shared/models/resource.models.ts | 4 +++- ui-ngx/src/app/shared/pipe/image.pipe.ts | 6 ++++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html index 5340293a12..849dc2e6a9 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html @@ -16,9 +16,9 @@ --> - +
-
+
diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts index e0eea202ff..7044fc040e 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts @@ -100,4 +100,12 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang isObject(value: any): boolean { return isObject(value); } + + trackByItemsRow(index: number, itemsRow: T[]): number { + return index; + } + + trackByItem(index: number, item: T): T { + return item; + } } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index e494e3842e..02ae41d481 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -82,7 +82,9 @@ export interface ImageDescriptor { previewDescriptor: ImageDescriptor; } -export type ImageResourceInfo = TbResourceInfo; +export interface ImageResourceInfo extends TbResourceInfo { + link?: string; +} export type ImageResourceType = 'tenant' | 'system'; diff --git a/ui-ngx/src/app/shared/pipe/image.pipe.ts b/ui-ngx/src/app/shared/pipe/image.pipe.ts index fb54cb6985..24f91c500d 100644 --- a/ui-ngx/src/app/shared/pipe/image.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/image.pipe.ts @@ -17,11 +17,13 @@ import { Pipe, PipeTransform } from '@angular/core'; import { ImageService } from '@core/http/image.service'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { isDefinedAndNotNull } from '@core/utils'; import { NO_IMAGE_DATA_URI } from '@shared/models/resource.models'; -const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; +const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRG' + + 'LTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAw' + + 'IDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; @Pipe({ name: 'image'