Showing
14 changed files
with
115 additions
and
86 deletions
... | ... | @@ -635,7 +635,7 @@ public class RuleChainController extends BaseController { |
635 | 635 | } |
636 | 636 | } |
637 | 637 | |
638 | - // TODO: refactor this - add new config to edge rule chain to set it as auto-assign | |
638 | + // TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign | |
639 | 639 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
640 | 640 | @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET) |
641 | 641 | @ResponseBody | ... | ... |
... | ... | @@ -27,6 +27,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; |
27 | 27 | import org.thingsboard.server.dao.edge.EdgeEventService; |
28 | 28 | import org.thingsboard.server.dao.edge.EdgeService; |
29 | 29 | import org.thingsboard.server.dao.rule.RuleChainService; |
30 | +import org.thingsboard.server.dao.settings.AdminSettingsService; | |
30 | 31 | import org.thingsboard.server.dao.user.UserService; |
31 | 32 | import org.thingsboard.server.dao.widget.WidgetsBundleService; |
32 | 33 | import org.thingsboard.server.queue.util.TbCoreComponent; |
... | ... | @@ -64,6 +65,10 @@ public class EdgeContextComponent { |
64 | 65 | |
65 | 66 | @Lazy |
66 | 67 | @Autowired |
68 | + private AdminSettingsService adminSettingsService; | |
69 | + | |
70 | + @Lazy | |
71 | + @Autowired | |
67 | 72 | private AssetService assetService; |
68 | 73 | |
69 | 74 | @Lazy | ... | ... |
... | ... | @@ -68,6 +68,7 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg; |
68 | 68 | import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; |
69 | 69 | import org.thingsboard.server.gen.edge.WidgetBundleTypesRequestMsg; |
70 | 70 | import org.thingsboard.server.service.edge.EdgeContextComponent; |
71 | +import org.thingsboard.server.service.edge.rpc.fetch.AdminSettingsEdgeEventFetcher; | |
71 | 72 | import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher; |
72 | 73 | import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher; |
73 | 74 | import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher; |
... | ... | @@ -207,8 +208,7 @@ public final class EdgeGrpcSession implements Closeable { |
207 | 208 | startProcessingEdgeEvents(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId())); |
208 | 209 | } |
209 | 210 | |
210 | - // TODO: voba - implement this functionality | |
211 | - // syncAdminSettings(edge); | |
211 | + startProcessingEdgeEvents(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService())); | |
212 | 212 | |
213 | 213 | startProcessingEdgeEvents(new AssetsEdgeEventFetcher(ctx.getAssetService())); |
214 | 214 | startProcessingEdgeEvents(new DashboardsEdgeEventFetcher(ctx.getDashboardService())); |
... | ... | @@ -285,7 +285,7 @@ public final class EdgeGrpcSession implements Closeable { |
285 | 285 | sendDownlinkMsg(edgeConfigMsg); |
286 | 286 | } |
287 | 287 | |
288 | - void processEdgeEvents() throws ExecutionException, InterruptedException { | |
288 | + void processEdgeEvents() throws Exception { | |
289 | 289 | log.trace("[{}] processHandleMessages started", this.sessionId); |
290 | 290 | if (isConnected() && isSyncCompleted()) { |
291 | 291 | Long queueStartTs = getQueueStartTs().get(); |
... | ... | @@ -301,7 +301,7 @@ public final class EdgeGrpcSession implements Closeable { |
301 | 301 | log.trace("[{}] processHandleMessages finished", this.sessionId); |
302 | 302 | } |
303 | 303 | |
304 | - private UUID startProcessingEdgeEvents(EdgeEventFetcher fetcher) throws InterruptedException { | |
304 | + private UUID startProcessingEdgeEvents(EdgeEventFetcher fetcher) throws Exception { | |
305 | 305 | PageLink pageLink = fetcher.getPageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()); |
306 | 306 | PageData<EdgeEvent> pageData; |
307 | 307 | UUID ifOffset = null; | ... | ... |
... | ... | @@ -41,7 +41,7 @@ public class DeviceProfileMsgConstructor { |
41 | 41 | .setDefault(deviceProfile.isDefault()) |
42 | 42 | .setType(deviceProfile.getType().name()) |
43 | 43 | .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData()))); |
44 | - // TODO: voba - should this be always null at the moment?? | |
44 | + // TODO: @voba - add possibility to setup edge rule chain as device profile default | |
45 | 45 | // if (deviceProfile.getDefaultRuleChainId() != null) { |
46 | 46 | // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits()) |
47 | 47 | // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits()); | ... | ... |
... | ... | @@ -15,90 +15,115 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.edge.rpc.fetch; |
17 | 17 | |
18 | +import com.datastax.oss.driver.api.core.uuid.Uuids; | |
19 | +import com.fasterxml.jackson.databind.JsonNode; | |
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
18 | 21 | import lombok.AllArgsConstructor; |
19 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | +import org.apache.commons.io.FileUtils; | |
24 | +import org.apache.commons.lang3.StringUtils; | |
25 | +import org.apache.commons.lang3.text.WordUtils; | |
26 | +import org.springframework.core.io.DefaultResourceLoader; | |
27 | +import org.thingsboard.server.common.data.AdminSettings; | |
20 | 28 | import org.thingsboard.server.common.data.edge.EdgeEvent; |
29 | +import org.thingsboard.server.common.data.edge.EdgeEventActionType; | |
30 | +import org.thingsboard.server.common.data.edge.EdgeEventType; | |
31 | +import org.thingsboard.server.common.data.id.AdminSettingsId; | |
21 | 32 | import org.thingsboard.server.common.data.id.EdgeId; |
22 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
23 | 34 | import org.thingsboard.server.common.data.page.PageData; |
24 | 35 | import org.thingsboard.server.common.data.page.PageLink; |
36 | +import org.thingsboard.server.dao.settings.AdminSettingsService; | |
37 | +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils; | |
38 | + | |
39 | +import java.io.File; | |
40 | +import java.nio.charset.StandardCharsets; | |
41 | +import java.util.ArrayList; | |
42 | +import java.util.HashMap; | |
43 | +import java.util.List; | |
44 | +import java.util.Map; | |
45 | +import java.util.regex.Matcher; | |
46 | +import java.util.regex.Pattern; | |
25 | 47 | |
26 | 48 | @AllArgsConstructor |
27 | 49 | @Slf4j |
28 | 50 | public class AdminSettingsEdgeEventFetcher extends BasePageableEdgeEventFetcher { |
29 | 51 | |
52 | + private final AdminSettingsService adminSettingsService; | |
53 | + | |
30 | 54 | @Override |
31 | - public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { | |
32 | - return null; | |
55 | + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, EdgeId edgeId, PageLink pageLink) throws Exception { | |
56 | + List<EdgeEvent> result = new ArrayList<>(); | |
57 | + | |
58 | + AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); | |
59 | + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, | |
60 | + EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings))); | |
61 | + | |
62 | + AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); | |
63 | + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, | |
64 | + EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings))); | |
65 | + | |
66 | + AdminSettings systemMailTemplates = loadMailTemplates(); | |
67 | + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, | |
68 | + EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates))); | |
69 | + | |
70 | + AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); | |
71 | + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, | |
72 | + EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates))); | |
73 | + | |
74 | + // @voba - returns PageData object to be in sync with other fetchers | |
75 | + return new PageData<>(result, 1, result.size(), false); | |
33 | 76 | } |
34 | 77 | |
78 | + private AdminSettings loadMailTemplates() throws Exception { | |
79 | + Map<String, Object> mailTemplates = new HashMap<>(); | |
80 | + Pattern startPattern = Pattern.compile("<div class=\"content\".*?>"); | |
81 | + Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>"); | |
82 | + File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles(); | |
83 | + for (File file : files) { | |
84 | + Map<String, String> mailTemplate = new HashMap<>(); | |
85 | + String name = validateName(file.getName()); | |
86 | + String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8); | |
87 | + Matcher start = startPattern.matcher(stringTemplate); | |
88 | + Matcher end = endPattern.matcher(stringTemplate); | |
89 | + if (start.find() && end.find()) { | |
90 | + String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", ""); | |
91 | + String subject = StringUtils.substringBetween(body, "<h2>", "</h2>"); | |
92 | + mailTemplate.put("subject", subject); | |
93 | + mailTemplate.put("body", body); | |
94 | + mailTemplates.put(name, mailTemplate); | |
95 | + } else { | |
96 | + log.error("Can't load mail template from file {}", file.getName()); | |
97 | + } | |
98 | + } | |
99 | + AdminSettings adminSettings = new AdminSettings(); | |
100 | + adminSettings.setId(new AdminSettingsId(Uuids.timeBased())); | |
101 | + adminSettings.setKey("mailTemplates"); | |
102 | + adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class)); | |
103 | + return adminSettings; | |
104 | + } | |
35 | 105 | |
36 | -// | |
37 | -// private void syncAdminSettings(TenantId tenantId, Edge edge) { | |
38 | -// log.trace("[{}] syncAdminSettings [{}]", tenantId, edge.getName()); | |
39 | -// try { | |
40 | -// AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); | |
41 | -// saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)); | |
42 | -// AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); | |
43 | -// saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)); | |
44 | -// AdminSettings systemMailTemplates = loadMailTemplates(); | |
45 | -// saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)); | |
46 | -// AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); | |
47 | -// saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)); | |
48 | -// } catch (Exception e) { | |
49 | -// log.error("Can't load admin settings", e); | |
50 | -// } | |
51 | -// } | |
52 | -// | |
53 | -// private AdminSettings loadMailTemplates() throws Exception { | |
54 | -// Map<String, Object> mailTemplates = new HashMap<>(); | |
55 | -// Pattern startPattern = Pattern.compile("<div class=\"content\".*?>"); | |
56 | -// Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>"); | |
57 | -// File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles(); | |
58 | -// for (File file : files) { | |
59 | -// Map<String, String> mailTemplate = new HashMap<>(); | |
60 | -// String name = validateName(file.getName()); | |
61 | -// String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8); | |
62 | -// Matcher start = startPattern.matcher(stringTemplate); | |
63 | -// Matcher end = endPattern.matcher(stringTemplate); | |
64 | -// if (start.find() && end.find()) { | |
65 | -// String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", ""); | |
66 | -// String subject = StringUtils.substringBetween(body, "<h2>", "</h2>"); | |
67 | -// mailTemplate.put("subject", subject); | |
68 | -// mailTemplate.put("body", body); | |
69 | -// mailTemplates.put(name, mailTemplate); | |
70 | -// } else { | |
71 | -// log.error("Can't load mail template from file {}", file.getName()); | |
72 | -// } | |
73 | -// } | |
74 | -// AdminSettings adminSettings = new AdminSettings(); | |
75 | -// adminSettings.setId(new AdminSettingsId(Uuids.timeBased())); | |
76 | -// adminSettings.setKey("mailTemplates"); | |
77 | -// adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class)); | |
78 | -// return adminSettings; | |
79 | -// } | |
80 | -// | |
81 | -// private String validateName(String name) throws Exception { | |
82 | -// StringBuilder nameBuilder = new StringBuilder(); | |
83 | -// name = name.replace(".vm", ""); | |
84 | -// String[] nameParts = name.split("\\."); | |
85 | -// if (nameParts.length >= 1) { | |
86 | -// nameBuilder.append(nameParts[0]); | |
87 | -// for (int i = 1; i < nameParts.length; i++) { | |
88 | -// String word = WordUtils.capitalize(nameParts[i]); | |
89 | -// nameBuilder.append(word); | |
90 | -// } | |
91 | -// return nameBuilder.toString(); | |
92 | -// } else { | |
93 | -// throw new Exception("Error during filename validation"); | |
94 | -// } | |
95 | -// } | |
96 | -// | |
97 | -// private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) { | |
98 | -// AdminSettings tenantMailSettings = new AdminSettings(); | |
99 | -// jsonValue.put("useSystemMailSettings", true); | |
100 | -// tenantMailSettings.setJsonValue(jsonValue); | |
101 | -// tenantMailSettings.setKey(key); | |
102 | -// return tenantMailSettings; | |
103 | -// } | |
106 | + private String validateName(String name) throws Exception { | |
107 | + StringBuilder nameBuilder = new StringBuilder(); | |
108 | + name = name.replace(".vm", ""); | |
109 | + String[] nameParts = name.split("\\."); | |
110 | + if (nameParts.length >= 1) { | |
111 | + nameBuilder.append(nameParts[0]); | |
112 | + for (int i = 1; i < nameParts.length; i++) { | |
113 | + String word = WordUtils.capitalize(nameParts[i]); | |
114 | + nameBuilder.append(word); | |
115 | + } | |
116 | + return nameBuilder.toString(); | |
117 | + } else { | |
118 | + throw new Exception("Error during filename validation"); | |
119 | + } | |
120 | + } | |
121 | + | |
122 | + private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) { | |
123 | + AdminSettings tenantMailSettings = new AdminSettings(); | |
124 | + jsonValue.put("useSystemMailSettings", true); | |
125 | + tenantMailSettings.setJsonValue(jsonValue); | |
126 | + tenantMailSettings.setKey(key); | |
127 | + return tenantMailSettings; | |
128 | + } | |
104 | 129 | } | ... | ... |
... | ... | @@ -15,10 +15,13 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.edge.rpc.fetch; |
17 | 17 | |
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
18 | 19 | import org.thingsboard.server.common.data.page.PageLink; |
19 | 20 | |
20 | 21 | public abstract class BasePageableEdgeEventFetcher implements EdgeEventFetcher { |
21 | 22 | |
23 | + protected static final ObjectMapper mapper = new ObjectMapper(); | |
24 | + | |
22 | 25 | @Override |
23 | 26 | public PageLink getPageLink(int pageSize) { |
24 | 27 | return new PageLink(pageSize); | ... | ... |
... | ... | @@ -25,5 +25,5 @@ public interface EdgeEventFetcher { |
25 | 25 | |
26 | 26 | PageLink getPageLink(int pageSize); |
27 | 27 | |
28 | - PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, EdgeId edgeId, PageLink pageLink); | |
28 | + PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, EdgeId edgeId, PageLink pageLink) throws Exception; | |
29 | 29 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
... | ... | @@ -72,8 +72,6 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor { |
72 | 72 | |
73 | 73 | private static final ReentrantLock deviceCreationLock = new ReentrantLock(); |
74 | 74 | |
75 | - // TODO onDeviceUpdateFromEdge onDeviceUpdateToEdge | |
76 | - | |
77 | 75 | public ListenableFuture<Void> processDeviceFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) { |
78 | 76 | log.trace("[{}] onDeviceUpdate [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName()); |
79 | 77 | switch (deviceUpdateMsg.getMsgType()) { | ... | ... |
... | ... | @@ -82,7 +82,7 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { |
82 | 82 | List<ListenableFuture<Void>> result = new ArrayList<>(); |
83 | 83 | EntityId entityId = constructEntityId(entityData); |
84 | 84 | if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) { |
85 | - // TODO: voba - in terms of performance we should not fetch device from DB by id | |
85 | + // @voba - in terms of performance we should not fetch device from DB by id | |
86 | 86 | // TbMsgMetaData metaData = constructBaseMsgMetadata(tenantId, entityId); |
87 | 87 | TbMsgMetaData metaData = new TbMsgMetaData(); |
88 | 88 | metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); | ... | ... |
... | ... | @@ -171,8 +171,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { |
171 | 171 | installation(); |
172 | 172 | |
173 | 173 | edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); |
174 | - // TODO: voba - should be less, but events from SyncEdgeService stack with events from controller. will be fixed in next releases | |
175 | - // so ideally sync process should check current edge queue and add only missing entities to the edge queue | |
176 | 174 | edgeImitator.expectMessageAmount(9); |
177 | 175 | edgeImitator.connect(); |
178 | 176 | } | ... | ... |
... | ... | @@ -497,7 +497,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic |
497 | 497 | |
498 | 498 | @Override |
499 | 499 | public ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) { |
500 | - // TODO: voba - rewrite 'find' to use native SQL queries instead of fetching relations | |
500 | + // TODO: @voba - rewrite 'find' to use native SQL queries instead of fetching relations | |
501 | 501 | |
502 | 502 | log.trace("[{}] Executing findRelatedEdgeIdsByEntityId [{}]", tenantId, entityId); |
503 | 503 | if (EntityType.TENANT.equals(entityId.getEntityType()) || | ... | ... |
... | ... | @@ -77,7 +77,7 @@ public abstract class AbstractEntityService { |
77 | 77 | List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId).get(); |
78 | 78 | if (entityViews != null && !entityViews.isEmpty()) { |
79 | 79 | EntityView entityView = entityViews.get(0); |
80 | - // TODO: voba - refactor this blocking operation in 3.3+ | |
80 | + // TODO: @voba - refactor this blocking operation in 3.3+ | |
81 | 81 | Boolean relationExists = relationService.checkRelation(tenantId,edgeId, entityView.getId(), |
82 | 82 | EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE).get(); |
83 | 83 | if (relationExists) { | ... | ... |
... | ... | @@ -223,7 +223,7 @@ public class TbMsgPushToEdgeNode implements TbNode { |
223 | 223 | private String getScope(Map<String, String> metadata) { |
224 | 224 | String scope = metadata.get(SCOPE); |
225 | 225 | if (StringUtils.isEmpty(scope)) { |
226 | - // TODO: voba - move this to configuration of the node UI or some other place | |
226 | + // TODO: @voba - move this to configuration of the node UI or some other place | |
227 | 227 | scope = DataConstants.SERVER_SCOPE; |
228 | 228 | } |
229 | 229 | return scope; | ... | ... |
... | ... | @@ -188,7 +188,7 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI |
188 | 188 | |
189 | 189 | fetchRuleChain(searchText?: string): Observable<Array<BaseData<EntityId>>> { |
190 | 190 | this.searchText = searchText; |
191 | - // voba: at the moment device profiles are not supported by edge, so 'core' hardcoded | |
191 | + // @voba: at the moment device profiles are not supported by edge, so 'core' hardcoded | |
192 | 192 | return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText, |
193 | 193 | 50, RuleChainType.CORE, {ignoreLoading: true}); |
194 | 194 | } | ... | ... |