From 41e2eaea44328e1892c3481d092bda0f55a8b207 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Nov 2018 15:22:00 +0200 Subject: [PATCH 01/74] Migrate to Spring Boot 2.1.0 and Spring 5.1.2 --- application/pom.xml | 2 +- .../config/AuditLogLevelProperties.java | 2 +- .../config/SchedulingConfiguration.java | 47 +++++++ .../ThingsboardMessageConfiguration.java | 128 +++++++++++++++++- .../ThingsboardSecurityConfiguration.java | 2 +- .../ThingsboardWebFluxSecurityConfig.java | 61 +++++++++ .../server/config/WebSocketConfiguration.java | 47 +++++-- .../controller/plugin/TbWebSocketHandler.java | 50 +++++-- ...assandraAbstractDatabaseSchemaService.java | 2 + .../CassandraDatabaseUpgradeService.java | 2 + .../service/mail/DefaultMailService.java | 32 ++++- .../webflux/WebfluxAuthenticationManager.java | 48 +++++++ .../JwtTokenSecurityContextRepository.java | 86 ++++++++++++ .../DefaultTelemetryWebSocketService.java | 3 +- .../TelemetryWebSocketMsgEndpoint.java | 2 +- .../src/main/resources/thingsboard.yml | 8 +- ...tractMqttServerSideRpcIntegrationTest.java | 4 +- .../org/thingsboard/server/dao/DaoUtil.java | 14 +- .../server/dao/audit/AuditLogServiceImpl.java | 2 +- .../dao/audit/CassandraAuditLogDao.java | 4 +- .../dao/audit/DummyAuditLogServiceImpl.java | 2 +- .../dao/audit/sink/DummyAuditLogSink.java | 2 +- .../audit/sink/ElasticsearchAuditLogSink.java | 16 +-- .../dao/cache/TBRedisCacheConfiguration.java | 43 ++++-- .../dao/cassandra/CassandraCluster.java | 2 +- .../cassandra/CassandraInstallCluster.java | 2 +- .../dao/nosql/CassandraAbstractDao.java | 2 + .../server/dao/sql/JpaAbstractDao.java | 9 +- .../dao/sql/attributes/JpaAttributeDao.java | 6 +- .../JpaBaseComponentDescriptorDao.java | 2 +- .../dao/sql/relation/JpaRelationDao.java | 8 +- .../dao/sql/relation/RelationRepository.java | 2 +- .../dao/sql/timeseries/JpaTimeseriesDao.java | 6 +- .../resources/application-test.properties | 8 +- dao/src/test/resources/sql-test.properties | 1 + docker/docker-compose.yml | 3 + pom.xml | 48 ++++--- 37 files changed, 586 insertions(+), 122 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java create mode 100644 application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java diff --git a/application/pom.xml b/application/pom.xml index d99f60773e..238c5f47f9 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -122,7 +122,7 @@ org.springframework.boot - spring-boot-starter-websocket + spring-boot-starter-webflux io.jsonwebtoken diff --git a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java index 4c36a15758..d05f8326b1 100644 --- a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java +++ b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; @Configuration -@ConfigurationProperties(prefix = "audit_log.logging_level") +@ConfigurationProperties(prefix = "audit-log.logging-level") public class AuditLogLevelProperties { private Map mask = new HashMap<>(); diff --git a/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java new file mode 100644 index 0000000000..aebd63698a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thingsboard.server.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +@Configuration +@EnableScheduling +public class SchedulingConfiguration implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskScheduler()); + } + + @Bean(destroyMethod="shutdown") + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler(); + threadPoolScheduler.setThreadNamePrefix("TB-Scheduling-"); + threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors()); + threadPoolScheduler.setRemoveOnCancelPolicy(true); + return threadPoolScheduler; + } +} diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java index 038f8bc6ae..a44d9fedcb 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java @@ -15,11 +15,28 @@ */ package org.thingsboard.server.config; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.ExtendedProperties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; @Configuration public class ThingsboardMessageConfiguration { @@ -32,5 +49,114 @@ public class ThingsboardMessageConfiguration { messageSource.setDefaultEncoding("UTF-8"); return messageSource; } - + + private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:/templates/"; + + private ResourceLoader resourceLoader = new DefaultResourceLoader(); + + @Bean + public VelocityEngine velocityEngine() { + VelocityEngine velocityEngine = new VelocityEngine(); + try { + Resource resource = resourceLoader.getResource(DEFAULT_RESOURCE_LOADER_PATH); + File file = resource.getFile(); + velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file"); + velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true"); + velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, file.getAbsolutePath()); + } catch (IOException e) { + initSpringResourceLoader(velocityEngine, DEFAULT_RESOURCE_LOADER_PATH); + } + velocityEngine.init(); + return velocityEngine; + } + + private void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) { + velocityEngine.setProperty( + RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME); + velocityEngine.setProperty( + SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName()); + velocityEngine.setProperty( + SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true"); + velocityEngine.setApplicationAttribute( + SpringResourceLoader.SPRING_RESOURCE_LOADER, resourceLoader); + velocityEngine.setApplicationAttribute( + SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath); + } + + @Slf4j + static class SpringResourceLoader extends org.apache.velocity.runtime.resource.loader.ResourceLoader { + + public static final String NAME = "spring"; + + public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class"; + + public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache"; + + public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader"; + + public static final String SPRING_RESOURCE_LOADER_PATH = "spring.resource.loader.path"; + + private org.springframework.core.io.ResourceLoader resourceLoader; + + private String[] resourceLoaderPaths; + + + @Override + public void init(ExtendedProperties configuration) { + this.resourceLoader = (org.springframework.core.io.ResourceLoader) + this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER); + String resourceLoaderPath = (String) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER_PATH); + if (this.resourceLoader == null) { + throw new IllegalArgumentException( + "'resourceLoader' application attribute must be present for SpringResourceLoader"); + } + if (resourceLoaderPath == null) { + throw new IllegalArgumentException( + "'resourceLoaderPath' application attribute must be present for SpringResourceLoader"); + } + this.resourceLoaderPaths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath); + for (int i = 0; i < this.resourceLoaderPaths.length; i++) { + String path = this.resourceLoaderPaths[i]; + if (!path.endsWith("/")) { + this.resourceLoaderPaths[i] = path + "/"; + } + } + if (log.isInfoEnabled()) { + log.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader + + "] and resource loader paths " + Arrays.asList(this.resourceLoaderPaths)); + } + } + + @Override + public InputStream getResourceStream(String source) throws ResourceNotFoundException { + if (log.isDebugEnabled()) { + log.debug("Looking for Velocity resource with name [" + source + "]"); + } + for (String resourceLoaderPath : this.resourceLoaderPaths) { + org.springframework.core.io.Resource resource = + this.resourceLoader.getResource(resourceLoaderPath + source); + try { + return resource.getInputStream(); + } + catch (IOException ex) { + if (log.isDebugEnabled()) { + log.debug("Could not find Velocity resource: " + resource); + } + } + } + throw new ResourceNotFoundException( + "Could not find resource [" + source + "] in Spring resource loader path"); + } + + @Override + public boolean isSourceModified(org.apache.velocity.runtime.resource.Resource resource) { + return false; + } + + @Override + public long getLastModified(org.apache.velocity.runtime.resource.Resource resource) { + return 0; + } + + } } diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 1901e49c3d..13f7c569c4 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -57,7 +57,7 @@ import java.util.List; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) -@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) +@Order(SecurityProperties.BASIC_AUTH_ORDER) public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java new file mode 100644 index 0000000000..bdb168f714 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thingsboard.server.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; + +//@EnableWebFluxSecurity +//@EnableReactiveMethodSecurity +public class ThingsboardWebFluxSecurityConfig { + + private static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; + + @Autowired + private ReactiveAuthenticationManager webfluxAuthenticationManager; + + @Autowired + private ServerSecurityContextRepository jwtTokenSecurityContextRepository; + + @Bean + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + return http + .cors() + .and() + .csrf().disable() + .formLogin().disable() + .httpBasic().disable() + .exceptionHandling() + .and() + .authenticationManager(webfluxAuthenticationManager) + .securityContextRepository(jwtTokenSecurityContextRepository) + .authorizeExchange() + .pathMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT) + .authenticated() + .and() + .build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index 59b7da2e76..4472db305e 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -21,38 +21,57 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; -import org.springframework.web.socket.server.HandshakeInterceptor; -import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; -import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; +import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService; +import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; +import org.springframework.web.server.ServerWebExchange; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.controller.plugin.TbWebSocketHandler; import org.thingsboard.server.service.security.model.SecurityUser; +import reactor.core.publisher.Mono; +import java.util.HashMap; import java.util.Map; @Configuration -@EnableWebSocket -public class WebSocketConfiguration implements WebSocketConfigurer { +public class WebSocketConfiguration { public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; public static final String WS_SECURITY_USER_ATTRIBUTE = "SECURITY_USER"; private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; - @Bean +/* @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(32768); container.setMaxBinaryMessageBufferSize(32768); return container; + }*/ + + @Bean + public HandlerMapping handlerMapping() { + Map map = new HashMap<>(); + map.put(WS_PLUGIN_MAPPING, wsHandler()); + + SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); + mapping.setUrlMap(map); + mapping.setOrder(-1); // before annotated controllers + return mapping; } - @Override + @Bean + public WebSocketHandlerAdapter handlerAdapter() { + return new WebSocketHandlerAdapter(); + } + +/* @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { @@ -79,19 +98,19 @@ public class WebSocketConfiguration implements WebSocketConfigurer { //Do nothing } }); - } + }*/ @Bean public WebSocketHandler wsHandler() { return new TbWebSocketHandler(); } - protected SecurityUser getCurrentUser() throws ThingsboardException { +/* protected SecurityUser getCurrentUser() throws ThingsboardException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { return (SecurityUser) authentication.getPrincipal(); } else { throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); } - } + }*/ } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index f579db8588..d01fc07e71 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -21,10 +21,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.springframework.web.reactive.socket.CloseStatus; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.WebSocketSession; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -37,10 +36,13 @@ import org.thingsboard.server.service.telemetry.SessionEvent; import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.io.IOException; import java.net.URI; import java.security.InvalidParameterException; +import java.security.Principal; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -48,12 +50,12 @@ import java.util.concurrent.ConcurrentMap; @Service @Slf4j -public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { +public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketMsgEndpoint { - private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); - private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); + //private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); + // private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); - @Autowired + /* @Autowired private TelemetryWebSocketService webSocketService; @Value("${server.ws.limits.max_sessions_per_tenant:0}") @@ -90,8 +92,26 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } catch (IOException e) { log.warn("IO error", e); } + }*/ + + @Override + public Mono handle(WebSocketSession session) { + return session.receive() + .doOnNext(message -> { + Principal principal = session.getHandshakeInfo().getPrincipal().block(); + if (principal instanceof SecurityUser) { + SecurityUser currentUser = (SecurityUser) principal; + log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText()); + } else { + log.info("[{}] Principal {}", session.getId(), principal); + log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText()); + } + }) + .then(); } + +/* @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); @@ -173,11 +193,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr this.session = session; this.sessionRef = sessionRef; } - } + }*/ @Override public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { - String externalId = sessionRef.getSessionId(); + /* String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing {}", externalId, msg); String internalId = externalSessionMap.get(externalId); if (internalId != null) { @@ -212,12 +232,12 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } else { log.warn("[{}] Failed to find session by external id", externalId); - } + }*/ } @Override public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { - String externalId = sessionRef.getSessionId(); + /* String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing close request", externalId); String internalId = externalSessionMap.get(externalId); if (internalId != null) { @@ -229,10 +249,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } else { log.warn("[{}] Failed to find session by external id", externalId); - } + }*/ } - private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { + /*private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { String sessionId = session.getId(); if (maxSessionsPerTenant > 0) { Set tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); @@ -322,6 +342,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } } - } + }*/ } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java index 10559ba2d2..f800ced5ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; import org.thingsboard.server.service.install.cql.CQLStatementsParser; @@ -30,6 +31,7 @@ public abstract class CassandraAbstractDatabaseSchemaService implements Database private static final String CASSANDRA_DIR = "cassandra"; @Autowired + @Qualifier("CassandraInstallCluster") private CassandraInstallCluster cluster; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index a2aeefaecf..58021a6d67 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.install; import com.datastax.driver.core.KeyspaceMetadata; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -65,6 +66,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { private CassandraCluster cluster; @Autowired + @Qualifier("CassandraInstallCluster") private CassandraInstallCluster installCluster; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 6c48ee144f..479c628d7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -18,7 +18,9 @@ package org.thingsboard.server.service.mail; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.VelocityException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.MessageSource; @@ -26,7 +28,7 @@ import org.springframework.core.NestedRuntimeException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; -import org.springframework.ui.velocity.VelocityEngineUtils; +//import org.springframework.ui.velocity.VelocityEngineUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -39,6 +41,8 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import javax.annotation.PostConstruct; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import java.io.StringWriter; +import java.io.Writer; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -126,7 +130,7 @@ public class DefaultMailService implements MailService { Map model = new HashMap(); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "test.vm", UTF_8, model); sendMail(testMailSender, mailFrom, email, subject, message); @@ -141,7 +145,7 @@ public class DefaultMailService implements MailService { model.put("activationLink", activationLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "activation.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -156,7 +160,7 @@ public class DefaultMailService implements MailService { model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "account.activated.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -171,7 +175,7 @@ public class DefaultMailService implements MailService { model.put("passwordResetLink", passwordResetLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "reset.password.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -186,7 +190,7 @@ public class DefaultMailService implements MailService { model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "password.was.reset.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -225,6 +229,22 @@ public class DefaultMailService implements MailService { } } + private static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation, + String encoding, Map model) throws VelocityException { + + StringWriter result = new StringWriter(); + mergeTemplate(velocityEngine, templateLocation, encoding, model, result); + return result.toString(); + } + + private static void mergeTemplate( + VelocityEngine velocityEngine, String templateLocation, String encoding, + Map model, Writer writer) throws VelocityException { + + VelocityContext velocityContext = new VelocityContext(model); + velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer); + } + protected ThingsboardException handleException(Exception exception) { String message; if (exception instanceof NestedRuntimeException) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java new file mode 100644 index 0000000000..b2aeb0ded8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.auth.webflux; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; +import reactor.core.publisher.Mono; + +@Component +public class WebfluxAuthenticationManager implements ReactiveAuthenticationManager { + + @Autowired + private JwtTokenFactory tokenFactory; + + @Override + public Mono authenticate(Authentication authentication) { + try { + if (authentication.getCredentials() != null && authentication.getCredentials() instanceof RawAccessJwtToken) { + RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); + SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken); + JwtAuthenticationToken auth = new JwtAuthenticationToken(securityUser); + return Mono.just(auth); + } + return Mono.empty(); + } catch (Exception e) { + return Mono.error(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java new file mode 100644 index 0000000000..482830086e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.auth.webflux.jwt; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; +import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.server.ServerWebExchange; +import org.thingsboard.server.config.ThingsboardSecurityConfiguration; +import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; +import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Component +public class JwtTokenSecurityContextRepository implements ServerSecurityContextRepository { + + public static final String DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME = "SPRING_SECURITY_CONTEXT"; + + @Autowired + private ReactiveAuthenticationManager webfluxAuthenticationManager; + + @Override + public Mono save(ServerWebExchange exchange, SecurityContext context) { + return exchange.getSession() + .doOnNext(session -> { + if (context == null) { + session.getAttributes().remove(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME); + } else { + session.getAttributes().put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, context); + } + }) + .flatMap(session -> session.changeSessionId()); + } + + @Override + public Mono load(ServerWebExchange exchange) { + ServerHttpRequest request = exchange.getRequest(); + String token = extractTokenFromQuery(request); + if (!StringUtils.isEmpty(token)) { + RawAccessJwtToken rawToken = new RawAccessJwtToken(token); + Authentication auth = new JwtAuthenticationToken(rawToken); + return this.webfluxAuthenticationManager.authenticate(auth).map((authentication) -> { + return new SecurityContextImpl(authentication); + }); + } else { + return Mono.empty(); + } + } + + private String extractTokenFromQuery(ServerHttpRequest request) { + String token = null; + if (request.getQueryParams() != null) { + List tokenParamValue = request.getQueryParams().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM); + if (tokenParamValue != null && !tokenParamValue.isEmpty()) { + token = tokenParamValue.get(0); + } + } + return token; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index 6b87e033fc..883eba8bc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -26,8 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.reactive.socket.CloseStatus; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java index b73aadff1b..0b097167ad 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.service.telemetry; -import org.springframework.web.socket.CloseStatus; +import org.springframework.web.reactive.socket.CloseStatus; import java.io.IOException; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 9f60fb6a90..1512961c8c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -282,11 +282,13 @@ spring.mvc.cors: # spring serve gzip compressed static resources spring.resources.chain: - gzipped: "true" + compressed: "true" strategy: content: enabled: "true" +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" + # HSQLDB DAO Configuration spring: data: @@ -320,7 +322,7 @@ spring: # password: "${SPRING_DATASOURCE_PASSWORD:postgres}" # Audit log parameters -audit_log: +audit-log: # Enable/disable audit log functionality. enabled: "${AUDIT_LOG_ENABLED:true}" # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS @@ -329,7 +331,7 @@ audit_log: default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}" # Logging levels per each entity type. # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) - logging_level: + logging-level: mask: "device": "${AUDIT_LOG_MASK_DEVICE:W}" "asset": "${AUDIT_LOG_MASK_ASSET:W}" diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java index 7453682644..9e3463672d 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -128,7 +128,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), @@ -183,7 +183,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index c7bcfbcd33..1031215204 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,11 +18,7 @@ package org.thingsboard.server.dao; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.dao.model.ToData; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; public abstract class DaoUtil { @@ -50,6 +46,14 @@ public abstract class DaoUtil { return object; } + public static T getData(Optional> data) { + T object = null; + if (data.isPresent()) { + object = data.get().toData(); + } + return object; + } + public static UUID getId(UUIDBased idBased) { UUID id = null; if (idBased != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 24c6a274f1..34ae9d306a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -60,7 +60,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service -@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "true") +@ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true") public class AuditLogServiceImpl implements AuditLogService { private static final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java index f2b2973996..ccc1880a12 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java @@ -88,11 +88,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao redisTemplate(RedisConnectionFactory cf) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(cf); - return redisTemplate; - } - - @Bean - public CacheManager cacheManager(RedisTemplate redisTemplate) { - return new RedisCacheManager(redisTemplate); + public CacheManager cacheManager(RedisConnectionFactory cf) { + DefaultFormattingConversionService redisConversionService = new DefaultFormattingConversionService(); + RedisCacheConfiguration.registerDefaultConverters(redisConversionService); + registerDefaultConverters(redisConversionService); + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig().withConversionService(redisConversionService); + return RedisCacheManager.builder(cf).cacheDefaults(configuration).build(); } @Bean @@ -73,5 +93,8 @@ public class TBRedisCacheConfiguration { return new PreviousDeviceCredentialsIdKeyGenerator(); } - + private static void registerDefaultConverters(ConverterRegistry registry) { + Assert.notNull(registry, "ConverterRegistry must not be null!"); + registry.addConverter(EntityId.class, String.class, EntityId::toString); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java index 19409ad146..c0c4853387 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java @@ -21,7 +21,7 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; import javax.annotation.PostConstruct; -@Component +@Component("CassandraCluster") @NoSqlAnyDao public class CassandraCluster extends AbstractCassandraCluster { diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java index 247a204ee5..718a4fa299 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java @@ -21,7 +21,7 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; import javax.annotation.PostConstruct; -@Component +@Component("CassandraInstallCluster") @NoSqlAnyDao @Profile("install") public class CassandraInstallCluster extends AbstractCassandraCluster { diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java index 1a419df9a1..ebcc4f61dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java @@ -27,6 +27,7 @@ import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.exceptions.CodecNotFoundException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.model.type.AuthorityCodec; @@ -44,6 +45,7 @@ import java.util.concurrent.ConcurrentMap; public abstract class CassandraAbstractDao { @Autowired + @Qualifier("CassandraCluster") protected CassandraCluster cluster; private ConcurrentMap preparedStatementMap = new ConcurrentHashMap<>(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 987eabe738..4c8eb372e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -27,6 +27,7 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.BaseEntity; import java.util.List; +import java.util.Optional; import java.util.UUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @@ -67,23 +68,23 @@ public abstract class JpaAbstractDao, D> @Override public D findById(TenantId tenantId, UUID key) { log.debug("Get entity by key {}", key); - E entity = getCrudRepository().findOne(fromTimeUUID(key)); + Optional entity = getCrudRepository().findById(fromTimeUUID(key)); return DaoUtil.getData(entity); } @Override public ListenableFuture findByIdAsync(TenantId tenantId, UUID key) { log.debug("Get entity by key async {}", key); - return service.submit(() -> DaoUtil.getData(getCrudRepository().findOne(fromTimeUUID(key)))); + return service.submit(() -> DaoUtil.getData(getCrudRepository().findById(fromTimeUUID(key)))); } @Override @Transactional public boolean removeById(TenantId tenantId, UUID id) { String key = fromTimeUUID(id); - getCrudRepository().delete(key); + getCrudRepository().deleteById(key); log.debug("Remove request: {}", key); - return getCrudRepository().findOne(key) == null; + return !getCrudRepository().existsById(key); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 2193646ec9..84ef8b88a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -52,7 +52,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl AttributeKvCompositeKey compositeKey = getAttributeKvCompositeKey(entityId, attributeType, attributeKey); return Futures.immediateFuture( - Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findOne(compositeKey)))); + Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findById(compositeKey)))); } @Override @@ -64,7 +64,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl getAttributeKvCompositeKey(entityId, attributeType, attributeKey)) .collect(Collectors.toList()); return Futures.immediateFuture( - DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAll(compositeKeys)))); + DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAllById(compositeKeys)))); } @Override @@ -103,7 +103,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl }).collect(Collectors.toList()); return service.submit(() -> { - attributeKvRepository.delete(entitiesToDelete); + attributeKvRepository.deleteAll(entitiesToDelete); return null; }); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java index 47b006e3b1..56b0ec7e7f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java @@ -66,7 +66,7 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - return service.submit(() -> relationRepository.findOne(key) != null); + return service.submit(() -> relationRepository.existsById(key)); } @Override public ListenableFuture getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - return service.submit(() -> DaoUtil.getData(relationRepository.findOne(key))); + return service.submit(() -> DaoUtil.getData(relationRepository.findById(key))); } private RelationCompositeKey getRelationCompositeKey(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { @@ -152,9 +152,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple } private boolean deleteRelationIfExists(RelationCompositeKey key) { - boolean relationExistsBeforeDelete = relationRepository.exists(key); + boolean relationExistsBeforeDelete = relationRepository.existsById(key); if (relationExistsBeforeDelete) { - relationRepository.delete(key); + relationRepository.deleteById(key); } return relationExistsBeforeDelete; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java index 60c6a0e9d7..cf1e2b22b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java @@ -53,7 +53,7 @@ public interface RelationRepository RelationEntity save(RelationEntity entity); @Transactional - void delete(RelationCompositeKey id); + void deleteById(RelationCompositeKey id); @Transactional void deleteByFromIdAndFromType(String fromId, String fromType); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java index 312014861f..7fc28b0ec3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java @@ -258,10 +258,10 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp entityId.getEntityType(), fromTimeUUID(entityId.getId()), key); - TsKvLatestEntity entry = tsKvLatestRepository.findOne(compositeKey); + Optional entry = tsKvLatestRepository.findById(compositeKey); TsKvEntry result; - if (entry != null) { - result = DaoUtil.getData(entry); + if (entry.isPresent()) { + result = DaoUtil.getData(entry.get()); } else { result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null)); } diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index a285676f08..c873c908db 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -4,10 +4,10 @@ zk.zk_dir=/thingsboard updates.enabled=false -audit_log.enabled=true -audit_log.by_tenant_partitioning=MONTHS -audit_log.default_query_period=30 -audit_log.sink.type=none +audit-log.enabled=true +audit-log.by_tenant_partitioning=MONTHS +audit-log.default_query_period=30 +audit-log.sink.type=none cache.type=caffeine #cache.type=redis diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 3357425fce..745aa9e1e0 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,6 +4,7 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 944ed66880..b89a214b39 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -23,6 +23,9 @@ services: image: "zookeeper:3.5" ports: - "2181" + environment: + ZOO_MY_ID: 1 + ZOO_SERVERS: server.1=zookeeper:2888:3888;zookeeper:2181 kafka: restart: always image: "wurstmeister/kafka" diff --git a/pom.xml b/pom.xml index 00c9c28567..27cb40121b 100755 --- a/pom.xml +++ b/pom.xml @@ -29,10 +29,10 @@ ${basedir} - 1.4.3.RELEASE - 4.3.4.RELEASE - 4.2.0.RELEASE - 1.8.10.RELEASE + 2.1.0.RELEASE + 5.1.2.RELEASE + 5.1.1.RELEASE + 2.1.2.RELEASE 2.9.0 0.7.0 2.2.0 @@ -41,8 +41,8 @@ 1.2.3 1.9.5 0.10 - 3.5.0 - 3.3.0.2 + 3.6.0 + 3.5.0.1 1.2.7 21.0 2.6.1 @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.8.11.1 + 2.9.7 2.2.6 2.11 2.4.2 @@ -58,13 +58,15 @@ 2.6.2 1.7 2.0 + 2.7.7 + 1.23 1.4.3 4.0.1 - 3.0.2 - 1.12.0 + 3.6.1 + 1.16.1 1.16.18 1.1.0 - 4.1.22.Final + 4.1.30.Final 1.5.0 4.8.0 2.19.1 @@ -410,7 +412,7 @@ org.springframework.boot - spring-boot-starter-websocket + spring-boot-starter-webflux ${spring-boot.version} @@ -485,20 +487,16 @@ org.apache.velocity velocity-tools ${velocity-tools.version} - - - javax.servlet - servlet-api - - - dom4j - dom4j - - - antlr - antlr - - + + + antlr + antlr + ${antlr.version} + + + org.yaml + snakeyaml + ${snakeyaml.version} com.rabbitmq From 54459eb202d57582abbb9dd4ce9c1bedff41787a Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Thu, 21 Feb 2019 16:56:10 +0200 Subject: [PATCH 02/74] update rulenode-core-config.js --- .../resources/public/static/rulenode/rulenode-core-config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index 567478f414..b614ca08a2 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,5 +1,5 @@ !function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(91)},function(e,t){},1,1,1,1,function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{scope.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
{{ 'tb.rulenode.use-message-alarm-data' | translate }}
tb.rulenode.alarm-type-required
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
"},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.entity-type-pattern-required
tb.rulenode.entity-type-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
tb.rulenode.create-entity-if-not-exists-hint
{{ 'tb.rulenode.remove-current-relations' | translate }}
tb.rulenode.remove-current-relations-hint
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
tb.rulenode.change-originator-to-related-entity-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
tb.rulenode.delete-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports="
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.headers-hint
'; -},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }}
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{ (\'relation.search-direction.\' + direction) | translate}}
relation.relation-type
device.device-types
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},28,function(e,t){e.exports='
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"; +},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }}
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{ (\'relation.search-direction.\' + direction) | translate}}
relation.relation-type
device.device-types
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},28,function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"; },function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(66),i=a(r),o=n(47),l=a(o),s=n(52),d=a(s),u=n(49),c=a(u),m=n(48),g=a(m),p=n(55),f=a(p),b=n(61),v=a(b),y=n(62),h=a(y),q=n(60),$=a(q),k=n(54),x=a(k),T=n(64),C=a(T),w=n(65),M=a(w),S=n(59),_=a(S),N=n(56),E=a(N),P=n(63),V=a(P),F=n(58),j=a(F),A=n(57),O=a(A),I=n(46),R=a(I),K=n(67),D=a(K),U=n(51),L=a(U),z=n(50),B=a(z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",x.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",V.default).directive("tbActionNodeMsgDelayConfig",j.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",D.default).directive("tbActionNodeDeleteRelationConfig",L.default).directive("tbActionNodeCreateRelationConfig",B.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(74),i=a(r),o=n(75),l=a(o),s=n(71),d=a(s),u=n(76),c=a(u),m=n(70),g=a(m),p=n(77),f=a(p),b=n(72),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(83),i=a(r),o=n(81),l=a(o),s=n(84),d=a(s),u=n(79),c=a(u),m=n(82),g=a(m),p=n(78),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(41),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(42),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(43),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(87),i=a(r),o=n(89),l=a(o),s=n(90),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(44),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(45),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(94),i=a(r),o=n(80),l=a(o),s=n(73),d=a(s),u=n(88),c=a(u),m=n(53),g=a(m),p=n(69),f=a(p),b=n(86),v=a(b),y=n(68),h=a(y),q=n(85),$=a(q),k=n(93),x=a(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(x.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", -"customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(92),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); +"customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(92),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file From 1022a3dc0183eada6f9c139f716e4064ddc6ebdf Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 14:15:49 +0200 Subject: [PATCH 03/74] Exlude old version of jackson-core that doesn't work with updated Spring framework --- pom.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f12049aaac..eb0650a124 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.7 + 2.9.0 2.2.6 2.11 2.4.2 @@ -792,6 +792,12 @@ de.ruedigermoeller fst ${fst.version} + + + com.fasterxml.jackson.core + jackson-core + +
io.springfox.ui From 301661946e321979afd6238b937bfc461e19560e Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 22 Feb 2019 14:16:45 +0200 Subject: [PATCH 04/74] backward compatibility --- .../rule/engine/action/TbAbstractRelationActionNode.java | 4 ++-- .../action/TbAbstractRelationActionNodeConfiguration.java | 2 +- .../thingsboard/rule/engine/action/TbCreateRelationNode.java | 2 +- .../rule/engine/action/TbCreateRelationNodeConfiguration.java | 2 +- .../thingsboard/rule/engine/action/TbDeleteRelationNode.java | 2 +- .../rule/engine/action/TbDeleteRelationNodeConfiguration.java | 2 +- .../resources/public/static/rulenode/rulenode-core-config.js | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java index f7a4f8023e..c6d5ce5246 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java @@ -131,9 +131,9 @@ public abstract class TbAbstractRelationActionNode> processListSearchDirection(TbContext ctx, TbMsg msg) { if (EntitySearchDirection.FROM.name().equals(this.config.getDirection())) { - return ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), processPattern(msg, this.config.getRelationTypePattern()), RelationTypeGroup.COMMON); + return ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), processPattern(msg, this.config.getRelationType()), RelationTypeGroup.COMMON); } else { - return ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), processPattern(msg, this.config.getRelationTypePattern()), RelationTypeGroup.COMMON); + return ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), processPattern(msg, this.config.getRelationType()), RelationTypeGroup.COMMON); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNodeConfiguration.java index 09c5469a2a..465b55e885 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNodeConfiguration.java @@ -21,7 +21,7 @@ import lombok.Data; public abstract class TbAbstractRelationActionNodeConfiguration { private String direction; - private String relationTypePattern; + private String relationType; private String entityType; private String entityNamePattern; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java index bb5099f9e5..8162401318 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java @@ -85,7 +85,7 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { - relationType = processPattern(msg, config.getRelationTypePattern()); + relationType = processPattern(msg, config.getRelationType()); SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer); ListenableFuture checkRelationFuture = Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), relationType, RelationTypeGroup.COMMON), result -> { if (!result) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeConfiguration.java index 2600c423a7..8802b2dbda 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeConfiguration.java @@ -30,7 +30,7 @@ public class TbCreateRelationNodeConfiguration extends TbAbstractRelationActionN public TbCreateRelationNodeConfiguration defaultConfiguration() { TbCreateRelationNodeConfiguration configuration = new TbCreateRelationNodeConfiguration(); configuration.setDirection(EntitySearchDirection.FROM.name()); - configuration.setRelationTypePattern("Contains"); + configuration.setRelationType("Contains"); configuration.setEntityNamePattern(""); configuration.setEntityCacheExpiration(300); configuration.setCreateEntityIfNotExists(false); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java index ff4172c128..1cac9cc216 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java @@ -70,7 +70,7 @@ public class TbDeleteRelationNode extends TbAbstractRelationActionNode getRelationContainerListenableFuture(TbContext ctx, TbMsg msg) { - relationType = processPattern(msg, config.getRelationTypePattern()); + relationType = processPattern(msg, config.getRelationType()); if (config.isDeleteForSingleEntity()) { return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer)); } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeConfiguration.java index d9a6a2df1a..3304f1a855 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeConfiguration.java @@ -29,7 +29,7 @@ public class TbDeleteRelationNodeConfiguration extends TbAbstractRelationActionN TbDeleteRelationNodeConfiguration configuration = new TbDeleteRelationNodeConfiguration(); configuration.setDeleteForSingleEntity(true); configuration.setDirection(EntitySearchDirection.FROM.name()); - configuration.setRelationTypePattern("Contains"); + configuration.setRelationType("Contains"); configuration.setEntityNamePattern(""); configuration.setEntityCacheExpiration(300); return configuration; diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index b614ca08a2..95bb72a870 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,5 +1,5 @@ -!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(91)},function(e,t){},1,1,1,1,function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{scope.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
{{ 'tb.rulenode.use-message-alarm-data' | translate }}
tb.rulenode.alarm-type-required
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
"},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.entity-type-pattern-required
tb.rulenode.entity-type-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
tb.rulenode.create-entity-if-not-exists-hint
{{ 'tb.rulenode.remove-current-relations' | translate }}
tb.rulenode.remove-current-relations-hint
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
tb.rulenode.change-originator-to-related-entity-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
tb.rulenode.delete-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports="
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.headers-hint
'; +!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(91)},function(e,t){},1,1,1,1,function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{scope.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
{{ 'tb.rulenode.use-message-alarm-data' | translate }}
tb.rulenode.alarm-type-required
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
"},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.entity-type-pattern-required
tb.rulenode.entity-type-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
tb.rulenode.create-entity-if-not-exists-hint
{{ 'tb.rulenode.remove-current-relations' | translate }}
tb.rulenode.remove-current-relations-hint
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
tb.rulenode.change-originator-to-related-entity-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
tb.rulenode.delete-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports="
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.headers-hint
'; },function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }}
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{ (\'relation.search-direction.\' + direction) | translate}}
relation.relation-type
device.device-types
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},28,function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"; -},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(66),i=a(r),o=n(47),l=a(o),s=n(52),d=a(s),u=n(49),c=a(u),m=n(48),g=a(m),p=n(55),f=a(p),b=n(61),v=a(b),y=n(62),h=a(y),q=n(60),$=a(q),k=n(54),x=a(k),T=n(64),C=a(T),w=n(65),M=a(w),S=n(59),_=a(S),N=n(56),E=a(N),P=n(63),V=a(P),F=n(58),j=a(F),A=n(57),O=a(A),I=n(46),R=a(I),K=n(67),D=a(K),U=n(51),L=a(U),z=n(50),B=a(z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",x.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",V.default).directive("tbActionNodeMsgDelayConfig",j.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",D.default).directive("tbActionNodeDeleteRelationConfig",L.default).directive("tbActionNodeCreateRelationConfig",B.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(74),i=a(r),o=n(75),l=a(o),s=n(71),d=a(s),u=n(76),c=a(u),m=n(70),g=a(m),p=n(77),f=a(p),b=n(72),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(83),i=a(r),o=n(81),l=a(o),s=n(84),d=a(s),u=n(79),c=a(u),m=n(82),g=a(m),p=n(78),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(41),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(42),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(43),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(87),i=a(r),o=n(89),l=a(o),s=n(90),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(44),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(45),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(94),i=a(r),o=n(80),l=a(o),s=n(73),d=a(s),u=n(88),c=a(u),m=n(53),g=a(m),p=n(69),f=a(p),b=n(86),v=a(b),y=n(68),h=a(y),q=n(85),$=a(q),k=n(93),x=a(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(x.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", +},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(66),i=a(r),o=n(47),l=a(o),s=n(52),d=a(s),u=n(49),c=a(u),m=n(48),g=a(m),p=n(55),f=a(p),b=n(61),v=a(b),y=n(62),h=a(y),q=n(60),$=a(q),k=n(54),x=a(k),T=n(64),C=a(T),w=n(65),M=a(w),S=n(59),_=a(S),N=n(56),E=a(N),V=n(63),P=a(V),F=n(58),j=a(F),A=n(57),O=a(A),I=n(46),R=a(I),K=n(67),D=a(K),U=n(51),L=a(U),z=n(50),B=a(z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",x.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",j.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",D.default).directive("tbActionNodeDeleteRelationConfig",L.default).directive("tbActionNodeCreateRelationConfig",B.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(74),i=a(r),o=n(75),l=a(o),s=n(71),d=a(s),u=n(76),c=a(u),m=n(70),g=a(m),p=n(77),f=a(p),b=n(72),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(83),i=a(r),o=n(81),l=a(o),s=n(84),d=a(s),u=n(79),c=a(u),m=n(82),g=a(m),p=n(78),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(41),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(42),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(43),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(87),i=a(r),o=n(89),l=a(o),s=n(90),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(44),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(45),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(94),i=a(r),o=n(80),l=a(o),s=n(73),d=a(s),u=n(88),c=a(u),m=n(53),g=a(m),p=n(69),f=a(p),b=n(86),v=a(b),y=n(68),h=a(y),q=n(85),$=a(q),k=n(93),x=a(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(x.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", "customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(92),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file From 83d4dbc6e5426f30c1a74655460e052c11204350 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 15:15:27 +0200 Subject: [PATCH 05/74] Websockets reverted --- application/pom.xml | 2 +- .../ThingsboardWebFluxSecurityConfig.java | 60 ------------- .../server/config/WebSocketConfiguration.java | 51 ++++------- .../controller/plugin/TbWebSocketHandler.java | 85 +++++++----------- .../webflux/WebfluxAuthenticationManager.java | 48 ----------- .../JwtTokenSecurityContextRepository.java | 86 ------------------- .../DefaultTelemetryWebSocketService.java | 2 +- .../TelemetryWebSocketMsgEndpoint.java | 2 +- pom.xml | 4 +- 9 files changed, 54 insertions(+), 286 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java diff --git a/application/pom.xml b/application/pom.xml index 91fe9f71f2..91965dfed5 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -126,7 +126,7 @@
org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-websocket io.jsonwebtoken diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java deleted file mode 100644 index db8f599350..0000000000 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2019 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.SecurityWebFiltersOrder; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; -import org.springframework.security.web.server.context.ServerSecurityContextRepository; - -//@EnableWebFluxSecurity -//@EnableReactiveMethodSecurity -public class ThingsboardWebFluxSecurityConfig { - - private static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; - - @Autowired - private ReactiveAuthenticationManager webfluxAuthenticationManager; - - @Autowired - private ServerSecurityContextRepository jwtTokenSecurityContextRepository; - - @Bean - public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { - return http - .cors() - .and() - .csrf().disable() - .formLogin().disable() - .httpBasic().disable() - .exceptionHandling() - .and() - .authenticationManager(webfluxAuthenticationManager) - .securityContextRepository(jwtTokenSecurityContextRepository) - .authorizeExchange() - .pathMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT) - .authenticated() - .and() - .build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index 6c08b1b9a6..b8b703e4fc 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -21,63 +21,44 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; -import org.springframework.web.reactive.HandlerMapping; -import org.springframework.web.reactive.HandlerResult; -import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; -import org.springframework.web.reactive.socket.WebSocketHandler; -import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService; -import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; -import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.HandshakeInterceptor; +import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.controller.plugin.TbWebSocketHandler; import org.thingsboard.server.service.security.model.SecurityUser; -import reactor.core.publisher.Mono; -import java.util.HashMap; import java.util.Map; @Configuration -public class WebSocketConfiguration { +@EnableWebSocket +public class WebSocketConfiguration implements WebSocketConfigurer { public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; -/* @Bean + @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(32768); container.setMaxBinaryMessageBufferSize(32768); return container; - }*/ - - @Bean - public HandlerMapping handlerMapping() { - Map map = new HashMap<>(); - map.put(WS_PLUGIN_MAPPING, wsHandler()); - - SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); - mapping.setUrlMap(map); - mapping.setOrder(-1); // before annotated controllers - return mapping; } - @Bean - public WebSocketHandlerAdapter handlerAdapter() { - return new WebSocketHandlerAdapter(); - } - -/* @Override + @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Map attributes) throws Exception { + Map attributes) throws Exception { SecurityUser user = null; try { user = getCurrentUser(); @@ -92,23 +73,23 @@ public class WebSocketConfiguration { @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Exception exception) { + Exception exception) { //Do nothing } }); - }*/ + } @Bean public WebSocketHandler wsHandler() { return new TbWebSocketHandler(); } -/* protected SecurityUser getCurrentUser() throws ThingsboardException { + protected SecurityUser getCurrentUser() throws ThingsboardException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { return (SecurityUser) authentication.getPrincipal(); } else { throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); } - }*/ + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index d20e4db433..169bba8895 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -16,51 +16,48 @@ package org.thingsboard.server.controller.plugin; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.BeanCreationNotAllowedException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; -import org.springframework.web.reactive.socket.CloseStatus; -import org.springframework.web.reactive.socket.WebSocketHandler; -import org.springframework.web.reactive.socket.WebSocketSession; +import org.springframework.util.StringUtils; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.adapter.NativeWebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.msg.tools.TbRateLimits; +import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.UserPrincipal; +import org.thingsboard.server.service.telemetry.SessionEvent; import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import reactor.core.publisher.Mono; +import javax.websocket.*; import java.io.IOException; -import java.security.Principal; +import java.net.URI; +import java.security.InvalidParameterException; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.LinkedBlockingQueue; @Service @Slf4j -public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketMsgEndpoint { +public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { - @Override - public Mono handle(WebSocketSession session) { - return session.receive() - .doOnNext(message -> { - Principal principal = session.getHandshakeInfo().getPrincipal().block(); - if (principal instanceof SecurityUser) { - SecurityUser currentUser = (SecurityUser) principal; - log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText()); - } else { - log.info("[{}] Principal {}", session.getId(), principal); - log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText()); - } - }) - .then(); - } - - @Override - public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { + private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); + private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); - } - - @Override - public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException { - - } - -// private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); -// private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); -/* @Autowired private TelemetryWebSocketService webSocketService; @@ -105,22 +102,6 @@ public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketM } } - @Override - public Mono handle(WebSocketSession session) { - return session.receive() - .doOnNext(message -> { - Principal principal = session.getHandshakeInfo().getPrincipal().block(); - if (principal instanceof SecurityUser) { - SecurityUser currentUser = (SecurityUser) principal; - log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText()); - } else { - log.info("[{}] Principal {}", session.getId(), principal); - log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText()); - } - }) - .then(); - } - @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); @@ -410,5 +391,5 @@ public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketM } } } - */ -} + +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java deleted file mode 100644 index 35bee6e9bd..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2019 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.security.auth.webflux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; -import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; -import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.security.model.token.JwtTokenFactory; -import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import reactor.core.publisher.Mono; - -@Component -public class WebfluxAuthenticationManager implements ReactiveAuthenticationManager { - - @Autowired - private JwtTokenFactory tokenFactory; - - @Override - public Mono authenticate(Authentication authentication) { - try { - if (authentication.getCredentials() != null && authentication.getCredentials() instanceof RawAccessJwtToken) { - RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); - SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken); - JwtAuthenticationToken auth = new JwtAuthenticationToken(securityUser); - return Mono.just(auth); - } - return Mono.empty(); - } catch (Exception e) { - return Mono.error(e); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java deleted file mode 100644 index f6fe103fc5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright © 2016-2019 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.security.auth.webflux.jwt; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.web.server.context.ServerSecurityContextRepository; -import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.server.ServerWebExchange; -import org.thingsboard.server.config.ThingsboardSecurityConfiguration; -import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; -import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; -import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import reactor.core.publisher.Mono; - -import java.util.List; - -@Component -public class JwtTokenSecurityContextRepository implements ServerSecurityContextRepository { - - public static final String DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME = "SPRING_SECURITY_CONTEXT"; - - @Autowired - private ReactiveAuthenticationManager webfluxAuthenticationManager; - - @Override - public Mono save(ServerWebExchange exchange, SecurityContext context) { - return exchange.getSession() - .doOnNext(session -> { - if (context == null) { - session.getAttributes().remove(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME); - } else { - session.getAttributes().put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, context); - } - }) - .flatMap(session -> session.changeSessionId()); - } - - @Override - public Mono load(ServerWebExchange exchange) { - ServerHttpRequest request = exchange.getRequest(); - String token = extractTokenFromQuery(request); - if (!StringUtils.isEmpty(token)) { - RawAccessJwtToken rawToken = new RawAccessJwtToken(token); - Authentication auth = new JwtAuthenticationToken(rawToken); - return this.webfluxAuthenticationManager.authenticate(auth).map((authentication) -> { - return new SecurityContextImpl(authentication); - }); - } else { - return Mono.empty(); - } - } - - private String extractTokenFromQuery(ServerHttpRequest request) { - String token = null; - if (request.getQueryParams() != null) { - List tokenParamValue = request.getQueryParams().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM); - if (tokenParamValue != null && !tokenParamValue.isEmpty()) { - token = tokenParamValue.get(0); - } - } - return token; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index 68646153a6..86747150c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -26,7 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.springframework.web.reactive.socket.CloseStatus; +import org.springframework.web.socket.CloseStatus; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java index ca3a91600f..8c18f2d42b 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.service.telemetry; -import org.springframework.web.reactive.socket.CloseStatus; +import org.springframework.web.socket.CloseStatus; import java.io.IOException; diff --git a/pom.xml b/pom.xml index eb0650a124..d3ff7a1635 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.0 + 2.9.7 2.2.6 2.11 2.4.2 @@ -424,7 +424,7 @@ org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-websocket ${spring-boot.version} From e543608275a39213f405318399cff28701cfeea1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 16:14:36 +0200 Subject: [PATCH 06/74] Removed unused import --- .../org/thingsboard/server/service/mail/DefaultMailService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index b9398c2a43..217d62b54e 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -28,7 +28,6 @@ import org.springframework.core.NestedRuntimeException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; -//import org.springframework.ui.velocity.VelocityEngineUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; From f009c436781994fa4a7edf3a015d41f122cb59f5 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 16:52:04 +0200 Subject: [PATCH 07/74] Attempt to fix test --- application/src/main/resources/thingsboard.yml | 2 -- dao/src/test/resources/sql-test.properties | 1 - 2 files changed, 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 72b8159407..57d3ebbcf9 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -298,8 +298,6 @@ spring.resources.chain: content: enabled: "true" -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" - # HSQLDB DAO Configuration spring: data: diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 745aa9e1e0..3357425fce 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,7 +4,6 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect From 54c9dc1f97b62f1a041db91d052fc11d72f83675 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 18:18:57 +0200 Subject: [PATCH 08/74] Attempt for test fix --- application/src/main/resources/thingsboard.yml | 2 ++ dao/src/test/resources/sql-test.properties | 1 + pom.xml | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 57d3ebbcf9..72b8159407 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -298,6 +298,8 @@ spring.resources.chain: content: enabled: "true" +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" + # HSQLDB DAO Configuration spring: data: diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 3357425fce..745aa9e1e0 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,6 +4,7 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect diff --git a/pom.xml b/pom.xml index d3ff7a1635..113a8f223b 100755 --- a/pom.xml +++ b/pom.xml @@ -442,6 +442,12 @@ org.springframework.boot spring-boot-starter-data-jpa ${spring-boot.version} + + + net.bytebuddy + byte-buddy + + org.apache.kafka From 76ba4d92c40a9be82b6a540c69561e590d08a913 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 18:26:01 +0200 Subject: [PATCH 09/74] Attempt for test fix --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 113a8f223b..750da6b013 100755 --- a/pom.xml +++ b/pom.xml @@ -437,11 +437,6 @@ spring-boot-starter-test ${spring-boot.version} test - - - org.springframework.boot - spring-boot-starter-data-jpa - ${spring-boot.version} net.bytebuddy @@ -449,6 +444,11 @@ + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + org.apache.kafka kafka-clients From 1f89ba39e67b9056c21a5ca1d44a040685221971 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 19:06:06 +0200 Subject: [PATCH 10/74] Attempt for test fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 750da6b013..44c35d9261 100755 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.6.2 1.7 2.0 - 2.7.7 + 3.5.2 1.23 1.4.3 4.0.1 From a76ff7007b3cdb395f3d9501f1aa49e04f0a569e Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 19:10:24 +0200 Subject: [PATCH 11/74] Attempt for test fix --- pom.xml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 44c35d9261..e01e7918d2 100755 --- a/pom.xml +++ b/pom.xml @@ -58,8 +58,6 @@ 2.6.2 1.7 2.0 - 3.5.2 - 1.23 1.4.3 4.0.1 3.6.1 @@ -505,16 +503,20 @@ org.apache.velocity velocity-tools ${velocity-tools.version} - - - antlr - antlr - ${antlr.version} - - - org.yaml - snakeyaml - ${snakeyaml.version} + + + javax.servlet + servlet-api + + + dom4j + dom4j + + + antlr + antlr + + com.rabbitmq From 86bffde8502932a4ee773623ae109dfafa830492 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 25 Feb 2019 10:25:15 +0200 Subject: [PATCH 12/74] Revert antlr and snakeyaml dependencies --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index e01e7918d2..5e6e59df4f 100755 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,8 @@ 2.0.0 4.1.1 2.57 + 2.7.7 + 1.23 @@ -518,6 +520,16 @@ + + org.yaml + snakeyaml + ${snakeyaml.version} + + + antlr + antlr + ${antlr.version} + com.rabbitmq amqp-client From 8545938c43c7e0c19539989aabd86daea99b60b3 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 25 Feb 2019 12:16:55 +0200 Subject: [PATCH 13/74] Exclude assertj-core to remove conflict dependency of bytebuddy --- application/paho14876741547223-tcplocalhost1883/.lck | 0 pom.xml | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 application/paho14876741547223-tcplocalhost1883/.lck diff --git a/application/paho14876741547223-tcplocalhost1883/.lck b/application/paho14876741547223-tcplocalhost1883/.lck new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pom.xml b/pom.xml index 5e6e59df4f..de61a9c317 100755 --- a/pom.xml +++ b/pom.xml @@ -442,6 +442,10 @@ net.bytebuddy byte-buddy + + org.assertj + assertj-core + From e86de7f30a9d20f15e5a0da6176aef87eb6f6026 Mon Sep 17 00:00:00 2001 From: VoBa Date: Mon, 25 Feb 2019 12:18:43 +0200 Subject: [PATCH 14/74] Delete .lck --- application/paho14876741547223-tcplocalhost1883/.lck | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 application/paho14876741547223-tcplocalhost1883/.lck diff --git a/application/paho14876741547223-tcplocalhost1883/.lck b/application/paho14876741547223-tcplocalhost1883/.lck deleted file mode 100644 index e69de29bb2..0000000000 From b6300f43fa3407cac97dc0c0b429fec95bfe9f58 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Tue, 26 Feb 2019 12:32:37 +0200 Subject: [PATCH 15/74] Trip Animation Widget --- ui/src/app/api/widget.service.js | 3 +- .../tripAnimation/trip-animation-widget.js | 686 ++++++++++++++++++ .../tripAnimation/trip-animation-widget.scss | 104 +++ .../trip-animation-widget.tpl.html | 49 ++ 4 files changed, 841 insertions(+), 1 deletion(-) create mode 100644 ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js create mode 100644 ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss create mode 100644 ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js index 4a09ffc336..edd2bee91a 100644 --- a/ui/src/app/api/widget.service.js +++ b/ui/src/app/api/widget.service.js @@ -32,6 +32,7 @@ import TbAnalogueCompass from '../widget/lib/analogue-compass'; import TbCanvasDigitalGauge from '../widget/lib/canvas-digital-gauge'; import TbMapWidget from '../widget/lib/map-widget'; import TbMapWidgetV2 from '../widget/lib/map-widget2'; +import TripAnimationWidget from '../widget/lib/tripAnimation/trip-animation-widget'; import 'jquery.terminal/js/jquery.terminal.min.js'; import 'jquery.terminal/css/jquery.terminal.min.css'; @@ -43,7 +44,7 @@ import thingsboardTypes from '../common/types.constant'; import thingsboardUtils from '../common/utils.service'; export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget, - thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardExtensionsTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils]) + thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardExtensionsTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils, TripAnimationWidget]) .factory('widgetService', WidgetService) .name; diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js new file mode 100644 index 0000000000..a55e655bb3 --- /dev/null +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -0,0 +1,686 @@ +/* + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import './trip-animation-widget.scss'; +import template from "./trip-animation-widget.tpl.html"; +import TbOpenStreetMap from '../openstreet-map'; +import L from 'leaflet'; +//import tinycolor from 'tinycolor2'; +import MultiOptionsPolyline from '../../../../vendor/leaflet-multi-options-polyline/Leaflet.MultiOptionsPolyline'; +import GeometryUtil from '../../../../vendor/leaflet-geometryutil/leaflet-geometryutil'; +import tinycolor from "tinycolor2"; +import {fillPatternWithActions, isNumber, padValue, processPattern} from "../widget-utils"; +//import {fillPatternWithActions, isNumber, padValue, processPattern, fillPattern} from "../widget-utils"; + +(function () { + // save these original methods before they are overwritten + var proto_initIcon = L.Marker.prototype._initIcon; + var proto_setPos = L.Marker.prototype._setPos; + + var oldIE = (L.DomUtil.TRANSFORM === 'msTransform'); + + L.Marker.addInitHook(function () { + var iconOptions = this.options.icon && this.options.icon.options; + var iconAnchor = iconOptions && this.options.icon.options.iconAnchor; + if (iconAnchor) { + iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px'); + } + this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom'; + this.options.rotationAngle = this.options.rotationAngle || 0; + + // Ensure marker keeps rotated during dragging + this.on('drag', function (e) { + e.target._applyRotation(); + }); + }); + + L.Marker.include({ + _initIcon: function () { + proto_initIcon.call(this); + }, + + _setPos: function (pos) { + proto_setPos.call(this, pos); + this._applyRotation(); + }, + + _applyRotation: function () { + if (this.options.rotationAngle) { + this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin; + + if (oldIE) { + // for IE 9, use the 2D rotation + this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)'; + } else { + // for modern browsers, prefer the 3D accelerated version + let rotation = ' rotateZ(' + this.options.rotationAngle + 'deg)'; + if (!this._icon.style[L.DomUtil.TRANSFORM].includes(rotation)) { + this._icon.style[L.DomUtil.TRANSFORM] += rotation; + } + } + } + }, + + setRotationAngle: function (angle) { + this.options.rotationAngle = angle; + this.update(); + return this; + }, + + setRotationOrigin: function (origin) { + this.options.rotationOrigin = origin; + this.update(); + return this; + } + }); +})(); + + +export default angular.module('thingsboard.widgets.tripAnimation', []) + .directive('tripAnimation', tripAnimationWidget) + .filter('tripAnimation', function ($filter) { + return function (label) { + label = label.toString(); + + let translateSelector = "widgets.tripAnimation." + label; + let translation = $filter('translate')(translateSelector); + + if (translation !== translateSelector) { + return translation; + } + + return label; + } + }) + .name; + + +/*@ngInject*/ +function tripAnimationWidget() { + return { + restrict: "E", + scope: true, + bindToController: { + ctx: '=', + self: '=' + }, + controller: tripAnimationController, + controllerAs: 'vm', + templateUrl: template + }; +} + +/*@ngInject*/ +function tripAnimationController($document, $scope, $http, $timeout, $filter, $log) { + let vm = this; + //const varsRegex = /\$\{([^\}]*)\}/g; + //let icon; + + vm.markers = []; + vm.index = 0; + vm.dsIndex = 0; + vm.isPlaying = false; + vm.minTime = 0; + vm.maxTime = 0; + vm.isPLaying = false; + vm.trackingLine = { + "type": "FeatureCollection", + features: [] + }; + vm.speeds = [1, 5, 10, 25]; + vm.speed = 1; + vm.trips = []; + vm.activeTripIndex = 0; + + vm.showHideTooltip = showHideTooltip; + vm.recalculateTrips = recalculateTrips; + + L.MultiOptionsPolyline = MultiOptionsPolyline; + L.GeometryUtil = GeometryUtil; + L.multiOptionsPolyline = function (latlngs, options) { + return new MultiOptionsPolyline(latlngs, options); + }; + + $scope.$watch('vm.ctx', function () { + if (vm.ctx) { + vm.utils = vm.ctx.$scope.$injector.get('utils'); + vm.settings = vm.ctx.settings; + vm.widgetConfig = vm.ctx.widgetConfig; + vm.data = vm.ctx.data; + vm.datasources = vm.ctx.datasources; + configureStaticSettings(); + initialize(); + initializeCallbacks(); + } + }); + + + function initializeCallbacks() { + vm.self.onDataUpdated = function () { + createUpdatePath(); + }; + + vm.self.onResize = function () { + resize(); + }; + + vm.self.typeParameters = function () { + return { + maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited + maxDataKeys: -1 //Maximum allowed data keys for this widget, -1 - unlimited + } + }; + return true; + } + + + function resize() { + if (vm.map) { + vm.map.invalidateSize(); + } + } + + function initCallback() { + //createUpdatePath(); + //resize(); + } + + vm.playMove = function (play) { + if (play && vm.isPLaying) return; + if (play || vm.isPLaying) vm.isPLaying = true; + if (vm.isPLaying) { + if (vm.index + 1 > vm.maxTime) return; + vm.index++; + vm.trips.forEach(function (trip) { + moveMarker(trip); + }); + vm.timeout = $timeout(function () { + vm.playMove(); + }, 1000 / vm.speed) + } + }; + + + vm.stopPlay = function () { + vm.isPLaying = false; + $timeout.cancel(vm.timeout); + }; + + function recalculateTrips() { + vm.trips.forEach(function (value) { + moveMarker(value); + }) + } + + function findAngle(lat1, lng1, lat2, lng2) { + let angle = Math.atan2(0, 0) - Math.atan2(lat2 - lat1, lng2 - lng1); + angle = angle * 180 / Math.PI; + return parseInt(angle.toFixed(2)); + } + + function initialize() { + $scope.currentDate = $filter('date')(0, "yyyy.MM.dd HH:mm:ss"); + + vm.self.actionSources = [vm.searchAction]; + vm.endpoint = vm.ctx.settings.endpointUrl; + $scope.title = vm.ctx.widgetConfig.title; + vm.utils = vm.self.ctx.$scope.$injector.get('utils'); + + vm.showTimestamp = vm.settings.showTimestamp !== false; + vm.ctx.$element = angular.element("#heat-map", vm.ctx.$container); + //vm.map = L.map(vm.ctx.$element[0]).setView([0, 0], 2); + vm.map = new TbOpenStreetMap(vm.ctx.$element, vm.utils, initCallback, 2, null, null, vm.staticSettings.mapProvider); + vm.map.bounds = vm.map.createBounds(); + vm.map.invalidateSize(true); + vm.map.bounds = vm.map.createBounds(); + + vm.tooltipActionsMap = {}; + var descriptors = vm.ctx.actionsApi.getActionDescriptors('tooltipAction'); + descriptors.forEach(function (descriptor) { + if (descriptor) vm.tooltipActionsMap[descriptor.name] = descriptor; + }); + } + + function configureStaticSettings() { + let staticSettings = {}; + vm.staticSettings = staticSettings; + //Calculate General Settings + staticSettings.mapProvider = vm.ctx.settings.mapProvider || "OpenStreetMap.Mapnik"; + staticSettings.latKeyName = vm.ctx.settings.latKeyName || "latitude"; + staticSettings.lngKeyName = vm.ctx.settings.lngKeyName || "longitude"; + staticSettings.rotationAngle = vm.ctx.settings.rotationAngle || 0; + staticSettings.displayTooltip = vm.ctx.settings.showTooltip || false; + staticSettings.showTooltip = false; + staticSettings.label = vm.ctx.settings.label || "${entityName}"; + staticSettings.useLabelFunction = vm.ctx.settings.useLabelFunction || false; + staticSettings.showLabel = vm.ctx.settings.showLabel || false; + staticSettings.useTooltipFunction = vm.ctx.settings.useTooltipFunction || false; + staticSettings.tooltipPattern = vm.ctx.settings.tooltipPattern || "${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Start Time: ${maxTime}
End Time: ${minTime}"; + staticSettings.tooltipOpacity = vm.ctx.settings.tooltipOpacity || 1; + staticSettings.tooltipColor = vm.ctx.settings.tooltipColor ? tinycolor(vm.ctx.settings.tooltipColor).toHexString() : "#ffffff"; + staticSettings.tooltipFontColor = vm.ctx.settings.tooltipFontColor ? tinycolor(vm.ctx.settings.tooltipFontColor).toHexString() : "#000000"; + staticSettings.pathColor = vm.ctx.settings.color ? tinycolor(vm.ctx.settings.color).toHexString() : "#ff6300"; + staticSettings.pathWeight = vm.ctx.settings.strokeWeight || 1; + staticSettings.pathOpacity = vm.ctx.settings.strokeOpacity || 1; + staticSettings.usePathColorFunction = vm.ctx.settings.useColorFunction || false; + staticSettings.showPoints = vm.ctx.settings.showPoints || false; + staticSettings.pointSize = vm.ctx.settings.pointSize || 1; + staticSettings.markerImageSize = vm.ctx.settings.markerImageSize || 20; + staticSettings.useMarkerImageFunction = vm.ctx.settings.useMarkerImageFunction || false; + staticSettings.pointColor = vm.ctx.settings.pointColor ? tinycolor(vm.ctx.settings.pointColor).toHexString() : "#ff6300"; + staticSettings.markerImages = vm.ctx.settings.markerImages || []; + staticSettings.icon = L.icon({ + iconUrl: "data:image/png;base64,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", + iconSize: [30, 30], + iconAnchor: [15, 15] + }); + if (angular.isDefined(vm.ctx.settings.markerImage)) { + staticSettings.icon = L.icon({ + iconUrl: vm.ctx.settings.markerImage, + iconSize: [staticSettings.markerImageSize, staticSettings.markerImageSize], + iconAnchor: [(staticSettings.markerImageSize / 2), (staticSettings.markerImageSize / 2)] + }) + } + + if (staticSettings.usePathColorFunction && angular.isDefined(vm.ctx.settings.colorFunction)) { + staticSettings.colorFunction = new Function('data, dsData, dsIndex', vm.ctx.settings.colorFunction); + } + + if (staticSettings.useLabelFunction && angular.isDefined(vm.ctx.settings.labelFunction)) { + staticSettings.labelFunction = new Function('data, dsData, dsIndex', vm.ctx.settings.labelFunction); + } + + if (staticSettings.useTooltipFunction && angular.isDefined(vm.ctx.settings.tooltipFunction)) { + staticSettings.tooltipFunction = new Function('data, dsData, dsIndex', vm.ctx.settings.tooltipFunction); + } + + if (staticSettings.useMarkerImageFunction && angular.isDefined(vm.ctx.settings.markerImageFunction)) { + staticSettings.markerImageFunction = new Function('data, images, dsData, dsIndex', vm.ctx.settings.markerImageFunction); + } + + if (!staticSettings.useMarkerImageFunction && + angular.isDefined(vm.ctx.settings.markerImage) && + vm.ctx.settings.markerImage.length > 0) { + staticSettings.useMarkerImage = true; + let url = vm.ctx.settings.markerImage; + let size = staticSettings.markerImageSize || 20; + staticSettings.currentImage = { + url: url, + size: size + }; + vm.utils.loadImageAspect(staticSettings.currentImage.url).then( + (aspect) => { + if (aspect) { + let width; + let height; + if (aspect > 1) { + width = staticSettings.currentImage.size; + height = staticSettings.currentImage.size / aspect; + } else { + width = staticSettings.currentImage.size * aspect; + height = staticSettings.currentImage.size; + } + staticSettings.icon = L.icon({ + iconUrl: staticSettings.currentImage.url, + iconSize: [width, height], + iconAnchor: [width / 2, height / 2] + }); + } + if (vm.trips) { + vm.trips.forEach(function (trip) { + if (trip.marker) { + trip.marker.setIcon(staticSettings.icon); + } + }); + } + } + ) + } + } + + function configureTripSettings(trip) { + trip.settings = {}; + trip.settings.color = calculateColor(trip); + trip.settings.strokeWeight = vm.staticSettings.pathWeight; + trip.settings.strokeOpacity = vm.staticSettings.pathOpacity; + trip.settings.pointColor = vm.staticSettings.pointColor; + trip.settings.pointSize = vm.staticSettings.pointSize; + trip.settings.labelText = calculateLabel(trip); + trip.settings.tooltipText = calculateTooltip(trip); + trip.settings.icon = calculateIcon(trip); + } + + function calculateLabel(trip) { + let label = ''; + if (vm.staticSettings.showLabel) { + let labelReplaceInfo; + let labelText = vm.staticSettings.label; + if (vm.staticSettings.useLabelFunction && angular.isDefined(vm.staticSettings.labelFunction)) { + try { + labelText = vm.staticSettings.labelFunction(vm.ctx.data, trip.timeRange[vm.index], trip.dsIndex); + } catch (e) { + labelText = null; + } + } + labelText = vm.utils.createLabelFromDatasource(trip.dataSource, labelText); + labelReplaceInfo = processPattern(labelText, vm.ctx.datasources, trip.dSIndex); + label = fillPattern(labelText, labelReplaceInfo, trip.timeRange[vm.index]); + if (vm.staticSettings.useLabelFunction && angular.isDefined(vm.staticSettings.labelFunction)) { + try { + labelText = vm.staticSettings.labelFunction(vm.ctx.data, trip.timeRange[vm.index], trip.dSIndex); + } catch (e) { + labelText = null; + } + } + } + return label; + } + + function calculateTooltip(trip) { + let tooltip = ''; + if (vm.staticSettings.displayTooltip) { + let tooltipReplaceInfo; + let tooltipText = vm.staticSettings.tooltipPattern; + if (vm.staticSettings.useTooltipFunction && angular.isDefined(vm.staticSettings.tooltipFunction)) { + try { + tooltipText = vm.staticSettings.tooltipFunction(vm.ctx.data, trip.timeRange[vm.index], trip.dSIndex); + } catch (e) { + tooltipText = null; + } + } + tooltipText = vm.utils.createLabelFromDatasource(trip.dataSource, tooltipText); + tooltipReplaceInfo = processPattern(tooltipText, vm.ctx.datasources, trip.dSIndex); + tooltip = fillPattern(tooltipText, tooltipReplaceInfo, trip.timeRange[vm.index]); + tooltip = fillPatternWithActions(tooltip, 'onTooltipAction', null); + + } + return tooltip; + } + + function calculateColor(trip) { + let color = vm.staticSettings.pathColor; + let colorFn; + if (vm.staticSettings.usePathColorFunction && angular.isDefined(vm.staticSettings.colorFunction)) { + try { + colorFn = vm.staticSettings.colorFunction(vm.ctx.data, trip.timeRange[vm.index], trip.dSIndex); + } catch (e) { + colorFn = null; + } + } + if (colorFn && colorFn !== color && trip.polyline) { + trip.polyline.setStyle({color: colorFn}); + } + return colorFn || color; + } + + function calculateIcon(trip) { + let icon = vm.staticSettings.icon; + if (vm.staticSettings.useMarkerImageFunction && angular.isDefined(vm.staticSettings.markerImageFunction)) { + let rawIcon; + try { + rawIcon = vm.staticSettings.markerImageFunction(vm.ctx.data, vm.staticSettings.markerImages, trip.timeRange[vm.index], trip.dSIndex); + } catch (e) { + rawIcon = null; + } + if (rawIcon) { + vm.utils.loadImageAspect(rawIcon).then( + (aspect) => { + if (aspect) { + let width; + let height; + if (aspect > 1) { + width = rawIcon.size; + height = rawIcon.size / aspect; + } else { + width = rawIcon.size * aspect; + height = rawIcon.size; + } + icon = L.icon({ + iconUrl: rawIcon, + iconSize: [width, height], + iconAnchor: [width / 2, height / 2] + }); + } + if (trip.marker) { + trip.marker.setIcon(icon); + } + } + ) + } + } + return icon; + } + + function createUpdatePath() { + if (vm.trips && vm.map) { + vm.trips.forEach(function (trip) { + if (trip.marker) { + trip.marker.remove(); + } + if (trip.polyline) { + trip.polyline.remove(); + } + if (trip.points && trip.points.length) { + trip.points.forEach(function (point) { + point.remove(); + }) + } + }) + } + let normalizedTimeRange = createNormalizedTime(vm.data, 1000); + createNormalizedTrips(normalizedTimeRange, vm.datasources); + createTripsOnMap(); + vm.trips.forEach(function (trip) { + vm.map.extendBounds(vm.map.bounds, trip.polyline); + vm.map.fitBounds(vm.map.bounds); + }) + + } + + function fillPattern(pattern, replaceInfo, currentNormalizedValue) { + let text = angular.copy(pattern); + let reg = /\$\{([^\}]*)\}/g; + if (replaceInfo) { + for (let v = 0; v < replaceInfo.variables.length; v++) { + let variableInfo = replaceInfo.variables[v]; + let label = reg.exec(pattern)[1].split(":")[0]; + let txtVal = ''; + if (label.length > -1 && angular.isDefined(currentNormalizedValue[label])) { + let varData = currentNormalizedValue[label]; + if (isNumber(varData)) { + txtVal = padValue(varData, variableInfo.valDec, 0); + } else { + txtVal = varData; + } + } + text = text.split(variableInfo.variable).join(txtVal); + } + } + return text; + } + + function createNormalizedTime(data, step) { + if (!step) step = 1000; + let max_time = null; + let min_time = null; + let normalizedArray = []; + if (data && data.length > 0) { + vm.data.forEach(function (data) { + if (data.data.length > 0) { + data.data.forEach(function (sData) { + if (max_time === null) { + max_time = sData[0]; + } else if (max_time < sData[0]) { + max_time = sData[0] + } + if (min_time === null) { + min_time = sData[0]; + } else if (min_time > sData[0]) { + min_time = sData[0]; + } + }) + } + }); + for (let i = min_time; i < max_time; i += step) { + normalizedArray.push({ts: i}) + } + if (normalizedArray[normalizedArray.length - 1] && normalizedArray[normalizedArray.length - 1].ts !== max_time) normalizedArray.push({ts: max_time}); + } + vm.maxTime = normalizedArray.length - 1; + vm.minTime = 0; + return normalizedArray; + } + + function createNormalizedTrips(timeRange, dataSources) { + vm.trips = []; + if (timeRange && timeRange.length > 0 && dataSources && dataSources.length > 0 && vm.data && vm.data.length > 0) { + dataSources.forEach(function (dS, index) { + vm.trips.push({ + dataSource: dS, + dSIndex: index, + timeRange: angular.copy(timeRange) + }) + }); + + vm.data.forEach(function (data) { + let ds = data.datasource; + let tripIndex = vm.trips.findIndex(function (el) { + return el.dataSource.entityId === ds.entityId; + }); + + if (tripIndex > -1) { + createNormalizedValue(data.data, data.dataKey.name, vm.trips[tripIndex].timeRange); + } + }) + } + + createNormalizedLatLngs(); + } + + function createNormalizedValue(dataArray, dataKey, timeRangeArray) { + timeRangeArray.forEach(function (timeStamp) { + let targetTDiff = null; + let value = null; + for (let i = 0; i < dataArray.length; i++) { + let tDiff = dataArray[i][0] - timeStamp.ts; + if (targetTDiff === null || (tDiff <= 0 && targetTDiff < tDiff)) { + targetTDiff = tDiff; + value = dataArray[i][1]; + + } + } + if (value !== null) timeStamp[dataKey] = value; + }); + } + + function createNormalizedLatLngs() { + vm.trips.forEach(function (el) { + el.latLngs = []; + el.timeRange.forEach(function (data) { + let lat = data[vm.staticSettings.latKeyName]; + let lng = data[vm.staticSettings.lngKeyName]; + if (lat && lng && vm.map) { + data.latLng = vm.map.createLatLng(lat, lng); + } + el.latLngs.push(data.latLng); + }); + addAngleForTip(el); + }) + } + + function addAngleForTip(trip) { + if (trip.timeRange && trip.timeRange.length > 0) { + trip.timeRange.forEach(function (point, index) { + let nextPoint, prevPoint; + nextPoint = index === (trip.timeRange.length - 1) ? trip.timeRange[index] : trip.timeRange[index + 1]; + prevPoint = index === 0 ? trip.timeRange[0] : trip.timeRange[index - 1]; + point.h = findAngle(prevPoint[vm.staticSettings.latKeyName], prevPoint[vm.staticSettings.lngKeyName], nextPoint[vm.staticSettings.latKeyName], nextPoint[vm.staticSettings.lngKeyName]); + point.h += vm.staticSettings.rotationAngle; + }); + } + } + + function createTripsOnMap() { + if (vm.trips.length > 0) { + vm.trips.forEach(function (trip) { + if (trip.timeRange.length > 0 && trip.latLngs.every(el => angular.isDefined(el))) { + configureTripSettings(trip, vm.index); + if (vm.staticSettings.showPoints) { + trip.points = []; + trip.latLngs.forEach(function (latLng) { + let point = L.circleMarker(latLng, { + color: trip.settings.pointColor, + radius: trip.settings.pointSize + }).addTo(vm.map.map); + trip.points.push(point); + }); + } + + if (angular.isUndefined(trip.marker)) { + trip.polyline = vm.map.createPolyline(trip.latLngs, trip.settings); + } + + if (trip.timeRange && trip.timeRange.length && angular.isUndefined(trip.marker)) { + trip.marker = L.marker(trip.timeRange[vm.index].latLng).addTo(vm.map.map); + trip.marker.setZIndexOffset(1000); + trip.marker.setIcon(vm.staticSettings.icon); + trip.marker.setRotationOrigin('center center'); + // trip.marker.addTo(vm.map.map); + trip.marker.on('click', function () { + showHideTooltip(trip); + }); + moveMarker(trip); + } + } + }); + } + } + + function moveMarker(trip) { + if (angular.isDefined(trip.marker)) { + trip.marker.setLatLng(trip.timeRange[vm.index].latLng); + trip.marker.setRotationAngle(trip.timeRange[vm.index].h + vm.staticSettings.rotationAngle); + trip.marker.update(); + } else { + if (trip.timeRange && trip.timeRange.length) { + trip.marker = L.marker(trip.timeRange[vm.index].latLng); + trip.marker.setZIndexOffset(1000); + trip.marker.setIcon(vm.staticSettings.icon); + trip.marker.setRotationOrigin('center center'); + trip.marker.addTo(vm.map.map); + trip.marker.on('click', function () { + showHideTooltip(trip); + }); + trip.marker.update(); + } + + } + configureTripSettings(trip); + } + + function showHideTooltip(trip) { + if (vm.staticSettings.displayTooltip) { + if (vm.staticSettings.showTooltip && trip && vm.activeTripIndex !== trip.dSIndex) { + vm.staticSettings.showTooltip = true; + } else { + vm.staticSettings.showTooltip = !vm.staticSettings.showTooltip; + } + } + if (trip && vm.activeTripIndex !== trip.dSIndex) vm.activeTripIndex = trip.dSIndex; + } + + $log.log(vm); +} \ No newline at end of file diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss new file mode 100644 index 0000000000..85ddcfdb3c --- /dev/null +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -0,0 +1,104 @@ +/** +* Copyright © 2016-2019 The Thingsboard Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +.heat-map-widget { + position: relative; + width: 100%; + height: 100%; + padding-top: 35px; +} + +.heat-map-info-panel { + position: absolute; + top: 0; + right: 0; + z-index: 2; + background-color: rgba(0, 0, 0, .3); + border-bottom-left-radius: 5px; + + .md-button { + min-width: auto; + } +} + +.heat-map-tooltip { + position: absolute; + top: 47px; + right: 0; + z-index: 2; + padding: 10px; + background-color: #fff; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + transition: .3s ease-in-out; + + &-hidden { + transform: translateX(100%); + } +} + +.heat-map-title { + padding: 10px; +} + +.heat-map-control-panel { + position: absolute; + bottom: 0; + z-index: 2; + box-sizing: border-box; + width: 100%; + padding-right: 70px; + padding-left: 20px; + background: rgba(0, 0, 0, .3); + + md-slider-container { + button { + max-width: none; + + ng-md-icon { + width: 36px; + height: 36px; + + svg { + width: inherit; + height: inherit; + } + } + } + + .panel-timer { + max-width: none; + font-size: 20px; + font-weight: 600; + } + } +} + +.heat-map-container { + position: relative; + z-index: 1; + flex: 1; + width: 100%; +} + +#heat-map { + z-index: 1; + width: 100%; + height: 100%; + + .pointsLayerMarkerIcon { + border-radius: 50%; + } +} diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html new file mode 100644 index 0000000000..96e05902f4 --- /dev/null +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -0,0 +1,49 @@ + + +
+
+ {{vm.trips[vm.activeTripIndex].settings.labelText}} +
+
+ + + + + + + {{ speed}} + + + + + +
{{vm.trips[0].timeRange[vm.index].ts | date:'medium'}} +
+
+
+
+
+
+ + + + + + +
+
+
+
+
\ No newline at end of file From c6786e03d93ff95ccbfa96b583424d051af423e3 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Tue, 26 Feb 2019 15:52:22 +0200 Subject: [PATCH 16/74] Trip animation widget added to library --- .../data/json/system/widget_bundles/maps.json | 16 ++++++++++++++++ .../lib/tripAnimation/trip-animation-widget.js | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index a873c7a7e6..963c9ad71e 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -116,6 +116,22 @@ "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.24727730589425012,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.8437014651129422,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"type\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7558240907832925,\"funcBody\":\"return \\\"colorpin\\\";\"}]},{\"type\":\"function\",\"name\":\"Second Point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.19266205227372524,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7995830793603149,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.04902495467943502,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.44120841439482095,\"funcBody\":\"return \\\"thermomether\\\";\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"tmDefaultMapType\":\"roadmap\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"autocloseTooltip\":true,\"tooltipPattern\":\"
${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details
\",\"markerImageSize\":34,\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', amount = percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageFunction\":\"var type = dsData[dsIndex]['type'];\\nif (type == 'thermomether') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"labelFunction\":\"var deviceType = dsData[dsIndex]['deviceType'];\\r\\nif (typeof deviceType !== undefined) {\\r\\n if (deviceType == \\\"energy meter\\\") {\\r\\n return '${entityName}, ${energy:2} kWt';\\r\\n } else if (deviceType == \\\"thermometer\\\") {\\r\\n return '${entityName}, ${temperature:2} °C';\\r\\n }\\r\\n}\",\"tooltipFunction\":\"var deviceType = dsData[dsIndex]['deviceType'];\\r\\nif (typeof deviceType !== undefined) {\\r\\n if (deviceType == \\\"energy meter\\\") {\\r\\n return '${entityName}
Energy: ${energy:2} kWt
';\\r\\n } else if (deviceType == \\\"thermometer\\\") {\\r\\n return '${entityName}
Temperature: ${temperature:2} °C
';\\r\\n }\\r\\n}\"},\"title\":\"Tencent Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } + }, + { + "alias": "test", + "name": "Trip Animation", + "descriptor": { + "type": "timeseries", + "sizeX": 10, + "sizeY": 6.5, + "resources": [], + "templateHtml": "", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n", + "settingsSchema": "{\n \"schema\": {\n \"title\": \"Openstreet Map Configuration\",\n \"type\": \"object\",\n \"properties\": {\n \"mapProvider\": {\n \"title\": \"Map provider\",\n \"type\": \"string\",\n \"default\": \"OpenStreetMap.Mapnik\"\n },\n \"latKeyName\": {\n \"title\": \"Latitude key name\",\n \"type\": \"string\",\n \"default\": \"latitude\"\n },\n \"lngKeyName\": {\n \"title\": \"Longitude key name\",\n \"type\": \"string\",\n \"default\": \"longitude\"\n },\n \"showLabel\": {\n \"title\": \"Show label\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"label\": {\n \"title\": \"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )\",\n \"type\": \"string\",\n \"default\": \"${entityName}\"\n },\n \"useLabelFunction\": {\n \"title\": \"Use label function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"labelFunction\": {\n \"title\": \"Label function: f(data, dsData, dsIndex)\",\n \"type\": \"string\"\n },\n \"showTooltip\": {\n \"title\": \"Show tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"tooltipColor\": {\n \"title\": \"Tooltip background color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"tooltipFontColor\": {\n \"title\": \"Tooltip font color\",\n \"type\": \"string\",\n \"default\": \"#000\"\n },\n \"tooltipOpacity\": {\n \"title\": \"Tooltip opacity (0-1)\",\n \"type\": \"number\",\n \"default\": 1 \n },\n \"tooltipPattern\": {\n \"title\": \"Tooltip (for ex. 'Text ${keyName} units.' or Link text')\",\n \"type\": \"string\",\n \"default\": \"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\"\n },\n \"useTooltipFunction\": {\n \"title\": \"Use tooltip function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"tooltipFunction\": {\n \"title\": \"Tooltip function: f(data, dsData, dsIndex)\",\n \"type\": \"string\"\n },\n \"color\": {\n \"title\": \"Path color\",\n \"type\": \"string\"\n },\n \"strokeWeight\": {\n \"title\": \"Stroke weight\",\n \"type\": \"number\",\n \"default\": 2\n },\n \"strokeOpacity\": {\n \"title\": \"Stroke opacity\",\n \"type\": \"number\",\n \"default\": 1\n },\n \"useColorFunction\": {\n \"title\": \"Use path color function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"colorFunction\": {\n \"title\": \"Path color function: f(data, dsData, dsIndex)\",\n \"type\": \"string\"\n },\n \"showPoints\": {\n \"title\": \"Show points\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"pointColor\": {\n \"title\": \"Point color\",\n \"type\": \"string\"\n },\n \"pointSize\": {\n \"title\": \"Point size (px)\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultMarkerColor\": {\n \"title\": \"color for default marker\",\n \"type\": \"string\"\n },\n \"markerImage\": {\n \"title\": \"Custom marker image\",\n \"type\": \"string\"\n },\n \"markerImageSize\": {\n \"title\": \"Custom marker image size (px)\",\n \"type\": \"number\",\n \"default\": 34\n },\n \"rotationAngle\": {\n \"title\": \"Set additional rotation angle for marker (deg)\",\n \"type\": \"number\",\n \"default\": 180\n },\n \"useMarkerImageFunction\":{\n \"title\":\"Use marker image function\",\n \"type\":\"boolean\",\n \"default\":false\n },\n \"markerImageFunction\":{\n \"title\":\"Marker image function: f(data, images, dsData, dsIndex)\",\n \"type\":\"string\"\n },\n \"markerImages\":{\n \"title\":\"Marker images\",\n \"type\":\"array\",\n \"items\":{\n \"title\":\"Marker image\",\n \"type\":\"string\"\n }\n }\n },\n \"required\": []\n },\n \"form\": [{\n \"key\": \"mapProvider\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [{\n \"value\": \"OpenStreetMap.Mapnik\",\n \"label\": \"OpenStreetMap.Mapnik (Default)\"\n }, {\n \"value\": \"OpenStreetMap.BlackAndWhite\",\n \"label\": \"OpenStreetMap.BlackAndWhite\"\n }, {\n \"value\": \"OpenStreetMap.HOT\",\n \"label\": \"OpenStreetMap.HOT\"\n }, {\n \"value\": \"Esri.WorldStreetMap\",\n \"label\": \"Esri.WorldStreetMap\"\n }, {\n \"value\": \"Esri.WorldTopoMap\",\n \"label\": \"Esri.WorldTopoMap\"\n }, {\n \"value\": \"CartoDB.Positron\",\n \"label\": \"CartoDB.Positron\"\n }, {\n \"value\": \"CartoDB.DarkMatter\",\n \"label\": \"CartoDB.DarkMatter\"\n }]\n }, \"latKeyName\", \"lngKeyName\", \"showLabel\", \"label\", \"useLabelFunction\", {\n \"key\": \"labelFunction\",\n \"type\": \"javascript\"\n }, \"showTooltip\", {\n \"key\": \"tooltipColor\",\n \"type\": \"color\"\n }, {\n \"key\": \"tooltipFontColor\",\n \"type\": \"color\"\n },\"tooltipOpacity\", {\n \"key\": \"tooltipPattern\",\n \"type\": \"textarea\"\n }, \"useTooltipFunction\", {\n \"key\": \"tooltipFunction\",\n \"type\": \"javascript\"\n }, {\n \"key\": \"color\",\n \"type\": \"color\"\n }, \"useColorFunction\", {\n \"key\": \"colorFunction\",\n \"type\": \"javascript\"\n }, \"strokeWeight\", \"strokeOpacity\", \"showPoints\",{\n \"key\": \"pointColor\",\n \"type\": \"color\"\n }, \"pointSize\", {\n \"key\": \"defaultMarkerColor\",\n \"type\": \"color\"\n }, {\n \"key\": \"markerImage\",\n \"type\": \"image\"\n }, \"markerImageSize\", \"rotationAngle\",\"useMarkerImageFunction\",\n {\n \"key\":\"markerImageFunction\",\n \"type\":\"javascript\"\n }, {\n \"key\":\"markerImages\",\n \"items\":[\n {\n \"key\":\"markerImages[]\",\n \"type\":\"image\"\n }\n ]\n }]\n}", + "dataKeySettingsSchema": "{}", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" + } } ] } \ No newline at end of file diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index a55e655bb3..343ad7fd11 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -562,7 +562,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l }); if (tripIndex > -1) { - createNormalizedValue(data.data, data.dataKey.name, vm.trips[tripIndex].timeRange); + createNormalizedValue(data.data, data.dataKey.label, vm.trips[tripIndex].timeRange); } }) } From 5f8547d6351a67287d546e675169add620a49730 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Fri, 1 Mar 2019 10:08:00 +0200 Subject: [PATCH 17/74] Licence Update fro trip animation --- .../lib/tripAnimation/trip-animation-widget.js | 3 +-- .../lib/tripAnimation/trip-animation-widget.scss | 15 +++++++++++++++ .../tripAnimation/trip-animation-widget.tpl.html | 8 ++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 343ad7fd11..015e3dd4c0 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2019 The Thingsboard Authors + * Copyright © 2016-2018 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import './trip-animation-widget.scss'; import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss index 85ddcfdb3c..2eb5b915d9 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Copyright © 2016-2019 The Thingsboard Authors * diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html index 96e05902f4..42d4bcdcec 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -1,16 +1,20 @@ +-->
{{vm.trips[vm.activeTripIndex].settings.labelText}} From d7b6453bf0728badaa7c312320dc9c89b223e08e Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Fri, 1 Mar 2019 10:29:46 +0200 Subject: [PATCH 18/74] Revert "Licence Update fro trip animation" This reverts commit 5f8547d --- .../lib/tripAnimation/trip-animation-widget.js | 3 ++- .../lib/tripAnimation/trip-animation-widget.scss | 15 --------------- .../tripAnimation/trip-animation-widget.tpl.html | 8 ++------ 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 015e3dd4c0..343ad7fd11 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2018 The Thingsboard Authors + * Copyright © 2016-2019 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import './trip-animation-widget.scss'; import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss index 2eb5b915d9..85ddcfdb3c 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -1,18 +1,3 @@ -/** - * Copyright © 2016-2018 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ /** * Copyright © 2016-2019 The Thingsboard Authors * diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html index 42d4bcdcec..96e05902f4 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -1,20 +1,16 @@ +
{{vm.trips[vm.activeTripIndex].settings.labelText}} From 3ffc455e6d3c9209785e3bedf38fdbe25311e6b4 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Fri, 1 Mar 2019 10:31:00 +0200 Subject: [PATCH 19/74] Licence Update for trip animation" --- .../tripAnimation/trip-animation-widget.js | 1 - .../tripAnimation/trip-animation-widget.scss | 29 ++++++++++--------- .../trip-animation-widget.tpl.html | 6 +++- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 343ad7fd11..7bcde55ba1 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import './trip-animation-widget.scss'; import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss index 85ddcfdb3c..6fae2e611e 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -1,18 +1,19 @@ /** -* Copyright © 2016-2019 The Thingsboard Authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + .heat-map-widget { position: relative; width: 100%; diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html index 96e05902f4..af6e729c01 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -1,16 +1,20 @@ +-->
{{vm.trips[vm.activeTripIndex].settings.labelText}} From 087d5f829343b18fc325c3c942e2076d7aebefd1 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 5 Mar 2019 13:41:45 +0200 Subject: [PATCH 20/74] Kubernetes resources for ThingsBoard Microservices. --- k8s/.env | 5 + k8s/README.md | 100 +++++ k8s/cassandra.yml | 164 ++++++++ k8s/database-setup.yml | 43 ++ k8s/k8s-delete-all.sh | 18 + k8s/k8s-delete-resources.sh | 21 + k8s/k8s-deploy-resources.sh | 26 ++ k8s/k8s-install-tb.sh | 93 +++++ k8s/k8s-upgrade-tb.sh | 43 ++ k8s/postgres.yml | 95 +++++ k8s/tb-coap-transport-configmap.yml | 65 +++ k8s/tb-http-transport-configmap.yml | 65 +++ k8s/tb-mqtt-transport-configmap.yml | 65 +++ k8s/tb-namespace.yml | 22 + k8s/tb-node-cassandra-configmap.yml | 28 ++ k8s/tb-node-configmap.yml | 67 +++ k8s/tb-node-postgres-configmap.yml | 31 ++ k8s/thingsboard.yml | 608 ++++++++++++++++++++++++++++ 18 files changed, 1559 insertions(+) create mode 100644 k8s/.env create mode 100644 k8s/README.md create mode 100644 k8s/cassandra.yml create mode 100644 k8s/database-setup.yml create mode 100755 k8s/k8s-delete-all.sh create mode 100755 k8s/k8s-delete-resources.sh create mode 100755 k8s/k8s-deploy-resources.sh create mode 100755 k8s/k8s-install-tb.sh create mode 100755 k8s/k8s-upgrade-tb.sh create mode 100644 k8s/postgres.yml create mode 100644 k8s/tb-coap-transport-configmap.yml create mode 100644 k8s/tb-http-transport-configmap.yml create mode 100644 k8s/tb-mqtt-transport-configmap.yml create mode 100644 k8s/tb-namespace.yml create mode 100644 k8s/tb-node-cassandra-configmap.yml create mode 100644 k8s/tb-node-configmap.yml create mode 100644 k8s/tb-node-postgres-configmap.yml create mode 100644 k8s/thingsboard.yml diff --git a/k8s/.env b/k8s/.env new file mode 100644 index 0000000000..e50e57d9b8 --- /dev/null +++ b/k8s/.env @@ -0,0 +1,5 @@ + +# Database used by ThingsBoard, can be either postgres (PostgreSQL) or cassandra (Cassandra). +# According to the database type corresponding kubernetes resources will be deployed (see postgres.yml, cassandra.yml for details). + +DATABASE=postgres diff --git a/k8s/README.md b/k8s/README.md new file mode 100644 index 0000000000..e57790ffc4 --- /dev/null +++ b/k8s/README.md @@ -0,0 +1,100 @@ +# Kubernetes resources configuration for ThingsBoard Microservices + +This folder containing scripts and Kubernetes resources configurations to run ThingsBoard in Microservices mode. + +## Prerequisites + +ThingsBoard Microservices are running on Kubernetes cluster. +You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. +If you do not already have a cluster, you can create one by using [Minikube](https://kubernetes.io/docs/setup/minikube), +or you can choose any other available [Kubernetes cluster deployment solutions](https://kubernetes.io/docs/setup/pick-right-solution/). + +## Installation + +Before performing initial installation you can configure the type of database to be used with ThingsBoard. +In order to set database type change the value of `DATABASE` variable in `.env` file to one of the following: + +- `postgres` - use PostgreSQL database; +- `cassandra` - use Cassandra database; + +**NOTE**: According to the database type corresponding kubernetes resources will be deployed (see `postgres.yml`, `cassandra.yml` for details). + +Execute the following command to run installation: + +` +$ ./k8s-install-tb.sh --loadDemo +` + +Where: + +- `--loadDemo` - optional argument. Whether to load additional demo data. + +## Running + +Execute the following command to deploy resources: + +` +$ ./k8s-deploy-resources.sh +` + +After a while when all resources will be successfully started you can open `http://{your-cluster-ip}` in you browser (for ex. `http://192.168.99.101`). +You should see ThingsBoard login page. + +Use the following default credentials: + +- **System Administrator**: sysadmin@thingsboard.org / sysadmin + +If you installed DataBase with demo data (using `--loadDemo` flag) you can also use the following credentials: + +- **Tenant Administrator**: tenant@thingsboard.org / tenant +- **Customer User**: customer@thingsboard.org / customer + +In case of any issues you can examine service logs for errors. +For example to see ThingsBoard node logs execute the following commands: + +1) Get list of the running tb-node pods: + +` +$ kubectl get pods -l app=tb-node +` + +2) Fetch logs of tb-node pod: + +` +$ kubectl logs -f [tb-node-pod-name] +` + +Where: + +- `tb-node-pod-name` - tb-node pod name obtained from the list of the running tb-node pods. + +Or use `kubectl get pods` to see the state of all the pods. +Or use `kubectl get services` to see the state of all the services. +Or use `kubectl get deployments` to see the state of all the deployments. +See [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) command reference for details. + +Execute the following command to delete all deployed microservices: + +` +$ ./k8s-delete-resources.sh +` + +Execute the following command to delete all resources (including database): + +` +$ ./k8s-delete-all.sh +` + +## Upgrading + +In case when database upgrade is needed, execute the following commands: + +``` +$ ./k8s-delete-resources.sh +$ ./k8s-upgrade-tb.sh --fromVersion=[FROM_VERSION] +$ ./k8s-deploy-resources.sh +``` + +Where: + +- `FROM_VERSION` - from which version upgrade should be started. See [Upgrade Instructions](https://thingsboard.io/docs/user-guide/install/upgrade-instructions) for valid `fromVersion` values. diff --git a/k8s/cassandra.yml b/k8s/cassandra.yml new file mode 100644 index 0000000000..aa1d18022c --- /dev/null +++ b/k8s/cassandra.yml @@ -0,0 +1,164 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: fast + namespace: thingsboard +provisioner: k8s.io/minikube-hostpath +parameters: + type: pd-ssd +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cassandra-probe-config + namespace: thingsboard + labels: + name: cassandra-probe-config +data: + probe: | + if [[ $(nodetool status | grep $POD_IP) == *"UN"* ]]; then + if [[ $DEBUG ]]; then + echo "UN"; + fi + exit 0; + else + if [[ $DEBUG ]]; then + echo "Not Up"; + fi + exit 1; + fi +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: cassandra + namespace: thingsboard + labels: + app: cassandra +spec: + serviceName: cassandra + replicas: 1 + selector: + matchLabels: + app: cassandra + template: + metadata: + labels: + app: cassandra + spec: + volumes: + - name: cassandra-probe-config + configMap: + name: cassandra-probe-config + items: + - key: probe + path: ready-probe.sh + mode: 0777 + terminationGracePeriodSeconds: 1800 + containers: + - name: cassandra + image: cassandra:3.11.3 + imagePullPolicy: Always + ports: + - containerPort: 7000 + name: intra-node + - containerPort: 7001 + name: tls-intra-node + - containerPort: 7199 + name: jmx + - containerPort: 9042 + name: cql + - containerPort: 9160 + name: thrift + resources: + limits: + cpu: "1000m" + memory: 2Gi + requests: + cpu: "1000m" + memory: 2Gi + securityContext: + capabilities: + add: + - IPC_LOCK + lifecycle: + preStop: + exec: + command: + - /bin/sh + - -c + - nodetool drain + env: + - name: CASSANDRA_SEEDS + value: "cassandra-0.cassandra.thingsboard.svc.cluster.local" + - name: MAX_HEAP_SIZE + value: 1024M + - name: HEAP_NEWSIZE + value: 256M + - name: CASSANDRA_CLUSTER_NAME + value: "Thingsboard Cluster" + - name: CASSANDRA_DC + value: "DC1-Thingsboard-Cluster" + - name: CASSANDRA_RACK + value: "Rack-Thingsboard-Cluster" + - name: CASSANDRA_AUTO_BOOTSTRAP + value: "false" + - name: CASSANDRA_ENDPOINT_SNITCH + value: GossipingPropertyFileSnitch + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + readinessProbe: + exec: + command: + - /bin/bash + - -c + - /probe/ready-probe.sh + initialDelaySeconds: 60 + timeoutSeconds: 5 + volumeMounts: + - name: cassandra-probe-config + mountPath: /probe + - name: cassandra-data + mountPath: /var/lib/cassandra + volumeClaimTemplates: + - metadata: + name: cassandra-data + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: fast + resources: + requests: + storage: 1Gi +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: cassandra + name: cassandra + namespace: thingsboard +spec: + clusterIP: None + ports: + - port: 9042 + selector: + app: cassandra +--- diff --git a/k8s/database-setup.yml b/k8s/database-setup.yml new file mode 100644 index 0000000000..d73a685b74 --- /dev/null +++ b/k8s/database-setup.yml @@ -0,0 +1,43 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: Pod +metadata: + name: tb-db-setup + namespace: thingsboard +spec: + volumes: + - name: tb-node-config + configMap: + name: tb-node-config + items: + - key: conf + path: thingsboard.conf + - key: logback + path: logback.xml + containers: + - name: tb-db-setup + imagePullPolicy: Always + image: thingsboard/tb-node:latest + envFrom: + - configMapRef: + name: tb-node-db-config + volumeMounts: + - mountPath: /config + name: tb-node-config + command: ['sh', '-c', 'while [ ! -f /install-finished ]; do sleep 2; done;'] + restartPolicy: Never diff --git a/k8s/k8s-delete-all.sh b/k8s/k8s-delete-all.sh new file mode 100755 index 0000000000..a90829368e --- /dev/null +++ b/k8s/k8s-delete-all.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +kubectl -n thingsboard delete svc,sts,deploy,pv,pvc,cm,po,ing --all --include-uninitialized diff --git a/k8s/k8s-delete-resources.sh b/k8s/k8s-delete-resources.sh new file mode 100755 index 0000000000..87f25ebedd --- /dev/null +++ b/k8s/k8s-delete-resources.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +kubectl config set-context $(kubectl config current-context) --namespace=thingsboard +kubectl delete -f thingsboard.yml diff --git a/k8s/k8s-deploy-resources.sh b/k8s/k8s-deploy-resources.sh new file mode 100755 index 0000000000..86ec235dd7 --- /dev/null +++ b/k8s/k8s-deploy-resources.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +kubectl apply -f tb-namespace.yml +kubectl config set-context $(kubectl config current-context) --namespace=thingsboard +kubectl apply -f tb-node-configmap.yml +kubectl apply -f tb-mqtt-transport-configmap.yml +kubectl apply -f tb-http-transport-configmap.yml +kubectl apply -f tb-coap-transport-configmap.yml +kubectl apply -f thingsboard.yml diff --git a/k8s/k8s-install-tb.sh b/k8s/k8s-install-tb.sh new file mode 100755 index 0000000000..9fa9c0ea3a --- /dev/null +++ b/k8s/k8s-install-tb.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +function installTb() { + + loadDemo=$1 + + kubectl apply -f tb-node-configmap.yml + kubectl apply -f database-setup.yml && + kubectl wait --for=condition=Ready pod/tb-db-setup --timeout=120s && + kubectl exec tb-db-setup -- sh -c 'export INSTALL_TB=true; export LOAD_DEMO='"$loadDemo"'; start-tb-node.sh; touch /install-finished;' + + kubectl delete pod tb-db-setup + +} + +function installPostgres() { + + kubectl apply -f postgres.yml + kubectl apply -f tb-node-postgres-configmap.yml + + kubectl rollout status deployment/postgres +} + +function installCassandra() { + + kubectl apply -f cassandra.yml + kubectl apply -f tb-node-cassandra-configmap.yml + + kubectl rollout status statefulset/cassandra + + kubectl exec -it cassandra-0 -- bash -c "cqlsh -e \ + \"CREATE KEYSPACE IF NOT EXISTS thingsboard \ + WITH replication = { \ + 'class' : 'SimpleStrategy', \ + 'replication_factor' : 1 \ + };\"" +} + +while [[ $# -gt 0 ]] +do +key="$1" + +case $key in + --loadDemo) + LOAD_DEMO=true + shift # past argument + ;; + *) + # unknown option + ;; +esac +shift # past argument or value +done + +if [ "$LOAD_DEMO" == "true" ]; then + loadDemo=true +else + loadDemo=false +fi + +source .env + +kubectl apply -f tb-namespace.yml +kubectl config set-context $(kubectl config current-context) --namespace=thingsboard + +case $DATABASE in + postgres) + installPostgres + installTb ${loadDemo} + ;; + cassandra) + installCassandra + installTb ${loadDemo} + ;; + *) + echo "Unknown DATABASE value specified: '${DATABASE}'. Should be either postgres or cassandra." >&2 + exit 1 +esac diff --git a/k8s/k8s-upgrade-tb.sh b/k8s/k8s-upgrade-tb.sh new file mode 100755 index 0000000000..35dc11f5d1 --- /dev/null +++ b/k8s/k8s-upgrade-tb.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +for i in "$@" +do +case $i in + --fromVersion=*) + FROM_VERSION="${i#*=}" + shift + ;; + *) + # unknown option + ;; +esac +done + +if [[ -z "${FROM_VERSION// }" ]]; then + echo "--fromVersion parameter is invalid or unspecified!" + echo "Usage: k8s-upgrade-tb.sh --fromVersion={VERSION}" + exit 1 +else + fromVersion="${FROM_VERSION// }" +fi + +kubectl apply -f database-setup.yml && +kubectl wait --for=condition=Ready pod/tb-db-setup --timeout=120s && +kubectl exec tb-db-setup -- sh -c 'export UPGRADE_TB=true; export FROM_VERSION='"$fromVersion"'; start-tb-node.sh; touch /install-finished;' + +kubectl delete pod tb-db-setup diff --git a/k8s/postgres.yml b/k8s/postgres.yml new file mode 100644 index 0000000000..2e7f2cc815 --- /dev/null +++ b/k8s/postgres.yml @@ -0,0 +1,95 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pv-claim + namespace: thingsboard + labels: + app: postgres +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: postgres + namespace: thingsboard + labels: + app: postgres +spec: + template: + metadata: + labels: + app: postgres + spec: + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-pv-claim + containers: + - name: postgres + imagePullPolicy: Always + image: postgres:9.6 + ports: + - containerPort: 5432 + name: postgres + env: + - name: POSTGRES_DB + value: "thingsboard" + volumeMounts: + - mountPath: /var/lib/postgresql/data + name: postgres-data + livenessProbe: + exec: + command: + - pg_isready + - -h + - localhost + - -U + - postgres + initialDelaySeconds: 60 + timeoutSeconds: 30 + readinessProbe: + exec: + command: + - pg_isready + - -h + - localhost + - -U + - postgres + initialDelaySeconds: 5 + timeoutSeconds: 1 + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-database + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: postgres + ports: + - port: 5432 + name: postgres +--- diff --git a/k8s/tb-coap-transport-configmap.yml b/k8s/tb-coap-transport-configmap.yml new file mode 100644 index 0000000000..5381f766f4 --- /dev/null +++ b/k8s/tb-coap-transport-configmap.yml @@ -0,0 +1,65 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: tb-coap-transport-config + namespace: thingsboard + labels: + name: tb-coap-transport-config +data: + conf: | + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" + export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" + export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" + export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" + export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" + export LOG_FILENAME=tb-coap-transport.out + export LOADER_PATH=/usr/share/tb-coap-transport/conf + logback: | + + + + + /var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.log + + /var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/k8s/tb-http-transport-configmap.yml b/k8s/tb-http-transport-configmap.yml new file mode 100644 index 0000000000..5376b82cef --- /dev/null +++ b/k8s/tb-http-transport-configmap.yml @@ -0,0 +1,65 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: tb-http-transport-config + namespace: thingsboard + labels: + name: tb-http-transport-config +data: + conf: | + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" + export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" + export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" + export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" + export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" + export LOG_FILENAME=tb-http-transport.out + export LOADER_PATH=/usr/share/tb-http-transport/conf + logback: | + + + + + /var/log/tb-http-transport/${TB_HOST}/tb-http-transport.log + + /var/log/tb-http-transport/${TB_HOST}/tb-http-transport.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/k8s/tb-mqtt-transport-configmap.yml b/k8s/tb-mqtt-transport-configmap.yml new file mode 100644 index 0000000000..1e73a08db7 --- /dev/null +++ b/k8s/tb-mqtt-transport-configmap.yml @@ -0,0 +1,65 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: tb-mqtt-transport-config + namespace: thingsboard + labels: + name: tb-mqtt-transport-config +data: + conf: | + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" + export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" + export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" + export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" + export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" + export LOG_FILENAME=tb-mqtt-transport.out + export LOADER_PATH=/usr/share/tb-mqtt-transport/conf + logback: | + + + + + /var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.log + + /var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/k8s/tb-namespace.yml b/k8s/tb-namespace.yml new file mode 100644 index 0000000000..113009635f --- /dev/null +++ b/k8s/tb-namespace.yml @@ -0,0 +1,22 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: Namespace +metadata: + name: thingsboard + labels: + name: thingsboard diff --git a/k8s/tb-node-cassandra-configmap.yml b/k8s/tb-node-cassandra-configmap.yml new file mode 100644 index 0000000000..21a05bd6f6 --- /dev/null +++ b/k8s/tb-node-cassandra-configmap.yml @@ -0,0 +1,28 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: tb-node-db-config + namespace: thingsboard + labels: + name: tb-node-db-config +data: + DATABASE_TS_TYPE: cassandra + DATABASE_ENTITIES_TYPE: cassandra + CASSANDRA_URL: cassandra:9042 + CASSANDRA_SOCKET_READ_TIMEOUT: "60000" diff --git a/k8s/tb-node-configmap.yml b/k8s/tb-node-configmap.yml new file mode 100644 index 0000000000..550a0665d1 --- /dev/null +++ b/k8s/tb-node-configmap.yml @@ -0,0 +1,67 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: tb-node-config + namespace: thingsboard + labels: + name: tb-node-config +data: + conf: | + export JAVA_OPTS="$JAVA_OPTS -Dplatform=deb -Dinstall.data_dir=/usr/share/thingsboard/data" + export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" + export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" + export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" + export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" + export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" + export LOG_FILENAME=thingsboard.out + export LOADER_PATH=/usr/share/thingsboard/conf,/usr/share/thingsboard/extensions + logback: | + + + + + /var/log/thingsboard/${TB_HOST}/thingsboard.log + + /var/log/thingsboard/${TB_HOST}/thingsboard.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/k8s/tb-node-postgres-configmap.yml b/k8s/tb-node-postgres-configmap.yml new file mode 100644 index 0000000000..3e07cb863c --- /dev/null +++ b/k8s/tb-node-postgres-configmap.yml @@ -0,0 +1,31 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: tb-node-db-config + namespace: thingsboard + labels: + name: tb-node-db-config +data: + DATABASE_TS_TYPE: sql + DATABASE_ENTITIES_TYPE: sql + SPRING_JPA_DATABASE_PLATFORM: org.hibernate.dialect.PostgreSQLDialect + SPRING_DRIVER_CLASS_NAME: org.postgresql.Driver + SPRING_DATASOURCE_URL: jdbc:postgresql://tb-database:5432/thingsboard + SPRING_DATASOURCE_USERNAME: postgres + SPRING_DATASOURCE_PASSWORD: postgres diff --git a/k8s/thingsboard.yml b/k8s/thingsboard.yml new file mode 100644 index 0000000000..0022e72d77 --- /dev/null +++ b/k8s/thingsboard.yml @@ -0,0 +1,608 @@ +# +# Copyright © 2016-2019 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: zookeeper + namespace: thingsboard +spec: + template: + metadata: + labels: + app: zookeeper + spec: + containers: + - name: server + imagePullPolicy: Always + image: zookeeper:3.5 + ports: + - containerPort: 2181 + readinessProbe: + periodSeconds: 5 + tcpSocket: + port: 2181 + livenessProbe: + periodSeconds: 5 + tcpSocket: + port: 2181 + env: + - name: ZOO_MY_ID + value: "1" + - name: ZOO_SERVERS + value: "server.1=0.0.0.0:2888:3888;0.0.0.0:2181" + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: zookeeper + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: zookeeper + ports: + - name: zk-port + port: 2181 +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-kafka + namespace: thingsboard +spec: + template: + metadata: + labels: + app: tb-kafka + spec: + containers: + - name: server + imagePullPolicy: Always + image: wurstmeister/kafka + ports: + - containerPort: 9092 + readinessProbe: + periodSeconds: 20 + tcpSocket: + port: 9092 + livenessProbe: + periodSeconds: 5 + tcpSocket: + port: 9092 + env: + - name: KAFKA_ZOOKEEPER_CONNECT + value: "zookeeper:2181" + - name: KAFKA_LISTENERS + value: "INSIDE://:9093,OUTSIDE://:9092" + - name: KAFKA_ADVERTISED_LISTENERS + value: "INSIDE://:9093,OUTSIDE://tb-kafka:9092" + - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP + value: "INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT" + - name: KAFKA_INTER_BROKER_LISTENER_NAME + value: "INSIDE" + - name: KAFKA_CREATE_TOPICS + value: "js.eval.requests:100:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.rule-engine:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600" + - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE + value: "false" + - name: KAFKA_LOG_RETENTION_BYTES + value: "1073741824" + - name: KAFKA_LOG_SEGMENT_BYTES + value: "268435456" + - name: KAFKA_LOG_RETENTION_MS + value: "300000" + - name: KAFKA_LOG_CLEANUP_POLICY + value: "delete" + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-kafka + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: tb-kafka + ports: + - name: tb-kafka-port + port: 9092 +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-redis + namespace: thingsboard +spec: + template: + metadata: + labels: + app: tb-redis + spec: + containers: + - name: server + imagePullPolicy: Always + image: redis:4.0 + ports: + - containerPort: 6379 + readinessProbe: + periodSeconds: 5 + tcpSocket: + port: 6379 + livenessProbe: + periodSeconds: 5 + tcpSocket: + port: 6379 + volumeMounts: + - mountPath: /data + name: redis-data + volumes: + - name: redis-data + emptyDir: {} + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-redis + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: tb-redis + ports: + - name: tb-redis-port + port: 6379 +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-js-executor + namespace: thingsboard +spec: + replicas: 20 + selector: + matchLabels: + app: tb-js-executor + template: + metadata: + labels: + app: tb-js-executor + spec: + containers: + - name: server + imagePullPolicy: Always + image: thingsboard/tb-js-executor:latest + env: + - name: REMOTE_JS_EVAL_REQUEST_TOPIC + value: "js.eval.requests" + - name: TB_KAFKA_SERVERS + value: "tb-kafka:9092" + - name: LOGGER_LEVEL + value: "info" + - name: LOG_FOLDER + value: "logs" + - name: LOGGER_FILENAME + value: "tb-js-executor-%DATE%.log" + - name: DOCKER_MODE + value: "true" + - name: SCRIPT_BODY_TRACE_FREQUENCY + value: "1000" + restartPolicy: Always +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-node + namespace: thingsboard +spec: + replicas: 2 + selector: + matchLabels: + app: tb-node + template: + metadata: + labels: + app: tb-node + spec: + volumes: + - name: tb-node-config + configMap: + name: tb-node-config + items: + - key: conf + path: thingsboard.conf + - key: logback + path: logback.xml + containers: + - name: server + imagePullPolicy: Always + image: thingsboard/tb-node:latest + ports: + - containerPort: 8080 + name: http + - containerPort: 9001 + name: rpc + env: + - name: RPC_HOST + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CLUSTER_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: TB_HOST + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ZOOKEEPER_ENABLED + value: "true" + - name: ZOOKEEPER_URL + value: "zookeeper:2181" + - name: TB_KAFKA_SERVERS + value: "tb-kafka:9092" + - name: JS_EVALUATOR + value: "remote" + - name: TRANSPORT_TYPE + value: "remote" + - name: CACHE_TYPE + value: "redis" + - name: REDIS_HOST + value: "tb-redis" + - name: HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE + value: "false" + envFrom: + - configMapRef: + name: tb-node-db-config + volumeMounts: + - mountPath: /config + name: tb-node-config + livenessProbe: + httpGet: + path: /login + port: http + initialDelaySeconds: 120 + timeoutSeconds: 10 + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-node + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: tb-node + ports: + - port: 8080 + name: http +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-mqtt-transport + namespace: thingsboard +spec: + replicas: 2 + selector: + matchLabels: + app: tb-mqtt-transport + template: + metadata: + labels: + app: tb-mqtt-transport + spec: + volumes: + - name: tb-mqtt-transport-config + configMap: + name: tb-mqtt-transport-config + items: + - key: conf + path: tb-mqtt-transport.conf + - key: logback + path: logback.xml + containers: + - name: server + imagePullPolicy: Always + image: thingsboard/tb-mqtt-transport:latest + ports: + - containerPort: 1883 + name: mqtt + env: + - name: CLUSTER_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: TB_HOST + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MQTT_BIND_ADDRESS + value: "0.0.0.0" + - name: MQTT_BIND_PORT + value: "1883" + - name: MQTT_TIMEOUT + value: "10000" + - name: TB_KAFKA_SERVERS + value: "tb-kafka:9092" + volumeMounts: + - mountPath: /config + name: tb-mqtt-transport-config + readinessProbe: + periodSeconds: 20 + tcpSocket: + port: 1883 + livenessProbe: + periodSeconds: 20 + tcpSocket: + port: 1883 + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-mqtt-transport + namespace: thingsboard +spec: + type: LoadBalancer + selector: + app: tb-mqtt-transport + ports: + - port: 1883 + targetPort: 1883 + name: mqtt +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-http-transport + namespace: thingsboard +spec: + replicas: 2 + selector: + matchLabels: + app: tb-http-transport + template: + metadata: + labels: + app: tb-http-transport + spec: + volumes: + - name: tb-http-transport-config + configMap: + name: tb-http-transport-config + items: + - key: conf + path: tb-http-transport.conf + - key: logback + path: logback.xml + containers: + - name: server + imagePullPolicy: Always + image: thingsboard/tb-http-transport:latest + ports: + - containerPort: 8080 + name: http + env: + - name: CLUSTER_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: TB_HOST + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: HTTP_BIND_ADDRESS + value: "0.0.0.0" + - name: HTTP_BIND_PORT + value: "8080" + - name: HTTP_REQUEST_TIMEOUT + value: "60000" + - name: TB_KAFKA_SERVERS + value: "tb-kafka:9092" + volumeMounts: + - mountPath: /config + name: tb-http-transport-config + readinessProbe: + periodSeconds: 20 + tcpSocket: + port: 8080 + livenessProbe: + periodSeconds: 20 + tcpSocket: + port: 8080 + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-http-transport + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: tb-http-transport + ports: + - port: 8080 + name: http +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-coap-transport + namespace: thingsboard +spec: + replicas: 2 + selector: + matchLabels: + app: tb-coap-transport + template: + metadata: + labels: + app: tb-coap-transport + spec: + volumes: + - name: tb-coap-transport-config + configMap: + name: tb-coap-transport-config + items: + - key: conf + path: tb-coap-transport.conf + - key: logback + path: logback.xml + containers: + - name: server + imagePullPolicy: Always + image: thingsboard/tb-coap-transport:latest + ports: + - containerPort: 5683 + name: coap + protocol: UDP + env: + - name: CLUSTER_NODE_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: TB_HOST + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: COAP_BIND_ADDRESS + value: "0.0.0.0" + - name: COAP_BIND_PORT + value: "5683" + - name: COAP_TIMEOUT + value: "10000" + - name: TB_KAFKA_SERVERS + value: "tb-kafka:9092" + volumeMounts: + - mountPath: /config + name: tb-coap-transport-config + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-coap-transport + namespace: thingsboard +spec: + type: LoadBalancer + selector: + app: tb-coap-transport + ports: + - port: 5683 + name: coap + protocol: UDP +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tb-web-ui + namespace: thingsboard +spec: + replicas: 2 + selector: + matchLabels: + app: tb-web-ui + template: + metadata: + labels: + app: tb-web-ui + spec: + containers: + - name: server + imagePullPolicy: Always + image: thingsboard/tb-web-ui:latest + ports: + - containerPort: 8080 + name: http + env: + - name: HTTP_BIND_ADDRESS + value: "0.0.0.0" + - name: HTTP_BIND_PORT + value: "8080" + - name: TB_ENABLE_PROXY + value: "false" + - name: LOGGER_LEVEL + value: "info" + - name: LOG_FOLDER + value: "logs" + - name: LOGGER_FILENAME + value: "tb-web-ui-%DATE%.log" + - name: DOCKER_MODE + value: "true" + livenessProbe: + httpGet: + path: /index.html + port: http + initialDelaySeconds: 120 + timeoutSeconds: 10 + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: tb-web-ui + namespace: thingsboard +spec: + type: ClusterIP + selector: + app: tb-web-ui + ports: + - port: 8080 + name: http +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: tb-ingress + namespace: thingsboard + annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" +spec: + rules: + - http: + paths: + - path: /api/v1/.* + backend: + serviceName: tb-http-transport + servicePort: 8080 + - path: /static/rulenode/.* + backend: + serviceName: tb-node + servicePort: 8080 + - path: /static/.* + backend: + serviceName: tb-web-ui + servicePort: 8080 + - path: /index.html.* + backend: + serviceName: tb-web-ui + servicePort: 8080 + - path: / + backend: + serviceName: tb-web-ui + servicePort: 8080 + - path: /.* + backend: + serviceName: tb-node + servicePort: 8080 +--- \ No newline at end of file From 491c5b0f3d5283c18c57bf4fe7b21a7e9c081a57 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 5 Mar 2019 16:50:11 +0200 Subject: [PATCH 21/74] Removed main method --- .../service/session/DefaultDeviceSessionCacheService.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/session/DefaultDeviceSessionCacheService.java b/application/src/main/java/org/thingsboard/server/service/session/DefaultDeviceSessionCacheService.java index c50fb62194..e4062b1b78 100644 --- a/application/src/main/java/org/thingsboard/server/service/session/DefaultDeviceSessionCacheService.java +++ b/application/src/main/java/org/thingsboard/server/service/session/DefaultDeviceSessionCacheService.java @@ -47,11 +47,4 @@ public class DefaultDeviceSessionCacheService implements DeviceSessionCacheServi log.debug("[{}] Pushing session data to cache: {}", deviceId, sessions); return sessions; } - - public static void main (String[] args){ - UUID uuid = UUID.fromString("d5db434e-9cd2-4903-8b3b-421b2d93664d"); - System.out.println(uuid.getMostSignificantBits()); - System.out.println(uuid.getLeastSignificantBits()); - } - } From 42f222dbac6009ee7d00d06a661ecb79850d5781 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 13:14:46 +0200 Subject: [PATCH 22/74] Fix main pom --- pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pom.xml b/pom.xml index de61a9c317..dcaad0a9e4 100755 --- a/pom.xml +++ b/pom.xml @@ -437,16 +437,6 @@ spring-boot-starter-test ${spring-boot.version} test - - - net.bytebuddy - byte-buddy - - - org.assertj - assertj-core - - org.springframework.boot From 378b893db9d233c929898bcb04bb0efc997ef11e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 13:58:37 +0200 Subject: [PATCH 23/74] Update dependency versions --- pom.xml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index dcaad0a9e4..8a6a9a7351 100755 --- a/pom.xml +++ b/pom.xml @@ -29,10 +29,10 @@ ${basedir} - 2.1.0.RELEASE - 5.1.2.RELEASE - 5.1.1.RELEASE - 2.1.2.RELEASE + 2.1.3.RELEASE + 5.1.5.RELEASE + 5.1.4.RELEASE + 2.1.5.RELEASE 2.9.0 0.7.0 2.2.0 @@ -87,6 +87,7 @@ 2.57 2.7.7 1.23 + 1.9.3 @@ -524,6 +525,16 @@ antlr ${antlr.version} + + net.bytebuddy + byte-buddy + ${bytebuddy.version} + + + net.bytebuddy + byte-buddy-agent + ${bytebuddy.version} + com.rabbitmq amqp-client From 4c7468102198fe06a406fb74e07a258b51d58bd6 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 15:57:43 +0200 Subject: [PATCH 24/74] Diagnostic output --- .../thingsboard/server/rules/RuleEngineSqlTestSuite.java | 8 ++++++++ .../org/thingsboard/server/system/SystemSqlTestSuite.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java index c49a029dc4..d4b04289a8 100644 --- a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -28,6 +28,14 @@ import java.util.Arrays; "org.thingsboard.server.rules.lifecycle.sql.*Test"}) public class RuleEngineSqlTestSuite { + static { + SecurityManager appsm = System.getSecurityManager(); + System.out.println("SECURITY MANAGER = " + appsm); + if (appsm != null) { + System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); + } + } + @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 3ddcdf752e..76145c954d 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -29,6 +29,14 @@ import java.util.Arrays; @ClasspathSuite.ClassnameFilters({"org.thingsboard.server.system.sql.*SqlTest"}) public class SystemSqlTestSuite { + static { + SecurityManager appsm = System.getSecurityManager(); + System.out.println("SECURITY MANAGER = " + appsm); + if (appsm != null) { + System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); + } + } + @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), From 1aac2ec758c245e5ab4c6705102a55fabe0de394 Mon Sep 17 00:00:00 2001 From: okolesnik Date: Wed, 6 Mar 2019 16:06:18 +0200 Subject: [PATCH 25/74] UI. Possibility to transmit interval into time service --- ui/src/app/api/time.service.js | 6 ++---- ui/src/app/components/dashboard.directive.js | 6 +++--- ui/src/app/components/widget/widget.controller.js | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ui/src/app/api/time.service.js b/ui/src/app/api/time.service.js index 1acea59b5b..9ca6e186b6 100644 --- a/ui/src/app/api/time.service.js +++ b/ui/src/app/api/time.service.js @@ -256,11 +256,9 @@ function TimeService($translate, $http, $q, types) { return timewindow; } - function toHistoryTimewindow(timewindow, startTimeMs, endTimeMs) { - - var interval = 0; + function toHistoryTimewindow(timewindow, startTimeMs, endTimeMs, interval = 0) { if (timewindow.history) { - interval = timewindow.history.interval; + interval = angular.isDefined(interval) ? interval : timewindow.history.interval; } else if (timewindow.realtime) { interval = timewindow.realtime.interval; } diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js index 5ffd218acc..831ecc29f6 100644 --- a/ui/src/app/components/dashboard.directive.js +++ b/ui/src/app/components/dashboard.directive.js @@ -219,14 +219,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } }, 0); }, - onUpdateTimewindow: function(startTimeMs, endTimeMs) { + onUpdateTimewindow: function(startTimeMs, endTimeMs, interval) { if (!vm.originalDashboardTimewindow) { vm.originalDashboardTimewindow = angular.copy(vm.dashboardTimewindow); } $timeout(function() { - vm.dashboardTimewindow = timeService.toHistoryTimewindow(vm.dashboardTimewindow, startTimeMs, endTimeMs); + vm.dashboardTimewindow = timeService.toHistoryTimewindow(vm.dashboardTimewindow, startTimeMs, endTimeMs, interval); }, 0); - } + }, }; addResizeListener(gridsterParent[0], onGridsterParentResize); // eslint-disable-line no-undef diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js index a58064320c..b5ad7ac0d5 100644 --- a/ui/src/app/components/widget/widget.controller.js +++ b/ui/src/app/components/widget/widget.controller.js @@ -76,9 +76,9 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele defaultSubscription: null, dashboardTimewindow: dashboardTimewindow, timewindowFunctions: { - onUpdateTimewindow: function(startTimeMs, endTimeMs) { + onUpdateTimewindow: function(startTimeMs, endTimeMs, interval) { if (widgetContext.defaultSubscription) { - widgetContext.defaultSubscription.onUpdateTimewindow(startTimeMs, endTimeMs); + widgetContext.defaultSubscription.onUpdateTimewindow(startTimeMs, endTimeMs, interval); } }, onResetTimewindow: function() { From 0d5f09a9fa2dda522cba5503fcdc856bc729d78e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 16:29:48 +0200 Subject: [PATCH 26/74] Diagnostic output --- .../server/system/SystemSqlTestSuite.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 76145c954d..ae45e35fa9 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -15,11 +15,21 @@ */ package org.thingsboard.server.system; +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.asm.MemberSubstitution; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager; +import org.hibernate.HibernateException; +import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; +import org.hibernate.bytecode.internal.bytebuddy.HibernateMethodLookupDispatcher; import org.junit.ClassRule; import org.junit.extensions.cpsuite.ClasspathSuite; import org.junit.runner.RunWith; import org.thingsboard.server.dao.CustomSqlUnit; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; /** @@ -30,11 +40,74 @@ import java.util.Arrays; public class SystemSqlTestSuite { static { + //ThreadAwareSecurityManager.install(); SecurityManager appsm = System.getSecurityManager(); System.out.println("SECURITY MANAGER = " + appsm); if (appsm != null) { System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); } + + AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution; + AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution; + + //if ( System.getSecurityManager() != null ) { + getDeclaredMethodMemberSubstitution = getDeclaredMethodMemberSubstitution(); + getMethodMemberSubstitution = getMethodMemberSubstitution(); + //} + //else { + // getDeclaredMethodMemberSubstitution = null; + // getMethodMemberSubstitution = null; + //} + + System.out.println("getDeclaredMethodMemberSubstitution = " + getDeclaredMethodMemberSubstitution); + System.out.println("getMethodMemberSubstitution = " + getMethodMemberSubstitution); + } + + private static class GetDeclaredMethodAction implements PrivilegedAction { + private final Class clazz; + private final String methodName; + private final Class[] parameterTypes; + + private GetDeclaredMethodAction(Class clazz, String methodName, Class... parameterTypes) { + this.clazz = clazz; + this.methodName = methodName; + this.parameterTypes = parameterTypes; + } + + @Override + public Method run() { + try { + Method method = clazz.getDeclaredMethod( methodName, parameterTypes ); + + return method; + } + catch (NoSuchMethodException e) { + throw new HibernateException( "Unable to prepare getDeclaredMethod()/getMethod() substitution", e ); + } + } + } + + + private static AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution() { + // this should only be called if the security manager is enabled, thus the privileged calls + return MemberSubstitution.relaxed() + .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, + "getDeclaredMethod", String.class, Class[].class ) ) ) ) + .replaceWith( + AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, + "getDeclaredMethod", Class.class, String.class, Class[].class ) ) ) + .on( ElementMatchers.isTypeInitializer() ); + } + + private static AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution() { + // this should only be called if the security manager is enabled, thus the privileged calls + return MemberSubstitution.relaxed() + .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, + "getMethod", String.class, Class[].class ) ) ) ) + .replaceWith( + AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, + "getMethod", Class.class, String.class, Class[].class ) ) ) + .on( ElementMatchers.isTypeInitializer() ); } @ClassRule From 72453499afc45bd8d71a6f26942d65440065691f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 17:10:53 +0200 Subject: [PATCH 27/74] Remove security manager after nosql test completion. --- .../server/rules/RuleEngineSqlTestSuite.java | 8 --- .../server/system/SystemSqlTestSuite.java | 72 ------------------- .../server/dao/CustomCassandraCQLUnit.java | 1 + 3 files changed, 1 insertion(+), 80 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java index d4b04289a8..c49a029dc4 100644 --- a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -28,14 +28,6 @@ import java.util.Arrays; "org.thingsboard.server.rules.lifecycle.sql.*Test"}) public class RuleEngineSqlTestSuite { - static { - SecurityManager appsm = System.getSecurityManager(); - System.out.println("SECURITY MANAGER = " + appsm); - if (appsm != null) { - System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); - } - } - @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index ae45e35fa9..2fbf3814f7 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -15,21 +15,11 @@ */ package org.thingsboard.server.system; -import net.bytebuddy.asm.AsmVisitorWrapper; -import net.bytebuddy.asm.MemberSubstitution; -import net.bytebuddy.matcher.ElementMatchers; -import org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager; -import org.hibernate.HibernateException; -import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; -import org.hibernate.bytecode.internal.bytebuddy.HibernateMethodLookupDispatcher; import org.junit.ClassRule; import org.junit.extensions.cpsuite.ClasspathSuite; import org.junit.runner.RunWith; import org.thingsboard.server.dao.CustomSqlUnit; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; /** @@ -46,68 +36,6 @@ public class SystemSqlTestSuite { if (appsm != null) { System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); } - - AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution; - AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution; - - //if ( System.getSecurityManager() != null ) { - getDeclaredMethodMemberSubstitution = getDeclaredMethodMemberSubstitution(); - getMethodMemberSubstitution = getMethodMemberSubstitution(); - //} - //else { - // getDeclaredMethodMemberSubstitution = null; - // getMethodMemberSubstitution = null; - //} - - System.out.println("getDeclaredMethodMemberSubstitution = " + getDeclaredMethodMemberSubstitution); - System.out.println("getMethodMemberSubstitution = " + getMethodMemberSubstitution); - } - - private static class GetDeclaredMethodAction implements PrivilegedAction { - private final Class clazz; - private final String methodName; - private final Class[] parameterTypes; - - private GetDeclaredMethodAction(Class clazz, String methodName, Class... parameterTypes) { - this.clazz = clazz; - this.methodName = methodName; - this.parameterTypes = parameterTypes; - } - - @Override - public Method run() { - try { - Method method = clazz.getDeclaredMethod( methodName, parameterTypes ); - - return method; - } - catch (NoSuchMethodException e) { - throw new HibernateException( "Unable to prepare getDeclaredMethod()/getMethod() substitution", e ); - } - } - } - - - private static AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution() { - // this should only be called if the security manager is enabled, thus the privileged calls - return MemberSubstitution.relaxed() - .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, - "getDeclaredMethod", String.class, Class[].class ) ) ) ) - .replaceWith( - AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, - "getDeclaredMethod", Class.class, String.class, Class[].class ) ) ) - .on( ElementMatchers.isTypeInitializer() ); - } - - private static AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution() { - // this should only be called if the security manager is enabled, thus the privileged calls - return MemberSubstitution.relaxed() - .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, - "getMethod", String.class, Class[].class ) ) ) ) - .replaceWith( - AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, - "getMethod", Class.class, String.class, Class[].class ) ) ) - .on( ElementMatchers.isTypeInitializer() ); } @ClassRule diff --git a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java index b57cd33c6c..c74dbd54e5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java +++ b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java @@ -82,6 +82,7 @@ public class CustomCassandraCQLUnit extends BaseCassandraUnit { session = null; cluster = null; } + System.setSecurityManager(null); } // Getters for those who do not like to directly access fields From 008b5dac47dd65cdc48593ef2ab8c68b84544a9e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 17:31:14 +0200 Subject: [PATCH 28/74] Cleanup --- .../thingsboard/server/system/SystemSqlTestSuite.java | 9 --------- pom.xml | 11 ----------- 2 files changed, 20 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 2fbf3814f7..3ddcdf752e 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -29,15 +29,6 @@ import java.util.Arrays; @ClasspathSuite.ClassnameFilters({"org.thingsboard.server.system.sql.*SqlTest"}) public class SystemSqlTestSuite { - static { - //ThreadAwareSecurityManager.install(); - SecurityManager appsm = System.getSecurityManager(); - System.out.println("SECURITY MANAGER = " + appsm); - if (appsm != null) { - System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); - } - } - @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), diff --git a/pom.xml b/pom.xml index 8a6a9a7351..7f026e3943 100755 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,6 @@ 2.57 2.7.7 1.23 - 1.9.3 @@ -525,16 +524,6 @@ antlr ${antlr.version} - - net.bytebuddy - byte-buddy - ${bytebuddy.version} - - - net.bytebuddy - byte-buddy-agent - ${bytebuddy.version} - com.rabbitmq amqp-client From adb08a1d7b7679d051317081b680702efda807ce Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 7 Mar 2019 12:58:45 +0200 Subject: [PATCH 29/74] Actor message processors NPE fix. --- .../device/DeviceActorMessageProcessor.java | 22 +++--- .../RuleChainActorMessageProcessor.java | 68 ++++++++++--------- .../RuleNodeActorMessageProcessor.java | 13 ++-- .../AbstractMqttTelemetryIntegrationTest.java | 2 +- 4 files changed, 60 insertions(+), 45 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 0f40dd37f5..aa1f8bc909 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -118,17 +118,23 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { this.rpcSubscriptions = new HashMap<>(); this.toDeviceRpcPendingMap = new HashMap<>(); this.toServerRpcPendingMap = new HashMap<>(); - initAttributes(); - restoreSessions(); + if (initAttributes()) { + restoreSessions(); + } } - private void initAttributes() { + private boolean initAttributes() { Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId); - this.deviceName = device.getName(); - this.deviceType = device.getType(); - this.defaultMetaData = new TbMsgMetaData(); - this.defaultMetaData.putValue("deviceName", deviceName); - this.defaultMetaData.putValue("deviceType", deviceType); + if (device != null) { + this.deviceName = device.getName(); + this.deviceType = device.getType(); + this.defaultMetaData = new TbMsgMetaData(); + this.defaultMetaData.putValue("deviceName", deviceName); + this.defaultMetaData.putValue("deviceType", deviceType); + return true; + } else { + return false; + } } void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 368634eb06..44b6f3b6c5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -91,17 +91,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); - log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); - // Creating and starting the actors; - for (RuleNode ruleNode : ruleNodeList) { - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + if (ruleChain != null) { + ruleChainName = ruleChain.getName(); + List ruleNodeList = service.getRuleChainNodes(tenantId, entityId); + log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); + // Creating and starting the actors; + for (RuleNode ruleNode : ruleNodeList) { + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } + initRoutes(ruleChain, ruleNodeList); + started = true; } - initRoutes(ruleChain, ruleNodeList); - started = true; } else { onUpdate(context); } @@ -110,31 +112,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); - log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); - for (RuleNode ruleNode : ruleNodeList) { - RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); - if (existing == null) { - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); - } else { - log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - existing.setSelf(ruleNode); - existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + if (ruleChain != null) { + ruleChainName = ruleChain.getName(); + List ruleNodeList = service.getRuleChainNodes(tenantId, entityId); + log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); + for (RuleNode ruleNode : ruleNodeList) { + RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); + if (existing == null) { + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } else { + log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + existing.setSelf(ruleNode); + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + } } - } - Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); - List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); - removedRules.forEach(ruleNodeId -> { - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); - RuleNodeCtx removed = nodeActors.remove(ruleNodeId); - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); - }); + Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); + List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); + removedRules.forEach(ruleNodeId -> { + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); + }); - initRoutes(ruleChain, ruleNodeList); + initRoutes(ruleChain, ruleNodeList); + } } @Override diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java index fc8ff3dc0a..a6543795ac 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java @@ -55,7 +55,9 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor componentClazz = Class.forName(ruleNode.getType()); - TbNode tbNode = (TbNode) (componentClazz.newInstance()); - tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + TbNode tbNode = null; + if (ruleNode != null) { + Class componentClazz = Class.forName(ruleNode.getType()); + tbNode = (TbNode) (componentClazz.newInstance()); + tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + } return tbNode; } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java index 8e8639c50c..50de5727f9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java @@ -111,7 +111,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); String payload = "{\"key\":\"value\"}"; String result = doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); - latch.await(3, TimeUnit.SECONDS); + latch.await(10, TimeUnit.SECONDS); assertEquals(payload, callback.getPayload()); assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); } From 92e5110ab990ae9a3bf69e7daceb2f5022fac3fe Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 7 Mar 2019 16:01:44 +0200 Subject: [PATCH 30/74] Remove unnecessary imports in trip animation widget. --- .../app/widget/lib/tripAnimation/trip-animation-widget.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 7bcde55ba1..1185ca5e37 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -18,8 +18,6 @@ import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; import L from 'leaflet'; //import tinycolor from 'tinycolor2'; -import MultiOptionsPolyline from '../../../../vendor/leaflet-multi-options-polyline/Leaflet.MultiOptionsPolyline'; -import GeometryUtil from '../../../../vendor/leaflet-geometryutil/leaflet-geometryutil'; import tinycolor from "tinycolor2"; import {fillPatternWithActions, isNumber, padValue, processPattern} from "../widget-utils"; //import {fillPatternWithActions, isNumber, padValue, processPattern, fillPattern} from "../widget-utils"; @@ -147,12 +145,6 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l vm.showHideTooltip = showHideTooltip; vm.recalculateTrips = recalculateTrips; - L.MultiOptionsPolyline = MultiOptionsPolyline; - L.GeometryUtil = GeometryUtil; - L.multiOptionsPolyline = function (latlngs, options) { - return new MultiOptionsPolyline(latlngs, options); - }; - $scope.$watch('vm.ctx', function () { if (vm.ctx) { vm.utils = vm.ctx.$scope.$injector.get('utils'); From 9145696b7fab9e58bafb7308dd1544a99fa242ff Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Thu, 7 Mar 2019 18:00:49 +0200 Subject: [PATCH 31/74] Update for trip animation --- .../tripAnimation/trip-animation-widget.js | 53 ++++++++++++------- .../tripAnimation/trip-animation-widget.scss | 3 +- .../trip-animation-widget.tpl.html | 5 +- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 7bcde55ba1..51a3a9b2c8 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -17,9 +17,7 @@ import './trip-animation-widget.scss'; import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; import L from 'leaflet'; -//import tinycolor from 'tinycolor2'; -import MultiOptionsPolyline from '../../../../vendor/leaflet-multi-options-polyline/Leaflet.MultiOptionsPolyline'; -import GeometryUtil from '../../../../vendor/leaflet-geometryutil/leaflet-geometryutil'; +// //import tinycolor from 'tinycolor2'; import tinycolor from "tinycolor2"; import {fillPatternWithActions, isNumber, padValue, processPattern} from "../widget-utils"; //import {fillPatternWithActions, isNumber, padValue, processPattern, fillPattern} from "../widget-utils"; @@ -128,6 +126,8 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l //const varsRegex = /\$\{([^\}]*)\}/g; //let icon; + vm.initBounds = true; + vm.markers = []; vm.index = 0; vm.dsIndex = 0; @@ -147,12 +147,6 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l vm.showHideTooltip = showHideTooltip; vm.recalculateTrips = recalculateTrips; - L.MultiOptionsPolyline = MultiOptionsPolyline; - L.GeometryUtil = GeometryUtil; - L.multiOptionsPolyline = function (latlngs, options) { - return new MultiOptionsPolyline(latlngs, options); - }; - $scope.$watch('vm.ctx', function () { if (vm.ctx) { vm.utils = vm.ctx.$scope.$injector.get('utils'); @@ -240,8 +234,14 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l vm.showTimestamp = vm.settings.showTimestamp !== false; vm.ctx.$element = angular.element("#heat-map", vm.ctx.$container); - //vm.map = L.map(vm.ctx.$element[0]).setView([0, 0], 2); - vm.map = new TbOpenStreetMap(vm.ctx.$element, vm.utils, initCallback, 2, null, null, vm.staticSettings.mapProvider); + vm.defaultZoomLevel = 2; + if (vm.ctx.settings.defaultZoomLevel) { + if (vm.ctx.settings.defaultZoomLevel > 0 && vm.ctx.settings.defaultZoomLevel < 21) { + vm.defaultZoomLevel = Math.floor(vm.ctx.settings.defaultZoomLevel); + } + } + vm.dontFitMapBounds = vm.ctx.settings.fitMapBounds === false; + vm.map = new TbOpenStreetMap(vm.ctx.$element, vm.utils, initCallback, vm.defaultZoomLevel, vm.dontFitMapBounds, null, vm.staticSettings.mapProvider); vm.map.bounds = vm.map.createBounds(); vm.map.invalidateSize(true); vm.map.bounds = vm.map.createBounds(); @@ -262,6 +262,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l staticSettings.lngKeyName = vm.ctx.settings.lngKeyName || "longitude"; staticSettings.rotationAngle = vm.ctx.settings.rotationAngle || 0; staticSettings.displayTooltip = vm.ctx.settings.showTooltip || false; + staticSettings.defaultZoomLevel = vm.ctx.settings.defaultZoomLevel || true; staticSettings.showTooltip = false; staticSettings.label = vm.ctx.settings.label || "${entityName}"; staticSettings.useLabelFunction = vm.ctx.settings.useLabelFunction || false; @@ -282,7 +283,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l staticSettings.pointColor = vm.ctx.settings.pointColor ? tinycolor(vm.ctx.settings.pointColor).toHexString() : "#ff6300"; staticSettings.markerImages = vm.ctx.settings.markerImages || []; staticSettings.icon = L.icon({ - iconUrl: "data:image/png;base64,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", + iconUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKoAAACqCAYAAAA9dtSCAAAAhnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjadY7LDcAwCEPvTNERCBA+40RVI3WDjl+iNMqp7wCWBZbheu4Ox6AggVRzDVVMJCSopXCcMGIhLGPnnHybSyraNjBNoeGGsg/l8xeV1bWbmGnVU0/KdLqY2HPmH4xUHDVih7S2Gv34q8ULVzos2Vmq5r4AAAoGaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTcwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTcwIgogICB0aWZmOkltYWdlV2lkdGg9IjE3MCIKICAgdGlmZjpJbWFnZUhlaWdodD0iMTcwIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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", iconSize: [30, 30], iconAnchor: [15, 15] }); @@ -468,24 +469,38 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l vm.trips.forEach(function (trip) { if (trip.marker) { trip.marker.remove(); + delete trip.marker; } if (trip.polyline) { trip.polyline.remove(); + delete trip.polyline; } if (trip.points && trip.points.length) { trip.points.forEach(function (point) { point.remove(); - }) + }); + delete trip.points; } - }) + }); + vm.initBounds = true; } let normalizedTimeRange = createNormalizedTime(vm.data, 1000); createNormalizedTrips(normalizedTimeRange, vm.datasources); createTripsOnMap(); - vm.trips.forEach(function (trip) { - vm.map.extendBounds(vm.map.bounds, trip.polyline); + if (vm.initBounds && !vm.initTrips) { + vm.trips.forEach(function (trip) { + vm.map.extendBounds(vm.map.bounds, trip.polyline); + vm.initBounds = !vm.datasources.every( + function (ds) { + return ds.dataReceived === true; + }); + vm.initTrips = vm.trips.every(function (trip) { + return angular.isDefined(trip.marker) && angular.isDefined(trip.polyline); + }); + }); + vm.map.fitBounds(vm.map.bounds); - }) + } } @@ -650,8 +665,9 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l function moveMarker(trip) { if (angular.isDefined(trip.marker)) { + trip.markerAngleIsSet = true; trip.marker.setLatLng(trip.timeRange[vm.index].latLng); - trip.marker.setRotationAngle(trip.timeRange[vm.index].h + vm.staticSettings.rotationAngle); + trip.marker.setRotationAngle(trip.timeRange[vm.index].h); trip.marker.update(); } else { if (trip.timeRange && trip.timeRange.length) { @@ -670,6 +686,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l configureTripSettings(trip); } + function showHideTooltip(trip) { if (vm.staticSettings.displayTooltip) { if (vm.staticSettings.showTooltip && trip && vm.activeTripIndex !== trip.dSIndex) { diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss index 6fae2e611e..1b6ae956c3 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -18,7 +18,8 @@ position: relative; width: 100%; height: 100%; - padding-top: 35px; + font-size: 16px; + line-height: 24px; } .heat-map-info-panel { diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html index af6e729c01..5ffc6d56f9 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -15,7 +15,8 @@ limitations under the License. --> -
+
{{vm.trips[vm.activeTripIndex].settings.labelText}}
@@ -32,7 +33,7 @@ -
{{vm.trips[0].timeRange[vm.index].ts | date:'medium'}} +
{{vm.trips[vm.activeTripIndex].timeRange[vm.index].ts | date:'medium'}}
From 56a7d4c8c0f455f0c9bbd59a931685b7137ff3d6 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 7 Mar 2019 18:26:00 +0200 Subject: [PATCH 32/74] Update fasterxml.jackson libraries version --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 7f026e3943..b0bf2a51c3 100755 --- a/pom.xml +++ b/pom.xml @@ -612,6 +612,16 @@ jackson-databind ${jackson.version} + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + com.github.fge json-schema-validator From e0bc7d838d89cb374b9565eafb4707c00981de1c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 7 Mar 2019 19:10:22 +0200 Subject: [PATCH 33/74] Update Trip animation widget settings. --- .../src/main/data/json/system/widget_bundles/maps.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index 963c9ad71e..1f4807c7d4 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -122,15 +122,15 @@ "name": "Trip Animation", "descriptor": { "type": "timeseries", - "sizeX": 10, + "sizeX": 8.5, "sizeY": 6.5, "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n", - "settingsSchema": "{\n \"schema\": {\n \"title\": \"Openstreet Map Configuration\",\n \"type\": \"object\",\n \"properties\": {\n \"mapProvider\": {\n \"title\": \"Map provider\",\n \"type\": \"string\",\n \"default\": \"OpenStreetMap.Mapnik\"\n },\n \"latKeyName\": {\n \"title\": \"Latitude key name\",\n \"type\": \"string\",\n \"default\": \"latitude\"\n },\n \"lngKeyName\": {\n \"title\": \"Longitude key name\",\n \"type\": \"string\",\n \"default\": \"longitude\"\n },\n \"showLabel\": {\n \"title\": \"Show label\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"label\": {\n \"title\": \"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )\",\n \"type\": \"string\",\n \"default\": \"${entityName}\"\n },\n \"useLabelFunction\": {\n \"title\": \"Use label function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"labelFunction\": {\n \"title\": \"Label function: f(data, dsData, dsIndex)\",\n \"type\": \"string\"\n },\n \"showTooltip\": {\n \"title\": \"Show tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"tooltipColor\": {\n \"title\": \"Tooltip background color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"tooltipFontColor\": {\n \"title\": \"Tooltip font color\",\n \"type\": \"string\",\n \"default\": \"#000\"\n },\n \"tooltipOpacity\": {\n \"title\": \"Tooltip opacity (0-1)\",\n \"type\": \"number\",\n \"default\": 1 \n },\n \"tooltipPattern\": {\n \"title\": \"Tooltip (for ex. 'Text ${keyName} units.' or Link text')\",\n \"type\": \"string\",\n \"default\": \"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\"\n },\n \"useTooltipFunction\": {\n \"title\": \"Use tooltip function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"tooltipFunction\": {\n \"title\": \"Tooltip function: f(data, dsData, dsIndex)\",\n \"type\": \"string\"\n },\n \"color\": {\n \"title\": \"Path color\",\n \"type\": \"string\"\n },\n \"strokeWeight\": {\n \"title\": \"Stroke weight\",\n \"type\": \"number\",\n \"default\": 2\n },\n \"strokeOpacity\": {\n \"title\": \"Stroke opacity\",\n \"type\": \"number\",\n \"default\": 1\n },\n \"useColorFunction\": {\n \"title\": \"Use path color function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"colorFunction\": {\n \"title\": \"Path color function: f(data, dsData, dsIndex)\",\n \"type\": \"string\"\n },\n \"showPoints\": {\n \"title\": \"Show points\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"pointColor\": {\n \"title\": \"Point color\",\n \"type\": \"string\"\n },\n \"pointSize\": {\n \"title\": \"Point size (px)\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultMarkerColor\": {\n \"title\": \"color for default marker\",\n \"type\": \"string\"\n },\n \"markerImage\": {\n \"title\": \"Custom marker image\",\n \"type\": \"string\"\n },\n \"markerImageSize\": {\n \"title\": \"Custom marker image size (px)\",\n \"type\": \"number\",\n \"default\": 34\n },\n \"rotationAngle\": {\n \"title\": \"Set additional rotation angle for marker (deg)\",\n \"type\": \"number\",\n \"default\": 180\n },\n \"useMarkerImageFunction\":{\n \"title\":\"Use marker image function\",\n \"type\":\"boolean\",\n \"default\":false\n },\n \"markerImageFunction\":{\n \"title\":\"Marker image function: f(data, images, dsData, dsIndex)\",\n \"type\":\"string\"\n },\n \"markerImages\":{\n \"title\":\"Marker images\",\n \"type\":\"array\",\n \"items\":{\n \"title\":\"Marker image\",\n \"type\":\"string\"\n }\n }\n },\n \"required\": []\n },\n \"form\": [{\n \"key\": \"mapProvider\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [{\n \"value\": \"OpenStreetMap.Mapnik\",\n \"label\": \"OpenStreetMap.Mapnik (Default)\"\n }, {\n \"value\": \"OpenStreetMap.BlackAndWhite\",\n \"label\": \"OpenStreetMap.BlackAndWhite\"\n }, {\n \"value\": \"OpenStreetMap.HOT\",\n \"label\": \"OpenStreetMap.HOT\"\n }, {\n \"value\": \"Esri.WorldStreetMap\",\n \"label\": \"Esri.WorldStreetMap\"\n }, {\n \"value\": \"Esri.WorldTopoMap\",\n \"label\": \"Esri.WorldTopoMap\"\n }, {\n \"value\": \"CartoDB.Positron\",\n \"label\": \"CartoDB.Positron\"\n }, {\n \"value\": \"CartoDB.DarkMatter\",\n \"label\": \"CartoDB.DarkMatter\"\n }]\n }, \"latKeyName\", \"lngKeyName\", \"showLabel\", \"label\", \"useLabelFunction\", {\n \"key\": \"labelFunction\",\n \"type\": \"javascript\"\n }, \"showTooltip\", {\n \"key\": \"tooltipColor\",\n \"type\": \"color\"\n }, {\n \"key\": \"tooltipFontColor\",\n \"type\": \"color\"\n },\"tooltipOpacity\", {\n \"key\": \"tooltipPattern\",\n \"type\": \"textarea\"\n }, \"useTooltipFunction\", {\n \"key\": \"tooltipFunction\",\n \"type\": \"javascript\"\n }, {\n \"key\": \"color\",\n \"type\": \"color\"\n }, \"useColorFunction\", {\n \"key\": \"colorFunction\",\n \"type\": \"javascript\"\n }, \"strokeWeight\", \"strokeOpacity\", \"showPoints\",{\n \"key\": \"pointColor\",\n \"type\": \"color\"\n }, \"pointSize\", {\n \"key\": \"defaultMarkerColor\",\n \"type\": \"color\"\n }, {\n \"key\": \"markerImage\",\n \"type\": \"image\"\n }, \"markerImageSize\", \"rotationAngle\",\"useMarkerImageFunction\",\n {\n \"key\":\"markerImageFunction\",\n \"type\":\"javascript\"\n }, {\n \"key\":\"markerImages\",\n \"items\":[\n {\n \"key\":\"markerImages[]\",\n \"type\":\"image\"\n }\n ]\n }]\n}", + "settingsSchema": "{\r\n \"schema\": {\r\n \"title\": \"Openstreet Map Configuration\",\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"mapProvider\": {\r\n \"title\": \"Map provider\",\r\n \"type\": \"string\",\r\n \"default\": \"OpenStreetMap.Mapnik\"\r\n },\r\n \"defaultZoomLevel\": {\r\n\t\t\t\t\t\"title\": \"Default map zoom level (1 - 20)\",\r\n\t\t\t\t\t\"type\": \"number\"\r\n\t\t\t\t},\r\n\t\t\t\"fitMapBounds\": {\r\n\t\t\t\t\"title\": \"Fit map bounds to cover all markers\",\r\n\t\t\t\t\"type\": \"boolean\",\r\n\t\t\t\t\"default\": true\r\n\t\t\t},\r\n \"latKeyName\": {\r\n \"title\": \"Latitude key name\",\r\n \"type\": \"string\",\r\n \"default\": \"latitude\"\r\n },\r\n \"lngKeyName\": {\r\n \"title\": \"Longitude key name\",\r\n \"type\": \"string\",\r\n \"default\": \"longitude\"\r\n },\r\n \"showLabel\": {\r\n \"title\": \"Show label\",\r\n \"type\": \"boolean\",\r\n \"default\": true\r\n },\r\n \"label\": {\r\n \"title\": \"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )\",\r\n \"type\": \"string\",\r\n \"default\": \"${entityName}\"\r\n },\r\n \"useLabelFunction\": {\r\n \"title\": \"Use label function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"labelFunction\": {\r\n \"title\": \"Label function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"showTooltip\": {\r\n \"title\": \"Show tooltip\",\r\n \"type\": \"boolean\",\r\n \"default\": true\r\n },\r\n \"tooltipColor\": {\r\n \"title\": \"Tooltip background color\",\r\n \"type\": \"string\",\r\n \"default\": \"#fff\"\r\n },\r\n \"tooltipFontColor\": {\r\n \"title\": \"Tooltip font color\",\r\n \"type\": \"string\",\r\n \"default\": \"#000\"\r\n },\r\n \"tooltipOpacity\": {\r\n \"title\": \"Tooltip opacity (0-1)\",\r\n \"type\": \"number\",\r\n \"default\": 1 \r\n },\r\n \"tooltipPattern\": {\r\n \"title\": \"Tooltip (for ex. 'Text ${keyName} units.' or Link text')\",\r\n \"type\": \"string\",\r\n \"default\": \"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\"\r\n },\r\n \"useTooltipFunction\": {\r\n \"title\": \"Use tooltip function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"tooltipFunction\": {\r\n \"title\": \"Tooltip function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"color\": {\r\n \"title\": \"Stroke color\",\r\n \"type\": \"string\"\r\n },\r\n \"strokeWeight\": {\r\n \"title\": \"Stroke weight\",\r\n \"type\": \"number\",\r\n \"default\": 2\r\n },\r\n \"strokeOpacity\": {\r\n \"title\": \"Stroke opacity\",\r\n \"type\": \"number\",\r\n \"default\": 1\r\n },\r\n \"useColorFunction\": {\r\n \"title\": \"Use stroke color function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"colorFunction\": {\r\n \"title\": \"Stroke color function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"showPoints\": {\r\n \"title\": \"Show points\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"pointColor\": {\r\n \"title\": \"Point color\",\r\n \"type\": \"string\"\r\n },\r\n \"pointSize\": {\r\n \"title\": \"Point size (px)\",\r\n \"type\": \"number\",\r\n \"default\": 10\r\n },\r\n \"defaultMarkerColor\": {\r\n \"title\": \"color for default marker\",\r\n \"type\": \"string\"\r\n },\r\n \"markerImage\": {\r\n \"title\": \"Custom marker image\",\r\n \"type\": \"string\"\r\n },\r\n \"markerImageSize\": {\r\n \"title\": \"Custom marker image size (px)\",\r\n \"type\": \"number\",\r\n \"default\": 34\r\n },\r\n \"rotationAngle\": {\r\n \"title\": \"Set additional rotation angle for marker (deg)\",\r\n \"type\": \"number\",\r\n \"default\": 180\r\n },\r\n \"useMarkerImageFunction\":{\r\n \"title\":\"Use marker image function\",\r\n \"type\":\"boolean\",\r\n \"default\":false\r\n },\r\n \"markerImageFunction\":{\r\n \"title\":\"Marker image function: f(data, images, dsData, dsIndex)\",\r\n \"type\":\"string\"\r\n },\r\n \"markerImages\":{\r\n \"title\":\"Marker images\",\r\n \"type\":\"array\",\r\n \"items\":{\r\n \"title\":\"Marker image\",\r\n \"type\":\"string\"\r\n }\r\n }\r\n },\r\n \"required\": []\r\n },\r\n \"form\": [{\r\n \"key\": \"mapProvider\",\r\n \"type\": \"rc-select\",\r\n \"multiple\": false,\r\n \"items\": [{\r\n \"value\": \"OpenStreetMap.Mapnik\",\r\n \"label\": \"OpenStreetMap.Mapnik (Default)\"\r\n }, {\r\n \"value\": \"OpenStreetMap.BlackAndWhite\",\r\n \"label\": \"OpenStreetMap.BlackAndWhite\"\r\n }, {\r\n \"value\": \"OpenStreetMap.HOT\",\r\n \"label\": \"OpenStreetMap.HOT\"\r\n }, {\r\n \"value\": \"Esri.WorldStreetMap\",\r\n \"label\": \"Esri.WorldStreetMap\"\r\n }, {\r\n \"value\": \"Esri.WorldTopoMap\",\r\n \"label\": \"Esri.WorldTopoMap\"\r\n }, {\r\n \"value\": \"CartoDB.Positron\",\r\n \"label\": \"CartoDB.Positron\"\r\n }, {\r\n \"value\": \"CartoDB.DarkMatter\",\r\n \"label\": \"CartoDB.DarkMatter\"\r\n }]\r\n },\"defaultZoomLevel\", \"fitMapBounds\", \"latKeyName\", \"lngKeyName\", \"showLabel\", \"label\", \"useLabelFunction\", {\r\n \"key\": \"labelFunction\",\r\n \"type\": \"javascript\"\r\n }, \"showTooltip\", {\r\n \"key\": \"tooltipColor\",\r\n \"type\": \"color\"\r\n }, {\r\n \"key\": \"tooltipFontColor\",\r\n \"type\": \"color\"\r\n },\"tooltipOpacity\", {\r\n \"key\": \"tooltipPattern\",\r\n \"type\": \"textarea\"\r\n }, \"useTooltipFunction\", {\r\n \"key\": \"tooltipFunction\",\r\n \"type\": \"javascript\"\r\n }, {\r\n \"key\": \"color\",\r\n \"type\": \"color\"\r\n }, \"useColorFunction\", {\r\n \"key\": \"colorFunction\",\r\n \"type\": \"javascript\"\r\n }, \"strokeWeight\", \"strokeOpacity\", \"showPoints\",{\r\n \"key\": \"pointColor\",\r\n \"type\": \"color\"\r\n }, \"pointSize\", {\r\n \"key\": \"defaultMarkerColor\",\r\n \"type\": \"color\"\r\n }, {\r\n \"key\": \"markerImage\",\r\n \"type\": \"image\"\r\n }, \"markerImageSize\", \"rotationAngle\",\"useMarkerImageFunction\",\r\n {\r\n \"key\":\"markerImageFunction\",\r\n \"type\":\"javascript\"\r\n }, {\r\n \"key\":\"markerImages\",\r\n \"items\":[\r\n {\r\n \"key\":\"markerImages[]\",\r\n \"type\":\"image\"\r\n }\r\n ]\r\n }]\r\n}", "dataKeySettingsSchema": "{}", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"strokeWeight\":3,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" } } ] From 76e1af60c2742f26355ab101f9fa3745e1d9c7a0 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Mon, 11 Mar 2019 11:31:13 +0200 Subject: [PATCH 34/74] improved polygon coordinates parser --- ui/src/app/widget/lib/map-widget2.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/src/app/widget/lib/map-widget2.js b/ui/src/app/widget/lib/map-widget2.js index 4762a3fda1..f330bd61bf 100644 --- a/ui/src/app/widget/lib/map-widget2.js +++ b/ui/src/app/widget/lib/map-widget2.js @@ -546,7 +546,13 @@ export default class TbMapWidgetV2 { function mapPolygonArray (rawArray) { let latLngArray = rawArray.map(function (el) { if (el.length === 2) { - return tbMap.map.createLatLng(el[0], el[1]); + if (!angular.isNumber(el[0]) && !angular.isNumber(el[1])) { + return el.map(function (subEl) { + return mapPolygonArray(subEl); + }) + } else { + return tbMap.map.createLatLng(el[0], el[1]); + } } else if (el.length > 2) { return mapPolygonArray(el); } else { From 8247bfb1d78dd67ea6ef33d816dc10fb8fc21f21 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Mon, 11 Mar 2019 13:03:18 +0200 Subject: [PATCH 35/74] improved polygon tooltip auto close --- ui/src/app/widget/lib/google-map.js | 10 ++++++++-- ui/src/app/widget/lib/tencent-map.js | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ui/src/app/widget/lib/google-map.js b/ui/src/app/widget/lib/google-map.js index ba1ae1f65e..5abad621d1 100644 --- a/ui/src/app/widget/lib/google-map.js +++ b/ui/src/app/widget/lib/google-map.js @@ -338,15 +338,21 @@ export default class TbGoogleMap { locationSettings: settings, dsIndex: location.dsIndex }); - + let map = this; if (onClickListener) { google.maps.event.addListener(polygon, 'click', function (event) { - if (settings.displayTooltip) { + if (settings.displayTooltip ) { + if (settings.autocloseTooltip) { + map.tooltips.forEach((tooltip) => { + tooltip.popup.close(); + }); + } if (!polygon.anchor) { polygon.anchor = new google.maps.MVCObject(); } polygon.anchor.set("position", event.latLng); popup.open(this.map, polygon.anchor); + } onClickListener(); }); diff --git a/ui/src/app/widget/lib/tencent-map.js b/ui/src/app/widget/lib/tencent-map.js index 6ce387e46a..0cab5f9e14 100644 --- a/ui/src/app/widget/lib/tencent-map.js +++ b/ui/src/app/widget/lib/tencent-map.js @@ -287,7 +287,7 @@ export default class TbTencentMap { popup.open(); popup.setPosition(marker); }); - this.tooltips.push({ + map.tooltips.push({ markerArgs: markerArgs, popup: popup, locationSettings: settings, @@ -353,6 +353,11 @@ export default class TbTencentMap { if (onClickListener) { qq.maps.event.addListener(polygon, 'click', function (event) { + if (settings.autocloseTooltip) { + map.tooltips.forEach((tooltip) => { + tooltip.popup.close(); + }); + } if (settings.displayTooltip) { popup.setMap(this.map); popup.setPosition(event.latLng); From 1037123569f3b0426b5a1b25156582b312f71099 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 12 Mar 2019 16:03:53 +0200 Subject: [PATCH 36/74] Update Angular Material version to 1.1.13 --- ui/package-lock.json | 6 +++--- ui/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 9b8224e0ff..8275da082b 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -468,9 +468,9 @@ "integrity": "sha512-o+V/OzwNGpS30QmgP7DJWTdBJ2BMDut481qqB72sM0L59dkO6TNjRV7qubQCntGqGe98h9vObweQUVYTfEO4vg==" }, "angular-material": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/angular-material/-/angular-material-1.1.9.tgz", - "integrity": "sha512-kxyigi+7823k/31qQ0j6wL5FkCe/mw2bAg1kfEFzIvhUoe5Myr+0YoQyN8D8EGaaOyolXU/VPtxgKSfOCSLEBw==" + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/angular-material/-/angular-material-1.1.13.tgz", + "integrity": "sha512-qWc5WOhRa/sbQmiRwenOla2Pky3w+wgW0l5Wp3J6jmB/WWxMWW7+JMdCXo1diGEETTKTF2vLdeWTceDTNehmSw==" }, "angular-material-data-table": { "version": "0.10.10", diff --git a/ui/package.json b/ui/package.json index 2600c62438..89ee741881 100644 --- a/ui/package.json +++ b/ui/package.json @@ -27,7 +27,7 @@ "angular-gridster": "^0.13.14", "angular-hotkeys": "^1.7.0", "angular-jwt": "^0.1.6", - "angular-material": "1.1.9", + "angular-material": "1.1.13", "angular-material-data-table": "^0.10.9", "angular-material-expansion-panel": "^0.7.2", "angular-material-icons": "^0.7.1", From 282b16b1af6e1602de19fa66fa93a1bea54b3930 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 12 Mar 2019 16:14:46 +0200 Subject: [PATCH 37/74] Update missing translation handler. --- ui/src/app/locale/translate-handler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/app/locale/translate-handler.js b/ui/src/app/locale/translate-handler.js index a3e76a9c9b..b47c6eb88e 100644 --- a/ui/src/app/locale/translate-handler.js +++ b/ui/src/app/locale/translate-handler.js @@ -21,9 +21,9 @@ function ThingsboardMissingTranslateHandler($log, types) { return function (translationId) { - if (translationId && !translationId.startsWith(types.translate.customTranslationsPrefix)) { + /*if (translationId && !translationId.startsWith(types.translate.customTranslationsPrefix)) { $log.warn('Translation for ' + translationId + ' doesn\'t exist'); - } + }*/ }; } \ No newline at end of file From 850376c34dc09907f6ec9ca8e806942f217a54fb Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 12 Mar 2019 16:15:41 +0200 Subject: [PATCH 38/74] Update missing translation handler. --- ui/src/app/locale/translate-handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/locale/translate-handler.js b/ui/src/app/locale/translate-handler.js index b47c6eb88e..a24cdee00e 100644 --- a/ui/src/app/locale/translate-handler.js +++ b/ui/src/app/locale/translate-handler.js @@ -18,7 +18,7 @@ .name; /*@ngInject*/ -function ThingsboardMissingTranslateHandler($log, types) { +function ThingsboardMissingTranslateHandler(/*$log, types*/) { return function (translationId) { /*if (translationId && !translationId.startsWith(types.translate.customTranslationsPrefix)) { From effb629a331398564a50546f0bbd6c9bc693746e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 12 Mar 2019 16:16:11 +0200 Subject: [PATCH 39/74] Update missing translation handler. --- ui/src/app/locale/translate-handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/locale/translate-handler.js b/ui/src/app/locale/translate-handler.js index a24cdee00e..21d80ec327 100644 --- a/ui/src/app/locale/translate-handler.js +++ b/ui/src/app/locale/translate-handler.js @@ -20,7 +20,7 @@ /*@ngInject*/ function ThingsboardMissingTranslateHandler(/*$log, types*/) { - return function (translationId) { + return function (/*translationId*/) { /*if (translationId && !translationId.startsWith(types.translate.customTranslationsPrefix)) { $log.warn('Translation for ' + translationId + ' doesn\'t exist'); }*/ From 0d933cf0652befd9887ee9af36d20d4d8e3c7d44 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 14 Mar 2019 19:30:52 +0200 Subject: [PATCH 40/74] Entities hierarchy widget. --- .../json/system/widget_bundles/cards.json | 16 + ui/package-lock.json | 16 + ui/package.json | 2 + ui/src/app/api/entity-relation.service.js | 8 +- ui/src/app/api/widget.service.js | 3 +- ui/src/app/app.js | 3 +- ui/src/app/components/nav-tree.directive.js | 185 +++++++ ui/src/app/components/nav-tree.scss | 346 +++++++++++++ ui/src/app/components/nav-tree.tpl.html | 18 + ui/src/app/layout/index.js | 2 + ui/src/app/locale/locale.constant-en_US.json | 3 +- .../widget/lib/entities-hierarchy-widget.js | 469 ++++++++++++++++++ .../widget/lib/entities-hierarchy-widget.scss | 96 ++++ .../lib/entities-hierarchy-widget.tpl.html | 29 ++ ui/src/png/jstree/32px.png | Bin 0 -> 19444 bytes ui/src/png/jstree/40px.png | Bin 0 -> 1880 bytes 16 files changed, 1189 insertions(+), 7 deletions(-) create mode 100644 ui/src/app/components/nav-tree.directive.js create mode 100644 ui/src/app/components/nav-tree.scss create mode 100644 ui/src/app/components/nav-tree.tpl.html create mode 100644 ui/src/app/widget/lib/entities-hierarchy-widget.js create mode 100644 ui/src/app/widget/lib/entities-hierarchy-widget.scss create mode 100644 ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html create mode 100644 ui/src/png/jstree/32px.png create mode 100644 ui/src/png/jstree/40px.png diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index 6550f58da4..8833517a7b 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -116,6 +116,22 @@ "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" } + }, + { + "alias": "entities_hierarchy", + "name": "Entities hierarchy", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.hierarchyId = \"hierarchy-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('entities-hierarchy-data-updated', self.ctx.$scope.hierarchyId);\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesHierarchySettings\",\n \"properties\": {\n \"nodeRelationQueryFunction\": {\n \"title\": \"Node relations query function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeHasChildrenFunction\": {\n \"title\": \"Node has children function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeDisabledFunction\": {\n \"title\": \"Node disabled function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeIconFunction\": {\n \"title\": \"Node icon function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeTextFunction\": {\n \"title\": \"Node text function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodesSortFunction\": {\n \"title\": \"Nodes sort function: f(nodeCtx1, nodeCtx2)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"nodeRelationQueryFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeHasChildrenFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeDisabledFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeIconFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeTextFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodesSortFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", + "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {},\n \"required\": []\n },\n \"form\": []\n}", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"nodeRelationQueryFunction\":\"/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: types.entitySearchDirection.from,\\n relationTypeGroup: \\\"COMMON\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" \\\"+ data['temperature'] +\\\" °C\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\"},\"title\":\"Entities hierarchy\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}" + } } ] } \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index 8275da082b..39846f4c4e 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -7675,6 +7675,22 @@ } } }, + "jstree": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/jstree/-/jstree-3.3.7.tgz", + "integrity": "sha512-yzzalO1TbZ4HdPezO43LesGI4Wv2sB0Nl+4GfwO0YYvehGws5qtTAhlBISxfur9phMLwCtf9GjHlRx2ZLXyRnw==", + "requires": { + "jquery": ">=1.9.1" + } + }, + "jstree-bootstrap-theme": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jstree-bootstrap-theme/-/jstree-bootstrap-theme-1.0.1.tgz", + "integrity": "sha1-fV7cc6hG6Np/lPV6HMXd7p2eq0s=", + "requires": { + "jquery": ">=1.9.1" + } + }, "keycode": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", diff --git a/ui/package.json b/ui/package.json index 89ee741881..0a1f4512a1 100644 --- a/ui/package.json +++ b/ui/package.json @@ -60,6 +60,8 @@ "jquery.terminal": "^1.5.0", "js-beautify": "^1.6.4", "json-schema-defaults": "^0.2.0", + "jstree": "^3.3.7", + "jstree-bootstrap-theme": "^1.0.1", "leaflet": "^1.0.3", "leaflet-providers": "^1.1.17", "material-ui": "^0.16.1", diff --git a/ui/src/app/api/entity-relation.service.js b/ui/src/app/api/entity-relation.service.js index ad5dc63da4..9294ca39d4 100644 --- a/ui/src/app/api/entity-relation.service.js +++ b/ui/src/app/api/entity-relation.service.js @@ -164,13 +164,13 @@ function EntityRelationService($http, $q) { return deferred.promise; } - function findByQuery(query) { + function findByQuery(query, config) { var deferred = $q.defer(); var url = '/api/relations'; - $http.post(url, query).then(function success(response) { + $http.post(url, query, config).then(function success(response) { deferred.resolve(response.data); - }, function fail() { - deferred.reject(); + }, function fail(e) { + deferred.reject(e); }); return deferred.promise; } diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js index 92b9275ce4..70cf76ff1a 100644 --- a/ui/src/app/api/widget.service.js +++ b/ui/src/app/api/widget.service.js @@ -21,6 +21,7 @@ import thingsboardLedLight from '../components/led-light.directive'; import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-widget'; import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget'; import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget'; +import thingsboardEntitiesHierarchyWidget from '../widget/lib/entities-hierarchy-widget'; import thingsboardExtensionsTableWidget from '../widget/lib/extensions-table-widget'; import thingsboardRpcWidgets from '../widget/lib/rpc'; @@ -44,7 +45,7 @@ import thingsboardTypes from '../common/types.constant'; import thingsboardUtils from '../common/utils.service'; export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget, - thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardExtensionsTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils, TripAnimationWidget]) + thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardEntitiesHierarchyWidget, thingsboardExtensionsTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils, TripAnimationWidget]) .factory('widgetService', WidgetService) .name; diff --git a/ui/src/app/app.js b/ui/src/app/app.js index dbef8a9c6b..7081415ff5 100644 --- a/ui/src/app/app.js +++ b/ui/src/app/app.js @@ -52,7 +52,8 @@ import 'react-schema-form'; import react from 'ngreact'; import '@flowjs/ng-flow/dist/ng-flow-standalone.min'; import 'ngFlowchart/dist/ngFlowchart'; - +import 'jstree/dist/jstree.min'; +import 'jstree-bootstrap-theme/dist/themes/proton/style.min.css'; import 'typeface-roboto'; import 'font-awesome/css/font-awesome.min.css'; import 'angular-material/angular-material.min.css'; diff --git a/ui/src/app/components/nav-tree.directive.js b/ui/src/app/components/nav-tree.directive.js new file mode 100644 index 0000000000..c179defa8e --- /dev/null +++ b/ui/src/app/components/nav-tree.directive.js @@ -0,0 +1,185 @@ +/* + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import './nav-tree.scss'; + +/* eslint-disable import/no-unresolved, import/default */ + +import navTreeTemplate from './nav-tree.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +export default angular.module('thingsboard.directives.navTree', []) + .directive('tbNavTree', NavTree) + .name; + +/*@ngInject*/ +function NavTree() { + return { + restrict: "E", + scope: true, + bindToController: { + loadNodes: '=', + editCallbacks: '=', + onNodeSelected: '&', + onNodesInserted: '&' + }, + controller: NavTreeController, + controllerAs: 'vm', + templateUrl: navTreeTemplate + }; +} + +/*@ngInject*/ +function NavTreeController($scope, $element, types) { + + var vm = this; + vm.types = types; + + $scope.$watch('vm.loadNodes', (newVal) => { + if (newVal) { + initTree(); + } + }); + + function initTree() { + vm.treeElement = angular.element('.tb-nav-tree-container', $element) + .jstree( + { + core: { + multiple: false, + check_callback: true, + themes: { name: 'proton', responsive: true }, + data: vm.loadNodes + } + } + ); + + vm.treeElement.on("changed.jstree", function (e, data) { + if (vm.onNodeSelected) { + vm.onNodeSelected({node: data.instance.get_selected(true)[0], event: e}); + } + }); + + vm.treeElement.on("model.jstree", function (e, data) { + if (vm.onNodesInserted) { + vm.onNodesInserted({nodes: data.nodes, parent: data.parent}); + } + }); + + if (vm.editCallbacks) { + vm.editCallbacks.selectNode = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + vm.treeElement.jstree('deselect_all', true); + vm.treeElement.jstree('select_node', node); + } + }; + vm.editCallbacks.deselectAll = () => { + vm.treeElement.jstree('deselect_all'); + }; + vm.editCallbacks.getNode = (id) => { + var node = vm.treeElement.jstree('get_node', id); + return node; + }; + vm.editCallbacks.getParentNodeId = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + return vm.treeElement.jstree('get_parent', node); + } + }; + vm.editCallbacks.openNode = (id, cb) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + vm.treeElement.jstree('open_node', node, cb); + } + }; + vm.editCallbacks.nodeIsOpen = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + return vm.treeElement.jstree('is_open', node); + } else { + return true; + } + }; + vm.editCallbacks.nodeIsLoaded = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + return vm.treeElement.jstree('is_loaded', node); + } else { + return true; + } + }; + vm.editCallbacks.refreshNode = (id) => { + if (id === '#') { + vm.treeElement.jstree('refresh'); + vm.treeElement.jstree('redraw'); + } else { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + var opened = vm.treeElement.jstree('is_open', node); + vm.treeElement.jstree('refresh_node', node); + vm.treeElement.jstree('redraw'); + if (node.children && opened/* && !node.children.length*/) { + vm.treeElement.jstree('open_node', node); + } + } + } + }; + vm.editCallbacks.updateNode = (id, newName) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + vm.treeElement.jstree('rename_node', node, newName); + } + }; + vm.editCallbacks.createNode = (parentId, node, pos) => { + var parentNode = vm.treeElement.jstree('get_node', parentId); + if (parentNode) { + vm.treeElement.jstree('create_node', parentNode, node, pos); + } + }; + vm.editCallbacks.deleteNode = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + vm.treeElement.jstree('delete_node', node); + } + }; + vm.editCallbacks.disableNode = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + vm.treeElement.jstree('disable_node', node); + } + }; + vm.editCallbacks.enableNode = (id) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + vm.treeElement.jstree('enable_node', node); + } + }; + vm.editCallbacks.setNodeHasChildren = (id, hasChildren) => { + var node = vm.treeElement.jstree('get_node', id); + if (node) { + if (!node.children || !node.children.length) { + node.children = hasChildren; + node.state.loaded = !hasChildren; + node.state.opened = false; + vm.treeElement.jstree('_node_changed', node.id); + vm.treeElement.jstree('redraw'); + } + } + }; + } + } +} diff --git a/ui/src/app/components/nav-tree.scss b/ui/src/app/components/nav-tree.scss new file mode 100644 index 0000000000..4a9ac4739b --- /dev/null +++ b/ui/src/app/components/nav-tree.scss @@ -0,0 +1,346 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-nav-tree-container { + padding: 15px; + font-family: Roboto, "Helvetica Neue", sans-serif; + + &.jstree-proton { + .jstree-node, + .jstree-icon { + background-image: url("../../png/jstree/32px.png"); + } + + .jstree-last { + background: transparent; + } + + .jstree-themeicon-custom { + background-image: none; + + &.material-icons { + font-size: 18px; + } + } + + .jstree-anchor { + font-size: 16px; + } + } + + &.jstree-proton-small { + .jstree-node, + .jstree-icon { + background-image: url("../../png/jstree/32px.png"); + } + + .jstree-last { + background: transparent; + } + + .jstree-themeicon-custom { + background-image: none; + + &.material-icons { + font-size: 14px; + } + } + + .jstree-anchor { + font-size: 14px; + } + } + + &.jstree-proton-large { + .jstree-node, + .jstree-icon { + background-image: url("../../png/jstree/32px.png"); + } + + .jstree-last { + background: transparent; + } + + .jstree-themeicon-custom { + background-image: none; + + &.material-icons { + font-size: 24px; + } + } + + .jstree-anchor { + font-size: 20px; + } + } + + a { + border-bottom: none; + + i.jstree-themeicon-custom { + &.tb-user-group { + &::before { + content: "account_circle"; + } + } + + &.tb-customer-group { + &::before { + content: "supervisor_account"; + } + } + + &.tb-asset-group { + &::before { + content: "domain"; + } + } + + &.tb-device-group { + &::before { + content: "devices_other"; + } + } + + &.tb-entity-view-group { + &::before { + content: "view_quilt"; + } + } + + &.tb-dashboard-group { + &::before { + content: "dashboard"; + } + } + + &.tb-customer { + &::before { + content: "supervisor_account"; + } + } + } + } +} + +@media (max-width: 768px) { + .tb-nav-tree-container { + &.jstree-proton-responsive { + .jstree-node, + .jstree-icon, + .jstree-node > .jstree-ocl, + .jstree-themeicon, + .jstree-checkbox { + background-image: url("../../png/jstree/40px.png"); + background-size: 120px 240px; + } + + .jstree-container-ul { + overflow: visible; + } + + .jstree-themeicon-custom { + background-color: transparent; + background-image: none; + background-position: 0 0; + + &.material-icons { + margin: 0; + font-size: 24px; + } + } + + .jstree-node, + .jstree-leaf > .jstree-ocl { + background: 0 0; + } + + .jstree-node { + min-width: 40px; + min-height: 40px; + margin-left: 40px; + line-height: 40px; + white-space: nowrap; + background-repeat: repeat-y; + background-position: -80px 0; + } + + .jstree-last { + background: 0 0; + } + + .jstree-anchor { + height: 40px; + font-size: 1.1em; + font-weight: 700; + line-height: 40px; + text-shadow: 1px 1px #fff; + } + + .jstree-icon, + .jstree-icon:empty { + width: 40px; + height: 40px; + line-height: 40px; + } + + > { + .jstree-container-ul > .jstree-node { + margin-right: 0; + margin-left: 0; + } + } + + .jstree-ocl, + .jstree-themeicon, + .jstree-checkbox { + background-size: 120px 240px; + } + + .jstree-leaf > .jstree-ocl { + background: 0 0; + background-position: -40px -120px; + } + + .jstree-last > .jstree-ocl { + background-position: -40px -160px; + } + + .jstree-open > .jstree-ocl { + background-position: 0 0 !important; + } + + .jstree-closed > .jstree-ocl { + background-position: 0 -40px !important; + } + + .jstree-themeicon { + background-position: -40px -40px; + } + + .jstree-checkbox, + .jstree-checkbox:hover { + background-position: -40px -80px; + } + + &.jstree-checkbox-selection { + .jstree-clicked > .jstree-checkbox, + .jstree-clicked > .jstree-checkbox:hover { + background-position: 0 -80px; + } + } + + .jstree-checked > .jstree-checkbox, + .jstree-checked > .jstree-checkbox:hover { + background-position: 0 -80px; + } + + .jstree-anchor > .jstree-undetermined, + .jstree-anchor > .jstree-undetermined:hover { + background-position: 0 -120px; + } + + .jstree-striped { + background: 0 0; + } + + .jstree-wholerow { + height: 40px; + background: #ebebeb; + border-top: 1px solid rgba(255, 255, 255, .7); + border-bottom: 1px solid rgba(64, 64, 64, .2); + } + + .jstree-wholerow-hovered { + background: #e7f4f9; + } + + .jstree-wholerow-clicked { + background: #beebff; + } + + .jstree-children { + .jstree-last > .jstree-wholerow { + box-shadow: inset 0 -6px 3px -5px #666; + } + + .jstree-open > .jstree-wholerow { + border-top: 0; + box-shadow: inset 0 6px 3px -5px #666; + } + + .jstree-open + .jstree-open { + box-shadow: none; + } + } + + &.jstree-rtl { + .jstree-node { + margin-right: 40px; + margin-left: 0; + } + + .jstree-container-ul > .jstree-node { + margin-right: 0; + } + + .jstree-closed > .jstree-ocl { + background-position: -40px 0 !important; + } + } + } + } +} + +.tb-nav-tree .md-button.tb-active { + font-weight: 500; + background-color: rgba(255, 255, 255, .15); +} + +.tb-nav-tree, +.tb-nav-tree ul { + margin-top: 0; + list-style: none; + + &:first-child { + padding: 0; + } + + li { + .md-button { + width: 100%; + max-height: 40px; + padding: 0 16px; + margin: 0; + overflow: hidden; + line-height: 40px; + color: inherit; + text-align: left; + text-decoration: none; + text-overflow: ellipsis; + text-transform: none; + text-rendering: optimizeLegibility; + white-space: nowrap; + cursor: pointer; + border-radius: 0; + + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } +} diff --git a/ui/src/app/components/nav-tree.tpl.html b/ui/src/app/components/nav-tree.tpl.html new file mode 100644 index 0000000000..88612965ff --- /dev/null +++ b/ui/src/app/components/nav-tree.tpl.html @@ -0,0 +1,18 @@ + +
diff --git a/ui/src/app/layout/index.js b/ui/src/app/layout/index.js index bb450490a8..c0ef817a3b 100644 --- a/ui/src/app/layout/index.js +++ b/ui/src/app/layout/index.js @@ -27,6 +27,7 @@ import thingsboardApiUser from '../api/user.service'; import thingsboardNoAnimate from '../components/no-animate.directive'; import thingsboardOnFinishRender from '../components/finish-render.directive'; import thingsboardSideMenu from '../components/side-menu.directive'; +import thingsboardNavTree from '../components/nav-tree.directive'; import thingsboardDashboardAutocomplete from '../components/dashboard-autocomplete.directive'; import thingsboardKvMap from '../components/kv-map.directive'; import thingsboardJsonObjectEdit from '../components/json-object-edit.directive'; @@ -89,6 +90,7 @@ export default angular.module('thingsboard.home', [ thingsboardNoAnimate, thingsboardOnFinishRender, thingsboardSideMenu, + thingsboardNavTree, thingsboardDashboardAutocomplete, thingsboardKvMap, thingsboardJsonObjectEdit, diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 9e38c55aaa..dfef502f09 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1566,7 +1566,8 @@ "row-click": "On row click", "polygon-click": "On polygon click", "marker-click": "On marker click", - "tooltip-tag-action": "Tooltip tag action" + "tooltip-tag-action": "Tooltip tag action", + "node-selected": "On node selected" } }, "language": { diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.js b/ui/src/app/widget/lib/entities-hierarchy-widget.js new file mode 100644 index 0000000000..80a92d68f6 --- /dev/null +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.js @@ -0,0 +1,469 @@ +/* + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import './entities-hierarchy-widget.scss'; + +/* eslint-disable import/no-unresolved, import/default */ + +import entitiesHierarchyWidgetTemplate from './entities-hierarchy-widget.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +export default angular.module('thingsboard.widgets.entitiesHierarchyWidget', []) + .directive('tbEntitiesHierarchyWidget', EntitiesHierarchyWidget) + .name; + +/*@ngInject*/ +function EntitiesHierarchyWidget() { + return { + restrict: "E", + scope: true, + bindToController: { + hierarchyId: '=', + ctx: '=' + }, + controller: EntitiesHierarchyWidgetController, + controllerAs: 'vm', + templateUrl: entitiesHierarchyWidgetTemplate + }; +} + +/*@ngInject*/ +function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast, types, entityService, entityRelationService /*$filter, $mdMedia, $mdPanel, $document, $translate, $timeout, utils, types*/) { + var vm = this; + + vm.showData = true; + + vm.nodeEditCallbacks = {}; + + vm.nodeIdCounter = 0; + + vm.nodesMap = {}; + vm.pendingUpdateNodeTasks = {}; + + $scope.$watch('vm.ctx', function() { + if (vm.ctx && vm.ctx.defaultSubscription) { + vm.settings = vm.ctx.settings; + vm.widgetConfig = vm.ctx.widgetConfig; + vm.subscription = vm.ctx.defaultSubscription; + vm.datasources = vm.subscription.datasources; + initializeConfig(); + updateDatasources(); + } + }); + + $scope.$on('entities-hierarchy-data-updated', function(event, hierarchyId) { + if (vm.hierarchyId == hierarchyId) { + if (vm.subscription) { + updateNodeData(vm.subscription.data); + } + } + }); + + vm.onNodesInserted = onNodesInserted; + + vm.onNodeSelected = onNodeSelected; + + function initializeConfig() { + + var testNodeCtx = { + entity: { + id: { + entityType: 'DEVICE', + id: '123' + }, + name: 'TEST DEV1' + }, + data: {}, + level: 2 + }; + var parentNodeCtx = angular.copy(testNodeCtx); + parentNodeCtx.level = 1; + testNodeCtx.parentNodeCtx = parentNodeCtx; + + var nodeRelationQueryFunction = loadNodeCtxFunction(vm.settings.nodeRelationQueryFunction, 'nodeCtx', testNodeCtx); + var nodeIconFunction = loadNodeCtxFunction(vm.settings.nodeIconFunction, 'nodeCtx', testNodeCtx); + var nodeTextFunction = loadNodeCtxFunction(vm.settings.nodeTextFunction, 'nodeCtx', testNodeCtx); + var nodeDisabledFunction = loadNodeCtxFunction(vm.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx); + var nodeHasChildrenFunction = loadNodeCtxFunction(vm.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx); + + var testNodeCtx2 = angular.copy(testNodeCtx); + testNodeCtx2.entity.name = 'TEST DEV2'; + + var nodesSortFunction = loadNodeCtxFunction(vm.settings.nodesSortFunction, 'nodeCtx1,nodeCtx2', testNodeCtx, testNodeCtx2); + + vm.nodeRelationQueryFunction = nodeRelationQueryFunction || defaultNodeRelationQueryFunction; + vm.nodeIconFunction = nodeIconFunction || defaultNodeIconFunction; + vm.nodeTextFunction = nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name); + vm.nodeDisabledFunction = nodeDisabledFunction || (() => false); + vm.nodeHasChildrenFunction = nodeHasChildrenFunction || (() => true); + vm.nodesSortFunction = nodesSortFunction || defaultSortFunction; + } + + function loadNodeCtxFunction(functionBody, argNames, ...args) { + var nodeCtxFunction = null; + if (angular.isDefined(functionBody) && functionBody.length) { + try { + nodeCtxFunction = new Function(argNames, functionBody); + var res = nodeCtxFunction.apply(null, args); + if (angular.isUndefined(res)) { + nodeCtxFunction = null; + } + } catch (e) { + nodeCtxFunction = null; + } + } + return nodeCtxFunction; + } + + function updateDatasources() { + vm.loadNodes = loadNodes; + } + + function onNodesInserted(nodes/*, parent*/) { + if (nodes) { + nodes.forEach((nodeId) => { + var task = vm.pendingUpdateNodeTasks[nodeId]; + if (task) { + task(); + delete vm.pendingUpdateNodeTasks[nodeId]; + } + }); + } + } + + function onNodeSelected(node, event) { + var nodeId; + if (!node) { + nodeId = -1; + } else { + nodeId = node.id; + } + if (nodeId !== -1) { + var selectedNode = vm.nodesMap[nodeId]; + if (selectedNode) { + var descriptors = vm.ctx.actionsApi.getActionDescriptors('nodeSelected'); + if (descriptors.length) { + var entity = selectedNode.data.nodeCtx.entity; + vm.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx }); + } + } + } + } + + function updateNodeData(subscriptionData) { + var affectedNodes = []; + if (subscriptionData) { + for (var i=0;i { + var node = vm.nodeEditCallbacks.getNode(nodeId); + if (node) { + updateNodeStyle(vm.nodesMap[nodeId]); + } else { + vm.pendingUpdateNodeTasks[nodeId] = () => { + updateNodeStyle(vm.nodesMap[nodeId]); + }; + } + }); + } + + function updateNodeStyle(node) { + var newText = prepareNodeText(node); + if (!angular.equals(node.text, newText)) { + node.text = newText; + vm.nodeEditCallbacks.updateNode(node.id, node.text); + } + var newDisabled = vm.nodeDisabledFunction(node.data.nodeCtx); + if (!angular.equals(node.state.disabled, newDisabled)) { + node.state.disabled = newDisabled; + if (node.state.disabled) { + vm.nodeEditCallbacks.disableNode(node.id); + } else { + vm.nodeEditCallbacks.enableNode(node.id); + } + } + var newHasChildren = vm.nodeHasChildrenFunction(node.data.nodeCtx); + if (!angular.equals(node.children, newHasChildren)) { + node.children = newHasChildren; + vm.nodeEditCallbacks.setNodeHasChildren(node.id, node.children); + } + } + + function prepareNodeText(node) { + var nodeIcon = prepareNodeIcon(node.data.nodeCtx); + var nodeText = vm.nodeTextFunction(node.data.nodeCtx); + return nodeIcon + nodeText; + } + + function loadNodes(node, cb) { + if (node.id === '#') { + var tasks = []; + for (var i=0;i { + cb(prepareNodes(nodes)); + updateNodeData(vm.subscription.data); + }); + } else { + if (node.data && node.data.nodeCtx.entity && node.data.nodeCtx.entity.id && node.data.nodeCtx.entity.id.entityType !== 'function') { + var relationQuery = prepareNodeRelationQuery(node.data.nodeCtx); + entityRelationService.findByQuery(relationQuery, {ignoreErrors: true, ignoreLoading: true}).then( + (entityRelations) => { + var tasks = []; + for (var i=0;i { + cb(prepareNodes(nodes)); + }); + }, + (error) => { + var errorText = "Failed to get relations!"; + if (error && error.status === 400) { + errorText = "Invalid relations query returned by 'Node relations query function'! Please check widget configuration!"; + } + showError(errorText); + } + ); + } else { + cb([]); + } + } + } + + function showError(errorText) { + var toastParent = angular.element('.tb-entities-hierarchy', $element); + toast.showError(errorText, toastParent, 'bottom left'); + } + + function prepareNodes(nodes) { + nodes = nodes.filter((node) => node !== null); + nodes.sort((node1, node2) => vm.nodesSortFunction(node1.data.nodeCtx, node2.data.nodeCtx)); + return nodes; + } + + function datasourceToNode(datasource, parentNodeCtx) { + var deferred = $q.defer(); + resolveEntity(datasource).then( + (entity) => { + if (entity != null) { + var node = { + id: ++vm.nodeIdCounter + }; + vm.nodesMap[node.id] = node; + datasource.nodeId = node.id; + node.icon = false; + var nodeCtx = { + parentNodeCtx: parentNodeCtx, + entity: entity, + data: {} + }; + nodeCtx.level = parentNodeCtx ? parentNodeCtx.level + 1 : 1; + node.data = { + datasource: datasource, + nodeCtx: nodeCtx + }; + node.state = { + disabled: vm.nodeDisabledFunction(node.data.nodeCtx) + }; + node.text = prepareNodeText(node); + node.children = vm.nodeHasChildrenFunction(node.data.nodeCtx); + deferred.resolve(node); + } else { + deferred.resolve(null); + } + } + ); + return deferred.promise; + } + + function entityIdToNode(entityType, entityId, parentDatasource, parentNodeCtx) { + var deferred = $q.defer(); + var datasource = { + dataKeys: parentDatasource.dataKeys, + type: types.datasourceType.entity, + entityType: entityType, + entityId: entityId + }; + datasourceToNode(datasource, parentNodeCtx).then( + (node) => { + if (node != null) { + var subscriptionOptions = { + type: types.widgetType.latest.value, + datasources: [datasource], + callbacks: { + onDataUpdated: (subscription) => { + updateNodeData(subscription.data); + } + } + }; + vm.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).then( + (/*subscription*/) => { + deferred.resolve(node); + } + ); + } else { + deferred.resolve(node); + } + } + ); + return deferred.promise; + } + + function resolveEntity(datasource) { + var deferred = $q.defer(); + if (datasource.type === types.datasourceType.function) { + var entity = { + id: { + entityType: "function" + }, + name: datasource.name + } + deferred.resolve(entity); + } else { + entityService.getEntity(datasource.entityType, datasource.entityId, {ignoreLoading: true}).then( + (entity) => { + deferred.resolve(entity); + }, + () => { + deferred.resolve(null); + } + ); + } + return deferred.promise; + } + + + function prepareNodeRelationQuery(nodeCtx) { + var relationQuery = vm.nodeRelationQueryFunction(nodeCtx); + if (relationQuery && relationQuery === 'default') { + relationQuery = defaultNodeRelationQueryFunction(nodeCtx); + } + return relationQuery; + } + + function defaultNodeRelationQueryFunction(nodeCtx) { + var entity = nodeCtx.entity; + var query = { + parameters: { + rootId: entity.id.id, + rootType: entity.id.entityType, + direction: types.entitySearchDirection.from, + relationTypeGroup: "COMMON", + maxLevel: 1 + }, + filters: [ + { + relationType: "Contains", + entityTypes: [] + } + ] + }; + return query; + } + + function prepareNodeIcon(nodeCtx) { + var iconInfo = vm.nodeIconFunction(nodeCtx); + if (iconInfo && iconInfo === 'default') { + iconInfo = defaultNodeIconFunction(nodeCtx); + } + if (iconInfo && (iconInfo.iconUrl || iconInfo.materialIcon)) { + if (iconInfo.materialIcon) { + return materialIconHtml(iconInfo.materialIcon); + } else { + return iconUrlHtml(iconInfo.iconUrl); + } + } else { + return ""; + } + } + + function materialIconHtml(materialIcon) { + return ''+materialIcon+''; + } + + function iconUrlHtml(iconUrl) { + return '
 
'; + } + + function defaultNodeIconFunction(nodeCtx) { + var materialIcon = 'insert_drive_file'; + var entity = nodeCtx.entity; + if (entity && entity.id && entity.id.entityType) { + switch (entity.id.entityType) { + case 'function': + materialIcon = 'functions'; + break; + case types.entityType.device: + materialIcon = 'devices_other'; + break; + case types.entityType.asset: + materialIcon = 'domain'; + break; + case types.entityType.tenant: + materialIcon = 'supervisor_account'; + break; + case types.entityType.customer: + materialIcon = 'supervisor_account'; + break; + case types.entityType.user: + materialIcon = 'account_circle'; + break; + case types.entityType.dashboard: + materialIcon = 'dashboards'; + break; + case types.entityType.alarm: + materialIcon = 'notifications_active'; + break; + case types.entityType.entityView: + materialIcon = 'view_quilt'; + break; + } + } + return { + materialIcon: materialIcon + }; + } + + function defaultSortFunction(nodeCtx1, nodeCtx2) { + var result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType); + if (result === 0) { + result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name); + } + return result; + } +} diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.scss b/ui/src/app/widget/lib/entities-hierarchy-widget.scss new file mode 100644 index 0000000000..06ac2fc73a --- /dev/null +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.scss @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.tb-entities-hierarchy { + .tb-entities-nav-tree-panel { + overflow-x: auto; + overflow-y: auto; + + .tb-nav-tree-container { + &.jstree-proton { + .jstree-anchor { + div.node-icon { + display: inline-block; + width: 22px; + height: 22px; + margin-right: 2px; + margin-bottom: 2px; + background-color: transparent; + background-repeat: no-repeat; + background-attachment: scroll; + background-position: center center; + background-size: 18px 18px; + } + + md-icon.node-icon { + width: 22px; + min-width: 22px; + height: 22px; + min-height: 22px; + margin-right: 2px; + margin-bottom: 2px; + color: inherit; + + &.material-icons { /* stylelint-disable-line selector-max-class */ + font-size: 18px; + line-height: 22px; + text-align: center; + } + } + + &.jstree-hovered:not(.jstree-clicked), + &.jstree-disabled { + div.node-icon { /* stylelint-disable-line selector-max-class */ + opacity: .5; + } + } + } + } + } + } +} + +@media (max-width: 768px) { + .tb-entities-hierarchy { + .tb-entities-nav-tree-panel { + .tb-nav-tree-container { + &.jstree-proton-responsive { + .jstree-anchor { + div.node-icon { + width: 40px; + height: 40px; + margin: 0; + background-size: 24px 24px; + } + + md-icon.node-icon { + width: 40px; + min-width: 40px; + height: 40px; + min-height: 40px; + margin: 0; + + &.material-icons { /* stylelint-disable-line selector-max-class */ + font-size: 24px; + line-height: 40px; + } + } + } + } + } + } + } +} diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html b/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html new file mode 100644 index 0000000000..a3ea257858 --- /dev/null +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html @@ -0,0 +1,29 @@ + +
+
+
+ +
+
+
diff --git a/ui/src/png/jstree/32px.png b/ui/src/png/jstree/32px.png new file mode 100644 index 0000000000000000000000000000000000000000..719a6bcdbd14938a3ed94de95842c40472a6f624 GIT binary patch literal 19444 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 iV&_@$q2+YC87j9T{b5} Date: Fri, 15 Mar 2019 16:49:14 +0200 Subject: [PATCH 41/74] Add search to Entities Hierarchy widget. Improve widget advanced forms: add fullscreen button for area fields. --- .../json/system/widget_bundles/cards.json | 4 +- ui/src/app/components/json-form.directive.js | 9 +++ ui/src/app/components/json-form.tpl.html | 4 +- ui/src/app/components/nav-tree.directive.js | 43 ++++++++--- .../components/react/json-form-ace-editor.jsx | 36 ++++++++-- .../react/json-form-ace-editor.scss | 7 ++ .../app/components/react/json-form-array.jsx | 2 +- .../components/react/json-form-fieldset.jsx | 2 +- .../app/components/react/json-form-react.jsx | 3 +- .../react/json-form-schema-form.jsx | 18 +++-- ui/src/app/components/react/json-form.scss | 18 +++++ .../widget/lib/entities-hierarchy-widget.js | 71 +++++++++++++++++-- .../widget/lib/entities-hierarchy-widget.scss | 14 ++++ .../lib/entities-hierarchy-widget.tpl.html | 22 ++++++ 14 files changed, 221 insertions(+), 32 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index 8833517a7b..e6fa1ab632 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -128,9 +128,9 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.hierarchyId = \"hierarchy-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('entities-hierarchy-data-updated', self.ctx.$scope.hierarchyId);\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesHierarchySettings\",\n \"properties\": {\n \"nodeRelationQueryFunction\": {\n \"title\": \"Node relations query function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeHasChildrenFunction\": {\n \"title\": \"Node has children function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeDisabledFunction\": {\n \"title\": \"Node disabled function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeIconFunction\": {\n \"title\": \"Node icon function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeTextFunction\": {\n \"title\": \"Node text function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodesSortFunction\": {\n \"title\": \"Nodes sort function: f(nodeCtx1, nodeCtx2)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"nodeRelationQueryFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeHasChildrenFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeDisabledFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeIconFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeTextFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodesSortFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesHierarchySettings\",\n \"properties\": {\n \"nodeRelationQueryFunction\": {\n \"title\": \"Node relations query function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeHasChildrenFunction\": {\n \"title\": \"Node has children function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeOpenedFunction\": {\n \"title\": \"Default node opened function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeDisabledFunction\": {\n \"title\": \"Node disabled function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeIconFunction\": {\n \"title\": \"Node icon function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeTextFunction\": {\n \"title\": \"Node text function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodesSortFunction\": {\n \"title\": \"Nodes sort function: f(nodeCtx1, nodeCtx2)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"nodeRelationQueryFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeHasChildrenFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeOpenedFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeDisabledFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeIconFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeTextFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodesSortFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {},\n \"required\": []\n },\n \"form\": []\n}", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"nodeRelationQueryFunction\":\"/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: types.entitySearchDirection.from,\\n relationTypeGroup: \\\"COMMON\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" \\\"+ data['temperature'] +\\\" °C\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\"},\"title\":\"Entities hierarchy\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}" + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"nodeRelationQueryFunction\":\"/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: types.entitySearchDirection.from,\\n relationTypeGroup: \\\"COMMON\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" \\\"+ data['temperature'] +\\\" °C\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\",\"nodeOpenedFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be opened (expanded) when it first loaded.\\n\\n// The following example code will open by default nodes up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n**/\\n \"},\"title\":\"Entities hierarchy\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}" } } ] diff --git a/ui/src/app/components/json-form.directive.js b/ui/src/app/components/json-form.directive.js index 1b460b6238..80f8a6cdda 100644 --- a/ui/src/app/components/json-form.directive.js +++ b/ui/src/app/components/json-form.directive.js @@ -71,7 +71,10 @@ function JsonForm($compile, $templateCache, $mdColorPicker) { $compile(element.contents())(childScope); } + scope.isFullscreen = false; + scope.formProps = { + isFullscreen: false, option: { formDefaults: { startEmpty: true @@ -86,6 +89,10 @@ function JsonForm($compile, $templateCache, $mdColorPicker) { }, onColorClick: function(event, key, val) { scope.showColorPicker(event, val); + }, + onToggleFullscreen: function() { + scope.isFullscreen = !scope.isFullscreen; + scope.formProps.isFullscreen = scope.isFullscreen; } }; @@ -116,6 +123,8 @@ function JsonForm($compile, $templateCache, $mdColorPicker) { }); } + scope.onFullscreenChanged = function() {} + scope.validate = function(){ if (scope.schema && scope.model) { var result = utils.validateBySchema(scope.schema, scope.model); diff --git a/ui/src/app/components/json-form.tpl.html b/ui/src/app/components/json-form.tpl.html index c15f3f5eb1..f5096c25b7 100644 --- a/ui/src/app/components/json-form.tpl.html +++ b/ui/src/app/components/json-form.tpl.html @@ -15,4 +15,6 @@ limitations under the License. --> - \ No newline at end of file +
+ +
diff --git a/ui/src/app/components/nav-tree.directive.js b/ui/src/app/components/nav-tree.directive.js index c179defa8e..119ef98529 100644 --- a/ui/src/app/components/nav-tree.directive.js +++ b/ui/src/app/components/nav-tree.directive.js @@ -33,8 +33,10 @@ function NavTree() { bindToController: { loadNodes: '=', editCallbacks: '=', + enableSearch: '@?', onNodeSelected: '&', - onNodesInserted: '&' + onNodesInserted: '&', + searchCallback: '&?' }, controller: NavTreeController, controllerAs: 'vm', @@ -55,17 +57,30 @@ function NavTreeController($scope, $element, types) { }); function initTree() { + var config = { + core: { + multiple: false, + check_callback: true, + themes: { name: 'proton', responsive: true }, + data: vm.loadNodes + } + }; + + if (vm.enableSearch) { + config.plugins = ["search"]; + config.search = { + case_sensitive: false, + show_only_matches: true, + show_only_matches_children: false, + search_leaves_only: false + }; + if (vm.searchCallback) { + config.search.search_callback = (searchText, node) => vm.searchCallback({searchText: searchText, node: node}); + } + } + vm.treeElement = angular.element('.tb-nav-tree-container', $element) - .jstree( - { - core: { - multiple: false, - check_callback: true, - themes: { name: 'proton', responsive: true }, - data: vm.loadNodes - } - } - ); + .jstree(config); vm.treeElement.on("changed.jstree", function (e, data) { if (vm.onNodeSelected) { @@ -180,6 +195,12 @@ function NavTreeController($scope, $element, types) { } } }; + vm.editCallbacks.search = (searchText) => { + vm.treeElement.jstree('search', searchText); + }; + vm.editCallbacks.clearSearch = () => { + vm.treeElement.jstree('clear_search'); + }; } } } diff --git a/ui/src/app/components/react/json-form-ace-editor.jsx b/ui/src/app/components/react/json-form-ace-editor.jsx index a1f576bc43..44df6cd38d 100644 --- a/ui/src/app/components/react/json-form-ace-editor.jsx +++ b/ui/src/app/components/react/json-form-ace-editor.jsx @@ -34,8 +34,10 @@ class ThingsboardAceEditor extends React.Component { this.onFocus = this.onFocus.bind(this); this.onTidy = this.onTidy.bind(this); this.onLoad = this.onLoad.bind(this); + this.onToggleFull = this.onToggleFull.bind(this); var value = props.value ? props.value + '' : ''; this.state = { + isFull: false, value: value, focused: false }; @@ -76,9 +78,26 @@ class ThingsboardAceEditor extends React.Component { } onLoad(editor) { + this.aceEditor = editor; fixAceEditor(editor); } + onToggleFull() { + this.setState({ isFull: !this.state.isFull }); + this.props.onToggleFullscreen(); + this.updateAceEditorSize = true; + } + + componentDidUpdate() { + if (this.updateAceEditorSize) { + if (this.aceEditor) { + this.aceEditor.resize(); + this.aceEditor.renderer.updateFull(); + } + this.updateAceEditorSize = false; + } + } + render() { const styles = reactCSS({ @@ -108,18 +127,23 @@ class ThingsboardAceEditor extends React.Component { if (this.state.focused) { labelClass += " tb-focused"; } - + var containerClass = "tb-container"; + var style = this.props.form.style || {width: '100%'}; + if (this.state.isFull) { + containerClass += " fullscreen-form-field"; + } return ( -
+
+
+ style={style}/>
{this.props.error}
+ style={{opacity: this.props.valid ? '0' : '1'}}>{this.props.error}
); } diff --git a/ui/src/app/components/react/json-form-ace-editor.scss b/ui/src/app/components/react/json-form-ace-editor.scss index 3ae4530b9c..fa32b74a07 100644 --- a/ui/src/app/components/react/json-form-ace-editor.scss +++ b/ui/src/app/components/react/json-form-ace-editor.scss @@ -13,6 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +.fullscreen-form-field { + .json-form-ace-editor { + height: calc(100% - 60px); + } +} + .json-form-ace-editor { position: relative; height: 100%; diff --git a/ui/src/app/components/react/json-form-array.jsx b/ui/src/app/components/react/json-form-array.jsx index b42bac55a8..46f6457b0f 100644 --- a/ui/src/app/components/react/json-form-array.jsx +++ b/ui/src/app/components/react/json-form-array.jsx @@ -131,7 +131,7 @@ class ThingsboardArray extends React.Component { } let forms = this.props.form.items.map(function(form, index){ var copy = this.copyWithIndex(form, i); - return this.props.builder(copy, this.props.model, index, this.props.onChange, this.props.onColorClick, this.props.mapper, this.props.builder); + return this.props.builder(copy, this.props.model, index, this.props.onChange, this.props.onColorClick, this.props.onToggleFullscreen, this.props.mapper, this.props.builder); }.bind(this)); arrays.push(
  • diff --git a/ui/src/app/components/react/json-form-fieldset.jsx b/ui/src/app/components/react/json-form-fieldset.jsx index 8434025c7b..5a0f94017c 100644 --- a/ui/src/app/components/react/json-form-fieldset.jsx +++ b/ui/src/app/components/react/json-form-fieldset.jsx @@ -19,7 +19,7 @@ class ThingsboardFieldSet extends React.Component { render() { let forms = this.props.form.items.map(function(form, index){ - return this.props.builder(form, this.props.model, index, this.props.onChange, this.props.onColorClick, this.props.mapper, this.props.builder); + return this.props.builder(form, this.props.model, index, this.props.onChange, this.props.onColorClick, this.props.onToggleFullscreen, this.props.mapper, this.props.builder); }.bind(this)); return ( diff --git a/ui/src/app/components/react/json-form-react.jsx b/ui/src/app/components/react/json-form-react.jsx index 59d007532c..18b2de26ef 100644 --- a/ui/src/app/components/react/json-form-react.jsx +++ b/ui/src/app/components/react/json-form-react.jsx @@ -50,7 +50,8 @@ ReactSchemaForm.propTypes = { model: React.PropTypes.object, option: React.PropTypes.object, onModelChange: React.PropTypes.func, - onColorClick: React.PropTypes.func + onColorClick: React.PropTypes.func, + onToggleFullscreen: React.PropTypes.func } ReactSchemaForm.defaultProps = { diff --git a/ui/src/app/components/react/json-form-schema-form.jsx b/ui/src/app/components/react/json-form-schema-form.jsx index 4c3aefe66e..47d20426b7 100644 --- a/ui/src/app/components/react/json-form-schema-form.jsx +++ b/ui/src/app/components/react/json-form-schema-form.jsx @@ -63,6 +63,7 @@ class ThingsboardSchemaForm extends React.Component { this.onChange = this.onChange.bind(this); this.onColorClick = this.onColorClick.bind(this); + this.onToggleFullscreen = this.onToggleFullscreen.bind(this); this.hasConditions = false; } @@ -78,7 +79,11 @@ class ThingsboardSchemaForm extends React.Component { this.props.onColorClick(event, key, val); } - builder(form, model, index, onChange, onColorClick, mapper) { + onToggleFullscreen() { + this.props.onToggleFullscreen(); + } + + builder(form, model, index, onChange, onColorClick, onToggleFullscreen, mapper) { var type = form.type; let Field = this.mapper[type]; if(!Field) { @@ -91,7 +96,7 @@ class ThingsboardSchemaForm extends React.Component { return null; } } - return + return } render() { @@ -101,11 +106,16 @@ class ThingsboardSchemaForm extends React.Component { mapper = _.merge(this.mapper, this.props.mapper); } let forms = merged.map(function(form, index) { - return this.builder(form, this.props.model, index, this.onChange, this.onColorClick, mapper); + return this.builder(form, this.props.model, index, this.onChange, this.onColorClick, this.onToggleFullscreen, mapper); }.bind(this)); + let formClass = 'SchemaForm'; + if (this.props.isFullscreen) { + formClass += ' SchemaFormFullscreen'; + } + return ( -
    {forms}
    +
    {forms}
    ); } } diff --git a/ui/src/app/components/react/json-form.scss b/ui/src/app/components/react/json-form.scss index eabf2725ef..d36ec5c0e8 100644 --- a/ui/src/app/components/react/json-form.scss +++ b/ui/src/app/components/react/json-form.scss @@ -21,6 +21,24 @@ $swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default; $input-label-float-offset: 6px !default; $input-label-float-scale: .75 !default; +.SchemaForm { + &.SchemaFormFullscreen { + position: relative; + width: 100%; + height: 100%; + + > div:not(.fullscreen-form-field) { + display: none; + } + + > div.fullscreen-form-field { + position: relative; + width: 100%; + height: 100%; + } + } +} + .json-form-error { position: relative; bottom: -5px; diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.js b/ui/src/app/widget/lib/entities-hierarchy-widget.js index 80a92d68f6..93b3912709 100644 --- a/ui/src/app/widget/lib/entities-hierarchy-widget.js +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.js @@ -54,6 +54,25 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast vm.nodesMap = {}; vm.pendingUpdateNodeTasks = {}; + vm.query = { + search: null + }; + + vm.searchAction = { + name: 'action.search', + show: true, + onAction: function() { + vm.enterFilterMode(); + }, + icon: 'search' + }; + + vm.onNodesInserted = onNodesInserted; + vm.onNodeSelected = onNodeSelected; + vm.enterFilterMode = enterFilterMode; + vm.exitFilterMode = exitFilterMode; + vm.searchCallback = searchCallback; + $scope.$watch('vm.ctx', function() { if (vm.ctx && vm.ctx.defaultSubscription) { vm.settings = vm.ctx.settings; @@ -65,6 +84,12 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast } }); + $scope.$watch("vm.query.search", function(newVal, prevVal) { + if (!angular.equals(newVal, prevVal) && vm.query.search != null) { + updateSearchNodes(); + } + }); + $scope.$on('entities-hierarchy-data-updated', function(event, hierarchyId) { if (vm.hierarchyId == hierarchyId) { if (vm.subscription) { @@ -73,12 +98,10 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast } }); - vm.onNodesInserted = onNodesInserted; - - vm.onNodeSelected = onNodeSelected; - function initializeConfig() { + vm.ctx.widgetActions = [ vm.searchAction ]; + var testNodeCtx = { entity: { id: { @@ -98,6 +121,7 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast var nodeIconFunction = loadNodeCtxFunction(vm.settings.nodeIconFunction, 'nodeCtx', testNodeCtx); var nodeTextFunction = loadNodeCtxFunction(vm.settings.nodeTextFunction, 'nodeCtx', testNodeCtx); var nodeDisabledFunction = loadNodeCtxFunction(vm.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx); + var nodeOpenedFunction = loadNodeCtxFunction(vm.settings.nodeOpenedFunction, 'nodeCtx', testNodeCtx); var nodeHasChildrenFunction = loadNodeCtxFunction(vm.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx); var testNodeCtx2 = angular.copy(testNodeCtx); @@ -109,6 +133,7 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast vm.nodeIconFunction = nodeIconFunction || defaultNodeIconFunction; vm.nodeTextFunction = nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name); vm.nodeDisabledFunction = nodeDisabledFunction || (() => false); + vm.nodeOpenedFunction = nodeOpenedFunction || defaultNodeOpenedFunction; vm.nodeHasChildrenFunction = nodeHasChildrenFunction || (() => true); vm.nodesSortFunction = nodesSortFunction || defaultSortFunction; } @@ -129,10 +154,40 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast return nodeCtxFunction; } + function enterFilterMode () { + vm.query.search = ''; + vm.ctx.hideTitlePanel = true; + $timeout(()=>{ + angular.element(vm.ctx.$container).find('.searchInput').focus(); + }) + } + + function exitFilterMode () { + vm.query.search = null; + updateSearchNodes(); + vm.ctx.hideTitlePanel = false; + } + + function searchCallback (searchText, node) { + var theNode = vm.nodesMap[node.id]; + if (theNode && theNode.data.searchText) { + return theNode.data.searchText.includes(searchText.toLowerCase()); + } + return false; + } + function updateDatasources() { vm.loadNodes = loadNodes; } + function updateSearchNodes() { + if (vm.query.search != null) { + vm.nodeEditCallbacks.search(vm.query.search); + } else { + vm.nodeEditCallbacks.clearSearch(); + } + } + function onNodesInserted(nodes/*, parent*/) { if (nodes) { nodes.forEach((nodeId) => { @@ -222,6 +277,7 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast function prepareNodeText(node) { var nodeIcon = prepareNodeIcon(node.data.nodeCtx); var nodeText = vm.nodeTextFunction(node.data.nodeCtx); + node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : ""; return nodeIcon + nodeText; } @@ -298,7 +354,8 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast nodeCtx: nodeCtx }; node.state = { - disabled: vm.nodeDisabledFunction(node.data.nodeCtx) + disabled: vm.nodeDisabledFunction(node.data.nodeCtx), + opened: vm.nodeOpenedFunction(node.data.nodeCtx) }; node.text = prepareNodeText(node); node.children = vm.nodeHasChildrenFunction(node.data.nodeCtx); @@ -459,6 +516,10 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast }; } + function defaultNodeOpenedFunction(nodeCtx) { + return nodeCtx.level <= 4; + } + function defaultSortFunction(nodeCtx1, nodeCtx2) { var result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType); if (result === 0) { diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.scss b/ui/src/app/widget/lib/entities-hierarchy-widget.scss index 06ac2fc73a..d0f02a1392 100644 --- a/ui/src/app/widget/lib/entities-hierarchy-widget.scss +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.scss @@ -14,7 +14,21 @@ * limitations under the License. */ +.tb-has-timewindow { + .tb-entities-hierarchy { + md-toolbar { + min-height: 60px; + max-height: 60px; + } + } +} + .tb-entities-hierarchy { + md-toolbar { + min-height: 39px; + max-height: 39px; + } + .tb-entities-nav-tree-panel { overflow-x: auto; overflow-y: auto; diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html b/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html index a3ea257858..ab18748f0c 100644 --- a/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.tpl.html @@ -17,12 +17,34 @@ -->
    + +
    + + search + + {{'entity.search' | translate}} + + + + + + + + close + + {{ 'action.close' | translate }} + + +
    +
    From fc4159915aa0a1f4594b23fffdfea3aa3d575066 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 15 Mar 2019 19:07:52 +0200 Subject: [PATCH 42/74] Handle accessToken and refreshToken from url parameters. --- ui/src/app/api/user.service.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ui/src/app/api/user.service.js b/ui/src/app/api/user.service.js index 3349558204..ffcdc5f896 100644 --- a/ui/src/app/api/user.service.js +++ b/ui/src/app/api/user.service.js @@ -362,6 +362,25 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time $location.search('publicId', null); deferred.reject(); }); + } else if (locationSearch.accessToken) { + var token = locationSearch.accessToken; + var refreshToken = locationSearch.refreshToken; + $location.search('accessToken', null); + if (refreshToken) { + $location.search('refreshToken', null); + } + try { + updateAndValidateToken(token, 'jwt_token', false); + if (refreshToken) { + updateAndValidateToken(refreshToken, 'refresh_token', false); + } else { + store.remove('refresh_token'); + store.remove('refresh_token_expiration'); + } + } catch (e) { + deferred.reject(); + } + procceedJwtTokenValidate(); } else { procceedJwtTokenValidate(); } From deda5e20cac68d141ce1eae6971003ed37f36cd3 Mon Sep 17 00:00:00 2001 From: Maksym Dudnik Date: Mon, 18 Mar 2019 11:58:07 +0200 Subject: [PATCH 43/74] removed log --- ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index a65ff22d37..f87ff8ed37 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -697,6 +697,4 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter, $l } if (trip && vm.activeTripIndex !== trip.dSIndex) vm.activeTripIndex = trip.dSIndex; } - - $log.log(vm); } \ No newline at end of file From a1883c75adbc3e78cafcac428959c298bee4e744 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 18 Mar 2019 13:38:17 +0200 Subject: [PATCH 44/74] Remove unused service from trip animation controller. --- ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index f87ff8ed37..311efebf24 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -121,7 +121,7 @@ function tripAnimationWidget() { } /*@ngInject*/ -function tripAnimationController($document, $scope, $http, $timeout, $filter, $log) { +function tripAnimationController($document, $scope, $http, $timeout, $filter) { let vm = this; //const varsRegex = /\$\{([^\}]*)\}/g; //let icon; From 87a97cb46211f3a5ce2c180f95a78cbd32a40c9b Mon Sep 17 00:00:00 2001 From: descipher Date: Mon, 18 Mar 2019 20:28:45 -0500 Subject: [PATCH 45/74] Add full Canadian postal code validation. --- ui/src/app/components/contact.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/components/contact.directive.js b/ui/src/app/components/contact.directive.js index 93912882ed..cab6f52111 100644 --- a/ui/src/app/components/contact.directive.js +++ b/ui/src/app/components/contact.directive.js @@ -283,7 +283,7 @@ function Contact($compile, $templateCache) { "Austria": "[0-9]{4}", "Belgium": "[0-9]{4}", "Brazil": "[0-9]{5}[\\-]?[0-9]{3}", - "Canada": "[A-Za-z][0-9][A-Za-z] [0-9][A-Za-z][0-9]", + "Canada": "^(?!.*[DFIOQU])[A-VXY][0-9][A-Z][ -]?[0-9][A-Z][0-9]$", "Denmark": "[0-9]{3,4}", "Faroe Islands": "[0-9]{3,4}", "Netherlands": "[1-9][0-9]{3}\\s?[a-zA-Z]{2}", From 5206a0e4e9c8b037e9ea3e18277a8b51b799c079 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 20 Mar 2019 14:08:13 +0200 Subject: [PATCH 46/74] Fix SQL Audit Log entity: set correct userId property --- .../org/thingsboard/server/dao/model/sql/AuditLogEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java index 813017ff8e..de0ec87359 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java @@ -140,7 +140,7 @@ public class AuditLogEntity extends BaseSqlEntity implements BaseEntit auditLog.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), toUUID(entityId).toString())); } if (userId != null) { - auditLog.setUserId(new UserId(toUUID(entityId))); + auditLog.setUserId(new UserId(toUUID(userId))); } auditLog.setEntityName(this.entityName); auditLog.setUserName(this.userName); From 0285832bed0c83a89cb34917215dcc38ef63ebf4 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 21 Mar 2019 15:07:14 +0200 Subject: [PATCH 47/74] Fixed CORS mapping --- application/src/main/resources/thingsboard.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 72b8159407..f52bfccabc 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -273,7 +273,7 @@ updates: spring.mvc.cors: mappings: # Intercept path - "/api/auth/**": + "[/api/auth/**]": #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled. allowed-origins: "*" #Comma-separated list of methods to allow. '*' allows all methods. @@ -284,7 +284,7 @@ spring.mvc.cors: max-age: "1800" #Set whether credentials are supported. When not set, credentials are not supported. allow-credentials: "true" - "/api/v1/**": + "[/api/v1/**]": allowed-origins: "*" allowed-methods: "*" allowed-headers: "*" From 1a1e66fa56a7da37da17aa187cacc1f8a1a4c3d3 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 21 Mar 2019 17:38:27 +0200 Subject: [PATCH 48/74] Fixed CORS mapping --- application/src/main/resources/thingsboard.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f52bfccabc..1069f9d6e6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -273,23 +273,17 @@ updates: spring.mvc.cors: mappings: # Intercept path - "[/api/auth/**]": + "[/api/**]": #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled. allowed-origins: "*" #Comma-separated list of methods to allow. '*' allows all methods. - allowed-methods: "POST,GET,OPTIONS" + allowed-methods: "*" #Comma-separated list of headers to allow in a request. '*' allows all headers. allowed-headers: "*" #How long, in seconds, the response from a pre-flight request can be cached by clients. max-age: "1800" #Set whether credentials are supported. When not set, credentials are not supported. allow-credentials: "true" - "[/api/v1/**]": - allowed-origins: "*" - allowed-methods: "*" - allowed-headers: "*" - max-age: "1800" - allow-credentials: "true" # spring serve gzip compressed static resources spring.resources.chain: From 3b52f9817df6f2a2473120a1b294bbc5d1ed12ec Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 25 Mar 2019 18:00:39 +0200 Subject: [PATCH 49/74] MessageId to PacketId refactoring --- .../org/thingsboard/mqtt/MqttChannelHandler.java | 12 ++++++------ .../java/org/thingsboard/mqtt/MqttClientImpl.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java index 1525510814..63e1ac3e63 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java @@ -185,21 +185,21 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler case AT_LEAST_ONCE: invokeHandlersForIncomingPublish(message); - if (message.variableHeader().messageId() != -1) { + if (message.variableHeader().packetId() != -1) { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().messageId()); + MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().packetId()); channel.writeAndFlush(new MqttPubAckMessage(fixedHeader, variableHeader)); } break; case EXACTLY_ONCE: - if (message.variableHeader().messageId() != -1) { + if (message.variableHeader().packetId() != -1) { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().messageId()); + MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().packetId()); MqttMessage pubrecMessage = new MqttMessage(fixedHeader, variableHeader); MqttIncomingQos2Publish incomingQos2Publish = new MqttIncomingQos2Publish(message, pubrecMessage); - this.client.getQos2PendingIncomingPublishes().put(message.variableHeader().messageId(), incomingQos2Publish); + this.client.getQos2PendingIncomingPublishes().put(message.variableHeader().packetId(), incomingQos2Publish); message.payload().retain(); incomingQos2Publish.startPubrecRetransmitTimer(this.client.getEventLoop().next(), this.client::sendAndFlushPacket); @@ -249,7 +249,7 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler MqttIncomingQos2Publish incomingQos2Publish = this.client.getQos2PendingIncomingPublishes().get(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); this.invokeHandlersForIncomingPublish(incomingQos2Publish.getIncomingPublish()); incomingQos2Publish.onPubrelReceived(); - this.client.getQos2PendingIncomingPublishes().remove(incomingQos2Publish.getIncomingPublish().variableHeader().messageId()); + this.client.getQos2PendingIncomingPublishes().remove(incomingQos2Publish.getIncomingPublish().variableHeader().packetId()); } MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 0); MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index da497e2d70..85b3abeeee 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -339,7 +339,7 @@ final class MqttClientImpl implements MqttClient { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retain, 0); MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topic, getNewMessageId().messageId()); MqttPublishMessage message = new MqttPublishMessage(fixedHeader, variableHeader, payload); - MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.messageId(), future, payload.retain(), message, qos); + MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.packetId(), future, payload.retain(), message, qos); ChannelFuture channelFuture = this.sendAndFlushPacket(message); if (channelFuture != null) { From 13e1d60a7cb558040238958e4808a2262e21d644 Mon Sep 17 00:00:00 2001 From: Sergey Tarnavskiy Date: Tue, 26 Mar 2019 12:29:30 +0200 Subject: [PATCH 50/74] added feature for resetting debug-mode in all rule-nodes --- ui/src/app/locale/locale.constant-en_US.json | 3 ++- ui/src/app/locale/locale.constant-ru_RU.json | 3 ++- ui/src/app/rulechain/rulechain.controller.js | 16 ++++++++++++++++ ui/src/app/rulechain/rulechain.tpl.html | 9 +++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index dfef502f09..d1fe2c88c9 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1295,7 +1295,8 @@ "metadata-required": "Metadata entries can't be empty.", "output": "Output", "test": "Test", - "help": "Help" + "help": "Help", + "reset-debug-mode": "Reset debug mode in all nodes" }, "tenant": { "tenant": "Tenant", diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 3ad4371cc1..2d05051de8 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1288,7 +1288,8 @@ "metadata-required": "Метаданные объекта не могут быть пустыми.", "output": "Выход", "test": "Протестировать", - "help": "Помощь" + "help": "Помощь", + "reset-debug-mode": "Сбросить режим отладки во всех правилах" }, "tenant": { "tenant": "Владелец", diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js index 3744b63198..195dcffef5 100644 --- a/ui/src/app/rulechain/rulechain.controller.js +++ b/ui/src/app/rulechain/rulechain.controller.js @@ -108,6 +108,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.objectsSelected = objectsSelected; vm.deleteSelected = deleteSelected; + vm.isDebugModeEnabled = isDebugModeEnabled; + vm.resetDebugModeInAllNodes = resetDebugModeInAllNodes; + vm.triggerResize = triggerResize; vm.openRuleChainContextMenu = openRuleChainContextMenu; @@ -1342,6 +1345,19 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.modelservice.deleteSelected(); } + function isDebugModeEnabled() { + var res = $filter('filter')(vm.ruleChainModel.nodes, {debugMode: true}); + return (res && res.length); + } + + function resetDebugModeInAllNodes() { + vm.ruleChainModel.nodes.forEach((node) => { + if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) { + node.debugMode = false; + } + }); + } + function triggerResize() { var w = angular.element($window); w.triggerHandler('resize'); diff --git a/ui/src/app/rulechain/rulechain.tpl.html b/ui/src/app/rulechain/rulechain.tpl.html index 80425e59c8..4a20947497 100644 --- a/ui/src/app/rulechain/rulechain.tpl.html +++ b/ui/src/app/rulechain/rulechain.tpl.html @@ -223,6 +223,15 @@ + + + {{ 'rulenode.reset-debug-mode' | translate }} + + + Date: Wed, 27 Mar 2019 13:38:06 +0200 Subject: [PATCH 51/74] Improve zookeeper client reconnect logic. UI: Flot widget - Fixed incorrect individual tooltip content. --- .../cluster/discovery/ZkDiscoveryService.java | 37 +++++++++++++++---- pom.xml | 4 +- ui/src/app/widget/lib/flot-widget.js | 10 +++-- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java index 5d1262f233..e44c28dc39 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java @@ -52,6 +52,8 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.List; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED; @@ -96,11 +98,13 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @Lazy private ClusterRoutingService routingService; + private ExecutorService reconnectExecutorService; + private CuratorFramework client; private PathChildrenCache cache; private String nodePath; - private volatile boolean stopped = false; + private volatile boolean stopped = true; @PostConstruct public void init() { @@ -110,9 +114,15 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi Assert.notNull(zkConnectionTimeout, MiscUtils.missingProperty("zk.connection_timeout_ms")); Assert.notNull(zkSessionTimeout, MiscUtils.missingProperty("zk.session_timeout_ms")); + reconnectExecutorService = Executors.newSingleThreadExecutor(); + log.info("Initializing discovery service using ZK connect string: {}", zkUrl); zkNodesDir = zkDir + "/nodes"; + initZkClient(); + } + + private void initZkClient() { try { client = CuratorFrameworkFactory.newClient(zkUrl, zkSessionTimeout, zkConnectionTimeout, new RetryForever(zkRetryInterval)); client.start(); @@ -120,6 +130,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi cache = new PathChildrenCache(client, zkNodesDir, true); cache.getListenable().addListener(this); cache.start(); + stopped = false; + log.info("ZK client connected"); } catch (Exception e) { log.error("Failed to connect to ZK: {}", e.getMessage(), e); CloseableUtils.closeQuietly(cache); @@ -128,12 +140,20 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi } } - @PreDestroy - public void destroy() { + private void destroyZkClient() { stopped = true; - unpublishCurrentServer(); + try { + unpublishCurrentServer(); + } catch (Exception e) {} CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); + log.info("ZK client disconnected"); + } + + @PreDestroy + public void destroy() { + destroyZkClient(); + reconnectExecutorService.shutdownNow(); log.info("Stopped discovery service"); } @@ -180,20 +200,21 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi return (client, newState) -> { log.info("[{}:{}] ZK state changed: {}", self.getHost(), self.getPort(), newState); if (newState == ConnectionState.LOST) { - reconnect(); + reconnectExecutorService.submit(this::reconnect); } }; } - private boolean reconnectInProgress = false; + private volatile boolean reconnectInProgress = false; private synchronized void reconnect() { if (!reconnectInProgress) { reconnectInProgress = true; try { - client.blockUntilConnected(); + destroyZkClient(); + initZkClient(); publishCurrentServer(); - } catch (InterruptedException e) { + } catch (Exception e) { log.error("Failed to reconnect to ZK: {}", e.getMessage(), e); } finally { reconnectInProgress = false; diff --git a/pom.xml b/pom.xml index b0bf2a51c3..59cc98aef0 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.7 + 2.9.8 2.2.6 2.11 2.4.2 @@ -59,7 +59,7 @@ 1.7 2.0 1.4.3 - 4.0.1 + 4.2.0 3.6.1 1.16.1 1.16.18 diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js index 8c9aca62c6..512f25b4ce 100644 --- a/ui/src/app/widget/lib/flot-widget.js +++ b/ui/src/app/widget/lib/flot-widget.js @@ -137,9 +137,11 @@ export default class TbFlot { }); content += dateDiv.prop('outerHTML'); if (tbFlot.ctx.tooltipIndividual) { - var seriesHoverInfo = hoverInfo.seriesHover[seriesIndex]; - if (seriesHoverInfo) { - content += seriesInfoDivFromInfo(seriesHoverInfo, seriesIndex); + var found = hoverInfo.seriesHover.filter((seriesHover) => { + return seriesHover.index === seriesIndex; + }); + if (found && found.length) { + content += seriesInfoDivFromInfo(found[0], seriesIndex); } } else { var seriesDiv = $('
    '); @@ -161,7 +163,7 @@ export default class TbFlot { if (i == hoverInfo.seriesHover.length) { break; } - seriesHoverInfo = hoverInfo.seriesHover[i]; + var seriesHoverInfo = hoverInfo.seriesHover[i]; columnContent += seriesInfoDivFromInfo(seriesHoverInfo, seriesIndex); } columnDiv.html(columnContent); From 080b0663ca377253b1a78af988c3e1bf2c371ee5 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 27 Mar 2019 14:50:23 +0200 Subject: [PATCH 52/74] UI: Add searchbox support for all ace editors. --- ui/src/app/alarm/alarm-details-dialog.controller.js | 1 + ui/src/app/audit/audit-log-details-dialog.controller.js | 1 + ui/src/app/components/js-func.directive.js | 1 + ui/src/app/components/json-content.directive.js | 1 + ui/src/app/components/json-object-edit.directive.js | 1 + ui/src/app/components/react/json-form-ace-editor.jsx | 1 + ui/src/app/entity/relation/relation-dialog.controller.js | 1 + ui/src/app/event/event-content-dialog.controller.js | 1 + .../extension/extensions-forms/extension-form-http.directive.js | 1 + .../extensions-forms/extension-form-modbus.directive.js | 1 + .../extension/extensions-forms/extension-form-opc.directive.js | 1 + ui/src/app/widget/widget-editor.controller.js | 1 + 12 files changed, 12 insertions(+) diff --git a/ui/src/app/alarm/alarm-details-dialog.controller.js b/ui/src/app/alarm/alarm-details-dialog.controller.js index f5e6c92e1c..3da3fa616e 100644 --- a/ui/src/app/alarm/alarm-details-dialog.controller.js +++ b/ui/src/app/alarm/alarm-details-dialog.controller.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; import beautify from 'js-beautify'; diff --git a/ui/src/app/audit/audit-log-details-dialog.controller.js b/ui/src/app/audit/audit-log-details-dialog.controller.js index ec839d5d3d..1093abc05a 100644 --- a/ui/src/app/audit/audit-log-details-dialog.controller.js +++ b/ui/src/app/audit/audit-log-details-dialog.controller.js @@ -15,6 +15,7 @@ */ import $ from 'jquery'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/java'; import 'brace/theme/github'; diff --git a/ui/src/app/components/js-func.directive.js b/ui/src/app/components/js-func.directive.js index cb3d58788f..bb600b789e 100644 --- a/ui/src/app/components/js-func.directive.js +++ b/ui/src/app/components/js-func.directive.js @@ -17,6 +17,7 @@ import './js-func.scss'; import ace from 'brace'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import $ from 'jquery'; import thingsboardToast from '../services/toast'; import thingsboardUtils from '../common/utils.service'; diff --git a/ui/src/app/components/json-content.directive.js b/ui/src/app/components/json-content.directive.js index 6686059a95..668d163a6d 100644 --- a/ui/src/app/components/json-content.directive.js +++ b/ui/src/app/components/json-content.directive.js @@ -16,6 +16,7 @@ import './json-content.scss'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/mode/text'; import 'brace/snippets/json'; diff --git a/ui/src/app/components/json-object-edit.directive.js b/ui/src/app/components/json-object-edit.directive.js index cc4ada19b6..a79eecb64e 100644 --- a/ui/src/app/components/json-object-edit.directive.js +++ b/ui/src/app/components/json-object-edit.directive.js @@ -16,6 +16,7 @@ import './json-object-edit.scss'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/snippets/json'; diff --git a/ui/src/app/components/react/json-form-ace-editor.jsx b/ui/src/app/components/react/json-form-ace-editor.jsx index 44df6cd38d..0720b86a41 100644 --- a/ui/src/app/components/react/json-form-ace-editor.jsx +++ b/ui/src/app/components/react/json-form-ace-editor.jsx @@ -21,6 +21,7 @@ import reactCSS from 'reactcss'; import AceEditor from 'react-ace'; import FlatButton from 'material-ui/FlatButton'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/theme/github'; import fixAceEditor from './../ace-editor-fix'; diff --git a/ui/src/app/entity/relation/relation-dialog.controller.js b/ui/src/app/entity/relation/relation-dialog.controller.js index 7ce2d4bd51..cd4bcebcfb 100644 --- a/ui/src/app/entity/relation/relation-dialog.controller.js +++ b/ui/src/app/entity/relation/relation-dialog.controller.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; import beautify from 'js-beautify'; diff --git a/ui/src/app/event/event-content-dialog.controller.js b/ui/src/app/event/event-content-dialog.controller.js index 80fc739056..9fb8a5a4ec 100644 --- a/ui/src/app/event/event-content-dialog.controller.js +++ b/ui/src/app/event/event-content-dialog.controller.js @@ -15,6 +15,7 @@ */ import $ from 'jquery'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/java'; import 'brace/theme/github'; import beautify from 'js-beautify'; diff --git a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js index 47a2b860c5..616d70d0e4 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; diff --git a/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js b/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js index 36686be529..52b2e16a1d 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-modbus.directive.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; diff --git a/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js b/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js index a9b1d73519..1598fd04ef 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js +++ b/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js @@ -14,6 +14,7 @@ * limitations under the License. */ import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/json'; import 'brace/theme/github'; diff --git a/ui/src/app/widget/widget-editor.controller.js b/ui/src/app/widget/widget-editor.controller.js index b9d861a647..6beb0a6213 100644 --- a/ui/src/app/widget/widget-editor.controller.js +++ b/ui/src/app/widget/widget-editor.controller.js @@ -16,6 +16,7 @@ import $ from 'jquery'; import ace from 'brace'; import 'brace/ext/language_tools'; +import 'brace/ext/searchbox'; import 'brace/mode/javascript'; import 'brace/mode/html'; import 'brace/mode/css'; From 0d29b130ba766c8e0d85ad68b053c6257d29acea Mon Sep 17 00:00:00 2001 From: Bartosz Ptaszynski Date: Wed, 27 Mar 2019 13:22:19 +0000 Subject: [PATCH 53/74] Fix deployment on GKE When creating postgres deployment on GKE Postgres complains about data folder being mounted directly on root of the PV claim. ``` The files belonging to this database system will be owned by user "postgres". This user must also own the server process. The database cluster will be initialized with locale "en_US.utf8". The default database encoding has accordingly been set to "UTF8". The default text search configuration will be set to "english". Data page checksums are disabled. initdb: directory "/var/lib/postgresql/data" exists but is not empty It contains a lost+found directory, perhaps due to it being a mount point. Using a mount point directly as the data directory is not recommended. Create a subdirectory under the mount point. ``` A simple solution is to force Postgres to use a sub folder using PGDATA env variable. --- k8s/postgres.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/k8s/postgres.yml b/k8s/postgres.yml index 2e7f2cc815..404b564933 100644 --- a/k8s/postgres.yml +++ b/k8s/postgres.yml @@ -55,6 +55,8 @@ spec: env: - name: POSTGRES_DB value: "thingsboard" + - name: PGDATA + value: /var/lib/postgresql/data/pgdata volumeMounts: - mountPath: /var/lib/postgresql/data name: postgres-data From bb68e575e7e0ba95dccbfea3562fcd2d9c4481c3 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 09:51:43 +0200 Subject: [PATCH 54/74] UI: Settings cards layout improvement. --- ui/src/app/admin/admin.controller.js | 3 +++ ui/src/app/admin/general-settings.tpl.html | 4 ++-- .../app/admin/outgoing-mail-settings.tpl.html | 4 ++-- ui/src/app/admin/settings-card.scss | 23 +++++++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 ui/src/app/admin/settings-card.scss diff --git a/ui/src/app/admin/admin.controller.js b/ui/src/app/admin/admin.controller.js index c697726137..1fd0b22eeb 100644 --- a/ui/src/app/admin/admin.controller.js +++ b/ui/src/app/admin/admin.controller.js @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import './settings-card.scss'; + /*@ngInject*/ export default function AdminController(adminService, toast, $scope, $rootScope, $state, $translate) { diff --git a/ui/src/app/admin/general-settings.tpl.html b/ui/src/app/admin/general-settings.tpl.html index 7023e133dd..0e651f8233 100644 --- a/ui/src/app/admin/general-settings.tpl.html +++ b/ui/src/app/admin/general-settings.tpl.html @@ -15,8 +15,8 @@ limitations under the License. --> -
    - +
    + admin.general-settings diff --git a/ui/src/app/admin/outgoing-mail-settings.tpl.html b/ui/src/app/admin/outgoing-mail-settings.tpl.html index 14049defd3..855da256c6 100644 --- a/ui/src/app/admin/outgoing-mail-settings.tpl.html +++ b/ui/src/app/admin/outgoing-mail-settings.tpl.html @@ -15,8 +15,8 @@ limitations under the License. --> -
    - +
    + admin.outgoing-mail-settings diff --git a/ui/src/app/admin/settings-card.scss b/ui/src/app/admin/settings-card.scss new file mode 100644 index 0000000000..9cbf5266b0 --- /dev/null +++ b/ui/src/app/admin/settings-card.scss @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../scss/constants"; + +md-card.settings-card { + @media (min-width: $layout-breakpoint-sm) { + width: 60%; + } +} From 2f06be1d0557a583617dc86177287ccd7487dd5c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 11:27:01 +0200 Subject: [PATCH 55/74] UI: Widgets - add entityDescription variable --- ui/src/app/common/utils.service.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index 0b8d23a726..f140893d2b 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -499,6 +499,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t label = label.split(variable).join(datasource.entityName); } else if (variableName === 'aliasName') { label = label.split(variable).join(datasource.aliasName); + } else if (variableName === 'entityDescription') { + label = label.split(variable).join(datasource.entityDescription); } match = varsRegex.exec(pattern); } From 2bc13b68122912fad60d78744cb110c1c2e9b713 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 18:36:43 +0200 Subject: [PATCH 56/74] UI: Trip Animation Widget Improvements. --- .../data/json/system/widget_bundles/maps.json | 2 +- .../tripAnimation/trip-animation-widget.js | 151 +++++++++++----- .../tripAnimation/trip-animation-widget.scss | 162 +++++++++++------- .../trip-animation-widget.tpl.html | 59 ++++--- 4 files changed, 238 insertions(+), 136 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index 1f4807c7d4..66c87e1536 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -130,7 +130,7 @@ "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n", "settingsSchema": "{\r\n \"schema\": {\r\n \"title\": \"Openstreet Map Configuration\",\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"mapProvider\": {\r\n \"title\": \"Map provider\",\r\n \"type\": \"string\",\r\n \"default\": \"OpenStreetMap.Mapnik\"\r\n },\r\n \"defaultZoomLevel\": {\r\n\t\t\t\t\t\"title\": \"Default map zoom level (1 - 20)\",\r\n\t\t\t\t\t\"type\": \"number\"\r\n\t\t\t\t},\r\n\t\t\t\"fitMapBounds\": {\r\n\t\t\t\t\"title\": \"Fit map bounds to cover all markers\",\r\n\t\t\t\t\"type\": \"boolean\",\r\n\t\t\t\t\"default\": true\r\n\t\t\t},\r\n \"latKeyName\": {\r\n \"title\": \"Latitude key name\",\r\n \"type\": \"string\",\r\n \"default\": \"latitude\"\r\n },\r\n \"lngKeyName\": {\r\n \"title\": \"Longitude key name\",\r\n \"type\": \"string\",\r\n \"default\": \"longitude\"\r\n },\r\n \"showLabel\": {\r\n \"title\": \"Show label\",\r\n \"type\": \"boolean\",\r\n \"default\": true\r\n },\r\n \"label\": {\r\n \"title\": \"Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )\",\r\n \"type\": \"string\",\r\n \"default\": \"${entityName}\"\r\n },\r\n \"useLabelFunction\": {\r\n \"title\": \"Use label function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"labelFunction\": {\r\n \"title\": \"Label function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"showTooltip\": {\r\n \"title\": \"Show tooltip\",\r\n \"type\": \"boolean\",\r\n \"default\": true\r\n },\r\n \"tooltipColor\": {\r\n \"title\": \"Tooltip background color\",\r\n \"type\": \"string\",\r\n \"default\": \"#fff\"\r\n },\r\n \"tooltipFontColor\": {\r\n \"title\": \"Tooltip font color\",\r\n \"type\": \"string\",\r\n \"default\": \"#000\"\r\n },\r\n \"tooltipOpacity\": {\r\n \"title\": \"Tooltip opacity (0-1)\",\r\n \"type\": \"number\",\r\n \"default\": 1 \r\n },\r\n \"tooltipPattern\": {\r\n \"title\": \"Tooltip (for ex. 'Text ${keyName} units.' or Link text')\",\r\n \"type\": \"string\",\r\n \"default\": \"${entityName}

    Latitude: ${latitude:7}
    Longitude: ${longitude:7}
    End Time: ${maxTime}
    Start Time: ${minTime}\"\r\n },\r\n \"useTooltipFunction\": {\r\n \"title\": \"Use tooltip function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"tooltipFunction\": {\r\n \"title\": \"Tooltip function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"color\": {\r\n \"title\": \"Stroke color\",\r\n \"type\": \"string\"\r\n },\r\n \"strokeWeight\": {\r\n \"title\": \"Stroke weight\",\r\n \"type\": \"number\",\r\n \"default\": 2\r\n },\r\n \"strokeOpacity\": {\r\n \"title\": \"Stroke opacity\",\r\n \"type\": \"number\",\r\n \"default\": 1\r\n },\r\n \"useColorFunction\": {\r\n \"title\": \"Use stroke color function\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"colorFunction\": {\r\n \"title\": \"Stroke color function: f(data, dsData, dsIndex)\",\r\n \"type\": \"string\"\r\n },\r\n \"showPoints\": {\r\n \"title\": \"Show points\",\r\n \"type\": \"boolean\",\r\n \"default\": false\r\n },\r\n \"pointColor\": {\r\n \"title\": \"Point color\",\r\n \"type\": \"string\"\r\n },\r\n \"pointSize\": {\r\n \"title\": \"Point size (px)\",\r\n \"type\": \"number\",\r\n \"default\": 10\r\n },\r\n \"defaultMarkerColor\": {\r\n \"title\": \"color for default marker\",\r\n \"type\": \"string\"\r\n },\r\n \"markerImage\": {\r\n \"title\": \"Custom marker image\",\r\n \"type\": \"string\"\r\n },\r\n \"markerImageSize\": {\r\n \"title\": \"Custom marker image size (px)\",\r\n \"type\": \"number\",\r\n \"default\": 34\r\n },\r\n \"rotationAngle\": {\r\n \"title\": \"Set additional rotation angle for marker (deg)\",\r\n \"type\": \"number\",\r\n \"default\": 180\r\n },\r\n \"useMarkerImageFunction\":{\r\n \"title\":\"Use marker image function\",\r\n \"type\":\"boolean\",\r\n \"default\":false\r\n },\r\n \"markerImageFunction\":{\r\n \"title\":\"Marker image function: f(data, images, dsData, dsIndex)\",\r\n \"type\":\"string\"\r\n },\r\n \"markerImages\":{\r\n \"title\":\"Marker images\",\r\n \"type\":\"array\",\r\n \"items\":{\r\n \"title\":\"Marker image\",\r\n \"type\":\"string\"\r\n }\r\n }\r\n },\r\n \"required\": []\r\n },\r\n \"form\": [{\r\n \"key\": \"mapProvider\",\r\n \"type\": \"rc-select\",\r\n \"multiple\": false,\r\n \"items\": [{\r\n \"value\": \"OpenStreetMap.Mapnik\",\r\n \"label\": \"OpenStreetMap.Mapnik (Default)\"\r\n }, {\r\n \"value\": \"OpenStreetMap.BlackAndWhite\",\r\n \"label\": \"OpenStreetMap.BlackAndWhite\"\r\n }, {\r\n \"value\": \"OpenStreetMap.HOT\",\r\n \"label\": \"OpenStreetMap.HOT\"\r\n }, {\r\n \"value\": \"Esri.WorldStreetMap\",\r\n \"label\": \"Esri.WorldStreetMap\"\r\n }, {\r\n \"value\": \"Esri.WorldTopoMap\",\r\n \"label\": \"Esri.WorldTopoMap\"\r\n }, {\r\n \"value\": \"CartoDB.Positron\",\r\n \"label\": \"CartoDB.Positron\"\r\n }, {\r\n \"value\": \"CartoDB.DarkMatter\",\r\n \"label\": \"CartoDB.DarkMatter\"\r\n }]\r\n },\"defaultZoomLevel\", \"fitMapBounds\", \"latKeyName\", \"lngKeyName\", \"showLabel\", \"label\", \"useLabelFunction\", {\r\n \"key\": \"labelFunction\",\r\n \"type\": \"javascript\"\r\n }, \"showTooltip\", {\r\n \"key\": \"tooltipColor\",\r\n \"type\": \"color\"\r\n }, {\r\n \"key\": \"tooltipFontColor\",\r\n \"type\": \"color\"\r\n },\"tooltipOpacity\", {\r\n \"key\": \"tooltipPattern\",\r\n \"type\": \"textarea\"\r\n }, \"useTooltipFunction\", {\r\n \"key\": \"tooltipFunction\",\r\n \"type\": \"javascript\"\r\n }, {\r\n \"key\": \"color\",\r\n \"type\": \"color\"\r\n }, \"useColorFunction\", {\r\n \"key\": \"colorFunction\",\r\n \"type\": \"javascript\"\r\n }, \"strokeWeight\", \"strokeOpacity\", \"showPoints\",{\r\n \"key\": \"pointColor\",\r\n \"type\": \"color\"\r\n }, \"pointSize\", {\r\n \"key\": \"defaultMarkerColor\",\r\n \"type\": \"color\"\r\n }, {\r\n \"key\": \"markerImage\",\r\n \"type\": \"image\"\r\n }, \"markerImageSize\", \"rotationAngle\",\"useMarkerImageFunction\",\r\n {\r\n \"key\":\"markerImageFunction\",\r\n \"type\":\"javascript\"\r\n }, {\r\n \"key\":\"markerImages\",\r\n \"items\":[\r\n {\r\n \"key\":\"markerImages[]\",\r\n \"type\":\"image\"\r\n }\r\n ]\r\n }]\r\n}", "dataKeySettingsSchema": "{}", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}

    Latitude: ${latitude:7}
    Longitude: ${longitude:7}
    End Time: ${maxTime}
    Start Time: ${minTime}\",\"strokeWeight\":3,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}\\n
    \\nTime: ${formattedTs}\\nLatitude: ${latitude:7}\\nLongitude: ${longitude:7}\",\"strokeWeight\":3,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" } } ] diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js index 311efebf24..e7dcad6299 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.js @@ -17,10 +17,8 @@ import './trip-animation-widget.scss'; import template from "./trip-animation-widget.tpl.html"; import TbOpenStreetMap from '../openstreet-map'; import L from 'leaflet'; -//import tinycolor from 'tinycolor2'; import tinycolor from "tinycolor2"; import {fillPatternWithActions, isNumber, padValue, processPattern} from "../widget-utils"; -//import {fillPatternWithActions, isNumber, padValue, processPattern, fillPattern} from "../widget-utils"; (function () { // save these original methods before they are overwritten @@ -121,20 +119,17 @@ function tripAnimationWidget() { } /*@ngInject*/ -function tripAnimationController($document, $scope, $http, $timeout, $filter) { +function tripAnimationController($document, $scope, $http, $timeout, $filter, $sce) { let vm = this; - //const varsRegex = /\$\{([^\}]*)\}/g; - //let icon; vm.initBounds = true; vm.markers = []; vm.index = 0; vm.dsIndex = 0; - vm.isPlaying = false; vm.minTime = 0; vm.maxTime = 0; - vm.isPLaying = false; + vm.isPlaying = false; vm.trackingLine = { "type": "FeatureCollection", features: [] @@ -163,7 +158,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { function initializeCallbacks() { vm.self.onDataUpdated = function () { - createUpdatePath(); + createUpdatePath(true); }; vm.self.onResize = function () { @@ -192,26 +187,54 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } vm.playMove = function (play) { - if (play && vm.isPLaying) return; - if (play || vm.isPLaying) vm.isPLaying = true; - if (vm.isPLaying) { - if (vm.index + 1 > vm.maxTime) return; - vm.index++; - vm.trips.forEach(function (trip) { - moveMarker(trip); - }); + if (play && vm.isPlaying) return; + if (play || vm.isPlaying) vm.isPlaying = true; + if (vm.isPlaying) { + moveInc(1); vm.timeout = $timeout(function () { vm.playMove(); }, 1000 / vm.speed) } }; + vm.moveNext = function () { + vm.stopPlay(); + moveInc(1); + } + + vm.movePrev = function () { + vm.stopPlay(); + moveInc(-1); + } + + vm.moveStart = function () { + vm.stopPlay(); + moveToIndex(vm.minTime); + } + + vm.moveEnd = function () { + vm.stopPlay(); + moveToIndex(vm.maxTime); + } vm.stopPlay = function () { - vm.isPLaying = false; - $timeout.cancel(vm.timeout); + if (vm.isPlaying) { + vm.isPlaying = false; + $timeout.cancel(vm.timeout); + } }; + function moveInc(inc) { + let newIndex = vm.index + inc; + moveToIndex(newIndex); + } + + function moveToIndex(newIndex) { + if (newIndex > vm.maxTime || newIndex < vm.minTime) return; + vm.index = newIndex; + recalculateTrips(); + } + function recalculateTrips() { vm.trips.forEach(function (value) { moveMarker(value); @@ -233,7 +256,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { vm.utils = vm.self.ctx.$scope.$injector.get('utils'); vm.showTimestamp = vm.settings.showTimestamp !== false; - vm.ctx.$element = angular.element("#heat-map", vm.ctx.$container); + vm.ctx.$element = angular.element("#trip-animation-map", vm.ctx.$container); vm.defaultZoomLevel = 2; if (vm.ctx.settings.defaultZoomLevel) { if (vm.ctx.settings.defaultZoomLevel > 0 && vm.ctx.settings.defaultZoomLevel < 21) { @@ -257,6 +280,8 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { let staticSettings = {}; vm.staticSettings = staticSettings; //Calculate General Settings + staticSettings.buttonColor = tinycolor(vm.widgetConfig.color).setAlpha(0.54).toRgbString(); + staticSettings.disabledButtonColor = tinycolor(vm.widgetConfig.color).setAlpha(0.3).toRgbString(); staticSettings.mapProvider = vm.ctx.settings.mapProvider || "OpenStreetMap.Mapnik"; staticSettings.latKeyName = vm.ctx.settings.latKeyName || "latitude"; staticSettings.lngKeyName = vm.ctx.settings.lngKeyName || "longitude"; @@ -268,10 +293,14 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { staticSettings.useLabelFunction = vm.ctx.settings.useLabelFunction || false; staticSettings.showLabel = vm.ctx.settings.showLabel || false; staticSettings.useTooltipFunction = vm.ctx.settings.useTooltipFunction || false; - staticSettings.tooltipPattern = vm.ctx.settings.tooltipPattern || "${entityName}

    Latitude: ${latitude:7}
    Longitude: ${longitude:7}
    Start Time: ${maxTime}
    End Time: ${minTime}"; - staticSettings.tooltipOpacity = vm.ctx.settings.tooltipOpacity || 1; - staticSettings.tooltipColor = vm.ctx.settings.tooltipColor ? tinycolor(vm.ctx.settings.tooltipColor).toHexString() : "#ffffff"; - staticSettings.tooltipFontColor = vm.ctx.settings.tooltipFontColor ? tinycolor(vm.ctx.settings.tooltipFontColor).toHexString() : "#000000"; + staticSettings.tooltipPattern = vm.ctx.settings.tooltipPattern || "${entityName}\n" + + "
    \n" + + "Time: ${formattedTs}\n" + + "Latitude: ${latitude:7}\n" + + "Longitude: ${longitude:7}"; + staticSettings.tooltipOpacity = angular.isNumber(vm.ctx.settings.tooltipOpacity) ? vm.ctx.settings.tooltipOpacity : 1; + staticSettings.tooltipColor = vm.ctx.settings.tooltipColor ? tinycolor(vm.ctx.settings.tooltipColor).toRgbString() : "#ffffff"; + staticSettings.tooltipFontColor = vm.ctx.settings.tooltipFontColor ? tinycolor(vm.ctx.settings.tooltipFontColor).toRgbString() : "#000000"; staticSettings.pathColor = vm.ctx.settings.color ? tinycolor(vm.ctx.settings.color).toHexString() : "#ff6300"; staticSettings.pathWeight = vm.ctx.settings.strokeWeight || 1; staticSettings.pathOpacity = vm.ctx.settings.strokeOpacity || 1; @@ -351,16 +380,23 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } } - function configureTripSettings(trip) { + function configureTripSettings(trip, index, apply) { trip.settings = {}; trip.settings.color = calculateColor(trip); trip.settings.strokeWeight = vm.staticSettings.pathWeight; trip.settings.strokeOpacity = vm.staticSettings.pathOpacity; trip.settings.pointColor = vm.staticSettings.pointColor; trip.settings.pointSize = vm.staticSettings.pointSize; - trip.settings.labelText = calculateLabel(trip); - trip.settings.tooltipText = calculateTooltip(trip); trip.settings.icon = calculateIcon(trip); + if (apply) { + $timeout(() => { + trip.settings.labelText = calculateLabel(trip); + trip.settings.tooltipText = $sce.trustAsHtml(calculateTooltip(trip)); + },0,true); + } else { + trip.settings.labelText = calculateLabel(trip); + trip.settings.tooltipText = $sce.trustAsHtml(calculateTooltip(trip)); + } } function calculateLabel(trip) { @@ -464,7 +500,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { return icon; } - function createUpdatePath() { + function createUpdatePath(apply) { if (vm.trips && vm.map) { vm.trips.forEach(function (trip) { if (trip.marker) { @@ -486,7 +522,7 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } let normalizedTimeRange = createNormalizedTime(vm.data, 1000); createNormalizedTrips(normalizedTimeRange, vm.datasources); - createTripsOnMap(); + createTripsOnMap(apply); if (vm.initBounds && !vm.initTrips) { vm.trips.forEach(function (trip) { vm.map.extendBounds(vm.map.bounds, trip.polyline); @@ -501,7 +537,6 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { vm.map.fitBounds(vm.map.bounds); } - } function fillPattern(pattern, replaceInfo, currentNormalizedValue) { @@ -549,12 +584,20 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } }); for (let i = min_time; i < max_time; i += step) { - normalizedArray.push({ts: i}) + normalizedArray.push({ts: i, formattedTs: $filter('date')(i, 'medium')}); + } - if (normalizedArray[normalizedArray.length - 1] && normalizedArray[normalizedArray.length - 1].ts !== max_time) normalizedArray.push({ts: max_time}); + if (normalizedArray[normalizedArray.length - 1] && normalizedArray[normalizedArray.length - 1].ts !== max_time) { + normalizedArray.push({ts: max_time, formattedTs: $filter('date')(max_time, 'medium')}); + } } vm.maxTime = normalizedArray.length - 1; - vm.minTime = 0; + vm.minTime = vm.maxTime > 1 ? 1 : 0; + if (vm.index < vm.minTime) { + vm.index = vm.minTime; + } else if (vm.index > vm.maxTime) { + vm.index = vm.maxTime; + } return normalizedArray; } @@ -611,27 +654,43 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } el.latLngs.push(data.latLng); }); - addAngleForTip(el); + addAngleForTrip(el); }) } - function addAngleForTip(trip) { + function addAngleForTrip(trip) { if (trip.timeRange && trip.timeRange.length > 0) { trip.timeRange.forEach(function (point, index) { let nextPoint, prevPoint; nextPoint = index === (trip.timeRange.length - 1) ? trip.timeRange[index] : trip.timeRange[index + 1]; prevPoint = index === 0 ? trip.timeRange[0] : trip.timeRange[index - 1]; - point.h = findAngle(prevPoint[vm.staticSettings.latKeyName], prevPoint[vm.staticSettings.lngKeyName], nextPoint[vm.staticSettings.latKeyName], nextPoint[vm.staticSettings.lngKeyName]); - point.h += vm.staticSettings.rotationAngle; + let nextLatLng = { + lat: nextPoint[vm.staticSettings.latKeyName], + lng: nextPoint[vm.staticSettings.lngKeyName] + }; + let prevLatLng = { + lat: prevPoint[vm.staticSettings.latKeyName], + lng: prevPoint[vm.staticSettings.lngKeyName] + }; + if (nextLatLng.lat === prevLatLng.lat && nextLatLng.lng === prevLatLng.lng) { + if (angular.isNumber(prevPoint.h)) { + point.h = prevPoint.h; + } else { + point.h = vm.staticSettings.rotationAngle; + } + } else { + point.h = findAngle(prevLatLng.lat, prevLatLng.lng, nextLatLng.lat, nextLatLng.lng); + point.h += vm.staticSettings.rotationAngle; + } }); } } - function createTripsOnMap() { + function createTripsOnMap(apply) { if (vm.trips.length > 0) { vm.trips.forEach(function (trip) { if (trip.timeRange.length > 0 && trip.latLngs.every(el => angular.isDefined(el))) { - configureTripSettings(trip, vm.index); + configureTripSettings(trip, vm.index, apply); if (vm.staticSettings.showPoints) { trip.points = []; trip.latLngs.forEach(function (latLng) { @@ -648,14 +707,14 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { } if (trip.timeRange && trip.timeRange.length && angular.isUndefined(trip.marker)) { - trip.marker = L.marker(trip.timeRange[vm.index].latLng).addTo(vm.map.map); + trip.marker = L.marker(trip.timeRange[vm.index].latLng); trip.marker.setZIndexOffset(1000); trip.marker.setIcon(vm.staticSettings.icon); trip.marker.setRotationOrigin('center center'); - // trip.marker.addTo(vm.map.map); - trip.marker.on('click', function () { - showHideTooltip(trip); - }); + trip.marker.on('click', function () { + showHideTooltip(trip); + }); + trip.marker.addTo(vm.map.map); moveMarker(trip); } } @@ -675,10 +734,10 @@ function tripAnimationController($document, $scope, $http, $timeout, $filter) { trip.marker.setZIndexOffset(1000); trip.marker.setIcon(vm.staticSettings.icon); trip.marker.setRotationOrigin('center center'); + trip.marker.on('click', function () { + showHideTooltip(trip); + }); trip.marker.addTo(vm.map.map); - trip.marker.on('click', function () { - showHideTooltip(trip); - }); trip.marker.update(); } diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss index 1b6ae956c3..b4616214fc 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.scss @@ -14,93 +14,123 @@ * limitations under the License. */ -.heat-map-widget { +.trip-animation-widget { + position: relative; width: 100%; height: 100%; font-size: 16px; line-height: 24px; -} - -.heat-map-info-panel { - position: absolute; - top: 0; - right: 0; - z-index: 2; - background-color: rgba(0, 0, 0, .3); - border-bottom-left-radius: 5px; - .md-button { - min-width: auto; + .trip-animation-label-container { + height: 24px; } -} -.heat-map-tooltip { - position: absolute; - top: 47px; - right: 0; - z-index: 2; - padding: 10px; - background-color: #fff; - border-top-left-radius: 10px; - border-bottom-left-radius: 10px; - transition: .3s ease-in-out; - - &-hidden { - transform: translateX(100%); - } -} + .trip-animation-container { + position: relative; + z-index: 1; + flex: 1; + width: 100%; -.heat-map-title { - padding: 10px; -} + #trip-animation-map { + z-index: 1; + width: 100%; + height: 100%; -.heat-map-control-panel { - position: absolute; - bottom: 0; - z-index: 2; - box-sizing: border-box; - width: 100%; - padding-right: 70px; - padding-left: 20px; - background: rgba(0, 0, 0, .3); + .pointsLayerMarkerIcon { + border-radius: 50%; + } + } - md-slider-container { - button { - max-width: none; + .trip-animation-info-panel { + position: absolute; + top: 0; + right: 0; + z-index: 2; + pointer-events: none; - ng-md-icon { - width: 36px; - height: 36px; + .md-button { + top: 0; + left: 0; + width: 32px; + min-width: 32px; + height: 32px; + min-height: 32px; + padding: 0 0 2px; + margin: 2px; + line-height: 24px; - svg { - width: inherit; - height: inherit; + ng-md-icon { + width: 24px; + height: 24px; + + svg { + width: inherit; + height: inherit; + } } } } - .panel-timer { - max-width: none; - font-size: 20px; - font-weight: 600; + .trip-animation-tooltip { + position: absolute; + top: 38px; + right: 0; + z-index: 2; + padding: 10px; + background-color: #fff; + transition: .3s ease-in-out; + + &-hidden { + transform: translateX(110%); + } } } -} -.heat-map-container { - position: relative; - z-index: 1; - flex: 1; - width: 100%; -} + .trip-animation-control-panel { + position: relative; + box-sizing: border-box; + width: 100%; + padding-left: 10px; -#heat-map { - z-index: 1; - width: 100%; - height: 100%; + md-slider-container { + md-slider { + min-width: 80px; + } + + button.md-button.md-icon-button { + width: 44px; + min-width: 44px; + height: 44px; + min-height: 44px; + margin: 0; + line-height: 28px; - .pointsLayerMarkerIcon { - border-radius: 50%; + md-icon { + width: 28px; + height: 28px; + font-size: 28px; + + svg { + width: inherit; + height: inherit; + } + } + } + + md-select { + margin: 0; + } + } + + .panel-timer { + max-width: none; + padding-right: 250px; + padding-left: 90px; + margin-top: -20px; + font-size: 12px; + font-weight: 500; + text-align: center; + } } } diff --git a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html index 5ffc6d56f9..498ca5cb71 100644 --- a/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html +++ b/ui/src/app/widget/lib/tripAnimation/trip-animation-widget.tpl.html @@ -15,40 +15,53 @@ limitations under the License. --> -
    -
    +
    {{vm.trips[vm.activeTripIndex].settings.labelText}}
    -
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + fast_rewind + + + skip_previous + - - + + skip_next + + + fast_forward + + + + play_circle_outline + {{ speed}} - - + + + pause_circle_outline + -
    {{vm.trips[vm.activeTripIndex].timeRange[vm.index].ts | date:'medium'}} -
    -
    -
    -
    -
    - - - - - - -
    -
    +
    {{vm.trips[vm.activeTripIndex].timeRange[vm.index].ts | date:'medium'}}
    -
    \ No newline at end of file +
    From 3ab8430a474102189a36ba25357af45098307a94 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2019 19:13:33 +0200 Subject: [PATCH 57/74] Add Ukrainian translation. --- ui/src/app/locale/locale.constant-de_DE.json | 3 +- ui/src/app/locale/locale.constant-en_US.json | 3 +- ui/src/app/locale/locale.constant-es_ES.json | 3 +- ui/src/app/locale/locale.constant-fa_IR.json | 3 +- ui/src/app/locale/locale.constant-fr_FR.json | 3 +- ui/src/app/locale/locale.constant-it_IT.json | 3 +- ui/src/app/locale/locale.constant-ja_JA.json | 19 +- ui/src/app/locale/locale.constant-ko_KR.json | 3 +- ui/src/app/locale/locale.constant-ru_RU.json | 3 +- ui/src/app/locale/locale.constant-tr_TR.json | 19 +- ui/src/app/locale/locale.constant-uk_UA.json | 2183 ++++++++++++++++++ ui/src/app/locale/locale.constant-zh_CN.json | 15 +- 12 files changed, 2227 insertions(+), 33 deletions(-) create mode 100644 ui/src/app/locale/locale.constant-uk_UA.json diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index bc241e28a5..ff55d1bee3 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -1577,7 +1577,8 @@ "es_ES": "Spanisch", "ja_JA": "Japanisch", "tr_TR": "Türkisch", - "fa_IR": "Persisch" + "fa_IR": "Persisch", + "uk_UA": "Ukrainisch" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index d1fe2c88c9..0f951d39c7 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1584,7 +1584,8 @@ "es_ES": "Spanish", "ja_JA": "Japanese", "tr_TR": "Turkish", - "fa_IR": "Persian" + "fa_IR": "Persian", + "uk_UA": "Ukrainian" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 48ace08e6b..cc5dd7f405 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -1577,7 +1577,8 @@ "es_ES": "Español", "ja_JA": "Japonés", "tr_TR": "Turco", - "fa_IR": "Persa" + "fa_IR": "Persa", + "uk_UA": "Ucraniano" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-fa_IR.json b/ui/src/app/locale/locale.constant-fa_IR.json index de9b2a4f5e..80c8c421cb 100644 --- a/ui/src/app/locale/locale.constant-fa_IR.json +++ b/ui/src/app/locale/locale.constant-fa_IR.json @@ -1576,7 +1576,8 @@ "es_ES": "اسپانيولي", "ja_JA": "ژاپني", "tr_TR": "ترکي", - "fa_IR": "فارسي" + "fa_IR": "فارسي", + "uk_UA": "اوکراین" } } } diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index f22f90548b..e10dd54c08 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -1014,7 +1014,8 @@ "zh_CN": "Chinois", "ja_JA": "Japonaise", "tr_TR": "Turc", - "fa_IR": "Persane" + "fa_IR": "Persane", + "uk_UA": "Ukrainien" } }, "layout": { diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json index 986fdc19a0..e2cdb9c3ab 100644 --- a/ui/src/app/locale/locale.constant-it_IT.json +++ b/ui/src/app/locale/locale.constant-it_IT.json @@ -1582,7 +1582,8 @@ "es_ES": "Spagnolo", "ja_JA": "Giapponese", "tr_TR": "Turco", - "fa_IR": "Persiana" + "fa_IR": "Persiana", + "uk_UA": "Ucraino" } } } diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json index 9fe6971ff9..9345359109 100644 --- a/ui/src/app/locale/locale.constant-ja_JA.json +++ b/ui/src/app/locale/locale.constant-ja_JA.json @@ -1205,10 +1205,10 @@ "tenant-required": "テナントが必要です" }, "timeinterval": { - "seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }", - "minutes-interval": "{ minutes, select, 1 {1 minute} other {# minutes} }", - "hours-interval": "{ hours, select, 1 {1 hour} other {# hours} }", - "days-interval": "{ days, select, 1 {1 day} other {# days} }", + "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", + "minutes-interval": "{ minutes, plural, 1 {1 minute} other {# minutes} }", + "hours-interval": "{ hours, plural, 1 {1 hour} other {# hours} }", + "days-interval": "{ days, plural, 1 {1 day} other {# days} }", "days": "日々", "hours": "時間", "minutes": "分", @@ -1216,10 +1216,10 @@ "advanced": "上級" }, "timewindow": { - "days": "{ days, select, 1 { day } other {# days } }", - "hours": "{ hours, select, 0 { hour } 1 {1 hour } other {# hours } }", - "minutes": "{ minutes, select, 0 { minute } 1 {1 minute } other {# minutes } }", - "seconds": "{ seconds, select, 0 { second } 1 {1 second } other {# seconds } }", + "days": "{ days, plural, 1 { day } other {# days } }", + "hours": "{ hours, plural, 0 { hour } 1 {1 hour } other {# hours } }", + "minutes": "{ minutes, plural, 0 { minute } 1 {1 minute } other {# minutes } }", + "seconds": "{ seconds, plural, 0 { second } 1 {1 second } other {# seconds } }", "realtime": "リアルタイム", "history": "歴史", "last-prefix": "最終", @@ -1460,7 +1460,8 @@ "es_ES": "スペイン語", "ja_JA": "日本語", "tr_TR": "トルコ語", - "fa_IR": "ペルシャ語" + "fa_IR": "ペルシャ語", + "uk_UA": "ウクライナ語" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-ko_KR.json b/ui/src/app/locale/locale.constant-ko_KR.json index 87f92dc3e4..7ef4935df5 100644 --- a/ui/src/app/locale/locale.constant-ko_KR.json +++ b/ui/src/app/locale/locale.constant-ko_KR.json @@ -1336,7 +1336,8 @@ "it_IT": "이탈리아 사람", "ja_JA": "일본어", "tr_TR": "터키어", - "fa_IR": "페르시아 인" + "fa_IR": "페르시아 인", + "uk_UA": "우크라이나의" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 2d05051de8..7477c6d70e 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1576,7 +1576,8 @@ "tr_TR": "Турецкий", "fr_FR": "Французский", "ja_JA": "Японский", - "fa_IR": "Персидский" + "fa_IR": "Персидский", + "uk_UA": "Украинский" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-tr_TR.json b/ui/src/app/locale/locale.constant-tr_TR.json index e65d07bd83..52dc6cc10a 100644 --- a/ui/src/app/locale/locale.constant-tr_TR.json +++ b/ui/src/app/locale/locale.constant-tr_TR.json @@ -1285,10 +1285,10 @@ "tenant-required": "Tenant gerekli" }, "timeinterval": { - "seconds-interval": "{ seconds, select, 1 {1 saniye} other {# saniye} }", - "minutes-interval": "{ minutes, select, 1 {1 dakika} other {# dakika} }", - "hours-interval": "{ hours, select, 1 {1 saat} other {# saat} }", - "days-interval": "{ days, select, 1 {1 gün} other {# gün} }", + "seconds-interval": "{ seconds, plural, 1 {1 saniye} other {# saniye} }", + "minutes-interval": "{ minutes, plural, 1 {1 dakika} other {# dakika} }", + "hours-interval": "{ hours, plural, 1 {1 saat} other {# saat} }", + "days-interval": "{ days, plural, 1 {1 gün} other {# gün} }", "days": "Gün", "hours": "Saat", "minutes": "Dakika", @@ -1296,10 +1296,10 @@ "advanced": "İleri düzey" }, "timewindow": { - "days": "{ days, select, 1 { gün } other {# gün } }", - "hours": "{ hours, select, 0 { saat } 1 {1 saat } other {# saat } }", - "minutes": "{ minutes, select, 0 { dakika } 1 {1 dakika } other {# dakika } }", - "seconds": "{ seconds, select, 0 { saniye } 1 {1 saniye } other {# saniye } }", + "days": "{ days, plural, 1 { gün } other {# gün } }", + "hours": "{ hours, plural, 0 { saat } 1 {1 saat } other {# saat } }", + "minutes": "{ minutes, plural, 0 { dakika } 1 {1 dakika } other {# dakika } }", + "seconds": "{ seconds, plural, 0 { saniye } 1 {1 saniye } other {# saniye } }", "realtime": "Gerçek zaman", "history": "Tarih", "last-prefix": "son", @@ -1542,7 +1542,8 @@ "es_ES": "İspanyol", "ja_JA": "Japonca", "tr_TR": "Türkçe", - "fa_IR": "Farsça" + "fa_IR": "Farsça", + "uk_UA": "Ukrayna" } } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-uk_UA.json b/ui/src/app/locale/locale.constant-uk_UA.json new file mode 100644 index 0000000000..4eb59a2d40 --- /dev/null +++ b/ui/src/app/locale/locale.constant-uk_UA.json @@ -0,0 +1,2183 @@ + { + "access": { + "unauthorized": "Неавторизований", + "unauthorized-access": "Неавторизований доступ", + "unauthorized-access-text": "Щоб отримати доступ до цього ресурсу, потрібно ввійти в систему!", + "access-forbidden": "Доступ заборонено", + "access-forbidden-text": "Недостатньо прав для доступу!
    Спробуйте увійти як інший користувач, якщо ви все ще хочете отримати доступ до цього ресурсу.", + "refresh-token-expired": "Дані про сесію застарілі", + "refresh-token-failed": "Не вдається відновити сеанс" + }, + "action": { + "activate": "Активувати", + "suspend": "Призупинити", + "save": "Зберегти", + "saveAs": "Зберегти як", + "cancel": "Скасувати", + "ok": "OK", + "delete": "Видалити", + "add": "Додати", + "yes": "Так", + "no": "Ні", + "update": "Оновити", + "remove": "Видалити", + "search": "Пошук", + "clear-search": "Очистити пошук", + "assign": "Надати", + "unassign": "Позбавити", + "share": "Поділитися", + "make-private": "Зробити приватним", + "apply": "Застосувати", + "apply-changes": "Застосувати зміни", + "edit-mode": "Режим редагування", + "enter-edit-mode": "Ввійти в режим редагування", + "decline-changes": "Відхилити зміни", + "close": "Закрити", + "back": "Назад", + "run": "Запустити", + "sign-in": "Увійти!", + "edit": "Редагувати", + "view": "Переглянути", + "create": "Створити", + "drag": "Перетягнути", + "refresh": "Оновити", + "undo": "Скасувати", + "copy": "Скопіювати", + "paste": "Вставити", + "copy-reference": "Копіювати посилання", + "paste-reference": "Вставити посилання", + "import": "Імпортувати", + "export": "Експортувати", + "share-via": "Поділитися через {{provider}}", + "move": "Перемістити", + "select": "Вибрати" + }, + "aggregation": { + "aggregation": "Агрегація", + "function": "Функція агрегації даних", + "limit": "Максимальні значення", + "group-interval": "Інтервал групування", + "min": "Мінімальний", + "max": "Максимальний", + "avg": "Середній", + "sum": "Сума", + "count": "Рахувати", + "none": "Відсутня" + }, + "admin": { + "general": "Загальне", + "general-settings": "Загальні налаштування", + "outgoing-mail": "Поштовий сервер", + "outgoing-mail-settings": "Налаштування сервера вихідної пошти", + "system-settings": "Налаштування системи", + "test-mail-sent": "Тестовий лист успішно відправлено!", + "base-url": "Базова URL-адреса", + "base-url-required": "Базова URL-адреса обов'язкова.", + "mail-from": "Електронна адреса", + "mail-from-required": "Електронна адреса обов'язкова.", + "smtp-protocol": "Протокол SMTP", + "smtp-host": "Хост SMTP", + "smtp-host-required": "Хост SMTP обов'язковий.", + "smtp-port": "SMTP-порт", + "smtp-port-required": "Ви повинні надати SMTP-порт.", + "smtp-port-invalid": "Це не схоже на дійсний SMTP-порт.", + "timeout-msec": "Час очікування (msec)", + "timeout-required": "Необхідно задати час очікування.", + "timeout-invalid": "Це не схоже на правильний час очікування.", + "enable-tls": "Увімкнути TLS", + "send-test-mail": "Надіслати тестове повідомлення", + "use-system-mail-settings": "Використовувати параметри системного поштового сервера", + "mail-templates": "Шаблони електронної пошти", + "mail-template-settings": "Налаштування шаблонів електронної пошти", + "use-system-mail-template-settings": "Використовувати шаблони системної електронної пошти", + "mail-template": { + "mail-template": "Шаблон електронної пошти", + "test": "Тестове повідомлення", + "activation": "Повідомлення про активацію рахунку", + "account-activated": "Обліковий запис активовано", + "reset-password": "Відновити повідомлення пароля", + "password-was-reset": "Пароль було надіслано повідомленням" + }, + "mail-subject": "Тема повідомлення", + "mail-body": "Вміст повідомлення" + }, + "alarm": { + "alarm": "Сигнал тривоги", + "alarms": "Сигнали тривоги", + "select-alarm": "Вибрати сигнал тривоги", + "no-alarms-matching": "Сигналів тривоги '{{entity}}' не знайдено.", + "alarm-required": "Сигнал тривоги необхідний", + "alarm-status": "Статус сигналу тривоги", + "search-status": { + "ANY": "Будь які", + "ACTIVE": "Активні", + "CLEARED": "Неактивні", + "ACK": "Прийняті", + "UNACK": "Неприйняті" + }, + "display-status": { + "ACTIVE_UNACK": "Активні та неприйняті", + "ACTIVE_ACK": "Активні та прийняті", + "CLEARED_UNACK": "Неактивні та неприйняті", + "CLEARED_ACK": "Неактивні та прийняті" + }, + "no-alarms-prompt": "Сигналів тривоги не знайдено", + "created-time": "Час створення", + "type": "Тип", + "severity": "Серйозність", + "originator": "Ініціатор", + "originator-type": "Тип ініціатору", + "details": "Деталі", + "status": "Статус", + "alarm-details": "Деталі сигналу тривоги", + "start-time": "Початок", + "end-time": "Кінець", + "ack-time": "Час прийняття", + "clear-time": "Час деактивації", + "severity-critical": "Критичні", + "severity-major": "Важливі", + "severity-minor": "Неважливі", + "severity-warning": "Попередження", + "severity-indeterminate": "Невизначені", + "acknowledge": "Прийняти", + "clear": "Деактивувати", + "search": "Шукати сигнали тривоги", + "selected-alarms": "{ count, plural, 1 {1 сигнал тривоги} other {# сигнали тривоги} } вибрані", + "no-data": "Немає даних для відображення", + "polling-interval": "Інтервал опитування (сек)", + "polling-interval-required": "Необхідно задати інтервал опитування.", + "min-polling-interval-message": "Дозволяється щонайменше 1 секунда інтервалу очікування.", + "aknowledge-alarms-title": "Підтвердити { count, plural, 1 {1 сигнал тривоги} other {# сигнали тривоги} }", + "aknowledge-alarms-text": "Ви впевнені, що хочете підтвердити { count, plural, 1 {1 сигнал тривоги} other {# сигнали тривоги} }?", + "aknowledge-alarm-title": "Підтвердити сигнал тривоги", + "aknowledge-alarm-text": "Ви впевнені, що хочете підтвердити сигнал тривоги?", + "clear-alarms-title": "Деактивувати { count, plural, 1 {1 сигнал тривоги} other {# сигнали тривоги} }", + "clear-alarms-text": "Ви впевнені, що хочете деактивувати { count, plural, 1 {1 сигнал тривоги} other {# сигнали тривоги} }?", + "clear-alarm-title": "Деактивувати сигнал тривоги", + "clear-alarm-text": "Ви впевнені, що хочете деактивувати сигнал тривоги?", + "alarm-status-filter": "Фільтр статусу сигналу тривоги" + }, + "alias": { + "add": "Додати псевдонім ", + "edit": "Редагувати псевдонім", + "name": "Ім'я", + "name-required": "Необхідно вказати псевдонім", + "duplicate-alias": "Псевдонім з такою назвою вже існує.", + "filter-type-single-entity": "Єдина сутність", + "filter-type-entity-group": "Група сутностей", + "filter-type-entity-list": "Список сутностей", + "filter-type-entity-name": "Назва сутності", + "filter-type-entity-group-list": "Список груп сутностей", + "filter-type-entity-group-name": "Назва групи сутностей", + "filter-type-state-entity": "Сутність з стану панелі пристроїв", + "filter-type-state-entity-description": "Сутність, взята з параметрів стану панелі пристроїв", + "filter-type-asset-type": "Тип активу", + "filter-type-asset-type-description": "Тип активів '{{assetType}}'", + "filter-type-asset-type-and-name-description": "Тип активів '{{assetType}}' і ім'я, що починаються з '{{prefix}}'", + "filter-type-device-type": "Тип пристрою", + "filter-type-device-type-description": "Тип пристроїв '{{deviceType}}'", + "filter-type-device-type-and-name-description": "Тип пристроїв '{{deviceType}}' і ім'я, що починаються з '{{prefix}}'", + "filter-type-entity-view-type": "Тип перегляду сутності", + "filter-type-entity-view-type-description": "Перегляд сутності з типом '{{entityView}}'", + "filter-type-entity-view-type-and-name-description": "Перегляд сутності з типом'{{entityView}}' і іменем, що починаються з '{{prefix}}'", + "filter-type-relations-query": "Запит відносин", + "filter-type-relations-query-description": "{{entities}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Запит пошуку активу", + "filter-type-asset-search-query-description": "Активи з типами {{assetTypes}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Запит пошуку пристрою", + "filter-type-device-search-query-description": "Пристрої з типами {{deviceTypes}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Запит пошуку переглядів сутностей", + "filter-type-entity-view-search-query-description": "Перегляд сутності з типами {{entityViewTypes}}, які мають {{relationType}} відношення {{direction}} {{rootEntity}}", + "entity-filter": "Фільтр сутності", + "resolve-multiple": "Як декілька сутностей", + "filter-type": "Тип фільтра", + "filter-type-required": "Необхідно вказати тип фільтра.", + "entity-filter-no-entity-matched": "Не знайдено жодних сутностей, які відповідають вказаному фільтру.", + "no-entity-filter-specified": "No entity filter specified", + "root-state-entity": "Use dashboard state entity as root", + "group-state-entity": "Use dashboard state entity as entity group", + "root-entity": "Root entity", + "state-entity-parameter-name": "State entity parameter name", + "default-state-entity": "Default state entity", + "default-state-entity-group": "Default state entity group", + "default-entity-parameter-name": "By default", + "max-relation-level": "Max relation level", + "unlimited-level": "Unlimited level", + "state-entity": "Dashboard state entity", + "entities-of-group-state-entity": "Entities from dashboard state entity group", + "all-entities": "All entities", + "any-relation": "any" + }, + "asset": { + "asset": "Актив", + "assets": "Активи", + "management": "Управління активами", + "view-assets": "Переглянути активи", + "add": "Додати активи", + "assign-to-customer": "Надати клієнту", + "assign-asset-to-customer": "Надати активи клієнту", + "assign-asset-to-customer-text": "Будь ласка, виберіть ресурси, призначені для клієнта", + "no-assets-text": "Не знайдено активів", + "assign-to-customer-text": "Будь ласка, виберіть клієнта, щоб надати активи", + "public": "Публічно", + "assignedToCustomer": "Наданий клієнту", + "make-public": "Зробити актив(и) публічним(и)", + "make-private": "Зробити актив(и) приватним(и)", + "unassign-from-customer": "Позбавити клієнта", + "delete": "Видалити актив", + "asset-public": "Актив є загальнодоступним", + "asset-type": "Тип активу", + "asset-type-required": "Тип активу обов'язковий.", + "select-asset-type": "Виберіть тип активу", + "enter-asset-type": "Введіть тип активу", + "any-asset": "Будь-який актив", + "no-asset-types-matching": "Не знайдено жодних активів, що відповідають даному типу '{{entitySubtype}}'.", + "asset-type-list-empty": "Не вибрано жодного типу активів.", + "asset-types": "Типи активів", + "name": "Ім'я", + "name-required": "Ім'я обов'язкове.", + "description": "Опис", + "type": "Тип", + "type-required": "Тип обов'язковий.", + "details": "Подробиці", + "events": "Події", + "add-asset-text": "Додати новий актив", + "asset-details": "Інформація про актив", + "assign-assets": "Надати активи", + "assign-assets-text": "Надати { count, plural, 1 {1 актив} other {# активи} } клієнту", + "delete-assets": "Видалити активи", + "unassign-assets": "Позбавити активів", + "unassign-assets-action-title": "Позбавити { count, plural, 1 {1 актив} other {# активи} } клієнта", + "assign-new-asset": "Надати новий актив", + "delete-asset-title": "Ви впевнені, що хочете видалити актив '{{assetName}}'?", + "delete-asset-text": "Будьте обережні, після підтвердження, актив і всі пов'язані з ним дані буде втрачено", + "delete-assets-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 актив} other {# активи} }?", + "delete-assets-action-title": "Видалити{ count, plural, 1 {1 актив} other {# активи} }", + "delete-assets-text": "Будьте обережні, після підтвердження всі вибрані об'єкти буде видалено, і всі пов'язані з ними дані буде втрачено.", + "make-public-asset-title": "Ви дійсно хочете, щоб актив '{{assetName}}' був загальнодоступним?", + "make-public-asset-text": "Після підтвердження, актив і всі його дані будуть доступними для інших.", + "make-private-asset-title": "Ви впевнені, що хочете зробити актив {{assetName}} приватним?", + "make-private-asset-text": "Після підтвердження, актив та всі його дані будуть приватними та не будуть доступні іншим.", + "unassign-asset-title": "Ви впевнені, що хочете позбавити активу '{{assetName}}'?", + "unassign-asset-text": "Після підтвердження клієнт буде позбавлений активу. Дані активу не будуть доступні клієнту.", + "unassign-asset": "Позбавити активу", + "unassign-assets-title": "Ви впевнені, що хочете позбавити активів { count, plural, 1 {1 актив} other {# активи} }?", + "unassign-assets-text": "Після підтвердження, клієнт буде позбавлений усіх вибраних активів. Дані активів не будуть доступні клієнту.", + "copyId": "Копіювати Id активу", + "idCopiedMessage": "Id активу був скопійований у буфер обміну", + "select-asset": "Виберіть актив", + "no-assets-matching": "Не знайдено жодних активів, що відповідають'{{entity}}'.", + "asset-required": "Необхідно задати актив", + "name-starts-with": "Назва активу починається з", + "selected-assets": "{ count, plural, 1 {1 актив} other {# активи} } selected", + "search": "Пошук активів", + "select-group-to-add": "Виберіть цільову групу, щоб додати вибрані активи", + "select-group-to-move": "Виберіть цільову групу для переміщення вибраних активів", + "remove-assets-from-group": "Ви впевнені, що хочете видалити { count, plural, 1 {1 актив} other {# актив} } з групи '{entityGroup}'?", + "group": "Група активів", + "list-of-groups": "{ count, plural, 1 {Одна група активів} other {Список # груп активів} }", + "group-name-starts-with": "Групи активів, чиї імена починаються з '{{prefix}}'" + }, + "attribute": { + "attributes": "Атрибути", + "latest-telemetry": "Остання телеметрія", + "attributes-scope": "Область видимості атрибутів", + "scope-latest-telemetry": "Остання телеметрія", + "scope-client": "Клієнтські атрибути", + "scope-server": "Серверні атрибути", + "scope-shared": "Спільні атрибути", + "add": "Додати атрибут", + "add-attribute-prompt": "Будь ласка, додайте атрибут", + "key": "Ключ", + "last-update-time": "Останнє оновлення", + "key-required": "Ключ атрибута обов'язковий.", + "value": "Значення", + "value-required": "Значення атрибута обов'язкове.", + "delete-attributes-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 attribute} other {# attributes} }?", + "delete-attributes-text": "Будьте обережні, після підтвердження, всі виділені атрибути будуть видалені.", + "delete-attributes": "Видалити атрибути", + "enter-attribute-value": "Введіть значення атрибута", + "show-on-widget": "Показати на віджеті", + "widget-mode": "Режим віджетів", + "next-widget": "Наступний віджет", + "prev-widget": "Попередній віджет", + "add-to-dashboard": "Додати до інформаційної панелі", + "add-widget-to-dashboard": "Додати віджет до інформаційної панелі", + "selected-attributes": "{ count, plural, 1 {1 attribute} other {# attributes} } selected ...вибрані вибрати", + "selected-telemetry": "{ count, plural, 1 {1 telemetry unit} other {# telemetry units} } selected" + }, + "audit-log": { + "audit": "Операція", + "audit-logs": "Журнал операцій", + "timestamp": "Тимчасова позначка", + "entity-type": "Тип одиниці", + "entity-name": "Назва організації", + "user": "Користувач", + "type": "Тип", + "status": "Статус", + "details": "Подробиці", + "type-added": "Додано", + "type-deleted": "Вилучено", + "type-updated": "Оновлено", + "type-attributes-updated": "Атрибути оновлені", + "type-attributes-deleted": "Атрибути видалені", + "type-rpc-call": "RPC дзвінок", + "type-credentials-updated": "Авторизаційні дані оновлено", + "type-assigned-to-customer": "Призначено клієнту", + "type-unassigned-from-customer": "Позбавлено від клієнта", + "type-activated": "Активовано", + "type-suspended": "Призупинено", + "type-credentials-read": "Авторизаційні дані прочитані", + "type-attributes-read": "Атрибути читаються", + "type-added-to-entity-group": "Додано до групи", + "type-removed-from-entity-group": "Вилучено з групи", + "type-relation-add-or-update": "Відношення оновлено", + "type-relation-delete": "Відношення видалено", + "type-relations-delete": "Всі відношення видалено", + "type-alarm-ack": "Визнано", + "type-alarm-clear": "Очищено", + "type-rest-api-rule-engine-call": "Rule engine REST API call", + "status-success": "Успішно", + "status-failure": "Невдало", + "audit-log-details": "Подробиці журналу операцій", + "no-audit-logs-prompt": "Жодних журналів операцій не знайдено", + "action-data": "Дані про дії", + "failure-details": "Невдалі подробиці", + "search": "Пошук журналів перевірки", + "clear-search": "Очистити пошук" + }, + "confirm-on-exit": { + "message": "У вас є незбережені зміни. Ви впевнені, що хочете залишити цю сторінку?", + "html-message": "У вас є незбережені зміни.
    Ви впевнені, що хочете залишити цю сторінку?", + "title": "Незбережені зміни" + }, + "contact": { + "country": "Країна", + "city": "Місто", + "state": "Штат / Провінція", + "postal-code": "Поштовий індекс", + "postal-code-invalid": "Неправильний формат поштового індексу.", + "address": "Адреса", + "address2": "Адреса 2", + "phone": "Телефон", + "email": "Електронна пошта", + "no-address": "Немає адреси" + }, + "common": { + "username": "Ім'я користувача", + "password": "Пароль", + "enter-username": "Введіть ім'я користувача", + "enter-password": "Введіть пароль", + "enter-search": "Введіть пошук" + }, + "converter": { + "converter": "Перетворювач даних", + "converters": "Перетворювачі даних", + "select-converter": "Виберіть перетворювач даних", + "no-converters-matching": "Не має перетворювачів даних, які відповідають '{{entity}}'.", + "converter-required": "Необхідно вказати перетворювач даних", + "delete": "Видалити перетворювач даних", + "management": "Управління перетворювачами даних", + "add-converter-text": "Додати новий перетворювач даних", + "no-converters-text": "Перетворювачів даних не знайдено", + "selected-converters": "{ count, plural, 1 {1 перетворювач даних} other {# перетворювачі даних} } вибраний", + "delete-converter-title": "Ви впевнені, що хочете видалити перетворювач даних '{{converterName}}'?", + "delete-converter-text": "Будьте обережні, після підтвердження, перетворювач даних та всі пов'язані з ним дані,стануть недоступними).", + "delete-converters-title": "Ви впевнені, що хочете видалити{ count, plural, 1 {1 перетворювач даних} other {# перетворювачі даних} }?", + "delete-converters-action-title": "Видалити { count, plural, 1 {1 перетворювач даних} other {# перетворювачі даних} }", + "delete-converters-text": "Будьте обережні, після підтвердження всі вибрані перетворювачі даних буде видалено, і всі пов'язані з ними дані буде втрачено.", + "events": "Події", + "add": "Додати перетворювач даних", + "converter-details": "Подробиці про перетворювач даних", + "details": "Подробиці", + "copyId": "Копіювати Id перетворювача даних", + "idCopiedMessage": "Id перетворювача даних було скопійовано у буфер обміну", + "debug-mode": "Режим налагодження", + "name": "Ім'я", + "name-required": "Ім'я обов'язкове.", + "description": "Опис", + "decoder": "Декодер", + "encoder": "Кодер", + "test-decoder-fuction": "Тестування функції декодера", + "test-encoder-fuction": "Тестування функції кодера", + "decoder-input-params": "Параметри введення декодера", + "encoder-input-params": "Параметри введення кодера", + "payload": "Вхідне повідомлення", + "payload-content-type": "Тип контенту вхідного повідомлення", + "payload-content": "Зміст вхідного повідомлення", + "message": "Повідомлення", + "message-type": "Тип повідомлення", + "message-type-required": "Необхідно задати тип повідомлення", + "test": "Тест", + "metadata": "Метадані", + "metadata-required": "Записи метаданих не можуть бути порожніми.", + "integration-metadata": "Інтеграція метаданих", + "integration-metadata-required": "Записи інтеграції метаданих не можуть бути порожніми.", + "output": "Вихідні дані", + "import": "Імпорт перетворювача даних", + "export": "Експорт перетворювача даних", + "export-failed-error": "Неможливо експортувати перетворювач даних: {{помилка}}", + "create-new-converter": "Створити новий перетворювач даних", + "converter-file": "Файл перетворювача даних(конвектер файл)", + "invalid-converter-file-error": "Неможливо імпортувати перетворювач даних: недійсна структура даних перетворювача.", + "type": "Тип", + "type-required": "Необхідно задати тип.", + "type-uplink": "Від пристрою", + "type-downlink": "До пристрою" + }, + "content-type": { + "json": "Json", + "text": "Текст", + "binary": "Бінарний (Base64)" + }, + "customer": { + "customer": "Клієнт", + "customers": "Клієнти", + "management": "Клієнтський менеджмент", + "dashboard": "Інформаційна панель клієнта", + "dashboards": "Інформаційні панелі клієнта", + "devices": "Пристрої клієнта", + "entity-views": "Представлення сутностей", + "assets": "Клієнтські активи", + "public-dashboards": "Публічні інформаційні панелі", + "public-devices": "Публічні пристрої", + "public-assets": "Публічні активи", + "public-entity-views": "Публічне представлення сутностей 440", + "add": "Додати клієнта", + "delete": "Видалити клієнта", + "manage-customer-users": "Керування користувачами клієнта", + "manage-customer-devices": "Керування пристроями клієнта", + "manage-customer-dashboards": "Керування інформаційними панелями клієнта", + "manage-public-devices": "Керувати загальнодоступними пристроями", + "manage-public-dashboards": "Керування загальнодоступними інформаційними панелями", + "manage-customer-assets": "Керування активами клієнта", + "manage-public-assets": "Керування загальнодоступними активами", + "add-customer-text": "Додати нового клієнта", + "no-customers-text": "Клієнтів не знайдено", + "customer-details": "Інформація про клієнта", + "delete-customer-title": "Ви впевнені, що хочете видалити клієнта '{{customerTitle}}'?", + "delete-customer-text": "Будьте обережні, після підтвердження, клієнт та всі пов'язані з ним дані, стануть недоступними.", + "delete-customers-title": "Ви впевнені, що хочете видалити {count, plural, 1 {1 клієнт}, інші {# клієнти}}?", + "delete-customers-action-title": "Видалити{ count, plural, 1 {1 клієнт} other {# клієнти} }", + "delete-customers-text": "Будьте обережні, після підтвердження, всі вибрані клієнти будуть видалені і всі пов'язані з ними дані, стануть недоступними.", + "manage-users": "Керування користувачами", + "manage-assets": "Керування активами", + "manage-devices": "Керування пристроями", + "manage-dashboards": "Керування інформаційними панелями", + "title": "Назва", + "title-required": "Необхідно задати назву.", + "description": "Опис", + "details": "Подробиці", + "events": "Події", + "copyId": "Копіювати Id клієнта", + "idCopiedMessage": "Id клієнта було скопійовано в буфер обміну", + "select-customer": "Виберіть клієнта", + "no-customers-matching": "Клієнтів, які відповідають '{{entity}}' не знайдено.", + "customer-required": "Необхідно задати клієнта", + "selected-customers": "{ count, plural, 1 {1 клієнт} інші {# клієнти} } вибрано", + "search": "Пошук клієнтів", + "select-group-to-add": "Виберіть цільову групу, щоб додати вибраних клієнтів", + "select-group-to-move": "Виберіть цільову групу для переміщення вибраних клієнтів", + "remove-customers-from-group": "Ви впевнені, що хочете видалити{ count, plural, 1 {1 клієнт} other {# клієнти} } з групи'{entityGroup}'?", + "group": "Група клієнтів", + "list-of-groups": "{ count, plural, 1 {Одна група клієнтів} other {Список # груп клієнтів} }", + "group-name-starts-with": "Групи клієнтів, імена яких починаються з '{{prefix}}'", + "select-default-customer": "Виберати клієнта за замовчуванням", + "default-customer": "Клієнт за замовчуванням", + "default-customer-required": "Необхідно вказати клієнта за замовчуванням для налагодження панелі візуалізації на рівні замовника", + "allow-white-labeling": "Дозволити брендування" + }, + "custom-translation": { + "custom-translation": "Переклад для користувача", + "translation-map": "Карта перекладу", + "key": "Ключ перекладу", + "import": "Імпорт перекладу", + "export": "Експорт перекладу", + "export-data": "Дані про експорт перекладу", + "import-data": "Дані про імпорт перекладу", + "translation-file": "Файл перекладу", + "invalid-translation-file-error": "Неможливо імпортувати файл перекладу: недійсна структура даних перекладу.", + "custom-translation-hint": "Визначте індивідуальний переклад в JSON нижче. Цей JSON перезапише переклад за замовчуванням. Натисніть 'Завантажити файл перекладу', щоб отримати існуючий переклад. Ви також можете скористатись завантаженим файлом як посиланням на наявні пари параметрів перекладу ключ-значення.", + "download-locale-file": "Завантажити файл перекладу" + }, + "datetime": { + "date-from": "Дата від", + "time-from": "Час від", + "date-to": "Дата до", + "time-to": "Час до" + }, + "dashboard": { + "dashboard": "Панель приладів", + "dashboards": "Панелі приладів", + "management": "Управління панеллю приладів", + "view-dashboards": "Переглянути панелі приладів", + "add": "Додати панель приладів", + "assign-dashboard-to-customer": "Призначити панель(і) приладів замовнику", + "assign-dashboard-to-customer-text": "Будь ласка, виберіть панелі пристроїв, щоб призначити їх клієнту", + "assign-to-customer-text": "Виберіть клієнта, щоб призначити панелі пристроїв", + "assign-to-customer": "Призначити клієнту", + "unassign-from-customer": "Позбавити клієнта", + "make-public": "Зробити панель приладів публічною", + "make-private": "Зробити панель приладів приватною", + "manage-assigned-customers": "Керування призначеними клієнтами", + "assigned-customers": "Призначені клієнтам", + "assign-to-customers": "Призначити панелі приладів клієнтам", + "assign-to-customers-text": "Виберіть клієнтів для призначення панелей приладів", + "unassign-from-customers": "Позбавити клієнтів призначенних панелей приладів", + "unassign-from-customers-text": "Виберіть клієнтів для позбавлення їх призначених панелей приладів", + "no-dashboards-text": "Панелі приладів не знайдені", + "no-widgets": "Не налаштовано жодних віджетів", + "add-widget": "Додати новий віджет", + "title": "Назва", + "select-widget-title": "Вибрати віджет", + "select-widget-subtitle": "Список доступних типів віджетів", + "delete": "Видалити панель приладів", + "title-required": "Необхідно задати назву.", + "description": "Опис", + "details": "подробиці", + "dashboard-details": "Подробиці панелі приладів", + "add-dashboard-text": "Додати нову панель приладів", + "assign-dashboards": "Призначити панель приладів", + "assign-new-dashboard": "Призначити нову панель приладів", + "assign-dashboards-text": "Призначити { count, plural, 1 {1 панель приладів} other {# панелі приладів} } користувачам", + "unassign-dashboards-action-text": "Позбавити { count, plural, 1 {1 палелі приладів} other {# панелей приладів} } клієнтів", + "delete-dashboards": "Видалити панель приладів", + "unassign-dashboards": "Позбавити панелей приладів", + "unassign-dashboards-action-title": "Позбавити { count, plural, 1 {1 палелі приладів} other {# панелей приладів} } клієнтів", + "delete-dashboard-title": "Ви впевнені, що хочете видалити панель приладів '{{назва панелі приладів}}'?", + "delete-dashboard-text": "Будьте обережні, після підтвердження, панель приладів і всі пов'язані з нею дані стануть недоступними.", + "delete-dashboards-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 панель приладів} other {# панелі приладів} }?", + "delete-dashboards-action-title": "Видалити { count, plural, 1 {1 панель приладів} other {# панелі приладів} }", + "delete-dashboards-text": "Будьте обережні, після підтвердження, всі вибрані панелі приладів буде видалено, і всі пов'язані з ними дані стануть недоступними.", + "unassign-dashboard-title": "Ви впевнені, що хочете позбавити панелі приладів '{{назва інформаційної панелі}}'?", + "unassign-dashboard-text": "Після підтвердження, клієнт буде позбавлений панелі приладів. Панель приладів і пов'язані з нею дані будуть недоступні клієнтові.", + "unassign-dashboard": "Позбавити панелі приладів", + "unassign-dashboards-title": "Ви впевнені, що хочете позбавити { count, plural, 1 {1 панелі приладів} other {# панелей приладів} }?", + "unassign-dashboards-text": "Після підтвердження, клієтн буде позбавлений усіх вибраних панелей приладів і даних, які з ними пов'язані.", + "public-dashboard-title": "Панель приладів тепер публічна", + "public-dashboard-text": "Ваша панель приладів {{dashboardTitle}} тепер публічна і доступна іншим link:", + "public-dashboard-notice": "Note: Не забудьте зробити спільні пристрої загальнодоступними, щоб отримати доступ до їхніх даних.", + "make-private-dashboard-title": "Ви впевнені, що хочете зробити панель приладів '{{назва панелі приладів}}' приватною?", + "make-private-dashboard-text": "Після підтвердження панель приладів стане приватною і не буде доступною іншим.", + "make-private-dashboard": "Зробити панель приладів приватною", + "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", + "select-dashboard": "Вибрати панель приладів", + "no-dashboards-matching": "Не знайдено жодних панелей прилодів'{{entity}}' які відповідають.", + "dashboard-required": "Необхідно задати панель приладів.", + "select-existing": "Виберіть існуючу панель приладів", + "create-new": "Створити нову панель приладів", + "new-dashboard-title": "Нова назва панелі приладів", + "open-dashboard": "Відрити панель приладів", + "set-background": "Встановити фон", + "background-color": "Колір фону", + "background-image": "Фонове зображення", + "background-size-mode": "Режим фонового розміру", + "no-image": "Не вибрано жодного зображення", + "drop-image": "Перетягніть зображення або клацніть, щоб вибрати файл для завантаження.", + "settings": "Налаштування", + "columns-count": "Кількість стовпців", + "columns-count-required": "Необхідно вказати кількість стовпців.", + "min-columns-count-message": "Дозволений мінімум -10 стовпців.", + "max-columns-count-message": "Дозволений максимум - 1000 стовпців.", + "widgets-margins": "Відступ між віджетами", + "horizontal-margin": "Горизонтальний відступ", + "horizontal-margin-required": "Необхідно вказати горизонтальний відступ.", + "min-horizontal-margin-message": "Допустиме мінімальне значення горизонтального відступу - 0.", + "max-horizontal-margin-message": "Допустиме максимальне значення горизонтального відступу - 50.", + "vertical-margin": "Вертикальний відступ", + "vertical-margin-required": "Необхідно вказати вертикальний відступ.", + "min-vertical-margin-message": "Допустиме мінімальне значення вертикального відступу - 0.", + "max-vertical-margin-message": "Допустиме максимальне значення вертикального відступу - 50.", + "autofill-height": "Висота автоматичного заповнення макета", + "mobile-layout": "Налаштування макета для мобільних пристроїв", + "mobile-row-height": "Висота рядка для мобільних пристроїв, px", + "mobile-row-height-required": "Потрібно вказати значення висоти рядка для мобільних пристроїв.", + "min-mobile-row-height-message": "Допустиме мінімальне значення висоти рядка для мобільних пристроїв - 5 пікселів.", + "max-mobile-row-height-message": "Допустиме максимальне значення висоти рядка для мобільних пристроїв - 200 пікселів.", + "display-title": "Відображати назву панелі візуалізації", + "toolbar-always-open": "Тримайте панель візуалізації відкритою", + "title-color": "Колір назви", + "display-dashboards-selection": "Відображення вибору панелей візуалізації", + "display-entities-selection": "Вибір відображення сутності", + "display-dashboard-timewindow": "Відобразити налаштування часового проміжку", + "display-dashboard-export": "Відображення експорту", + "import": "Імпортувати панель візуалізації", + "export": "Експортувати панель візуалізації", + "export-failed-error": "Неможливо експортувати панель візуалізації: {{error}}", + "export-pdf": "Експортувати як PDF", + "export-png": "Експортувати як PNG", + "export-jpg": "Експортувати як JPEG", + "export-json-config": "Експортувати конфігурацію JSON", + "download-dashboard-progress": "Генерування панелі візуалізації {{reportType}} ...", + "create-new-dashboard": "Створити нову панель візуалізації", + "dashboard-file": "Файл панелі візуалізації", + "invalid-dashboard-file-error": "Неможливо імпортувати панель візуалізації: неправильна структура даних панелі візуалізації.", + "dashboard-import-missing-aliases-title": "Configure aliases used by imported dashboard Налаштування псевдонімів, що використовуються імпортованою панеллю візуалізації", + "create-new-widget": "Створити новий віджет", + "import-widget": "Імпортувати віджет", + "widget-file": "Файл віджета", + "invalid-widget-file-error": "Неможливо імпортувати віджет: неправильна структура даних віджета.", + "widget-import-missing-aliases-title": "Налаштувати псевдоніми, що використовуються імпортованим віджетом", + "open-toolbar": "Відкрити панель інструменів ", + "close-toolbar": "Закрити панель інструменів", + "configuration-error": "Помилка конфігурації", + "alias-resolution-error-title": "Помилка конфігурації псевдонімів панелі візуалізації", + "invalid-aliases-config": "Неможливо знайти пристрої, які відповідають певному фільтру псевдонімів.
    Зверніться до свого адміністратора, щоб вирішити цю проблему.", + "select-devices": "Вибрати пристрої", + "assignedToCustomer": "Призначений клієнту", + "assignedToCustomers": "Призначений клієнтам", + "public": "Публічно", + "public-link": "Публічне посилання", + "copy-public-link": "Копіювати публічне посилання", + "public-link-copied-message": "Публічне посилання було скопійоване в буфер обміну панелі візуалізації", + "manage-states": "Керування станами панелі візуалізації", + "states": "Стани панелі візуалізації", + "search-states": "Пошук станів панелі візуалізації", + "selected-states": "{ count, plural, 1 {1 dashboard state} other {# dashboard states} } вибрано", + "edit-state": "Редагувати стан панелі візуалізації", + "delete-state": "Видалити стан панелі візуалізації ", + "add-state": "Додати стан панелі візуалізації", + "state": "Стан панелі візуалізації", + "state-name": "Ім'я", + "state-name-required": "Необхідно вказати назву стану панелі візуалізації.", + "state-id": "Стан Id", + "state-id-required": "Необхідно вказати id стану панелі візуалізації.", + "state-id-exists": "Стан інформаційної панелі з таким id вже існує.", + "is-root-state": "Основний стан", + "delete-state-title": "Видалити стан панелі візуалізації", + "delete-state-text": "Ви впевнені, що хочете видалити стан панелі візуалізації з іменем '{{stateName}}'?", + "show-details": "Показати деталі", + "hide-details": "Приховати деталі", + "select-state": "Виберіть цільовий стан", + "state-controller": "Контроль стану" + }, + "datakey": { + "settings": "Налаштування", + "advanced": "Додатково", + "label": "Мітка", + "color": "Колір", + "units": "Спеціальний символ, який показує наступне значення", + "decimals": "Кількість цифр після плаваючої точки", + "data-generation-func": "Функція генерації даних", + "use-data-post-processing-func": "Використовувати функцію пост-обробки даних", + "configuration": "Конфігурація ключа даних", + "timeseries": "Телеметрія", + "attributes": "Атрибути", + "alarm": "Поля сигнала тривоги", + "timeseries-required": "Необхідно вказати Телеметрія.", + "timeseries-or-attributes-required": "Необхідно вказати телеметрію/атрибути.", + "maximum-timeseries-or-attributes": "Максимальні { count, plural, 1 {1 телеметрія/атрибут дозволені.} other {# телеметрія/атрибути дозволені} }", + "alarm-fields-required": "Необхідно вказати поля сигнала тривоги.", + "function-types": "Типи функцій", + "function-types-required": "Необхідно вказати типи функцій.", + "maximum-function-types": "Maximum { count, plural, 1 {1 function type is allowed.} other {# function types are allowed} }", + "time-description": "мітка часу поточного значення;", + "value-description": "поточне значення;", + "prev-value-description": "результат попереднього виклику функції;", + "time-prev-description": "мітка часу попереднього значення;", + "prev-orig-value-description": "оригінальне попереднє значення;" + }, + "datasource": { + "type": "Тип джерела даних", + "name": "Ім'я", + "add-datasource-prompt": "Додайте джерело даних" + }, + "details": { + "details": "Деталі", + "edit-mode": "Режим редагування", + "toggle-edit-mode": "Перемкнути режим редагування" + }, + "device": { + "device": "Пристрій", + "device-required": "Необхідно задати пристрій.", + "devices": "Пристрої", + "management": "Управління пристроєм", + "view-devices": "Перегляд пристроїв", + "device-alias": "Псевдонім пристрою", + "aliases": "Псевдонім пристроїв", + "no-alias-matching": "'{{alias}}' не знайдено.", + "no-aliases-found": "Псевдонімів не знайдено.", + "no-key-matching": "'{{key}}' не знайдено.", + "no-keys-found": "Ключі не знайдено.", + "create-new-alias": "Створити новий!", + "create-new-key": "Створити новий!", + "duplicate-alias-error": "Псевдонім з таким іменем '{{alias}}' вже існює.
    Псевдоніми пристроїв повинні бути унікальними на панелі візуалізації.", + "configure-alias": "Налаштувати псевдонім '{{alias}}'", + "no-devices-matching": "Не знайдено жодних пристроїв, які відповідають '{{entity}}'.", + "alias": "Псевдонім", + "alias-required": "Необхідно задати псевдонім пристрою.", + "remove-alias": "Видалити псевдонім пристрою", + "add-alias": "Додати псевдонім пристрою", + "name-starts-with": "Ім'я пристрою починається з", + "device-list": "Список пристроїв", + "use-device-name-filter": "Використати фільтр", + "device-list-empty": "Не вибрано жодного пристрою.", + "device-name-filter-required": "Необхідно задати назву фільтра пристрою.", + "device-name-filter-no-device-matched": "Не знайдено жодних пристроїв, що починаються з '{{device}}'.", + "add": "Додати пристрій", + "assign-to-customer": "Призначити клієнту", + "assign-device-to-customer": "Призначити пристрій (ої) клієнту", + "assign-device-to-customer-text": "Виберіть пристрої, які слід призначити клієнту", + "make-public": "Зробити пристрій публічним", + "make-private": "Зробити пристрій приватним", + "no-devices-text": "Не знайдено жодного пристрою", + "assign-to-customer-text": "Виберіть клієнта для призначення пристрою (їв)", + "device-details": "Деталі пристрою", + "add-device-text": "Додати новий пристрій", + "credentials": "Авторизаційні дані", + "manage-credentials": "Керування авторизаційними даними", + "delete": "Видалити пристрій", + "assign-devices": "Призначити пристрої", + "assign-devices-text": "Призначити { count, plural, 1 {1 пристрій} other {# пристрої} } клієнту", + "delete-devices": "Видалити пристрої", + "unassign-from-customer": "Позбавити клієнта пристроїв", + "unassign-devices": "Позбавити пристроїв", + "unassign-devices-action-title": "Позбавити клієнта { count, plural, 1 {1 пристрою} other {# пристроїв} }", + "assign-new-device": "Призначити новий пристрій", + "make-public-device-title": "Ви впевнені, що хочете зробити пристрій '{{deviceName}}' публічним?", + "make-public-device-text": "Після підтвердження пристрій і всі його дані будуть публічними та доступними для інших.", + "make-private-device-title": "Ви впевнені, що хочете зробити пристрій '{{deviceName}}' приватним?", + "make-private-device-text": "Після підтвердження пристрій і всі його дані будуть приватними та недоступними для інших.", + "view-credentials": "Переглянути авторизаційні дані", + "delete-device-title": "Ви впевнені, що хочете видалити пристрій '{{deviceName}}'?", + "delete-device-text": "Будьте обережні, після підтвердження, пристрій і всі пов'язані з ним дані стануть недоступними.", + "delete-devices-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 пристрій} other {# пристрої} }?", + "delete-devices-action-title": "Видалити { count, plural, 1 {1 пристрій} other {# пристрої} }", + "delete-devices-text": "Будьте обережні, після підтвердження, всі вибрані пристрої будуть видалені, і всі пов'язані з ними дані стануть недоступними.", + "unassign-device-title": "Ви впевнені, що хочете позбавити пристрою '{{deviceName}}'?", + "unassign-device-text": "Після підтвердження, клієнт буде позбавлений пристрою.", + "unassign-device": "Позбавити пристою", + "unassign-devices-title": "Ви впевненені, що хочете позбавити { count, plural, 1 {1 пристрою} other {# пристроїв} }?", + "unassign-devices-text": "Після підтвердження, клієнт буде позбавлений пристрою і пристрій стане не доступним клієнту", + "device-credentials": "Авторизаційні дані прстрою", + "credentials-type": "Тип авторизаційних даних", + "access-token": "Маркер доступу", + "access-token-required": "Необхідно вказати маркер доступу.", + "access-token-invalid": "Маркер доступу має бути від 1 до 20 символів.", + "rsa-key": "Публічний ключ RSA", + "rsa-key-required": "Необхідно вказати публічний ключ RSA.", + "secret": "Секрет", + "secret-required": "Необхідно вказати секрет.", + "device-type": "Тип пристрою", + "device-type-required": "Необхідно вказати тип пристрою.", + "select-device-type": "Виберіть тип пристрою", + "enter-device-type": "Введіть тип пристрою", + "any-device": "Будь-який пристрій", + "no-device-types-matching": "Не знайдено типів пристроїв, які відповідають '{{entitySubtype}}'.", + "device-type-list-empty": "Не вибрано типів пристроїв.", + "device-types": "Типи пристрою", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "events": "Події", + "details": "Деталі", + "copyId": "Копіювати Id пристрою", + "copyAccessToken": "Копіювати маркер доступу", + "idCopiedMessage": "Id пристрою скопійовано в буфер обміну", + "accessTokenCopiedMessage": "Маркер доступу до пристрою скопійовано в буфер обміну", + "assignedToCustomer": "Призначений клієнту", + "unable-delete-device-alias-title": "Неможливо видалити псевдонім пристрою", + "unable-delete-device-alias-text": "Псевдонім пристрою '{{deviceAlias}}' не може бути видалений, оскільки він використовується наступним(и) віджетом(ами):
    {{widgetsList}}", + "is-gateway": "Шлюз", + "public": "Публічно", + "device-public": "Пристрій є публічним", + "select-device": "Виберіть пристрій", + "selected-devices": "{ count, plural, 1 {1 пристрій} other {# пристрої} } вибрано", + "search": "Шукати пристрої", + "select-group-to-add": "Виберіть цільову групу, щоб додати вибраний пристрій", + "select-group-to-move": "Виберіть цільову групу для переміщення вибраних пристроїв", + "remove-devices-from-group": "Ви впевнені, що хочете видалити { count, plural, 1 {1 пристрій} other {# пристрої} } з групи '{entityGroup}'?", + "group": "Група пристроїв", + "list-of-groups": "{ count, plural, 1 {Одна група пристроїв} other {Список # груп пристроїв} }", + "group-name-starts-with": "Групи пристроїв, імена яких починаються з '{{prefix}}'" + }, + "dialog": { + "close": "Закрити діалогове вікно" + }, + "error": { + "unable-to-connect": "Неможливо підключитися до сервера! Перевірте підключення до Інтернету.", + "unhandled-error-code": "Неопрацьований помилковий код: {{errorCode}}", + "unknown-error": "Невідома помилка" + }, + "entity": { + "entity": "Сутність", + "entities": "Сутності", + "aliases": "Псевдоніми сутності", + "entity-alias": "Псевдонім сутності", + "unable-delete-entity-alias-title": "Неможливо видалити псевдонім сутності", + "unable-delete-entity-alias-text": "Псевдонім сутності'{{entityAlias}}' неможливо видалити, так як це використовується наступним віджетом(s):
    {{widgetsList}}", + "duplicate-alias-error": "Знайдено повторюваний псевдонім '{{alias}}'.
    Псевдоніми сутностей повинні бути унікальними на панелі візуалізації.", + "missing-entity-filter-error": "Відсутній фільтр для псевдоніма '{{alias}}'.", + "configure-alias": "Налаштувати '{{alias}}' псевдонім", + "alias": "Псевдонім", + "alias-required": "Необхідно вказати псевдонім сутності.", + "remove-alias": "Видалити псевдонім сутності", + "add-alias": "Додати псевдонім сутності", + "entity-list": "Список сутності", + "entity-type": "Тип сутності", + "entity-types": "Типи сутності", + "entity-type-list": "Список типу сутності", + "any-entity": "Будь-яка сутність", + "enter-entity-type": "Введіть тип сутності", + "no-entities-matching": "Не знайдено жожних сутностей, що відповідають '{{entity}}' що відповідають.", + "no-entity-types-matching": "Не знайдено жожних типів сутностей, що відповідають '{{entityType}}'.", + "name-starts-with": "Назва починається з", + "use-entity-name-filter": "Використовуйте фільтр", + "entity-list-empty": "Не вибрано жодних сутностей.", + "entity-type-list-empty": "Не вибрано жодних типів сутностей.", + "entity-name-filter-required": "Необхідно задати фільтр по імені.", + "entity-name-filter-no-entity-matched": "Не знайдено жодних сутностей, що починаються з '{{entity}}'.", + "all-subtypes": "Всі", + "select-entities": "Виберіть сутність", + "no-aliases-found": "Псевдонімів не знайдено.", + "no-alias-matching": "'{{alias}}' не знайдено.", + "create-new-alias": "Створити новий псевдонім!", + "key": "Ключ", + "key-name": "Ім'я ключа", + "no-keys-found": "No keys found.", + "no-key-matching": "'{{key}}' не знайдено.", + "create-new-key": "Створити новий ключ!", + "type": "Тип", + "type-required": "Необхідно задати тип сутності.", + "type-device": "Пристрій", + "type-devices": "Пристрої", + "list-of-devices": "{ count, plural, 1 {Один пристрій} other {Список # пристроїв} }", + "device-name-starts-with": "Пристрої, імена яких починаються з '{{prefix}}'", + "type-asset": "Актив", + "type-assets": "Активи", + "list-of-assets": "{ count, plural, 1 {Один актив} other {Список # активів} }", + "asset-name-starts-with": "Активи, імена яких починаються з '{{prefix}}'", + "type-entity-view": "Перегляд сутності", + "type-entity-views": "Перегляди сутності", + "list-of-entity-views": "{ count, plural, 1 {Один перегляд сутності} other {Список # переглядів сутності} }", + "entity-view-name-starts-with": "Перегляди сутностей, імена яких починаються з '{{prefix}}'", + "type-rule": "Правило", + "type-rules": "Правила", + "list-of-rules": "{ count, plural, 1 {Одне правило} other {Список # правил} }", + "rule-name-starts-with": "Правила, імена яких починаються з '{{prefix}}'", + "type-plugin": "Плагін", + "type-plugins": "Плагіни", + "list-of-plugins": "{ count, plural, 1 {Один плагін} other {Список # плагінів} }", + "plugin-name-starts-with": "Плагіни, імена яких починаються з '{{prefix}}'", + "type-tenant": "Власник", + "type-tenants": "Власники", + "list-of-tenants": "{ count, plural, 1 {Один власник} other {Список # власників} }", + "tenant-name-starts-with": "Власники, імена яких починаються з '{{prefix}}'", + "type-customer": "Клієнт", + "type-customers": "Клієнти", + "list-of-customers": "{ count, plural, 1 {Один клієнт} other {Список # клієнтів} }", + "customer-name-starts-with": "Клієнти, імена яких починаються з '{{prefix}}'", + "type-user": "Користувач", + "type-users": "Користувачі", + "list-of-users": "{ count, plural, 1 {Один користувач} other {Список # користувачів } }", + "user-name-starts-with": "Користувачі, імена яких починаються з '{{prefix}}'", + "type-dashboard": "Панель візуалізації", + "type-dashboards": "Панелі візуалізації", + "list-of-dashboards": "{ count, plural, 1 {Одна панель візуалізації} other {Список # панелей візуалізації} }", + "dashboard-name-starts-with": "Панелі візуалізації, імена яких починаються з '{{prefix}}'", + "type-alarm": "Сигнал тривоги", + "type-alarms": "Сигнали тривоги", + "list-of-alarms": "{ count, plural, 1 {Один сигнал тривоги} other {Список # сигналів тривоги} }", + "alarm-name-starts-with": "Сигнали тривоги, імена яких починаються '{{prefix}}'", + "type-rulechain": "Правило ланцюжка", + "type-rulechains": "Правило ланцюжків", + "list-of-rulechains": "{ count, plural, 1 {Одне правило ланцюжка} other {Список # правил ланцюжків} }", + "rulechain-name-starts-with": "Правило ланцюжків, імена яких починаються '{{prefix}}'", + "type-scheduler-event": "Scheduler event", + "type-scheduler-events": "Scheduler events", + "list-of-scheduler-events": "{ count, plural, 1 {One scheduler event} other {List of # scheduler events} }", + "scheduler-event-name-starts-with": "Scheduler events whose names start with '{{prefix}}'", + "type-blob-entity": "Blob entity", + "type-blob-entities": "Blob entities", + "list-of-blob-entities": "{ count, plural, 1 {One blob entity} other {List of # blob entities} }", + "blob-entity-name-starts-with": "Blob entities whose names start with '{{prefix}}'", + "type-rulenode": "Правило", + "type-rulenodes": "Правила", + "list-of-rulenodes": "{ count, plural, 1 {Одне правило} other {Список # правил} }", + "rulenode-name-starts-with": "Список правил, імена яких починаються '{{prefix}}'", + "type-current-customer": "Поточний клієнт", + "search": "Пошук сутностей", + "selected-entities": "{ count, plural, 1 {1 сутність} other {# сутності} } вибрано", + "entity-name": "Ім'я сутності", + "details": "Подробиці сутності", + "no-entities-prompt": "Сутності не знайдено", + "no-data": "Немає даних для відображення", + "columns-to-display": "Стовпці для відображення", + "type-entity-group": "Група сутностей", + "type-converter": "Перетворювач даних", + "type-converters": "Перетворювачі даних", + "list-of-converters": "{ count, plural, 1 {Однин перетворювач даних} other {Список # перетворювачів даних} }", + "converter-name-starts-with": "Перетворювачі даних, імена яких починаються з '{{prefix}}'", + "type-integration": "Інтеграція", + "type-integrations": "Інтеграції", + "list-of-integrations": "{ count, plural, 1 {Одна інтеграція} other {Список # інтеграцій} }", + "integration-name-starts-with": "Інтеграції, імена яких починаються з '{{prefix}}'" + }, + "entity-group": { + "entity-group": "Група сутності", + "details": "Деталі", + "columns": "Стовпці", + "add-column": "Додати стовпець", + "column-value": "Значення", + "column-value-required": "Необхідно вказати значення.", + "column-title": "Назва", + "default-sort-order": "Основний порядок сортування", + "default-sort-order-required": "Необхідно вказати основний порядок сортування.", + "hide-in-mobile-view": "Мобільний приховано", + "use-cell-style-function": "Використовувати функцію стилю комірки", + "use-cell-content-function": "Use cell content function", + "edit-column": "Редагувати стовпець", + "column-details": "Деталі стовпця", + "actions": "Дії", + "settings": "Налаштування", + "delete": "Видалити групу сутностей", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "add": "Додати групу сутностей", + "add-entity-group-text": "Додати нову групу сутностей", + "no-entity-groups-text": "Не знайдено жодних груп сутності", + "entity-group-details": "Деталі групи сутності", + "delete-entity-groups": "Видалити групи сутностей", + "delete-entity-group-title": "Ви впевнені, що хочете видалити групу сутності '{{entityGroupName}}'?", + "delete-entity-group-text": "Будьте обережні, після підтвердження, група сутностей і всі пов'язані з нею дані стануть недоступними.", + "delete-entity-groups-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 групу сутності} other {# групи сутностей} }?", + "delete-entity-groups-action-title": "Видалити { count, plural, 1 {1 групу сутності} other {# групи сутностей} }", + "delete-entity-groups-text": "Будьте обережні, після підтвердження, всі виділені групи сутностей і пов'язані з ними дані, стануть недоступними.", + "device-groups": "Групи пристроїв", + "asset-groups": "Групи активів", + "customer-groups": "Групи клієнтів", + "device-group": "Група пристроїв", + "asset-group": "Група активів", + "customer-group": "Група клієнтів", + "fetch-more": "Отримати більше", + "column-type": { + "column-type": "Тип стовпця", + "client-attribute": "Атрибут клієнта", + "shared-attribute": "Спільний атрибут", + "server-attribute": "Атрибут сервера", + "timeseries": "Телеметрія", + "entity-field": "Поле сутності" + }, + "column-type-required": "Необхідно вказати тип стовпця.", + "entity-field": { + "created-time": "Створений час", + "name": "Ім'я", + "type": "Тип", + "assigned_customer": "Призначений клієнт", + "authority": "Авторитет", + "first_name": "Ім'я", + "last_name": "Прізвище", + "email": "Електронна пошта", + "title": "Назва", + "country": "Країна", + "state": "Штат", + "city": "Місто", + "address": "Адреса", + "address2": "Адреса 2", + "zip": "Zip", + "phone": "Телефон" + }, + "sort-order": { + "asc": "У порядку зростання", + "desc": "У порядку зменшення", + "none": "Не має" + }, + "details-mode": { + "on-row-click": "Клацніть на рядок", + "on-action-button-click": "Клацніть на кнопку детелі", + "disabled": "Вимкнено" + }, + "add-to-group": "Додати до групи", + "move-to-group": "Перемістити до групи", + "select-entity-group": "Виберати групу сутностей", + "no-entity-groups-matching": "Не знайдено жодних груп сутностей, що відповідають '{{entityGroup}}'.", + "target-entity-group-required": "Необхідно вказати цільову групу сутності.", + "remove-from-group": "Видалити з групи", + "group-table-title": "Group table title", + "enable-search": "Увімкнути пошук сутностей", + "enable-add": "Увімкнути додавання сутностей", + "enable-delete": "Увімкнути видалення сутностей", + "enable-selection": "Увімкнути вибір сутностей", + "enable-group-transfer": "Увімкнути дії групового перенесення", + "display-pagination": "Відображення сторінок", + "default-page-size": "Розмір сторінки за замовчуванням", + "enable-assignment-actions": "Увімкнути дії призначення", + "enable-credentials-management": "Увімкнути керування авторизаційними даними", + "enable-users-management": "Увімкнути керування користувачами", + "enable-assets-management": "Увімкнути керування активами", + "enable-devices-management": "Увімкнути керування пристроями", + "enable-dashboards-management": "Увімкнути керування панелями візуалізації", + "open-details-on": "Відкрити деталі сутності", + "select-existing": "Виберіть існуючу групу сутностей", + "create-new": "Створити нову групу сутностей", + "new-entity-group-name": "Нове ім'я групи сутностей", + "entity-group-list": "Список групи сутностей", + "entity-group-list-empty": "Не вибрано жодної групи сутностей.", + "name-starts-with": "Назва групи сутностей починається з", + "entity-group-name-filter-required": "Необхідно задати назву групи сутностей." + }, + "entity-view": { + "entity-view": "Перегляд сутності", + "entity-view-required": "Необхідно вказати перегляд сутності.", + "entity-views": "Перегляди сутностей", + "management": "Керування переглядом сутностей", + "view-entity-views": "Переглянути перегляд сутностей", + "entity-view-alias": "Псевдонім перегляду сутності", + "aliases": "Псевдоніми перегляду сутності", + "no-alias-matching": "Псевдонім'{{alias}}' не знайдено.", + "no-aliases-found": "Псевдоніми не знайдено.", + "no-key-matching": "'Ключ {{key}}' не знайдено.", + "no-keys-found": "Ключі не знайдено.", + "create-new-alias": "Створити новий!", + "create-new-key": "Створити новий!", + "duplicate-alias-error": "Псевдонім з такою назвою вже існує '{{alias}}'.
    Псевдоніми перегляду повинні бути унікальними на панелі візуалізації.", + "configure-alias": "Налаштувати псевдонім '{{alias}}'", + "no-entity-views-matching": "Сутності, які відповідають '{{entity}}' не знайдені.", + "alias": "Псевдонім", + "alias-required": "Необхідно вказати псевдонім перегляду сутності.", + "remove-alias": "Видалити псевдонім перегляду сутності", + "add-alias": "Додати псевдонім перегляду сутності", + "name-starts-with": "Ім'я перегляду сутності починається з", + "entity-view-list": "Список перегляду сутності", + "use-entity-view-name-filter": "Використати фільтр", + "entity-view-list-empty": "Не вибрано жодного перегляду сутності.", + "entity-view-name-filter-required": "Необхідно вказвти фільтр назв представлення сутності.", + "entity-view-name-filter-no-entity-view-matched": "Перегляди сутностей, назви яких починаються з '{{entityView}}' не знайдено.", + "add": "Додати перегляд сутності", + "assign-to-customer": "Призначити клієнту", + "assign-entity-view-to-customer": "Призначити перегляд(и) сутності(ей) клієнту", + "assign-entity-view-to-customer-text": "Будь ласка, виберіть перегляд сутності для призначення клієнту", + "no-entity-views-text": "Перегляду сутності не знайдено", + "assign-to-customer-text": "Будь ласка, виберіть клієнта, для призначиення перегляду(ів) сутності(ей)", + "entity-view-details": "Деталі перегляду сутності", + "add-entity-view-text": "Додати новий перегляд сутносі", + "delete": "Видалити перегляд сутності", + "assign-entity-views": "Призначити перегляд сутності", + "assign-entity-views-text": "Призначити { count, plural, 1 {1 перегляд сутності} other {# перегляди сутностей } } клієнту", + "delete-entity-views": "Видалити перегляди сутностей", + "unassign-from-customer": "Позбавити клієнта", + "unassign-entity-views": "Позбавити переглядів сутностей", + "unassign-entity-views-action-title": "Позбавити { count, plural, 1 {1 перегляду сутності} other {# переглядів сутностей} } клієнта", + "assign-new-entity-view": "Призначити новий перегляд сутності", + "delete-entity-view-title": "Ви впевнені, що хочете видалити перегляд сутності'{{entityViewName}}'?", + "delete-entity-view-text": "Будьте обережні, після підтвердження, перегляд сутності та всі пов'язані з нею дані стануть недоступними.", + "delete-entity-views-title": "Ви впевнені, що хочете видалити перегляд сутності { count, plural, 1 {1 перегляд сутності } other {# перегляди сутностей } }?", + "delete-entity-views-action-title": "Видалити { count, plural, 1 {1 перегляд сутності } other {# перегляди сутностей } }", + "delete-entity-views-text": "Будьте обережні, після підтвердження, всі виділені перегляди сутностей та дні, пов'язані з ними стануть недоступними.", + "unassign-entity-view-title": "Ви впевнені, що хочете позбавити перегляду сутності '{{entityViewName}}'?", + "unassign-entity-view-text": "Після підтвердження клієнт буде позбавлений перегляду сутності. Дані перегляду сутності не будуть доступні клієнту.", + "unassign-entity-view": "Позбавити перегляду сутності", + "unassign-entity-views-title": "Ви впевнені, що хочете позбавити { count, plural, 1 {1 перегляду сутності} other {# переглядів сутностей} }?", + "unassign-entity-views-text": "Після підтвердження, клієнта буде позбавлено всіх виділених переглядів сутності. Дані переглядів сутностей не будуть доступні клієнту .", + "entity-view-type": "Тип перегляду сутності", + "entity-view-type-required": "Необхідно вказати тип перегляду сутності.", + "select-entity-view-type": "Виберіть тип перегляду сутності", + "enter-entity-view-type": "Введіть тип перегляду сутності", + "any-entity-view": "Будь-який перегляд сутності", + "no-entity-view-types-matching": "Не знайдено жодних типів перегляду сутності, що відповідають '{{entitySubtype}}'.", + "entity-view-type-list-empty": "Не вибрано тип перегляду сутності.", + "entity-view-types": "Типи перегляду сутності", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "events": "Події", + "details": "Деталі", + "copyId": "Скопіювати Id перегляду сутності", + "assignedToCustomer": "Призначений клієнту", + "unable-entity-view-device-alias-title": "Неможливо видалити псевдонім перегляду сутності", + "unable-entity-view-device-alias-text": "Не вдалося видалити псевдонім пристрою'{{entityViewAlias}}', так як він використовується наступним(и) віджетом(ами):
    {{widgetsList}}", + "select-entity-view": "Виберати перегляд сутності", + "make-public": "Зробити перегляд сутності публічним", + "start-date": "Дата початку", + "start-ts": "Час початку", + "end-date": "Дата закінчення", + "end-ts": "Час завершення", + "date-limits": "Обмеження дати", + "client-attributes": "Атрибути клієнта", + "shared-attributes": "Спільні атрибути", + "server-attributes": "Атрибути сервера", + "timeseries": "Телеметрія", + "client-attributes-placeholder": "Атрибути клієнта", + "shared-attributes-placeholder": "Спільні атрибути", + "server-attributes-placeholder": "Атрибути сервера", + "timeseries-placeholder": "Телеметрія", + "target-entity": "Цільова сутність", + "attributes-propagation": "Поширення атрибутів", + "attributes-propagation-hint": "Перегляд сутностей автоматично копіюватиме вказані атрибути з цільової сутності кожного разу, коли ви зберігаєте або оновлюєте ці перегляди. В цілях продуктивності, атрибути цільової сутності не поширюються на представлення сутності на кожній зміні їх атрибутів. Можна ввімкнути автоматичне поширення, налаштувавши у вашому ланцюжку правило \"copy to view\" і пов'язуючи його з повідомленнями типу \"Post attributes\" і \"Attributes Updated\"..", + "timeseries-data": "Дані телеметрії", + "timeseries-data-hint": "Налаштуйте ключі даних телеметрії цільової сутності, які будуть доступні перегляду сутності. Ці дані доступні лише для читання." + }, + "event": { + "events": "Події", + "event-type": "Тип події", + "type-error": "Помилка", + "type-lc-event": "Подія життєвого циклу", + "type-stats": "Статистика", + "type-debug-converter": "Налагоджувати", + "type-debug-integration": "Налагоджувати", + "type-debug-rule-node": "Налагоджувати", + "type-debug-rule-chain": "Налагоджувати", + "no-events-prompt": "Не знайдено жодних подій", + "error": "Помилка", + "alarm": "Сигнал тривоги", + "event-time": "Час події", + "server": "Сервер", + "body": "Тіло", + "method": "Метод", + "type": "Тип", + "in": "In", + "out": "Out", + "metadata": "Метадані", + "message": "Повідомлення", + "entity": "Сутність", + "message-id": "Id повідомлення", + "message-type": "Тип повідомлення", + "data-type": "Тип даних", + "relation-type": "Тип зв'язку", + "data": "Дані", + "event": "Подія", + "status": "Статус", + "success": "Успіх", + "failed": "Невдача", + "messages-processed": "Повідомлення опрацьовані", + "errors-occurred": "Виникли помилки" + }, + "extension": { + "extensions": "Розширення", + "selected-extensions": " вибрано { count, plural, 1 {1 розширення} other {# розширення} }", + "type": "Тип", + "key": "Ключ", + "value": "Значення", + "id": "Id", + "extension-id": "Id розширення", + "extension-type": "Тип розширення", + "transformer-json": "JSON *", + "unique-id-required": "Таке Id розширення вже існує.", + "delete": "Видалити розширення", + "add": "Додати розширення", + "edit": "Редагувати розширення", + "delete-extension-title": "Ви дійсно бажаєте видалити розширення '{{extensionId}}'?", + "delete-extension-text": "Будьте обережні, після підтвердження, розширення та всі пов'язані з ним дані стануть недоступними.", + "delete-extensions-title": "Ви дійсно бажаєте видалити { count, plural, 1 {1 розширення} other {# розширення} }?", + "delete-extensions-text": "Будьте обережні, після підтвердження, всі вибрані розширення будуть видалені.", + "converters": "Перетворювачі", + "converter-id": "Id перетворювача", + "configuration": "Конфігурація", + "converter-configurations": "Конфігурації перетворювача", + "token": "Маркер безпеки", + "add-converter": "Додати конвертер", + "add-config": "Додати конфігурацію конвертера", + "device-name-expression": "Маска імені пристрою", + "device-type-expression": "Маска типу пристрою", + "custom": "Користувач", + "to-double": "Подвоїти", + "transformer": "Трансформатор", + "json-required": "Необхідно вказати json трансформатора.", + "json-parse": "Неможливо проаналізувати json трансформатора.", + "attributes": "Атрибути", + "add-attribute": "Додати атрибут", + "add-map": "Додати елемент відображення", + "timeseries": "Телеметрія", + "add-timeseries": "Додати параметри телеметрії", + "field-required": "Field is required", + "brokers": "Брокери", + "add-broker": "Додати брокера", + "host": "Хост", + "port": "Порт", + "port-range": "Значення порту має бути в діапазоні від 1 до 65535.", + "ssl": "Ssl", + "credentials": "Авторизаційні дані", + "username": "Ім'я користувача", + "password": "Пароль", + "retry-interval": "Інтервал повтору в мілісекундах", + "anonymous": "Анонімний", + "basic": "Основний", + "pem": "PEM", + "ca-cert": "Файл CA сертифіката *", + "private-key": "Файл приватного ключа *", + "cert": "Файл сертифіката *", + "no-file": "Не вибрано жодного файлу.", + "drop-file": "Перетягніть файл, або клацніть, щоб вибрати файл для завантаження.", + "mapping": "Зіставлення", + "topic-filter": "Фільтр тем", + "converter-type": "Тип конвертера", + "converter-json": "Json", + "json-name-expression": " Json вираз для назви пристрою", + "topic-name-expression": "Вираз для назви пристрою в назві теми", + "json-type-expression": "Json вираз для типу пристрою", + "topic-type-expression": "Вираз для типу пристрою в назві теми", + "attribute-key-expression": "Вираз для ключа атрибута", + "attr-json-key-expression": " Json вираз для ключа атрибута", + "attr-topic-key-expression": "Вираз для ключа атрибута в назві теми", + "request-id-expression": "Вираз для id запиту", + "request-id-json-expression": "Json вираз для id запиту", + "request-id-topic-expression": "Вираз для id запиту в назві теми", + "response-topic-expression": "Вираз для теми відповідей", + "value-expression": "Вираз для значення", + "topic": "Тема", + "timeout": "Час очікування в мілісекундах", + "converter-json-required": "Необхідно вказати json конвертер.", + "converter-json-parse": "Неможливо проаналізувати json конвертера.", + "filter-expression": "Вираз для фільтра", + "connect-requests": "Запити на підключення", + "add-connect-request": "Додати запит на підключення", + "disconnect-requests": "Відключення запитів", + "add-disconnect-request": "Додати запит на відключення", + "attribute-requests": "Запити атрибутів", + "add-attribute-request": "Додати запит атрибута", + "attribute-updates": "Оновлення атрибутів", + "add-attribute-update": "Додати оновлення атрибутів", + "server-side-rpc": "Серверна сторона RPC", + "add-server-side-rpc-request": "Додати RPC-запит на стороні сервера", + "device-name-filter": "Фільтр назви пристрою", + "attribute-filter": "Фільтр атрибутів", + "method-filter": "Фільтр методів", + "request-topic-expression": "Вираз для теми запитів", + "response-timeout": "Час очікування відповіді в мілісекундах", + "topic-expression": "Вираз для назви теми", + "client-scope": "Обсяг клієнта", + "add-device": "Додати пристрій", + "opc-server": "Сервери", + "opc-add-server": "Додати сервер", + "opc-add-server-prompt": "Будь ласка, додайте сервер", + "opc-application-name": "Назва програми", + "opc-application-uri": "URI програми", + "opc-scan-period-in-seconds": "Період сканування в секундах", + "opc-security": "Безпека", + "opc-identity": "Ідентифікація", + "opc-keystore": "Сховище ключів", + "opc-type": "Тип", + "opc-keystore-type": "Тип", + "opc-keystore-location": "Розташування *", + "opc-keystore-password": "Пароль", + "opc-keystore-alias": "Псевдонім", + "opc-keystore-key-password": "Пароль для ключа", + "opc-device-node-pattern": "Патерн OPC вузла пристрою", + "opc-device-name-pattern": "Патерн назви пристрою", + "modbus-server": "Сервери/ведені пристрої", + "modbus-add-server": "Додати сервер/ведений пристрій", + "modbus-add-server-prompt": "Будь ласка, додайте сервер/ведений пристрій", + "modbus-transport": "Транспорт", + "modbus-port-name": "Ім'я послідовного порту", + "modbus-encoding": "Кодування", + "modbus-parity": "Паритет", + "modbus-baudrate": "Швидкість передачі даних", + "modbus-databits": "Біти даних", + "modbus-stopbits": "Стоп-біти", + "modbus-databits-range": "Біти даних повинні знаходитися в діапазоні від 7 до 8.", + "modbus-stopbits-range": "Стоп-біти повинні знаходитися в діапазоні від 1 до 2.", + "modbus-unit-id": "Unit ID", + "modbus-unit-id-range": "Unit ID should be in a range from 1 to 247.", + "modbus-device-name": "Ім'я пристрою", + "modbus-poll-period": "Період опитування (мс)", + "modbus-attributes-poll-period": "Період опитування атрибутів (мс)", + "modbus-timeseries-poll-period": "Період опитування телеметрії (мс)", + "modbus-poll-period-range": "Період опитування повинен бути більше 0.", + "modbus-tag": "Тег", + "modbus-function": "Modbus функція", + "modbus-register-address": "Адреса регістру ", + "modbus-register-address-range": "Адреса регістру повинна бути в діапазоні від 0 до 65535.", + "modbus-register-bit-index": "Номер бітів", + "modbus-register-bit-index-range": "Номер бітів повинен знаходитися в діапазоні від 0 до 15.", + "modbus-register-count": "Рахунок регістру", + "modbus-register-count-range": "Рахунок регістру повинен бути більше 0.", + "modbus-byte-order": "Порядок байтів", + + "sync": { + "status": "Статус", + "sync": "Синхронізований", + "not-sync": "Не синхронізований", + "last-sync-time": "Час останньої синхронізації", + "not-available": "Недоступний" + }, + + "export-extensions-configuration": "Експортувати конфігурацію розширень", + "import-extensions-configuration": "Імпортувати конфігурацію розширень", + "import-extensions": "Імпортувати розширення", + "import-extension": "Імпортувати розширення", + "export-extension": "Експортувати розширення", + "file": "Файл розширень", + "invalid-file-error": "Не правильний формат файла" + }, + "fullscreen": { + "expand": "Відкрити у повноекранному режимі", + "exit": "Вийти з повноекранного режиму", + "toggle": "Перемкнути повноекранний режим", + "fullscreen": "Повноекранний режим" + }, + "function": { + "function": "Функція" + }, + "grid": { + "delete-item-title": "Ви впенені, що хочете видалити цей елемент?", + "delete-item-text": "Будьте обережні, після підтвердження, цей елемент і всі пов'язані з ним дані, стануть недоступними.", + "delete-items-title": "Ви впенені, що хочете видалити { count, plural, 1 {1 елемент} other {# елементи} }?", + "delete-items-action-title": "Видалити{ count, plural, 1 {1 елемент} other {# елементи} }", + "delete-items-text": "Будьте обережні, після підтвердження, всі виділені елементи і пов'язані з ними дані, стануть недоступними.", + "add-item-text": "Додати новий елемент", + "no-items-text": "Не знайдено жодного елемента", + "item-details": "Деталі елемента", + "delete-item": "Видалити елементи", + "scroll-to-top": "Перейти угору" + }, + "help": { + "goto-help-page": "Перейти на сторінку довідки" + }, + "home": { + "home": "Домашня сторінка", + "profile": "Профіль", + "logout": "Вийти", + "menu": "Меню", + "avatar": "Аватар", + "open-user-menu": "Відкрити меню користувача" + }, + "import": { + "no-file": "Не вибрано жодного файлу", + "drop-file": "Перетягніть JSON файл, або клацніть, щоб вибрати файл для завантаження.", + "drop-csv-file": "Перетягніть CSV файл, або клацніть, щоб вибрати файл для завантаження." + }, + "integration": { + "integration": "Інтеграція", + "integrations": "Інтеграції", + "select-integration": "Виберіть інтеграцію", + "no-integrations-matching": "Не знайдено жодних інтеграцій, які відповідають '{{entity}}'.", + "integration-required": "Необхідно вказати інтеграцію", + "delete": "Видалити інтеграцію", + "management": "Управління інтеграціями", + "add-integration-text": "Додати нову інтеграцію", + "no-integrations-text": "Не знайдено жодної інтеграції", + "selected-integrations": "{ count, plural, 1 {1 інтеграція} other {# інтеграції} } вибрано", + "delete-integration-title": "Ви впевнені, що хочете видалити інтеграцію '{{integrationName}}'?", + "delete-integration-text": "Будьте обережні, після підтвердження інтеграція та всі пов'язані з нею дані стануть недоступними.", + "delete-integrations-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 інтеграцію} other {# інтеграції} }?", + "delete-integrations-action-title": "Видалити { count, plural, 1 {1 інтеграцію} other {# інтеграції} }", + "delete-integrations-text": "Будьте обережні, після підтвердження всі вибрані інтеграції будуть видалені, і всі пов'язані з ними дані стануть недоступними.", + "events": "Події", + "add": "Додати інтеграцію", + "integration-details": "Деталі інтеграції", + "details": "Деталі", + "copyId": "Копіювати Id інтеграції", + "idCopiedMessage": "Id інтеграції скопійовано в буфер обміну", + "debug-mode": "Режим налагодження", + "enable-security": "Увімкнути безпеку", + "headers-filter": "Заголовки фільтра", + "header": "Заголовок", + "no-headers-filter": "Немає фільтрів заголовків", + "downlink-url": "Downlink URL", + "application-uri": "URI програми", + "as-id": "AS ID", + "as-id-required": "Необхідно вказати AS ID.", + "as-key": "AS ключ", + "as-key-required": "Необхідно вказати AS ключ.", + "max-time-diff-in-seconds": "Максимальна різниця в часі (секунди)", + "max-time-diff-in-seconds-required": "Необхідно вказати максимальну різницю в часі.", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "base-url": "Базова URL-адреса", + "base-url-required": "Необхідно вказати базову URL-адресу", + "security-key": "Ключ захисту", + "http-endpoint": "URL кінцевої точки HTTP", + "copy-http-endpoint-url": "Скопіювати URL-адресу кінцевої точки HTTP", + "http-endpoint-url-copied-message": "URL кінцевої точки HTTP скопійовано в буфер обміну", + "host": "Хост", + "host-required": "Необхідно вказати хост.", + "host-type": "Тип хоста", + "host-type-required": "Необхідно вказати тип хоста.", + "custom-host": "Хост користувача", + "custom-host-required": "Необхідний спеціальний хост.", + "port": "Порт", + "port-required": "Необхідно вказати порт.", + "port-range": "Порт має бути в діапазоні від 1 до 65535.", + "connect-timeout": "Час очікування з'єднання (сек)", + "connect-timeout-required": " Необхідно вказати час з'єднання підключення.", + "connect-timeout-range": "Час очікування з'єднання має бути в діапазоні від 1 до 200.", + "client-id": "ID клієнта", + "clean-session": "Очистити сеанс", + "enable-ssl": "Увімкнути SSL", + "credentials": "Авторизаційні дані", + "credentials-type": "Тип авторизаційних даних", + "credentials-type-required": "Необхідно вказати тип авторизаційних даних.", + "username": "Ім'я користувача", + "username-required": "Необхідно вказати ім'я користувача.", + "password": "Пароль", + "password-required": "Необхідно вказати пароль.", + "ca-cert": "Файл сертифіката CA *", + "private-key": "Файл приватного ключа *", + "private-key-password": "Пароль приватного ключа", + "cert": "Файл сертифіката*", + "no-file": "Не вибрано жодного файлу.", + "drop-file": "Перетягніть файл, або клацніть, щоб вибрати файл для завантаження.", + "topic-filters": "Тематичні фільтри", + "remove-topic-filter": "Видалити фільтр тем", + "add-topic-filter": "Додати фільтр тем", + "add-topic-filter-prompt": "Будь ласка, додайте фільтр тем", + "topic": "Тема", + "mqtt-qos": "QoS", + "mqtt-qos-at-most-once": "Не більше одного разу", + "mqtt-qos-at-least-once": "Принаймні, один раз", + "mqtt-qos-exactly-once": "Точно один раз", + "downlink-topic-pattern": "Downlink topic pattern", + "downlink-topic-pattern-required": "Downlink topic pattern is required.", + "aws-iot-endpoint": "AWS IoT Endpoint", + "aws-iot-endpoint-required": "AWS IoT Endpoint is required.", + "aws-iot-credentials": "Авторизаційні дані AWS IoT", + "application-credentials": "Авторизаційні дані додатків", + "api-key": "API ключ", + "api-key-required": "Необхідно вказати API ключ.", + "auth-token": "Маркер аутентифікації", + "auth-token-required": "Необхідно вказати маркер аутентифікації.", + "region": "Регіон", + "region-required": "Необхідно вказати регіон.", + "application-id": "ID програми", + "application-id-required": "Необхідно вказати ID програми.", + "access-key": "Ключ доступу", + "access-key-required": "Необхідно вказати ключ доступу.", + "connection-parameters": "Параметри підключення", + "service-bus-namespace-name": "Service Bus Namespace Name", + "service-bus-namespace-name-required": "Необхідно вказати Service Bus Namespace Name is required.", + "event-hub-name": "Event Hub Name", + "event-hub-name-required": "Необхідно вказати Event Hub Name is required.", + "sas-key-name": "Назва ключа SAS", + "sas-key-name-required": "Необхідно вказати назву ключа SAS.", + "sas-key": "Ключ SAS", + "sas-key-required": "SAS Key is required.", + "iot-hub-name": "IoT Hub Name (required for downlink)", + "metadata": "Метадані", + "type": "Тип", + "type-required": "Необхідно вказати тип.", + "uplink-converter": "Конвертер передачі даних", + "uplink-converter-required": "Необхідно вказати конвертер передачі даних.", + "downlink-converter": "Downlink data converter", + "type-http": "HTTP", + "type-ocean-connect": "OceanConnect", + "type-sigfox": "SigFox", + "type-thingpark": "ThingPark", + "type-tmobile-iot-cdp": "T-Mobile – IoT CDP", + "type-mqtt": "MQTT", + "type-aws-iot": "AWS IoT", + "type-ibm-watson-iot": "IBM Watson IoT", + "type-ttn": "TheThingsNetwork", + "type-azure-event-hub": "Azure Event Hub", + "type-opc-ua": "OPC-UA", + "type-ffb": "FFB", + "opc-ua-application-name": "Назва програми", + "opc-ua-application-uri": "Application uri", + "opc-ua-scan-period-in-seconds": "Період сканування в секундах", + "opc-ua-scan-period-in-seconds-required": "Необхідно вказати період сканування в секундах", + "opc-ua-timeout": "Час очікування в мілісекундах", + "opc-ua-timeout-required": "Необхідно вказати час очікування в мілісекундах", + "opc-ua-security": "Безпека", + "opc-ua-security-required": "Необхідно задати безпеку", + "opc-ua-identity": "Ідентифікація", + "opc-ua-identity-required": "Необхідно вказати ідентифікацію", + "opc-ua-keystore": "Сховище ключів", + "add-opc-ua-keystore-prompt": "Будь ласка, додайте файл сховища ключів", + "opc-ua-keystore-required": "Необхідно вказати сховище ключів", + "opc-ua-type": "Тип", + "opc-ua-keystore-type":"Тип сховища ключів", + "opc-ua-keystore-type-required":"Необхідно вказати тип", + "opc-ua-keystore-location":"Розташування *", + "opc-ua-keystore-password":"Пароль", + "opc-ua-keystore-password-required":"Необхідно вказати пароль", + "opc-ua-keystore-alias":"Псевдонім", + "opc-ua-keystore-alias-required":"Необхідно вказати псевдонім", + "opc-ua-keystore-key-password":"Пароль ключа", + "opc-ua-keystore-key-password-required":"Необхідно вказати пароль ключа", + "opc-ua-mapping":"Зіставлення", + "add-opc-ua-mapping-prompt": "Будь ласка, додайте зіставлення", + "opc-ua-mapping-type":"Тип зіставлення", + "opc-ua-mapping-type-required":"Необхідно вказати тип зіставлення", + "opc-ua-device-node-pattern":"Шаблон вузла пристрою", + "opc-ua-device-node-pattern-required":"Необхідно вказати шаблон вузла пристрою", + "opc-ua-add-map": "Додати елемент зіставлення", + "subscription-tags": "Теги передплати Теги підписки", + "remove-subscription-tag": "Видалити тег підписки", + "add-subscription-tag": "Додати тег підписки", + "add-subscription-tag-prompt": "Будь ласка, додайте тег підписки", + "key": "Ключ", + "path": "Шлях", + "required": "Необхідно" + }, + "item": { + "selected": "Вибрані" + }, + "js-func": { + "no-return-error": "Функція повинна повертати значення!", + "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!", + "tidy": "Tidy" + }, + "key-val": { + "key": "Ключ", + "value": "Значення", + "remove-entry": "Видалити елемент", + "add-entry": "Додати елемент", + "no-data": "Елементи відсутні" + }, + "layout": { + "layout": "Макет", + "manage": "Керування макетами", + "settings": "Налаштування макета", + "color": "Колір", + "main": "Основний", + "right": "Правий", + "select": "Вибрати макет" + }, + "legend": { + "position": "Розташування легенди", + "show-max": "Показати максимальне значення", + "show-min": "Показати мінімальне значення ", + "show-avg": "Показати середнє значення", + "show-total": "Показати суму", + "settings": "Налаштування легенди", + "min": "мін", + "max": "макс", + "avg": "середнє", + "total": "Сума" + }, + "login": { + "login": "Вхід", + "request-password-reset": "Запит скидання пароля", + "reset-password": "Скинути пароль", + "create-password": "Створити пароль", + "passwords-mismatch-error": "Введені паролі повинні бути однаковими!", + "password-again": "Введіть пароль ще раз", + "sign-in": "Будь ласка, увійдіть в систему", + "username": "Ім'я користувача (ел. пошта)", + "remember-me": "Запам'ятати мене", + "forgot-password": "Забули пароль?", + "password-reset": "Скидання пароля", + "new-password": "Новий пароль", + "new-password-again": "Повторіть новий пароль", + "password-link-sent-message": "Посилання для скидання пароля було успішно надіслано!", + "email": "Електронна пошта" + }, + "position": { + "top": "Угорі", + "bottom": "Знизу", + "left": "Ліворуч", + "right": "Праворуч" + }, + "profile": { + "profile": "Профіль", + "change-password": "Змінити пароль", + "current-password": "Поточний пароль" + }, + "relation": { + "relations": "Відношення", + "direction": "Напрямок", + "search-direction": { + "FROM": "З", + "TO": "До" + }, + "direction-type": { + "FROM": "з", + "TO": "до" + }, + "from-relations": "Вихідні відношення", + "to-relations": "Вхідні відношення", + "selected-relations": "Вибрано { count, plural, 1 {1 відношення} other {# відношення} }", + "type": "Тип", + "to-entity-type": "До типу сутності", + "to-entity-name": "До імені сутності", + "from-entity-type": "Від типу сутності", + "from-entity-name": "Від імені сутності", + "to-entity": "До сутності", + "from-entity": "Від сутності", + "delete": "Видалити відношення", + "relation-type": "Тип відношення", + "relation-type-required": "Необхідно вказати тип відношення.", + "any-relation-type": "Будь-який тип", + "add": "Додати відношення", + "edit": "Редагувати відношення", + "delete-to-relation-title": "Ви впевнені, що хочете видалити відношення до сутності '{{entityName}}'?", + "delete-to-relation-text": "Будьте обережні, після підтвердження, сутність '{{entityName}}' не буде пов'язана з поточним об'єктом.", + "delete-to-relations-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 відношення} other {# відношення} }?", + "delete-to-relations-text": "Будьте обережні, після підтвердження, всі вибрані відношення стануть не пов'язані з поточною сутністю.", + "delete-from-relation-title": "Ви впевнені, що хочете видалити зв'язок, який йде від сутності '{{entityName}}'?", + "delete-from-relation-text": "Будьте обережні, після підтвердження поточна сутність буде відв'язана від сутності '{{entityName}}'.", + "delete-from-relations-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 відношення} other {# відношення} }?", + "delete-from-relations-text": "Будьте обережні, після підтвердження, всі вибрані відношення будуть видалені, а поточна сутність стане не зв'язаною з відповідними сутностями.", + "remove-relation-filter": "Видалити фільтр відношення", + "add-relation-filter": "Додати фільтр відношення", + "any-relation": "Будь-яке відношення", + "relation-filters": "Фільтри відношення", + "additional-info": "Додаткова інформація (JSON)", + "invalid-additional-info": "Не вдалося розібрати JSON з додатковою інформацією ." + }, + "rulechain": { + "rulechain": "Ланцюг правил", + "rulechains": "Ланцюги правил", + "root": "Основний", + "delete": "Видалити ланцюг правил", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "description": "Опис", + "add": "Додати ланцюг правил", + "set-root": "Зробити ланцюг правил основним", + "set-root-rulechain-title": "Ви впевнені, що хочете зробити ланцюг правил '{{ruleChainName}}' основним?", + "set-root-rulechain-text": "Після підтвердження ланцюг правил стане основним і буде обробляти всі вхідні транспортні повідомлення.", + "delete-rulechain-title": "Ви впевнені, що хочете видалити ланцюг правил '{{ruleChainName}}'?", + "delete-rulechain-text": "Будьте обережні, після підтвердження ланцюг правил і всі пов'язані з ним дані стануть недоступними.", + "delete-rulechains-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 ланцюг правил} other {# ланцюги правил} }?", + "delete-rulechains-action-title": "Видалити{ count, plural, 1 {1 ланцюг правил} other {# ланцюги правил} }", + "delete-rulechains-text": "Будьте обережні, після підтвердження, вибрані ланцюги правил і всі пов'язані з ними дані стануть недоступними.", + "add-rulechain-text": "Додати новий ланцюг правил", + "no-rulechains-text": "Ланцюг правил не знайдено", + "rulechain-details": "Деталі ланцюга правил", + "details": "Деталі", + "events": "Події", + "system": "Система", + "import": "Імпортувати ланцюг правил", + "export": "Експортувати ланцюг правил", + "export-failed-error": "Не вдалося експортувати ланцюг правил: {{error}}", + "create-new-rulechain": "Створити новий ланцюг правил", + "rulechain-file": "Файл ланцюга правил", + "invalid-rulechain-file-error": "Неможливо імпортувати ланцюг правил: недійсна структуру даних ланцюга правил.", + "copyId": "Копіювати Id ланцюга правил", + "idCopiedMessage": "Id ланцюга правил скопійовано в буфер обміну", + "select-rulechain": "Вибрати ланцюг правил", + "no-rulechains-matching": "Не знайдено жодних ланцюгів правил, які відповідають '{{entity}}'.", + "rulechain-required": "Необхідно вказати ланцюг правил", + "management": "Управління ланцюгами правил", + "debug-mode": "Режим налагодження" + }, + "rulenode": { + "details": "Деталі", + "events": "Події", + "search": "Пошук вузлів", + "open-node-library": "Відкрити бібліотеку вузлів", + "add": "Додати вузол правил", + "name": "Ім'я", + "name-required": "Необхідно вказати ім'я.", + "type": "Тип", + "description": "Опис", + "delete": "Видалити вузол правил", + "select-all-objects": "Вибрати усі вузли та з'єднання", + "deselect-all-objects": "Зняти виділення з усіх вузлів і з'єднань", + "delete-selected-objects": "Видалити вибрані вузли та з'єднання", + "delete-selected": "Видалити вибране", + "select-all": "Вибрати все", + "copy-selected": "Копіювати вибране", + "deselect-all": "Відмінити вибране", + "rulenode-details": "Деталі вузла правил", + "debug-mode": "Режим налагодження", + "configuration": "Конфігурація", + "link": "Посилання", + "link-details": "Деталі посилання про вузол правил", + "add-link": "Додати посилання", + "link-label": "Мітка посилання", + "link-label-required": "Необхідно вказати мітку посилання.", + "custom-link-label": "Мітка посилання користувача", + "custom-link-label-required": "Необхідно вказати мітку посилання користувача.", + "link-labels": "Мітки посилання", + "link-labels-required": "Необхідно вказати мітки посилання.", + "no-link-labels-found": "Не знайдено жодних міток посилання", + "no-link-label-matching": "Мітка'{{label}}' не знайдена.", + "create-new-link-label": "Створити нову!", + "type-filter": "Filter", + "type-filter-details": "Фільтрувати вхідні повідомлення з заданими умовами", + "type-enrichment": "Насичення", + "type-enrichment-details": "Додати додаткову інформацію до метаданих повідомлень", + "type-transformation": "Трансформація", + "type-transformation-details": "Змінити склад повідомлення та його метадані", + "type-action": "Дія", + "type-action-details": "Виконати задану дію", + "type-analytics": "Аналітика", + "type-analytics-details": "Виконати аналіз потокових або збережених даних", + "type-external": "Зовнішній", + "type-external-details": "Взаємодіє з зовнішньою системою", + "type-rule-chain": "Ланцюг правил", + "type-rule-chain-details": "Перенаправити вхідне повідомлення на вказаний ланцюг правил", + "type-input": "Вхід", + "type-input-details": "Логічний вхід ланцюга правил, перенаправляє вхідні повідомлення на наступний пов'язаний вузол правил", + "type-unknown": "Невідомий", + "type-unknown-details": "Невизначений вузол правил", + "directive-is-not-loaded": "Вказана директива конфігурації '{{directiveName}}' недоступна.", + "ui-resources-load-error": "Не вдалося завантажити UI ресурси.", + "invalid-target-rulechain": "Не вдається визначити цільовий ланцюг правил!", + "test-script-function": "Протестувати скрипт", + "message": "Повідомлення", + "message-type": "Тип повідомлення", + "select-message-type": "Вибрати тип повідомлення", + "message-type-required": "Необхідно вказати тип повідомлення", + "metadata": "Метадані", + "metadata-required": "Записи метаданих не можуть бути порожніми.", + "output": "Вихід", + "test": "Тест", + "help": "Допомога" + }, + "scheduler": { + "scheduler": "Планувальник", + "scheduler-event": "Scheduler event Планування події. Запланувати подію. Подія планувальника", + "select-scheduler-event": "Виберати подію", + "no-scheduler-events-matching": "Не знайдено жодних подій, які відповідають '{{entity}}'.", + "scheduler-event-required": "Необхвдно вказати заплановану подію", + "management": "Управління планувальником", + "scheduler-events": "Планування подій", + "add-scheduler-event": "Додати подію", + "search-scheduler-events": "Пошук події", + "created-time": "Час створення", + "name": "Ім'я", + "type": "Тип", + "assigned_customer": "Призначений клієнт", + "edit-scheduler-event": "Редагувати подію", + "delete-scheduler-event": "Видалити подію", + "no-scheduler-events": "Не знайдено жодних запланованих подій", + "selected-scheduler-events": "{ count, plural, 1 {1 запланована подія} other {# заплановані події} } вибрано", + "delete-scheduler-event-title": "Ви впевнені, що хочете видалити подію '{{schedulerEventName}}'?", + "delete-scheduler-event-text": "Будьте обережні, після підтвердження подія і всі пов'язані з нею дані стануть недоступними.", + "delete-scheduler-events-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 запланована подія} other {# заплановані події} }?", + "delete-scheduler-events-text": "Будьте обережні, після підтвердження всі вибрані події будуть видалені, і всі пов'язані з ними дані стануть недоступними.", + "create": "Створити подію планувальника", + "edit": "Змінити подію планувальника", + "name-required": "Необхідно задати ім'я", + "configuration": "Конфігурація", + "schedule": "Розклад", + "start": "Початок", + "date": "Дата", + "time": "Час", + "repeat": "Повтор", + "repeats": "Повтори", + "daily": "Щодня", + "weekly": "Щотижня", + "repeats-required": "Потрібно вказати повторення.", + "repeat-on": "Повторити на", + "ends-on": "Завершити на", + "sunday-label": "Нд", + "monday-label": "Пн", + "tuesday-label": "Вт", + "wednesday-label": "Ср", + "thursday-label": "Чт", + "friday-label": "Пт", + "saturday-label": "Сб", + "repeat-on-sunday": "Повторити у неділю", + "repeat-on-monday": "Повторити в понеділок", + "repeat-on-tuesday": "Повторити у вівторок", + "repeat-on-wednesday": "Повторити в середу", + "repeat-on-thursday": "Повторити в червер", + "repeat-on-friday": "Повторити в п'ятницю", + "repeat-on-saturday": "Повторити в суботу", + "event-type": "Тип події", + "select-event-type": "Вибрати тип події", + "event-type-required": "Необхідно вказати типи події.", + "list-mode": "Перегляд списку", + "calendar-mode": "Перегляд календаря", + "calendar-view-type": "Тип перегляду календаря", + "month": "Місяць", + "week": "Тиждень", + "day": "День", + "agenda-week": "Порядок тижня", + "agenda-day": "Порялок дня", + "list-year": "Список року", + "list-month": "Список місяця", + "list-week": "Список тижня", + "list-day": "Список дня", + "today": "Сьогодні", + "navigate-before": "Перейти до", + "navigate-next": "Перейти далі", + "starting-from": "Починаючи з", + "until": "до", + "on": "на", + "sunday": "Неділя", + "monday": "Понеділок", + "tuesday": "Вівторок", + "wednesday": "Середа", + "thursday": "Четвер", + "friday": "П'ятниця", + "saturday": "Субота", + "originator": "Засновник", + "single-entity": "Самостійна сутність", + "group-of-entities": "Група сутностей", + "single-device": "Один пристрій", + "group-of-devices": "Група пристроїв", + "message-body": "Тіло повідомлення", + "target": "Ціль", + "rpc-method": "Метод", + "rpc-method-required": "Необхідно вказати метод", + "rpc-params": "Парами. Парні.", + "select-dashboard-state": "Виберіть стан панелі візуалізації" + }, + "report": { + "report-config": "Конфігурація звіту", + "email-config": "Конфігурація електронної пошти", + "dashboard-state-param": "Значення параметра стану панелі візуалізації", + "base-url": "Базова URL-адреса", + "base-url-required": "Необхідно вказати базову URL-адресу.", + "use-dashboard-timewindow": "Використовуйте вікно часу на панелі інструментів", + "timewindow": "Вікно часу", + "name-pattern": "Шаблон імені звіту", + "name-pattern-required": "Необхідно задати шаблон назви звіту", + "type": "Report type", + "use-current-user-credentials": "Використовувати поточні авторизаційні дані користувача", + "customer-user-credentials": "Авторизаційні дані користувачів", + "customer-user-credentials-required": "Необхідно задати авторизаційні дані користувачів", + "generate-test-report": "Створити звіт про перевірку", + "send-email": "Відправити лист", + "from": "Від", + "from-required": "Необхідно вказати від кого.", + "to": "До", + "to-required": "Необхідно вказати до кого.", + "cc": "Cc", + "bcc": "Bcc", + "subject": "Тема", + "subject-required": "Необхідно вказати тему.", + "body": "Тіло", + "body-required": "Необідно вказати тіло." + }, + "blob-entity": { + "blob-entity": "Blob сутності", + "select-blob-entity": "Вибрати blob сутності", + "no-blob-entities-matching": "Не знайдено жодних сутностей blob, які відповідають '{{entity}}'.", + "blob-entity-required": "Необхідно вказати blob сутності", + "files": "Файли", + "search": "Пошук файлів", + "clear-search": "Очистити пошук", + "no-blob-entities-prompt": "Файлів не знайдено", + "report": "Звіт", + "created-time": "Створено час", + "name": "Ім'я", + "type": "Тип", + "assigned_customer": "Призначений клієнт", + "download-blob-entity": "Завантажити файл", + "delete-blob-entity": "Видалити файл", + "delete-blob-entity-title": "Ви впевнені, що хочете видалити файл '{{blobEntityName}}'?", + "delete-blob-entity-text": "Будьте обережні, після підствердження, дані файлу стануть недоступними." + }, + "timezone": { + "timezone": "Часовий пояс", + "select-timezone": "Виберати часовий пояс ", + "no-timezones-matching": "Не знайдено жодних часових поясів, які відповідають '{{timezone}}'.", + "timezone-required": "Необхідно вказати часовий пояс." + }, + "tenant": { + "tenant": "Власник", + "tenants": "Власники", + "management": "Управління власниками", + "add": "Додати власника", + "admins": "Адміністратори", + "manage-tenant-admins": "Керування адміністраторами власника", + "delete": "Видалити власника", + "add-tenant-text": "Додати нового власника", + "no-tenants-text": "Не знайдено жодного власника", + "tenant-details": "Подробиці про власника", + "delete-tenant-title": "Ви впевнені, що хочете видалити власника'{{tenantTitle}}'?", + "delete-tenant-text": "Будьте обережні, після підтвердження власник і всі пов'язані з ним дані стануть недоступними.", + "delete-tenants-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 власник} other {# власники} }?", + "delete-tenants-action-title": "Видалити { count, plural, 1 {1 власник} other {# власники} }", + "delete-tenants-text": "Будьте обережні, після підтвердження, усі вибрані власники будуть видалені, і всі пов'язані з ними дані стануть недоступними.", + "title": "Назва", + "title-required": "Необхідно вказати назву.", + "description": "Опис", + "details": "Деталі", + "events": "Події", + "copyId": "Крпіювати Id власника", + "idCopiedMessage": "Id власника скопійовано в буфер обміну", + "select-tenant": "Вибрати власника", + "no-tenants-matching": "Не знайдено жодних власників, які відповідають '{{entity}}'.", + "tenant-required": "Необхідно вказати власника", + "selected-tenants": "{ count, plural, 1 {1 власник} other {# власники} } вибрано", + "search": "Пошук власників" + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, 1 {1 секунда} other {# секунди} }", + "minutes-interval": "{ minutes, plural, 1 {1 хвилина} other {# хвилини} }", + "hours-interval": "{ hours, plural, 1 {1 година} other {# години} }", + "days-interval": "{ days, plural, 1 {1 день} other {# дні} }", + "days": "Дні", + "hours": "Години", + "minutes": "Хвилини", + "seconds": "Секунди", + "advanced": "Додатково" + }, + "timewindow": { + "days": "{ days, plural, 1 { день } other {# дні } }", + "hours": "{ hours, plural, 0 { годин } 1 {1 година } other {# години } }", + "minutes": "{ minutes, plural, 0 { хвилин } 1 {1 хвилина } other {# хвилини } }", + "seconds": "{ seconds, plural, 0 { секунд } 1 {1 секунда } other {# секунди } }", + "realtime": "Реальний час", + "history": "Історія", + "last-prefix": "Останнє", + "period": "з {{ startTime }} до {{ endTime }}", + "edit": "Редагувати вікно часу", + "date-range": "Проміжок часу", + "last": "Останнє", + "time-period": "Період часу" + }, + "user": { + "user": "Користувач", + "users": "Користувачі", + "customer-users": "Користувачі клієнта", + "tenant-admins": "Адміністратори власників", + "sys-admin": "Системний адміністратор", + "tenant-admin": "Адміністратор власника", + "customer": "Клієнт", + "anonymous": "Анонім", + "add": "Додати користувача", + "delete": "Видалити користувача", + "add-user-text": "Додати нового користувача", + "no-users-text": "Не знайдено жодного користувача", + "user-details": "Подробиці про користувача", + "delete-user-title": "Ви впевнені, що хочете видалити користувача'{{userEmail}}'?", + "delete-user-text": "Будьте обережні, після підтвердження, користувач і всі пов'язані з ним дані стануть недоступними.", + "delete-users-title": "Ви впевнені, що хочете видалити { count, plural, 1 {1 користувача} other {# користувачів} }?", + "delete-users-action-title": "Видалити { count, plural, 1 {1 користувача} other {# користувачів} }", + "delete-users-text": "Будьте обережні, після підтвердження, усіх виділених користувачів буде видалено, і всі пов'язані з ними дані стануть недоступними.", + "activation-email-sent-message": "Повідомлення про активацію успішно надіслано!", + "resend-activation": "Повторно надіслати активацію", + "email": "Електронна пошта", + "email-required": "Необхідно вказати електронну пошту.", + "invalid-email-format": "Недійсний формат електронної пошти.", + "first-name": "Ім'я", + "last-name": "Прізвище", + "description": "Опис", + "default-dashboard": "Стандартна панель візуалізації", + "always-fullscreen": "Завжди в повноекранному режимі", + "select-user": "Вибрати користувача", + "no-users-matching": "Не знайдено жодного користувача, що відповідає '{{entity}}'.", + "user-required": "Необхідно вказати користувача", + "activation-method": "Спосіб активації", + "display-activation-link": "Показати посилання для активації", + "send-activation-mail": "Надіслати активаційного листа", + "activation-link": "Активаційне посилання для користувача", + "activation-link-text": "Для активувації користувача, скористайтеся наступним activation link :", + "copy-activation-link": "Скопіювати активаційне посилання ", + "activation-link-copied-message": "Посилання на активацію користувача було скопійовано в буфер обміну", + "selected-users": "{ count, plural, 1 {1 користувач} other {# користувачі} } вибрано", + "search": "Пошук користувачів", + "details": "Подробиці", + "login-as-tenant-admin": "Увійти як адміністратор власника", + "login-as-customer-user": "Увійти як користувач клієнта" + }, + "value": { + "type": "Тип значення", + "string": "Рядок", + "string-value": "Значення рядка", + "integer": "Ціле", + "integer-value": "Ціле значення", + "invalid-integer-value": "Недійсне ціле значення", + "double": "Подвійне", + "double-value": "Подвійне значення", + "boolean": "Логічне", + "boolean-value": "Логічне значення", + "false": "Помилкове", + "true": "Правдиве", + "long": "Довге" + }, + "widget": { + "widget-library": "Бібліотека віджетів", + "widget-bundle": "Пакет віджетів", + "select-widgets-bundle": "Виберіть пакет віджетів", + "management": "Керування віджетами", + "editor": "Редактор віджетів", + "widget-type-not-found": "Помилка завантаження конфігурації віджетів.
    Можливо, пов'язаний з нею\n тип віджета було видалено.", + "widget-type-load-error": "Віджет не вдалося завантажити з наступних причин:", + "remove": "Видалити віджет", + "edit": "Відредагувати віджет", + "remove-widget-title": "Ви впевнені, що хочете видалити віджет '{{widgetTitle}}'?", + "remove-widget-text": "Після підтвердження віджет і всі пов'язані з ним дані стануть недоступними.", + "timeseries": "Телеметрія", + "search-data": "Пошук даних", + "no-data-found": "Даних не знайдено", + "latest-values": "Останні значення", + "rpc": "Керуючий віджет", + "alarm": "Віджет сигнала тривоги", + "static": "Статичний віджет", + "select-widget-type": "Вибрати тип віджета", + "missing-widget-title-error": "Необхідно вказати назву віджета!", + "widget-saved": "Віджет збережено", + "unable-to-save-widget-error": "Неможливо зберегти віджет! Віджет має помилки!", + "save": "Зберегти віджет", + "saveAs": "Зберегти віджет як", + "save-widget-type-as": "Зберегти тип віджета як", + "save-widget-type-as-text": "Введіть новий заголовок віджета та / або виберіть цільові віджети", + "toggle-fullscreen": "Перейти в повноекранний режим", + "run": "Запустити віджет", + "title": "Назва віджета", + "title-required": "Необхідно вказати назву віджета.", + "type": "Тип віджета", + "resources": "Ресурси", + "resource-url": "JavaScript/CSS URL", + "remove-resource": "Видалити ресурс", + "add-resource": "Додати ресурс", + "html": "HTML", + "tidy": "Форматувати", + "css": "CSS", + "settings-schema": "Схема налаштувань", + "datakey-settings-schema": "Схема налаштувань ключів даних", + "javascript": "Javascript", + "remove-widget-type-title": "Ви впевнені, що хочете видалити тип віджета '{{widgetName}}'?", + "remove-widget-type-text": "Будьте обережні, після підтвердження, тип віджета і всі пов'язані з ним дані стануть недоступними.", + "remove-widget-type": "Видалити тип віджета", + "add-widget-type": "Додати новий тип віджета", + "widget-type-load-failed-error": "Не вдалося завантажити тип віджета!", + "widget-template-load-failed-error": "Не вдалося завантажити шаблон віджета!", + "add": "Додати віджет", + "undo": "Скасувати зміни віджета", + "export": "Експртувати віджет", + "export-data": "Експортувати дані віджетів", + "export-to-csv": "Експортувати дані в CSV...", + "export-to-excel": "Експортувати дані в XLS..." + }, + "widget-action": { + "header-button": "Кнопка заголовка віджета", + "open-dashboard-state": "Перейти до нового стану панелі візуалізації", + "update-dashboard-state": "Оновити поточний стан панелі візуалізації", + "open-dashboard": "Перейти до іншої панелі візуалізації", + "custom": "Дії користувачів", + "target-dashboard-state": "Цільовий стан панелі візуалізації", + "target-dashboard-state-required": "Необхідно вказати цільовий стан панелі візуалізації", + "set-entity-from-widget": "Встановити сутність із віджета", + "target-dashboard": "Цільова панель візуалізації", + "open-right-layout": "Відкрити мобільний режим панелі візуалізації" + }, + "widgets-bundle": { + "current": "Поточний зв'язок", + "widgets-bundles": "Пакети віджетів", + "add": "Додати пакет віджетів", + "delete": "Видалити пакет віджетів", + "title": "Назва", + "title-required": "Необхідно вказати назву віджета.", + "add-widgets-bundle-text": "Додати новий пакет віджетів", + "no-widgets-bundles-text": "Не знайдено жодних пакетів віджетів", + "empty": "Пакет віджетів порожній", + "details": "Подробиці", + "widgets-bundle-details": "Деталі пакетів віджетів", + "delete-widgets-bundle-title": "Ви впевнені, що хочете видалити пакет віджетів '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text": "Будьте обережні, після підтвердження, пакети віджетів і всі пов'язані з ними дані стануть недоступними.", + "delete-widgets-bundles-title": "Ви впевнені, що хочете видалити { count, plural, 1 {пакет віджетів} other {# пакети віджетів} }?", + "delete-widgets-bundles-action-title": "Видалити { count, plural, 1 {1 пакет віджетів} other {# пакет віджетів} }", + "delete-widgets-bundles-text": "Будьте обережні, після підтвердження, всі виділені пакети віджетів і всі пов'язані з ними дані стануть недоступними.", + "no-widgets-bundles-matching": "Не знайдено жодних пакетів віджетів, які відповідають '{{widgetsBundle}}'.", + "widgets-bundle-required": "Необхідно вказати пакет віджетів.", + "system": "Системний", + "import": "Імпортувати пакет віджетів", + "export": "Експортувати пакет віджетів", + "export-failed-error": "Неможливо експортувати пакет віджетів: {{error}}", + "create-new-widgets-bundle": "Створити новий пакет віджетів", + "widgets-bundle-file": "Файл набору віджетів", + "invalid-widgets-bundle-file-error": "Неможливо імпортувати пакет віджетів: недійсна структура даних пакету віджетів." + }, + "widget-config": { + "data": "Дані", + "settings": "Налаштування", + "advanced": "Додатково", + "title": "Назва", + "general-settings": "Загальні налаштування", + "display-title": "Відобразити назву", + "drop-shadow": "Тінь", + "enable-fullscreen": "Увімкнути повноекранний режим", + "enable-data-export": "Увімкнути експорт даних", + "background-color": "Колір фону", + "text-color": "Колір тексту", + "padding": "Відступ", + "margin": "Границі", + "widget-style": "Стиль віджетів", + "title-style": "Стиль заголовка", + "mobile-mode-settings": "Налаштування мобільного режиму", + "order": "Порядок", + "height": "Висота", + "units": "Спеціальний символ після значення", + "decimals": "Кількість цифр після коми", + "timewindow": "Вікно часу", + "use-dashboard-timewindow": "Використати вікно часу на панелі візуалізації", + "display-legend": "Показати легенду", + "datasources": "Джерела даних", + "maximum-datasources": "Максимально { count, plural, 1 {1 дозволене джерело даних.} other {# дозволені джерела даних } }", + "datasource-type": "Тип", + "datasource-parameters": "Параметри", + "remove-datasource": "Видалити джерело даних", + "add-datasource": "Додати джерело даних", + "target-device": "Цільовий пристрій", + "alarm-source": "Джерело сигнала тривоги", + "actions": "Дії", + "action": "Дія", + "add-action": "Додати дію", + "search-actions": "Пошук дії", + "action-source": "Джерело дії", + "action-source-required": "Необхідно вказати джерело дії.", + "action-name": "Ім'я дії", + "action-name-required": "Необхідно вказати ім'я дії.", + "action-name-not-unique": "Дія з такою назвою вже існує.
    Назва дії має бути унікальною в межах одного джерела дії.", + "action-icon": "Іконка", + "action-type": "Тип", + "action-type-required": "Необхідно вказати тип дії.", + "edit-action": "Редагувати дію", + "delete-action": "Видалити дію", + "delete-action-title": "Видалити дію віджета", + "delete-action-text": "Ви впевнені, що хочете видалити дію віджета '{{actionName}}'?" + }, + "widget-type": { + "import": "Імпортувати тип віджета", + "export": "Експортувати тип віджета", + "export-failed-error": "Неможливо експортувати тип віджета: {{error}}", + "create-new-widget-type": "Створити новий тип віджета", + "widget-type-file": "Файл типу віджета", + "invalid-widget-type-file-error": "Неможливо імпортувати тип віджету: неправильна структура даних типу віджета." + }, + "white-labeling": { + "white-labeling": "Білий маркування", + "login-white-labeling": "Login White Labeling", + "preview": "Попередній перегляд", + "app-title": "Назва програми", + "favicon": "Іконка веб-сайту", + "favicon-description": "*.ico, *.gif or *.png image with maximum size {{kbSize}} KBytes.", + "favicon-size-error": "Зображення веб-сайту завелике. Максимально дозволений розмір зображення веб-сайту {{kbSize}} KBytes.", + "favicon-type-error": "Недійсний формат файлу зображення веб-сайту. Приймаються лише зображення ICO, GIF або PNG.", + "drop-favicon-image": "Зніміть зображення піктограми веб-сайту або клацніть, щоб вибрати файл для завантаження.", + "no-favicon-image": "Не вибрано жодної іконки", + "logo": "Логотип", + "logo-description": "Будь-яке зображення з максимальним розміром {{kbSize}} KBytes.", + "logo-size-error": "Зображення логотипу занадто велике. Максимально дозволений розмір зображення логотипу{{kbSize}} KBytes.", + "logo-type-error": "Недійсний формат файлу логотипу. Приймаються тільки зображення.", + "drop-logo-image": "Зніміть зображення логотипу або клацніть, щоб вибрати файл для завантаження.", + "no-logo-image": "Не вибрано жожного логотипу", + "logo-height": "Висота логотипу, px", + "primary-palette": "Основна палітра", + "accent-palette": "Палітра акцент", + "customize-palette": "Налаштування", + "edit-palette": "Редагувати палітру", + "save-palette": "Зберегти палітру", + "primary-background": "Первинний фон", + "secondary-background": "Вторинний фон", + "hue1": "HUE 1", + "hue2": "HUE 2", + "hue3": "HUE 3", + "page-background-color": "Колір фону сторінки", + "dark-foreground": "Темний передній план", + "domain-name": "Доменне ім'я" + }, + "icon": { + "icon": "веб-іконка", + "select-icon": "Виберіть веб-іконку", + "material-icons": "Матеріал веб-іконки", + "show-all": "Показати всі веб-іконки" + }, + "custom": { + "widget-action": { + "action-cell-button": "Кнопка дії клітинки", + "row-click": "Клацніть на рядок", + "marker-click": "Клацніть на маркер", + "tooltip-tag-action": "Дії при підказці" + } + }, + "language": { + "language": "Мова", + "locales": { + "fr_FR": "Французька", + "zh_CN": "Китайська", + "en_US": "Англійська", + "it_IT": "Італійська", + "ko_KR": "Корейська", + "ru_RU": "Російська", + "es_ES": "Іспанська", + "ja_JA": "Японська", + "tr_TR": "Турецька", + "de_DE": "Німецька", + "uk_UA": "Українська", + "fa_IR": "Перська" + } + } +} diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json index eaf810c986..41089cf611 100644 --- a/ui/src/app/locale/locale.constant-zh_CN.json +++ b/ui/src/app/locale/locale.constant-zh_CN.json @@ -589,11 +589,11 @@ "manage-credentials": "管理凭据", "delete": "删除设备", "assign-devices": "分配设备", - "assign-devices-text": "将{count,select,1 {1 设备} other {# 设备}}分配给客户", + "assign-devices-text": "将{count,plural,1 {1 设备} other {# 设备}}分配给客户", "delete-devices": "删除设备", "unassign-from-customer": "取消分配客户", "unassign-devices": "取消分配设备", - "unassign-devices-action-title": "从客户处取消分配{count,select,1 {1 设备} other {# 设备}}", + "unassign-devices-action-title": "从客户处取消分配{count,plural,1 {1 设备} other {# 设备}}", "assign-new-device": "分配新设备", "make-public-device-title": "您确定要将设备 '{{deviceName}}' 设为公开吗?", "make-public-device-text": "确认后,设备及其所有数据将被设为公开并可被其他人访问。", @@ -602,13 +602,13 @@ "view-credentials": "查看凭据", "delete-device-title": "您确定要删除设备的{{deviceName}}吗?", "delete-device-text": "小心!确认后设备及其所有相关数据将不可恢复。", - "delete-devices-title": "您确定要删除{count,select,1 {1 设备} other {# 设备}} 吗?", - "delete-devices-action-title": "删除 {count,select,1 {1 设备} other {# 设备}}", + "delete-devices-title": "您确定要删除{count,plural,1 {1 设备} other {# 设备}} 吗?", + "delete-devices-action-title": "删除 {count,plural,1 {1 设备} other {# 设备}}", "delete-devices-text": "小心!确认后所有选定的设备将被删除,所有相关数据将不可恢复。", "unassign-device-title": "您确定要取消分配设备 '{{deviceName}}'?", "unassign-device-text": "确认后,设备将被取消分配,客户将无法访问。", "unassign-device": "取消分配设备", - "unassign-devices-title": "您确定要取消分配{count,select,1 {1 设备} other {# 设备}} 吗?", + "unassign-devices-title": "您确定要取消分配{count,plural,1 {1 设备} other {# 设备}} 吗?", "unassign-devices-text": "确认后,所有选定的设备将被取消分配,并且客户将无法访问。", "device-credentials": "设备凭据", "credentials-type": "凭据类型", @@ -1176,7 +1176,7 @@ "tenant-details": "租客详情", "delete-tenant-title": "您确定要删除租户'{{tenantTitle}}'吗?", "delete-tenant-text": "小心!确认后,租户和所有相关数据将不可恢复。", - "delete-tenants-title": "您确定要删除 {count,select,1 {1 租户} other {# 租户}} 吗?", + "delete-tenants-title": "您确定要删除 {count,plural,1 {1 租户} other {# 租户}} 吗?", "delete-tenants-action-title": "删除 { count, plural, 1 {1 租户} other {# 租户} }", "delete-tenants-text": "小心!确认后,所有选定的租户将被删除,所有相关数据将不可恢复。", "title": "标题", @@ -1446,7 +1446,8 @@ "it_IT": "意大利", "ja_JA": "日本", "tr_TR": "土耳其", - "fa_IR": "波斯语" + "fa_IR": "波斯语", + "uk_UA": "乌克兰" } } } \ No newline at end of file From bc7db03351957b2ed410c1d0c918bfb9ca43b833 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 29 Mar 2019 13:50:52 +0200 Subject: [PATCH 58/74] Improve GRPC session error handling. --- .../service/cluster/rpc/GrpcSession.java | 19 ++++++++++++++----- pom.xml | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java index b5deda2edd..55b83e8ad9 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java @@ -95,15 +95,24 @@ public final class GrpcSession implements Closeable { } public void sendMsg(ClusterAPIProtos.ClusterMessage msg) { - outputStream.onNext(msg); - } - - public void onError(Throwable t) { - outputStream.onError(t); + if (connected) { + try { + outputStream.onNext(msg); + } catch (Throwable t) { + try { + outputStream.onError(t); + } catch (Throwable t2) { + } + listener.onError(GrpcSession.this, t); + } + } else { + log.warn("[{}] Failed to send message due to closed session!", sessionId); + } } @Override public void close() { + connected = false; try { outputStream.onCompleted(); } catch (IllegalStateException e) { diff --git a/pom.xml b/pom.xml index 59cc98aef0..67a2eef187 100755 --- a/pom.xml +++ b/pom.xml @@ -61,10 +61,10 @@ 1.4.3 4.2.0 3.6.1 - 1.16.1 + 1.19.0 1.16.18 1.1.0 - 4.1.30.Final + 4.1.34.Final 1.5.0 4.8.0 2.19.1 From d049ad8e51e6e5f92c4ae615825b1c5b19d40a97 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 29 Mar 2019 16:11:08 +0200 Subject: [PATCH 59/74] Fixed doulbe PUBACK for attribute request messages --- .../server/transport/mqtt/session/GatewaySessionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index 6a14330813..33352be30e 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -279,10 +279,10 @@ public class GatewaySessionHandler { @Override public void onFailure(Throwable t) { + ack(msg); log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t); } }, context.getExecutor()); - ack(msg); } else { throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); } From e5eac7bad0f90fefdd69659986fbd02dbf4d1cef Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 29 Mar 2019 17:44:22 +0200 Subject: [PATCH 60/74] feature/TbSaveToCustomCassandraTable --- .../server/actors/ActorSystemContext.java | 12 + .../actors/ruleChain/DefaultTbContext.java | 13 + .../rule/engine/api/TbContext.java | 7 + .../action/TbSaveToCustomCassandraTable.java | 267 ++++++++++++++++++ ...veToCustomCassandraTableConfiguration.java | 41 +++ .../static/rulenode/rulenode-core-config.js | 8 +- 6 files changed, 344 insertions(+), 4 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 2cc7e470c2..c35aae1fd6 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -48,11 +48,13 @@ import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.audit.AuditLogService; +import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -308,6 +310,16 @@ public class ActorSystemContext { @Getter private final Config config; + @Autowired(required = false) + @Getter + private CassandraCluster cassandraCluster; + + @Autowired(required = false) + @Getter + private CassandraBufferedRateExecutor cassandraBufferedRateExecutor; + + + public ActorSystemContext() { config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 3b89c09367..604626a050 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -44,10 +44,12 @@ import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -292,4 +294,15 @@ class DefaultTbContext implements TbContext { } }; } + + @Override + public CassandraCluster getCassandraCluster() { + return mainCtx.getCassandraCluster(); + } + + @Override + public CassandraBufferedRateExecutor getCassandraBufferedRateExecutor() { + return mainCtx.getCassandraBufferedRateExecutor(); + } + } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 427244753b..d3871e383a 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -25,10 +25,12 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -111,4 +113,9 @@ public interface TbContext { EventLoopGroup getSharedEventLoop(); + CassandraCluster getCassandraCluster(); + + CassandraBufferedRateExecutor getCassandraBufferedRateExecutor(); + + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java new file mode 100644 index 0000000000..1dffaa5491 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java @@ -0,0 +1,267 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.action; + +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.CodecRegistry; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.exceptions.CodecNotFoundException; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.dao.cassandra.CassandraCluster; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.type.AuthorityCodec; +import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec; +import org.thingsboard.server.dao.model.type.ComponentScopeCodec; +import org.thingsboard.server.dao.model.type.ComponentTypeCodec; +import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec; +import org.thingsboard.server.dao.model.type.EntityTypeCodec; +import org.thingsboard.server.dao.model.type.JsonCodec; +import org.thingsboard.server.dao.nosql.CassandraStatementTask; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; +import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback; + +@Slf4j +@RuleNode(type = ComponentType.ACTION, + name = "save to custom table", + configClazz = TbSaveToCustomCassandraTableConfiguration.class, + nodeDescription = "Node stores data from incoming Message payload to the Cassandra database into the predefined custom table" + + " that should have cs_tb_ prefix, to avoid the data insertion to the common TB tables.
    " + + "Note: rule node can be used only for Cassandra DB.", + nodeDetails = "Administrator should set the custom table name without prefix: cs_tb_.
    " + + "Administrator can configure the mapping between the Message field names and Table columns name.
    " + + "Note: the first table column would be always entity_id, that is identified by the Message Originator.

    " + + "If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via failure chain," + + " otherwise, the message will be routed via success chain.", + uiResources = {"static/rulenode/rulenode-core-config.js"}, + configDirective = "tbActionNodeCustomTableConfig", + icon = "file_upload") +public class TbSaveToCustomCassandraTable implements TbNode { + + private static final String TABLE_PREFIX = "cs_tb_"; + private static final JsonParser parser = new JsonParser(); + + private TbSaveToCustomCassandraTableConfiguration config; + private Session session; + private CassandraCluster cassandraCluster; + private ConsistencyLevel defaultWriteLevel; + private PreparedStatement saveStmt; + private ExecutorService readResultsProcessingExecutor; + private Map fieldsMap; + + @Override + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + config = TbNodeUtils.convert(configuration, TbSaveToCustomCassandraTableConfiguration.class); + cassandraCluster = ctx.getCassandraCluster(); + if (cassandraCluster == null) { + throw new RuntimeException("Unable to connect to Cassandra database"); + } else { + startExecutor(); + saveStmt = getSaveStmt(); + } + } + + @Override + public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { + withCallback(save(msg, ctx), aVoid -> { + ctx.tellNext(msg, SUCCESS); + }, e -> ctx.tellFailure(msg, e), ctx.getDbCallbackExecutor()); + } + + @Override + public void destroy() { + stopExecutor(); + saveStmt = null; + } + + private void startExecutor() { + readResultsProcessingExecutor = Executors.newCachedThreadPool(); + } + + private void stopExecutor() { + if (readResultsProcessingExecutor != null) { + readResultsProcessingExecutor.shutdownNow(); + } + } + + private PreparedStatement prepare(String query) { + return getSession().prepare(query); + } + + private Session getSession() { + if (session == null) { + session = cassandraCluster.getSession(); + defaultWriteLevel = cassandraCluster.getDefaultWriteConsistencyLevel(); + CodecRegistry registry = session.getCluster().getConfiguration().getCodecRegistry(); + registerCodecIfNotFound(registry, new JsonCodec()); + registerCodecIfNotFound(registry, new DeviceCredentialsTypeCodec()); + registerCodecIfNotFound(registry, new AuthorityCodec()); + registerCodecIfNotFound(registry, new ComponentLifecycleStateCodec()); + registerCodecIfNotFound(registry, new ComponentTypeCodec()); + registerCodecIfNotFound(registry, new ComponentScopeCodec()); + registerCodecIfNotFound(registry, new EntityTypeCodec()); + } + return session; + } + + private void registerCodecIfNotFound(CodecRegistry registry, TypeCodec codec) { + try { + registry.codecFor(codec.getCqlType(), codec.getJavaType()); + } catch (CodecNotFoundException e) { + registry.register(codec); + } + } + + + private PreparedStatement getSaveStmt() { + fieldsMap = config.getFieldsMapping(); + if (fieldsMap.isEmpty()) { + throw new RuntimeException("Fields(key,value) map is empty!"); + } else { + return prepareStatement(new ArrayList<>(fieldsMap.values())); + } + } + + private PreparedStatement prepareStatement(List fieldsList) { + return prepare(createQuery(fieldsList)); + } + + private String createQuery(List fieldsList) { + StringBuilder query = new StringBuilder(); + query.append("INSERT INTO ") + .append(TABLE_PREFIX) + .append(config.getTableName()) + .append("(").append(ModelConstants.ENTITY_ID_COLUMN) + .append(","); + int size = fieldsList.size(); + for (String field : fieldsList) { + query.append(field); + if (fieldsList.get(size - 1).equals(field)) { + query.append(")"); + } else { + query.append(","); + } + } + query.append(" VALUES("); + for (int i = 0; i <= size; i++) { + if (i == size) { + query.append("?)"); + } else { + query.append("?, "); + } + } + return query.toString(); + } + + private ListenableFuture save(TbMsg msg, TbContext ctx) { + JsonElement data = parser.parse(msg.getData()); + if (!data.isJsonObject()) { + throw new IllegalStateException("Invalid message structure, it is not a JSON Object:" + data); + } else { + JsonObject dataAsObject = data.getAsJsonObject(); + BoundStatement stmt = saveStmt.bind(); + AtomicInteger i = new AtomicInteger(0); + stmt.setUUID(i.get(), msg.getOriginator().getId()); + fieldsMap.forEach((key, value) -> { + i.getAndIncrement(); + if (dataAsObject.has(key)) { + if (dataAsObject.get(key).isJsonPrimitive()) { + JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive(); + if (primitive.isNumber()) { + stmt.setLong(i.get(), dataAsObject.get(key).getAsLong()); + } else if (primitive.isBoolean()) { + stmt.setBool(i.get(), dataAsObject.get(key).getAsBoolean()); + } else if (primitive.isString()) { + stmt.setString(i.get(), dataAsObject.get(key).getAsString()); + } else { + stmt.setToNull(i.get()); + } + } else { + throw new IllegalStateException("Message data key: '" + key + "' with value: '" + value + "' is not a JSON Primitive!"); + } + } else { + throw new RuntimeException("Message data doesn't contain key: " + "'" + key + "'!"); + } + }); + return getFuture(executeAsyncWrite(ctx, stmt), rs -> null); + } + } + + private ResultSetFuture executeAsyncWrite(TbContext ctx, Statement statement) { + return executeAsync(ctx, statement, defaultWriteLevel); + } + + private ResultSetFuture executeAsync(TbContext ctx, Statement statement, ConsistencyLevel level) { + if (log.isDebugEnabled()) { + log.debug("Execute cassandra async statement {}", statementToString(statement)); + } + if (statement.getConsistencyLevel() == null) { + statement.setConsistencyLevel(level); + } + return ctx.getCassandraBufferedRateExecutor().submit(new CassandraStatementTask(ctx.getTenantId(), getSession(), statement)); + } + + private static String statementToString(Statement statement) { + if (statement instanceof BoundStatement) { + return ((BoundStatement) statement).preparedStatement().getQueryString(); + } else { + return statement.toString(); + } + } + + private ListenableFuture getFuture(ResultSetFuture future, java.util.function.Function transformer) { + return Futures.transform(future, new Function() { + @Nullable + @Override + public T apply(@Nullable ResultSet input) { + return transformer.apply(input); + } + }, readResultsProcessingExecutor); + } + +} \ No newline at end of file diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java new file mode 100644 index 0000000000..a50f268785 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.action; + +import lombok.Data; +import org.thingsboard.rule.engine.api.NodeConfiguration; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class TbSaveToCustomCassandraTableConfiguration implements NodeConfiguration { + + + private String tableName; + private Map fieldsMapping; + + + @Override + public TbSaveToCustomCassandraTableConfiguration defaultConfiguration() { + TbSaveToCustomCassandraTableConfiguration configuration = new TbSaveToCustomCassandraTableConfiguration(); + configuration.setTableName(""); + Map map = new HashMap<>(); + map.put("", ""); + configuration.setFieldsMapping(map); + return configuration; + } +} diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index 95bb72a870..d610ab2e8e 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,5 +1,5 @@ -!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(91)},function(e,t){},1,1,1,1,function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    {{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{scope.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    tb.rulenode.alarm-type-required
    tb.rulenode.entity-type-pattern-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    {{ 'tb.rulenode.use-message-alarm-data' | translate }}
    tb.rulenode.alarm-type-required
    {{ severity.name | translate}}
    tb.rulenode.alarm-severity-required
    {{ 'tb.rulenode.propagate' | translate }}
    "},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.entity-type-pattern-required
    tb.rulenode.entity-type-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    {{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
    tb.rulenode.create-entity-if-not-exists-hint
    {{ 'tb.rulenode.remove-current-relations' | translate }}
    tb.rulenode.remove-current-relations-hint
    {{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
    tb.rulenode.change-originator-to-related-entity-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
    tb.rulenode.delete-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    tb.rulenode.message-count-required
    tb.rulenode.min-message-count-message
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-seconds-message
    {{ 'tb.rulenode.test-generator-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.bootstrap-servers-required
    tb.rulenode.min-retries-message
    tb.rulenode.min-batch-size-bytes-message
    tb.rulenode.min-linger-ms-message
    tb.rulenode.min-buffer-memory-bytes-message
    {{ ackValue }}
    tb.rulenode.key-serializer-required
    tb.rulenode.value-serializer-required
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-to-string-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.mqtt-topic-pattern-hint
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    tb.rulenode.connect-timeout-required
    tb.rulenode.connect-timeout-range
    tb.rulenode.connect-timeout-range
    {{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{credentialsValue.name | translate}}
    tb.rulenode.credentials-type-required
    tb.rulenode.username-required
    tb.rulenode.password-required
    '},function(e,t){e.exports="
    tb.rulenode.interval-seconds-required
    tb.rulenode.min-interval-seconds-message
    tb.rulenode.output-timeseries-key-prefix-required
    "},function(e,t){e.exports="
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-0-seconds-message
    tb.rulenode.max-pending-messages-required
    tb.rulenode.max-pending-messages-range
    tb.rulenode.max-pending-messages-range
    "},function(e,t){e.exports='
    {{ property }}
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    {{ \'tb.rulenode.automatic-recovery\' | translate }}
    tb.rulenode.min-connection-timeout-ms-message
    tb.rulenode.min-handshake-timeout-ms-message
    '},function(e,t){e.exports='
    tb.rulenode.endpoint-url-pattern-required
    tb.rulenode.endpoint-url-pattern-hint
    {{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
    tb.rulenode.headers-hint
    '; -},function(e,t){e.exports="
    "},function(e,t){e.exports="
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-message
    "},function(e,t){e.exports='
    {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
    {{smtpProtocol.toUpperCase()}}
    tb.rulenode.smtp-host-required
    tb.rulenode.smtp-port-required
    tb.rulenode.smtp-port-range
    tb.rulenode.smtp-port-range
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-msec-message
    {{ \'tb.rulenode.enable-tls\' | translate }}
    '},function(e,t){e.exports="
    tb.rulenode.topic-arn-pattern-required
    tb.rulenode.topic-arn-pattern-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    "},function(e,t){e.exports='
    {{ type.name | translate }}
    tb.rulenode.queue-url-pattern-required
    tb.rulenode.queue-url-pattern-hint
    tb.rulenode.min-delay-seconds-message
    tb.rulenode.max-delay-seconds-message
    tb.rulenode.message-attributes-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    '},function(e,t){e.exports="
    tb.rulenode.default-ttl-required
    tb.rulenode.min-default-ttl-message
    "},function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{ (\'relation.search-direction.\' + direction) | translate}}
    relation.relation-type
    device.device-types
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},function(e,t){e.exports='
    '},function(e,t){e.exports='
    {{ type }}
    tb.rulenode.fetch-mode-hint
    {{ type }}
    tb.rulenode.order-by-hint
    {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
    tb.rulenode.use-metadata-interval-patterns-hint
    tb.rulenode.start-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.end-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.start-interval-pattern-required
    tb.rulenode.start-interval-pattern-hint
    tb.rulenode.end-interval-pattern-required
    tb.rulenode.end-interval-pattern-hint
    '},function(e,t){e.exports='
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},28,function(e,t){e.exports='
    tb.rulenode.separator-hint
    tb.rulenode.separator-hint
    {{ \'tb.rulenode.check-all-keys\' | translate }}
    tb.rulenode.check-all-keys-hint
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
    tb.rulenode.check-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    "},function(e,t){e.exports='
    {{item}}
    tb.rulenode.no-message-types-found
    tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
    {{$chip.name}}
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-filter-function' | translate }}
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-switch-function' | translate }}
    "},function(e,t){e.exports='
    {{ keyText }} {{ valText }}  
    {{keyRequiredText}}
    {{valRequiredText}}
    {{ \'tb.key-val.remove-entry\' | translate }} close
    {{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
    '},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    relation.relation-filters
    "},function(e,t){e.exports='
    {{ source.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-transformer-function' | translate }}
    "},function(e,t){e.exports="
    tb.rulenode.from-template-required
    tb.rulenode.from-template-hint
    tb.rulenode.to-template-required
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.subject-template-required
    tb.rulenode.subject-template-hint
    tb.rulenode.body-template-required
    tb.rulenode.body-template-hint
    "; -},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(66),i=a(r),o=n(47),l=a(o),s=n(52),d=a(s),u=n(49),c=a(u),m=n(48),g=a(m),p=n(55),f=a(p),b=n(61),v=a(b),y=n(62),h=a(y),q=n(60),$=a(q),k=n(54),x=a(k),T=n(64),C=a(T),w=n(65),M=a(w),S=n(59),_=a(S),N=n(56),E=a(N),V=n(63),P=a(V),F=n(58),j=a(F),A=n(57),O=a(A),I=n(46),R=a(I),K=n(67),D=a(K),U=n(51),L=a(U),z=n(50),B=a(z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",x.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",j.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",D.default).directive("tbActionNodeDeleteRelationConfig",L.default).directive("tbActionNodeCreateRelationConfig",B.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("
    "),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(74),i=a(r),o=n(75),l=a(o),s=n(71),d=a(s),u=n(76),c=a(u),m=n(70),g=a(m),p=n(77),f=a(p),b=n(72),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(83),i=a(r),o=n(81),l=a(o),s=n(84),d=a(s),u=n(79),c=a(u),m=n(82),g=a(m),p=n(78),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(41),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(42),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(43),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(87),i=a(r),o=n(89),l=a(o),s=n(90),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(44),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(45),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(94),i=a(r),o=n(80),l=a(o),s=n(73),d=a(s),u=n(88),c=a(u),m=n(53),g=a(m),p=n(69),f=a(p),b=n(86),v=a(b),y=n(68),h=a(y),q=n(85),$=a(q),k=n(93),x=a(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(x.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", -"customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.'},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(92),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); +!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(93)},function(e,t){},1,1,1,1,function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    {{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{scope.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    tb.rulenode.alarm-type-required
    tb.rulenode.entity-type-pattern-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    {{ 'tb.rulenode.use-message-alarm-data' | translate }}
    tb.rulenode.alarm-type-required
    {{ severity.name | translate}}
    tb.rulenode.alarm-severity-required
    {{ 'tb.rulenode.propagate' | translate }}
    "},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.entity-type-pattern-required
    tb.rulenode.entity-type-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    {{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
    tb.rulenode.create-entity-if-not-exists-hint
    {{ 'tb.rulenode.remove-current-relations' | translate }}
    tb.rulenode.remove-current-relations-hint
    {{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
    tb.rulenode.change-originator-to-related-entity-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
    tb.rulenode.delete-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    tb.rulenode.message-count-required
    tb.rulenode.min-message-count-message
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-seconds-message
    {{ 'tb.rulenode.test-generator-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.bootstrap-servers-required
    tb.rulenode.min-retries-message
    tb.rulenode.min-batch-size-bytes-message
    tb.rulenode.min-linger-ms-message
    tb.rulenode.min-buffer-memory-bytes-message
    {{ ackValue }}
    tb.rulenode.key-serializer-required
    tb.rulenode.value-serializer-required
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-to-string-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.mqtt-topic-pattern-hint
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    tb.rulenode.connect-timeout-required
    tb.rulenode.connect-timeout-range
    tb.rulenode.connect-timeout-range
    {{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{credentialsValue.name | translate}}
    tb.rulenode.credentials-type-required
    tb.rulenode.username-required
    tb.rulenode.password-required
    '},function(e,t){e.exports="
    tb.rulenode.interval-seconds-required
    tb.rulenode.min-interval-seconds-message
    tb.rulenode.output-timeseries-key-prefix-required
    "},function(e,t){e.exports="
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-0-seconds-message
    tb.rulenode.max-pending-messages-required
    tb.rulenode.max-pending-messages-range
    tb.rulenode.max-pending-messages-range
    "},function(e,t){e.exports='
    {{ property }}
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    {{ \'tb.rulenode.automatic-recovery\' | translate }}
    tb.rulenode.min-connection-timeout-ms-message
    tb.rulenode.min-handshake-timeout-ms-message
    '},function(e,t){e.exports='
    tb.rulenode.endpoint-url-pattern-required
    tb.rulenode.endpoint-url-pattern-hint
    {{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
    tb.rulenode.headers-hint
    '; +},function(e,t){e.exports="
    "},function(e,t){e.exports="
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-message
    "},function(e,t){e.exports='
    tb.rulenode.custom-table-name-required
    tb.rulenode.custom-table-hint
    '},function(e,t){e.exports='
    {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
    {{smtpProtocol.toUpperCase()}}
    tb.rulenode.smtp-host-required
    tb.rulenode.smtp-port-required
    tb.rulenode.smtp-port-range
    tb.rulenode.smtp-port-range
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-msec-message
    {{ \'tb.rulenode.enable-tls\' | translate }}
    '},function(e,t){e.exports="
    tb.rulenode.topic-arn-pattern-required
    tb.rulenode.topic-arn-pattern-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    "},function(e,t){e.exports='
    {{ type.name | translate }}
    tb.rulenode.queue-url-pattern-required
    tb.rulenode.queue-url-pattern-hint
    tb.rulenode.min-delay-seconds-message
    tb.rulenode.max-delay-seconds-message
    tb.rulenode.message-attributes-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    '},function(e,t){e.exports="
    tb.rulenode.default-ttl-required
    tb.rulenode.min-default-ttl-message
    "},function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{ (\'relation.search-direction.\' + direction) | translate}}
    relation.relation-type
    device.device-types
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},function(e,t){e.exports='
    '},function(e,t){e.exports='
    {{ type }}
    tb.rulenode.fetch-mode-hint
    {{ type }}
    tb.rulenode.order-by-hint
    {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
    tb.rulenode.use-metadata-interval-patterns-hint
    tb.rulenode.start-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.end-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.start-interval-pattern-required
    tb.rulenode.start-interval-pattern-hint
    tb.rulenode.end-interval-pattern-required
    tb.rulenode.end-interval-pattern-hint
    '},function(e,t){e.exports='
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},29,function(e,t){e.exports='
    tb.rulenode.separator-hint
    tb.rulenode.separator-hint
    {{ \'tb.rulenode.check-all-keys\' | translate }}
    tb.rulenode.check-all-keys-hint
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
    tb.rulenode.check-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    "},function(e,t){e.exports='
    {{item}}
    tb.rulenode.no-message-types-found
    tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
    {{$chip.name}}
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-filter-function' | translate }}
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-switch-function' | translate }}
    "},function(e,t){e.exports='
    {{ keyText }} {{ valText }}  
    {{keyRequiredText}}
    {{valRequiredText}}
    {{ \'tb.key-val.remove-entry\' | translate }} close
    {{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
    '},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    relation.relation-filters
    "},function(e,t){e.exports='
    {{ source.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-transformer-function' | translate }}
    "},function(e,t){e.exports="
    tb.rulenode.from-template-required
    tb.rulenode.from-template-hint
    tb.rulenode.to-template-required
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.subject-template-required
    tb.rulenode.subject-template-hint
    tb.rulenode.body-template-required
    tb.rulenode.body-template-hint
    "; +},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(68),i=a(r),o=n(48),l=a(o),s=n(53),d=a(s),u=n(50),c=a(u),m=n(49),g=a(m),p=n(56),f=a(p),b=n(62),v=a(b),y=n(63),h=a(y),q=n(61),$=a(q),k=n(55),x=a(k),T=n(66),C=a(T),w=n(67),M=a(w),_=n(60),S=a(_),N=n(57),E=a(N),V=n(65),P=a(V),F=n(59),j=a(F),A=n(58),O=a(A),I=n(47),R=a(I),K=n(69),D=a(K),U=n(52),L=a(U),z=n(51),B=a(z),H=n(64),Y=a(H);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",x.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",j.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",D.default).directive("tbActionNodeDeleteRelationConfig",L.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",Y.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("
    "),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(76),i=a(r),o=n(77),l=a(o),s=n(73),d=a(s),u=n(78),c=a(u),m=n(72),g=a(m),p=n(79),f=a(p),b=n(74),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(37),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(85),i=a(r),o=n(83),l=a(o),s=n(86),d=a(s),u=n(81),c=a(u),m=n(84),g=a(m),p=n(80),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(42),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(43),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(44),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(89),i=a(r),o=n(91),l=a(o),s=n(92),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(45),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(46),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(96),i=a(r),o=n(82),l=a(o),s=n(75),d=a(s),u=n(90),c=a(u),m=n(54),g=a(m),p=n(71),f=a(p),b=n(88),v=a(b),y=n(70),h=a(y),q=n(87),$=a(q),k=n(95),x=a(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(x.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.", +"customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(94),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file From 37a52d4471f60c52724142064f09a5b7a59587c2 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 29 Mar 2019 17:46:56 +0200 Subject: [PATCH 61/74] feature/TbSaveToCustomCassandraTable --- ...able.java => TbSaveToCustomCassandraTableNode.java} | 10 ++++------ ...TbSaveToCustomCassandraTableNodeConfiguration.java} | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) rename rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/{TbSaveToCustomCassandraTable.java => TbSaveToCustomCassandraTableNode.java} (97%) rename rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/{TbSaveToCustomCassandraTableConfiguration.java => TbSaveToCustomCassandraTableNodeConfiguration.java} (76%) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java similarity index 97% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index 1dffaa5491..6d127a41e2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTable.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -53,8 +53,6 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -69,7 +67,7 @@ import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback; @Slf4j @RuleNode(type = ComponentType.ACTION, name = "save to custom table", - configClazz = TbSaveToCustomCassandraTableConfiguration.class, + configClazz = TbSaveToCustomCassandraTableNodeConfiguration.class, nodeDescription = "Node stores data from incoming Message payload to the Cassandra database into the predefined custom table" + " that should have cs_tb_ prefix, to avoid the data insertion to the common TB tables.
    " + "Note: rule node can be used only for Cassandra DB.", @@ -81,12 +79,12 @@ import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCustomTableConfig", icon = "file_upload") -public class TbSaveToCustomCassandraTable implements TbNode { +public class TbSaveToCustomCassandraTableNode implements TbNode { private static final String TABLE_PREFIX = "cs_tb_"; private static final JsonParser parser = new JsonParser(); - private TbSaveToCustomCassandraTableConfiguration config; + private TbSaveToCustomCassandraTableNodeConfiguration config; private Session session; private CassandraCluster cassandraCluster; private ConsistencyLevel defaultWriteLevel; @@ -96,7 +94,7 @@ public class TbSaveToCustomCassandraTable implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - config = TbNodeUtils.convert(configuration, TbSaveToCustomCassandraTableConfiguration.class); + config = TbNodeUtils.convert(configuration, TbSaveToCustomCassandraTableNodeConfiguration.class); cassandraCluster = ctx.getCassandraCluster(); if (cassandraCluster == null) { throw new RuntimeException("Unable to connect to Cassandra database"); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java similarity index 76% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java index a50f268785..0c78618ebf 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeConfiguration.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; @Data -public class TbSaveToCustomCassandraTableConfiguration implements NodeConfiguration { +public class TbSaveToCustomCassandraTableNodeConfiguration implements NodeConfiguration { private String tableName; @@ -30,8 +30,8 @@ public class TbSaveToCustomCassandraTableConfiguration implements NodeConfigurat @Override - public TbSaveToCustomCassandraTableConfiguration defaultConfiguration() { - TbSaveToCustomCassandraTableConfiguration configuration = new TbSaveToCustomCassandraTableConfiguration(); + public TbSaveToCustomCassandraTableNodeConfiguration defaultConfiguration() { + TbSaveToCustomCassandraTableNodeConfiguration configuration = new TbSaveToCustomCassandraTableNodeConfiguration(); configuration.setTableName(""); Map map = new HashMap<>(); map.put("", ""); From cd076089c3d5691c3a5b28f812b6d372976e1804 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 1 Apr 2019 13:56:35 +0300 Subject: [PATCH 62/74] fix hardcode --- .../TbSaveToCustomCassandraTableNode.java | 17 +++++++++-------- .../static/rulenode/rulenode-core-config.js | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index 6d127a41e2..ddacfc5f15 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -73,7 +73,7 @@ import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback; "Note: rule node can be used only for Cassandra DB.", nodeDetails = "Administrator should set the custom table name without prefix: cs_tb_.
    " + "Administrator can configure the mapping between the Message field names and Table columns name.
    " + - "Note: the first table column would be always entity_id, that is identified by the Message Originator.

    " + + "Note:If the mapping key is $entity_id, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.

    " + "If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via failure chain," + " otherwise, the message will be routed via success chain.", uiResources = {"static/rulenode/rulenode-core-config.js"}, @@ -83,6 +83,7 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { private static final String TABLE_PREFIX = "cs_tb_"; private static final JsonParser parser = new JsonParser(); + private static final String ENTITY_ID = "$entityId"; private TbSaveToCustomCassandraTableNodeConfiguration config; private Session session; @@ -170,13 +171,12 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { } private String createQuery(List fieldsList) { + int size = fieldsList.size(); StringBuilder query = new StringBuilder(); query.append("INSERT INTO ") .append(TABLE_PREFIX) .append(config.getTableName()) - .append("(").append(ModelConstants.ENTITY_ID_COLUMN) - .append(","); - int size = fieldsList.size(); + .append("("); for (String field : fieldsList) { query.append(field); if (fieldsList.get(size - 1).equals(field)) { @@ -186,8 +186,8 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { } } query.append(" VALUES("); - for (int i = 0; i <= size; i++) { - if (i == size) { + for (int i = 0; i < size; i++) { + if (i == size - 1) { query.append("?)"); } else { query.append("?, "); @@ -204,9 +204,7 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { JsonObject dataAsObject = data.getAsJsonObject(); BoundStatement stmt = saveStmt.bind(); AtomicInteger i = new AtomicInteger(0); - stmt.setUUID(i.get(), msg.getOriginator().getId()); fieldsMap.forEach((key, value) -> { - i.getAndIncrement(); if (dataAsObject.has(key)) { if (dataAsObject.get(key).isJsonPrimitive()) { JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive(); @@ -222,9 +220,12 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { } else { throw new IllegalStateException("Message data key: '" + key + "' with value: '" + value + "' is not a JSON Primitive!"); } + } else if (key.equals(ENTITY_ID)) { + stmt.setUUID(i.get(), msg.getOriginator().getId()); } else { throw new RuntimeException("Message data doesn't contain key: " + "'" + key + "'!"); } + i.getAndIncrement(); }); return getFuture(executeAsyncWrite(ctx, stmt), rs -> null); } diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index d610ab2e8e..dae2d132d7 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,5 +1,5 @@ -!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(93)},function(e,t){},1,1,1,1,function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    {{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{scope.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    tb.rulenode.alarm-type-required
    tb.rulenode.entity-type-pattern-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    {{ 'tb.rulenode.use-message-alarm-data' | translate }}
    tb.rulenode.alarm-type-required
    {{ severity.name | translate}}
    tb.rulenode.alarm-severity-required
    {{ 'tb.rulenode.propagate' | translate }}
    "},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.entity-type-pattern-required
    tb.rulenode.entity-type-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    {{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
    tb.rulenode.create-entity-if-not-exists-hint
    {{ 'tb.rulenode.remove-current-relations' | translate }}
    tb.rulenode.remove-current-relations-hint
    {{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
    tb.rulenode.change-originator-to-related-entity-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
    tb.rulenode.delete-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    tb.rulenode.message-count-required
    tb.rulenode.min-message-count-message
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-seconds-message
    {{ 'tb.rulenode.test-generator-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.bootstrap-servers-required
    tb.rulenode.min-retries-message
    tb.rulenode.min-batch-size-bytes-message
    tb.rulenode.min-linger-ms-message
    tb.rulenode.min-buffer-memory-bytes-message
    {{ ackValue }}
    tb.rulenode.key-serializer-required
    tb.rulenode.value-serializer-required
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-to-string-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.mqtt-topic-pattern-hint
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    tb.rulenode.connect-timeout-required
    tb.rulenode.connect-timeout-range
    tb.rulenode.connect-timeout-range
    {{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{credentialsValue.name | translate}}
    tb.rulenode.credentials-type-required
    tb.rulenode.username-required
    tb.rulenode.password-required
    '},function(e,t){e.exports="
    tb.rulenode.interval-seconds-required
    tb.rulenode.min-interval-seconds-message
    tb.rulenode.output-timeseries-key-prefix-required
    "},function(e,t){e.exports="
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-0-seconds-message
    tb.rulenode.max-pending-messages-required
    tb.rulenode.max-pending-messages-range
    tb.rulenode.max-pending-messages-range
    "},function(e,t){e.exports='
    {{ property }}
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    {{ \'tb.rulenode.automatic-recovery\' | translate }}
    tb.rulenode.min-connection-timeout-ms-message
    tb.rulenode.min-handshake-timeout-ms-message
    '},function(e,t){e.exports='
    tb.rulenode.endpoint-url-pattern-required
    tb.rulenode.endpoint-url-pattern-hint
    {{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
    tb.rulenode.headers-hint
    '; -},function(e,t){e.exports="
    "},function(e,t){e.exports="
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-message
    "},function(e,t){e.exports='
    tb.rulenode.custom-table-name-required
    tb.rulenode.custom-table-hint
    '},function(e,t){e.exports='
    {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
    {{smtpProtocol.toUpperCase()}}
    tb.rulenode.smtp-host-required
    tb.rulenode.smtp-port-required
    tb.rulenode.smtp-port-range
    tb.rulenode.smtp-port-range
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-msec-message
    {{ \'tb.rulenode.enable-tls\' | translate }}
    '},function(e,t){e.exports="
    tb.rulenode.topic-arn-pattern-required
    tb.rulenode.topic-arn-pattern-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    "},function(e,t){e.exports='
    {{ type.name | translate }}
    tb.rulenode.queue-url-pattern-required
    tb.rulenode.queue-url-pattern-hint
    tb.rulenode.min-delay-seconds-message
    tb.rulenode.max-delay-seconds-message
    tb.rulenode.message-attributes-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    '},function(e,t){e.exports="
    tb.rulenode.default-ttl-required
    tb.rulenode.min-default-ttl-message
    "},function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{ (\'relation.search-direction.\' + direction) | translate}}
    relation.relation-type
    device.device-types
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},function(e,t){e.exports='
    '},function(e,t){e.exports='
    {{ type }}
    tb.rulenode.fetch-mode-hint
    {{ type }}
    tb.rulenode.order-by-hint
    {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
    tb.rulenode.use-metadata-interval-patterns-hint
    tb.rulenode.start-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.end-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.start-interval-pattern-required
    tb.rulenode.start-interval-pattern-hint
    tb.rulenode.end-interval-pattern-required
    tb.rulenode.end-interval-pattern-hint
    '},function(e,t){e.exports='
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},29,function(e,t){e.exports='
    tb.rulenode.separator-hint
    tb.rulenode.separator-hint
    {{ \'tb.rulenode.check-all-keys\' | translate }}
    tb.rulenode.check-all-keys-hint
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
    tb.rulenode.check-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    "},function(e,t){e.exports='
    {{item}}
    tb.rulenode.no-message-types-found
    tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
    {{$chip.name}}
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-filter-function' | translate }}
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-switch-function' | translate }}
    "},function(e,t){e.exports='
    {{ keyText }} {{ valText }}  
    {{keyRequiredText}}
    {{valRequiredText}}
    {{ \'tb.key-val.remove-entry\' | translate }} close
    {{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
    '},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    relation.relation-filters
    "},function(e,t){e.exports='
    {{ source.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-transformer-function' | translate }}
    "},function(e,t){e.exports="
    tb.rulenode.from-template-required
    tb.rulenode.from-template-hint
    tb.rulenode.to-template-required
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.subject-template-required
    tb.rulenode.subject-template-hint
    tb.rulenode.body-template-required
    tb.rulenode.body-template-hint
    "; -},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var d=o.default;i.html(d),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(68),i=a(r),o=n(48),l=a(o),s=n(53),d=a(s),u=n(50),c=a(u),m=n(49),g=a(m),p=n(56),f=a(p),b=n(62),v=a(b),y=n(63),h=a(y),q=n(61),$=a(q),k=n(55),x=a(k),T=n(66),C=a(T),w=n(67),M=a(w),_=n(60),S=a(_),N=n(57),E=a(N),V=n(65),P=a(V),F=n(59),j=a(F),A=n(58),O=a(A),I=n(47),R=a(I),K=n(69),D=a(K),U=n(52),L=a(U),z=n(51),B=a(z),H=n(64),Y=a(H);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",x.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",j.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",D.default).directive("tbActionNodeDeleteRelationConfig",L.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",Y.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(28),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("
    "),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(76),i=a(r),o=n(77),l=a(o),s=n(73),d=a(s),u=n(78),c=a(u),m=n(72),g=a(m),p=n(79),f=a(p),b=n(74),v=a(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(36),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(37),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(85),i=a(r),o=n(83),l=a(o),s=n(86),d=a(s),u=n(81),c=a(u),m=n(84),g=a(m),p=n(80),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(42),o=a(i);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(43),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(44),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(89),i=a(r),o=n(91),l=a(o),s=n(92),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(45),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(46),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(96),i=a(r),o=n(82),l=a(o),s=n(75),d=a(s),u=n(90),c=a(u),m=n(54),g=a(m),p=n(71),f=a(p),b=n(88),v=a(b),y=n(70),h=a(y),q=n(87),$=a(q),k=n(95),x=a(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(x.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.", -"customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(94),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); +!function(e){function t(i){if(n[i])return n[i].exports;var a=n[i]={exports:{},id:i,loaded:!1};return e[i].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),i=e[t[0]];return function(e,t,a){i.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(97)},function(e,t){},1,1,1,1,function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    {{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{scope.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    tb.rulenode.alarm-type-required
    tb.rulenode.entity-type-pattern-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    {{ 'tb.rulenode.use-message-alarm-data' | translate }}
    tb.rulenode.alarm-type-required
    {{ severity.name | translate}}
    tb.rulenode.alarm-severity-required
    {{ 'tb.rulenode.propagate' | translate }}
    "},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.entity-type-pattern-required
    tb.rulenode.entity-type-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    {{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
    tb.rulenode.create-entity-if-not-exists-hint
    {{ 'tb.rulenode.remove-current-relations' | translate }}
    tb.rulenode.remove-current-relations-hint
    {{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
    tb.rulenode.change-originator-to-related-entity-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
    tb.rulenode.delete-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    tb.rulenode.message-count-required
    tb.rulenode.min-message-count-message
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-seconds-message
    {{ 'tb.rulenode.test-generator-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.latitude-key-name-required
    tb.rulenode.longitude-key-name-required
    {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
    {{ type.name | translate}}
    tb.rulenode.circle-center-latitude-required
    tb.rulenode.circle-center-longitude-required
    tb.rulenode.range-required
    {{ type.name | translate}}
    tb.rulenode.polygon-definition-required
    tb.rulenode.polygon-definition-hint
    tb.rulenode.min-inside-duration-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.min-outside-duration-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    '},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.bootstrap-servers-required
    tb.rulenode.min-retries-message
    tb.rulenode.min-batch-size-bytes-message
    tb.rulenode.min-linger-ms-message
    tb.rulenode.min-buffer-memory-bytes-message
    {{ ackValue }}
    tb.rulenode.key-serializer-required
    tb.rulenode.value-serializer-required
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-to-string-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.mqtt-topic-pattern-hint
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    tb.rulenode.connect-timeout-required
    tb.rulenode.connect-timeout-range
    tb.rulenode.connect-timeout-range
    {{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{credentialsValue.name | translate}}
    tb.rulenode.credentials-type-required
    tb.rulenode.username-required
    tb.rulenode.password-required
    '},function(e,t){e.exports="
    tb.rulenode.interval-seconds-required
    tb.rulenode.min-interval-seconds-message
    tb.rulenode.output-timeseries-key-prefix-required
    "; +},function(e,t){e.exports="
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-0-seconds-message
    tb.rulenode.max-pending-messages-required
    tb.rulenode.max-pending-messages-range
    tb.rulenode.max-pending-messages-range
    "},function(e,t){e.exports='
    {{ property }}
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    {{ \'tb.rulenode.automatic-recovery\' | translate }}
    tb.rulenode.min-connection-timeout-ms-message
    tb.rulenode.min-handshake-timeout-ms-message
    '},function(e,t){e.exports='
    tb.rulenode.endpoint-url-pattern-required
    tb.rulenode.endpoint-url-pattern-hint
    {{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
    tb.rulenode.headers-hint
    '},function(e,t){e.exports="
    "},function(e,t){e.exports="
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-message
    "},function(e,t){e.exports='
    tb.rulenode.custom-table-name-required
    tb.rulenode.custom-table-hint
    '},function(e,t){e.exports='
    {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
    {{smtpProtocol.toUpperCase()}}
    tb.rulenode.smtp-host-required
    tb.rulenode.smtp-port-required
    tb.rulenode.smtp-port-range
    tb.rulenode.smtp-port-range
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-msec-message
    {{ \'tb.rulenode.enable-tls\' | translate }}
    '},function(e,t){e.exports="
    tb.rulenode.topic-arn-pattern-required
    tb.rulenode.topic-arn-pattern-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    "},function(e,t){e.exports='
    {{ type.name | translate }}
    tb.rulenode.queue-url-pattern-required
    tb.rulenode.queue-url-pattern-hint
    tb.rulenode.min-delay-seconds-message
    tb.rulenode.max-delay-seconds-message
    tb.rulenode.message-attributes-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    '},function(e,t){e.exports="
    tb.rulenode.default-ttl-required
    tb.rulenode.min-default-ttl-message
    "},function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{ (\'relation.search-direction.\' + direction) | translate}}
    relation.relation-type
    device.device-types
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},function(e,t){e.exports='
    '},function(e,t){e.exports='
    {{ type }}
    tb.rulenode.fetch-mode-hint
    {{ type }}
    tb.rulenode.order-by-hint
    {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
    tb.rulenode.use-metadata-interval-patterns-hint
    tb.rulenode.start-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.end-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.start-interval-pattern-required
    tb.rulenode.start-interval-pattern-hint
    tb.rulenode.end-interval-pattern-required
    tb.rulenode.end-interval-pattern-hint
    '},function(e,t){e.exports='
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},30,function(e,t){e.exports='
    tb.rulenode.separator-hint
    tb.rulenode.separator-hint
    {{ \'tb.rulenode.check-all-keys\' | translate }}
    tb.rulenode.check-all-keys-hint
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
    tb.rulenode.check-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    "},function(e,t){e.exports='
    tb.rulenode.latitude-key-name-required
    tb.rulenode.longitude-key-name-required
    {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
    {{ type.name | translate}}
    tb.rulenode.circle-center-latitude-required
    tb.rulenode.circle-center-longitude-required
    tb.rulenode.range-required
    {{ type.name | translate}}
    tb.rulenode.polygon-definition-required
    tb.rulenode.polygon-definition-hint
    '; +},function(e,t){e.exports='
    {{item}}
    tb.rulenode.no-message-types-found
    tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
    {{$chip.name}}
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-filter-function' | translate }}
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-switch-function' | translate }}
    "},function(e,t){e.exports='
    {{ keyText }} {{ valText }}  
    {{keyRequiredText}}
    {{valRequiredText}}
    {{ \'tb.key-val.remove-entry\' | translate }} close
    {{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
    '},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    relation.relation-filters
    "},function(e,t){e.exports='
    {{ source.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-transformer-function' | translate }}
    "},function(e,t){e.exports="
    tb.rulenode.from-template-required
    tb.rulenode.from-template-hint
    tb.rulenode.to-template-required
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.subject-template-required
    tb.rulenode.subject-template-hint
    tb.rulenode.body-template-required
    tb.rulenode.body-template-hint
    "},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(6),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(7),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(8),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(9),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(10),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(11),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.originator=null,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue,a.configuration.originatorId&&a.configuration.originatorType?a.originator={id:a.configuration.originatorId,entityType:a.configuration.originatorType}:a.originator=null,a.$watch("originator",function(e,t){angular.equals(e,t)||(a.originator?(s.$viewValue.originatorId=a.originator.id,s.$viewValue.originatorType=a.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},a.testScript=function(e){var n=angular.copy(a.configuration.jsScript);i.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var r=n(12),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(13),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(71),r=i(a),o=n(50),l=i(o),s=n(55),d=i(s),u=n(52),c=i(u),m=n(51),g=i(m),p=n(59),f=i(p),b=n(65),v=i(b),y=n(66),q=i(y),h=n(64),$=i(h),x=n(58),k=i(x),T=n(69),C=i(T),w=n(70),M=i(w),N=n(63),_=i(N),S=n(60),E=i(S),F=n(68),P=i(F),V=n(62),A=i(V),j=n(61),O=i(j),I=n(49),R=i(I),D=n(72),K=i(D),L=n(54),U=i(L),z=n(53),B=i(z),H=n(67),Y=i(H),G=n(56),Q=i(G);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",A.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",K.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",Y.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(14),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(15),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$mdExpansionPanel=t,i.ruleNodeTypes=n,i.credentialsTypeChanged=function(){var e=i.configuration.credentials.type;i.configuration.credentials={},i.configuration.credentials.type=e,i.updateValidity()},i.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){i.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(i.configuration.credentials.caCertFileName=e.name,i.configuration.credentials.caCert=a),"privateKey"==t&&(i.configuration.credentials.privateKeyFileName=e.name,i.configuration.credentials.privateKey=a),"Cert"==t&&(i.configuration.credentials.certFileName=e.name,i.configuration.credentials.cert=a)),i.updateValidity()}})},n.readAsText(e.file)},i.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(i.configuration.credentials.caCertFileName=null,i.configuration.credentials.caCert=null),"privateKey"==e&&(i.configuration.credentials.privateKeyFileName=null,i.configuration.credentials.privateKey=null),"Cert"==e&&(i.configuration.credentials.certFileName=null,i.configuration.credentials.cert=null),i.updateValidity()},i.updateValidity=function(){var e=!0,t=i.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var r=n(16),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(17),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(18),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(19),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(20),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(21),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(22),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(23),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(24),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(25),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(26),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(27),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(28),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(29),o=i(r)},function(e,t){"use strict";function n(e){var t=function(t,n,i,a){n.html("
    "),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(30),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(31),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s);var d=186;i.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],i.ruleNodeTypes=n,i.aggPeriodTimeUnits={},i.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,i.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,i.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,i.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,i.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{},link:i}}a.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(32),o=i(r);n(3)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(79),r=i(a),o=n(80),l=i(o),s=n(76),d=i(s),u=n(81),c=i(u),m=n(75),g=i(m),p=n(82),f=i(p),b=n(77),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(33),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(34),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(35),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(36),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(37),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(38),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){ +var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(39),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(89),r=i(a),o=n(87),l=i(o),s=n(90),d=i(s),u=n(84),c=i(u),m=n(88),g=i(m),p=n(83),f=i(p),b=n(85),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=a,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(44),o=i(r);n(5)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(45),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(46),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(93),r=i(a),o=n(95),l=i(o),s=n(96),d=i(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(47),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(48),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(100),r=i(a),o=n(86),l=i(o),s=n(78),d=i(s),u=n(94),c=i(u),m=n(57),g=i(m),p=n(74),f=i(p),b=n(92),v=i(b),y=n(73),q=i(y),h=n(91),$=i(h),x=n(99),k=i(x);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",$.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lon2,lon4], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){(0,o.default)(e)}a.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(98),o=i(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file From f009c8522e64b47df904b13f4093249e2abe6b4f Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 1 Apr 2019 14:25:59 +0300 Subject: [PATCH 63/74] fix typo --- .../engine/action/TbSaveToCustomCassandraTableNode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index ddacfc5f15..7bcf710492 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -205,7 +205,9 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { BoundStatement stmt = saveStmt.bind(); AtomicInteger i = new AtomicInteger(0); fieldsMap.forEach((key, value) -> { - if (dataAsObject.has(key)) { + if (key.equals(ENTITY_ID)) { + stmt.setUUID(i.get(), msg.getOriginator().getId()); + } else if (dataAsObject.has(key)) { if (dataAsObject.get(key).isJsonPrimitive()) { JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive(); if (primitive.isNumber()) { @@ -220,8 +222,6 @@ public class TbSaveToCustomCassandraTableNode implements TbNode { } else { throw new IllegalStateException("Message data key: '" + key + "' with value: '" + value + "' is not a JSON Primitive!"); } - } else if (key.equals(ENTITY_ID)) { - stmt.setUUID(i.get(), msg.getOriginator().getId()); } else { throw new RuntimeException("Message data doesn't contain key: " + "'" + key + "'!"); } From f41cd97cbbcfaaef91f880bfd16c85b0153e97c9 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 25 Mar 2019 18:00:39 +0200 Subject: [PATCH 64/74] MessageId to PacketId refactoring --- .../org/thingsboard/mqtt/MqttChannelHandler.java | 12 ++++++------ .../java/org/thingsboard/mqtt/MqttClientImpl.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java index 1525510814..63e1ac3e63 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java @@ -185,21 +185,21 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler case AT_LEAST_ONCE: invokeHandlersForIncomingPublish(message); - if (message.variableHeader().messageId() != -1) { + if (message.variableHeader().packetId() != -1) { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().messageId()); + MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().packetId()); channel.writeAndFlush(new MqttPubAckMessage(fixedHeader, variableHeader)); } break; case EXACTLY_ONCE: - if (message.variableHeader().messageId() != -1) { + if (message.variableHeader().packetId() != -1) { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().messageId()); + MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().packetId()); MqttMessage pubrecMessage = new MqttMessage(fixedHeader, variableHeader); MqttIncomingQos2Publish incomingQos2Publish = new MqttIncomingQos2Publish(message, pubrecMessage); - this.client.getQos2PendingIncomingPublishes().put(message.variableHeader().messageId(), incomingQos2Publish); + this.client.getQos2PendingIncomingPublishes().put(message.variableHeader().packetId(), incomingQos2Publish); message.payload().retain(); incomingQos2Publish.startPubrecRetransmitTimer(this.client.getEventLoop().next(), this.client::sendAndFlushPacket); @@ -249,7 +249,7 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler MqttIncomingQos2Publish incomingQos2Publish = this.client.getQos2PendingIncomingPublishes().get(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); this.invokeHandlersForIncomingPublish(incomingQos2Publish.getIncomingPublish()); incomingQos2Publish.onPubrelReceived(); - this.client.getQos2PendingIncomingPublishes().remove(incomingQos2Publish.getIncomingPublish().variableHeader().messageId()); + this.client.getQos2PendingIncomingPublishes().remove(incomingQos2Publish.getIncomingPublish().variableHeader().packetId()); } MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 0); MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index da497e2d70..85b3abeeee 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -339,7 +339,7 @@ final class MqttClientImpl implements MqttClient { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retain, 0); MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topic, getNewMessageId().messageId()); MqttPublishMessage message = new MqttPublishMessage(fixedHeader, variableHeader, payload); - MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.messageId(), future, payload.retain(), message, qos); + MqttPendingPublish pendingPublish = new MqttPendingPublish(variableHeader.packetId(), future, payload.retain(), message, qos); ChannelFuture channelFuture = this.sendAndFlushPacket(message); if (channelFuture != null) { From 876b9eeda4253926ee797609aa33187d6e2cf4c1 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 28 Mar 2019 20:38:35 +0200 Subject: [PATCH 65/74] Initial rule nodes commit --- pom.xml | 12 ++ rule-engine/rule-engine-components/pom.xml | 16 ++- .../engine/geo/AbstractGeofencingNode.java | 120 +++++++++++++++++ .../rule/engine/geo/Coordinates.java | 24 ++++ .../engine/geo/EntityGeofencingState.java | 29 +++++ .../thingsboard/rule/engine/geo/GeoUtil.java | 68 ++++++++++ .../rule/engine/geo/Perimeter.java | 34 +++++ .../rule/engine/geo/PerimeterType.java | 20 +++ .../rule/engine/geo/RangeUnit.java | 30 +++++ .../engine/geo/TbGpsGeofencingActionNode.java | 121 ++++++++++++++++++ ...bGpsGeofencingActionNodeConfiguration.java | 45 +++++++ .../engine/geo/TbGpsGeofencingFilterNode.java | 71 ++++++++++ ...bGpsGeofencingFilterNodeConfiguration.java | 47 +++++++ 13 files changed, 633 insertions(+), 4 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Coordinates.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/EntityGeofencingState.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Perimeter.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/PerimeterType.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/RangeUnit.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java diff --git a/pom.xml b/pom.xml index 67a2eef187..1298edab9d 100755 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,8 @@ 3.0.2 2.6.1 1.0.0 + 0.7 + 1.15.0 1.56 2.0.1 2.4.0 @@ -828,6 +830,16 @@ springfox-swagger-ui-rfc6570 ${springfox-swagger-ui-rfc6570.version} + + org.locationtech.spatial4j + spatial4j + ${spatial4j.version} + + + org.locationtech.jts + jts-core + ${jts.version} + diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 01253f0acc..64a6548dc8 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -97,6 +97,14 @@ org.bouncycastle bcpkix-jdk15on + + org.locationtech.spatial4j + spatial4j + + + org.locationtech.jts + jts-core + junit junit @@ -142,10 +150,10 @@ true true - - - - + + + + diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java new file mode 100644 index 0000000000..f876967917 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java @@ -0,0 +1,120 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.locationtech.spatial4j.context.jts.JtsSpatialContext; +import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; +import org.springframework.util.StringUtils; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.msg.TbMsg; + +import java.util.Collections; +import java.util.List; + +public abstract class AbstractGeofencingNode implements TbNode { + + protected T config; + protected JtsSpatialContext jtsCtx; + + @Override + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + this.config = TbNodeUtils.convert(configuration, getConfigClazz()); + JtsSpatialContextFactory factory = new JtsSpatialContextFactory(); + factory.normWrapLongitude = true; + jtsCtx = factory.newSpatialContext(); + } + + abstract protected Class getConfigClazz(); + + protected boolean checkMatches(TbMsg msg) throws TbNodeException { + JsonElement msgDataElement = new JsonParser().parse(msg.getData()); + if (!msgDataElement.isJsonObject()) { + throw new TbNodeException("Incoming Message is not a valid JSON object"); + } + JsonObject msgDataObj = msgDataElement.getAsJsonObject(); + double latitude = getValueFromMessageByName(msg, msgDataObj, config.getLatitudeKeyName()); + double longitude = getValueFromMessageByName(msg, msgDataObj, config.getLongitudeKeyName()); + List perimeters = getPerimeters(msg, msgDataObj); + boolean matches = false; + for (Perimeter perimeter : perimeters) { + if (checkMatches(perimeter, latitude, longitude)) { + matches = true; + break; + } + } + return matches; + } + + protected boolean checkMatches(Perimeter perimeter, double latitude, double longitude) throws TbNodeException { + if (perimeter.getPerimeterType() == PerimeterType.CIRCLE) { + Coordinates entityCoordinates = new Coordinates(latitude, longitude); + Coordinates perimeterCoordinates = new Coordinates(perimeter.getCenterLatitude(), perimeter.getCenterLongitude()); + return perimeter.getRange() > GeoUtil.distance(entityCoordinates, perimeterCoordinates, perimeter.getRangeUnit()); + } else if (perimeter.getPerimeterType() == PerimeterType.POLYGON) { + return GeoUtil.contains(perimeter.getPolygonsDefinition(), new Coordinates(latitude, longitude)); + } else { + throw new TbNodeException("Unsupported perimeter type: " + perimeter.getPerimeterType()); + } + } + + protected List getPerimeters(TbMsg msg, JsonObject msgDataObj) throws TbNodeException { + if (config.isFetchPerimeterInfoFromMessage()) { + //TODO: add fetching perimeters from the message itself, if configuration is empty. + if (!StringUtils.isEmpty(msg.getMetaData().getValue("perimeter"))) { + Perimeter perimeter = new Perimeter(); + perimeter.setPerimeterType(PerimeterType.POLYGON); + perimeter.setPolygonsDefinition(msg.getMetaData().getValue("perimeter")); + return Collections.singletonList(perimeter); + } else if (!StringUtils.isEmpty(msg.getMetaData().getValue("centerLatitude"))) { + Perimeter perimeter = new Perimeter(); + perimeter.setPerimeterType(PerimeterType.CIRCLE); + perimeter.setCenterLatitude(Double.parseDouble(msg.getMetaData().getValue("centerLatitude"))); + perimeter.setCenterLongitude(Double.parseDouble(msg.getMetaData().getValue("centerLongitude"))); + perimeter.setRange(Double.parseDouble(msg.getMetaData().getValue("range"))); + perimeter.setRangeUnit(RangeUnit.valueOf(msg.getMetaData().getValue("rangeUnit"))); + return Collections.singletonList(perimeter); + } else { + throw new TbNodeException("Missing perimeter definition!"); + } + } else { + return config.getPerimeters(); + } + } + + protected Double getValueFromMessageByName(TbMsg msg, JsonObject msgDataObj, String keyName) throws TbNodeException { + double value; + if (msgDataObj.has(keyName) && msgDataObj.get(keyName).isJsonPrimitive()) { + value = msgDataObj.get(keyName).getAsDouble(); + } else { + String valueStr = msg.getMetaData().getValue(keyName); + if (!StringUtils.isEmpty(valueStr)) { + value = Double.parseDouble(valueStr); + } else { + throw new TbNodeException("Incoming Message has no " + keyName + " in data or metadata!"); + } + } + return value; + } + + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Coordinates.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Coordinates.java new file mode 100644 index 0000000000..2dc4bcf6b2 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Coordinates.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import lombok.Data; + +@Data +public class Coordinates { + private final double latitude; + private final double longitude; +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/EntityGeofencingState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/EntityGeofencingState.java new file mode 100644 index 0000000000..4024cbf22e --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/EntityGeofencingState.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class EntityGeofencingState { + + private boolean inside; + private long stateSwitchTime; + private boolean staid; + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java new file mode 100644 index 0000000000..3e1918e6bf --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.locationtech.spatial4j.context.SpatialContext; +import org.locationtech.spatial4j.context.jts.JtsSpatialContext; +import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; +import org.locationtech.spatial4j.distance.DistanceUtils; +import org.locationtech.spatial4j.shape.Point; +import org.locationtech.spatial4j.shape.Shape; +import org.locationtech.spatial4j.shape.ShapeFactory; +import org.locationtech.spatial4j.shape.SpatialRelation; + +public class GeoUtil { + + private static final SpatialContext distCtx = SpatialContext.GEO; + private static final JtsSpatialContext jtsCtx; + + static { + JtsSpatialContextFactory factory = new JtsSpatialContextFactory(); + factory.normWrapLongitude = true; + jtsCtx = factory.newSpatialContext(); + } + + public static synchronized double distance(Coordinates x, Coordinates y, RangeUnit unit) { + Point xLL = distCtx.getShapeFactory().pointXY(x.getLongitude(), x.getLatitude()); + Point yLL = distCtx.getShapeFactory().pointXY(y.getLongitude(), y.getLatitude()); + return unit.fromKm(distCtx.getDistCalc().distance(xLL, yLL) * DistanceUtils.DEG_TO_KM); + } + + public static synchronized boolean contains(String polygon, Coordinates coordinates) { + ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon(); + JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray(); + boolean first = true; + double firstLat = 0.0; + double firstLng = 0.0; + for (JsonElement jsonElement : polygonArray) { + double lat = jsonElement.getAsJsonArray().get(0).getAsDouble(); + double lng = jsonElement.getAsJsonArray().get(1).getAsDouble(); + if (first) { + firstLat = lat; + firstLng = lng; + first = false; + } + polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(lng), jtsCtx.getShapeFactory().normY(lat)); + } + polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(firstLng), jtsCtx.getShapeFactory().normY(firstLat)); + Shape shape = polygonBuilder.buildOrRect(); + Point point = jtsCtx.makePoint(coordinates.getLongitude(), coordinates.getLatitude()); + return shape.relate(point).equals(SpatialRelation.CONTAINS); + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Perimeter.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Perimeter.java new file mode 100644 index 0000000000..6ae3372d9f --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/Perimeter.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import lombok.Data; + +@Data +public class Perimeter { + + private PerimeterType perimeterType; + + //For Polygons + private String polygonsDefinition; + + //For Circles + private Double centerLatitude; + private Double centerLongitude; + private Double range; + private RangeUnit rangeUnit; + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/PerimeterType.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/PerimeterType.java new file mode 100644 index 0000000000..8f9d86370d --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/PerimeterType.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +public enum PerimeterType { + CIRCLE, POLYGON +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/RangeUnit.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/RangeUnit.java new file mode 100644 index 0000000000..509d829552 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/RangeUnit.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +public enum RangeUnit { + METER(1000.0), KILOMETER(1.0), FOOT(3280.84), MILE(0.62137), NAUTICAL_MILE(0.539957); + + private final double fromKm; + + RangeUnit(double fromKm) { + this.fromKm = fromKm; + } + + public double fromKm(double v) { + return v * fromKm; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java new file mode 100644 index 0000000000..952109286f --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.msg.TbMsg; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Created by ashvayka on 19.01.18. + */ +@Slf4j +@RuleNode( + type = ComponentType.ACTION, + name = "gps geofencing events", + configClazz = TbGpsGeofencingFilterNodeConfiguration.class, + relationTypes = {"Entered", "Left", "Stays Inside", "Stays Outside"}, + nodeDescription = "Produces incoming messages using GPS based geofencing", + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters") +public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { + + private final Map entityStates = new HashMap<>(); + private final Gson gson = new Gson(); + private final JsonParser parser = new JsonParser(); + + @Override + public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { + boolean matches = checkMatches(msg); + long ts = System.currentTimeMillis(); + + EntityGeofencingState entityState = entityStates.computeIfAbsent(msg.getOriginator(), key -> { + try { + Optional entry = ctx.getAttributesService() + .find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getNodeId()) + .get(1, TimeUnit.MINUTES); + if (entry.isPresent()) { + JsonObject element = parser.parse(entry.get().getValueAsString()).getAsJsonObject(); + return new EntityGeofencingState(element.get("inside").getAsBoolean(), element.get("stateSwitchTime").getAsLong(), element.get("staid").getAsBoolean()); + } else { + return new EntityGeofencingState(false, 0L, false); + } + } catch (InterruptedException | TimeoutException | ExecutionException e) { + throw new RuntimeException(e); + } + }); + if (entityState.getStateSwitchTime() == 0L || entityState.isInside() != matches) { + switchState(ctx, msg.getOriginator(), entityState, matches, ts); + ctx.tellNext(msg, matches ? "Entered" : "Left"); + } else if (!entityState.isStaid()) { + long stayTime = ts - entityState.getStateSwitchTime(); + if (stayTime > (entityState.isInside() ? config.getMinInsideDuration() : config.getMinOutsideDuration())) { + setStaid(ctx, msg.getOriginator(), entityState); + } + } + } + + private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) { + entityState.setInside(matches); + entityState.setStateSwitchTime(ts); + entityState.setStaid(false); + persist(ctx, entityId, entityState); + } + + private void setStaid(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) { + entityState.setStaid(true); + persist(ctx, entityId, entityState); + } + + private void persist(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) { + JsonObject object = new JsonObject(); + object.addProperty("inside", entityState.isInside()); + object.addProperty("stateSwitchTime", entityState.getStateSwitchTime()); + object.addProperty("staid", entityState.isStaid()); + AttributeKvEntry entry = new BaseAttributeKvEntry(new StringDataEntry(ctx.getNodeId(), gson.toJson(object)), System.currentTimeMillis()); + List attributeKvEntryList = Collections.singletonList(entry); + ctx.getAttributesService().save(ctx.getTenantId(), entityId, DataConstants.SERVER_SCOPE, attributeKvEntryList); + } + + @Override + public void destroy() { + + } + + @Override + protected Class getConfigClazz() { + return TbGpsGeofencingActionNodeConfiguration.class; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java new file mode 100644 index 0000000000..dac9e7d23d --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import lombok.Data; +import org.thingsboard.rule.engine.api.NodeConfiguration; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Created by ashvayka on 19.01.18. + */ +@Data +public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilterNodeConfiguration { + + private double minInsideDuration; + private double minOutsideDuration; + + @Override + public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() { + TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration(); + configuration.setLatitudeKeyName("latitude"); + configuration.setLongitudeKeyName("longitude"); + configuration.setFetchPerimeterInfoFromMessage(true); + configuration.setPerimeters(Collections.emptyList()); + configuration.setMinInsideDuration(TimeUnit.MINUTES.toMillis(1)); + configuration.setMinOutsideDuration(TimeUnit.MINUTES.toMillis(1)); + return configuration; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java new file mode 100644 index 0000000000..66a23ca2a8 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.extern.slf4j.Slf4j; +import org.locationtech.spatial4j.context.jts.JtsSpatialContext; +import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; +import org.locationtech.spatial4j.shape.Point; +import org.locationtech.spatial4j.shape.Shape; +import org.locationtech.spatial4j.shape.ShapeFactory; +import org.locationtech.spatial4j.shape.SpatialRelation; +import org.springframework.util.StringUtils; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.filter.TbMsgTypeFilterNodeConfiguration; +import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.msg.TbMsg; + +import java.util.Collections; +import java.util.List; + +/** + * Created by ashvayka on 19.01.18. + */ +@Slf4j +@RuleNode( + type = ComponentType.FILTER, + name = "gps geofencing filter", + configClazz = TbGpsGeofencingFilterNodeConfiguration.class, + relationTypes = {"True", "False"}, + nodeDescription = "Filter incoming messages by GPS based geofencing", + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.") +public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { + + @Override + public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { + boolean matches = checkMatches(msg); + ctx.tellNext(msg, matches ? "True" : "False"); + } + + @Override + public void destroy() { + + } + + @Override + protected Class getConfigClazz() { + return TbGpsGeofencingFilterNodeConfiguration.class; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java new file mode 100644 index 0000000000..7d54699741 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import lombok.Data; +import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.msg.session.SessionMsgType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Created by ashvayka on 19.01.18. + */ +@Data +public class TbGpsGeofencingFilterNodeConfiguration implements NodeConfiguration { + + private String latitudeKeyName; + private String longitudeKeyName; + private boolean fetchPerimeterInfoFromMessage; + private List perimeters; + + @Override + public TbGpsGeofencingFilterNodeConfiguration defaultConfiguration() { + TbGpsGeofencingFilterNodeConfiguration configuration = new TbGpsGeofencingFilterNodeConfiguration(); + configuration.setLatitudeKeyName("latitude"); + configuration.setLongitudeKeyName("longitude"); + configuration.setFetchPerimeterInfoFromMessage(true); + configuration.setPerimeters(Collections.emptyList()); + return configuration; + } +} From 42d2f0be1032d874a1c276f83a8999ceaf08cd6e Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 29 Mar 2019 18:23:58 +0200 Subject: [PATCH 66/74] Geofencing rule nodes --- .../engine/geo/AbstractGeofencingNode.java | 13 +++++++++++- .../engine/geo/EntityGeofencingState.java | 2 +- .../engine/geo/TbGpsGeofencingActionNode.java | 20 ++++++++----------- ...bGpsGeofencingActionNodeConfiguration.java | 1 - .../engine/geo/TbGpsGeofencingFilterNode.java | 8 +------- ...bGpsGeofencingFilterNodeConfiguration.java | 13 ++++++++++-- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java index f876967917..2aec28f08b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java @@ -97,7 +97,14 @@ public abstract class AbstractGeofencingNode { @@ -68,7 +68,7 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode (entityState.isInside() ? config.getMinInsideDuration() : config.getMinOutsideDuration())) { setStaid(ctx, msg.getOriginator(), entityState); + ctx.tellNext(msg, entityState.isInside() ? "Inside" : "Outside"); } } } @@ -90,12 +91,12 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode attributeKvEntryList = Collections.singletonList(entry); ctx.getAttributesService().save(ctx.getTenantId(), entityId, DataConstants.SERVER_SCOPE, attributeKvEntryList); } - @Override - public void destroy() { - - } - @Override protected Class getConfigClazz() { return TbGpsGeofencingActionNodeConfiguration.class; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java index dac9e7d23d..f6b275e008 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java @@ -37,7 +37,6 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte configuration.setLatitudeKeyName("latitude"); configuration.setLongitudeKeyName("longitude"); configuration.setFetchPerimeterInfoFromMessage(true); - configuration.setPerimeters(Collections.emptyList()); configuration.setMinInsideDuration(TimeUnit.MINUTES.toMillis(1)); configuration.setMinOutsideDuration(TimeUnit.MINUTES.toMillis(1)); return configuration; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java index 66a23ca2a8..e816cb6cdc 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -55,13 +55,7 @@ public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode perimeters; + + private PerimeterType perimeterType; + + //For Polygons + private String polygonsDefinition; + + //For Circles + private Double centerLatitude; + private Double centerLongitude; + private Double range; + private RangeUnit rangeUnit; @Override public TbGpsGeofencingFilterNodeConfiguration defaultConfiguration() { @@ -41,7 +51,6 @@ public class TbGpsGeofencingFilterNodeConfiguration implements NodeConfiguration configuration.setLatitudeKeyName("latitude"); configuration.setLongitudeKeyName("longitude"); configuration.setFetchPerimeterInfoFromMessage(true); - configuration.setPerimeters(Collections.emptyList()); return configuration; } } From 0a668d0a6f61ec2f46e8f22412886c6e6057c37a Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 1 Apr 2019 13:22:33 +0300 Subject: [PATCH 67/74] add time-units to ActionNode and UI --- .../rule/engine/geo/AbstractGeofencingNode.java | 2 +- .../engine/geo/TbGpsGeofencingActionNode.java | 7 +++++-- .../TbGpsGeofencingActionNodeConfiguration.java | 15 ++++++++++----- .../engine/geo/TbGpsGeofencingFilterNode.java | 4 +++- .../TbGpsGeofencingFilterNodeConfiguration.java | 4 ++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java index 2aec28f08b..55cc53377b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java @@ -78,7 +78,7 @@ public abstract class AbstractGeofencingNode getPerimeters(TbMsg msg, JsonObject msgDataObj) throws TbNodeException { - if (config.isFetchPerimeterInfoFromMessage()) { + if (config.isFetchPerimeterInfoFromMessageMetadata()) { //TODO: add fetching perimeters from the message itself, if configuration is empty. if (!StringUtils.isEmpty(msg.getMetaData().getValue("perimeter"))) { Perimeter perimeter = new Perimeter(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java index 26a5d55a5f..b5211b7020 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -49,7 +49,9 @@ import java.util.concurrent.TimeoutException; configClazz = TbGpsGeofencingActionNodeConfiguration.class, relationTypes = {"Entered", "Left", "Inside", "Outside"}, nodeDescription = "Produces incoming messages using GPS based geofencing", - nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters") + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters", + uiResources = {"static/rulenode/rulenode-core-config.js"}, + configDirective = "tbActionNodeGpsGeofencingConfig") public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { private final Map entityStates = new HashMap<>(); @@ -81,7 +83,8 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode (entityState.isInside() ? config.getMinInsideDuration() : config.getMinOutsideDuration())) { + if (stayTime > (entityState.isInside() ? + TimeUnit.valueOf(config.getMinInsideDurationTimeUnit()).toMillis(config.getMinInsideDuration()) : TimeUnit.valueOf(config.getMinOutsideDurationTimeUnit()).toMillis(config.getMinOutsideDuration()))) { setStaid(ctx, msg.getOriginator(), entityState); ctx.tellNext(msg, entityState.isInside() ? "Inside" : "Outside"); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java index f6b275e008..ef183b6920 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java @@ -28,17 +28,22 @@ import java.util.concurrent.TimeUnit; @Data public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilterNodeConfiguration { - private double minInsideDuration; - private double minOutsideDuration; + private int minInsideDuration; + private int minOutsideDuration; + + private String minInsideDurationTimeUnit; + private String minOutsideDurationTimeUnit; @Override public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() { TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration(); configuration.setLatitudeKeyName("latitude"); configuration.setLongitudeKeyName("longitude"); - configuration.setFetchPerimeterInfoFromMessage(true); - configuration.setMinInsideDuration(TimeUnit.MINUTES.toMillis(1)); - configuration.setMinOutsideDuration(TimeUnit.MINUTES.toMillis(1)); + configuration.setFetchPerimeterInfoFromMessageMetadata(true); + configuration.setMinInsideDurationTimeUnit(TimeUnit.MINUTES.name()); + configuration.setMinOutsideDurationTimeUnit(TimeUnit.MINUTES.name()); + configuration.setMinInsideDuration(1); + configuration.setMinOutsideDuration(1); return configuration; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java index e816cb6cdc..ed62022edf 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -50,7 +50,9 @@ import java.util.List; configClazz = TbGpsGeofencingFilterNodeConfiguration.class, relationTypes = {"True", "False"}, nodeDescription = "Filter incoming messages by GPS based geofencing", - nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.") + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.", + uiResources = {"static/rulenode/rulenode-core-config.js"}, + configDirective = "tbFilterNodeGpsGeofencingConfig") public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java index ca2ac4c8d3..55957c9f1c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeConfiguration.java @@ -32,7 +32,7 @@ public class TbGpsGeofencingFilterNodeConfiguration implements NodeConfiguration private String latitudeKeyName; private String longitudeKeyName; - private boolean fetchPerimeterInfoFromMessage; + private boolean fetchPerimeterInfoFromMessageMetadata; private PerimeterType perimeterType; @@ -50,7 +50,7 @@ public class TbGpsGeofencingFilterNodeConfiguration implements NodeConfiguration TbGpsGeofencingFilterNodeConfiguration configuration = new TbGpsGeofencingFilterNodeConfiguration(); configuration.setLatitudeKeyName("latitude"); configuration.setLongitudeKeyName("longitude"); - configuration.setFetchPerimeterInfoFromMessage(true); + configuration.setFetchPerimeterInfoFromMessageMetadata(true); return configuration; } } From edcad249a313fc3c69f833efe43d6a654edd058a Mon Sep 17 00:00:00 2001 From: rodrigoabella Date: Mon, 11 Feb 2019 14:09:43 -0300 Subject: [PATCH 68/74] deviceName added to attribute response message --- .../server/transport/mqtt/adaptors/JsonMqttAdaptor.java | 4 ++-- .../server/transport/mqtt/adaptors/MqttTransportAdaptor.java | 2 +- .../transport/mqtt/session/GatewayDeviceSessionCtx.java | 2 +- .../server/common/transport/adaptor/JsonConverter.java | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 492564d2f4..a263c649b6 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -139,11 +139,11 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { + public Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { if (!StringUtils.isEmpty(responseMsg.getError())) { throw new AdaptorException(responseMsg.getError()); } else { - JsonObject result = JsonConverter.getJsonObjectForGateway(responseMsg); + JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result)); } } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 7c844a4a11..a6d746caf8 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -48,7 +48,7 @@ public interface MqttTransportAdaptor { Optional convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; - Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; + Optional convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, GetAttributeResponseMsg responseMsg) throws AdaptorException; Optional convertToPublish(MqttDeviceAwareSessionContext ctx, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index b196abc53a..2316cceec6 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -65,7 +65,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple @Override public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { try { - parent.getAdaptor().convertToGatewayPublish(this, response).ifPresent(parent::writeAndFlush); + parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), response).ifPresent(parent::writeAndFlush); } catch (Exception e) { log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 563a6160a6..eea2cec6ae 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -257,9 +257,10 @@ public class JsonConverter { return result; } - public static JsonObject getJsonObjectForGateway(TransportProtos.GetAttributeResponseMsg responseMsg) { + public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) { JsonObject result = new JsonObject(); result.addProperty("id", responseMsg.getRequestId()); + result.addProperty(DEVICE_PROPERTY, deviceName); if (responseMsg.getClientAttributeListCount() > 0) { addValues(result, responseMsg.getClientAttributeListList()); } From d3e041db134e4aa8141b8f39cba7481323bca1fe Mon Sep 17 00:00:00 2001 From: okolesnik Date: Fri, 29 Mar 2019 16:56:04 +0200 Subject: [PATCH 69/74] UI. Date-range-navigator-widget (init) --- .../data/json/system/widget_bundles/date.json | 25 + ui/package-lock.json | 671 +++++++++--------- ui/package.json | 1 + ui/src/app/api/widget.service.js | 5 +- ui/src/app/app.js | 3 + ui/src/app/locale/locale.constant-de_DE.json | 59 ++ ui/src/app/locale/locale.constant-en_US.json | 59 ++ ui/src/app/locale/locale.constant-es_ES.json | 59 ++ ui/src/app/locale/locale.constant-fa_IR.json | 59 ++ ui/src/app/locale/locale.constant-fr_FR.json | 59 ++ ui/src/app/locale/locale.constant-it_IT.json | 59 ++ ui/src/app/locale/locale.constant-ja_JA.json | 59 ++ ui/src/app/locale/locale.constant-ko_KR.json | 59 ++ ui/src/app/locale/locale.constant-ru_RU.json | 59 ++ ui/src/app/locale/locale.constant-tr_TR.json | 59 ++ ui/src/app/locale/locale.constant-zh_CN.json | 59 ++ .../date-range-navigator.js | 303 ++++++++ .../date-range-navigator.scss | 124 ++++ .../date-range-navigator.tpl.html | 87 +++ 19 files changed, 1538 insertions(+), 330 deletions(-) create mode 100644 application/src/main/data/json/system/widget_bundles/date.json create mode 100644 ui/src/app/widget/lib/date-range-navigator/date-range-navigator.js create mode 100644 ui/src/app/widget/lib/date-range-navigator/date-range-navigator.scss create mode 100644 ui/src/app/widget/lib/date-range-navigator/date-range-navigator.tpl.html diff --git a/application/src/main/data/json/system/widget_bundles/date.json b/application/src/main/data/json/system/widget_bundles/date.json new file mode 100644 index 0000000000..cdde7bcad3 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/date.json @@ -0,0 +1,25 @@ +{ + "widgetsBundle": { + "alias": "date", + "title": "Date", + "image": null + }, + "widgetTypes": [ + { + "alias": "date_range_navigator", + "name": "Date-range-navigator", + "descriptor": { + "type": "static", + "sizeX": 5, + "sizeY": 5.5, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"hidePicker\": {\n \"title\": \"Hide date range picker\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"onePanel\": {\n \"title\": \"Date range picker one panel\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"autoConfirm\": {\n \"title\": \"Date range picker auto confirm\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"showTemplate\": {\n \"title\": \"Date range picker show template\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"firstDayOfWeek\": {\n \"title\": \"First day of the week\",\n \"type\": \"number\",\n \"default\": 1\n },\n \"hideInterval\": {\n \"title\": \"Hide interval\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"initialInterval\": {\n\t\t\t\t\"title\": \"Initial interval\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"week\"\n\t\t\t},\n \"hideStepSize\": {\n \"title\": \"Hide step size\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"stepSize\": {\n\t\t\t\t\"title\": \"Initial step size\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"day\"\n\t\t\t},\n \"hideLabels\": {\n \"title\": \"Hide labels\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useSessionStorage\": {\n \"title\": \"Use session storage\",\n \"type\": \"boolean\",\n \"default\": true\n }\n }\n },\n \"form\": [\n \"hidePicker\",\n\t\t\"onePanel\",\n\t\t\"autoConfirm\",\n\t\t\"showTemplate\",\n\t\t\"firstDayOfWeek\",\n \"hideInterval\",\n {\n\t\t\t\"key\": \"initialInterval\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n \"hideStepSize\",\n {\n\t\t\t\"key\": \"stepSize\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t\"hideLabels\",\n\t\t\"useSessionStorage\"\n ]\n}", + "dataKeySettingsSchema": "{}\n", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + } + } + ] +} \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index 9b8224e0ff..d4b8b5c3e3 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -7,7 +7,7 @@ "@babel/code-frame": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "integrity": "sha1-BuKrGb21NThVWaq7W6WXKUgoAPg=", "dev": true, "requires": { "@babel/highlight": "^7.0.0" @@ -56,7 +56,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", "dev": true } } @@ -85,7 +85,7 @@ "@babel/helper-function-name": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "integrity": "sha1-oM6wFoX3M1XUNgwSR/WCv6/I/1M=", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", @@ -96,7 +96,7 @@ "@babel/helper-get-function-arity": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "integrity": "sha1-g1ctQyDipGVyY3NBE8QoaLZOScM=", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -105,7 +105,7 @@ "@babel/helper-split-export-declaration": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "integrity": "sha1-Oq4oXAMRwqsJXZl7jJqUytVH2BM=", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -125,7 +125,7 @@ "@babel/highlight": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "integrity": "sha1-9xDDjI1Fjm3ZogGvtjf8t4HOmeQ=", "dev": true, "requires": { "chalk": "^2.0.0", @@ -136,7 +136,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -145,7 +145,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -162,7 +162,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -194,7 +194,7 @@ "@babel/template": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", - "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", + "integrity": "sha1-CQSEpXT+9aLS13JqZ07O2lxbVkQ=", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -237,7 +237,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", "dev": true } } @@ -269,7 +269,7 @@ "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "integrity": "sha1-UkryQNGjYFJ7cwR17PoTRKpUDd4=", "dev": true, "requires": { "call-me-maybe": "^1.0.1", @@ -428,7 +428,7 @@ "angular-carousel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/angular-carousel/-/angular-carousel-1.1.0.tgz", - "integrity": "sha512-UiLMgT7Ueqk4xpliF1gWt4dYKXezdJA1jyZPNsUWkOGO/dwLuKi284h3BgWl4CnaH7kEBw8L2gsBOyqbYaumNQ==" + "integrity": "sha1-PmlA5ovRio85L8Qx2XGSrDSIMdE=" }, "angular-cookies": { "version": "1.5.8", @@ -449,7 +449,7 @@ } }, "angular-fullscreen": { - "version": "git://github.com/fabiobiondi/angular-fullscreen.git#119b7fbac911d154fd56ace38ebe3432475e8a20", + "version": "git://github.com/fabiobiondi/angular-fullscreen.git#8217174565761d3566807bc60a73b5ca015b8cb6", "from": "git://github.com/fabiobiondi/angular-fullscreen.git#master" }, "angular-gridster": { @@ -523,7 +523,7 @@ "angular-translate": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz", - "integrity": "sha512-Mw0kFBqsv5j8ItL9IhRZunIlVmIRW6iFsiTmRs9wGr2QTt8z4rehYlWyHos8qnXc/kyOYJiW50iH50CSNHGB9A==", + "integrity": "sha1-sp7Q0vm6xEB156rTKEFmxZ4VB5E=", "requires": { "angular": ">=1.2.26 <=1.7" } @@ -531,7 +531,7 @@ "angular-translate-handler-log": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate-handler-log/-/angular-translate-handler-log-2.18.1.tgz", - "integrity": "sha512-TyKzCW4GubNazwCgLpCVXd2212CWdZOckf+aL5+gLuThPhVpOvlg18RSmz8MNPto3kwCcCw3LzShlZ6RX/MQRA==", + "integrity": "sha1-icu1mCeALYb4EVJ1+/iNbYiWsNQ=", "requires": { "angular-translate": "~2.18.1" } @@ -539,7 +539,7 @@ "angular-translate-interpolation-messageformat": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate-interpolation-messageformat/-/angular-translate-interpolation-messageformat-2.18.1.tgz", - "integrity": "sha512-SlmyxLB/UUy7FWoGx5QJHrhq8fUu/xzCR0h/ngexOtXZopQjs1vm+TrFZ69d4c/LI7C91sfP4mq4ES29o1xCxA==", + "integrity": "sha1-FsUq4MYcJA8PJBZKBSGUPPi6QI4=", "requires": { "angular-translate": "~2.18.1", "messageformat": "~1.0.2" @@ -548,7 +548,7 @@ "angular-translate-loader-static-files": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate-loader-static-files/-/angular-translate-loader-static-files-2.18.1.tgz", - "integrity": "sha512-5MuyzAROfc493kjLjKlLGLBzXiRmZIFbcWZGutDRxW5SRXSpwrH0u0hh0ENNnUyUQbe2vUspHNPIuZqlq8qIhw==", + "integrity": "sha1-rQw8iDsYsIm9uNsCu9Nm2QP4V8w=", "requires": { "angular-translate": "~2.18.1" } @@ -556,7 +556,7 @@ "angular-translate-storage-cookie": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate-storage-cookie/-/angular-translate-storage-cookie-2.18.1.tgz", - "integrity": "sha512-wiMaF/0OGN/3ilaYunfsqdLNpfGZEJK0fj4zT8yjD3XPq7Q9kM88xZ4XJiWKgodZShBljGCRzqgQbKMF7d1MLw==", + "integrity": "sha1-j8vaspb6gkkOALQorxp0ahf0QVY=", "requires": { "angular-cookies": ">=1.2.26 <1.8", "angular-translate": "~2.18.1" @@ -565,7 +565,7 @@ "angular-translate-storage-local": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate-storage-local/-/angular-translate-storage-local-2.18.1.tgz", - "integrity": "sha512-zPxcbIJ8tdWXtWNKLtaswynKid0w5le6WPMwiLWhgKPnyzOp/y5WLBW+JEfnZnkGE24yOGhJ6jVPgRNzelLgzg==", + "integrity": "sha1-lHQP5NgBq3gpopofBeHDkFTIcwM=", "requires": { "angular-translate": "~2.18.1", "angular-translate-storage-cookie": "~2.18.1" @@ -632,7 +632,7 @@ "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", "dev": true, "requires": { "micromatch": "^2.1.5", @@ -675,7 +675,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -693,7 +693,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", "dev": true }, "arr-union": { @@ -838,7 +838,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", "dev": true }, "attr-accept": { @@ -954,7 +954,7 @@ "babel-core": { "version": "6.26.3", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "integrity": "sha1-suLwnjQtDwyI4vAuBneUEl51wgc=", "dev": true, "requires": { "babel-code-frame": "^6.26.0", @@ -994,7 +994,7 @@ "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "integrity": "sha1-GERAjTuPDTWkBOp6wYDwh6YBvZA=", "dev": true, "requires": { "babel-messages": "^6.23.0", @@ -1282,7 +1282,7 @@ "babel-plugin-transform-es2015-modules-commonjs": { "version": "6.26.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "integrity": "sha1-WKeThjqefKhwvcWogRF/+sJ9tvM=", "dev": true, "requires": { "babel-plugin-transform-strict-mode": "^6.24.1", @@ -1606,13 +1606,13 @@ "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", "dev": true }, "bail": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", - "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==", + "integrity": "sha1-Y8+53brIKbAqMSjNUyJL545sIaM=", "dev": true }, "balanced-match": { @@ -1623,7 +1623,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -1647,7 +1647,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1656,7 +1656,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1665,7 +1665,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -1682,7 +1682,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -1690,7 +1690,7 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM=" }, "batch": { "version": "0.6.1", @@ -1716,7 +1716,7 @@ "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", "dev": true }, "bin-build": { @@ -1886,6 +1886,7 @@ "resolved": "http://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, + "optional": true, "requires": { "hoek": "2.x.x" } @@ -2088,7 +2089,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -2201,7 +2202,7 @@ "canvas-gauges": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/canvas-gauges/-/canvas-gauges-2.1.5.tgz", - "integrity": "sha512-7GUd1uukePQPQPIoM8Sh4UrG8om+2RG+D8WN5BCkIp9wAfByzPuZZinsUkfFCyRrEOZ/rhuwBfFnb1ld8IfNrw==" + "integrity": "sha1-YuRhKAzPfqolRIYpETAomnkKfT8=" }, "capture-stack-trace": { "version": "1.0.1", @@ -2238,7 +2239,7 @@ "ccount": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", - "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==", + "integrity": "sha1-8c7EPzMuLqWlaf1G+fW95OYQKv8=", "dev": true }, "center-align": { @@ -2276,25 +2277,25 @@ "character-entities": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", - "integrity": "sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ==", + "integrity": "sha1-WMjzccB3TvC6myrKXwDY8QDm42M=", "dev": true }, "character-entities-html4": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.2.tgz", - "integrity": "sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw==", + "integrity": "sha1-xE/d485mtS6NMh1sG/RhAfAVBhA=", "dev": true }, "character-entities-legacy": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz", - "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA==", + "integrity": "sha1-fG3vuBZISYIiyYVTCZU9BfTWOpw=", "dev": true }, "character-reference-invalid": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", - "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ==", + "integrity": "sha1-IeQhrT2EBVlS2rSkOgTnPNQl0+0=", "dev": true }, "chardet": { @@ -2328,13 +2329,13 @@ "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "integrity": "sha1-gVyZ6oT2gJUp0vRXkb34JxE1LWY=", "dev": true }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", "dev": true, "requires": { "chalk": "^1.1.3" @@ -2343,7 +2344,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -2455,7 +2456,7 @@ "clone-regexp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", - "integrity": "sha512-Fcij9IwRW27XedRIJnSOEupS7RVcXtObJXbcUOX93UCLqqOdRpkvzKywOOSizmEK/Is3S/RHX9dLdfo6R1Q1mw==", + "integrity": "sha1-BRgFzTMXM3XYIRj8CRhgbaOf1g8=", "dev": true, "requires": { "is-regexp": "^1.0.0", @@ -2492,7 +2493,7 @@ "collapse-white-space": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", - "integrity": "sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw==", + "integrity": "sha1-zgXPSeVMMneuVzA2omhRukMKAJE=", "dev": true }, "collection-visit": { @@ -2543,7 +2544,7 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "integrity": "sha1-k4NDeaHMmgxh+C9S8NBDIiUb1aI=", "dev": true }, "colormin": { @@ -2634,7 +2635,7 @@ "compression-webpack-plugin": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-1.1.12.tgz", - "integrity": "sha512-UpBXSHbrCSdSZieAffqXlAQpLO2fikVVRYibrWlbHYzKpOw1Y4jwkVZ/+S91GzWuJvXSbc8SBy/e8fQJh8uEMQ==", + "integrity": "sha1-vs0q7GIKzpa7P+mkKlXPSKzItNQ=", "dev": true, "requires": { "cacache": "^10.0.1", @@ -2683,7 +2684,7 @@ "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -2749,7 +2750,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "convert-source-map": { @@ -2776,7 +2777,7 @@ "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", "dev": true, "requires": { "aproba": "^1.1.1", @@ -2854,13 +2855,13 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", "dev": true }, "js-yaml": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "integrity": "sha1-6u1lbsg0TxD1J8a/obbiJE3hZ9E=", "dev": true, "requires": { "argparse": "^1.0.7", @@ -2891,7 +2892,7 @@ "create-react-class": { "version": "15.6.3", "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", - "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "integrity": "sha1-LXMjf7P5cK5uvgEanmb0bbyoADY=", "requires": { "fbjs": "^0.8.9", "loose-envify": "^1.3.1", @@ -3138,7 +3139,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -3447,7 +3448,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -3457,7 +3458,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3466,7 +3467,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3475,7 +3476,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -3492,7 +3493,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -3512,7 +3513,7 @@ "delegate": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" }, "delegates": { "version": "1.0.0", @@ -3544,7 +3545,7 @@ "dir-glob": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "integrity": "sha1-CyBdK2rvmCOMooZZioIE0p0KADQ=", "dev": true, "requires": { "arrify": "^1.0.1", @@ -3554,7 +3555,7 @@ "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", "dev": true, "requires": { "pify": "^3.0.0" @@ -3571,7 +3572,7 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3631,7 +3632,7 @@ "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", "dev": true }, "domelementtype": { @@ -3662,7 +3663,7 @@ "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "integrity": "sha1-HxngwuGqDjJ5fEl5nyg3rGr2nFc=", "dev": true, "requires": { "is-obj": "^1.0.0" @@ -3794,7 +3795,7 @@ "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, "requires": { "once": "^1.4.0" @@ -3828,7 +3829,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", "dev": true, "requires": { "prr": "~1.0.1" @@ -4115,7 +4116,7 @@ "eslint-loader": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.9.0.tgz", - "integrity": "sha512-40aN976qSNPyb9ejTqjEthZITpls1SVKtwguahmH1dzGCwQU/vySE+xX33VZmD8csU0ahVNCtFlsPgKqRBiqgg==", + "integrity": "sha1-fhvp/t3KMo09z67xrUnVvv/oOhM=", "dev": true, "requires": { "loader-fs-cache": "^1.0.0", @@ -4199,7 +4200,7 @@ "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", "dev": true, "requires": { "estraverse": "^4.0.0" @@ -4208,7 +4209,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -4255,7 +4256,7 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "events": { @@ -4276,7 +4277,7 @@ "exec-buffer": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", - "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "integrity": "sha1-sWhtvZBMfPmC5lLB9aebHlVzCCs=", "dev": true, "requires": { "execa": "^0.7.0", @@ -4411,7 +4412,7 @@ "ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "integrity": "sha1-C5jmTtgvWs8PKTG6v2khLvUt3Tc=", "dev": true, "requires": { "mime-db": "^1.28.0" @@ -4420,7 +4421,7 @@ "ext-name": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "integrity": "sha1-cHgZgdGD7hXROZPIgiBFxQbI8KY=", "dev": true, "requires": { "ext-list": "^2.0.0", @@ -4554,7 +4555,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -4656,7 +4657,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -4667,7 +4668,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -4675,7 +4676,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -4755,7 +4756,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4764,7 +4765,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4773,7 +4774,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -4825,13 +4826,13 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -4967,7 +4968,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", "dev": true, "requires": { "is-number": "^2.1.0", @@ -5060,7 +5061,7 @@ "flush-write-stream": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "integrity": "sha1-xdWG7zivYJdlC0m8QbVfq7GfNb0=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -5079,7 +5080,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -5159,7 +5160,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", "dev": true }, "fs-extra": { @@ -5178,7 +5179,7 @@ "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "integrity": "sha1-4y/AMKLM7kSmtTcTCNpUvgs5fSc=", "dev": true }, "fs-write-stream-atomic": { @@ -5742,7 +5743,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", "dev": true }, "gauge": { @@ -6036,7 +6037,7 @@ "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", "dev": true }, "globby": { @@ -6096,7 +6097,7 @@ "glogg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", - "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", + "integrity": "sha1-3PdY5EeJzD89MsHzVio2duajSBA=", "dev": true, "requires": { "sparkles": "^1.0.0" @@ -6105,7 +6106,7 @@ "gonzales-pe": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.2.3.tgz", - "integrity": "sha512-Kjhohco0esHQnOiqqdJeNz/5fyPkOMD/d6XVjwTAoPGUFh0mCollPUTUTa2OZy4dYNAqlPIQdTiNzJTWdd9Htw==", + "integrity": "sha1-QQkXA2JUMyheCu46pHgp/B++tvI=", "dev": true, "requires": { "minimist": "1.1.x" @@ -6348,13 +6349,13 @@ "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "integrity": "sha1-FAn5i8ACR9pF2mfO4KNvKC/yZFU=", "dev": true }, "has-to-string-tag-x": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "integrity": "sha1-oEWrOD17SyASoAFIqwql8pAETU0=", "dev": true, "requires": { "has-symbol-support-x": "^1.4.1" @@ -6652,7 +6653,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -6776,7 +6777,7 @@ "imagemin-gifsicle": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-5.2.0.tgz", - "integrity": "sha512-K01m5QuPK+0en8oVhiOOAicF7KjrHlCZxS++mfLI2mV/Ksfq/Y9nCXCWDz6jRv13wwlqe5T7hXT+ji2DnLc2yQ==", + "integrity": "sha1-N4FSTEV2Eu8EkWrzQkGitCv8tAo=", "dev": true, "requires": { "exec-buffer": "^3.0.0", @@ -6809,7 +6810,7 @@ "imagemin-pngquant": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/imagemin-pngquant/-/imagemin-pngquant-5.1.0.tgz", - "integrity": "sha512-RtIUPbp8/HYX5EKY6p/L1NLKnkxNj37I92IFNsrptzBVql8FqBgPra9DO/eUgE4EWx+zq6ih4a/Y9YhF3pNM5A==", + "integrity": "sha1-uetWPZ5qOHb2JIvgBhuhsO8mnAc=", "dev": true, "requires": { "execa": "^0.10.0", @@ -6821,7 +6822,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -6834,7 +6835,7 @@ "execa": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "integrity": "sha1-/0Vqj1P5D47MxxqW0Rvfx/CCy1A=", "dev": true, "requires": { "cross-spawn": "^6.0.0", @@ -6851,7 +6852,7 @@ "imagemin-svgo": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-5.2.4.tgz", - "integrity": "sha512-1bNZdlWVKdfxzu0xDD1pWjwK/G8FLcztUh/GWaI7xLgCFrn0j35o+uBbY7VcdY2AmKgiLYTXhrzrbkQk6xj8aA==", + "integrity": "sha1-bNXTQsrkvNi0g1lOUxVpXfArnps=", "dev": true, "requires": { "is-svg": "^2.0.0", @@ -6930,7 +6931,7 @@ "import-lazy": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", - "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "integrity": "sha1-iRJ5ICyKIoD9vWZ029jaGh38Z8w=", "dev": true }, "imurmurhash": { @@ -6983,7 +6984,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=" }, "inline-style-prefixer": { "version": "2.0.5", @@ -7023,7 +7024,7 @@ "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -7074,7 +7075,7 @@ "is-alphabetical": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz", - "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg==", + "integrity": "sha1-H6bkkhPLeIW3XRWGL7Pz2WyIT0E=", "dev": true }, "is-alphanumeric": { @@ -7086,7 +7087,7 @@ "is-alphanumerical": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz", - "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==", + "integrity": "sha1-ETjprlBAFY3G/3a4IKzWt6GB/UA=", "dev": true, "requires": { "is-alphabetical": "^1.0.0", @@ -7111,7 +7112,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, "is-builtin-module": { @@ -7141,13 +7142,13 @@ "is-decimal": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", - "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg==", + "integrity": "sha1-iUZi1qhwnTB/OidspDOcj6Xf8P8=", "dev": true }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -7158,7 +7159,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -7234,7 +7235,7 @@ "is-hexadecimal": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", - "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==", + "integrity": "sha1-tucQ19B7tmuYy4zs5cm0kh3uuDU=", "dev": true }, "is-jpg": { @@ -7246,7 +7247,7 @@ "is-my-ip-valid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "integrity": "sha1-ezUbjo7dTTmV1NBmaA5mTZRpaCQ=", "dev": true }, "is-my-json-valid": { @@ -7298,7 +7299,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { "isobject": "^3.0.1" @@ -7362,7 +7363,7 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", "dev": true }, "is-retry-allowed": { @@ -7379,7 +7380,7 @@ "is-supported-regexp-flag": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz", - "integrity": "sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ==", + "integrity": "sha1-Ie4WUY0sHdPt0+mg1X5QIHrDZMo=", "dev": true }, "is-svg": { @@ -7406,7 +7407,7 @@ "is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "integrity": "sha1-BKTfRtKMTP89c9Af8Gq+sxihqlI=", "dev": true }, "is-utf8": { @@ -7424,19 +7425,19 @@ "is-whitespace-character": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", - "integrity": "sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ==", + "integrity": "sha1-7eU7TG9vs4dFM3UeySgNAZKNA+0=", "dev": true }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", "dev": true }, "is-word-character": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.2.tgz", - "integrity": "sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA==", + "integrity": "sha1-RqXaw/KhhAiYuR5XbNQNST865VM=", "dev": true }, "is-zip": { @@ -7484,7 +7485,7 @@ "isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=", "dev": true, "requires": { "has-to-string-tag-x": "^1.2.0", @@ -7510,7 +7511,7 @@ "jquery": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + "integrity": "sha1-lYzinoHJeQ8xvneS311NlfxX+8o=" }, "jquery.terminal": { "version": "1.23.2", @@ -7581,7 +7582,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", "dev": true }, "json-schema": { @@ -7598,7 +7599,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "json-stable-stringify": { @@ -7747,7 +7748,7 @@ "less": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", - "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "integrity": "sha1-zBJg9RyQCp7A2R+2mYE54CUHtjs=", "dev": true, "requires": { "errno": "^0.1.1", @@ -8020,7 +8021,7 @@ "lodash.merge": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" + "integrity": "sha1-rcJdnLmbk5HFliTzefu6YNcRHVQ=" }, "lodash.mergewith": { "version": "4.6.1", @@ -8090,7 +8091,7 @@ "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "integrity": "sha1-V0Dhxdbw39pK2TI7UzIQfva0xAo=", "dev": true, "requires": { "chalk": "^2.0.1" @@ -8099,7 +8100,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -8108,7 +8109,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -8125,7 +8126,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8164,7 +8165,7 @@ "longest-streak": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", - "integrity": "sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==", + "integrity": "sha1-JCG2upOaRDu5/+v1llhaULTDji4=", "dev": true }, "loose-envify": { @@ -8194,7 +8195,7 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", "dev": true }, "lpad-align": { @@ -8221,7 +8222,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "requires": { "pify": "^3.0.0" @@ -8259,13 +8260,13 @@ "markdown-escapes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.2.tgz", - "integrity": "sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA==", + "integrity": "sha1-5jnL3nuZyEHAusyKB5goc7RtISI=", "dev": true }, "markdown-table": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.2.tgz", - "integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==", + "integrity": "sha1-x425SPqHmQOkG85SLjuW+AHGN4Y=", "dev": true }, "material-design-icons": { @@ -8317,7 +8318,7 @@ "mathml-tag-names": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz", - "integrity": "sha512-3Zs9P/0zzwTob2pdgT0CHZuMbnSUSp8MB1bddfm+HDmnFWHGT4jvEZRf+2RuPoa+cjdn/z25SEt5gFTqdhvJAg==", + "integrity": "sha1-SQtw4GLuJGNlNuPZSB4zNzPQDyw=", "dev": true }, "md-color-picker": { @@ -8328,6 +8329,18 @@ "tinycolor2": "*" } }, + "md-date-range-picker": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/md-date-range-picker/-/md-date-range-picker-0.8.4.tgz", + "integrity": "sha512-TgLyozMJypi92yvXaljLcermTFhd1+0rlaVwV+Duo0EplbKfDJfFF3WohWhB7VmPwJNP//o44sUlecY+r/ZvXA==", + "requires": { + "angular": "^1.5.8", + "angular-animate": "^1.5.8", + "angular-aria": "^1.5.8", + "angular-material": "^1.1.0", + "angular-messages": "^1.5.8" + } + }, "mdPickers": { "version": "git://github.com/alenaksu/mdPickers.git#72592ae51c81a7260701055ea21870efa57fa7c8", "from": "git://github.com/alenaksu/mdPickers.git#0.7.5" @@ -8335,7 +8348,7 @@ "mdast-util-compact": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz", - "integrity": "sha512-d2WS98JSDVbpSsBfVvD9TaDMlqPRz7ohM/11G0rp5jOBb5q96RJ6YLszQ/09AAixyzh23FeIpCGqfaamEADtWg==", + "integrity": "sha1-wS6+Fv/8hFc9Phl2dybeIm6V9kk=", "dev": true, "requires": { "unist-util-visit": "^1.1.0" @@ -8411,7 +8424,7 @@ "messageformat-parser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-1.1.0.tgz", - "integrity": "sha512-Hwem6G3MsKDLS1FtBRGIs8T50P1Q00r3srS6QJePCFbad9fq0nYxwf3rnU2BreApRGhmpKMV7oZI06Sy1c9TPA==" + "integrity": "sha1-E7oiUKdrvejg/KDbs0dflcWUqQo=" }, "methods": { "version": "1.1.2", @@ -8443,7 +8456,7 @@ "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", "dev": true }, "mime-db": { @@ -8464,7 +8477,7 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=" }, "mimic-response": { "version": "1.0.1", @@ -8484,7 +8497,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { "brace-expansion": "^1.1.7" } @@ -8497,7 +8510,7 @@ "minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "integrity": "sha1-+6TIGRM54T7PTWG+sD8HAQPz2VQ=", "dev": true, "requires": { "arrify": "^1.0.1", @@ -8507,7 +8520,7 @@ "mississippi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "integrity": "sha1-NEKlCPr8KFAEhv7qmUCWduTuWm8=", "dev": true, "requires": { "concat-stream": "^1.5.0", @@ -8525,7 +8538,7 @@ "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { "for-in": "^1.0.2", @@ -8535,7 +8548,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -8641,7 +8654,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -8672,7 +8685,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -8815,7 +8828,7 @@ "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", "dev": true, "requires": { "lower-case": "^1.1.1" @@ -8833,7 +8846,7 @@ "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "integrity": "sha1-mA9vcthSEaU0fGsrwYxbhMPrR+8=", "requires": { "encoding": "^0.1.11", "is-stream": "^1.0.1" @@ -9275,7 +9288,7 @@ "npm-conf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", "dev": true, "requires": { "config-chain": "^1.1.11", @@ -9610,7 +9623,7 @@ "osenv": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=", "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -9630,7 +9643,7 @@ "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "integrity": "sha1-ueEjgAvOu3rBOkeb4ZW1B7mNMPo=", "dev": true }, "p-event": { @@ -9651,7 +9664,7 @@ "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", "dev": true, "requires": { "p-try": "^1.0.0" @@ -9731,7 +9744,7 @@ "parse-entities": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz", - "integrity": "sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g==", + "integrity": "sha1-nerAh2YbLjaBQVPLeNflSkxf1vQ=", "dev": true, "requires": { "character-entities": "^1.0.0", @@ -10237,7 +10250,7 @@ "postcss": { "version": "5.2.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", "dev": true, "requires": { "chalk": "^1.1.3", @@ -10347,7 +10360,7 @@ "postcss-html": { "version": "0.34.0", "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.34.0.tgz", - "integrity": "sha512-BIW982Kbf9/RikInNhNS3/GA6x/qY/+jhVS9KumqXZtU9ss8Yq15HhPJ6mnaXcU5bFq2ULxpOv96mHPAErpGMQ==", + "integrity": "sha1-m/1jetjD06Q2JbXvhE3IBLM3CGg=", "dev": true, "requires": { "htmlparser2": "^3.9.2" @@ -10362,7 +10375,7 @@ "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", "dev": true, "requires": { "domelementtype": "1" @@ -10471,7 +10484,7 @@ "postcss-load-config": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "integrity": "sha1-8TEt2/WRLNdHF3CDxe96GdYu5IQ=", "dev": true, "requires": { "cosmiconfig": "^4.0.0", @@ -10481,7 +10494,7 @@ "postcss-loader": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "integrity": "sha1-a5eUPkfHLYRfqeA/Jzdz1OjdbC0=", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -10493,7 +10506,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -10502,7 +10515,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -10541,13 +10554,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -10558,7 +10571,7 @@ "postcss-markdown": { "version": "0.34.0", "resolved": "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-0.34.0.tgz", - "integrity": "sha512-cKPggF9OMOKPoqDm5YpYszCqMsImFh78FK6P8p6IsEKZB6IkUJYKz0/QgadYy4jLb60jcFIHJ6v6jsMH7/ZQrA==", + "integrity": "sha1-egQ+bu46uEakzv46tD0UEDji2nk=", "dev": true, "requires": { "remark": "^9.0.0", @@ -10667,7 +10680,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -10676,7 +10689,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -10704,7 +10717,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { @@ -10731,7 +10744,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -10740,7 +10753,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -10790,7 +10803,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { @@ -10817,7 +10830,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -10826,7 +10839,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -10876,7 +10889,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { @@ -10903,7 +10916,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -10912,7 +10925,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -10940,7 +10953,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { @@ -11018,7 +11031,7 @@ "postcss-reporter": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.0.tgz", - "integrity": "sha512-5xQXm1UPWuFObjbtyQzWvQaupru8yFcFi4HUlm6OPo1o2bUszYASuqRJ7bVArb3svGCdbYtqdMBKrqR1Aoy+tw==", + "integrity": "sha1-RMhzEp2MAppDC20hhiENecjeiLg=", "dev": true, "requires": { "chalk": "^2.0.1", @@ -11030,7 +11043,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -11039,7 +11052,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -11067,13 +11080,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -11090,7 +11103,7 @@ "postcss-safe-parser": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", - "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", + "integrity": "sha1-h1bZ5MNv3OLHKwkbvIyhdqsfzeo=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -11099,7 +11112,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -11108,7 +11121,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -11136,13 +11149,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -11163,7 +11176,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -11172,7 +11185,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -11200,13 +11213,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -11217,7 +11230,7 @@ "postcss-scss": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.0.0.tgz", - "integrity": "sha512-um9zdGKaDZirMm+kZFKKVsnKPF7zF7qBAtIfTSnZXD1jZ0JNZIxdB6TxQOjCnlSzLRInVl2v3YdBh/M881C4ug==", + "integrity": "sha1-JIsKKK936nsysQEaug9zi9on3qE=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -11226,7 +11239,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -11235,7 +11248,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -11263,13 +11276,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -11301,7 +11314,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -11310,7 +11323,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -11338,13 +11351,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -11355,7 +11368,7 @@ "postcss-styled": { "version": "0.34.0", "resolved": "https://registry.npmjs.org/postcss-styled/-/postcss-styled-0.34.0.tgz", - "integrity": "sha512-Uaeetr/xOiQWGJgzPFOr32/Bwykpfh9TVE26OpmwDb8eEN205TS/gqkt9ri+C6otQzQKXqbMfeZNbKYi7QpeNA==", + "integrity": "sha1-B9R7yxNwcol4KqBYYF/Z/q+EOR0=", "dev": true }, "postcss-svgo": { @@ -11373,7 +11386,7 @@ "postcss-syntax": { "version": "0.34.0", "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.34.0.tgz", - "integrity": "sha512-L36NZwq2UK743US+vl1CRMdBRZCBmFYfThP9n9jCFhX1Wfk6BqnRSgt0Fy8q44IwxPee/GCzlo7T1c1JIeUDlQ==", + "integrity": "sha1-SoXAIvHN7OpyECd1yRrx5/UG2Do=", "dev": true }, "postcss-unique-selectors": { @@ -11456,7 +11469,7 @@ "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", "dev": true }, "process": { @@ -11480,7 +11493,7 @@ "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", "requires": { "asap": "~2.0.3" } @@ -11535,7 +11548,7 @@ "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -11545,7 +11558,7 @@ "pumpify": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", "dev": true, "requires": { "duplexify": "^3.6.0", @@ -11617,7 +11630,7 @@ "ramda": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz", - "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", + "integrity": "sha1-j99oIxz/qQvC+UYDkKDLdKKbKak=", "dev": true }, "randomatic": { @@ -11634,13 +11647,13 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -11727,7 +11740,7 @@ "rc-menu": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-5.1.4.tgz", - "integrity": "sha512-ZUkUNda70GtTXcQDiO3rSDdk3sgIwDwzPUm5dVM8nRH/j84qv0BVBkIUwIBu8+s+G3G9lWLurRqh22dCqZPeOA==", + "integrity": "sha1-5d8I/ouDPoFGkTX/E7MKuPIf88Y=", "requires": { "babel-runtime": "6.x", "classnames": "2.x", @@ -11758,7 +11771,7 @@ "rc-trigger": { "version": "1.11.5", "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-1.11.5.tgz", - "integrity": "sha512-MBuUPw1nFzA4K7jQOwb7uvFaZFjXGd00EofUYiZ+l/fgKVq8wnLC0lkv36kwqM7vfKyftRo2sh7cWVpdPuNnnw==", + "integrity": "sha1-+I+fhODnn44O8cjRv4rCIItxViA=", "requires": { "babel-runtime": "6.x", "create-react-class": "15.x", @@ -11860,7 +11873,7 @@ "react-hot-loader": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-3.1.3.tgz", - "integrity": "sha512-d7nZf78irxoGN5PY4zd6CSgZiroOhvIWzRast3qwTn4sSnBwlt08kV8WMQ9mitmxEdlCTwZt+5ClrRSjxWguMQ==", + "integrity": "sha1-b5KHcyaVjHywE0tRJHRReGkSYII=", "dev": true, "requires": { "global": "^4.3.0", @@ -11873,7 +11886,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -11914,7 +11927,7 @@ "react-transition-group": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz", - "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==", + "integrity": "sha1-4R9yslf5IbITIpp3TfRmEjRsfKY=", "requires": { "chain-function": "^1.0.0", "dom-helpers": "^3.2.0", @@ -11926,7 +11939,7 @@ "reactcss": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", - "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "integrity": "sha1-wAATh15Vexzw39mjaKHD2rO1SN0=", "requires": { "lodash": "^4.0.1" } @@ -12335,7 +12348,7 @@ "redbox-react": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/redbox-react/-/redbox-react-1.6.0.tgz", - "integrity": "sha512-mLjM5eYR41yOp5YKHpd3syFeGq6B4Wj5vZr64nbLvTZW5ZLff4LYk7VE4ITpVxkZpCY6OZuqh0HiP3A3uEaCpg==", + "integrity": "sha1-51OsAllbwb9pWzk1iJpPWxtaIaE=", "dev": true, "requires": { "error-stack-parser": "^1.3.6", @@ -12393,7 +12406,7 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "integrity": "sha1-SoVuxLVuQHfFV1icroXnpMiGmhE=", "dev": true }, "regenerator-runtime": { @@ -12404,7 +12417,7 @@ "regenerator-transform": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "integrity": "sha1-HkmWg3Ix2ot/PPQRTXG1aRoGgN0=", "dev": true, "requires": { "babel-runtime": "^6.18.0", @@ -12415,7 +12428,7 @@ "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", "dev": true, "requires": { "is-equal-shallow": "^0.1.3" @@ -12424,7 +12437,7 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { "extend-shallow": "^3.0.2", @@ -12474,7 +12487,7 @@ "remark": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", - "integrity": "sha512-amw8rGdD5lHbMEakiEsllmkdBP+/KpjW/PRK6NSGPZKCQowh0BT4IWXDAkRMyG3SB9dKPXWMviFjNusXzXNn3A==", + "integrity": "sha1-xc+o7FNcc6Z8Sw8Sv9vTpn2LL2A=", "dev": true, "requires": { "remark-parse": "^5.0.0", @@ -12485,7 +12498,7 @@ "remark-parse": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", - "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "integrity": "sha1-TAd/nkmQRNHVwT+A16mM97koXZU=", "dev": true, "requires": { "collapse-white-space": "^1.0.2", @@ -12508,7 +12521,7 @@ "remark-stringify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-5.0.0.tgz", - "integrity": "sha512-Ws5MdA69ftqQ/yhRF9XhVV29mhxbfGhbz0Rx5bQH+oJcNhhSM6nCu1EpLod+DjrFGrU0BMPs+czVmJZU7xiS7w==", + "integrity": "sha1-M206TUpqM5DZM+66YujeS9KAr7o=", "dev": true, "requires": { "ccount": "^1.0.0", @@ -12632,7 +12645,7 @@ "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "integrity": "sha1-iaf92TgmEmcxjq/hT5wy5ZjDaQk=", "dev": true }, "require-main-filename": { @@ -12705,7 +12718,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", "dev": true }, "right-align": { @@ -12763,7 +12776,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", "dev": true }, "safe-regex": { @@ -12778,7 +12791,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, "sass-graph": { "version": "2.2.4", @@ -12822,7 +12835,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", "dev": true }, "schema-inspector": { @@ -12836,7 +12849,7 @@ "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "integrity": "sha1-C3mpMgTXtgDUsoUNH2bCo0lRx3A=", "dev": true, "requires": { "ajv": "^6.1.0", @@ -12933,7 +12946,7 @@ "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "integrity": "sha1-bsyh4PjBVtFBWXVZhI32RzCmu8E=", "dev": true, "requires": { "debug": "2.6.9", @@ -12954,7 +12967,7 @@ "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=", "dev": true } } @@ -12968,7 +12981,7 @@ "serialize-javascript": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "integrity": "sha1-GqM2FiyIqJDdrVOEuuvJOmVRYf4=", "dev": true }, "serve-index": { @@ -12989,7 +13002,7 @@ "serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "integrity": "sha1-CV6Ecv1bRiN9tQzkhqQ/S4bGzsE=", "dev": true, "requires": { "encodeurl": "~1.0.2", @@ -13013,7 +13026,7 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -13041,7 +13054,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "sha.js": { @@ -13126,7 +13139,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { "base": "^0.11.1", @@ -13162,7 +13175,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { "define-property": "^1.0.0", @@ -13182,7 +13195,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -13191,7 +13204,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -13200,7 +13213,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -13217,7 +13230,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -13225,7 +13238,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -13244,7 +13257,7 @@ "sockjs": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "integrity": "sha1-2Xa76ACve9IK4IWY1YI5NQiZPA0=", "dev": true, "requires": { "faye-websocket": "^0.10.0", @@ -13324,7 +13337,7 @@ "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", "dev": true, "requires": { "atob": "^2.1.1", @@ -13337,7 +13350,7 @@ "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", "dev": true, "requires": { "source-map": "^0.5.6" @@ -13369,7 +13382,7 @@ "sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "integrity": "sha1-AI22XtzmxQ7sDF4ijhlFBh3QQ3w=", "dev": true }, "spdx-correct": { @@ -13391,7 +13404,7 @@ "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -13407,13 +13420,13 @@ "specificity": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "integrity": "sha1-qrXmRQEtsIuhguFRFlc40AiHsBk=", "dev": true }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -13469,7 +13482,7 @@ "ssri": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "integrity": "sha1-ujhyycbTOgcEp9cf8EXl7EiZnQY=", "dev": true, "requires": { "safe-buffer": "^5.1.1" @@ -13478,7 +13491,7 @@ "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "integrity": "sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=", "dev": true }, "stackframe": { @@ -13496,7 +13509,7 @@ "state-toggle": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz", - "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og==", + "integrity": "sha1-w8sJdPQKag+OkFuWeJ60Gvoc3jo=", "dev": true }, "static-extend": { @@ -13523,7 +13536,7 @@ "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=", "dev": true }, "stdout-stream": { @@ -13564,7 +13577,7 @@ "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "integrity": "sha1-6+J6DDibBPvMIzZClS4Qcxr6m64=", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -13574,7 +13587,7 @@ "stream-http": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", "dev": true, "requires": { "builtin-status-codes": "^3.0.0", @@ -13599,7 +13612,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -13623,7 +13636,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -13632,7 +13645,7 @@ "stringify-entities": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", - "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==", + "integrity": "sha1-qYQX5Ucf0iez5F09sYYcEcr2aPc=", "dev": true, "requires": { "character-entities-html4": "^1.0.0", @@ -13656,7 +13669,7 @@ "stringstream": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "integrity": "sha1-eIAiWw1K0Q4wkn0Weh1vL9OzOnI=", "dev": true, "optional": true }, @@ -13733,7 +13746,7 @@ "strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, "requires": { "escape-string-regexp": "^1.0.2" @@ -13838,7 +13851,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -13930,7 +13943,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -14143,13 +14156,13 @@ "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "integrity": "sha1-ngm/cSs2CrkiXoEgSPcf3pyJZXs=", "dev": true }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -14313,7 +14326,7 @@ "meow": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "integrity": "sha1-38c9Y6mvxxSl43F2DrXIi5EHiqQ=", "dev": true, "requires": { "camelcase-keys": "^4.0.0", @@ -14351,7 +14364,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", "dev": true }, "parse-json": { @@ -14367,7 +14380,7 @@ "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", "dev": true, "requires": { "pify": "^3.0.0" @@ -14443,7 +14456,7 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", "dev": true }, "slash": { @@ -14455,7 +14468,7 @@ "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "integrity": "sha1-BE8aSdiEL/MHqta1Be0Xi9lQE00=", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0" @@ -14464,7 +14477,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "strip-indent": { @@ -14476,7 +14489,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -14485,7 +14498,7 @@ "table": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/table/-/table-5.1.0.tgz", - "integrity": "sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg==", + "integrity": "sha1-aaVGRPbwGtFij4F4cVtAjca/Efc=", "dev": true, "requires": { "ajv": "^6.5.3", @@ -14503,7 +14516,7 @@ "yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "integrity": "sha1-cgImW4n36eny5XZeD+c1qQXtuqg=", "dev": true, "requires": { "camelcase": "^4.1.0" @@ -14514,13 +14527,13 @@ "stylelint-config-recommended": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz", - "integrity": "sha512-ajMbivOD7JxdsnlS5945KYhvt7L/HwN6YeYF2BH6kE4UCLJR0YvXMf+2j7nQpJyYLZx9uZzU5G1ZOSBiWAc6yA==", + "integrity": "sha1-9SbVx3HGgRGG2ertvtAhlf7jCFg=", "dev": true }, "stylelint-config-recommended-scss": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-3.2.0.tgz", - "integrity": "sha512-M8BFHMRf8KNz5EQPKJd8nMCGmBd2o5coDEObfHVbEkyLDgjIf1V+U5dHjaGgvhm0zToUxshxN+Gc5wpbOOew4g==", + "integrity": "sha1-V2G+ZeKLWPpqSmKLcSkeg+kh0Q8=", "dev": true, "requires": { "stylelint-config-recommended": "^2.0.0" @@ -14529,7 +14542,7 @@ "stylelint-config-standard": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-18.2.0.tgz", - "integrity": "sha512-07x0TaSIzvXlbOioUU4ORkCIM07kyIuojkbSVCyFWNVgXMXYHfhnQSCkqu+oHWJf3YADAnPGWzdJ53NxkoJ7RA==", + "integrity": "sha1-YoMUmrp/ZPGHMa748Kv7Nc9hngY=", "dev": true, "requires": { "stylelint-config-recommended": "^2.1.0" @@ -14538,7 +14551,7 @@ "stylelint-order": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-1.0.0.tgz", - "integrity": "sha512-2IVM8GzeKIDQDTETNdmgX99ywGrb7OqFWkniCw7QLqS/xONPGMLY/xAQnvGcUS3oBSo8znsoshsWVBqPz2Kv4Q==", + "integrity": "sha1-CJ/D1c3359SsGIL2W2CyXbdQQTw=", "dev": true, "requires": { "lodash": "^4.17.10", @@ -14549,7 +14562,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -14558,7 +14571,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -14586,13 +14599,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -14616,13 +14629,13 @@ "cssesc": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-1.0.1.tgz", - "integrity": "sha512-S2hzrpWvE6G/rW7i7IxJfWBYn27QWfOIncUW++8Rbo1VB5zsJDSVPcnI+Q8z7rhxT6/yZeLOCja4cZnghJrNGA==", + "integrity": "sha1-73vY0CKe1qOnBR/3dxJl/nMw4Kg=", "dev": true }, "postcss-selector-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-4.0.0.tgz", - "integrity": "sha512-5h+MvEjnzu1qy6MabjuoPatsGAjjDV9B24e7Cktjl+ClNtjVjmvAXjOFQr1u7RlWULKNGYaYVE4s+DIIQ4bOGA==", + "integrity": "sha1-UMZXD0BXkDbY5j8j5sBib+V0NSc=", "dev": true, "requires": { "cssesc": "^1.0.1", @@ -14635,7 +14648,7 @@ "stylelint-webpack-plugin": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-0.10.5.tgz", - "integrity": "sha512-jtYx3aJ2qDMvBMswe5NRPTO7kJgAKafc6GilAkWDp/ewoAmnoxA6TsYMnIPtLECRLwXevaCPvlh2JEUMGZCoUQ==", + "integrity": "sha1-C24NNz/14DuqgZfr4PJiWYG9Jms=", "dev": true, "requires": { "arrify": "^1.0.1", @@ -14659,7 +14672,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -14761,7 +14774,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -14772,7 +14785,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -14780,7 +14793,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -14839,7 +14852,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -14848,7 +14861,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -14857,7 +14870,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -14894,13 +14907,13 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -14923,7 +14936,7 @@ "sugarss": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", - "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "integrity": "sha1-3dduASSyl9QL88yjHIsi7LQ7xh0=", "dev": true, "requires": { "postcss": "^7.0.2" @@ -14932,7 +14945,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -14941,7 +14954,7 @@ "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "integrity": "sha1-GMSasWoDe26wFSzIPjRxM4IVtm4=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -14969,13 +14982,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -15021,7 +15034,7 @@ "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + "integrity": "sha1-wiaIrtTqs83C3+rLtWFmBWCgCAQ=" }, "table": { "version": "3.8.3", @@ -15131,7 +15144,7 @@ "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "integrity": "sha1-HSjj0qrfHVpZlsTp+VYBzQU0gK4=", "dev": true, "requires": { "setimmediate": "^1.0.4" @@ -15140,7 +15153,7 @@ "tiny-emitter": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", - "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==" + "integrity": "sha1-gtJ0aKylrejl/R5tIrV91D69+3w=" }, "tinycolor2": { "version": "1.4.1", @@ -15150,7 +15163,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "requires": { "os-tmpdir": "~1.0.2" } @@ -15184,7 +15197,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", "dev": true }, "to-fast-properties": { @@ -15205,7 +15218,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { "define-property": "^2.0.2", @@ -15249,7 +15262,7 @@ "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", "dev": true, "optional": true, "requires": { @@ -15286,13 +15299,13 @@ "trim-trailing-lines": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz", - "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg==", + "integrity": "sha1-4OwIEP08PxcwUWtF9JCDyq8ndNk=", "dev": true }, "trough": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz", - "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", + "integrity": "sha1-4pvRYUxkWNRIafwoslWreFfvfCQ=", "dev": true }, "true-case-path": { @@ -15361,7 +15374,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15403,7 +15416,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -15529,7 +15542,7 @@ "unherit": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", - "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==", + "integrity": "sha1-EydI2j6I6rdn4I+r+7icXp0oYow=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -15539,7 +15552,7 @@ "unified": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", - "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "integrity": "sha1-f71jD3GRJtZ9QMZEt+P2FwNfbbo=", "dev": true, "requires": { "bail": "^1.0.0", @@ -15628,7 +15641,7 @@ "unist-util-find-all-after": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.2.tgz", - "integrity": "sha512-nDl79mKpffXojLpCimVXnxhlH/jjaTnDuScznU9J4jjsaUtBdDbxmlc109XtcqxY4SDO0SwzngsxxW8DIISt1w==", + "integrity": "sha1-m+Sc+65coVZrJ1NmcKkoNr8vjW0=", "dev": true, "requires": { "unist-util-is": "^2.0.0" @@ -15637,13 +15650,13 @@ "unist-util-is": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz", - "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw==", + "integrity": "sha1-EZP6jyv7u4IVBjPzqNLrmhwdVds=", "dev": true }, "unist-util-remove-position": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz", - "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", + "integrity": "sha1-hrXa0QTQu/vrHbX1yS81cFdcEss=", "dev": true, "requires": { "unist-util-visit": "^1.1.0" @@ -15652,13 +15665,13 @@ "unist-util-stringify-position": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", - "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", + "integrity": "sha1-Pzf881EnncvKdICrWIm7ioMu4cY=", "dev": true }, "unist-util-visit": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.0.tgz", - "integrity": "sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==", + "integrity": "sha1-HLdjZHGG3Cb14d9dtr0eSLPML7E=", "dev": true, "requires": { "unist-util-visit-parents": "^2.0.0" @@ -15667,7 +15680,7 @@ "unist-util-visit-parents": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz", - "integrity": "sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA==", + "integrity": "sha1-Y//8iSkCe+4Ev+99LM5HT3HLYhc=", "dev": true, "requires": { "unist-util-is": "^2.1.2" @@ -15740,7 +15753,7 @@ "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "dev": true, "requires": { "punycode": "^2.1.0" @@ -15749,7 +15762,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", "dev": true } } @@ -15781,7 +15794,7 @@ "url-loader": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz", - "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", + "integrity": "sha1-zI/qgse5Bud3cBklCGnlaemVwpU=", "dev": true, "requires": { "loader-utils": "^1.0.2", @@ -15844,7 +15857,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", "dev": true }, "user-home": { @@ -15856,7 +15869,7 @@ "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "integrity": "sha1-OqASW/5mikZy3liFfTrOJ+y3aQE=", "dev": true, "requires": { "inherits": "2.0.3" @@ -15925,7 +15938,7 @@ "vendors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", - "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", + "integrity": "sha1-f8te759WI7FWvOqJ7DfWNnbyGAE=", "dev": true }, "verror": { @@ -15950,7 +15963,7 @@ "vfile": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "integrity": "sha1-5i2OcrIOg8MkvGxnJ47ickiL+Eo=", "dev": true, "requires": { "is-buffer": "^1.1.4", @@ -16228,7 +16241,7 @@ "webpack-dev-middleware": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", - "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "integrity": "sha1-+PwRIM47T8VoDO7LQ9d3lmshEF4=", "dev": true, "requires": { "memory-fs": "~0.4.1", @@ -16312,7 +16325,7 @@ "webpack-sources": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "integrity": "sha1-KijcufH0X+lg2PFJMlK17mUw+oU=", "dev": true, "requires": { "source-list-map": "^2.0.0", @@ -16322,7 +16335,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -16340,7 +16353,7 @@ "websocket-extensions": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "integrity": "sha1-XS/yKXcAPsaHpLhwc9+7rBRszyk=", "dev": true }, "whatwg-fetch": { @@ -16465,7 +16478,7 @@ "ws": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", - "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", + "integrity": "sha1-y9nm514J/F0skAFfIfDECHXg3VE=", "requires": { "options": ">=0.0.5", "ultron": "1.0.x" diff --git a/ui/package.json b/ui/package.json index 2600c62438..0b06c5d06a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -65,6 +65,7 @@ "material-ui": "^0.16.1", "material-ui-number-input": "^5.0.16", "md-color-picker": "0.2.6", + "md-date-range-picker": "^0.8.4", "mdPickers": "git://github.com/alenaksu/mdPickers.git#0.7.5", "moment": "^2.15.0", "ngFlowchart": "git://github.com/thingsboard/ngFlowchart.git#master", diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js index a32d2b0f5c..21451f6340 100644 --- a/ui/src/app/api/widget.service.js +++ b/ui/src/app/api/widget.service.js @@ -22,6 +22,7 @@ import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-wid import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget'; import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget'; import thingsboardExtensionsTableWidget from '../widget/lib/extensions-table-widget'; +import thingsboardDateRangeNavigatorWidget from '../widget/lib/date-range-navigator/date-range-navigator'; import thingsboardRpcWidgets from '../widget/lib/rpc'; @@ -33,6 +34,7 @@ import TbCanvasDigitalGauge from '../widget/lib/canvas-digital-gauge'; import TbMapWidget from '../widget/lib/map-widget'; import TbMapWidgetV2 from '../widget/lib/map-widget2'; + import 'jquery.terminal/js/jquery.terminal.min.js'; import 'jquery.terminal/css/jquery.terminal.min.css'; @@ -43,7 +45,8 @@ import thingsboardTypes from '../common/types.constant'; import thingsboardUtils from '../common/utils.service'; export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget, - thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardExtensionsTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils]) + thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardExtensionsTableWidget, + thingsboardDateRangeNavigatorWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils]) .factory('widgetService', WidgetService) .name; diff --git a/ui/src/app/app.js b/ui/src/app/app.js index dbef8a9c6b..c99b7f67e1 100644 --- a/ui/src/app/app.js +++ b/ui/src/app/app.js @@ -29,6 +29,7 @@ import 'angular-translate-storage-cookie'; import 'angular-translate-handler-log'; import 'angular-translate-interpolation-messageformat'; import 'md-color-picker'; +import 'md-date-range-picker'; import mdPickers from 'mdPickers'; import ngSanitize from 'angular-sanitize'; import FBAngular from 'angular-fullscreen'; @@ -65,6 +66,7 @@ import 'angular-hotkeys/build/hotkeys.min.css'; import 'angular-carousel/dist/angular-carousel.min.css'; import 'angular-material-expansion-panel/dist/md-expansion-panel.min.css'; import 'ngFlowchart/dist/flowchart.css'; +import 'md-date-range-picker/src/md-date-range-picker.css'; import '../scss/main.scss'; import thingsboardThirdpartyFix from './common/thirdparty-fix'; @@ -106,6 +108,7 @@ angular.module('thingsboard', [ angularSocialshare, 'pascalprecht.translate', 'mdColorPicker', + 'ngMaterialDateRangePicker', mdPickers, ngSanitize, FBAngular.name, diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index bc241e28a5..bace3d4467 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -1549,6 +1549,65 @@ "widget-type-file": "Widget-Typdatei", "invalid-widget-type-file-error": "Widget-Typ kann nicht importiert werden: Ungültige Datenstruktur des Widget-Typs." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "So.", + "Mon": "Mo.", + "Tue": "Di.", + "Wed": "Mi.", + "Thu": "Do.", + "Fri": "Fr.", + "Sat": "Sa.", + "Jan": "Jan.", + "Feb": "Feb.", + "Mar": "März", + "Apr": "Apr.", + "May": "Mai", + "Jun": "Juni", + "Jul": "Juli", + "Aug": "Aug.", + "Sep": "Sep.", + "Oct": "Okt.", + "Nov": "Nov.", + "Dec": "Dez.", + "January": "Januar", + "February": "Februar", + "March": "März", + "April": "April", + "June": "Juni", + "July": "Juli", + "August": "August", + "September": "September", + "October": "Oktober", + "November": "November", + "December": "Dezember", + "Custom Date Range": "Benutzerdefinierter Datumsbereich", + "Date Range Template": "Datumsbereichsvorlage", + "Today": "Heute", + "Yesterday": "Gestern", + "This Week": "Diese Woche", + "Last Week": "Letzte Woche", + "This Month": "Diesen Monat", + "Last Month": "Im vergangenen Monat", + "Year": "Jahr", + "This Year": "Dieses Jahr", + "Last Year": "Vergangenes Jahr", + "Date picker": "Datumsauswahl", + "Hour": "Stunde", + "Day": "Tag", + "Week": "Woche", + "2 weeks": "2 Wochen", + "Month": "Monat", + "3 months": "3 Monate", + "6 months": "6 Monate", + "Custom interval": "Benutzerdefiniertes Intervall", + "Interval": "Intervall", + "Step size": "Schrittlänge", + "Ok": "Ok" + } + } + }, "icon": { "icon": "Symbol", "select-icon": "Symbol auswählen", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 9e38c55aaa..dab0b0ae24 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1554,6 +1554,65 @@ "widget-type-file": "Widget type file", "invalid-widget-type-file-error": "Unable to import widget type: Invalid widget type data structure." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "Sun", + "Mon": "Mon", + "Tue": "Tue", + "Wed": "Wed", + "Thu": "Thu", + "Fri": "Fri", + "Sat": "Sat", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "May", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Oct", + "Nov": "Nov", + "Dec": "Dec", + "January": "January", + "February": "February", + "March": "March", + "April": "April", + "June": "June", + "July": "July", + "August": "August", + "September": "September", + "October": "October", + "November": "November", + "December": "December", + "Custom Date Range": "Custom Date Range", + "Date Range Template": "Date Range Template", + "Today": "Today", + "Yesterday": "Yesterday", + "This Week": "This Week", + "Last Week": "Last Week", + "This Month": "This Month", + "Last Month": "Last Month", + "Year": "Year", + "This Year": "This Year", + "Last Year": "Last Year", + "Date picker": "Date picker", + "Hour": "Hour", + "Day": "Day", + "Week": "Week", + "2 weeks": "2 Weeks", + "Month": "Month", + "3 months": "3 Months", + "6 months": "6 Months", + "Custom interval": "Custom interval", + "Interval": "Interval", + "Step size": "Step size", + "Ok": "Ok" + } + } + }, "icon": { "icon": "Icon", "select-icon": "Select icon", diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 48ace08e6b..9d4b3cd4d6 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -1549,6 +1549,65 @@ "widget-type-file": "Archivo de tipo de widget", "invalid-widget-type-file-error": "No se puede importar tipo de widget: Estructura de datos del tipo de widget es inválida." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "Dom.", + "Mon": "Lun.", + "Tue": "Mar.", + "Wed": "Mié", + "Thu": "Jue.", + "Fri": "Vie.", + "Sat": "Sáb.", + "Jan": "Ene.", + "Feb": "Feb.", + "Mar": "Mar.", + "Apr": "Abr.", + "May": "May.", + "Jun": "Jun.", + "Jul": "Jul.", + "Aug": "Ago.", + "Sep": "Sept.", + "Oct": "Oct.", + "Nov": "Nov.", + "Dec": "Dic.", + "January": "Enero", + "February": "Febrero", + "March": "Marzo", + "April": "Abril", + "June": "Junio", + "July": "Julio", + "August": "Agosto", + "September": "Septiembre", + "October": "Octubre", + "November": "Noviembre", + "December": "Diciembre", + "Custom Date Range": "Intervalo de fechas personalizado", + "Date Range Template": "Plantilla de rango de fechas", + "Today": "Hoy", + "Yesterday": "Ayer", + "This Week": "Esta semana", + "Last Week": "La semana pasada", + "This Month": "Este mes", + "Last Month": "El mes pasado", + "Year": "Año", + "This Year": "Este año", + "Last Year": "Último", + "Date picker": "Date picker", + "Hour": "Hora", + "Day": "Día", + "Week": "Semana", + "2 weeks": "2 Semanas", + "Month": "Mes", + "3 months": "3 Meses", + "6 months": "6 Meses", + "Custom interval": "Intervalo personalizado", + "Interval": "Intervalo", + "Step size": "Numero de pie", + "Ok": "De acuerdo" + } + } + }, "icon": { "icon": "Icono", "select-icon": "Seleccionar icono", diff --git a/ui/src/app/locale/locale.constant-fa_IR.json b/ui/src/app/locale/locale.constant-fa_IR.json index de9b2a4f5e..0bf55c6831 100644 --- a/ui/src/app/locale/locale.constant-fa_IR.json +++ b/ui/src/app/locale/locale.constant-fa_IR.json @@ -1549,6 +1549,65 @@ "widget-type-file": "پرونده نوع ويجت", "invalid-widget-type-file-error": ".وارد کردن نوع ويجت ممکن نيست: ساختار داده نوع ويجت نامعتبر است" }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "یکشنبه", + "Mon": "دوشنبه", + "Tue": "سه‌شنبه", + "Wed": "چهارشنبه", + "Thu": "پنجشنبه", + "Fri": "جمعه", + "Sat": "شنبه", + "Jan": "ژانویهٔ", + "Feb": "فوریهٔ", + "Mar": "مارس", + "Apr": "آوریل", + "May": "مهٔ", + "Jun": "ژوئن", + "Jul": "ژوئیهٔ", + "Aug": "اوت", + "Sep": "سپتامبر", + "Oct": "اکتبر", + "Nov": "نوامبر", + "Dec": "دسامبر", + "January": "January", + "February": "February", + "March": "March", + "April": "April", + "June": "June", + "July": "July", + "August": "August", + "September": "September", + "October": "October", + "November": "November", + "December": "December", + "Custom Date Range": "Custom Date Range", + "Date Range Template": "Date Range Template", + "Today": "Today", + "Yesterday": "Yesterday", + "This Week": "This Week", + "Last Week": "Last Week", + "This Month": "This Month", + "Last Month": "Last Month", + "Year": "Year", + "This Year": "This Year", + "Last Year": "Last Year", + "Date picker": "Date picker", + "Hour": "Hour", + "Day": "Day", + "Week": "Week", + "2 weeks": "2 weeks", + "Month": "Month", + "3 months": "3 months", + "6 months": "6 months", + "Custom interval": "Custom interval", + "Interval": "Interval", + "Step size": "Step size", + "Ok": "Ok" + } + } + }, "icon": { "icon": "آيکون", "select-icon": "انتخاب آيکون", diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index f22f90548b..687f2ed902 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -1436,6 +1436,65 @@ "invalid-widget-type-file-error": "Impossible d'importer le type de widget: structure de données de type widget invalide.", "widget-type-file": "Fichier de type Widget" }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "Dim.", + "Mon": "Lun.", + "Tue": "Mar.", + "Wed": "Mer.", + "Thu": "Jeu.", + "Fri": "Ven.", + "Sat": "Sam.", + "Jan": "Janv.", + "Feb": "Févr.", + "Mar": "Mars", + "Apr": "Avr.", + "May": "Mai", + "Jun": "Juin", + "Jul": "Juil.", + "Aug": "Août", + "Sep": "Sept.", + "Oct": "Oct.", + "Nov": "Nov.", + "Dec": "Déc.", + "January": "Janvier", + "February": "Février", + "March": "Mars", + "April": "Avril", + "June": "Juin", + "July": "Juillet", + "August": "Août", + "September": "Septembre", + "October": "Octobre", + "November": "Novembre", + "December": "Décembre", + "Custom Date Range": "Plage de dates personnalisée", + "Date Range Template": "Modèle de plage de dates", + "Today": "Aujourd'hui", + "Yesterday": "Hier", + "This Week": "Cette semaine", + "Last Week": "La semaine dernière", + "This Month": "Ce mois-ci", + "Last Month": "Le mois dernier", + "Year": "Année", + "This Year": "Cette année", + "Last Year": "L'année dernière", + "Date picker": "Sélecteur de date", + "Hour": "Heure", + "Day": "Journée", + "Week": "La semaine", + "2 weeks": "2 Semaines", + "Month": "Mois", + "3 months": "3 Mois", + "6 months": "6 Mois", + "Custom interval": "Intervalle personnalisé", + "Interval": "Intervalle", + "Step size": "Taille de pas", + "Ok": "Ok" + } + } + }, "widgets-bundle": { "add": "Ajouter un groupe de widgets", "add-widgets-bundle-text": "Ajouter un nouveau groupe de widgets", diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json index 986fdc19a0..fc69b97b89 100644 --- a/ui/src/app/locale/locale.constant-it_IT.json +++ b/ui/src/app/locale/locale.constant-it_IT.json @@ -1554,6 +1554,65 @@ "widget-type-file": "File tipo di widget", "invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "Dom", + "Mon": "Lun", + "Tue": "Mar", + "Wed": "Mer", + "Thu": "Gio", + "Fri": "Ven", + "Sat": "Sab", + "Jan": "Gen", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "Mag", + "Jun": "Giu", + "Jul": "Lug", + "Aug": "Ago", + "Sep": "Set", + "Oct": "Ott", + "Nov": "Nov", + "Dec": "Dic", + "January": "Gennaio", + "February": "Febbraio", + "March": "Marzo", + "April": "Aprile", + "June": "Giugno", + "July": "Luglio", + "August": "Agosto", + "September": "Settembre", + "October": "Ottobre", + "November": "Novembre", + "December": "Dicembre", + "Custom Date Range": "Intervallo di date personalizzato", + "Date Range Template": "Modello di intervallo di date", + "Today": "Oggi", + "Yesterday": "Ieri", + "This Week": "Questa settimana", + "Last Week": "La settimana scorsa", + "This Month": "Questo mese", + "Last Month": "Lo scorso mese", + "Year": "Anno", + "This Year": "Quest'anno", + "Last Year": "L'anno scorso", + "Date picker": "Date picker", + "Hour": "Ora", + "Day": "Giorno", + "Week": "Settimana", + "2 weeks": "2 Settimane", + "Month": "Mese", + "3 months": "3 Mesi", + "6 months": "6 Mesi", + "Custom interval": "Intervallo personalizzato", + "Interval": "Intervallo", + "Step size": "Dimensione del passo", + "Ok": "Ok" + } + } + }, "icon": { "icon": "Icona", "select-icon": "Seleziona icona", diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json index 9fe6971ff9..fb9c8cf90f 100644 --- a/ui/src/app/locale/locale.constant-ja_JA.json +++ b/ui/src/app/locale/locale.constant-ja_JA.json @@ -1432,6 +1432,65 @@ "widget-type-file": "ウィジェットタイプファイル", "invalid-widget-type-file-error": "ウィジェットタイプをインポートできません:ウィジェットタイプのデータ構造が無効です。" }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "日", + "Mon": "月", + "Tue": "火", + "Wed": "水", + "Thu": "木", + "Fri": "金", + "Sat": "土", + "Jan": "1月", + "Feb": "2月", + "Mar": "3月", + "Apr": "4月", + "May": "5月", + "Jun": "6月", + "Jul": "7月", + "Aug": "8月", + "Sep": "9月", + "Oct": "10月", + "Nov": "11月", + "Dec": "12月", + "January": "1月", + "February": "2月", + "March": "行進", + "April": "4月", + "June": "六月", + "July": "7月", + "August": "8月", + "September": "9月", + "October": "10月", + "November": "11月", + "December": "12月", + "Custom Date Range": "カスタム期間", + "Date Range Template": "日付範囲テンプレート", + "Today": "今日", + "Yesterday": "昨日", + "This Week": "今週", + "Last Week": "先週", + "This Month": "今月", + "Last Month": "先月", + "Year": "年", + "This Year": "今年", + "Last Year": "昨年", + "Date picker": "日付ピッカー", + "Hour": "時", + "Day": "日", + "Week": "週間", + "2 weeks": "2週間", + "Month": "月", + "3 months": "3ヶ月", + "6 months": "6ヵ月", + "Custom interval": "カスタム間隔", + "Interval": "間隔", + "Step size": "刻み幅", + "Ok": "Ok" + } + } + }, "icon": { "icon": "アイコン", "select-icon": "選択アイコン", diff --git a/ui/src/app/locale/locale.constant-ko_KR.json b/ui/src/app/locale/locale.constant-ko_KR.json index 87f92dc3e4..559a8fab22 100644 --- a/ui/src/app/locale/locale.constant-ko_KR.json +++ b/ui/src/app/locale/locale.constant-ko_KR.json @@ -1308,6 +1308,65 @@ "widget-type-file": "위젯 타입 파일", "invalid-widget-type-file-error": "위젯 타입을 가져오기 할 수 없습니다.: 잘못된 위젯 타입 데이터 구조입니다." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "일", + "Mon": "월", + "Tue": "화", + "Wed": "수", + "Thu": "목", + "Fri": "금", + "Sat": "토", + "Jan": "1월", + "Feb": "2월", + "Mar": "3월", + "Apr": "4월", + "May": "5월", + "Jun": "6월", + "Jul": "7월", + "Aug": "8월", + "Sep": "9월", + "Oct": "10월", + "Nov": "11월", + "Dec": "12월", + "January": "일월", + "February": "이월", + "March": "행진", + "April": "4 월", + "June": "유월", + "July": "칠월", + "August": "팔월", + "September": "구월", + "October": "십월", + "November": "십일월", + "December": "12 월", + "Custom Date Range": "맞춤 기간", + "Date Range Template": "기간 템플릿", + "Today": "오늘", + "Yesterday": "어제", + "This Week": "이번 주", + "Last Week": "지난주", + "This Month": "이번 달", + "Last Month": "지난 달", + "Year": "년", + "This Year": "올해", + "Last Year": "작년", + "Date picker": "날짜 선택기", + "Hour": "시간", + "Day": "일", + "Week": "주", + "2 weeks": "이주", + "Month": "달", + "3 months": "3 개월", + "6 months": "6 개월", + "Custom interval": "사용자 지정 간격", + "Interval": "간격", + "Step size": "단계 크기", + "Ok": "Ok" + } + } + }, "icon": { "icon": "Icon", "select-icon": "Select icon", diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 3ad4371cc1..0abb458c5d 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1547,6 +1547,65 @@ "widget-type-file": "Файл типа виджета", "invalid-widget-type-file-error": "Не удалось импортировать виджет: неизвестная схема данных типа виджета." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "Вс", + "Mon": "Пн", + "Tue": "Вт", + "Wed": "Ср", + "Thu": "Чт", + "Fri": "Пт", + "Sat": "Сб", + "Jan": "Янв.", + "Feb": "Февр.", + "Mar": "Март", + "Apr": "Апр.", + "May": "Май", + "Jun": "Июнь", + "Jul": "Июль", + "Aug": "Авг.", + "Sep": "Сент.", + "Oct": "Окт.", + "Nov": "Нояб.", + "Dec": "Дек.", + "January": "Январь", + "February": "Февраль", + "March": "Март", + "April": "Апрель", + "June": "Июнь", + "July": "Июль", + "August": "Август", + "September": "Сентябрь", + "October": "Октября", + "November": "Ноябрь", + "December": "Декабрь", + "Custom Date Range": "Пользовательский диапазон дат", + "Date Range Template": "Шаблон диапазона дат", + "Today": "Сегодня", + "Yesterday": "Вчера", + "This Week": "На этой неделе", + "Last Week": "Прошлая неделя", + "This Month": "Этот месяц", + "Last Month": "Прошлый месяц", + "Year": "Год", + "This Year": "В этом году", + "Last Year": "Прошлый год", + "Date picker": "Выбор даты", + "Hour": "Час", + "Day": "День", + "Week": "Неделю", + "2 weeks": "2 Недели", + "Month": "Месяц", + "3 months": "3 Месяца", + "6 months": "6 Месяцев", + "Custom interval": "Пользовательский интервал", + "Interval": "Интервал", + "Step size": "Размер шага", + "Ok": "Ok" + } + } + }, "icon": { "icon": "Иконка", "select-icon": "Выбрать иконку", diff --git a/ui/src/app/locale/locale.constant-tr_TR.json b/ui/src/app/locale/locale.constant-tr_TR.json index e65d07bd83..d9743cdf7c 100644 --- a/ui/src/app/locale/locale.constant-tr_TR.json +++ b/ui/src/app/locale/locale.constant-tr_TR.json @@ -1514,6 +1514,65 @@ "widget-type-file": "Gösterge türü dosyası", "invalid-widget-type-file-error": "Gösterge türü içe aktarılamadı: Geçersiz gösterge türü veri yapısı." }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "Paz", + "Mon": "Pzt", + "Tue": "Sal", + "Wed": "Çar", + "Thu": "Per", + "Fri": "Cum", + "Sat": "Cmt", + "Jan": "Oca", + "Feb": "Şub", + "Mar": "Mar", + "Apr": "Nis", + "May": "May", + "Jun": "Haz", + "Jul": "Tem", + "Aug": "Ağu", + "Sep": "Eyl", + "Oct": "Eki", + "Nov": "Kas", + "Dec": "Ara", + "January": "Ocak", + "February": "Şubat", + "March": "Mart", + "April": "Nisan", + "June": "Haziran", + "July": "Temmuz", + "August": "Ağustos", + "September": "Eylül", + "October": "Ekim", + "November": "Kasım", + "December": "Aralık", + "Custom Date Range": "Özel Tarih Aralığı", + "Date Range Template": "Tarih Aralığı Şablonu", + "Today": "Bugün", + "Yesterday": "Dün", + "This Week": "Bu hafta", + "Last Week": "Geçen hafta", + "This Month": "Bu ay", + "Last Month": "Geçen ay", + "Year": "Yıl", + "This Year": "Bu yıl", + "Last Year": "Geçen yıl", + "Date picker": "Tarih seçici", + "Hour": "Saat", + "Day": "Gün", + "Week": "Hafta", + "2 weeks": "2 Hafta", + "Month": "Ay", + "3 months": "3 Ay", + "6 months": "6 Ay", + "Custom interval": "Özel aralık", + "Interval": "Aralık", + "Step size": "Adım boyutu", + "Ok": "Ok" + } + } + }, "icon": { "icon": "İkon", "select-icon": "İkon seç", diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json index eaf810c986..a4971dcf02 100644 --- a/ui/src/app/locale/locale.constant-zh_CN.json +++ b/ui/src/app/locale/locale.constant-zh_CN.json @@ -1418,6 +1418,65 @@ "widget-type-file": "部件类型文件", "invalid-widget-type-file-error": "无法导入部件类型:无效的部件类型数据结构。" }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "周日", + "Mon": "周一", + "Tue": "周二", + "Wed": "周三", + "Thu": "周四", + "Fri": "周五", + "Sat": "周六", + "Jan": "1月", + "Feb": "2月", + "Mar": "3月", + "Apr": "4月", + "May": "5月", + "Jun": "6月", + "Jul": "7月", + "Aug": "8月", + "Sep": "9月", + "Oct": "10月", + "Nov": "11月", + "Dec": "12月", + "January": "一月", + "February": "二月", + "March": "游行", + "April": "四月", + "June": "六月", + "July": "七月", + "August": "八月", + "September": "九月", + "October": "十月", + "November": "十一月", + "December": "十二月", + "Custom Date Range": "自定义日期范围", + "Date Range Template": "日期范围模板", + "Today": "今天", + "Yesterday": "昨天", + "This Week": "本星期", + "Last Week": "上个星期", + "This Month": "这个月", + "Last Month": "上个月", + "Year": "年", + "This Year": "今年", + "Last Year": "去年", + "Date picker": "日期选择器", + "Hour": "小时", + "Day": "天", + "Week": "周", + "2 weeks": "2周", + "Month": "月", + "3 months": "3个月", + "6 months": "6个月", + "Custom interval": "自定义间隔", + "Interval": "间隔", + "Step size": "一步的大小", + "Ok": "Ok" + } + } + }, "icon": { "icon": "图标", "select-icon": "选择图标", diff --git a/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.js b/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.js new file mode 100644 index 0000000000..e15689da0e --- /dev/null +++ b/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.js @@ -0,0 +1,303 @@ +/* + * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * + * Copyright © 2016-2019 ThingsBoard, Inc. All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of ThingsBoard, Inc. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to ThingsBoard, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * + * Dissemination of this information or reproduction of this material is strictly forbidden + * unless prior written permission is obtained from COMPANY. + * + * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, + * managers or contractors who have executed Confidentiality and Non-disclosure agreements + * explicitly covering such access. + * + * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes + * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. + * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, + * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT + * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, + * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. + * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION + * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, + * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + */ + +/* eslint-disable import/no-unresolved, import/default */ +import widgetTpl from './date-range-navigator.tpl.html'; +import './date-range-navigator.scss'; +/* eslint-enable import/no-unresolved, import/default */ + +export default angular.module('thingsboard.widgets.dateRangeNavigator', []) + .directive('dateRangeNavigatorWidget', DateRangeNavigatorWidget) + .name; + +/*@ngInject*/ +function DateRangeNavigatorWidget() { + return { + restrict: "E", + scope: true, + bindToController: { + tableId: '=', + ctx: '=' + }, + controller: DateRangeNavigatorWidgetController, + controllerAs: 'vm', + templateUrl: widgetTpl + }; +} + +/*@ngInject*/ +function DateRangeNavigatorWidgetController($scope, $window, $filter) { + + let vm = this, + hour = 3600000, + day = 86400000, + week = 604800000, + month = 2629743000, + words = [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + "Ok", + 'Custom Date Range', + 'Date Range Template', + 'Today', + 'Yesterday', + 'This Week', + 'Last Week', + 'Month', + 'This Month', + 'Last Month', + 'Year', + 'This Year', + 'Last Year' + ], + firstUpdate = true; + + $scope.datesMap = { + hour: { + ts: hour, + label: "Hour" + }, + day: { + ts: day, + label: "Day" + }, + week: { + ts: week, + label: "Week" + }, + twoWeeks: { + ts: week * 2, + label: "2 weeks" + }, + month: { + ts: month, + label: "Month" + }, + threeMonths: { + ts: month * 3, + label: "3 months" + }, + sixMonths: { + ts: month * 6, + label: "6 months" + } + }; + $scope.advancedModel = {}; + $scope.endRestrictionDate = Date.now(); + $scope.localizationMap = getLocalizationMap(); + + $scope.changeInterval = changeInterval; + $scope.goForth = goForth; + $scope.goBack = goBack; + $scope.triggerChange = triggerChange; + + $scope.$watch('vm.ctx', function () { + if (vm.ctx && vm.ctx.dashboard.dashboardTimewindow) { + $scope.settings = vm.ctx.widgetConfig.settings; + let selection; + if ($scope.settings.useSessionStorage) { + selection = readFromStorage('date-range'); + } + if (selection) { + $scope.advancedModel = { + selectedTemplateName: selection.name, + dateStart: selection.start, + dateEnd: selection.end + }; + } else { + let end = new Date(); + end.setHours(23,59,59,999); + + let formattedDate = getFormattedDate( + (end.getTime() + 1) - $scope.datesMap[$scope.settings.initialInterval || "week"].ts, + end.getTime() + ); + $scope.advancedModel = formattedDate; + } + $scope.selectedStepSize = $scope.datesMap[$scope.settings.stepSize || "day"].ts; + + widgetContextTimewindowSync(); + } + }); + + $scope.$on('dashboardTimewindowChanged', function () { + $scope.dashboardTimewindowChanged = true; + widgetContextTimewindowSync(); + }); + + function getLocalizationMap() { + let result = {}; + + words.forEach(function (key) { + result[key] = $filter('translate')('widgets.date-range-navigator.localizationMap.' + key); + }); + + return result; + } + + function triggerChange() { + updateTimewindow($scope.advancedModel.dateStart.getTime(), $scope.advancedModel.dateEnd.getTime() + day - 1); + } + + function widgetContextTimewindowSync() { + if (vm.ctx && vm.ctx.dashboardTimewindow && $scope.dashboardTimewindowChanged && + vm.ctx.dashboard.dashboardTimewindow.history && + vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow) { + + + if (!firstUpdate) { + updateAdvancedModel(); + } + updateDateInterval(); + if ($scope.settings.useSessionStorage) { + updateStorageDate(); + } + if (firstUpdate) { + updateTimewindow($scope.advancedModel.dateStart.getTime(), $scope.advancedModel.dateEnd.getTime()); + firstUpdate = false; + } + } + } + + function getFormattedDate(startTime, endTime) { + var template; + + let startDate = new Date(startTime); + let endDate = new Date(endTime); + + if (getDateDiff(startDate, endDate) === 0) { + template = $filter('date')(startDate, 'dd MMM yyyy'); + } else { + template = $filter('date')( + startDate, + 'dd' + (startDate.getMonth() !== endDate.getMonth() || startDate.getFullYear() !== endDate.getFullYear() ? ' MMM' : '') + (startDate.getFullYear() !== endDate.getFullYear() ? ' yyyy' : '') + ) + + ' - ' + + $filter('date')( + endDate, + 'dd MMM yyyy' + ); + } + + return { + selectedTemplateName: template, + dateStart: startDate, + dateEnd: endDate + }; + } + + + function readFromStorage(itemKey) { + if ($window.sessionStorage.getItem(itemKey)) { + let selection = angular.fromJson($window.sessionStorage.getItem(itemKey)); + selection.start = new Date(parseInt(selection.start)); + selection.end = new Date(parseInt(selection.end)); + return selection; + } + + return undefined; + } + + function goForth() { + updateTimewindow(vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.startTimeMs + $scope.selectedStepSize, vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.endTimeMs + $scope.selectedStepSize); + } + + function goBack() { + updateTimewindow(vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.startTimeMs - $scope.selectedStepSize, vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.endTimeMs - $scope.selectedStepSize); + } + + function changeInterval() { + updateTimewindow(vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.endTimeMs - $scope.selectedDateInterval / 2, vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.endTimeMs + $scope.selectedDateInterval / 2); + } + + function getDateDiff(date1, date2) { + if (!date1 || !date2) return; + var _d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()), + _d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); + return _d2 - _d1; + } + + function updateTimewindow(startTime, endTime) { + vm.ctx.dashboard.dashboardTimewindowApi.onUpdateTimewindow(startTime, endTime, 10); + } + + function updateDateInterval() { + let interval = $scope.advancedModel.dateEnd.getTime() - $scope.advancedModel.dateStart.getTime(); + + for (let i in $scope.datesMap) { + if ($scope.datesMap.hasOwnProperty(i)) { + if ($scope.datesMap[i].ts === interval || $scope.datesMap[i].ts === interval + 1 || $scope.datesMap[i].ts === interval - 1) { + $scope.selectedDateInterval = $scope.datesMap[i].ts; + $scope.customInterval = false; + return; + } + } + } + + $scope.selectedDateInterval = interval; + $scope.customInterval = {ts: interval, label: "Custom interval"}; + } + + function updateAdvancedModel() { + $scope.advancedModel = getFormattedDate(vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.startTimeMs, vm.ctx.dashboard.dashboardTimewindow.history.fixedTimewindow.endTimeMs); + } + + function updateStorageDate() { + saveIntoStorage('date-range', { + start: $scope.advancedModel.dateStart.getTime(), + end: $scope.advancedModel.dateEnd.getTime(), + name: $scope.advancedModel.selectedTemplateName + }); + } + + function saveIntoStorage(keyName, selection) { + if (selection) { + $window.sessionStorage.setItem(keyName, angular.toJson(selection)); + } + } +} \ No newline at end of file diff --git a/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.scss b/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.scss new file mode 100644 index 0000000000..3899a832c7 --- /dev/null +++ b/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.scss @@ -0,0 +1,124 @@ +/** + * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * + * Copyright © 2016-2019 ThingsBoard, Inc. All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of ThingsBoard, Inc. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to ThingsBoard, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * + * Dissemination of this information or reproduction of this material is strictly forbidden + * unless prior written permission is obtained from COMPANY. + * + * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, + * managers or contractors who have executed Confidentiality and Non-disclosure agreements + * explicitly covering such access. + * + * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes + * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. + * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, + * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT + * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, + * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. + * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION + * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, + * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + */ + +.date-range-navigator-widget { + display: flex; + height: 100%; +} + +.date-range-navigator { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-evenly; + width: 100%; + margin: auto; + + .drn__element { + display: flex; + flex-direction: row; + align-items: center; + max-width: 100%; + height: 60px; + margin: 4px 0; + } + + .navigation { + md-input-container { + margin: 0; + } + } + + .picker { + .picker__wrapper { + position: relative; + max-width: 100%; + padding: 2px; + + > label { + position: absolute; + right: -3px; + bottom: 100%; + left: 0; + padding-left: 3px; + color: #787878; + transform: scale(.75); + transform-origin: left bottom; + } + } + + .md-select-value { + min-width: 225px; + border-color: #e1e1e1; + + .md-select-icon { + color: #757575; + } + } + } + + &.short-mode { + display: block; + width: 90%; + + .drn__element { + width: 100%; + + md-input-container { + flex: 1; + } + } + + .picker { + .picker__wrapper { + width: 100%; + } + + .md-select-value { + min-width: initial; + } + } + + &.labels-hidden { + .drn__element { + margin: 0; + } + } + } + + &.long-mode { + &.labels-hidden { + .drn__element { + height: 36px; + } + } + } +} diff --git a/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.tpl.html b/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.tpl.html new file mode 100644 index 0000000000..0ba9d9ce7e --- /dev/null +++ b/ui/src/app/widget/lib/date-range-navigator/date-range-navigator.tpl.html @@ -0,0 +1,87 @@ + +
    +
    +
    + + +
    +
    + + + +
    + + keyboard_arrow_left + + + + + + {{'widgets.date-range-navigator.localizationMap.'+dateValue.label | translate}} + + + + + keyboard_arrow_right + +
    +
    From bc2534ed5259906ccb93d37b7143a4b5195e8eab Mon Sep 17 00:00:00 2001 From: Valerii Sosliuk Date: Wed, 3 Apr 2019 12:25:29 +0300 Subject: [PATCH 70/74] Added deviceName and deviceType to metadata on RPC call (#1596) * Create Alarm Node add originator and status when using message data * Added deviceName and deviceType to metadata on RPC call --- .../server/service/rpc/DefaultDeviceRpcService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java index a047ae6155..2e1078fa8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java @@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.RpcError; import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbMsg; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; @@ -67,6 +69,9 @@ public class DefaultDeviceRpcService implements DeviceRpcService { @Autowired private ClusterRpcService rpcService; + @Autowired + private DeviceService deviceService; + @Autowired @Lazy private ActorService actorService; @@ -171,6 +176,12 @@ public class DefaultDeviceRpcService implements DeviceRpcService { metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime())); metaData.putValue("oneway", Boolean.toString(msg.isOneway())); + Device device = deviceService.findDeviceById(msg.getTenantId(), msg.getDeviceId()); + if (device != null) { + metaData.putValue("deviceName", device.getName()); + metaData.putValue("deviceType", device.getType()); + } + entityNode.put("method", msg.getBody().getMethod()); entityNode.put("params", msg.getBody().getParams()); From d5d64ae3b8e6bb5461c63891aa50ccef35f3346b Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 3 Apr 2019 12:58:28 +0300 Subject: [PATCH 71/74] Update install service. --- .../thingsboard/server/install/ThingsboardInstallService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 8e2f00b345..66f8c24424 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -124,6 +124,7 @@ public class ThingsboardInstallService { systemDataLoaderService.deleteSystemWidgetBundle("maps_v2"); systemDataLoaderService.deleteSystemWidgetBundle("gateway_widgets"); systemDataLoaderService.deleteSystemWidgetBundle("input_widgets"); + systemDataLoaderService.deleteSystemWidgetBundle("date"); systemDataLoaderService.loadSystemWidgets(); break; From 4ff006640f072ab28ffe2e431b18b69719a4c32a Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Wed, 3 Apr 2019 13:02:13 +0300 Subject: [PATCH 72/74] Feature/tb get entity details nodes (#1598) * init commit * add ui-configuration * EntityDetailsNodes --- .../TbAbstractGetEntityDetailsNode.java | 95 ++++++++++++ ...ractGetEntityDetailsNodeConfiguration.java | 31 ++++ .../metadata/TbGetCustomerDetailsNode.java | 142 ++++++++++++++++++ ...TbGetCustomerDetailsNodeConfiguration.java | 33 ++++ .../metadata/TbGetTenantDetailsNode.java | 105 +++++++++++++ .../TbGetTenantDetailsNodeConfiguration.java | 33 ++++ .../rule/engine/util/EntityDetails.java | 22 +++ .../static/rulenode/rulenode-core-config.js | 9 +- 8 files changed, 466 insertions(+), 4 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java new file mode 100644 index 0000000000..7aedcf6aa9 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java @@ -0,0 +1,95 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; + +@Slf4j +public abstract class TbAbstractGetEntityDetailsNode implements TbNode { + + private static final Gson gson = new Gson(); + private static final JsonParser jsonParser = new JsonParser(); + private static final Type TYPE = new TypeToken>() {}.getType(); + + protected C config; + + @Override + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + this.config = loadGetEntityDetailsNodeConfiguration(configuration); + } + + @Override + public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { + try { + ctx.tellNext(getDetails(ctx, msg), SUCCESS); + } catch (Exception e) { + ctx.tellFailure(msg, e); + } + } + + @Override + public void destroy() {} + + protected abstract C loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException; + + protected abstract TbMsg getDetails(TbContext ctx, TbMsg msg); + + protected MessageData getDataAsJson(TbMsg msg) { + if (this.config.isAddToMetadata()) { + return new MessageData(gson.toJsonTree(msg.getMetaData().getData(), TYPE), "metadata"); + } else { + return new MessageData(jsonParser.parse(msg.getData()), "data"); + } + } + + protected TbMsg transformMsg(TbContext ctx, TbMsg msg, JsonElement resultObject, MessageData messageData) { + if (messageData.getDataType().equals("metadata")) { + Map metadataMap = gson.fromJson(resultObject.toString(), TYPE); + return ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), new TbMsgMetaData(metadataMap), msg.getData()); + } else { + return ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), gson.toJson(resultObject)); + } + } + + @Data + @AllArgsConstructor + protected static class MessageData { + + private JsonElement data; + private String dataType; + + } + + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java new file mode 100644 index 0000000000..fa7d8405d2 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import lombok.Data; +import org.thingsboard.rule.engine.util.EntityDetails; + +import java.util.List; + +@Data +public abstract class TbAbstractGetEntityDetailsNodeConfiguration { + + + private List detailsList; + + private boolean addToMetadata; + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java new file mode 100644 index 0000000000..a0b34a00f9 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java @@ -0,0 +1,142 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.EntityDetails; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.msg.TbMsg; + +@Slf4j +@RuleNode(type = ComponentType.ENRICHMENT, + name = "customer details", + configClazz = TbGetCustomerDetailsNodeConfiguration.class, + nodeDescription = "Node find the customer of the message originator and fetch his details that selected from the drop-down list and add them to the message if they exist.", + nodeDetails = "If selected checkbox: Add selected details to the message metadata, existing fields will add to the message metadata instead of message data.

    " + + "Note: only Device, Asset, and Entity View type are allowed.

    " + + "If the originator of the message didn't assign to Customer, or originator type is not supported - Message send via Failure chain, otherwise, Success chain will be used.", + uiResources = {"static/rulenode/rulenode-core-config.js"}, + configDirective = "tbEnrichmentNodeEntityDetailsConfig") +public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { + + private static final String CUSTOMER_PREFIX = "customer_"; + + @Override + protected TbGetCustomerDetailsNodeConfiguration loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + return TbNodeUtils.convert(configuration, TbGetCustomerDetailsNodeConfiguration.class); + } + + @Override + protected TbMsg getDetails(TbContext ctx, TbMsg msg) { + return getCustomerTbMsg(ctx, msg, getDataAsJson(msg)); + } + + private TbMsg getCustomerTbMsg(TbContext ctx, TbMsg msg, MessageData messageData) { + JsonElement resultObject = null; + if (!config.getDetailsList().isEmpty()) { + for (EntityDetails entityDetails : config.getDetailsList()) { + resultObject = addCustomerProperties(messageData.getData(), getCustomer(ctx, msg), entityDetails); + } + return transformMsg(ctx, msg, resultObject, messageData); + } else { + return msg; + } + } + + private Customer getCustomer(TbContext ctx, TbMsg msg) { + switch (msg.getOriginator().getEntityType()) { + case DEVICE: + Device device = ctx.getDeviceService().findDeviceById(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId())); + if (!device.getCustomerId().isNullUid()) { + return ctx.getCustomerService().findCustomerById(ctx.getTenantId(), device.getCustomerId()); + } else { + throw new RuntimeException("Device with name '" + device.getName() + "' didn't assign to Customer."); + } + case ASSET: + Asset asset = ctx.getAssetService().findAssetById(ctx.getTenantId(), new AssetId(msg.getOriginator().getId())); + if (!asset.getCustomerId().isNullUid()) { + return ctx.getCustomerService().findCustomerById(ctx.getTenantId(), asset.getCustomerId()); + } else { + throw new RuntimeException("Asset with name '" + asset.getName() + "' didn't assign to Customer."); + } + case ENTITY_VIEW: + EntityView entityView = ctx.getEntityViewService().findEntityViewById(ctx.getTenantId(), new EntityViewId(msg.getOriginator().getId())); + if (!entityView.getCustomerId().isNullUid()) { + return ctx.getCustomerService().findCustomerById(ctx.getTenantId(), entityView.getCustomerId()); + } else { + throw new RuntimeException("EntityView with name '" + entityView.getName() + "' didn't assign to Customer."); + } + default: + throw new RuntimeException("Entity with entityType '" + msg.getOriginator().getEntityType() + "' can't be assigned to Customer."); + } + } + + private JsonElement addCustomerProperties(JsonElement data, Customer customer, EntityDetails entityDetails) { + JsonObject dataAsObject = data.getAsJsonObject(); + switch (entityDetails) { + case ADDRESS: + if (customer.getAddress() != null) + dataAsObject.addProperty(CUSTOMER_PREFIX + "address", customer.getAddress()); + break; + case ADDRESS2: + if (customer.getAddress2() != null) + dataAsObject.addProperty(CUSTOMER_PREFIX + "address2", customer.getAddress2()); + break; + case CITY: + if (customer.getCity() != null) dataAsObject.addProperty(CUSTOMER_PREFIX + "city", customer.getCity()); + break; + case COUNTRY: + if (customer.getCountry() != null) + dataAsObject.addProperty(CUSTOMER_PREFIX + "country", customer.getCountry()); + break; + case STATE: + if (customer.getState() != null) + dataAsObject.addProperty(CUSTOMER_PREFIX + "state", customer.getState()); + break; + case EMAIL: + if (customer.getEmail() != null) + dataAsObject.addProperty(CUSTOMER_PREFIX + "email", customer.getEmail()); + break; + case PHONE: + if (customer.getPhone() != null) + dataAsObject.addProperty(CUSTOMER_PREFIX + "phone", customer.getPhone()); + break; + case ZIP: + if (customer.getZip() != null) dataAsObject.addProperty(CUSTOMER_PREFIX + "zip", customer.getZip()); + break; + case ADDITIONAL_INFO: + if (customer.getAdditionalInfo().hasNonNull("description")) { + dataAsObject.addProperty(CUSTOMER_PREFIX + "additionalInfo", customer.getAdditionalInfo().get("description").asText()); + } + break; + } + return dataAsObject; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java new file mode 100644 index 0000000000..39a3e45c5f --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import lombok.Data; +import org.thingsboard.rule.engine.api.NodeConfiguration; + +import java.util.Collections; + +@Data +public class TbGetCustomerDetailsNodeConfiguration extends TbAbstractGetEntityDetailsNodeConfiguration implements NodeConfiguration { + + + @Override + public TbGetCustomerDetailsNodeConfiguration defaultConfiguration() { + TbGetCustomerDetailsNodeConfiguration configuration = new TbGetCustomerDetailsNodeConfiguration(); + configuration.setDetailsList(Collections.emptyList()); + return configuration; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java new file mode 100644 index 0000000000..064d90f9b3 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.EntityDetails; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.msg.TbMsg; + +@Slf4j +@RuleNode(type = ComponentType.ENRICHMENT, + name = "tenant details", + configClazz = TbGetTenantDetailsNodeConfiguration.class, + nodeDescription = "Node fetch current Tenant details that selected from the drop-down list and add them to the message if they exist.", + nodeDetails = "If selected checkbox: Add selected details to the message metadata, existing fields will add to the message metadata instead of message data.", + uiResources = {"static/rulenode/rulenode-core-config.js"}, + configDirective = "tbEnrichmentNodeEntityDetailsConfig") +public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { + + + private static final String TENANT_PREFIX = "tenant_"; + + @Override + protected TbGetTenantDetailsNodeConfiguration loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + return TbNodeUtils.convert(configuration, TbGetTenantDetailsNodeConfiguration.class); + } + + @Override + protected TbMsg getDetails(TbContext ctx, TbMsg msg) { + return getTenantTbMsg(ctx, msg, getDataAsJson(msg)); + } + + private TbMsg getTenantTbMsg(TbContext ctx, TbMsg msg, MessageData messageData) { + JsonElement resultObject = null; + Tenant tenant = ctx.getTenantService().findTenantById(ctx.getTenantId()); + if (!config.getDetailsList().isEmpty()) { + for (EntityDetails entityDetails : config.getDetailsList()) { + resultObject = addTenantProperties(messageData.getData(), tenant, entityDetails); + } + return transformMsg(ctx, msg, resultObject, messageData); + } else { + return msg; + } + } + + private JsonElement addTenantProperties(JsonElement data, Tenant tenant, EntityDetails entityDetails) { + JsonObject dataAsObject = data.getAsJsonObject(); + switch (entityDetails) { + case ADDRESS: + if (tenant.getAddress() != null) + dataAsObject.addProperty(TENANT_PREFIX + "address", tenant.getAddress()); + break; + case ADDRESS2: + if (tenant.getAddress2() != null) + dataAsObject.addProperty(TENANT_PREFIX + "address2", tenant.getAddress2()); + break; + case CITY: + if (tenant.getCity() != null) dataAsObject.addProperty(TENANT_PREFIX + "city", tenant.getCity()); + break; + case COUNTRY: + if (tenant.getCountry() != null) + dataAsObject.addProperty(TENANT_PREFIX + "country", tenant.getCountry()); + break; + case STATE: + if (tenant.getState() != null) dataAsObject.addProperty(TENANT_PREFIX + "state", tenant.getState()); + break; + case EMAIL: + if (tenant.getEmail() != null) dataAsObject.addProperty(TENANT_PREFIX + "email", tenant.getEmail()); + break; + case PHONE: + if (tenant.getPhone() != null) dataAsObject.addProperty(TENANT_PREFIX + "phone", tenant.getPhone()); + break; + case ZIP: + if (tenant.getZip() != null) dataAsObject.addProperty(TENANT_PREFIX + "zip", tenant.getZip()); + break; + case ADDITIONAL_INFO: + if (tenant.getAdditionalInfo().hasNonNull("description")) { + dataAsObject.addProperty(TENANT_PREFIX + "additionalInfo", tenant.getAdditionalInfo().get("description").asText()); + } + break; + } + return dataAsObject; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java new file mode 100644 index 0000000000..4158104e31 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import lombok.Data; +import org.thingsboard.rule.engine.api.NodeConfiguration; + +import java.util.Collections; + +@Data +public class TbGetTenantDetailsNodeConfiguration extends TbAbstractGetEntityDetailsNodeConfiguration implements NodeConfiguration { + + + @Override + public TbGetTenantDetailsNodeConfiguration defaultConfiguration() { + TbGetTenantDetailsNodeConfiguration configuration = new TbGetTenantDetailsNodeConfiguration(); + configuration.setDetailsList(Collections.emptyList()); + return configuration; + } +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java new file mode 100644 index 0000000000..8e87af959d --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +public enum EntityDetails { + + COUNTRY, CITY, STATE, ZIP, ADDRESS, ADDRESS2, PHONE, EMAIL, ADDITIONAL_INFO + +} diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index dae2d132d7..0aa43b327d 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,5 +1,6 @@ -!function(e){function t(i){if(n[i])return n[i].exports;var a=n[i]={exports:{},id:i,loaded:!1};return e[i].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),i=e[t[0]];return function(e,t,a){i.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(97)},function(e,t){},1,1,1,1,function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    {{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{scope.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    tb.rulenode.alarm-type-required
    tb.rulenode.entity-type-pattern-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    {{ 'tb.rulenode.use-message-alarm-data' | translate }}
    tb.rulenode.alarm-type-required
    {{ severity.name | translate}}
    tb.rulenode.alarm-severity-required
    {{ 'tb.rulenode.propagate' | translate }}
    "},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.entity-type-pattern-required
    tb.rulenode.entity-type-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    {{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
    tb.rulenode.create-entity-if-not-exists-hint
    {{ 'tb.rulenode.remove-current-relations' | translate }}
    tb.rulenode.remove-current-relations-hint
    {{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
    tb.rulenode.change-originator-to-related-entity-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
    tb.rulenode.delete-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    tb.rulenode.message-count-required
    tb.rulenode.min-message-count-message
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-seconds-message
    {{ 'tb.rulenode.test-generator-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.latitude-key-name-required
    tb.rulenode.longitude-key-name-required
    {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
    {{ type.name | translate}}
    tb.rulenode.circle-center-latitude-required
    tb.rulenode.circle-center-longitude-required
    tb.rulenode.range-required
    {{ type.name | translate}}
    tb.rulenode.polygon-definition-required
    tb.rulenode.polygon-definition-hint
    tb.rulenode.min-inside-duration-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.min-outside-duration-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    '},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.bootstrap-servers-required
    tb.rulenode.min-retries-message
    tb.rulenode.min-batch-size-bytes-message
    tb.rulenode.min-linger-ms-message
    tb.rulenode.min-buffer-memory-bytes-message
    {{ ackValue }}
    tb.rulenode.key-serializer-required
    tb.rulenode.value-serializer-required
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-to-string-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.mqtt-topic-pattern-hint
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    tb.rulenode.connect-timeout-required
    tb.rulenode.connect-timeout-range
    tb.rulenode.connect-timeout-range
    {{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{credentialsValue.name | translate}}
    tb.rulenode.credentials-type-required
    tb.rulenode.username-required
    tb.rulenode.password-required
    '},function(e,t){e.exports="
    tb.rulenode.interval-seconds-required
    tb.rulenode.min-interval-seconds-message
    tb.rulenode.output-timeseries-key-prefix-required
    "; -},function(e,t){e.exports="
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-0-seconds-message
    tb.rulenode.max-pending-messages-required
    tb.rulenode.max-pending-messages-range
    tb.rulenode.max-pending-messages-range
    "},function(e,t){e.exports='
    {{ property }}
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    {{ \'tb.rulenode.automatic-recovery\' | translate }}
    tb.rulenode.min-connection-timeout-ms-message
    tb.rulenode.min-handshake-timeout-ms-message
    '},function(e,t){e.exports='
    tb.rulenode.endpoint-url-pattern-required
    tb.rulenode.endpoint-url-pattern-hint
    {{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
    tb.rulenode.headers-hint
    '},function(e,t){e.exports="
    "},function(e,t){e.exports="
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-message
    "},function(e,t){e.exports='
    tb.rulenode.custom-table-name-required
    tb.rulenode.custom-table-hint
    '},function(e,t){e.exports='
    {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
    {{smtpProtocol.toUpperCase()}}
    tb.rulenode.smtp-host-required
    tb.rulenode.smtp-port-required
    tb.rulenode.smtp-port-range
    tb.rulenode.smtp-port-range
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-msec-message
    {{ \'tb.rulenode.enable-tls\' | translate }}
    '},function(e,t){e.exports="
    tb.rulenode.topic-arn-pattern-required
    tb.rulenode.topic-arn-pattern-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    "},function(e,t){e.exports='
    {{ type.name | translate }}
    tb.rulenode.queue-url-pattern-required
    tb.rulenode.queue-url-pattern-hint
    tb.rulenode.min-delay-seconds-message
    tb.rulenode.max-delay-seconds-message
    tb.rulenode.message-attributes-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    '},function(e,t){e.exports="
    tb.rulenode.default-ttl-required
    tb.rulenode.min-default-ttl-message
    "},function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{ (\'relation.search-direction.\' + direction) | translate}}
    relation.relation-type
    device.device-types
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},function(e,t){e.exports='
    '},function(e,t){e.exports='
    {{ type }}
    tb.rulenode.fetch-mode-hint
    {{ type }}
    tb.rulenode.order-by-hint
    {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
    tb.rulenode.use-metadata-interval-patterns-hint
    tb.rulenode.start-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.end-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.start-interval-pattern-required
    tb.rulenode.start-interval-pattern-hint
    tb.rulenode.end-interval-pattern-required
    tb.rulenode.end-interval-pattern-hint
    '},function(e,t){e.exports='
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},30,function(e,t){e.exports='
    tb.rulenode.separator-hint
    tb.rulenode.separator-hint
    {{ \'tb.rulenode.check-all-keys\' | translate }}
    tb.rulenode.check-all-keys-hint
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
    tb.rulenode.check-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    "},function(e,t){e.exports='
    tb.rulenode.latitude-key-name-required
    tb.rulenode.longitude-key-name-required
    {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
    {{ type.name | translate}}
    tb.rulenode.circle-center-latitude-required
    tb.rulenode.circle-center-longitude-required
    tb.rulenode.range-required
    {{ type.name | translate}}
    tb.rulenode.polygon-definition-required
    tb.rulenode.polygon-definition-hint
    '; -},function(e,t){e.exports='
    {{item}}
    tb.rulenode.no-message-types-found
    tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
    {{$chip.name}}
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-filter-function' | translate }}
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-switch-function' | translate }}
    "},function(e,t){e.exports='
    {{ keyText }} {{ valText }}  
    {{keyRequiredText}}
    {{valRequiredText}}
    {{ \'tb.key-val.remove-entry\' | translate }} close
    {{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
    '},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    relation.relation-filters
    "},function(e,t){e.exports='
    {{ source.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-transformer-function' | translate }}
    "},function(e,t){e.exports="
    tb.rulenode.from-template-required
    tb.rulenode.from-template-hint
    tb.rulenode.to-template-required
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.subject-template-required
    tb.rulenode.subject-template-hint
    tb.rulenode.body-template-required
    tb.rulenode.body-template-hint
    "},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(6),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(7),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(8),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(9),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(10),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(11),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.originator=null,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue,a.configuration.originatorId&&a.configuration.originatorType?a.originator={id:a.configuration.originatorId,entityType:a.configuration.originatorType}:a.originator=null,a.$watch("originator",function(e,t){angular.equals(e,t)||(a.originator?(s.$viewValue.originatorId=a.originator.id,s.$viewValue.originatorType=a.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},a.testScript=function(e){var n=angular.copy(a.configuration.jsScript);i.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var r=n(12),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(13),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(71),r=i(a),o=n(50),l=i(o),s=n(55),d=i(s),u=n(52),c=i(u),m=n(51),g=i(m),p=n(59),f=i(p),b=n(65),v=i(b),y=n(66),q=i(y),h=n(64),$=i(h),x=n(58),k=i(x),T=n(69),C=i(T),w=n(70),M=i(w),N=n(63),_=i(N),S=n(60),E=i(S),F=n(68),P=i(F),V=n(62),A=i(V),j=n(61),O=i(j),I=n(49),R=i(I),D=n(72),K=i(D),L=n(54),U=i(L),z=n(53),B=i(z),H=n(67),Y=i(H),G=n(56),Q=i(G);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",A.default).directive("tbActionNodeMsgCountConfig",O.default).directive("tbActionNodeAssignToCustomerConfig",R.default).directive("tbActionNodeUnAssignToCustomerConfig",K.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",Y.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(14),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(15),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$mdExpansionPanel=t,i.ruleNodeTypes=n,i.credentialsTypeChanged=function(){var e=i.configuration.credentials.type;i.configuration.credentials={},i.configuration.credentials.type=e,i.updateValidity()},i.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){i.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(i.configuration.credentials.caCertFileName=e.name,i.configuration.credentials.caCert=a),"privateKey"==t&&(i.configuration.credentials.privateKeyFileName=e.name,i.configuration.credentials.privateKey=a),"Cert"==t&&(i.configuration.credentials.certFileName=e.name,i.configuration.credentials.cert=a)),i.updateValidity()}})},n.readAsText(e.file)},i.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(i.configuration.credentials.caCertFileName=null,i.configuration.credentials.caCert=null),"privateKey"==e&&(i.configuration.credentials.privateKeyFileName=null,i.configuration.credentials.privateKey=null),"Cert"==e&&(i.configuration.credentials.certFileName=null,i.configuration.credentials.cert=null),i.updateValidity()},i.updateValidity=function(){var e=!0,t=i.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var r=n(16),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(17),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(18),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(19),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(20),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(21),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(22),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(23),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(24),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(25),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(26),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(27),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(28),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(29),o=i(r)},function(e,t){"use strict";function n(e){var t=function(t,n,i,a){n.html("
    "),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(30),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(31),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s);var d=186;i.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],i.ruleNodeTypes=n,i.aggPeriodTimeUnits={},i.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,i.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,i.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,i.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,i.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{},link:i}}a.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(32),o=i(r);n(3)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(79),r=i(a),o=n(80),l=i(o),s=n(76),d=i(s),u=n(81),c=i(u),m=n(75),g=i(m),p=n(82),f=i(p),b=n(77),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(33),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(34),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(35),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(36),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(37),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(38),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){ -var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(39),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(89),r=i(a),o=n(87),l=i(o),s=n(90),d=i(s),u=n(84),c=i(u),m=n(88),g=i(m),p=n(83),f=i(p),b=n(85),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=a,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(44),o=i(r);n(5)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(45),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(46),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(93),r=i(a),o=n(95),l=i(o),s=n(96),d=i(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(47),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(48),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(100),r=i(a),o=n(86),l=i(o),s=n(78),d=i(s),u=n(94),c=i(u),m=n(57),g=i(m),p=n(74),f=i(p),b=n(92),v=i(b),y=n(73),q=i(y),h=n(91),$=i(h),x=n(99),k=i(x);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",$.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lon2,lon4], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){(0,o.default)(e)}a.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(98),o=i(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); +!function(e){function t(i){if(n[i])return n[i].exports;var a=n[i]={exports:{},id:i,loaded:!1};return e[i].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),i=e[t[0]];return function(e,t,a){i.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(99)},function(e,t){},1,1,1,1,function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    {{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{scope.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    tb.rulenode.alarm-type-required
    tb.rulenode.entity-type-pattern-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-details-function' | translate }}
    {{ 'tb.rulenode.use-message-alarm-data' | translate }}
    tb.rulenode.alarm-type-required
    {{ severity.name | translate}}
    tb.rulenode.alarm-severity-required
    {{ 'tb.rulenode.propagate' | translate }}
    "},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.entity-type-pattern-required
    tb.rulenode.entity-type-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    {{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
    tb.rulenode.create-entity-if-not-exists-hint
    {{ 'tb.rulenode.remove-current-relations' | translate }}
    tb.rulenode.remove-current-relations-hint
    {{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
    tb.rulenode.change-originator-to-related-entity-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
    tb.rulenode.delete-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    tb.rulenode.entity-name-pattern-required
    tb.rulenode.entity-name-pattern-hint
    tb.rulenode.relation-type-pattern-required
    tb.rulenode.relation-type-pattern-hint
    tb.rulenode.entity-cache-expiration-required
    tb.rulenode.entity-cache-expiration-range
    tb.rulenode.entity-cache-expiration-hint
    "},function(e,t){e.exports="
    tb.rulenode.message-count-required
    tb.rulenode.min-message-count-message
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-seconds-message
    {{ 'tb.rulenode.test-generator-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.latitude-key-name-required
    tb.rulenode.longitude-key-name-required
    {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
    {{ type.name | translate}}
    tb.rulenode.circle-center-latitude-required
    tb.rulenode.circle-center-longitude-required
    tb.rulenode.range-required
    {{ type.name | translate}}
    tb.rulenode.polygon-definition-required
    tb.rulenode.polygon-definition-hint
    tb.rulenode.min-inside-duration-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.min-outside-duration-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    '},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.bootstrap-servers-required
    tb.rulenode.min-retries-message
    tb.rulenode.min-batch-size-bytes-message
    tb.rulenode.min-linger-ms-message
    tb.rulenode.min-buffer-memory-bytes-message
    {{ ackValue }}
    tb.rulenode.key-serializer-required
    tb.rulenode.value-serializer-required
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-to-string-function' | translate }}
    "},function(e,t){e.exports='
    tb.rulenode.topic-pattern-required
    tb.rulenode.mqtt-topic-pattern-hint
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    tb.rulenode.connect-timeout-required
    tb.rulenode.connect-timeout-range
    tb.rulenode.connect-timeout-range
    {{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{ \'tb.rulenode.credentials\' | translate }}
    {{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
    {{credentialsValue.name | translate}}
    tb.rulenode.credentials-type-required
    tb.rulenode.username-required
    tb.rulenode.password-required
    '},function(e,t){e.exports="
    tb.rulenode.interval-seconds-required
    tb.rulenode.min-interval-seconds-message
    tb.rulenode.output-timeseries-key-prefix-required
    "; +},function(e,t){e.exports="
    tb.rulenode.period-seconds-required
    tb.rulenode.min-period-0-seconds-message
    tb.rulenode.max-pending-messages-required
    tb.rulenode.max-pending-messages-range
    tb.rulenode.max-pending-messages-range
    "},function(e,t){e.exports='
    {{ property }}
    tb.rulenode.host-required
    tb.rulenode.port-required
    tb.rulenode.port-range
    tb.rulenode.port-range
    {{ \'tb.rulenode.automatic-recovery\' | translate }}
    tb.rulenode.min-connection-timeout-ms-message
    tb.rulenode.min-handshake-timeout-ms-message
    '},function(e,t){e.exports='
    tb.rulenode.endpoint-url-pattern-required
    tb.rulenode.endpoint-url-pattern-hint
    {{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
    tb.rulenode.headers-hint
    '},function(e,t){e.exports="
    "},function(e,t){e.exports="
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-message
    "},function(e,t){e.exports='
    tb.rulenode.custom-table-name-required
    tb.rulenode.custom-table-hint
    '},function(e,t){e.exports='
    {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
    {{smtpProtocol.toUpperCase()}}
    tb.rulenode.smtp-host-required
    tb.rulenode.smtp-port-required
    tb.rulenode.smtp-port-range
    tb.rulenode.smtp-port-range
    tb.rulenode.timeout-required
    tb.rulenode.min-timeout-msec-message
    {{ \'tb.rulenode.enable-tls\' | translate }}
    '},function(e,t){e.exports="
    tb.rulenode.topic-arn-pattern-required
    tb.rulenode.topic-arn-pattern-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    "},function(e,t){e.exports='
    {{ type.name | translate }}
    tb.rulenode.queue-url-pattern-required
    tb.rulenode.queue-url-pattern-hint
    tb.rulenode.min-delay-seconds-message
    tb.rulenode.max-delay-seconds-message
    tb.rulenode.message-attributes-hint
    tb.rulenode.aws-access-key-id-required
    tb.rulenode.aws-secret-access-key-required
    tb.rulenode.aws-region-required
    '},function(e,t){e.exports="
    tb.rulenode.default-ttl-required
    tb.rulenode.min-default-ttl-message
    "},function(e,t){e.exports="
    tb.rulenode.customer-name-pattern-required
    tb.rulenode.customer-name-pattern-hint
    tb.rulenode.customer-cache-expiration-required
    tb.rulenode.customer-cache-expiration-range
    tb.rulenode.customer-cache-expiration-hint
    "},function(e,t){e.exports='
    {{ (\'relation.search-direction.\' + direction) | translate}}
    relation.relation-type
    device.device-types
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},function(e,t){e.exports='
    '},function(e,t){e.exports='
    {{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
    tb.rulenode.add-to-metadata-hint
    '},function(e,t){e.exports='
    {{ type }}
    tb.rulenode.fetch-mode-hint
    {{ type }}
    tb.rulenode.order-by-hint
    {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
    tb.rulenode.use-metadata-interval-patterns-hint
    tb.rulenode.start-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.end-interval-value-required
    tb.rulenode.time-value-range
    tb.rulenode.time-value-range
    {{timeUnit.name | translate}}
    tb.rulenode.start-interval-pattern-required
    tb.rulenode.start-interval-pattern-hint
    tb.rulenode.end-interval-pattern-required
    tb.rulenode.end-interval-pattern-hint
    '},function(e,t){e.exports='
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.latest-telemetry' | translate }}
    "},30,function(e,t){e.exports='
    tb.rulenode.separator-hint
    tb.rulenode.separator-hint
    {{ \'tb.rulenode.check-all-keys\' | translate }}
    tb.rulenode.check-all-keys-hint
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
    tb.rulenode.check-relation-hint
    {{ ('relation.search-direction.' + direction) | translate}}
    "; +},function(e,t){e.exports='
    tb.rulenode.latitude-key-name-required
    tb.rulenode.longitude-key-name-required
    {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
    {{ type.name | translate}}
    tb.rulenode.circle-center-latitude-required
    tb.rulenode.circle-center-longitude-required
    tb.rulenode.range-required
    {{ type.name | translate}}
    tb.rulenode.polygon-definition-required
    tb.rulenode.polygon-definition-hint
    '},function(e,t){e.exports='
    {{item}}
    tb.rulenode.no-message-types-found
    tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
    {{$chip.name}}
    '},function(e,t){e.exports='
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-filter-function' | translate }}
    "},function(e,t){e.exports="
    {{ 'tb.rulenode.test-switch-function' | translate }}
    "},function(e,t){e.exports='
    {{ keyText }} {{ valText }}  
    {{keyRequiredText}}
    {{valRequiredText}}
    {{ \'tb.key-val.remove-entry\' | translate }} close
    {{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
    '},function(e,t){e.exports="
    {{ ('relation.search-direction.' + direction) | translate}}
    relation.relation-filters
    "},function(e,t){e.exports='
    {{ source.name | translate}}
    '},function(e,t){e.exports="
    {{ 'tb.rulenode.test-transformer-function' | translate }}
    "},function(e,t){e.exports="
    tb.rulenode.from-template-required
    tb.rulenode.from-template-hint
    tb.rulenode.to-template-required
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.mail-address-list-template-hint
    tb.rulenode.subject-template-required
    tb.rulenode.subject-template-hint
    tb.rulenode.body-template-required
    tb.rulenode.body-template-hint
    "},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(6),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(7),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(8),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);i.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(9),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(10),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(11),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,i){var a=function(a,r,l,s){var d=o.default;r.html(d),a.types=n,a.originator=null,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue,a.configuration.originatorId&&a.configuration.originatorType?a.originator={id:a.configuration.originatorId,entityType:a.configuration.originatorType}:a.originator=null,a.$watch("originator",function(e,t){angular.equals(e,t)||(a.originator?(s.$viewValue.originatorId=a.originator.id,s.$viewValue.originatorType=a.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},a.testScript=function(e){var n=angular.copy(a.configuration.jsScript);i.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var r=n(12),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(13),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(72),r=i(a),o=n(51),l=i(o),s=n(56),d=i(s),u=n(53),c=i(u),m=n(52),g=i(m),p=n(60),f=i(p),b=n(66),v=i(b),y=n(67),h=i(y),q=n(65),$=i(q),x=n(59),k=i(x),T=n(70),C=i(T),w=n(71),M=i(w),N=n(64),_=i(N),S=n(61),E=i(S),P=n(69),F=i(P),V=n(63),A=i(V),I=n(62),j=i(I),O=n(50),D=i(O),R=n(73),K=i(R),L=n(55),U=i(L),z=n(54),B=i(z),H=n(68),Y=i(H),G=n(57),Q=i(G);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",E.default).directive("tbActionNodeSendEmailConfig",F.default).directive("tbActionNodeMsgDelayConfig",A.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",K.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",B.default).directive("tbActionNodeCustomTableConfig",Y.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(14),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(15),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$mdExpansionPanel=t,i.ruleNodeTypes=n,i.credentialsTypeChanged=function(){var e=i.configuration.credentials.type;i.configuration.credentials={},i.configuration.credentials.type=e,i.updateValidity()},i.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){i.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(i.configuration.credentials.caCertFileName=e.name,i.configuration.credentials.caCert=a),"privateKey"==t&&(i.configuration.credentials.privateKeyFileName=e.name,i.configuration.credentials.privateKey=a),"Cert"==t&&(i.configuration.credentials.certFileName=e.name,i.configuration.credentials.cert=a)),i.updateValidity()}})},n.readAsText(e.file)},i.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(i.configuration.credentials.caCertFileName=null,i.configuration.credentials.caCert=null),"privateKey"==e&&(i.configuration.credentials.privateKeyFileName=null,i.configuration.credentials.privateKey=null),"Cert"==e&&(i.configuration.credentials.certFileName=null,i.configuration.credentials.cert=null),i.updateValidity()},i.updateValidity=function(){var e=!0,t=i.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:i}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var r=n(16),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(17),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(18),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(19),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(20),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(21),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(22),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(23),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(24),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(25),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(26),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(27),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(28),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(29),o=i(r)},function(e,t){"use strict";function n(e){var t=function(t,n,i,a){n.html("
    "),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(30),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(31),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(32),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){ +var s=o.default;a.html(s);var d=186;i.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],i.ruleNodeTypes=n,i.aggPeriodTimeUnits={},i.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,i.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,i.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,i.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,i.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{},link:i}}a.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(33),o=i(r);n(3)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(81),r=i(a),o=n(82),l=i(o),s=n(77),d=i(s),u=n(83),c=i(u),m=n(76),g=i(m),p=n(84),f=i(p),b=n(79),v=i(b),y=n(78),h=i(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(34),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(35),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(36),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(37),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(38),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(39),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(40),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(91),r=i(a),o=n(89),l=i(o),s=n(92),d=i(s),u=n(86),c=i(u),m=n(90),g=i(m),p=n(85),f=i(p),b=n(87),v=i(b);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=a,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(45),o=i(r);n(5)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(46),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,i,a,r){var l=o.default;i.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(i.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(47),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(95),r=i(a),o=n(97),l=i(o),s=n(98),d=i(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var i=function(i,a,r,l){var s=o.default;a.html(s),i.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(i.configuration)}),l.$render=function(){i.configuration=l.$viewValue},i.testScript=function(e){var a=angular.copy(i.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(48),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,i,a){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(49),o=i(r)},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(102),r=i(a),o=n(88),l=i(o),s=n(80),d=i(s),u=n(96),c=i(u),m=n(58),g=i(m),p=n(75),f=i(p),b=n(94),v=i(b),y=n(74),h=i(y),q=n(93),$=i(q),x=n(101),k=i(x);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",$.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","relation-type-pattern-required":"Relation type pattern is required","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory",headers:"Headers","headers-hint":"Use ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use ${metaKeyName} in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-country":"Country","entity-details-state":"State","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"You should enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lon2,lon4], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}function a(e){(0,o.default)(e)}a.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var r=n(100),o=i(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info", +value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file From d1c6a9073e19c4f5b3f2877413c85a0eda552934 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 3 Apr 2019 13:18:00 +0300 Subject: [PATCH 73/74] Improvements to GetEntityDetails Node --- .../TbAbstractGetEntityDetailsNode.java | 46 ++++++++++++++- .../metadata/TbGetCustomerDetailsNode.java | 57 +++---------------- .../metadata/TbGetTenantDetailsNode.java | 49 ++-------------- 3 files changed, 57 insertions(+), 95 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java index 7aedcf6aa9..e9d38c91fb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java @@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.metadata; import com.google.gson.Gson; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.reflect.TypeToken; import lombok.AllArgsConstructor; @@ -26,6 +27,8 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.EntityDetails; +import org.thingsboard.server.common.data.ContactBased; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -50,7 +53,7 @@ public abstract class TbAbstractGetEntityDetailsNodeNote: only Device, Asset, and Entity View type are allowed.

    " + - "If the originator of the message didn't assign to Customer, or originator type is not supported - Message send via Failure chain, otherwise, Success chain will be used.", + "If the originator of the message is not assigned to Customer, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig") public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { @@ -62,7 +62,7 @@ public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNodeNote: only Device, Asset, and Entity View type are allowed.

    " + + "If the originator of the message is not assigned to Tenant, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig") public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { - private static final String TENANT_PREFIX = "tenant_"; @Override @@ -56,50 +58,11 @@ public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode Date: Fri, 5 Apr 2019 16:19:46 +0300 Subject: [PATCH 74/74] Update maps.json --- .../src/main/data/json/system/widget_bundles/maps.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index 119c07e172..2364758198 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -27,7 +27,7 @@ "descriptor": { "type": "latest", "sizeX": 8.5, - "sizeY": 6, + "sizeY": 6.5, "resources": [], "templateHtml": "", "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", @@ -106,7 +106,7 @@ "name": "Tencent Maps", "descriptor": { "type": "latest", - "sizeX": 8.5, + "sizeX": 9, "sizeY": 6, "resources": [], "templateHtml": "", @@ -150,4 +150,4 @@ } } ] -} \ No newline at end of file +}