diff --git a/application/pom.xml b/application/pom.xml index ddd045a13..70f26290b 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -322,6 +322,10 @@ spring-cloud-starter-contract-stub-runner test + + io.kubernetes + client-java + diff --git a/application/src/it/java/com/hashmapinc/server/controller/BaseTempusGatewayConfigurationControllerTest.java b/application/src/it/java/com/hashmapinc/server/controller/BaseTempusGatewayConfigurationControllerTest.java new file mode 100644 index 000000000..ba9ad15be --- /dev/null +++ b/application/src/it/java/com/hashmapinc/server/controller/BaseTempusGatewayConfigurationControllerTest.java @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +public class BaseTempusGatewayConfigurationControllerTest extends AbstractControllerTest { + @Before + public void beforeTest() throws Exception { + loginTenantAdmin(); + } + + @Test + public void testSaveAndUpdateTempusGatewayConfiguration() throws Exception { + final TempusGatewayConfiguration saveTempusGatewayConfiguration = createTempusGatewayConfiguration(); + + saveTempusGatewayConfiguration.setGatewayToken("gateway-config-token-new"); + final TempusGatewayConfiguration updatedTempusGatewayConfiguration = + doPost("/api/configuration/tempusGateway", saveTempusGatewayConfiguration, TempusGatewayConfiguration.class); + Assert.assertNotNull(updatedTempusGatewayConfiguration); + Assert.assertEquals(saveTempusGatewayConfiguration.getGatewayToken(), updatedTempusGatewayConfiguration.getGatewayToken()); + } + + @Test + public void testFindTempusGatewayConfigurationByIdAndByTenantId() throws Exception { + final TempusGatewayConfiguration saveTempusGatewayConfiguration = createTempusGatewayConfiguration(); + final TempusGatewayConfiguration tempusGatewayConfigurationById = + doGet("/api/configuration/tempusGateway/"+saveTempusGatewayConfiguration.getId().getId().toString(), TempusGatewayConfiguration.class); + Assert.assertNotNull(tempusGatewayConfigurationById); + Assert.assertEquals(saveTempusGatewayConfiguration.getId(), tempusGatewayConfigurationById.getId()); + final TempusGatewayConfiguration tempusGatewayConfigurationByTenantId = + doGet("/api/configuration/tempusGateway", TempusGatewayConfiguration.class); + Assert.assertNotNull(tempusGatewayConfigurationByTenantId); + Assert.assertEquals(saveTempusGatewayConfiguration.getId(), tempusGatewayConfigurationByTenantId.getId()); + + } + + private TempusGatewayConfiguration createTempusGatewayConfiguration() throws Exception { + TempusGatewayConfiguration tempusGatewayConfiguration = new TempusGatewayConfiguration(); + tempusGatewayConfiguration.setReplicas(1); + tempusGatewayConfiguration.setGatewayToken("token"); + tempusGatewayConfiguration.setTenantId(tenantId); + TempusGatewayConfiguration saveTempusGatewayConfiguration = + doPost("/api/configuration/tempusGateway", tempusGatewayConfiguration, TempusGatewayConfiguration.class); + + Assert.assertNotNull(saveTempusGatewayConfiguration); + Assert.assertNotNull(saveTempusGatewayConfiguration.getId()); + Assert.assertTrue(saveTempusGatewayConfiguration.getCreatedTime() > 0); + Assert.assertEquals(tempusGatewayConfiguration.getTenantId(), saveTempusGatewayConfiguration.getTenantId()); + Assert.assertEquals(tempusGatewayConfiguration.getGatewayToken(), saveTempusGatewayConfiguration.getGatewayToken()); + return saveTempusGatewayConfiguration; + } +} diff --git a/application/src/it/java/com/hashmapinc/server/controller/nosql/TempusGatewayConfigurationControllerNoSqlTest.java b/application/src/it/java/com/hashmapinc/server/controller/nosql/TempusGatewayConfigurationControllerNoSqlTest.java new file mode 100644 index 000000000..c08529431 --- /dev/null +++ b/application/src/it/java/com/hashmapinc/server/controller/nosql/TempusGatewayConfigurationControllerNoSqlTest.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.controller.nosql; + +import com.hashmapinc.server.controller.BaseTempusGatewayConfigurationControllerTest; +import com.hashmapinc.server.dao.service.DaoNoSqlTest; + +@DaoNoSqlTest +public class TempusGatewayConfigurationControllerNoSqlTest extends BaseTempusGatewayConfigurationControllerTest { +} diff --git a/application/src/it/java/com/hashmapinc/server/controller/sql/TempusGatewayConfigurationControllerSqlTest.java b/application/src/it/java/com/hashmapinc/server/controller/sql/TempusGatewayConfigurationControllerSqlTest.java new file mode 100644 index 000000000..7b60c019d --- /dev/null +++ b/application/src/it/java/com/hashmapinc/server/controller/sql/TempusGatewayConfigurationControllerSqlTest.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.controller.sql; + +import com.hashmapinc.server.controller.BaseTempusGatewayConfigurationControllerTest; +import com.hashmapinc.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class TempusGatewayConfigurationControllerSqlTest extends BaseTempusGatewayConfigurationControllerTest { +} diff --git a/application/src/main/java/com/hashmapinc/server/config/TempusGatewayProperties.java b/application/src/main/java/com/hashmapinc/server/config/TempusGatewayProperties.java new file mode 100644 index 000000000..d76af9e22 --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/config/TempusGatewayProperties.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +@EnableConfigurationProperties(TempusGatewayProperties.class) +@ConfigurationProperties(prefix = "gateway") +public class TempusGatewayProperties { + + private String image; + private String host; + private int port; + private List commands; + + public TempusGatewayProperties() { + super(); + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public List getCommands() { + return commands; + } + + public void setCommands(List commands) { + this.commands = commands; + } + +} diff --git a/application/src/main/java/com/hashmapinc/server/controller/BaseController.java b/application/src/main/java/com/hashmapinc/server/controller/BaseController.java index 942a3f56d..92f49a0d1 100644 --- a/application/src/main/java/com/hashmapinc/server/controller/BaseController.java +++ b/application/src/main/java/com/hashmapinc/server/controller/BaseController.java @@ -49,6 +49,7 @@ import com.hashmapinc.server.dao.device.DeviceService; import com.hashmapinc.server.dao.exception.DataValidationException; import com.hashmapinc.server.dao.exception.IncorrectParameterException; +import com.hashmapinc.server.dao.gatewayconfiguration.TempusGatewayConfigurationService; import com.hashmapinc.server.dao.metadataingestion.MetadataIngestionService; import com.hashmapinc.server.dao.model.ModelConstants; import com.hashmapinc.server.dao.plugin.PluginService; @@ -61,6 +62,7 @@ import com.hashmapinc.server.exception.TempusErrorResponseHandler; import com.hashmapinc.server.exception.TempusException; import com.hashmapinc.server.service.component.ComponentDiscoveryService; +import com.hashmapinc.server.service.kubernetes.gateway.TempusGatewayKubernetesService; import com.hashmapinc.server.service.metadataingestion.MetadataConfigService; import com.hashmapinc.server.service.metadataingestion.MetadataQueryService; import com.hashmapinc.server.service.security.auth.AttributeBasedPermissionEvaluator; @@ -168,6 +170,12 @@ public abstract class BaseController { @Autowired protected CustomerGroupService customerGroupService; + @Autowired + protected TempusGatewayConfigurationService tempusGatewayConfigurationService; + + @Autowired + protected TempusGatewayKubernetesService tempusGatewayKubernetesService; + @Autowired protected AttributeBasedPermissionEvaluator evaluator; @@ -313,6 +321,22 @@ CustomerGroup checkCustomerGroupId(CustomerGroupId customerGroupId) throws Tempu } } + TempusGatewayConfiguration checkTempusGatewayConfigurationId(TempusGatewayConfigurationId tempusGatewayConfigurationId) throws TempusException { + try { + validateId(tempusGatewayConfigurationId, "Incorrect tempusGatewayConfigurationId " + tempusGatewayConfigurationId); + SecurityUser authUser = getCurrentUser(); + TempusGatewayConfiguration tempusGatewayConfiguration = tempusGatewayConfigurationService.findTempusGatewayConfigurationById(tempusGatewayConfigurationId); + checkTempusGatewayConfiguration(tempusGatewayConfiguration); + if (!tempusGatewayConfiguration.getTenantId().getId().equals(authUser.getTenantId().getId())) { + throw new TempusException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, + TempusErrorCode.PERMISSION_DENIED); + } + return tempusGatewayConfiguration; + } catch (Exception e) { + throw handleException(e, false); + } + } + Long checkLong(String value, String paramName) { try { return Long.parseLong(value); @@ -341,6 +365,11 @@ private void checkCustomerGroup(CustomerGroup customerGroup) throws TempusExcept checkCustomerId(customerGroup.getCustomerId()); } + private void checkTempusGatewayConfiguration(TempusGatewayConfiguration tempusGatewayConfiguration) throws TempusException { + checkNotNull(tempusGatewayConfiguration); + checkTenantId(tempusGatewayConfiguration.getTenantId()); + } + User checkUserId(UserId userId) throws TempusException { try { validateId(userId, "Incorrect userId " + userId); diff --git a/application/src/main/java/com/hashmapinc/server/controller/TempusGatewayConfigurationController.java b/application/src/main/java/com/hashmapinc/server/controller/TempusGatewayConfigurationController.java new file mode 100644 index 000000000..af44c8c3f --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/controller/TempusGatewayConfigurationController.java @@ -0,0 +1,118 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.controller; + +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.id.TempusGatewayConfigurationId; +import com.hashmapinc.server.common.data.id.TenantId; +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetStatus; +import com.hashmapinc.server.exception.TempusException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController +@RequestMapping("/api/configuration/") +@Slf4j +public class TempusGatewayConfigurationController extends BaseController { + public static final String TEMPUS_GATEWAY_CONFIGURATION_ID = "tempusGatewayConfigurationId"; + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @GetMapping(value = "tempusGateway/{tempusGatewayConfigurationId}") + @ResponseBody + public TempusGatewayConfiguration getTempusGatewayConfigurationById(@PathVariable(TEMPUS_GATEWAY_CONFIGURATION_ID) String strTempusGatewayConfigurationId) + throws TempusException { + checkParameter(TEMPUS_GATEWAY_CONFIGURATION_ID, strTempusGatewayConfigurationId); + try { + TempusGatewayConfigurationId tempusGatewayConfigurationId = new TempusGatewayConfigurationId(toUUID(strTempusGatewayConfigurationId)); + return checkTempusGatewayConfigurationId(tempusGatewayConfigurationId); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @PostMapping(value = "/tempusGateway") + @ResponseBody + public TempusGatewayConfiguration saveTempusGatewayConfiguration(@RequestBody TempusGatewayConfiguration tempusGatewayConfiguration) throws TempusException { + try { + tempusGatewayConfiguration.setTenantId(getCurrentUser().getTenantId()); + TempusGatewayConfiguration savedTempusGatewayConfiguration = + checkNotNull(tempusGatewayConfigurationService.saveTempusGatewayConfiguration(tempusGatewayConfiguration)); + + log.debug("savedTempusGatewayConfiguration : [{}]", savedTempusGatewayConfiguration); + return savedTempusGatewayConfiguration; + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping(value = "/tempusGateway") + @ResponseBody + public TempusGatewayConfiguration getTempusGatewayConfiguration() throws TempusException { + try { + TenantId tenantId = getCurrentUser().getTenantId(); + return tempusGatewayConfigurationService.findTempusGatewayConfigurationByTenantId(tenantId).orElse(new TempusGatewayConfiguration()); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping(value = "/tempusGateway/deploy") + @ResponseBody + public Boolean deployTempusGateway() throws TempusException { + try { + TenantId tenantId = getCurrentUser().getTenantId(); + final Optional tempusGatewayConfigurationByTenantId = + tempusGatewayConfigurationService.findTempusGatewayConfigurationByTenantId(tenantId); + + if(!tempusGatewayConfigurationByTenantId.isPresent()){ + return false; + } + tempusGatewayConfigurationByTenantId.ifPresent(tempusGatewayConfiguration -> + tempusGatewayKubernetesService.deployTempusGateway(tempusGatewayConfiguration)); + return true; + } catch (Exception e) { + log.debug("Exception [{}]", e); + return false; + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @GetMapping(value = "/tempusGateway/status") + @ResponseBody + public ReplicaSetStatus getTempusGatewayPodsStatus() throws TempusException { + try { + TenantId tenantId = getCurrentUser().getTenantId(); + final Optional tempusGatewayConfiguration = + tempusGatewayConfigurationService.findTempusGatewayConfigurationByTenantId(tenantId); + + if(!tempusGatewayConfiguration.isPresent()){ + return null; + } + return tempusGatewayKubernetesService.getTempusGatewayReplicaSetStatus(tenantId); + + } catch (Exception e) { + log.debug("Exception [{}]", e); + return null; + } + } +} diff --git a/application/src/main/java/com/hashmapinc/server/service/kubernetes/KubernetesReplicaSetService.java b/application/src/main/java/com/hashmapinc/server/service/kubernetes/KubernetesReplicaSetService.java new file mode 100644 index 000000000..9c76f28d2 --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/service/kubernetes/KubernetesReplicaSetService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.service.kubernetes; + +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetConfig; +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetStatus; + +public interface KubernetesReplicaSetService { + + boolean deployReplicaSet(ReplicaSetConfig replicaSetConfig); + + ReplicaSetStatus getReplicaSetStatus(String labelSelector); + +} diff --git a/application/src/main/java/com/hashmapinc/server/service/kubernetes/KubernetesReplicaSetServiceImpl.java b/application/src/main/java/com/hashmapinc/server/service/kubernetes/KubernetesReplicaSetServiceImpl.java new file mode 100644 index 000000000..98187ed0b --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/service/kubernetes/KubernetesReplicaSetServiceImpl.java @@ -0,0 +1,147 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.service.kubernetes; + +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetConfig; +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetStatus; +import com.hashmapinc.server.utils.KubernetesConnectionClient; +import io.kubernetes.client.ApiException; +import io.kubernetes.client.models.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@Slf4j +public class KubernetesReplicaSetServiceImpl implements KubernetesReplicaSetService { + + public static final String DEFAULT_NAMESPACE = "default"; + public static final String REPLICA_KIND = "ReplicaSet"; + + @Autowired + KubernetesConnectionClient connectionClient; + + @Override + public boolean deployReplicaSet(ReplicaSetConfig replicaSetConfig) { + V1beta1ReplicaSet replicaSet = createReplicaSet(replicaSetConfig); + try { + connectionClient.getExtensionsV1beta1Api().createNamespacedReplicaSet(DEFAULT_NAMESPACE , replicaSet , null); + return true; + } catch (ApiException e) { + log.error("Error while deploying replica-set : [{}]", e.getMessage()); + } + return false; + } + + @Override + public ReplicaSetStatus getReplicaSetStatus(String labelSelector) { + ReplicaSetStatus replicaSetStatus = null; + try { + V1beta1ReplicaSetList v1beta1ReplicaSetList = connectionClient.getExtensionsV1beta1Api().listNamespacedReplicaSet(DEFAULT_NAMESPACE , null , null , null , null , labelSelector , null , null , null , null); + for (V1beta1ReplicaSet replicaSet: v1beta1ReplicaSetList.getItems() ) { + replicaSetStatus = createReplicaSetStatus(replicaSet.getStatus(), replicaSet.getStatus().getReplicas()); + } + } catch (ApiException e) { + log.error("Error while listing replica-set : [{}]", e.getMessage()); + } + log.error("ReplicaSetStatus [{}]", replicaSetStatus); + return replicaSetStatus; + } + + private ReplicaSetStatus createReplicaSetStatus(V1beta1ReplicaSetStatus status, int replicaCount) { + ReplicaSetStatus replicaSetStatus = new ReplicaSetStatus(); + replicaSetStatus.setReplica(replicaCount); + + if (status.getReadyReplicas() != null) { + replicaSetStatus.setReady(status.getReadyReplicas()); + } else { + replicaSetStatus.setReady(0); + } + + replicaSetStatus.setInProgress(status.getReplicas() - replicaSetStatus.getReady()); + replicaSetStatus.setCrashed(replicaCount - status.getReplicas()); + return replicaSetStatus; + } + + private V1beta1ReplicaSet createReplicaSet(ReplicaSetConfig replicaSetConfig) { + V1beta1ReplicaSet replicaSet = new V1beta1ReplicaSet(); + replicaSet.setKind(REPLICA_KIND); + replicaSet.setMetadata(createMetadata(replicaSetConfig.getReplicaName(), replicaSetConfig.getReplicaLabels())); + replicaSet.setSpec(createReplicaSetSpec(replicaSetConfig)); + return replicaSet; + } + + private V1beta1ReplicaSetSpec createReplicaSetSpec(ReplicaSetConfig replicaSetConfig) { + V1beta1ReplicaSetSpec spec = new V1beta1ReplicaSetSpec(); + spec.setReplicas(replicaSetConfig.getReplica()); + spec.setSelector(new V1LabelSelector() + .matchLabels(replicaSetConfig.getSelectorLabels()) + ); + spec.template(createPodTemplateSpec(replicaSetConfig)); + return spec; + } + + private V1PodTemplateSpec createPodTemplateSpec(ReplicaSetConfig replicaSetConfig) { + V1PodTemplateSpec podTemplateSpec = new V1PodTemplateSpec(); + podTemplateSpec.setMetadata(createMetadata(replicaSetConfig.getPodTemplateName(), replicaSetConfig.getPodTemplateLabels())); + podTemplateSpec.setSpec(createPodSpec(replicaSetConfig)); + return podTemplateSpec; + } + + private V1PodSpec createPodSpec(ReplicaSetConfig replicaSetConfig) { + V1PodSpec podSpec = new V1PodSpec(); + podSpec.setContainers(createContainers(replicaSetConfig)); + return podSpec; + } + + private List createContainers(ReplicaSetConfig replicaSetConfig) { + return Collections.singletonList(new V1Container() + .name(replicaSetConfig.getContainerName()) + .image(replicaSetConfig.getImage()) + .ports(createContainerPorts(replicaSetConfig.getPorts())) + .env(createEnvVariables(replicaSetConfig.getEnvVariables())) + .command(replicaSetConfig.getCommands()) + ); + } + + private List createContainerPorts(Map ports) { + List portList = new ArrayList<>(); + ports.forEach((portNum, portName) -> portList.add(new V1ContainerPort() + .containerPort(portNum) + .name(portName) + )); + return portList; + } + + private List createEnvVariables(Map envVariables) { + List envVarList = new ArrayList<>(); + envVariables.forEach((envVarName , envVarValue) -> envVarList.add(new V1EnvVar() + .name(envVarName) + .value(envVarValue) + )); + return envVarList; + } + + private V1ObjectMeta createMetadata(String name, Map labels) { + V1ObjectMeta meta = new V1ObjectMeta(); + meta.setName(name); + meta.setLabels(labels); + return meta; + } +} diff --git a/application/src/main/java/com/hashmapinc/server/service/kubernetes/gateway/TempusGatewayKubernetesService.java b/application/src/main/java/com/hashmapinc/server/service/kubernetes/gateway/TempusGatewayKubernetesService.java new file mode 100644 index 000000000..d4bc5741a --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/service/kubernetes/gateway/TempusGatewayKubernetesService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.service.kubernetes.gateway; + +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.id.TenantId; +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetStatus; + +public interface TempusGatewayKubernetesService { + + boolean deployTempusGateway(TempusGatewayConfiguration gatewayConfiguration); + + ReplicaSetStatus getTempusGatewayReplicaSetStatus(TenantId tenantId); +} diff --git a/application/src/main/java/com/hashmapinc/server/service/kubernetes/gateway/TempusGatewayKubernetesServiceImpl.java b/application/src/main/java/com/hashmapinc/server/service/kubernetes/gateway/TempusGatewayKubernetesServiceImpl.java new file mode 100644 index 000000000..f1ae8be24 --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/service/kubernetes/gateway/TempusGatewayKubernetesServiceImpl.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.service.kubernetes.gateway; + +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.UUIDConverter; +import com.hashmapinc.server.common.data.id.TenantId; +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetConfig; +import com.hashmapinc.server.common.data.kubernetes.ReplicaSetStatus; +import com.hashmapinc.server.config.TempusGatewayProperties; +import com.hashmapinc.server.service.kubernetes.KubernetesReplicaSetService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class TempusGatewayKubernetesServiceImpl implements TempusGatewayKubernetesService { + + private static final String GATEWAY_ACCESS_TOKEN = "GATEWAY_ACCESS_TOKEN"; + private static final String CONTAINER_NAME = "gateway"; + private static final String TENANT_ID = "tenantId"; + private static final String GATEWAY_PORT = "port"; + private static final String GATEWAY_HOST = "GATEWAY_HOST"; + + @Autowired + TempusGatewayProperties tempusGatewayProperties; + + @Autowired + KubernetesReplicaSetService replicaSetService; + + @Override + public boolean deployTempusGateway(TempusGatewayConfiguration gatewayConfiguration) { + ReplicaSetConfig replicaSetConfig = createReplicaSetConfig(gatewayConfiguration); + return replicaSetService.deployReplicaSet(replicaSetConfig); + } + + @Override + public ReplicaSetStatus getTempusGatewayReplicaSetStatus(TenantId tenantId) { + String strTenantId = UUIDConverter.fromTimeUUID(tenantId.getId()); + String labelSelectorStr = TENANT_ID + "=" + strTenantId; + return replicaSetService.getReplicaSetStatus(labelSelectorStr); + } + + private ReplicaSetConfig createReplicaSetConfig(TempusGatewayConfiguration gatewayConfiguration) { + ReplicaSetConfig replicaSetConfig = new ReplicaSetConfig(); + String replicaName = UUIDConverter.fromTimeUUID(gatewayConfiguration.getTenantId().getId()); + Map labels = new HashMap<>(); + labels.put(TENANT_ID, replicaName); + + replicaSetConfig.setReplicaName(replicaName); + replicaSetConfig.setPodTemplateName(replicaName); + replicaSetConfig.setContainerName(CONTAINER_NAME); + replicaSetConfig.setReplicaLabels(labels); + replicaSetConfig.setSelectorLabels(labels); + replicaSetConfig.setPodTemplateLabels(labels); + replicaSetConfig.setReplica(gatewayConfiguration.getReplicas()); + replicaSetConfig.setImage(tempusGatewayProperties.getImage()); + replicaSetConfig.setPorts(new HashMap() {{ + put(tempusGatewayProperties.getPort(), GATEWAY_PORT); + }}); + replicaSetConfig.setEnvVariables(new HashMap() {{ + put(GATEWAY_ACCESS_TOKEN, gatewayConfiguration.getGatewayToken()); + put(GATEWAY_HOST, tempusGatewayProperties.getHost()); + }}); + replicaSetConfig.setCommands(tempusGatewayProperties.getCommands()); + return replicaSetConfig; + } +} diff --git a/application/src/main/java/com/hashmapinc/server/utils/KubernetesConnectionClient.java b/application/src/main/java/com/hashmapinc/server/utils/KubernetesConnectionClient.java new file mode 100644 index 000000000..b3cbe5bde --- /dev/null +++ b/application/src/main/java/com/hashmapinc/server/utils/KubernetesConnectionClient.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.utils; + +import io.kubernetes.client.ApiClient; +import io.kubernetes.client.Configuration; +import io.kubernetes.client.apis.CoreV1Api; +import io.kubernetes.client.apis.ExtensionsV1beta1Api; +import io.kubernetes.client.util.Config; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.IOException; + +@Slf4j +@NoArgsConstructor +@Component +@Getter +public class KubernetesConnectionClient { + + private CoreV1Api coreV1Api; + private ExtensionsV1beta1Api extensionsV1beta1Api ; + + @Value("${kubernetes.cluster_mode_enabled}") + private boolean clusterModeEnabled; + + @Value("${kubernetes.kube_config_path}") + private String configPath; + + private ApiClient apiClient; + + @PostConstruct + public void init() { + try { + apiClient = clusterModeEnabled ? Config.fromCluster() : Config.fromConfig(configPath); + Configuration.setDefaultApiClient(apiClient); + coreV1Api = new CoreV1Api(); + extensionsV1beta1Api = new ExtensionsV1beta1Api(apiClient); + } catch (IOException e) { + log.error("Exception in apiClient creation : [{}]", e.getMessage()); + } + } + +} diff --git a/application/src/main/resources/tempus.yml b/application/src/main/resources/tempus.yml index 22c969dc9..de3b5fe5b 100644 --- a/application/src/main/resources/tempus.yml +++ b/application/src/main/resources/tempus.yml @@ -408,4 +408,20 @@ audit_log: user_name: "${AUDIT_LOG_SINK_USER_NAME:}" password: "${AUDIT_LOG_SINK_PASSWORD:}" -authorization.rules.filePath: "default-rules.json" \ No newline at end of file +authorization.rules.filePath: "default-rules.json" + +#Tempus Gateway +gateway: + image: "${TEMPUS_GATEWAY_IMAGE:hashmapinc/tempus-gateway}" + host: "${GATEWAY_HOST:tempus}" + port: "${GATEWAY_PORT:9095}" + commands: + - "sh" + - "-c" + - "/run-application.sh" + +#Kubernetes Configuration +kubernetes: + cluster_mode_enabled: "${CLUSTER_MODE_ENABLED:true}" + kube_config_path: "${KUBE_CONFIG_PATH:/home/.kube/config}" + diff --git a/common/data/src/main/java/com/hashmapinc/server/common/data/EntityType.java b/common/data/src/main/java/com/hashmapinc/server/common/data/EntityType.java index afb709286..b6bb50611 100644 --- a/common/data/src/main/java/com/hashmapinc/server/common/data/EntityType.java +++ b/common/data/src/main/java/com/hashmapinc/server/common/data/EntityType.java @@ -19,5 +19,5 @@ public enum EntityType { TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM, COMPUTATION, COMPUTATION_JOB, NODE_METRIC,THEME, LOGO, - DATA_MODEL_OBJECT, DATA_MODEL, CUSTOMER_GROUP + DATA_MODEL_OBJECT, DATA_MODEL, CUSTOMER_GROUP, TEMPUS_GATEWAY_CONFIGURATION } diff --git a/common/data/src/main/java/com/hashmapinc/server/common/data/TempusGatewayConfiguration.java b/common/data/src/main/java/com/hashmapinc/server/common/data/TempusGatewayConfiguration.java new file mode 100644 index 000000000..3ad5f2b5e --- /dev/null +++ b/common/data/src/main/java/com/hashmapinc/server/common/data/TempusGatewayConfiguration.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.common.data; + +import com.hashmapinc.server.common.data.id.TempusGatewayConfigurationId; +import com.hashmapinc.server.common.data.id.TenantId; +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@ToString +public class TempusGatewayConfiguration extends BaseData{ + private static final long serialVersionUID = -7052031036883142312L; + private TenantId tenantId; + private int replicas; + private String gatewayToken; + + public TempusGatewayConfiguration(TempusGatewayConfigurationId id) { + super(id); + } + + public TempusGatewayConfiguration(TempusGatewayConfiguration tempusGatewayConfiguration){ + super(tempusGatewayConfiguration); + this.tenantId = tempusGatewayConfiguration.tenantId; + this.replicas = tempusGatewayConfiguration.replicas; + this.gatewayToken = tempusGatewayConfiguration.gatewayToken; + } +} diff --git a/common/data/src/main/java/com/hashmapinc/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/com/hashmapinc/server/common/data/id/EntityIdFactory.java index 55835f54f..fa53c5d3f 100644 --- a/common/data/src/main/java/com/hashmapinc/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/com/hashmapinc/server/common/data/id/EntityIdFactory.java @@ -72,6 +72,8 @@ public static EntityId getByTypeAndUuid(EntityType type, UUID uuid) { return new DataModelObjectId(uuid); case CUSTOMER_GROUP: return new CustomerGroupId(uuid); + case TEMPUS_GATEWAY_CONFIGURATION: + return new TempusGatewayConfigurationId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/com/hashmapinc/server/common/data/id/TempusGatewayConfigurationId.java b/common/data/src/main/java/com/hashmapinc/server/common/data/id/TempusGatewayConfigurationId.java new file mode 100644 index 000000000..3c8a408ca --- /dev/null +++ b/common/data/src/main/java/com/hashmapinc/server/common/data/id/TempusGatewayConfigurationId.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.hashmapinc.server.common.data.EntityType; + +import java.util.UUID; + +public class TempusGatewayConfigurationId extends UUIDBased implements EntityId { + + @JsonCreator + public TempusGatewayConfigurationId(@JsonProperty("id") UUID id) { + super(id); + } + + public static UserId fromString(String tempusGatewayConfigurationId) { + return new UserId(UUID.fromString(tempusGatewayConfigurationId)); + } + + @JsonIgnore + @Override + public EntityType getEntityType() { + return EntityType.TEMPUS_GATEWAY_CONFIGURATION; + } + +} diff --git a/common/data/src/main/java/com/hashmapinc/server/common/data/kubernetes/ReplicaSetConfig.java b/common/data/src/main/java/com/hashmapinc/server/common/data/kubernetes/ReplicaSetConfig.java new file mode 100644 index 000000000..ab337aac1 --- /dev/null +++ b/common/data/src/main/java/com/hashmapinc/server/common/data/kubernetes/ReplicaSetConfig.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.common.data.kubernetes; + + +import lombok.*; + +import java.util.List; +import java.util.Map; + +@Data +public class ReplicaSetConfig { + + private String replicaName; + private String podTemplateName; + private Map replicaLabels; + private Map selectorLabels; + private Map podTemplateLabels; + private int replica; + private String containerName; + private String image; + private Map ports; + private Map envVariables; + private List commands; +} diff --git a/common/data/src/main/java/com/hashmapinc/server/common/data/kubernetes/ReplicaSetStatus.java b/common/data/src/main/java/com/hashmapinc/server/common/data/kubernetes/ReplicaSetStatus.java new file mode 100644 index 000000000..a2364beb7 --- /dev/null +++ b/common/data/src/main/java/com/hashmapinc/server/common/data/kubernetes/ReplicaSetStatus.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.common.data.kubernetes; + +import lombok.Data; + +@Data +public class ReplicaSetStatus { + + Integer replica; + Integer ready; + Integer inProgress; + Integer crashed; +} diff --git a/dao/src/it/java/com/hashmapinc/server/dao/service/AbstractServiceTest.java b/dao/src/it/java/com/hashmapinc/server/dao/service/AbstractServiceTest.java index 6a479482d..ee99682fa 100644 --- a/dao/src/it/java/com/hashmapinc/server/dao/service/AbstractServiceTest.java +++ b/dao/src/it/java/com/hashmapinc/server/dao/service/AbstractServiceTest.java @@ -58,6 +58,7 @@ import com.hashmapinc.server.dao.device.DeviceCredentialsService; import com.hashmapinc.server.dao.device.DeviceService; import com.hashmapinc.server.dao.event.EventService; +import com.hashmapinc.server.dao.gatewayconfiguration.TempusGatewayConfigurationService; import com.hashmapinc.server.dao.logo.LogoService; import com.hashmapinc.server.dao.metadataingestion.MetadataIngestionService; import com.hashmapinc.server.dao.plugin.PluginService; @@ -180,6 +181,9 @@ public abstract class AbstractServiceTest { @Autowired protected MetadataIngestionService metadataIngestionService; + @Autowired + protected TempusGatewayConfigurationService tempusGatewayConfigurationService; + @Autowired protected AttributeDefinitionDao attributeDefinitionDao; diff --git a/dao/src/it/java/com/hashmapinc/server/dao/service/BaseTempusGatewayConfigurationServiceTest.java b/dao/src/it/java/com/hashmapinc/server/dao/service/BaseTempusGatewayConfigurationServiceTest.java new file mode 100644 index 000000000..aa19a4d01 --- /dev/null +++ b/dao/src/it/java/com/hashmapinc/server/dao/service/BaseTempusGatewayConfigurationServiceTest.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.service; + +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.Tenant; +import com.hashmapinc.server.common.data.id.TenantId; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +public abstract class BaseTempusGatewayConfigurationServiceTest extends AbstractServiceTest { + private TenantId tenantId; + + @Before + public void before() { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + } + + @After + public void after() { + tenantService.deleteTenant(tenantId); + } + + @Test + public void testSaveAndUpdateTempusGatewayConfiguration() { + final TempusGatewayConfiguration saveTempusGatewayConfiguration = createTempusGatewayConfiguration(); + + saveTempusGatewayConfiguration.setGatewayToken("gateway-config-token-new"); + final TempusGatewayConfiguration updatedTempusGatewayConfiguration = tempusGatewayConfigurationService.saveTempusGatewayConfiguration(saveTempusGatewayConfiguration); + Assert.assertNotNull(updatedTempusGatewayConfiguration); + Assert.assertEquals(saveTempusGatewayConfiguration.getGatewayToken(), updatedTempusGatewayConfiguration.getGatewayToken()); + + tempusGatewayConfigurationService.deleteTempusGatewayConfiguration(saveTempusGatewayConfiguration.getId()); + } + + @Test + public void testFindTempusGatewayConfigurationByIdAndByTenantId(){ + final TempusGatewayConfiguration saveTempusGatewayConfiguration = createTempusGatewayConfiguration(); + final TempusGatewayConfiguration tempusGatewayConfigurationById = tempusGatewayConfigurationService.findTempusGatewayConfigurationById(saveTempusGatewayConfiguration.getId()); + Assert.assertNotNull(tempusGatewayConfigurationById); + Assert.assertEquals(saveTempusGatewayConfiguration.getId(), tempusGatewayConfigurationById.getId()); + final Optional tempusGatewayConfigurationByTenantId = + tempusGatewayConfigurationService.findTempusGatewayConfigurationByTenantId(saveTempusGatewayConfiguration.getTenantId()); + Assert.assertTrue(tempusGatewayConfigurationByTenantId.isPresent()); + Assert.assertEquals(saveTempusGatewayConfiguration.getId(), tempusGatewayConfigurationByTenantId.get().getId()); + + tempusGatewayConfigurationService.deleteTempusGatewayConfiguration(saveTempusGatewayConfiguration.getId()); + } + + private TempusGatewayConfiguration createTempusGatewayConfiguration() { + TempusGatewayConfiguration tempusGatewayConfiguration = new TempusGatewayConfiguration(); + tempusGatewayConfiguration.setReplicas(1); + tempusGatewayConfiguration.setGatewayToken("token"); + tempusGatewayConfiguration.setTenantId(tenantId); + final TempusGatewayConfiguration saveTempusGatewayConfiguration = tempusGatewayConfigurationService.saveTempusGatewayConfiguration(tempusGatewayConfiguration); + + Assert.assertNotNull(saveTempusGatewayConfiguration); + Assert.assertNotNull(saveTempusGatewayConfiguration.getId()); + Assert.assertTrue(saveTempusGatewayConfiguration.getCreatedTime() > 0); + Assert.assertEquals(tempusGatewayConfiguration.getTenantId(), saveTempusGatewayConfiguration.getTenantId()); + Assert.assertEquals(tempusGatewayConfiguration.getGatewayToken(), saveTempusGatewayConfiguration.getGatewayToken()); + return saveTempusGatewayConfiguration; + } +} diff --git a/dao/src/it/java/com/hashmapinc/server/dao/service/nosql/TempusGatewayConfigurationServiceNoSqlTest.java b/dao/src/it/java/com/hashmapinc/server/dao/service/nosql/TempusGatewayConfigurationServiceNoSqlTest.java new file mode 100644 index 000000000..9dcc80389 --- /dev/null +++ b/dao/src/it/java/com/hashmapinc/server/dao/service/nosql/TempusGatewayConfigurationServiceNoSqlTest.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.service.nosql; + +import com.hashmapinc.server.dao.service.BaseTempusGatewayConfigurationServiceTest; +import com.hashmapinc.server.dao.service.DaoNoSqlTest; + +@DaoNoSqlTest +public class TempusGatewayConfigurationServiceNoSqlTest extends BaseTempusGatewayConfigurationServiceTest { +} diff --git a/dao/src/it/java/com/hashmapinc/server/dao/service/sql/TempusGatewayConfigurationServiceSqlTest.java b/dao/src/it/java/com/hashmapinc/server/dao/service/sql/TempusGatewayConfigurationServiceSqlTest.java new file mode 100644 index 000000000..feb144d0b --- /dev/null +++ b/dao/src/it/java/com/hashmapinc/server/dao/service/sql/TempusGatewayConfigurationServiceSqlTest.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.service.sql; + +import com.hashmapinc.server.dao.service.BaseTempusGatewayConfigurationServiceTest; +import com.hashmapinc.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class TempusGatewayConfigurationServiceSqlTest extends BaseTempusGatewayConfigurationServiceTest { +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/CassandraTempusGatewayConfigurationDao.java b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/CassandraTempusGatewayConfigurationDao.java new file mode 100644 index 000000000..d53ead1e9 --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/CassandraTempusGatewayConfigurationDao.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.gatewayconfiguration; + +import com.datastax.driver.core.querybuilder.Select; +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.dao.DaoUtil; +import com.hashmapinc.server.dao.model.ModelConstants; +import com.hashmapinc.server.dao.model.nosql.TempusGatewayConfigurationEntity; +import com.hashmapinc.server.dao.nosql.CassandraAbstractModelDao; +import com.hashmapinc.server.dao.nosql.CassandraAbstractSearchTextDao; +import com.hashmapinc.server.dao.util.NoSqlDao; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.UUID; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; + +@Component +@Slf4j +@NoSqlDao +public class CassandraTempusGatewayConfigurationDao extends CassandraAbstractModelDao + implements TempusGatewayConfigurationDao { + + @Override + protected Class getColumnFamilyClass() { + return TempusGatewayConfigurationEntity.class; + } + + @Override + protected String getColumnFamilyName() { + return ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_FAMILY_NAME; + } + + + @Override + public Optional findTempusGatewayConfigurationByTenantId(UUID tenantId) { + log.debug("Try to find TempusGatewayConfiguration by tenantId [{}]", tenantId); + Select select = select().from(ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_BY_TENANT); + Select.Where query = select.where(); + query.and(eq(ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_TENANT_ID_PROPERTY, tenantId)); + TempusGatewayConfigurationEntity tempusGatewayConfigurationEntity = findOneByStatement(query); + TempusGatewayConfiguration tempusGatewayConfiguration = DaoUtil.getData(tempusGatewayConfigurationEntity); + return Optional.ofNullable(tempusGatewayConfiguration); + } +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationDao.java b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationDao.java new file mode 100644 index 000000000..099411027 --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationDao.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.gatewayconfiguration; + +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.dao.Dao; + +import java.util.Optional; +import java.util.UUID; + +public interface TempusGatewayConfigurationDao extends Dao { + /** + * Save or update tempusGatewayConfiguration object + * + * @param tempusGatewayConfiguration the tempusGatewayConfiguration object + * @return saved customerGroup object + */ + TempusGatewayConfiguration save(TempusGatewayConfiguration tempusGatewayConfiguration); + + /** + * Find tempusGatewayConfiguration by tenant id. + * + * @param tenantId the tenant id + * @return optional tempusGatewayConfiguration object + */ + Optional findTempusGatewayConfigurationByTenantId(UUID tenantId); + +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationService.java b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationService.java new file mode 100644 index 000000000..2294a89c0 --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationService.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.gatewayconfiguration; + +import com.google.common.util.concurrent.ListenableFuture; +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.id.TempusGatewayConfigurationId; +import com.hashmapinc.server.common.data.id.TenantId; + +import java.util.Optional; + +public interface TempusGatewayConfigurationService { + TempusGatewayConfiguration findTempusGatewayConfigurationById(TempusGatewayConfigurationId tempusGatewayConfigurationId); + + TempusGatewayConfiguration saveTempusGatewayConfiguration(TempusGatewayConfiguration tempusGatewayConfiguration); + + void deleteTempusGatewayConfiguration(TempusGatewayConfigurationId tempusGatewayConfigurationId); + + Optional findTempusGatewayConfigurationByTenantId(TenantId tenantId); + +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationServiceImpl.java b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationServiceImpl.java new file mode 100644 index 000000000..131d209ae --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/gatewayconfiguration/TempusGatewayConfigurationServiceImpl.java @@ -0,0 +1,119 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.gatewayconfiguration; + +import com.google.common.util.concurrent.ListenableFuture; +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.Tenant; +import com.hashmapinc.server.common.data.id.TempusGatewayConfigurationId; +import com.hashmapinc.server.common.data.id.TenantId; +import com.hashmapinc.server.dao.entity.AbstractEntityService; +import com.hashmapinc.server.dao.exception.DataValidationException; +import com.hashmapinc.server.dao.exception.IncorrectParameterException; +import com.hashmapinc.server.dao.service.DataValidator; +import com.hashmapinc.server.dao.service.Validator; +import com.hashmapinc.server.dao.tenant.TenantDao; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +import static com.hashmapinc.server.dao.service.Validator.validateId; + +@Service +@Slf4j +public class TempusGatewayConfigurationServiceImpl extends AbstractEntityService implements TempusGatewayConfigurationService { + private static final String PUBLIC_CUSTOMER_TITLE = "Public"; + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_TEMPUS_GATEWAY_CONFIGURATION_ID = "Incorrect tempus gateway configuration id "; + + @Autowired + TempusGatewayConfigurationDao tempusGatewayConfigurationDao; + + @Autowired + TenantDao tenantDao; + + @Override + public TempusGatewayConfiguration findTempusGatewayConfigurationById(TempusGatewayConfigurationId tempusGatewayConfigurationId) { + log.trace("Executing findCustomerById [{}]", tempusGatewayConfigurationId); + Validator.validateId(tempusGatewayConfigurationId, INCORRECT_TEMPUS_GATEWAY_CONFIGURATION_ID + tempusGatewayConfigurationId); + return tempusGatewayConfigurationDao.findById(tempusGatewayConfigurationId.getId()); + } + + @Override + public TempusGatewayConfiguration saveTempusGatewayConfiguration(TempusGatewayConfiguration tempusGatewayConfiguration) { + log.trace("Executing saveCustomer [{}]", tempusGatewayConfiguration); + tempusGatewayConfigurationValidator.validate(tempusGatewayConfiguration); + return tempusGatewayConfigurationDao.save(tempusGatewayConfiguration); + } + + @Override + public void deleteTempusGatewayConfiguration(TempusGatewayConfigurationId tempusGatewayConfigurationId) { + log.trace("Executing deleteTempusGatewayConfiguration [{}]", tempusGatewayConfigurationId); + Validator.validateId(tempusGatewayConfigurationId, INCORRECT_TEMPUS_GATEWAY_CONFIGURATION_ID + tempusGatewayConfigurationId); + TempusGatewayConfiguration tempusGatewayConfiguration = findTempusGatewayConfigurationById(tempusGatewayConfigurationId); + if (tempusGatewayConfiguration == null) { + throw new IncorrectParameterException("Unable to delete non-existent tempusGatewayConfiguration."); + } + deleteEntityRelations(tempusGatewayConfigurationId); + tempusGatewayConfigurationDao.removeById(tempusGatewayConfigurationId.getId()); + + } + + @Override + public Optional findTempusGatewayConfigurationByTenantId(TenantId tenantId) { + log.trace("Executing findTempusGatewayConfigurationByTenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return tempusGatewayConfigurationDao.findTempusGatewayConfigurationByTenantId(tenantId.getId()); + } + + private DataValidator tempusGatewayConfigurationValidator = + new DataValidator() { + + @Override + protected void validateCreate(TempusGatewayConfiguration tempusGatewayConfiguration) { + tempusGatewayConfigurationDao.findTempusGatewayConfigurationByTenantId(tempusGatewayConfiguration.getTenantId().getId()).ifPresent( + c -> { + throw new DataValidationException("TempusGatewayConfiguration for this tenant already exists!"); + } + ); + } + + @Override + protected void validateUpdate(TempusGatewayConfiguration tempusGatewayConfiguration) { + if(!tempusGatewayConfigurationDao.findTempusGatewayConfigurationByTenantId(tempusGatewayConfiguration.getTenantId().getId()).isPresent()){ + throw new DataValidationException("TempusGatewayConfiguration for this tenant does not exists!"); + } + } + + @Override + protected void validateDataImpl(TempusGatewayConfiguration tempusGatewayConfiguration) { + if (tempusGatewayConfiguration.getTenantId() == null) { + throw new DataValidationException("TempusGatewayConfiguration should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(tempusGatewayConfiguration.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("TempusGatewayConfiguration is referencing to non-existent tenant!"); + } + } + } + }; + + +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/model/ModelConstants.java b/dao/src/main/java/com/hashmapinc/server/dao/model/ModelConstants.java index 6fe830e98..4d4d505a1 100644 --- a/dao/src/main/java/com/hashmapinc/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/com/hashmapinc/server/dao/model/ModelConstants.java @@ -580,6 +580,23 @@ private ModelConstants() { public static final String CUSTOMER_GROUP_POLICY_ID_PROPERTY = "group_id"; public static final String CUSTOMER_GROUP_POLICY_PROPERTY = "policies"; + /** + * TempusGatewayConfiguration Constants + */ + public static final String TEMPUS_GATEWAY_CONFIGURATION_TABLE_NAME = "tempus_gateway_configuration"; + public static final String TEMPUS_GATEWAY_CONFIGURATION_TENANT_ID = TENANT_ID_PROPERTY; + public static final String TEMPUS_GATEWAY_CONFIGURATION_REPLICAS = "replicas"; + public static final String TEMPUS_GATEWAY_CONFIGURATION_GATEWAY_TOKEN = "gateway_token"; + + /** + * Cassandra TempusGatewayConfiguration Constants + */ + public static final String TEMPUS_GATEWAY_CONFIGURATION_FAMILY_NAME = "tempus_gateway_configuration"; + public static final String TEMPUS_GATEWAY_CONFIGURATION_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String TEMPUS_GATEWAY_CONFIGURATION_REPLICAS_PROPERTY = "replicas"; + public static final String TEMPUS_GATEWAY_CONFIGURATION_GATEWAY_TOKEN_PROPERTY = "gateway_token"; + public static final String TEMPUS_GATEWAY_CONFIGURATION_BY_TENANT = "tempus_gateway_configuration_by_tenant"; + protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN, TS_DIFF}; protected static final String[] NONE_DS_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, DS_COLUMN, DS_DIFF}; diff --git a/dao/src/main/java/com/hashmapinc/server/dao/model/nosql/TempusGatewayConfigurationEntity.java b/dao/src/main/java/com/hashmapinc/server/dao/model/nosql/TempusGatewayConfigurationEntity.java new file mode 100644 index 000000000..4f6a81093 --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/model/nosql/TempusGatewayConfigurationEntity.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.model.nosql; + +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.id.TempusGatewayConfigurationId; +import com.hashmapinc.server.common.data.id.TenantId; +import com.hashmapinc.server.dao.model.BaseEntity; +import com.hashmapinc.server.dao.model.ModelConstants; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.UUID; + +import static com.hashmapinc.server.dao.model.ModelConstants.*; + + +@NoArgsConstructor +@Data +@Table(name = TEMPUS_GATEWAY_CONFIGURATION_FAMILY_NAME) +@EqualsAndHashCode +@ToString +public class TempusGatewayConfigurationEntity implements BaseEntity { + @PartitionKey(value = 0) + @Column(name = ID_PROPERTY) + private UUID id; + + @PartitionKey(value = 1) + @Column(name = TEMPUS_GATEWAY_CONFIGURATION_TENANT_ID_PROPERTY) + private UUID tenantId; + + @Column(name = ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_REPLICAS_PROPERTY) + private int replicas; + + @Column(name = ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_GATEWAY_TOKEN_PROPERTY) + private String gatewayToken; + + public TempusGatewayConfigurationEntity(TempusGatewayConfiguration tempusGatewayConfiguration) { + if (tempusGatewayConfiguration.getId() != null) { + this.setId(tempusGatewayConfiguration.getId().getId()); + } + this.tenantId = tempusGatewayConfiguration.getTenantId().getId(); + this.replicas = tempusGatewayConfiguration.getReplicas(); + this.gatewayToken = tempusGatewayConfiguration.getGatewayToken(); + } + + @Override + public TempusGatewayConfiguration toData() { + TempusGatewayConfiguration tempusGatewayConfiguration = new TempusGatewayConfiguration(new TempusGatewayConfigurationId(getId())); + tempusGatewayConfiguration.setCreatedTime(UUIDs.unixTimestamp(getId())); + tempusGatewayConfiguration.setTenantId(new TenantId(tenantId)); + tempusGatewayConfiguration.setReplicas(replicas); + tempusGatewayConfiguration.setGatewayToken(gatewayToken); + return tempusGatewayConfiguration; + } +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/model/sql/TempusGatewayConfigurationEntity.java b/dao/src/main/java/com/hashmapinc/server/dao/model/sql/TempusGatewayConfigurationEntity.java new file mode 100644 index 000000000..195d603fc --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/model/sql/TempusGatewayConfigurationEntity.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.model.sql; + +import com.datastax.driver.core.utils.UUIDs; +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.UUIDConverter; +import com.hashmapinc.server.common.data.id.TempusGatewayConfigurationId; +import com.hashmapinc.server.common.data.id.TenantId; +import com.hashmapinc.server.dao.model.BaseEntity; +import com.hashmapinc.server.dao.model.BaseSqlEntity; +import com.hashmapinc.server.dao.model.ModelConstants; +import com.hashmapinc.server.dao.util.mapping.JsonStringType; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@NoArgsConstructor +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_TABLE_NAME) +public class TempusGatewayConfigurationEntity extends BaseSqlEntity implements BaseEntity { + @Column(name = ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_TENANT_ID) + private String tenantId; + + @Column(name = ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_REPLICAS) + private int replicas; + + @Column(name = ModelConstants.TEMPUS_GATEWAY_CONFIGURATION_GATEWAY_TOKEN) + private String gatewayToken; + + public TempusGatewayConfigurationEntity(TempusGatewayConfiguration tempusGatewayConfiguration) { + if (tempusGatewayConfiguration.getId() != null) { + this.setId(tempusGatewayConfiguration.getId().getId()); + } + this.tenantId = UUIDConverter.fromTimeUUID(tempusGatewayConfiguration.getTenantId().getId()); + this.replicas = tempusGatewayConfiguration.getReplicas(); + this.gatewayToken = tempusGatewayConfiguration.getGatewayToken(); + } + + + @Override + public TempusGatewayConfiguration toData() { + TempusGatewayConfiguration tempusGatewayConfiguration = new TempusGatewayConfiguration(new TempusGatewayConfigurationId(getId())); + tempusGatewayConfiguration.setCreatedTime(UUIDs.unixTimestamp(getId())); + tempusGatewayConfiguration.setTenantId(new TenantId(UUIDConverter.fromString(tenantId))); + tempusGatewayConfiguration.setReplicas(replicas); + tempusGatewayConfiguration.setGatewayToken(gatewayToken); + return tempusGatewayConfiguration; + } +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/sql/gatewayconfiguration/JpaTempusGatewayConfigurationDao.java b/dao/src/main/java/com/hashmapinc/server/dao/sql/gatewayconfiguration/JpaTempusGatewayConfigurationDao.java new file mode 100644 index 000000000..fafb52148 --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/sql/gatewayconfiguration/JpaTempusGatewayConfigurationDao.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.sql.gatewayconfiguration; + +import com.hashmapinc.server.common.data.TempusGatewayConfiguration; +import com.hashmapinc.server.common.data.UUIDConverter; +import com.hashmapinc.server.dao.DaoUtil; +import com.hashmapinc.server.dao.gatewayconfiguration.TempusGatewayConfigurationDao; +import com.hashmapinc.server.dao.model.sql.TempusGatewayConfigurationEntity; +import com.hashmapinc.server.dao.sql.JpaAbstractDao; +import com.hashmapinc.server.dao.util.SqlDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.UUID; + +@Component +@SqlDao +public class JpaTempusGatewayConfigurationDao extends JpaAbstractDao implements TempusGatewayConfigurationDao { + @Autowired + private TempusGatewayConfigurationRepository tempusGatewayConfigurationRepository; + + @Override + protected Class getEntityClass() { + return TempusGatewayConfigurationEntity.class; + } + @Override + protected CrudRepository getCrudRepository() { + return tempusGatewayConfigurationRepository; + } + + @Override + public Optional findTempusGatewayConfigurationByTenantId(UUID tenantId) { + return Optional.ofNullable( + DaoUtil.getData(tempusGatewayConfigurationRepository.findByTenantId(UUIDConverter.fromTimeUUID(tenantId))) + ); + } +} diff --git a/dao/src/main/java/com/hashmapinc/server/dao/sql/gatewayconfiguration/TempusGatewayConfigurationRepository.java b/dao/src/main/java/com/hashmapinc/server/dao/sql/gatewayconfiguration/TempusGatewayConfigurationRepository.java new file mode 100644 index 000000000..85cac87c2 --- /dev/null +++ b/dao/src/main/java/com/hashmapinc/server/dao/sql/gatewayconfiguration/TempusGatewayConfigurationRepository.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 com.hashmapinc.server.dao.sql.gatewayconfiguration; + +import com.hashmapinc.server.dao.model.sql.TempusGatewayConfigurationEntity; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +public interface TempusGatewayConfigurationRepository extends CrudRepository { + TempusGatewayConfigurationEntity findByTenantId(String tenantId); +} diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql index bd7cc89c1..59f89ff48 100644 --- a/dao/src/main/resources/cassandra/schema.cql +++ b/dao/src/main/resources/cassandra/schema.cql @@ -792,4 +792,19 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS tempus.asset_landing_info_by_data_model_o from tempus.asset_landing_info WHERE data_model_object_id IS NOT NULL AND id IS NOT NULL PRIMARY KEY ( data_model_object_id, id) - WITH CLUSTERING ORDER BY ( id DESC ); \ No newline at end of file + WITH CLUSTERING ORDER BY ( id DESC ); + +CREATE TABLE IF NOT EXISTS tempus.tempus_gateway_configuration ( + id timeuuid, + tenant_id timeuuid, + replicas int, + gateway_token text, + PRIMARY KEY(id, tenant_id) +); + +CREATE MATERIALIZED VIEW IF NOT exists tempus.tempus_gateway_configuration_by_tenant AS + SELECT * +FROM tempus.tempus_gateway_configuration + WHERE tenant_id IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, id ) + WITH CLUSTERING ORDER BY ( id DESC ); \ No newline at end of file diff --git a/dao/src/main/resources/sql/hsql/schema.sql b/dao/src/main/resources/sql/hsql/schema.sql index 156a6778e..36a0966d7 100644 --- a/dao/src/main/resources/sql/hsql/schema.sql +++ b/dao/src/main/resources/sql/hsql/schema.sql @@ -391,4 +391,11 @@ CREATE TABLE IF NOT EXISTS user_groups ( CREATE TABLE IF NOT EXISTS customer_group_policy ( group_id varchar(31), policy varchar +); + +CREATE TABLE IF NOT EXISTS tempus_gateway_configuration ( + id varchar(31) NOT NULL CONSTRAINT tempus_gateway_configuration_pkey PRIMARY KEY, + tenant_id varchar(31), + replicas integer NOT NULL, + gateway_token varchar(255) NOT NULL ); \ No newline at end of file diff --git a/dao/src/main/resources/sql/postgres/schema.sql b/dao/src/main/resources/sql/postgres/schema.sql index 001125169..7076c7b9a 100644 --- a/dao/src/main/resources/sql/postgres/schema.sql +++ b/dao/src/main/resources/sql/postgres/schema.sql @@ -413,4 +413,11 @@ CREATE TABLE IF NOT EXISTS user_groups ( CREATE TABLE IF NOT EXISTS customer_group_policy ( group_id varchar(31), policy varchar +); + +CREATE TABLE IF NOT EXISTS tempus_gateway_configuration ( + id varchar(31) NOT NULL CONSTRAINT tempus_gateway_configuration_pkey PRIMARY KEY, + tenant_id varchar(31), + replicas integer NOT NULL, + gateway_token varchar(255) NOT NULL ); \ No newline at end of file diff --git a/docs/_images/admin/agent_conf.png b/docs/_images/admin/agent_conf.png new file mode 100644 index 000000000..8493f2d9b Binary files /dev/null and b/docs/_images/admin/agent_conf.png differ diff --git a/docs/_images/admin/agent_status.png b/docs/_images/admin/agent_status.png new file mode 100644 index 000000000..f904a1bd7 Binary files /dev/null and b/docs/_images/admin/agent_status.png differ diff --git a/docs/admin/agent.rst b/docs/admin/agent.rst new file mode 100644 index 000000000..bf800e926 --- /dev/null +++ b/docs/admin/agent.rst @@ -0,0 +1,30 @@ +####### +Agent +####### + +Tempus support configuring and deploying agent. Agent is nothing but tempus-gateway +that allows you to integrate IoT devices connected to legacy and third-party systems with Tempus Cloud. + +******************** +Agent Configuration +******************** + +Currently agent configuration consists of two attributes which are: + +* Gateway device - Select any gateway device from the list. +* Replicas - Represents number of instances of the agent for deployment. (Currently, supports single replica) + +.. image:: ../_images/admin/agent_conf.png + :align: center + :alt: Agent configuration + +***************** +Agent Deployment +***************** + +Once the agent configuration has been saved. It can deploy by clicking on deploy button. +Here is the screenshot representing how the status will look after clicking on deploy: + +.. image:: ../_images/admin/agent_status.png + :align: center + :alt: Agent status \ No newline at end of file diff --git a/docs/admin/index.rst b/docs/admin/index.rst index 8dbb06d41..c3286d32f 100644 --- a/docs/admin/index.rst +++ b/docs/admin/index.rst @@ -22,4 +22,5 @@ Administration dashboards cluster mail - whitelabeling \ No newline at end of file + whitelabeling + agent \ No newline at end of file diff --git a/ui/src/app/api/gateway-configuration.service.js b/ui/src/app/api/gateway-configuration.service.js new file mode 100644 index 000000000..bdda86b95 --- /dev/null +++ b/ui/src/app/api/gateway-configuration.service.js @@ -0,0 +1,84 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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. + */ +export default angular.module('tempus.api.gatewayConfiguration', []) + .factory('gatewayConfigurationService', GatewayConfigurationService) + .name; + +/*@ngInject*/ +function GatewayConfigurationService($q, $http) { + + + var service = { + saveGatewayConfiguration: saveGatewayConfiguration, + getGatewayConfiguration:getGatewayConfiguration, + deployTempusGateway:deployTempusGateway, + getTempusGatewayPodsStatus:getTempusGatewayPodsStatus + } + + return service; + + function saveGatewayConfiguration(item) { + + var deferred = $q.defer(); + var url = '/api/configuration/tempusGateway'; + $http.post(url, item).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + + function getTempusGatewayPodsStatus() { + + var deferred = $q.defer(); + var url = '/api/configuration/tempusGateway/status'; + $http.get(url).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + + } + + function getGatewayConfiguration() { + + var deferred = $q.defer(); + var url = '/api/configuration/tempusGateway'; + $http.get(url).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + function deployTempusGateway(){ + + var deferred = $q.defer(); + var url = '/api/configuration/tempusGateway/deploy'; + $http.get(url).then(function success(response) { + deferred.resolve(response); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + +} diff --git a/ui/src/app/app.js b/ui/src/app/app.js index 4d60918ff..d7ae7f336 100644 --- a/ui/src/app/app.js +++ b/ui/src/app/app.js @@ -79,6 +79,7 @@ import tempusClusterInfo from './api/cluster-info.service'; import tempusApiComputation from './api/computation.service'; import tempusApiComputationJob from './api/computation-job.service'; import tempusDataModels from './data_models'; +import tempusgateway from './tempusgateway'; import tempusMetadata from './metadata'; import tempusApiAuditLog from './api/audit-log.service'; @@ -148,6 +149,7 @@ angular.module('tempus', [ tempusApiComputation, tempusApiComputationJob, tempusDataModels, + tempusgateway, tempusMetadata, tempusApiAuditLog, uiRouter]) diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index e5c4ee399..58c86fd90 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -417,8 +417,7 @@ export default angular.module('tempus.types', []) }, entityTypes: { 'ALL':'ALL', - 'ASSET': "ASSET", - 'DEVICE': "DEVICE" + 'ASSET': "ASSET" }, userTypes: { 'CUSTOMER_USER':'CUSTOMER_USER', diff --git a/ui/src/app/data_models/data_models.scss b/ui/src/app/data_models/data_models.scss index 65cc804b5..bbd162732 100644 --- a/ui/src/app/data_models/data_models.scss +++ b/ui/src/app/data_models/data_models.scss @@ -23,3 +23,89 @@ border-top: 1px solid lightgray; } +.half { + float: left; + width: 40%; + padding: 0 1em; + margin-top: 12px; +} +/* Acordeon styles */ +.tab { + position: relative; + margin-bottom: 3px; + width: 244%; + color: #fff; + overflow: hidden; + margin-left: -12px; +} +.tab input { + position: absolute; + opacity: 0; + z-index: -1; +} +.tab label { + position: relative; + display: block; + padding: 0 0 0 1em; + background: #320085; + font-weight: bold; + line-height: 2; + cursor: pointer; + margin-left: 3px; +} +.blue label { + background: #2980b9; +} +.tab-content { + max-height: 0; + overflow: hidden; + color: black; + -webkit-transition: max-height .35s; + -o-transition: max-height .35s; + transition: max-height .35s; +} +.blue .tab-content { + background: #3498db; +} +.tab-content p { + margin: 1em; +} +/* :checked */ +.tab input:checked ~ .tab-content { + max-height: 100vh; +} +/* Icon */ +.tab label::after { + position: absolute; + right: 0; + top: -6px; + display: block; + width: 3em; + height: 3em; + line-height: 3; + text-align: center; + -webkit-transition: all .35s; + -o-transition: all .35s; + transition: all .35s; +} +.tab input[type=checkbox] + label::after { + content: "+"; +} +.tab input[type=radio] + label::after { + content: "\25BC"; +} +.tab input[type=checkbox]:checked + label::after { + transform: rotate(315deg); +} +.tab input[type=radio]:checked + label::after { + transform: rotateX(180deg); +} + +.md-headline-info{ + font-weight: 400; + line-height: 34px; +} + +.datamodel-value{ + color:#808080 +} diff --git a/ui/src/app/data_models/object_info.tpl.html b/ui/src/app/data_models/object_info.tpl.html index fe888e220..8af69a379 100644 --- a/ui/src/app/data_models/object_info.tpl.html +++ b/ui/src/app/data_models/object_info.tpl.html @@ -16,51 +16,70 @@ limitations under the License. --> - + + +
-

dataModels.objectInfo

+

dataModels.objectInfo

+
+
+ + +
+ +

dataModels.name: {{vm.nodeValue.datamodelObject.name}}

+
+ +

dataModels.description: {{vm.nodeValue.datamodelObject.desc}}

+
+ +

dataModels.type: {{vm.nodeValue.datamodelObject.type}}

+
- - - - dataModels.datamodelObject - -

dataModels.name: {{vm.nodeValue.datamodelObject.name}}

-
- -

dataModels.description: {{vm.nodeValue.datamodelObject.desc}}

-
- -

dataModels.type: {{vm.nodeValue.datamodelObject.type}}

-
- - dataModels.objectAttribute - -

{{ attrib }}

-
- - dataModels.relationships - -

dataModels.parent: {{vm.nodeValue.datamodelObject.parent_id ? vm.nodeValue.datamodelObject.parent : "None"}}

-
- - dataModels.image +
+
+
+ + +
+ +

{{attribute}}

+
+ +

dataModels.noAttribute

+
- -

dataModels.icon:

-
+
+
+
+ + +
+ +

dataModels.parent: {{vm.nodeValue.datamodelObject.parent_id ? vm.nodeValue.datamodelObject.parent : "None"}}

+
+
+
- +
+ + +
+ +

dataModels.image:

+
+
+
- +
\ No newline at end of file diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 3339f53cf..7096d2e8a 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -326,6 +326,23 @@ export default angular.module('tempus.locale', []) "start-required":"Start is required", "end-required":"End is required" + }, + + "gateway":{ + "configuration":"Agent", + "configurationLabel": "Agent Configuration", + "device-label":"Gateway Device", + "replica":"Replica", + "deploy":"Deploy", + "field-required":"Field is required", + "config-save-message":"Configuration saved successfully", + "config-deploy-message":"Agent deployed successfully", + "config-deploy-fail-message":"Agent not deployed", + "total-replica":"Total Replica", + "ready":"Ready", + "in-progress":"In Progress", + "crash":"Crashed" + }, "audit-log": { "audit": "Audit", @@ -599,8 +616,15 @@ export default angular.module('tempus.locale', []) "add": "Add Data Model", "name": "Title", "description" :"Description", - "name-required":"This feild is required" - + "name-required":"This feild is required", + "objectInfo":"Datamodel Object Information", + "datamodelObject":"Datamodel Object", + "type":"Type", + "objectAttribute":"Attributes", + "relationships":"Relationships", + "image":"Icon", + "parent":"Parent", + "noAttribute":"No Attributes" }, "datakey": { "settings": "Settings", diff --git a/ui/src/app/services/menu.service.js b/ui/src/app/services/menu.service.js index 70774887d..79c128656 100644 --- a/ui/src/app/services/menu.service.js +++ b/ui/src/app/services/menu.service.js @@ -301,7 +301,15 @@ function Menu(userService, $state, $rootScope, $log,datamodelService,customerSer type: 'link', state: 'home.auditLogs', icon: 'track_changes' - }]; + }, + { + name: 'gateway.configuration', + type: 'link', + state: 'home.gateway', + icon: 'settings_applications' + } + + ]; generatedSectionTree = {}; @@ -394,7 +402,20 @@ function Menu(userService, $state, $rootScope, $log,datamodelService,customerSer state: 'home.metadata' } ] - } + }, + + { + name: 'gateway.configurationLabel', + places: [ + { + name: 'gateway.configuration', + type: 'link', + state: 'home.gateway', + icon: 'settings_applications' + } + ] + } + ]; } else if (authority === 'CUSTOMER_USER') { diff --git a/ui/src/app/tempusgateway/gateway-configuration.tpl.html b/ui/src/app/tempusgateway/gateway-configuration.tpl.html new file mode 100755 index 000000000..d8fe949b4 --- /dev/null +++ b/ui/src/app/tempusgateway/gateway-configuration.tpl.html @@ -0,0 +1,103 @@ + +
+ + + + gateway.configurationLabel + + + + + + +
+
+ + + + + {{value}} + + +
+
gateway.field-required
+
+
+ + + +
+
gateway.field-required
+
+ + +
+ +
+ {{'gateway.deploy' | translate}} + {{'action.save' | translate}} +
+
+
+
+
+ + + +
+ +
+ + + + refresh + + {{ 'action.refresh' | translate }} + + +
+
+ + + + + + + + + + + + + + + + + + + + +
gateway.total-replicagateway.readygateway.in-progressgateway.crash
{{vm.podStatus.replica}}{{vm.podStatus.ready}}{{vm.podStatus.inProgress}}{{vm.podStatus.crashed}}
+
+ diff --git a/ui/src/app/tempusgateway/gateway.controller.js b/ui/src/app/tempusgateway/gateway.controller.js new file mode 100644 index 000000000..8e8612951 --- /dev/null +++ b/ui/src/app/tempusgateway/gateway.controller.js @@ -0,0 +1,124 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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. + */ +/* eslint-disable import/no-unresolved, import/default, no-unused-vars */ +import './gateway.scss'; + +/*@ngInject*/ +export function GatewayController(deviceService, $scope, gatewayConfigurationService, $translate, toast) { + + var vm = this; + + vm.getGatewayDevices = getGatewayDevices; + vm.save = save; + vm.replica = 1; + vm.loadGatewayConfiguration = loadGatewayConfiguration; + vm.deployTempusGateway = deployTempusGateway; + vm.getTempusGatewayPodsStatus = getTempusGatewayPodsStatus; + vm.refresh = refresh; + vm.scopeData = ''; + vm.configId =''; + vm.podStatus =''; + vm.getGatewayDevices(); + vm.loadGatewayConfiguration(); + vm.getTempusGatewayPodsStatus(); + + function getGatewayDevices() { + + var pageLink = {limit:100}; + deviceService.getTenantDevices(pageLink,'','','Gateway').then(function success(response) { + var devicelist = response.data; + devicelist.forEach(device => { + vm.gatewayDevicelist = {}; + deviceService.getDeviceCredentials(device.id.id).then(function success(res) { + var data = res; + vm.gatewayDevicelist[data.credentialsId] = device.name; + + }) + + }); + + }); + } + + function getTempusGatewayPodsStatus(){ + + gatewayConfigurationService.getTempusGatewayPodsStatus().then(function success(response){ + vm.podStatus = response; + if(vm.podStatus.replica == 0) { + + vm.getTempusGatewayPodsStatus(); + } + }); + + } + + function refresh() { + + vm.getTempusGatewayPodsStatus(); + } + + function loadGatewayConfiguration() { + + gatewayConfigurationService.getGatewayConfiguration().then(function success(response){ + vm.scopeData = response; + if(response.id !== null) { + vm.replica = response.replicas; + vm.accesstoken = response.gatewayToken; + vm.configId = response.id.id; + } + }); + + } + + function save() { + + if(vm.scopeData.id !== null) { + vm.item = vm.scopeData; + vm.item.replicas = vm.replica; + vm.item.gatewayToken = vm.accesstoken; + } else { + vm.item ={}; + vm.item = { + replicas:vm.replica, + gatewayToken:vm.accesstoken + }; + } + gatewayConfigurationService.saveGatewayConfiguration(vm.item).then(function success(item){ + if(item) { + vm.scopeData = item; + vm.accesstoken = item.gatewayToken; + vm.configId = item.id.id; + toast.showSuccess($translate.instant('gateway.config-save-message')); + } + }); + } + + + function deployTempusGateway() { + + gatewayConfigurationService.deployTempusGateway().then(function success(res){ + if(res.data == false) { + toast.showError($translate.instant('gateway.config-deploy-fail-message')); + } else { + vm.getTempusGatewayPodsStatus(); + toast.showSuccess($translate.instant('gateway.config-deploy-message')); + } + }); + + } + +} \ No newline at end of file diff --git a/ui/src/app/tempusgateway/gateway.scss b/ui/src/app/tempusgateway/gateway.scss new file mode 100644 index 000000000..77fea54cc --- /dev/null +++ b/ui/src/app/tempusgateway/gateway.scss @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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-gateway-container{ + margin-left: 1%; + margin-right: 3%; + width: 92.9%; +} + +.tb-gateway-table{ + width: 64% !important; + background-color: white; +} + +.tb-gateway-toolbar { + margin-left: 1%; + width: 959px; +} \ No newline at end of file diff --git a/ui/src/app/tempusgateway/index.js b/ui/src/app/tempusgateway/index.js new file mode 100755 index 000000000..50f71e309 --- /dev/null +++ b/ui/src/app/tempusgateway/index.js @@ -0,0 +1,31 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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 uiRouter from 'angular-ui-router'; +import tempusApiGatewayConfiguration from '../api/gateway-configuration.service'; + +import TempusGatewayRoutes from './tempus_gateway.routes'; +import {GatewayController} from './gateway.controller'; + + +export default angular.module('tempus.gateway', [ + uiRouter, + tempusApiGatewayConfiguration +]) + .config(TempusGatewayRoutes) + .controller('TempusGatewayController',GatewayController) + .name; diff --git a/ui/src/app/tempusgateway/tempus_gateway.routes.js b/ui/src/app/tempusgateway/tempus_gateway.routes.js new file mode 100755 index 000000000..8728c15e2 --- /dev/null +++ b/ui/src/app/tempusgateway/tempus_gateway.routes.js @@ -0,0 +1,47 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * Modifications © 2017-2018 Hashmap, Inc + * + * 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. + */ +/* eslint-disable import/no-unresolved, import/default */ + +import gatewayConfigurationTemplate from './gateway-configuration.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +/*@ngInject*/ +export default function TempusGatewayRoutes($stateProvider) { + $stateProvider + .state('home.gateway', { + url: '/gateway/config', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: gatewayConfigurationTemplate, + controller: 'TempusGatewayController', + controllerAs: 'vm' + } + } + , + data: { + pageTitle: 'gateway.configuration' + }, + ncyBreadcrumb: { + label: '{"icon": "settings_applications", "label": "gateway.configuration"}' + } + }) + +}