Import Export of images. Search without system images
This commit is contained in:
parent
414d758429
commit
3322957630
@ -142,6 +142,8 @@ public class ControllerConstants {
|
|||||||
protected static final String RESOURCE_INFO_DESCRIPTION = "Resource Info is a lightweight object that includes main information about the Resource excluding the heavyweight data. ";
|
protected static final String RESOURCE_INFO_DESCRIPTION = "Resource Info is a lightweight object that includes main information about the Resource excluding the heavyweight data. ";
|
||||||
protected static final String RESOURCE_DESCRIPTION = "Resource is a heavyweight object that includes main information about the Resource and also data. ";
|
protected static final String RESOURCE_DESCRIPTION = "Resource is a heavyweight object that includes main information about the Resource and also data. ";
|
||||||
|
|
||||||
|
protected static final String RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION = "Use 'true' to include system images. Disabled by default. Ignored for requests by users with system administrator authority.";
|
||||||
|
|
||||||
protected static final String RESOURCE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the resource title.";
|
protected static final String RESOURCE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the resource title.";
|
||||||
protected static final String RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, resourceType, tenantId";
|
protected static final String RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, resourceType, tenantId";
|
||||||
protected static final String RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES = "LWM2M_MODEL, JKS, PKCS_12, JS_MODULE";
|
protected static final String RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES = "LWM2M_MODEL, JKS, PKCS_12, JS_MODULE";
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import org.springframework.http.HttpHeaders;
|
|||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@ -38,6 +39,7 @@ import org.springframework.web.bind.annotation.RequestPart;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.thingsboard.server.common.data.ImageDescriptor;
|
import org.thingsboard.server.common.data.ImageDescriptor;
|
||||||
|
import org.thingsboard.server.common.data.ImageExportData;
|
||||||
import org.thingsboard.server.common.data.ResourceType;
|
import org.thingsboard.server.common.data.ResourceType;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.TbResource;
|
import org.thingsboard.server.common.data.TbResource;
|
||||||
@ -60,6 +62,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
|
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.PAGE_SIZE_DESCRIPTION;
|
||||||
|
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES;
|
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION;
|
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
|
||||||
@ -140,6 +143,39 @@ public class ImageController extends BaseController {
|
|||||||
return downloadIfChanged(type, key, etag, false);
|
return downloadIfChanged(type, key, etag, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||||
|
@GetMapping(value = IMAGE_URL + "/export")
|
||||||
|
public ImageExportData exportImage(@PathVariable String type, @PathVariable String key) throws Exception {
|
||||||
|
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ);
|
||||||
|
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class);
|
||||||
|
byte[] data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId());
|
||||||
|
return new ImageExportData(descriptor.getMediaType(), imageInfo.getFileName(), imageInfo.getTitle(), imageInfo.getResourceKey(), Base64Utils.encodeToString(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||||
|
@PutMapping("/api/image/import")
|
||||||
|
public TbResourceInfo importImage(@RequestBody ImageExportData imageData) throws Exception {
|
||||||
|
SecurityUser user = getCurrentUser();
|
||||||
|
TbResource image = new TbResource();
|
||||||
|
image.setTenantId(user.getTenantId());
|
||||||
|
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image);
|
||||||
|
|
||||||
|
image.setFileName(imageData.getFileName());
|
||||||
|
if (StringUtils.isNotEmpty(imageData.getTitle())) {
|
||||||
|
image.setTitle(imageData.getTitle());
|
||||||
|
} else {
|
||||||
|
image.setTitle(imageData.getFileName());
|
||||||
|
}
|
||||||
|
image.setResourceKey(imageData.getResourceKey());
|
||||||
|
image.setResourceType(ResourceType.IMAGE);
|
||||||
|
ImageDescriptor descriptor = new ImageDescriptor();
|
||||||
|
descriptor.setMediaType(imageData.getMediaType());
|
||||||
|
image.setDescriptorValue(descriptor);
|
||||||
|
image.setData(Base64Utils.decodeFromString(imageData.getData()));
|
||||||
|
return tbImageService.save(image, user);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||||
@GetMapping(value = IMAGE_URL + "/preview", produces = "image/png")
|
@GetMapping(value = IMAGE_URL + "/preview", produces = "image/png")
|
||||||
public ResponseEntity<ByteArrayResource> downloadImagePreview(@PathVariable String type,
|
public ResponseEntity<ByteArrayResource> downloadImagePreview(@PathVariable String type,
|
||||||
@ -161,6 +197,8 @@ public class ImageController extends BaseController {
|
|||||||
@RequestParam int pageSize,
|
@RequestParam int pageSize,
|
||||||
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
|
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
|
||||||
@RequestParam int page,
|
@RequestParam int page,
|
||||||
|
@ApiParam(value = RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION)
|
||||||
|
@RequestParam(required = false) boolean includeSystemImages,
|
||||||
@ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION)
|
@ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION)
|
||||||
@RequestParam(required = false) String textSearch,
|
@RequestParam(required = false) String textSearch,
|
||||||
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES)
|
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES)
|
||||||
@ -170,7 +208,7 @@ public class ImageController extends BaseController {
|
|||||||
// PE: generic permission
|
// PE: generic permission
|
||||||
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
|
||||||
TenantId tenantId = getTenantId();
|
TenantId tenantId = getTenantId();
|
||||||
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
|
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN || includeSystemImages) {
|
||||||
return checkNotNull(imageService.getImagesByTenantId(tenantId, pageLink));
|
return checkNotNull(imageService.getImagesByTenantId(tenantId, pageLink));
|
||||||
} else {
|
} else {
|
||||||
return checkNotNull(imageService.getAllImagesByTenantId(tenantId, pageLink));
|
return checkNotNull(imageService.getAllImagesByTenantId(tenantId, pageLink));
|
||||||
|
|||||||
@ -80,6 +80,12 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb
|
|||||||
TenantId tenantId = image.getTenantId();
|
TenantId tenantId = image.getTenantId();
|
||||||
try {
|
try {
|
||||||
var oldEtag = getEtag(image);
|
var oldEtag = getEtag(image);
|
||||||
|
if (image.getId() == null && StringUtils.isNotEmpty(image.getResourceKey())) {
|
||||||
|
var existingImage = imageService.getImageInfoByTenantIdAndKey(tenantId, image.getResourceKey());
|
||||||
|
if (existingImage != null) {
|
||||||
|
image.setId(existingImage.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
TbResourceInfo savedImage = imageService.saveImage(image);
|
TbResourceInfo savedImage = imageService.saveImage(image);
|
||||||
notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user);
|
notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user);
|
||||||
if (oldEtag.isPresent()) {
|
if (oldEtag.isPresent()) {
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@ApiModel
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class ImageExportData {
|
||||||
|
|
||||||
|
private final String mediaType;
|
||||||
|
private final String fileName;
|
||||||
|
private final String title;
|
||||||
|
private final String resourceKey;
|
||||||
|
private final String data;
|
||||||
|
|
||||||
|
}
|
||||||
@ -109,7 +109,7 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
|
|||||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
public String getLink() {
|
public String getLink() {
|
||||||
if (resourceType == ResourceType.IMAGE) {
|
if (resourceType == ResourceType.IMAGE) {
|
||||||
return "/api/images/" + (tenantId.isSysTenantId() ? "system" : "tenant") + "/" + resourceKey;
|
return "/api/images/" + ((tenantId == null || !tenantId.isSysTenantId()) ? "tenant" : "system") + "/" + resourceKey; // tenantId is null in case of export to git
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,11 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.dao.resource;
|
package org.thingsboard.server.dao.resource;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.thingsboard.common.util.RegexUtils;
|
import org.thingsboard.common.util.RegexUtils;
|
||||||
@ -91,7 +89,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public TbResourceInfo saveImage(TbResource image) throws Exception {
|
public TbResourceInfo saveImage(TbResource image) throws Exception {
|
||||||
if (image.getId() == null) {
|
if (image.getId() == null && StringUtils.isEmpty(image.getResourceKey())) {
|
||||||
image.setResourceKey(getUniqueKey(image.getTenantId(), image.getFileName()));
|
image.setResourceKey(getUniqueKey(image.getTenantId(), image.getFileName()));
|
||||||
}
|
}
|
||||||
resourceValidator.validate(image, TbResourceInfo::getTenantId);
|
resourceValidator.validate(image, TbResourceInfo::getTenantId);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user