|
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 |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/** |
|||
* 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.springframework.http.converter.xml; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.fasterxml.jackson.dataformat.xml.XmlMapper; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; |
|||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; |
|||
import org.springframework.util.Assert; |
|||
|
|||
import java.lang.reflect.Type; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* RestTemplate firstly uses MappingJackson2XmlHttpMessageConverter converter instead of MappingJackson2HttpMessageConverter. |
|||
* It produces error UnsupportedMediaType, so this converter had to be shadowed for read and write operations to use the correct converter |
|||
*/ |
|||
public class MappingJackson2XmlHttpMessageConverter extends AbstractJackson2HttpMessageConverter { |
|||
private static final List<MediaType> problemDetailMediaTypes; |
|||
|
|||
public MappingJackson2XmlHttpMessageConverter() { |
|||
this(Jackson2ObjectMapperBuilder.xml().build()); |
|||
} |
|||
|
|||
public MappingJackson2XmlHttpMessageConverter(ObjectMapper objectMapper) { |
|||
super(objectMapper, new MediaType[]{new MediaType("application", "xml", StandardCharsets.UTF_8), new MediaType("text", "xml", StandardCharsets.UTF_8), new MediaType("application", "*+xml", StandardCharsets.UTF_8)}); |
|||
Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required"); |
|||
} |
|||
|
|||
public void setObjectMapper(ObjectMapper objectMapper) { |
|||
Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required"); |
|||
super.setObjectMapper(objectMapper); |
|||
} |
|||
|
|||
protected List<MediaType> getMediaTypesForProblemDetail() { |
|||
return problemDetailMediaTypes; |
|||
} |
|||
|
|||
static { |
|||
problemDetailMediaTypes = Collections.singletonList(MediaType.APPLICATION_PROBLEM_XML); |
|||
} |
|||
|
|||
@Override |
|||
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public boolean canWrite(Class<?> clazz, MediaType mediaType) { |
|||
return false; |
|||
} |
|||
} |
|||
@ -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()); |
|||
} |
|||
|
|||
} |
|||