70 changed files with 1853 additions and 500 deletions
@ -0,0 +1,67 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class TenantMsgConstructor { |
||||
|
|
||||
|
public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { |
||||
|
TenantUpdateMsg.Builder builder = TenantUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(tenant.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(tenant.getId().getId().getLeastSignificantBits()) |
||||
|
.setTitle(tenant.getTitle()) |
||||
|
.setProfileIdMSB(tenant.getTenantProfileId().getId().getMostSignificantBits()) |
||||
|
.setProfileIdLSB(tenant.getTenantProfileId().getId().getLeastSignificantBits()) |
||||
|
.setRegion(tenant.getRegion()); |
||||
|
if (tenant.getCountry() != null) { |
||||
|
builder.setCountry(tenant.getCountry()); |
||||
|
} |
||||
|
if (tenant.getState() != null) { |
||||
|
builder.setState(tenant.getState()); |
||||
|
} |
||||
|
if (tenant.getCity() != null) { |
||||
|
builder.setCity(tenant.getCity()); |
||||
|
} |
||||
|
if (tenant.getAddress() != null) { |
||||
|
builder.setAddress(tenant.getAddress()); |
||||
|
} |
||||
|
if (tenant.getAddress2() != null) { |
||||
|
builder.setAddress2(tenant.getAddress2()); |
||||
|
} |
||||
|
if (tenant.getZip() != null) { |
||||
|
builder.setZip(tenant.getZip()); |
||||
|
} |
||||
|
if (tenant.getPhone() != null) { |
||||
|
builder.setPhone(tenant.getPhone()); |
||||
|
} |
||||
|
if (tenant.getEmail() != null) { |
||||
|
builder.setEmail(tenant.getEmail()); |
||||
|
} |
||||
|
if (tenant.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(tenant.getAdditionalInfo())); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import com.google.protobuf.ByteString; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.DataDecodingEncodingService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class TenantProfileMsgConstructor { |
||||
|
|
||||
|
@Autowired |
||||
|
private DataDecodingEncodingService dataDecodingEncodingService; |
||||
|
|
||||
|
public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile) { |
||||
|
TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(tenantProfile.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(tenantProfile.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(tenantProfile.getName()) |
||||
|
.setDefault(tenantProfile.isDefault()) |
||||
|
.setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine()) |
||||
|
.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData()))); |
||||
|
if (tenantProfile.getDescription() != null) { |
||||
|
builder.setDescription(tenantProfile.getDescription()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.fetch; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.tenant.TenantService; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@AllArgsConstructor |
||||
|
@Slf4j |
||||
|
public class TenantEdgeEventFetcher extends BasePageableEdgeEventFetcher<Tenant> { |
||||
|
|
||||
|
private final TenantService tenantService; |
||||
|
|
||||
|
@Override |
||||
|
PageData<Tenant> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { |
||||
|
Tenant tenant = tenantService.findTenantById(tenantId); |
||||
|
// returns PageData object to be in sync with other fetchers
|
||||
|
return new PageData<>(List.of(tenant), 1, 1, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Tenant entity) { |
||||
|
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.TENANT, |
||||
|
EdgeEventActionType.UPDATED, entity.getId(), null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor.tenant; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class TenantEdgeProcessor extends BaseEdgeProcessor { |
||||
|
|
||||
|
public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent) { |
||||
|
TenantId tenantId = new TenantId(edgeEvent.getEntityId()); |
||||
|
DownlinkMsg downlinkMsg = null; |
||||
|
if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { |
||||
|
Tenant tenant = tenantService.findTenantById(tenantId); |
||||
|
if (tenant != null) { |
||||
|
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); |
||||
|
TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant); |
||||
|
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); |
||||
|
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile); |
||||
|
downlinkMsg = DownlinkMsg.newBuilder() |
||||
|
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) |
||||
|
.addTenantUpdateMsg(tenantUpdateMsg) |
||||
|
.addTenantProfileUpdateMsg(tenantProfileUpdateMsg) |
||||
|
.build(); |
||||
|
} |
||||
|
} |
||||
|
return downlinkMsg; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor.tenant; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.id.TenantProfileId; |
||||
|
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { |
||||
|
|
||||
|
public DownlinkMsg convertTenantProfileEventToDownlink(EdgeEvent edgeEvent) { |
||||
|
TenantProfileId tenantProfileId = new TenantProfileId(edgeEvent.getEntityId()); |
||||
|
DownlinkMsg downlinkMsg = null; |
||||
|
if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { |
||||
|
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(edgeEvent.getTenantId(), tenantProfileId); |
||||
|
if (tenantProfile != null) { |
||||
|
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); |
||||
|
TenantProfileUpdateMsg tenantProfileUpdateMsg = |
||||
|
tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile); |
||||
|
downlinkMsg = DownlinkMsg.newBuilder() |
||||
|
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) |
||||
|
.addTenantProfileUpdateMsg(tenantProfileUpdateMsg) |
||||
|
.build(); |
||||
|
} |
||||
|
} |
||||
|
return downlinkMsg; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,194 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.notification.channels; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.google.common.base.Strings; |
||||
|
import lombok.Data; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import org.apache.commons.codec.binary.Base64; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.boot.web.client.RestTemplateBuilder; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.web.client.RestTemplate; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; |
||||
|
import org.thingsboard.server.common.data.notification.info.NotificationInfo; |
||||
|
import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig; |
||||
|
import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate; |
||||
|
import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate.Button.LinkType; |
||||
|
import org.thingsboard.server.service.notification.NotificationProcessingContext; |
||||
|
import org.thingsboard.server.service.security.system.SystemSecurityService; |
||||
|
|
||||
|
import java.time.Duration; |
||||
|
import java.time.temporal.ChronoUnit; |
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@Component |
||||
|
@RequiredArgsConstructor |
||||
|
public class MicrosoftTeamsNotificationChannel implements NotificationChannel<MicrosoftTeamsNotificationTargetConfig, MicrosoftTeamsDeliveryMethodNotificationTemplate> { |
||||
|
|
||||
|
private final SystemSecurityService systemSecurityService; |
||||
|
|
||||
|
@Setter |
||||
|
private RestTemplate restTemplate = new RestTemplateBuilder() |
||||
|
.setConnectTimeout(Duration.of(15, ChronoUnit.SECONDS)) |
||||
|
.setReadTimeout(Duration.of(15, ChronoUnit.SECONDS)) |
||||
|
.build(); |
||||
|
|
||||
|
@Override |
||||
|
public void sendNotification(MicrosoftTeamsNotificationTargetConfig targetConfig, MicrosoftTeamsDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception { |
||||
|
Message message = new Message(); |
||||
|
message.setThemeColor(Strings.emptyToNull(processedTemplate.getThemeColor())); |
||||
|
if (StringUtils.isEmpty(processedTemplate.getSubject())) { |
||||
|
message.setText(processedTemplate.getBody()); |
||||
|
} else { |
||||
|
message.setSummary(processedTemplate.getSubject()); |
||||
|
Message.Section section = new Message.Section(); |
||||
|
section.setActivityTitle(processedTemplate.getSubject()); |
||||
|
section.setActivitySubtitle(processedTemplate.getBody()); |
||||
|
message.setSections(List.of(section)); |
||||
|
} |
||||
|
var button = processedTemplate.getButton(); |
||||
|
if (button != null && button.isEnabled()) { |
||||
|
String uri; |
||||
|
if (button.getLinkType() == LinkType.DASHBOARD) { |
||||
|
String state = null; |
||||
|
if (button.isSetEntityIdInState() || StringUtils.isNotEmpty(button.getDashboardState())) { |
||||
|
ObjectNode stateObject = JacksonUtil.newObjectNode(); |
||||
|
if (button.isSetEntityIdInState()) { |
||||
|
stateObject.putObject("params") |
||||
|
.set("entityId", Optional.ofNullable(ctx.getRequest().getInfo()) |
||||
|
.map(NotificationInfo::getStateEntityId) |
||||
|
.map(JacksonUtil::valueToTree) |
||||
|
.orElse(null)); |
||||
|
} else { |
||||
|
stateObject.putObject("params"); |
||||
|
} |
||||
|
if (StringUtils.isNotEmpty(button.getDashboardState())) { |
||||
|
stateObject.put("id", button.getDashboardState()); |
||||
|
} |
||||
|
state = Base64.encodeBase64String(JacksonUtil.OBJECT_MAPPER.writeValueAsBytes(List.of(stateObject))); |
||||
|
} |
||||
|
String baseUrl = systemSecurityService.getBaseUrl(ctx.getTenantId(), null, null); |
||||
|
if (StringUtils.isEmpty(baseUrl)) { |
||||
|
throw new IllegalStateException("Failed to determine base url to construct dashboard link"); |
||||
|
} |
||||
|
uri = baseUrl + "/dashboards/" + button.getDashboardId(); |
||||
|
if (state != null) { |
||||
|
uri += "?state=" + state; |
||||
|
} |
||||
|
} else { |
||||
|
uri = button.getLink(); |
||||
|
} |
||||
|
if (StringUtils.isNotBlank(uri) && button.getText() != null) { |
||||
|
Message.ActionCard actionCard = new Message.ActionCard(); |
||||
|
actionCard.setType("OpenUri"); |
||||
|
actionCard.setName(button.getText()); |
||||
|
var target = new Message.ActionCard.Target("default", uri); |
||||
|
actionCard.setTargets(List.of(target)); |
||||
|
message.setPotentialAction(List.of(actionCard)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
restTemplate.postForEntity(targetConfig.getWebhookUrl(), message, String.class); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void check(TenantId tenantId) throws Exception { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public NotificationDeliveryMethod getDeliveryMethod() { |
||||
|
return NotificationDeliveryMethod.MICROSOFT_TEAMS; |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class Message { |
||||
|
@JsonProperty("@type") |
||||
|
private final String type = "MessageCard"; |
||||
|
@JsonProperty("@context") |
||||
|
private final String context = "http://schema.org/extensions"; |
||||
|
private String themeColor; |
||||
|
private String summary; |
||||
|
private String text; |
||||
|
private List<Section> sections; |
||||
|
private List<ActionCard> potentialAction; |
||||
|
|
||||
|
@Data |
||||
|
public static class Section { |
||||
|
private String activityTitle; |
||||
|
private String activitySubtitle; |
||||
|
private String activityImage; |
||||
|
private List<Fact> facts; |
||||
|
private boolean markdown; |
||||
|
|
||||
|
@Data |
||||
|
public static class Fact { |
||||
|
private final String name; |
||||
|
private final String value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) |
||||
|
public static class ActionCard { |
||||
|
@JsonProperty("@type") |
||||
|
private String type; // ActionCard, OpenUri
|
||||
|
private String name; |
||||
|
private List<Input> inputs; // for ActionCard
|
||||
|
private List<Action> actions; // for ActionCard
|
||||
|
private List<Target> targets; |
||||
|
|
||||
|
@Data |
||||
|
public static class Input { |
||||
|
@JsonProperty("@type") |
||||
|
private String type; // TextInput, DateInput, MultichoiceInput
|
||||
|
private String id; |
||||
|
private boolean isMultiple; |
||||
|
private String title; |
||||
|
private boolean isMultiSelect; |
||||
|
|
||||
|
@Data |
||||
|
public static class Choice { |
||||
|
private final String display; |
||||
|
private final String value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class Action { |
||||
|
@JsonProperty("@type") |
||||
|
private final String type; // HttpPOST
|
||||
|
private final String name; |
||||
|
private final String target; // url
|
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
public static class Target { |
||||
|
private final String os; |
||||
|
private final String uri; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,90 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.edge; |
||||
|
|
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.id.TenantProfileId; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class TenantEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testUpdateTenant() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
// save current value into tmp to revert after test
|
||||
|
Tenant savedTenant = doGet("/api/tenant/" + tenantId, Tenant.class); |
||||
|
|
||||
|
// updated edge tenant
|
||||
|
savedTenant.setTitle("Updated Title for Tenant Edge Test"); |
||||
|
edgeImitator.expectMessageAmount(2); // expect tenant and tenant profile update msg
|
||||
|
savedTenant = doPost("/api/tenant", savedTenant, Tenant.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
Optional<TenantUpdateMsg> tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); |
||||
|
Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); |
||||
|
TenantUpdateMsg tenantUpdateMsg = tenantUpdateMsgOpt.get(); |
||||
|
Optional<TenantProfileUpdateMsg> tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); |
||||
|
Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); |
||||
|
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedTenant.getUuidId().getMostSignificantBits(), tenantUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedTenant.getUuidId().getLeastSignificantBits(), tenantUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedTenant.getTitle(), tenantUpdateMsg.getTitle()); |
||||
|
Assert.assertEquals("Updated Title for Tenant Edge Test", tenantUpdateMsg.getTitle()); |
||||
|
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId |
||||
|
(new UUID(tenantUpdateMsg.getProfileIdMSB(), tenantUpdateMsg.getProfileIdLSB()))); |
||||
|
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId |
||||
|
(new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB()))); |
||||
|
|
||||
|
//change tenant profile for tenant
|
||||
|
TenantProfile tenantProfile = createTenantProfile(); |
||||
|
savedTenant.setTenantProfileId(tenantProfile.getId()); |
||||
|
edgeImitator.expectMessageAmount(2); // expect tenant and tenant profile update msg
|
||||
|
savedTenant = doPost("/api/tenant", savedTenant, Tenant.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); |
||||
|
Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); |
||||
|
tenantUpdateMsg = tenantUpdateMsgOpt.get(); |
||||
|
tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); |
||||
|
Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); |
||||
|
tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); |
||||
|
// tenant update
|
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedTenant.getUuidId().getMostSignificantBits(), tenantUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedTenant.getUuidId().getLeastSignificantBits(), tenantUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedTenant.getTitle(), tenantUpdateMsg.getTitle()); |
||||
|
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId |
||||
|
(new UUID(tenantUpdateMsg.getProfileIdMSB(), tenantUpdateMsg.getProfileIdLSB()))); |
||||
|
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId |
||||
|
(new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB()))); |
||||
|
} |
||||
|
|
||||
|
private TenantProfile createTenantProfile() { |
||||
|
TenantProfile tenantProfile = new TenantProfile(); |
||||
|
tenantProfile.setName("TestEdge tenant profile"); |
||||
|
return doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.edge; |
||||
|
|
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class TenantProfileEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testTenantProfiles() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
// save current values into tmp to revert after test
|
||||
|
TenantProfile edgeTenantProfile = doGet("/api/tenantProfile/" + tenantProfileId.getId(), TenantProfile.class); |
||||
|
|
||||
|
// updated edge tenant profile
|
||||
|
edgeTenantProfile.setName("Tenant Profile Edge Test"); |
||||
|
edgeTenantProfile.setDescription("Updated tenant profile Edge Test"); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
edgeTenantProfile = doPost("/api/tenantProfile", edgeTenantProfile, TenantProfile.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof TenantProfileUpdateMsg); |
||||
|
TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(edgeTenantProfile.getUuidId().getMostSignificantBits(), tenantProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(edgeTenantProfile.getUuidId().getLeastSignificantBits(), tenantProfileUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(edgeTenantProfile.getDescription(), tenantProfileUpdateMsg.getDescription()); |
||||
|
Assert.assertEquals("Updated tenant profile Edge Test", tenantProfileUpdateMsg.getDescription()); |
||||
|
Assert.assertEquals("Tenant Profile Edge Test", tenantProfileUpdateMsg.getName()); |
||||
|
|
||||
|
loginTenantAdmin(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.common.data.notification.targets; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
|
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
import javax.validation.constraints.NotEmpty; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class MicrosoftTeamsNotificationTargetConfig extends NotificationTargetConfig implements NotificationRecipient { |
||||
|
|
||||
|
@NotBlank |
||||
|
private String webhookUrl; |
||||
|
@NotEmpty |
||||
|
private String channelName; |
||||
|
|
||||
|
@Override |
||||
|
public NotificationTargetType getType() { |
||||
|
return NotificationTargetType.MICROSOFT_TEAMS; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Object getId() { |
||||
|
return webhookUrl; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getTitle() { |
||||
|
return channelName; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,90 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.common.data.notification.template; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.ToString; |
||||
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@NoArgsConstructor |
||||
|
@ToString(callSuper = true) |
||||
|
public class MicrosoftTeamsDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { |
||||
|
|
||||
|
private String subject; |
||||
|
private String themeColor; |
||||
|
private Button button; |
||||
|
|
||||
|
private final List<TemplatableValue> templatableValues = List.of( |
||||
|
TemplatableValue.of(this::getBody, this::setBody), |
||||
|
TemplatableValue.of(this::getSubject, this::setSubject), |
||||
|
TemplatableValue.of(() -> button != null ? button.getText() : null, |
||||
|
processed -> { if (button != null) button.setText(processed); }), |
||||
|
TemplatableValue.of(() -> button != null ? button.getLink() : null, |
||||
|
processed -> { if (button != null) button.setLink(processed); }) |
||||
|
); |
||||
|
|
||||
|
public MicrosoftTeamsDeliveryMethodNotificationTemplate(MicrosoftTeamsDeliveryMethodNotificationTemplate other) { |
||||
|
super(other); |
||||
|
this.subject = other.subject; |
||||
|
this.themeColor = other.themeColor; |
||||
|
this.button = other.button != null ? new Button(other.button) : null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public NotificationDeliveryMethod getMethod() { |
||||
|
return NotificationDeliveryMethod.MICROSOFT_TEAMS; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public MicrosoftTeamsDeliveryMethodNotificationTemplate copy() { |
||||
|
return new MicrosoftTeamsDeliveryMethodNotificationTemplate(this); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
@NoArgsConstructor |
||||
|
public static class Button { |
||||
|
private boolean enabled; |
||||
|
private String text; |
||||
|
private LinkType linkType; |
||||
|
private String link; |
||||
|
|
||||
|
private UUID dashboardId; |
||||
|
private String dashboardState; |
||||
|
private boolean setEntityIdInState; |
||||
|
|
||||
|
public Button(Button other) { |
||||
|
this.enabled = other.enabled; |
||||
|
this.text = other.text; |
||||
|
this.linkType = other.linkType; |
||||
|
this.link = other.link; |
||||
|
this.dashboardId = other.dashboardId; |
||||
|
this.dashboardState = other.dashboardState; |
||||
|
this.setEntityIdInState = other.setEntityIdInState; |
||||
|
} |
||||
|
|
||||
|
public enum LinkType { |
||||
|
LINK, DASHBOARD |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.common.data.notification.template; |
||||
|
|
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
|
||||
|
import java.util.Collection; |
||||
|
import java.util.function.Consumer; |
||||
|
import java.util.function.Supplier; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
public class TemplatableValue { |
||||
|
private final Supplier<String> getter; |
||||
|
private final Consumer<String> setter; |
||||
|
|
||||
|
public static TemplatableValue of(Supplier<String> getter, Consumer<String> setter) { |
||||
|
return new TemplatableValue(getter, setter); |
||||
|
} |
||||
|
|
||||
|
public String get() { |
||||
|
return getter.get(); |
||||
|
} |
||||
|
|
||||
|
public void set(String processed) { |
||||
|
setter.accept(processed); |
||||
|
} |
||||
|
|
||||
|
public boolean containsParams(Collection<String> params) { |
||||
|
return StringUtils.containsAny(get(), params.toArray(String[]::new)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue