Commit 8e0eab37a8b5832c304c7475f021653b36e84cf1
Committed by
GitHub
Merge pull request #5474 from ViacheslavKlimov/sysadmin-entities-search
[3.3.3] Entities search within all tenants
Showing
11 changed files
with
532 additions
and
39 deletions
... | ... | @@ -17,9 +17,10 @@ package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import io.swagger.annotations.ApiOperation; |
19 | 19 | import io.swagger.annotations.ApiParam; |
20 | -import org.springframework.beans.factory.annotation.Autowired; | |
20 | +import lombok.RequiredArgsConstructor; | |
21 | 21 | import org.springframework.http.ResponseEntity; |
22 | 22 | import org.springframework.security.access.prepost.PreAuthorize; |
23 | +import org.springframework.web.bind.annotation.PostMapping; | |
23 | 24 | import org.springframework.web.bind.annotation.RequestBody; |
24 | 25 | import org.springframework.web.bind.annotation.RequestMapping; |
25 | 26 | import org.springframework.web.bind.annotation.RequestMethod; |
... | ... | @@ -36,20 +37,30 @@ import org.thingsboard.server.common.data.query.EntityCountQuery; |
36 | 37 | import org.thingsboard.server.common.data.query.EntityData; |
37 | 38 | import org.thingsboard.server.common.data.query.EntityDataPageLink; |
38 | 39 | import org.thingsboard.server.common.data.query.EntityDataQuery; |
40 | +import org.thingsboard.server.data.search.EntitiesSearchRequest; | |
41 | +import org.thingsboard.server.data.search.EntitySearchResult; | |
39 | 42 | import org.thingsboard.server.queue.util.TbCoreComponent; |
43 | +import org.thingsboard.server.service.query.EntitiesSearchService; | |
40 | 44 | import org.thingsboard.server.service.query.EntityQueryService; |
41 | 45 | |
42 | 46 | import static org.thingsboard.server.controller.ControllerConstants.ALARM_DATA_QUERY_DESCRIPTION; |
43 | 47 | import static org.thingsboard.server.controller.ControllerConstants.ENTITY_COUNT_QUERY_DESCRIPTION; |
44 | 48 | import static org.thingsboard.server.controller.ControllerConstants.ENTITY_DATA_QUERY_DESCRIPTION; |
49 | +import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; | |
50 | +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; | |
51 | +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; | |
52 | +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; | |
53 | +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; | |
54 | +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; | |
45 | 55 | |
46 | 56 | @RestController |
47 | 57 | @TbCoreComponent |
48 | 58 | @RequestMapping("/api") |
59 | +@RequiredArgsConstructor | |
49 | 60 | public class EntityQueryController extends BaseController { |
50 | 61 | |
51 | - @Autowired | |
52 | - private EntityQueryService entityQueryService; | |
62 | + private final EntityQueryService entityQueryService; | |
63 | + private final EntitiesSearchService entitiesSearchService; | |
53 | 64 | |
54 | 65 | private static final int MAX_PAGE_SIZE = 100; |
55 | 66 | |
... | ... | @@ -123,4 +134,70 @@ public class EntityQueryController extends BaseController { |
123 | 134 | } |
124 | 135 | } |
125 | 136 | |
137 | + @ApiOperation(value = "Search entities (searchEntities)", notes = "Search entities with specified entity type by id or name within the whole platform. " + | |
138 | + "Searchable entity types are: CUSTOMER, USER, DEVICE, DEVICE_PROFILE, ASSET, ENTITY_VIEW, DASHBOARD, " + | |
139 | + "RULE_CHAIN, EDGE, OTA_PACKAGE, TB_RESOURCE, WIDGETS_BUNDLE, TENANT, TENANT_PROFILE." + NEW_LINE + | |
140 | + "The platform will search for entities, where a name contains the search text (case-insensitively), " + | |
141 | + "or if the search query is a valid UUID (e.g. 128e4d40-26b3-11ec-aaeb-c7661c54701e) then " + | |
142 | + "it will also search for an entity where id fully matches the query. If search query is empty " + | |
143 | + "then all entities will be returned (according to page number, page size and sorting)." + NEW_LINE + | |
144 | + "The returned result is a page of EntitySearchResult, which contains: " + | |
145 | + "entity id, entity fields represented as strings, tenant info and owner info. " + | |
146 | + "Returned entity fields are: name, type (will be present for USER, DEVICE, ASSET, ENTITY_VIEW, RULE_CHAIN, " + | |
147 | + "EDGE, OTA_PACKAGE, TB_RESOURCE entity types; in case of USER - the type is its authority), " + | |
148 | + "createdTime and lastActivityTime (will only be present for DEVICE and USER; for USER it is its last login time). " + | |
149 | + "Tenant info contains tenant's id and title; owner info contains the same info for an entity's owner " + | |
150 | + "(its customer, or if it is not a customer's entity - tenant)." + NEW_LINE + | |
151 | + "Example response value:\n" + | |
152 | + "{\n" + | |
153 | + " \"data\": [\n" + | |
154 | + " {\n" + | |
155 | + " \"entityId\": {\n" + | |
156 | + " \"entityType\": \"DEVICE\",\n" + | |
157 | + " \"id\": \"48be0670-25c9-11ec-a618-8165eb6b112a\"\n" + | |
158 | + " },\n" + | |
159 | + " \"fields\": {\n" + | |
160 | + " \"name\": \"Thermostat T1\",\n" + | |
161 | + " \"createdTime\": \"1633430698071\",\n" + | |
162 | + " \"lastActivityTime\": \"1635761085285\",\n" + | |
163 | + " \"type\": \"thermostat\"\n" + | |
164 | + " },\n" + | |
165 | + " \"tenantInfo\": {\n" + | |
166 | + " \"id\": {\n" + | |
167 | + " \"entityType\": \"TENANT\",\n" + | |
168 | + " \"id\": \"2ddd6120-25c9-11ec-a618-8165eb6b112a\"\n" + | |
169 | + " },\n" + | |
170 | + " \"name\": \"Tenant\"\n" + | |
171 | + " },\n" + | |
172 | + " \"ownerInfo\": {\n" + | |
173 | + " \"id\": {\n" + | |
174 | + " \"entityType\": \"CUSTOMER\",\n" + | |
175 | + " \"id\": \"26cba800-eee3-11eb-9e2c-fb031bd4619c\"\n" + | |
176 | + " },\n" + | |
177 | + " \"name\": \"Customer A\"\n" + | |
178 | + " }\n" + | |
179 | + " }\n" + | |
180 | + " ],\n" + | |
181 | + " \"totalPages\": 1,\n" + | |
182 | + " \"totalElements\": 1,\n" + | |
183 | + " \"hasNext\": false\n" + | |
184 | + "}") | |
185 | + @PostMapping("/entities/search") | |
186 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") | |
187 | + public PageData<EntitySearchResult> searchEntities(@RequestBody EntitiesSearchRequest request, | |
188 | + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) | |
189 | + @RequestParam int page, | |
190 | + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) | |
191 | + @RequestParam int pageSize, | |
192 | + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = "name, type, createdTime, lastActivityTime, createdTime, tenantId, customerId", required = false) | |
193 | + @RequestParam(required = false) String sortProperty, | |
194 | + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES, required = false) | |
195 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
196 | + try { | |
197 | + return entitiesSearchService.searchEntities(getCurrentUser(), request, createPageLink(pageSize, page, null, sortProperty, sortOrder)); | |
198 | + } catch (Exception e) { | |
199 | + throw handleException(e); | |
200 | + } | |
201 | + } | |
202 | + | |
126 | 203 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.data.search; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.EntityType; | |
20 | + | |
21 | +@Data | |
22 | +public class EntitiesSearchRequest { | |
23 | + private EntityType entityType; | |
24 | + private String searchQuery; | |
25 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.data.search; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Data; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import org.thingsboard.server.common.data.id.EntityId; | |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
23 | + | |
24 | +import java.util.Map; | |
25 | + | |
26 | +@Data | |
27 | +public class EntitySearchResult { | |
28 | + private EntityId entityId; | |
29 | + private Map<String, String> fields; | |
30 | + | |
31 | + private EntityTenantInfo tenantInfo; | |
32 | + private EntityOwnerInfo ownerInfo; | |
33 | + | |
34 | + @Data | |
35 | + @AllArgsConstructor | |
36 | + @NoArgsConstructor | |
37 | + public static final class EntityTenantInfo { | |
38 | + private TenantId id; | |
39 | + private String name; | |
40 | + } | |
41 | + | |
42 | + @Data | |
43 | + @AllArgsConstructor | |
44 | + @NoArgsConstructor | |
45 | + public static final class EntityOwnerInfo { | |
46 | + private EntityId id; | |
47 | + private String name; | |
48 | + } | |
49 | + | |
50 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/query/EntitiesSearchService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.query; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.page.PageData; | |
19 | +import org.thingsboard.server.common.data.page.PageLink; | |
20 | +import org.thingsboard.server.data.search.EntitiesSearchRequest; | |
21 | +import org.thingsboard.server.data.search.EntitySearchResult; | |
22 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
23 | + | |
24 | +public interface EntitiesSearchService { | |
25 | + PageData<EntitySearchResult> searchEntities(SecurityUser user, EntitiesSearchRequest request, PageLink pageLink); | |
26 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/query/EntitiesSearchServiceImpl.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.query; | |
17 | + | |
18 | +import com.google.common.base.Strings; | |
19 | +import lombok.RequiredArgsConstructor; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
21 | +import org.apache.commons.lang3.StringUtils; | |
22 | +import org.springframework.stereotype.Service; | |
23 | +import org.thingsboard.server.common.data.ContactBased; | |
24 | +import org.thingsboard.server.common.data.Customer; | |
25 | +import org.thingsboard.server.common.data.EntityType; | |
26 | +import org.thingsboard.server.common.data.Tenant; | |
27 | +import org.thingsboard.server.common.data.id.CustomerId; | |
28 | +import org.thingsboard.server.common.data.id.EntityId; | |
29 | +import org.thingsboard.server.common.data.id.TenantId; | |
30 | +import org.thingsboard.server.common.data.page.PageData; | |
31 | +import org.thingsboard.server.common.data.page.PageLink; | |
32 | +import org.thingsboard.server.common.data.page.SortOrder; | |
33 | +import org.thingsboard.server.common.data.query.EntityData; | |
34 | +import org.thingsboard.server.common.data.query.EntityDataPageLink; | |
35 | +import org.thingsboard.server.common.data.query.EntityDataQuery; | |
36 | +import org.thingsboard.server.common.data.query.EntityDataSortOrder; | |
37 | +import org.thingsboard.server.common.data.query.EntityKey; | |
38 | +import org.thingsboard.server.common.data.query.EntityKeyType; | |
39 | +import org.thingsboard.server.common.data.query.EntityNameOrIdFilter; | |
40 | +import org.thingsboard.server.dao.customer.CustomerService; | |
41 | +import org.thingsboard.server.dao.tenant.TenantService; | |
42 | +import org.thingsboard.server.data.search.EntitiesSearchRequest; | |
43 | +import org.thingsboard.server.data.search.EntitySearchResult; | |
44 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
45 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
46 | +import org.thingsboard.server.service.state.DefaultDeviceStateService; | |
47 | + | |
48 | +import java.util.ArrayList; | |
49 | +import java.util.Collections; | |
50 | +import java.util.EnumSet; | |
51 | +import java.util.HashMap; | |
52 | +import java.util.List; | |
53 | +import java.util.Map; | |
54 | +import java.util.Optional; | |
55 | +import java.util.Set; | |
56 | +import java.util.UUID; | |
57 | +import java.util.stream.Collectors; | |
58 | +import java.util.stream.Stream; | |
59 | + | |
60 | +import static org.thingsboard.server.common.data.EntityType.ASSET; | |
61 | +import static org.thingsboard.server.common.data.EntityType.CUSTOMER; | |
62 | +import static org.thingsboard.server.common.data.EntityType.DASHBOARD; | |
63 | +import static org.thingsboard.server.common.data.EntityType.DEVICE; | |
64 | +import static org.thingsboard.server.common.data.EntityType.DEVICE_PROFILE; | |
65 | +import static org.thingsboard.server.common.data.EntityType.EDGE; | |
66 | +import static org.thingsboard.server.common.data.EntityType.ENTITY_VIEW; | |
67 | +import static org.thingsboard.server.common.data.EntityType.OTA_PACKAGE; | |
68 | +import static org.thingsboard.server.common.data.EntityType.RULE_CHAIN; | |
69 | +import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE; | |
70 | +import static org.thingsboard.server.common.data.EntityType.TENANT; | |
71 | +import static org.thingsboard.server.common.data.EntityType.TENANT_PROFILE; | |
72 | +import static org.thingsboard.server.common.data.EntityType.USER; | |
73 | +import static org.thingsboard.server.common.data.EntityType.WIDGETS_BUNDLE; | |
74 | +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; | |
75 | +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CUSTOMER_ID; | |
76 | +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.LAST_ACTIVITY_TIME; | |
77 | +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.NAME; | |
78 | +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.TENANT_ID; | |
79 | +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.TYPE; | |
80 | + | |
81 | +@Service | |
82 | +@TbCoreComponent | |
83 | +@RequiredArgsConstructor | |
84 | +@Slf4j | |
85 | +public class EntitiesSearchServiceImpl implements EntitiesSearchService { | |
86 | + private final EntityQueryService entityQueryService; | |
87 | + | |
88 | + private final TenantService tenantService; | |
89 | + private final CustomerService customerService; | |
90 | + private final DefaultDeviceStateService deviceStateService; | |
91 | + | |
92 | + private static final List<EntityKey> entityResponseFields = Stream.of(CREATED_TIME, NAME, TYPE, TENANT_ID, CUSTOMER_ID) | |
93 | + .map(field -> new EntityKey(EntityKeyType.ENTITY_FIELD, field)) | |
94 | + .collect(Collectors.toList()); | |
95 | + | |
96 | + private static final Set<EntityType> searchableEntityTypes = EnumSet.of( | |
97 | + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, RULE_CHAIN, ENTITY_VIEW, | |
98 | + WIDGETS_BUNDLE, TENANT_PROFILE, DEVICE_PROFILE, TB_RESOURCE, OTA_PACKAGE, EDGE | |
99 | + ); | |
100 | + | |
101 | + @Override | |
102 | + public PageData<EntitySearchResult> searchEntities(SecurityUser user, EntitiesSearchRequest request, PageLink pageLink) { | |
103 | + EntityType entityType = request.getEntityType(); | |
104 | + if (!searchableEntityTypes.contains(entityType)) { | |
105 | + return new PageData<>(); | |
106 | + } | |
107 | + | |
108 | + EntityDataQuery query = createSearchQuery(request.getSearchQuery(), entityType, pageLink); | |
109 | + PageData<EntityData> resultPage = entityQueryService.findEntityDataByQuery(user, query); | |
110 | + | |
111 | + Map<EntityId, ContactBased<? extends EntityId>> localOwnersCache = new HashMap<>(); | |
112 | + return resultPage.mapData(entityData -> { | |
113 | + Map<String, String> fields = new HashMap<>(); | |
114 | + entityData.getLatest().values().stream() | |
115 | + .flatMap(values -> values.entrySet().stream()) | |
116 | + .forEach(entry -> fields.put(entry.getKey(), Strings.emptyToNull(entry.getValue().getValue()))); | |
117 | + | |
118 | + EntitySearchResult entitySearchResult = new EntitySearchResult(); | |
119 | + | |
120 | + entitySearchResult.setEntityId(entityData.getEntityId()); | |
121 | + entitySearchResult.setFields(fields); | |
122 | + setOwnerInfo(entitySearchResult, localOwnersCache); | |
123 | + | |
124 | + return entitySearchResult; | |
125 | + }); | |
126 | + } | |
127 | + | |
128 | + private EntityDataQuery createSearchQuery(String searchQuery, EntityType entityType, PageLink pageLink) { | |
129 | + EntityDataPageLink entityDataPageLink = new EntityDataPageLink(); | |
130 | + entityDataPageLink.setPageSize(pageLink.getPageSize()); | |
131 | + entityDataPageLink.setPage(pageLink.getPage()); | |
132 | + if (pageLink.getSortOrder() != null && StringUtils.isNotEmpty(pageLink.getSortOrder().getProperty())) { | |
133 | + entityDataPageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, pageLink.getSortOrder().getProperty()), | |
134 | + EntityDataSortOrder.Direction.valueOf(Optional.ofNullable(pageLink.getSortOrder().getDirection()).orElse(SortOrder.Direction.ASC).name()))); | |
135 | + } | |
136 | + | |
137 | + EntityNameOrIdFilter filter = new EntityNameOrIdFilter(); | |
138 | + filter.setEntityType(entityType); | |
139 | + filter.setNameOrId(searchQuery); | |
140 | + | |
141 | + List<EntityKey> entityFields = entityResponseFields; | |
142 | + List<EntityKey> latestValues = Collections.emptyList(); | |
143 | + | |
144 | + if (entityType == USER) { | |
145 | + entityFields = new ArrayList<>(entityFields); | |
146 | + entityFields.add(new EntityKey(EntityKeyType.ENTITY_FIELD, LAST_ACTIVITY_TIME)); | |
147 | + } else if (entityType == DEVICE) { | |
148 | + EntityKey lastActivityTimeKey; | |
149 | + if (deviceStateService.isPersistToTelemetry()) { | |
150 | + lastActivityTimeKey = new EntityKey(EntityKeyType.TIME_SERIES, LAST_ACTIVITY_TIME); | |
151 | + } else { | |
152 | + lastActivityTimeKey = new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, LAST_ACTIVITY_TIME); | |
153 | + } | |
154 | + latestValues = List.of(lastActivityTimeKey); | |
155 | + if (entityDataPageLink.getSortOrder() != null && entityDataPageLink.getSortOrder().getKey().getKey().equals(LAST_ACTIVITY_TIME)) { | |
156 | + entityDataPageLink.getSortOrder().setKey(lastActivityTimeKey); | |
157 | + } | |
158 | + } | |
159 | + | |
160 | + return new EntityDataQuery(filter, entityDataPageLink, entityFields, latestValues, Collections.emptyList()); | |
161 | + } | |
162 | + | |
163 | + private void setOwnerInfo(EntitySearchResult entitySearchResult, Map<EntityId, ContactBased<? extends EntityId>> localOwnersCache) { | |
164 | + Map<String, String> fields = entitySearchResult.getFields(); | |
165 | + | |
166 | + UUID tenantUuid = toUuid(fields.remove(TENANT_ID)); | |
167 | + UUID customerUuid = toUuid(fields.remove(CUSTOMER_ID)); | |
168 | + | |
169 | + Tenant tenant = null; | |
170 | + if (tenantUuid != null) { | |
171 | + tenant = getTenant(new TenantId(tenantUuid), localOwnersCache); | |
172 | + } | |
173 | + | |
174 | + ContactBased<? extends EntityId> owner; | |
175 | + if (customerUuid != null) { | |
176 | + owner = getCustomer(new CustomerId(customerUuid), localOwnersCache); | |
177 | + } else { | |
178 | + owner = tenant; | |
179 | + } | |
180 | + | |
181 | + if (tenant != null) { | |
182 | + entitySearchResult.setTenantInfo(new EntitySearchResult.EntityTenantInfo(tenant.getId(), tenant.getName())); | |
183 | + } | |
184 | + if (owner != null) { | |
185 | + entitySearchResult.setOwnerInfo(new EntitySearchResult.EntityOwnerInfo(owner.getId(), owner.getName())); | |
186 | + } | |
187 | + } | |
188 | + | |
189 | + private Tenant getTenant(TenantId tenantId, Map<EntityId, ContactBased<? extends EntityId>> localOwnersCache) { | |
190 | + return (Tenant) localOwnersCache.computeIfAbsent(tenantId, id -> tenantService.findTenantById(tenantId)); | |
191 | + } | |
192 | + | |
193 | + private Customer getCustomer(CustomerId customerId, Map<EntityId, ContactBased<? extends EntityId>> localOwnersCache) { | |
194 | + return (Customer) localOwnersCache.computeIfAbsent(customerId, id -> customerService.findCustomerById(TenantId.SYS_TENANT_ID, customerId)); | |
195 | + } | |
196 | + | |
197 | + private UUID toUuid(String uuid) { | |
198 | + try { | |
199 | + UUID id = UUID.fromString(uuid); | |
200 | + if (!id.equals(EntityId.NULL_UUID)) { | |
201 | + return id; | |
202 | + } | |
203 | + } catch (Exception ignored) {} | |
204 | + | |
205 | + return null; | |
206 | + } | |
207 | + | |
208 | +} | ... | ... |
... | ... | @@ -69,5 +69,6 @@ public interface PermissionChecker<I extends EntityId, T extends HasTenantId> { |
69 | 69 | } |
70 | 70 | }; |
71 | 71 | |
72 | + PermissionChecker allowReadPermissionChecker = new GenericPermissionChecker(Operation.READ, Operation.READ_TELEMETRY, Operation.READ_ATTRIBUTES); | |
72 | 73 | |
73 | 74 | } | ... | ... |
... | ... | @@ -17,10 +17,7 @@ package org.thingsboard.server.service.security.permission; |
17 | 17 | |
18 | 18 | import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.data.HasTenantId; |
20 | -import org.thingsboard.server.common.data.User; | |
21 | 20 | import org.thingsboard.server.common.data.id.EntityId; |
22 | -import org.thingsboard.server.common.data.id.UserId; | |
23 | -import org.thingsboard.server.common.data.security.Authority; | |
24 | 21 | import org.thingsboard.server.service.security.model.SecurityUser; |
25 | 22 | |
26 | 23 | @Component(value="sysAdminPermissions") |
... | ... | @@ -29,23 +26,29 @@ public class SysAdminPermissions extends AbstractPermissions { |
29 | 26 | public SysAdminPermissions() { |
30 | 27 | super(); |
31 | 28 | put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker); |
32 | - put(Resource.DASHBOARD, new PermissionChecker.GenericPermissionChecker(Operation.READ)); | |
29 | + put(Resource.DASHBOARD, PermissionChecker.allowReadPermissionChecker); | |
33 | 30 | put(Resource.TENANT, PermissionChecker.allowAllPermissionChecker); |
34 | - put(Resource.RULE_CHAIN, systemEntityPermissionChecker); | |
35 | - put(Resource.USER, userPermissionChecker); | |
31 | + put(Resource.RULE_CHAIN, PermissionChecker.allowReadPermissionChecker); | |
32 | + put(Resource.USER, PermissionChecker.allowAllPermissionChecker); | |
36 | 33 | put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); |
37 | 34 | put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); |
38 | 35 | put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker); |
39 | 36 | put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); |
40 | 37 | put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker); |
41 | - put(Resource.TB_RESOURCE, systemEntityPermissionChecker); | |
38 | + put(Resource.TB_RESOURCE, PermissionChecker.allowAllPermissionChecker); | |
39 | + put(Resource.CUSTOMER, PermissionChecker.allowReadPermissionChecker); | |
40 | + put(Resource.ASSET, PermissionChecker.allowReadPermissionChecker); | |
41 | + put(Resource.DEVICE, PermissionChecker.allowReadPermissionChecker); | |
42 | + put(Resource.ENTITY_VIEW, PermissionChecker.allowReadPermissionChecker); | |
43 | + put(Resource.DEVICE_PROFILE, PermissionChecker.allowReadPermissionChecker); | |
44 | + put(Resource.OTA_PACKAGE, PermissionChecker.allowReadPermissionChecker); | |
45 | + put(Resource.EDGE, PermissionChecker.allowReadPermissionChecker); | |
42 | 46 | } |
43 | 47 | |
44 | 48 | private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { |
45 | 49 | |
46 | 50 | @Override |
47 | 51 | public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { |
48 | - | |
49 | 52 | if (entity.getTenantId() != null && !entity.getTenantId().isNullUid()) { |
50 | 53 | return false; |
51 | 54 | } |
... | ... | @@ -53,16 +56,4 @@ public class SysAdminPermissions extends AbstractPermissions { |
53 | 56 | } |
54 | 57 | }; |
55 | 58 | |
56 | - private static final PermissionChecker userPermissionChecker = new PermissionChecker<UserId, User>() { | |
57 | - | |
58 | - @Override | |
59 | - public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) { | |
60 | - if (Authority.CUSTOMER_USER.equals(userEntity.getAuthority())) { | |
61 | - return false; | |
62 | - } | |
63 | - return true; | |
64 | - } | |
65 | - | |
66 | - }; | |
67 | - | |
68 | 59 | } | ... | ... |
... | ... | @@ -29,7 +29,8 @@ public enum EntityFilterType { |
29 | 29 | DEVICE_SEARCH_QUERY("deviceSearchQuery"), |
30 | 30 | ENTITY_VIEW_SEARCH_QUERY("entityViewSearchQuery"), |
31 | 31 | EDGE_SEARCH_QUERY("edgeSearchQuery"), |
32 | - API_USAGE_STATE("apiUsageState"); | |
32 | + API_USAGE_STATE("apiUsageState"), | |
33 | + ENTITY_NAME_OR_ID("entityNameOrId"); | |
33 | 34 | |
34 | 35 | private final String label; |
35 | 36 | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/query/EntityNameOrIdFilter.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.query; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.thingsboard.server.common.data.EntityType; | |
21 | + | |
22 | +@EqualsAndHashCode(callSuper = true) | |
23 | +@Data | |
24 | +public class EntityNameOrIdFilter extends EntitySearchQueryFilter { | |
25 | + private String nameOrId; | |
26 | + private EntityType entityType; | |
27 | + | |
28 | + @Override | |
29 | + public EntityFilterType getType() { | |
30 | + return EntityFilterType.ENTITY_NAME_OR_ID; | |
31 | + } | |
32 | +} | ... | ... |
... | ... | @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityFilterType; |
44 | 44 | import org.thingsboard.server.common.data.query.EntityKeyType; |
45 | 45 | import org.thingsboard.server.common.data.query.EntityListFilter; |
46 | 46 | import org.thingsboard.server.common.data.query.EntityNameFilter; |
47 | +import org.thingsboard.server.common.data.query.EntityNameOrIdFilter; | |
47 | 48 | import org.thingsboard.server.common.data.query.EntitySearchQueryFilter; |
48 | 49 | import org.thingsboard.server.common.data.query.EntityTypeFilter; |
49 | 50 | import org.thingsboard.server.common.data.query.EntityViewSearchQueryFilter; |
... | ... | @@ -236,6 +237,12 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
236 | 237 | entityTableMap.put(EntityType.TENANT, "tenant"); |
237 | 238 | entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE); |
238 | 239 | entityTableMap.put(EntityType.EDGE, "edge"); |
240 | + entityTableMap.put(EntityType.RULE_CHAIN, "rule_chain"); | |
241 | + entityTableMap.put(EntityType.WIDGETS_BUNDLE, "widgets_bundle"); | |
242 | + entityTableMap.put(EntityType.TENANT_PROFILE, "tenant_profile"); | |
243 | + entityTableMap.put(EntityType.DEVICE_PROFILE, "device_profile"); | |
244 | + entityTableMap.put(EntityType.TB_RESOURCE, "resource"); | |
245 | + entityTableMap.put(EntityType.OTA_PACKAGE, "ota_package"); | |
239 | 246 | } |
240 | 247 | |
241 | 248 | public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ |
... | ... | @@ -441,7 +448,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
441 | 448 | Optional<EntityKeyMapping> sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst(); |
442 | 449 | if (sortOrderMappingOpt.isPresent()) { |
443 | 450 | EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get(); |
444 | - String direction = sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC ? "asc" : "desc"; | |
451 | + String direction = sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC ? "asc" : "desc nulls last"; | |
445 | 452 | if (sortOrderMapping.getEntityKey().getType() == EntityKeyType.ENTITY_FIELD) { |
446 | 453 | dataQuery = String.format("%s order by %s %s, result.id %s", dataQuery, sortOrderMapping.getValueAlias(), direction, direction); |
447 | 454 | } else { |
... | ... | @@ -471,15 +478,26 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
471 | 478 | String entityFieldsQuery = EntityKeyMapping.buildQuery(ctx, entityFieldsFilters, entityFilter.getType()); |
472 | 479 | String result = permissionQuery; |
473 | 480 | if (!entityFilterQuery.isEmpty()) { |
474 | - result += " and (" + entityFilterQuery + ")"; | |
481 | + if (!result.isEmpty()) { | |
482 | + result += " and (" + entityFilterQuery + ")"; | |
483 | + } else { | |
484 | + result = "(" + entityFilterQuery + ")"; | |
485 | + } | |
475 | 486 | } |
476 | 487 | if (!entityFieldsQuery.isEmpty()) { |
477 | - result += " and (" + entityFieldsQuery + ")"; | |
488 | + if (!result.isEmpty()) { | |
489 | + result += " and (" + entityFieldsQuery + ")"; | |
490 | + } else { | |
491 | + result = "(" + entityFieldsQuery + ")"; | |
492 | + } | |
478 | 493 | } |
479 | 494 | return result; |
480 | 495 | } |
481 | 496 | |
482 | 497 | private String buildPermissionQuery(QueryContext ctx, EntityFilter entityFilter) { |
498 | + if (ctx.getTenantId().equals(TenantId.SYS_TENANT_ID)) { | |
499 | + return ""; | |
500 | + } | |
483 | 501 | switch (entityFilter.getType()) { |
484 | 502 | case RELATIONS_QUERY: |
485 | 503 | case DEVICE_SEARCH_QUERY: |
... | ... | @@ -548,6 +566,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
548 | 566 | case API_USAGE_STATE: |
549 | 567 | case ENTITY_TYPE: |
550 | 568 | return ""; |
569 | + case ENTITY_NAME_OR_ID: | |
570 | + return entityNameOrIdQuery(ctx, (EntityNameOrIdFilter) entityFilter); | |
551 | 571 | default: |
552 | 572 | throw new RuntimeException("Not implemented!"); |
553 | 573 | } |
... | ... | @@ -759,7 +779,27 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
759 | 779 | |
760 | 780 | private String entityNameQuery(QueryContext ctx, EntityNameFilter filter) { |
761 | 781 | ctx.addStringParameter("entity_filter_name_filter", filter.getEntityNameFilter()); |
762 | - return "lower(e.search_text) like lower(concat(:entity_filter_name_filter, '%%'))"; | |
782 | + return "lower(e.search_text) like lower(concat('%', :entity_filter_name_filter, '%'))"; | |
783 | + } | |
784 | + | |
785 | + private String entityNameOrIdQuery(QueryContext ctx, EntityNameOrIdFilter filter) { | |
786 | + String nameOrId = filter.getNameOrId(); | |
787 | + if (StringUtils.isNotEmpty(nameOrId)) { | |
788 | + nameOrId = nameOrId.replaceAll("%", "\\\\%").replaceAll("_", "\\\\_"); | |
789 | + ctx.addStringParameter("entity_id_or_search_text_filter", nameOrId); | |
790 | + String query = ""; | |
791 | + | |
792 | + String searchTextField = EntityKeyMapping.searchTextFields.get(filter.getEntityType()); | |
793 | + query += "lower(e." + searchTextField + ") like lower(concat('%', :entity_id_or_search_text_filter, '%'))"; | |
794 | + | |
795 | + try { | |
796 | + UUID.fromString(nameOrId); | |
797 | + query += " or e.id = :entity_id_or_search_text_filter::uuid"; | |
798 | + } catch (Exception ignored) {} | |
799 | + | |
800 | + return query; | |
801 | + } | |
802 | + return "true"; | |
763 | 803 | } |
764 | 804 | |
765 | 805 | private String typeQuery(QueryContext ctx, EntityFilter filter) { |
... | ... | @@ -787,7 +827,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
787 | 827 | } |
788 | 828 | ctx.addStringParameter("entity_filter_type_query_type", type); |
789 | 829 | ctx.addStringParameter("entity_filter_type_query_name", name); |
790 | - return "e.type = :entity_filter_type_query_type and lower(e.search_text) like lower(concat(:entity_filter_type_query_name, '%%'))"; | |
830 | + return "e.type = :entity_filter_type_query_type and lower(e.search_text) like lower(concat('%', :entity_filter_type_query_name, '%'))"; | |
791 | 831 | } |
792 | 832 | |
793 | 833 | private EntityType resolveEntityType(EntityFilter entityFilter) { |
... | ... | @@ -816,6 +856,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
816 | 856 | return ((RelationsQueryFilter) entityFilter).getRootEntity().getEntityType(); |
817 | 857 | case API_USAGE_STATE: |
818 | 858 | return EntityType.API_USAGE_STATE; |
859 | + case ENTITY_NAME_OR_ID: | |
860 | + return ((EntityNameOrIdFilter) entityFilter).getEntityType(); | |
819 | 861 | default: |
820 | 862 | throw new RuntimeException("Not implemented!"); |
821 | 863 | } | ... | ... |
... | ... | @@ -38,6 +38,7 @@ import org.thingsboard.server.dao.model.ModelConstants; |
38 | 38 | import java.util.ArrayList; |
39 | 39 | import java.util.Arrays; |
40 | 40 | import java.util.Collections; |
41 | +import java.util.EnumMap; | |
41 | 42 | import java.util.HashMap; |
42 | 43 | import java.util.HashSet; |
43 | 44 | import java.util.List; |
... | ... | @@ -53,6 +54,8 @@ public class EntityKeyMapping { |
53 | 54 | private static final Map<EntityType, Set<String>> allowedEntityFieldMap = new HashMap<>(); |
54 | 55 | private static final Map<String, String> entityFieldColumnMap = new HashMap<>(); |
55 | 56 | private static final Map<EntityType, Map<String, String>> aliases = new HashMap<>(); |
57 | + private static final Map<EntityType, Map<String, String>> propertiesFunctions = new EnumMap<>(EntityType.class); | |
58 | + public static final Map<EntityType, String> searchTextFields = new EnumMap<>(EntityType.class); | |
56 | 59 | |
57 | 60 | public static final String CREATED_TIME = "createdTime"; |
58 | 61 | public static final String ENTITY_TYPE = "entityType"; |
... | ... | @@ -72,35 +75,51 @@ public class EntityKeyMapping { |
72 | 75 | public static final String ZIP = "zip"; |
73 | 76 | public static final String PHONE = "phone"; |
74 | 77 | public static final String ADDITIONAL_INFO = "additionalInfo"; |
78 | + public static final String TENANT_ID = "tenantId"; | |
79 | + public static final String CUSTOMER_ID = "customerId"; | |
80 | + public static final String AUTHORITY = "authority"; | |
81 | + public static final String RESOURCE_TYPE = "resourceType"; | |
82 | + public static final String LAST_ACTIVITY_TIME = "lastActivityTime"; | |
75 | 83 | |
76 | - public static final List<String> typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO); | |
77 | - public static final List<String> widgetEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME); | |
84 | + public static final List<String> typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO, TENANT_ID); | |
78 | 85 | public static final List<String> commonEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, ADDITIONAL_INFO); |
79 | - public static final List<String> dashboardEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, TITLE); | |
80 | - public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); | |
86 | + | |
87 | + public static final List<String> dashboardEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, TITLE, TENANT_ID); | |
88 | + public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO, TENANT_ID, CUSTOMER_ID); | |
81 | 89 | public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); |
82 | 90 | |
83 | - public static final Set<String> apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME)); | |
91 | + public static final Set<String> apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME)); | |
84 | 92 | public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields); |
85 | 93 | public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO)); |
86 | 94 | |
87 | 95 | static { |
88 | 96 | allowedEntityFieldMap.put(EntityType.DEVICE, new HashSet<>(labeledEntityFields)); |
89 | 97 | allowedEntityFieldMap.put(EntityType.ASSET, new HashSet<>(labeledEntityFields)); |
98 | + allowedEntityFieldMap.put(EntityType.EDGE, new HashSet<>(labeledEntityFields)); | |
90 | 99 | allowedEntityFieldMap.put(EntityType.ENTITY_VIEW, new HashSet<>(typedEntityFields)); |
100 | + allowedEntityFieldMap.get(EntityType.ENTITY_VIEW).add(CUSTOMER_ID); | |
91 | 101 | |
92 | 102 | allowedEntityFieldMap.put(EntityType.TENANT, new HashSet<>(contactBasedEntityFields)); |
93 | 103 | allowedEntityFieldMap.get(EntityType.TENANT).add(REGION); |
94 | 104 | allowedEntityFieldMap.put(EntityType.CUSTOMER, new HashSet<>(contactBasedEntityFields)); |
105 | + allowedEntityFieldMap.get(EntityType.CUSTOMER).add(TENANT_ID); | |
106 | + | |
107 | + allowedEntityFieldMap.put(EntityType.USER, new HashSet<>(Arrays.asList(CREATED_TIME, FIRST_NAME, LAST_NAME, EMAIL, | |
108 | + ADDITIONAL_INFO, AUTHORITY, TENANT_ID, CUSTOMER_ID))); | |
95 | 109 | |
96 | - allowedEntityFieldMap.put(EntityType.USER, new HashSet<>(Arrays.asList(CREATED_TIME, FIRST_NAME, LAST_NAME, EMAIL, ADDITIONAL_INFO))); | |
110 | + allowedEntityFieldMap.put(EntityType.DEVICE_PROFILE, new HashSet<>(commonEntityFields)); | |
111 | + allowedEntityFieldMap.get(EntityType.DEVICE_PROFILE).add(TENANT_ID); | |
97 | 112 | |
98 | 113 | allowedEntityFieldMap.put(EntityType.DASHBOARD, new HashSet<>(dashboardEntityFields)); |
99 | 114 | allowedEntityFieldMap.put(EntityType.RULE_CHAIN, new HashSet<>(commonEntityFields)); |
115 | + allowedEntityFieldMap.get(EntityType.RULE_CHAIN).add(TENANT_ID); | |
116 | + allowedEntityFieldMap.get(EntityType.RULE_CHAIN).add(TYPE); | |
100 | 117 | allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields)); |
101 | - allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields)); | |
102 | - allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields)); | |
118 | + allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TENANT_ID))); | |
119 | + allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, TITLE, TENANT_ID))); | |
103 | 120 | allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields); |
121 | + allowedEntityFieldMap.put(EntityType.TB_RESOURCE, Set.of(CREATED_TIME, ENTITY_TYPE, RESOURCE_TYPE, TITLE, TENANT_ID)); | |
122 | + allowedEntityFieldMap.put(EntityType.OTA_PACKAGE, Set.of(CREATED_TIME, ENTITY_TYPE, TYPE, TITLE, TENANT_ID)); | |
104 | 123 | |
105 | 124 | entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); |
106 | 125 | entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); |
... | ... | @@ -120,25 +139,42 @@ public class EntityKeyMapping { |
120 | 139 | entityFieldColumnMap.put(ZIP, ModelConstants.ZIP_PROPERTY); |
121 | 140 | entityFieldColumnMap.put(PHONE, ModelConstants.PHONE_PROPERTY); |
122 | 141 | entityFieldColumnMap.put(ADDITIONAL_INFO, ModelConstants.ADDITIONAL_INFO_PROPERTY); |
142 | + entityFieldColumnMap.put(TENANT_ID, ModelConstants.TENANT_ID_PROPERTY); | |
143 | + entityFieldColumnMap.put(CUSTOMER_ID, ModelConstants.CUSTOMER_ID_PROPERTY); | |
144 | + entityFieldColumnMap.put(AUTHORITY, ModelConstants.USER_AUTHORITY_PROPERTY); | |
145 | + entityFieldColumnMap.put(RESOURCE_TYPE, ModelConstants.RESOURCE_TYPE_COLUMN); | |
123 | 146 | |
124 | 147 | Map<String, String> contactBasedAliases = new HashMap<>(); |
125 | 148 | contactBasedAliases.put(NAME, TITLE); |
126 | 149 | contactBasedAliases.put(LABEL, TITLE); |
127 | 150 | aliases.put(EntityType.TENANT, contactBasedAliases); |
128 | - aliases.put(EntityType.CUSTOMER, contactBasedAliases); | |
151 | + aliases.put(EntityType.CUSTOMER, new HashMap<>(contactBasedAliases)); | |
129 | 152 | aliases.put(EntityType.DASHBOARD, contactBasedAliases); |
130 | 153 | Map<String, String> commonEntityAliases = new HashMap<>(); |
131 | 154 | commonEntityAliases.put(TITLE, NAME); |
132 | 155 | aliases.put(EntityType.DEVICE, commonEntityAliases); |
133 | 156 | aliases.put(EntityType.ASSET, commonEntityAliases); |
134 | 157 | aliases.put(EntityType.ENTITY_VIEW, commonEntityAliases); |
135 | - aliases.put(EntityType.WIDGETS_BUNDLE, commonEntityAliases); | |
158 | + aliases.put(EntityType.EDGE, commonEntityAliases); | |
159 | + aliases.put(EntityType.WIDGETS_BUNDLE, new HashMap<>(commonEntityAliases)); | |
160 | + aliases.get(EntityType.WIDGETS_BUNDLE).put(NAME, TITLE); | |
136 | 161 | |
137 | 162 | Map<String, String> userEntityAliases = new HashMap<>(); |
138 | 163 | userEntityAliases.put(TITLE, EMAIL); |
139 | 164 | userEntityAliases.put(LABEL, EMAIL); |
140 | 165 | userEntityAliases.put(NAME, EMAIL); |
166 | + userEntityAliases.put(TYPE, AUTHORITY); | |
141 | 167 | aliases.put(EntityType.USER, userEntityAliases); |
168 | + aliases.put(EntityType.TB_RESOURCE, Map.of(NAME, TITLE, TYPE, RESOURCE_TYPE)); | |
169 | + aliases.put(EntityType.OTA_PACKAGE, Map.of(NAME, TITLE)); | |
170 | + | |
171 | + propertiesFunctions.put(EntityType.USER, Map.of( | |
172 | + LAST_ACTIVITY_TIME, "cast(e.additional_info::json ->> 'lastLoginTs' as bigint)" | |
173 | + )); | |
174 | + | |
175 | + Arrays.stream(EntityType.values()).forEach(entityType -> { | |
176 | + searchTextFields.put(entityType, ModelConstants.SEARCH_TEXT_PROPERTY); | |
177 | + }); | |
142 | 178 | } |
143 | 179 | |
144 | 180 | private int index; |
... | ... | @@ -179,6 +215,10 @@ public class EntityKeyMapping { |
179 | 215 | String column = entityFieldColumnMap.get(alias); |
180 | 216 | return String.format("cast(e.%s as varchar) as %s", column, getValueAlias()); |
181 | 217 | } else { |
218 | + Map<String, String> entityPropertiesFunctions = propertiesFunctions.get(entityType); | |
219 | + if (entityPropertiesFunctions != null && entityPropertiesFunctions.containsKey(alias)) { | |
220 | + return String.format("%s as %s", entityPropertiesFunctions.get(alias), getValueAlias()); | |
221 | + } | |
182 | 222 | return String.format("'' as %s", getValueAlias()); |
183 | 223 | } |
184 | 224 | } | ... | ... |