Browse Source

Implement widgets bundles ordering.

pull/9548/head
Igor Kulikov 3 years ago
parent
commit
449296a604
  1. 3
      application/src/main/data/upgrade/3.6.0/schema_update.sql
  2. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java
  3. 6
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java
  4. 3
      common/edge-api/src/main/proto/edge.proto
  5. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  6. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java
  7. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java
  8. 1
      dao/src/main/resources/sql/schema-entities.sql
  9. 54
      dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java
  10. 4
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html
  11. 6
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts
  12. 1
      ui-ngx/src/app/shared/models/widgets-bundle.model.ts
  13. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
application/src/main/data/upgrade/3.6.0/schema_update.sql

@ -17,6 +17,9 @@
ALTER TABLE widget_type ALTER TABLE widget_type
ADD COLUMN IF NOT EXISTS tags text[]; ADD COLUMN IF NOT EXISTS tags text[];
ALTER TABLE widgets_bundle
ADD COLUMN IF NOT EXISTS widgets_bundle_order int;
ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS tbel_exec varchar(32); ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS tbel_exec varchar(32);
UPDATE api_usage_state SET tbel_exec = js_exec WHERE tbel_exec IS NULL; UPDATE api_usage_state SET tbel_exec = js_exec WHERE tbel_exec IS NULL;

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java

@ -45,6 +45,9 @@ public class WidgetsBundleMsgConstructor {
if (widgetsBundle.getDescription() != null) { if (widgetsBundle.getDescription() != null) {
builder.setDescription(widgetsBundle.getDescription()); builder.setDescription(widgetsBundle.getDescription());
} }
if (widgetsBundle.getOrder() != null) {
builder.setOrder(widgetsBundle.getOrder());
}
if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true); builder.setIsSystem(true);
} }

6
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java

@ -70,6 +70,11 @@ public class WidgetsBundle extends BaseData<WidgetsBundleId> implements HasName,
@ApiModelProperty(position = 7, value = "Description", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @ApiModelProperty(position = 7, value = "Description", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String description; private String description;
@Getter
@Setter
@ApiModelProperty(position = 8, value = "Order", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private Integer order;
@Getter @Getter
@Setter @Setter
private WidgetsBundleId externalId; private WidgetsBundleId externalId;
@ -89,6 +94,7 @@ public class WidgetsBundle extends BaseData<WidgetsBundleId> implements HasName,
this.title = widgetsBundle.getTitle(); this.title = widgetsBundle.getTitle();
this.image = widgetsBundle.getImage(); this.image = widgetsBundle.getImage();
this.description = widgetsBundle.getDescription(); this.description = widgetsBundle.getDescription();
this.order = widgetsBundle.getOrder();
this.externalId = widgetsBundle.getExternalId(); this.externalId = widgetsBundle.getExternalId();
} }

3
common/edge-api/src/main/proto/edge.proto

@ -361,7 +361,8 @@ message WidgetsBundleUpdateMsg {
optional bytes image = 6; optional bytes image = 6;
bool isSystem = 7; bool isSystem = 7;
optional string description = 8; optional string description = 8;
optional string widgets = 9; optional int32 order = 9;
optional string widgets = 10;
} }
message WidgetTypeUpdateMsg { message WidgetTypeUpdateMsg {

1
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -302,6 +302,7 @@ public class ModelConstants {
public static final String WIDGETS_BUNDLE_TITLE_PROPERTY = TITLE_PROPERTY; public static final String WIDGETS_BUNDLE_TITLE_PROPERTY = TITLE_PROPERTY;
public static final String WIDGETS_BUNDLE_IMAGE_PROPERTY = "image"; public static final String WIDGETS_BUNDLE_IMAGE_PROPERTY = "image";
public static final String WIDGETS_BUNDLE_DESCRIPTION = "description"; public static final String WIDGETS_BUNDLE_DESCRIPTION = "description";
public static final String WIDGETS_BUNDLE_ORDER = "widgets_bundle_order";
/** /**
* Widget_type constants. * Widget_type constants.

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java

@ -50,6 +50,9 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
@Column(name = ModelConstants.WIDGETS_BUNDLE_DESCRIPTION) @Column(name = ModelConstants.WIDGETS_BUNDLE_DESCRIPTION)
private String description; private String description;
@Column(name = ModelConstants.WIDGETS_BUNDLE_ORDER)
private Integer order;
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
private UUID externalId; private UUID externalId;
@ -69,6 +72,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
this.title = widgetsBundle.getTitle(); this.title = widgetsBundle.getTitle();
this.image = widgetsBundle.getImage(); this.image = widgetsBundle.getImage();
this.description = widgetsBundle.getDescription(); this.description = widgetsBundle.getDescription();
this.order = widgetsBundle.getOrder();
if (widgetsBundle.getExternalId() != null) { if (widgetsBundle.getExternalId() != null) {
this.externalId = widgetsBundle.getExternalId().getId(); this.externalId = widgetsBundle.getExternalId().getId();
} }
@ -85,6 +89,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity<WidgetsBundle> {
widgetsBundle.setTitle(title); widgetsBundle.setTitle(title);
widgetsBundle.setImage(image); widgetsBundle.setImage(image);
widgetsBundle.setDescription(description); widgetsBundle.setDescription(description);
widgetsBundle.setOrder(order);
if (externalId != null) { if (externalId != null) {
widgetsBundle.setExternalId(new WidgetsBundleId(externalId)); widgetsBundle.setExternalId(new WidgetsBundleId(externalId));
} }

3
dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java

@ -105,7 +105,8 @@ public interface WidgetsBundleRepository extends JpaRepository<WidgetsBundleEnti
"OR :textSearch ILIKE currentTag || ' %' " + "OR :textSearch ILIKE currentTag || ' %' " +
"OR :textSearch ILIKE '% ' || currentTag " + "OR :textSearch ILIKE '% ' || currentTag " +
"OR :textSearch ILIKE '% ' || currentTag || ' %')" + "OR :textSearch ILIKE '% ' || currentTag || ' %')" +
"))))", ")))) " +
"ORDER BY wb.widgets_bundle_order ASC NULLS LAST",
countQuery = "SELECT count(*) FROM widgets_bundle wb WHERE wb.tenant_id IN (:tenantIds) " + countQuery = "SELECT count(*) FROM widgets_bundle wb WHERE wb.tenant_id IN (:tenantIds) " +
"AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " + "AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " +
"OR wb.description ILIKE CONCAT('%', :textSearch, '%') " + "OR wb.description ILIKE CONCAT('%', :textSearch, '%') " +

1
dao/src/main/resources/sql/schema-entities.sql

@ -503,6 +503,7 @@ CREATE TABLE IF NOT EXISTS widgets_bundle (
title varchar(255), title varchar(255),
image varchar(1000000), image varchar(1000000),
description varchar(1024), description varchar(1024),
widgets_bundle_order int,
external_id uuid, external_id uuid,
CONSTRAINT uq_widgets_bundle_alias UNIQUE (tenant_id, alias), CONSTRAINT uq_widgets_bundle_alias UNIQUE (tenant_id, alias),
CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id) CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id)

54
dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java

@ -43,9 +43,6 @@ import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/**
* Created by Valerii Sosliuk on 4/23/2017.
*/
public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
@ -274,7 +271,7 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
createSystemWidgetBundles(2, "WB_SYS_" + i + "_"); createSystemWidgetBundles(2, "WB_SYS_" + i + "_");
} }
widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted(Comparator.comparing(WidgetsBundle::getTitle)).collect(Collectors.toList());; widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted(Comparator.comparing(WidgetsBundle::getTitle)).collect(Collectors.toList());;
assertEquals(100, widgetsBundleDao.find(TenantId.SYS_TENANT_ID).size()); assertEquals(100, widgetsBundles.size());
var widgetType1 = createAndSaveWidgetType(new TenantId(tenantId1), 1, "Test widget type 1", "This is the widget type 1", new String[]{"tag1", "Tag2", "TEST_TAG"}); var widgetType1 = createAndSaveWidgetType(new TenantId(tenantId1), 1, "Test widget type 1", "This is the widget type 1", new String[]{"tag1", "Tag2", "TEST_TAG"});
var widgetType2 = createAndSaveWidgetType(new TenantId(tenantId2), 2, "Test widget type 2", "This is the widget type 2", new String[]{"tag3", "Tag5", "TEST_Tag2"}); var widgetType2 = createAndSaveWidgetType(new TenantId(tenantId2), 2, "Test widget type 2", "This is the widget type 2", new String[]{"tag3", "Tag5", "TEST_Tag2"});
@ -358,6 +355,34 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
widgetTypeList = new ArrayList<>(); widgetTypeList = new ArrayList<>();
} }
@Test
public void testOrderInFindAllWidgetsBundlesByTenantIdFullSearch() {
UUID tenantId1 = Uuids.timeBased();
for (int i = 0; i < 10; i++) {
createWidgetsBundle(TenantId.fromUUID(tenantId1), "WB1_" + i, "WB1_" + (10-i), i % 2 == 1 ? null : (int)(Math.random() * 1000));
createWidgetsBundle(TenantId.SYS_TENANT_ID, "WB_SYS_" + i, "WB_SYS_" + (10-i), i % 2 == 0 ? null : (int)(Math.random() * 1000));
}
widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted((o1, o2) -> {
int result = 0;
if (o1.getOrder() != null && o2.getOrder() != null) {
result = o1.getOrder() - o2.getOrder();
} else if (o1.getOrder() == null && o2.getOrder() != null) {
result = 1;
} else if (o1.getOrder() != null) {
result = -1;
}
if (result == 0) {
result = o1.getTitle().compareTo(o2.getTitle());
}
return result;
}).collect(Collectors.toList());;
assertEquals(20, widgetsBundles.size());
PageLink pageLink = new PageLink(100, 0, "", new SortOrder("title"));
PageData<WidgetsBundle> widgetsBundlesData = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, true, pageLink);
assertEquals(20, widgetsBundlesData.getData().size());
assertEquals(widgetsBundles, widgetsBundlesData.getData());
}
@Test @Test
public void testSearchTextNotFound() { public void testSearchTextNotFound() {
UUID tenantId = Uuids.timeBased(); UUID tenantId = Uuids.timeBased();
@ -372,32 +397,27 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
private void createWidgetBundles(int count, UUID tenantId, String prefix) { private void createWidgetBundles(int count, UUID tenantId, String prefix) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
WidgetsBundle widgetsBundle = new WidgetsBundle(); createWidgetsBundle(TenantId.fromUUID(tenantId), prefix + i, prefix + i, null);
widgetsBundle.setAlias(prefix + i);
widgetsBundle.setTitle(prefix + i);
widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased()));
widgetsBundle.setTenantId(TenantId.fromUUID(tenantId));
widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle);
} }
} }
private void createSystemWidgetBundles(int count, String prefix) { private void createSystemWidgetBundles(int count, String prefix) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
WidgetsBundle widgetsBundle = new WidgetsBundle(); createWidgetsBundle(TenantId.SYS_TENANT_ID, prefix + i, prefix + i, null);
widgetsBundle.setAlias(prefix + i);
widgetsBundle.setTitle(prefix + i);
widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID);
widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased()));
widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle);
} }
} }
private WidgetsBundle createSystemWidgetBundle(String alias, String title) { private WidgetsBundle createSystemWidgetBundle(String alias, String title) {
return createWidgetsBundle(TenantId.SYS_TENANT_ID, alias, title, null);
}
private WidgetsBundle createWidgetsBundle(TenantId tenantId, String alias, String title, Integer order) {
WidgetsBundle widgetsBundle = new WidgetsBundle(); WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setAlias(alias); widgetsBundle.setAlias(alias);
widgetsBundle.setTitle(title); widgetsBundle.setTitle(title);
widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID); widgetsBundle.setTenantId(tenantId);
widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased()));
widgetsBundle.setOrder(order);
return widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle); return widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle);
} }

