|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"widgetsBundle": { |
||||
|
"alias": "scada_symbols", |
||||
|
"title": "SCADA symbols", |
||||
|
"scada": true, |
||||
|
"image": null, |
||||
|
"description": "Bundle with SCADA symbols", |
||||
|
"order": 9200, |
||||
|
"name": "SCADA symbols" |
||||
|
}, |
||||
|
"widgetTypeFqns": [ |
||||
|
"scada_symbol" |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
{ |
||||
|
"widgetsBundle": { |
||||
|
"alias": "scada_water_system_symbols", |
||||
|
"title": "SCADA water system symbols", |
||||
|
"scada": true, |
||||
|
"image": null, |
||||
|
"description": "Bundle with SCADA symbols for water system", |
||||
|
"order": 9300, |
||||
|
"name": "SCADA water system symbols" |
||||
|
}, |
||||
|
"widgetTypeFqns": [ |
||||
|
"horizontal_pipe", |
||||
|
"long_horizontal_pipe", |
||||
|
"vertical_pipe", |
||||
|
"long_vertical_pipe", |
||||
|
"left_bottom_elbow_pipe", |
||||
|
"bottom_right_elbow_pipe", |
||||
|
"top_right_elbow_pipe", |
||||
|
"left_top_elbow_pipe", |
||||
|
"cross_pipe", |
||||
|
"left_tee_pipe", |
||||
|
"bottom_tee_pipe", |
||||
|
"right_tee_pipe", |
||||
|
"top_tee_pipe", |
||||
|
"right_elbow_drain_pipe", |
||||
|
"left_elbow_drain_pipe", |
||||
|
"left_drain_pipe", |
||||
|
"right_drain_pipe", |
||||
|
"short_left_drain_pipe", |
||||
|
"short_right_drain_pipe", |
||||
|
"top_flow_meter", |
||||
|
"right_flow_meter", |
||||
|
"bottom_flow_meter", |
||||
|
"left_flow_meter", |
||||
|
"horizontal_inline_flow_meter", |
||||
|
"vertical_inline_flow_meter", |
||||
|
"centrifugal_pump", |
||||
|
"small_right_motor_pump", |
||||
|
"small_left_motor_pump", |
||||
|
"right_motor_pump", |
||||
|
"left_motor_pump", |
||||
|
"right_heat_pump", |
||||
|
"left_heat_pump", |
||||
|
"short_bottom_filter", |
||||
|
"long_bottom_filter", |
||||
|
"short_top_filter", |
||||
|
"long_top_filter", |
||||
|
"stand_filter", |
||||
|
"horizontal_wheel_valve", |
||||
|
"vertical_wheel_valve", |
||||
|
"horizontal_ball_valve", |
||||
|
"vertical_ball_valve", |
||||
|
"vertical_tank", |
||||
|
"stand_vertical_tank", |
||||
|
"cylindrical_tank", |
||||
|
"stand_cylindrical_tank", |
||||
|
"vertical_short_tank", |
||||
|
"stand_vertical_short_tank", |
||||
|
"large_cylindrical_tank", |
||||
|
"large_stand_cylindrical_tank", |
||||
|
"large_vertical_tank", |
||||
|
"large_stand_vertical_tank", |
||||
|
"horizontal_tank", |
||||
|
"stand_horizontal_tank", |
||||
|
"spherical_tank", |
||||
|
"small_spherical_tank", |
||||
|
"elevated_tank", |
||||
|
"pool" |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
{ |
||||
|
"fqn": "scada_symbol", |
||||
|
"name": "SCADA symbol", |
||||
|
"deprecated": false, |
||||
|
"scada": true, |
||||
|
"image": null, |
||||
|
"description": "", |
||||
|
"descriptor": { |
||||
|
"type": "rpc", |
||||
|
"sizeX": 3, |
||||
|
"sizeY": 3, |
||||
|
"resources": [], |
||||
|
"templateHtml": "<tb-scada-symbol-widget\n [ctx]='ctx'\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-scada-symbol-widget>", |
||||
|
"templateCss": "", |
||||
|
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '300px',\n previewHeight: '320px',\n embedTitlePanel: true,\n targetDeviceOptional: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", |
||||
|
"settingsSchema": "", |
||||
|
"dataKeySettingsSchema": "{}\n", |
||||
|
"settingsDirective": "tb-scada-symbol-widget-settings", |
||||
|
"hasBasicMode": true, |
||||
|
"basicModeDirective": "tb-scada-symbol-basic-config", |
||||
|
"defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"background\":{\"type\":\"color\",\"imageUrl\":null,\"color\":\"rgb(255, 255, 255)\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"12px\",\"scadaSymbolUrl\":\"\",\"scadaSymbolObjectSettings\":{\"behavior\":{},\"properties\":{}}},\"title\":\"SCADA symbol\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"actions\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"1.6\"},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":null,\"pageSize\":1024,\"titleIcon\":\"mdi:lightbulb-outline\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"configMode\":\"basic\",\"targetDevice\":null,\"titleColor\":null,\"borderRadius\":\"0px\",\"margin\":\"0px\"}" |
||||
|
}, |
||||
|
"tags": null |
||||
|
} |
||||
@ -1,78 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2024 The Thingsboard Authors |
|
||||
* |
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
* you may not use this file except in compliance with the License. |
|
||||
* You may obtain a copy of the License at |
|
||||
* |
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||
* |
|
||||
* Unless required by applicable law or agreed to in writing, software |
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
* See the License for the specific language governing permissions and |
|
||||
* limitations under the License. |
|
||||
*/ |
|
||||
package org.thingsboard.server.config; |
|
||||
|
|
||||
import jakarta.servlet.FilterChain; |
|
||||
import jakarta.servlet.ServletException; |
|
||||
import jakarta.servlet.http.HttpServletRequest; |
|
||||
import jakarta.servlet.http.HttpServletResponse; |
|
||||
import lombok.RequiredArgsConstructor; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Value; |
|
||||
import org.springframework.stereotype.Component; |
|
||||
import org.springframework.util.AntPathMatcher; |
|
||||
import org.springframework.web.filter.OncePerRequestFilter; |
|
||||
import org.thingsboard.server.common.msg.tools.MaxPayloadSizeExceededException; |
|
||||
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; |
|
||||
|
|
||||
import java.io.IOException; |
|
||||
import java.util.List; |
|
||||
|
|
||||
@Slf4j |
|
||||
@Component |
|
||||
@RequiredArgsConstructor |
|
||||
public class RequestSizeFilter extends OncePerRequestFilter { |
|
||||
|
|
||||
private final List<String> urls = List.of("/api/plugins/rpc/**", "/api/rpc/**"); |
|
||||
private final AntPathMatcher pathMatcher = new AntPathMatcher(); |
|
||||
private final ThingsboardErrorResponseHandler errorResponseHandler; |
|
||||
|
|
||||
@Value("${transport.http.max_payload_size:65536}") |
|
||||
private int maxPayloadSize; |
|
||||
|
|
||||
@Override |
|
||||
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { |
|
||||
if (request.getContentLength() > maxPayloadSize) { |
|
||||
if (log.isDebugEnabled()) { |
|
||||
log.debug("Too large payload size. Url: {}, client ip: {}, content length: {}", request.getRequestURL(), |
|
||||
request.getRemoteAddr(), request.getContentLength()); |
|
||||
} |
|
||||
errorResponseHandler.handle(new MaxPayloadSizeExceededException(), response); |
|
||||
return; |
|
||||
} |
|
||||
chain.doFilter(request, response); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) { |
|
||||
for (String url : urls) { |
|
||||
if (pathMatcher.match(url, request.getRequestURI())) { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected boolean shouldNotFilterAsyncDispatch() { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected boolean shouldNotFilterErrorDispatch() { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,132 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2024 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.controller; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.Parameter; |
||||
|
import io.swagger.v3.oas.annotations.media.ArraySchema; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.Valid; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.DeleteMapping; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.PutMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import org.thingsboard.server.common.data.domain.Domain; |
||||
|
import org.thingsboard.server.common.data.domain.DomainInfo; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.DomainId; |
||||
|
import org.thingsboard.server.common.data.id.OAuth2ClientId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.config.annotations.ApiOperation; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.entitiy.domain.TbDomainService; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; |
||||
|
|
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class DomainController extends BaseController { |
||||
|
|
||||
|
private final TbDomainService tbDomainService; |
||||
|
|
||||
|
@ApiOperation(value = "Save or Update Domain (saveDomain)", |
||||
|
notes = "Create or update the Domain. When creating domain, platform generates Domain Id as " + UUID_WIKI_LINK + |
||||
|
"The newly created Domain Id will be present in the response. " + |
||||
|
"Specify existing Domain Id to update the domain. " + |
||||
|
"Referencing non-existing Domain Id will cause 'Not Found' error." + |
||||
|
"\n\nDomain name is unique for entire platform setup.\n\n" + SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@PostMapping(value = "/domain") |
||||
|
public Domain saveDomain( |
||||
|
@Parameter(description = "A JSON value representing the Domain.", required = true) |
||||
|
@RequestBody @Valid Domain domain, |
||||
|
@Parameter(description = "A list of oauth2 client registration ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) |
||||
|
@RequestParam(name = "oauth2ClientIds", required = false) UUID[] ids) throws Exception { |
||||
|
domain.setTenantId(getTenantId()); |
||||
|
checkEntity(domain.getId(), domain, Resource.DOMAIN); |
||||
|
return tbDomainService.save(domain, getOAuth2ClientIds(ids), getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)", |
||||
|
notes = "Update oauth2 clients for the specified domain. ") |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@PutMapping(value = "/domain/{id}/oauth2Clients") |
||||
|
public void updateOauth2Clients(@PathVariable UUID id, |
||||
|
@RequestBody UUID[] clientIds) throws ThingsboardException { |
||||
|
DomainId domainId = new DomainId(id); |
||||
|
Domain domain = checkDomainId(domainId, Operation.WRITE); |
||||
|
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds); |
||||
|
tbDomainService.updateOauth2Clients(domain, oAuth2ClientIds, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get Domain infos (getTenantDomainInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@GetMapping(value = "/domain/infos") |
||||
|
public PageData<DomainInfo> getTenantDomainInfos(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) |
||||
|
@RequestParam int pageSize, |
||||
|
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) |
||||
|
@RequestParam int page, |
||||
|
@Parameter(description = "Case-insensitive 'substring' filter based on domain's name") |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@Parameter(description = SORT_PROPERTY_DESCRIPTION) |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@Parameter(description = SORT_ORDER_DESCRIPTION) |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
accessControlService.checkPermission(getCurrentUser(), Resource.DOMAIN, Operation.READ); |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return domainService.findDomainInfosByTenantId(getTenantId(), pageLink); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get Domain info by Id (getDomainInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@GetMapping(value = "/domain/info/{id}") |
||||
|
public DomainInfo getDomainInfoById(@PathVariable UUID id) throws ThingsboardException { |
||||
|
DomainId domainId = new DomainId(id); |
||||
|
return checkEntityId(domainId, domainService::findDomainInfoById, Operation.READ); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Delete Domain by ID (deleteDomain)", |
||||
|
notes = "Deletes Domain by ID. Referencing non-existing domain Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAuthority('SYS_ADMIN')") |
||||
|
@DeleteMapping(value = "/domain/{id}") |
||||
|
public void deleteDomain(@PathVariable UUID id) throws Exception { |
||||
|
DomainId domainId = new DomainId(id); |
||||
|
Domain domain = checkDomainId(domainId, Operation.DELETE); |
||||
|
tbDomainService.delete(domain, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,133 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2024 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.controller; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.Parameter; |
||||
|
import io.swagger.v3.oas.annotations.media.ArraySchema; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.Valid; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.DeleteMapping; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.PutMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.MobileAppId; |
||||
|
import org.thingsboard.server.common.data.id.OAuth2ClientId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.mobile.MobileApp; |
||||
|
import org.thingsboard.server.common.data.mobile.MobileAppInfo; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.config.annotations.ApiOperation; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.entitiy.mobile.TbMobileAppService; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; |
||||
|
|
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class MobileAppController extends BaseController { |
||||
|
|
||||
|
private final TbMobileAppService tbMobileAppService; |
||||
|
|
||||
|
@ApiOperation(value = "Save Or update Mobile app (saveMobileApp)", |
||||
|
notes = "Create or update the Mobile app. When creating mobile app, platform generates Mobile App Id as " + UUID_WIKI_LINK + |
||||
|
"The newly created Mobile App Id will be present in the response. " + |
||||
|
"Specify existing Mobile App Id to update the mobile app. " + |
||||
|
"Referencing non-existing Mobile App Id will cause 'Not Found' error." + |
||||
|
"\n\nMobile app package name is unique for entire platform setup.\n\n" + SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@PostMapping(value = "/mobileApp") |
||||
|
public MobileApp saveMobileApp( |
||||
|
@Parameter(description = "A JSON value representing the Mobile Application.", required = true) |
||||
|
@RequestBody @Valid MobileApp mobileApp, |
||||
|
@Parameter(description = "A list of entity oauth2 client ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) |
||||
|
@RequestParam(name = "oauth2ClientIds", required = false) UUID[] ids) throws Exception { |
||||
|
mobileApp.setTenantId(getTenantId()); |
||||
|
checkEntity(mobileApp.getId(), mobileApp, Resource.MOBILE_APP); |
||||
|
return tbMobileAppService.save(mobileApp, getOAuth2ClientIds(ids), getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)", |
||||
|
notes = "Update oauth2 clients of the specified mobile app. ") |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@PutMapping(value = "/mobileApp/{id}/oauth2Clients") |
||||
|
public void updateOauth2Clients(@PathVariable UUID id, |
||||
|
@RequestBody UUID[] clientIds) throws ThingsboardException { |
||||
|
MobileAppId mobileAppId = new MobileAppId(id); |
||||
|
MobileApp mobileApp = checkMobileAppId(mobileAppId, Operation.WRITE); |
||||
|
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds); |
||||
|
tbMobileAppService.updateOauth2Clients(mobileApp, oAuth2ClientIds, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get mobile app infos (getTenantMobileAppInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@GetMapping(value = "/mobileApp/infos") |
||||
|
public PageData<MobileAppInfo> getTenantMobileAppInfos(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) |
||||
|
@RequestParam int pageSize, |
||||
|
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) |
||||
|
@RequestParam int page, |
||||
|
@Parameter(description = "Case-insensitive 'substring' filter based on app's name") |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@Parameter(description = SORT_PROPERTY_DESCRIPTION) |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@Parameter(description = SORT_ORDER_DESCRIPTION) |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
accessControlService.checkPermission(getCurrentUser(), Resource.MOBILE_APP, Operation.READ); |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return mobileAppService.findMobileAppInfosByTenantId(getTenantId(), pageLink); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@GetMapping(value = "/mobileApp/info/{id}") |
||||
|
public MobileAppInfo getMobileAppInfoById(@PathVariable UUID id) throws ThingsboardException { |
||||
|
MobileAppId mobileAppId = new MobileAppId(id); |
||||
|
return checkEntityId(mobileAppId, mobileAppService::findMobileAppInfoById, Operation.READ); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Delete Mobile App by ID (deleteMobileApp)", |
||||
|
notes = "Deletes Mobile App by ID. Referencing non-existing mobile app Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH) |
||||
|
@PreAuthorize("hasAuthority('SYS_ADMIN')") |
||||
|
@DeleteMapping(value = "/mobileApp/{id}") |
||||
|
public void deleteMobileApp(@PathVariable UUID id) throws Exception { |
||||
|
MobileAppId mobileAppId = new MobileAppId(id); |
||||
|
MobileApp mobileApp = checkMobileAppId(mobileAppId, Operation.DELETE); |
||||
|
tbMobileAppService.delete(mobileApp, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,222 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2024 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; |
|
||||
|
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|
||||
import jakarta.annotation.PostConstruct; |
|
||||
import jakarta.annotation.PreDestroy; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||
import org.springframework.beans.factory.annotation.Value; |
|
||||
import org.springframework.context.ApplicationEventPublisher; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.common.util.JacksonUtil; |
|
||||
import org.thingsboard.common.util.ThingsBoardExecutors; |
|
||||
import org.thingsboard.server.common.data.audit.ActionType; |
|
||||
import org.thingsboard.server.common.data.edge.Edge; |
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventType; |
|
||||
import org.thingsboard.server.common.data.id.RuleChainId; |
|
||||
import org.thingsboard.server.common.data.id.TenantId; |
|
||||
import org.thingsboard.server.common.msg.queue.TbCallback; |
|
||||
import org.thingsboard.server.dao.edge.EdgeService; |
|
||||
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; |
|
||||
import org.thingsboard.server.gen.transport.TransportProtos; |
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.customer.CustomerEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.notification.NotificationEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.oauth2.OAuth2EdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor; |
|
||||
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor; |
|
||||
|
|
||||
import java.util.UUID; |
|
||||
import java.util.concurrent.ExecutorService; |
|
||||
import java.util.concurrent.TimeUnit; |
|
||||
|
|
||||
@Service |
|
||||
@TbCoreComponent |
|
||||
@Slf4j |
|
||||
public class DefaultEdgeNotificationService implements EdgeNotificationService { |
|
||||
|
|
||||
public static final String EDGE_IS_ROOT_BODY_KEY = "isRoot"; |
|
||||
|
|
||||
@Autowired |
|
||||
private EdgeService edgeService; |
|
||||
|
|
||||
@Autowired |
|
||||
private EdgeProcessor edgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private AssetEdgeProcessor assetProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private AssetProfileEdgeProcessor assetProfileEdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private DeviceEdgeProcessor deviceProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private DeviceProfileEdgeProcessor deviceProfileEdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private EntityViewEdgeProcessor entityViewProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private DashboardEdgeProcessor dashboardProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private RuleChainEdgeProcessor ruleChainProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private UserEdgeProcessor userProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private CustomerEdgeProcessor customerProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private OtaPackageEdgeProcessor otaPackageProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private WidgetBundleEdgeProcessor widgetBundleProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private WidgetTypeEdgeProcessor widgetTypeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private QueueEdgeProcessor queueProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private TenantEdgeProcessor tenantEdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private TenantProfileEdgeProcessor tenantProfileEdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private AlarmEdgeProcessor alarmProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private RelationEdgeProcessor relationProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private ResourceEdgeProcessor resourceEdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private NotificationEdgeProcessor notificationEdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
private OAuth2EdgeProcessor oAuth2EdgeProcessor; |
|
||||
|
|
||||
@Autowired |
|
||||
protected ApplicationEventPublisher eventPublisher; |
|
||||
|
|
||||
@Value("${actors.system.edge_dispatcher_pool_size:4}") |
|
||||
private int edgeDispatcherSize; |
|
||||
|
|
||||
private ExecutorService executor; |
|
||||
|
|
||||
@PostConstruct |
|
||||
public void initExecutor() { |
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(edgeDispatcherSize, "edge-notifications"); |
|
||||
} |
|
||||
|
|
||||
@PreDestroy |
|
||||
public void shutdownExecutor() { |
|
||||
if (executor != null) { |
|
||||
executor.shutdownNow(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) { |
|
||||
edge.setRootRuleChainId(ruleChainId); |
|
||||
Edge savedEdge = edgeService.saveEdge(edge); |
|
||||
ObjectNode isRootBody = JacksonUtil.newObjectNode(); |
|
||||
isRootBody.put(EDGE_IS_ROOT_BODY_KEY, Boolean.TRUE); |
|
||||
eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edge.getId()).entityId(ruleChainId) |
|
||||
.body(JacksonUtil.toString(isRootBody)).actionType(ActionType.UPDATED).build()); |
|
||||
return savedEdge; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { |
|
||||
TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); |
|
||||
log.debug("[{}] Pushing notification to edge {}", tenantId, edgeNotificationMsg); |
|
||||
final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(60); |
|
||||
try { |
|
||||
executor.submit(() -> { |
|
||||
try { |
|
||||
if (deadline < System.nanoTime()) { |
|
||||
log.warn("[{}] Skipping notification message because deadline reached {}", tenantId, edgeNotificationMsg); |
|
||||
return; |
|
||||
} |
|
||||
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
|
||||
switch (type) { |
|
||||
case EDGE -> edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg); |
|
||||
case ASSET -> assetProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case ASSET_PROFILE -> assetProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case DEVICE -> deviceProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case DEVICE_PROFILE -> deviceProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case ENTITY_VIEW -> entityViewProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case DASHBOARD -> dashboardProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case RULE_CHAIN -> ruleChainProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case USER -> userProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case CUSTOMER -> customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg); |
|
||||
case OTA_PACKAGE -> otaPackageProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case WIDGETS_BUNDLE -> widgetBundleProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case WIDGET_TYPE -> widgetTypeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case QUEUE -> queueProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case ALARM -> alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg); |
|
||||
case ALARM_COMMENT -> alarmProcessor.processAlarmCommentNotification(tenantId, edgeNotificationMsg); |
|
||||
case RELATION -> relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg); |
|
||||
case TENANT -> tenantEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case TENANT_PROFILE -> tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case NOTIFICATION_RULE, NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE -> |
|
||||
notificationEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case TB_RESOURCE -> resourceEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); |
|
||||
case OAUTH2 -> oAuth2EdgeProcessor.processOAuth2Notification(tenantId, edgeNotificationMsg); |
|
||||
default -> log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type); |
|
||||
} |
|
||||
} catch (Exception e) { |
|
||||
callBackFailure(tenantId, edgeNotificationMsg, callback, e); |
|
||||
} |
|
||||
}); |
|
||||
callback.onSuccess(); |
|
||||
} catch (Exception e) { |
|
||||
callBackFailure(tenantId, edgeNotificationMsg, callback, e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void callBackFailure(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) { |
|
||||
log.error("[{}] Can't push to edge updates, edgeNotificationMsg [{}]", tenantId, edgeNotificationMsg, throwable); |
|
||||
callback.onFailure(throwable); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,80 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2024 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; |
||||
|
|
||||
|
import jakarta.annotation.PostConstruct; |
||||
|
import jakarta.annotation.PreDestroy; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.transaction.event.TransactionalEventListener; |
||||
|
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
||||
|
import org.thingsboard.server.dao.edge.RelatedEdgesService; |
||||
|
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; |
||||
|
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; |
||||
|
|
||||
|
import java.util.concurrent.ExecutorService; |
||||
|
import java.util.concurrent.Executors; |
||||
|
|
||||
|
@Component |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class RelatedEdgesSourcingListener { |
||||
|
|
||||
|
private final RelatedEdgesService relatedEdgesService; |
||||
|
|
||||
|
private ExecutorService executorService; |
||||
|
|
||||
|
@PostConstruct |
||||
|
public void init() { |
||||
|
log.debug("RelatedEdgesSourcingListener initiated"); |
||||
|
executorService = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("related-edges-listener")); |
||||
|
} |
||||
|
|
||||
|
@PreDestroy |
||||
|
public void destroy() { |
||||
|
log.debug("RelatedEdgesSourcingListener destroy"); |
||||
|
if (executorService != null && !executorService.isShutdown()) { |
||||
|
executorService.shutdown(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@TransactionalEventListener(fallbackExecution = true) |
||||
|
public void handleEvent(ActionEntityEvent<?> event) { |
||||
|
executorService.submit(() -> { |
||||
|
try { |
||||
|
switch (event.getActionType()) { |
||||
|
case ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE -> |
||||
|
relatedEdgesService.publishRelatedEdgeIdsEvictEvent(event.getTenantId(), event.getEntityId()); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("[{}] failed to process ActionEntityEvent: {}", event.getTenantId(), event, e); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@TransactionalEventListener(fallbackExecution = true) |
||||
|
public void handleEvent(DeleteEntityEvent<?> event) { |
||||
|
executorService.submit(() -> { |
||||
|
try { |
||||
|
relatedEdgesService.publishRelatedEdgeIdsEvictEvent(event.getTenantId(), event.getEntityId()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("[{}] failed to process DeleteEntityEvent: {}", event.getTenantId(), event, e); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||