4
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html

@ -58,6 +58,10 @@
<textarea matInput formControlName="description" rows="2" maxlength="1024" #descriptionInput></textarea> <textarea matInput formControlName="description" rows="2" maxlength="1024" #descriptionInput></textarea>
<mat-hint align="end">{{descriptionInput.value?.length || 0}}/1024</mat-hint> <mat-hint align="end">{{descriptionInput.value?.length || 0}}/1024</mat-hint>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>widgets-bundle.order</mat-label>
<input matInput type="number" step="1" formControlName="order">
</mat-form-field>
</fieldset> </fieldset>
</form> </form>
</div> </div>

6
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts

@ -50,7 +50,8 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
{ {
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
image: [entity ? entity.image : ''], image: [entity ? entity.image : ''],
description: [entity ? entity.description : '', Validators.maxLength(1024)] description: [entity ? entity.description : '', Validators.maxLength(1024)],
order: [entity ? entity.order : null]
} }
); );
} }
@ -59,7 +60,8 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
this.entityForm.patchValue({ this.entityForm.patchValue({
title: entity.title, title: entity.title,
image: entity.image, image: entity.image,
description: entity.description description: entity.description,
order: entity.order
}); });
} }
} }

1
ui-ngx/src/app/shared/models/widgets-bundle.model.ts

@ -24,4 +24,5 @@ export interface WidgetsBundle extends BaseData<WidgetsBundleId>, ExportableEnti
title: string; title: string;
image: string; image: string;
description: string; description: string;
order: number;
} }

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4911,6 +4911,7 @@
"title-max-length": "Title should be less than 256", "title-max-length": "Title should be less than 256",
"description": "Description", "description": "Description",
"image-preview": "Image preview", "image-preview": "Image preview",
"order": "Order",
"add-widgets-bundle-text": "Add new widgets bundle", "add-widgets-bundle-text": "Add new widgets bundle",
"no-widgets-bundles-text": "No widgets bundles found", "no-widgets-bundles-text": "No widgets bundles found",
"empty": "Widgets bundle is empty", "empty": "Widgets bundle is empty",

Loading…
Cancel
Save