396 changed files with 9839 additions and 1432 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,26 @@ |
|||
-- |
|||
-- 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. |
|||
-- |
|||
|
|||
UPDATE rule_node SET |
|||
configuration = (configuration::jsonb || jsonb_build_object( |
|||
'notifyDevice', |
|||
CASE WHEN configuration::jsonb ->> 'notifyDevice' = 'false' THEN false ELSE true END, |
|||
'sendAttributesUpdatedNotification', |
|||
CASE WHEN configuration::jsonb ->> 'sendAttributesUpdatedNotification' = 'true' THEN true ELSE false END, |
|||
'updateAttributesOnlyOnValueChange', |
|||
CASE WHEN configuration::jsonb ->> 'updateAttributesOnlyOnValueChange' = 'false' THEN false ELSE true END)::jsonb)::varchar, |
|||
configuration_version = 2 |
|||
WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' AND configuration_version = 1; |
|||
@ -0,0 +1,37 @@ |
|||
-- |
|||
-- 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. |
|||
-- |
|||
|
|||
-- RESOURCES UPDATE START |
|||
|
|||
DO |
|||
$$ |
|||
BEGIN |
|||
IF NOT EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'resource' AND column_name = 'data' AND data_type = 'bytea') THEN |
|||
ALTER TABLE resource RENAME COLUMN data TO base64_data; |
|||
ALTER TABLE resource ADD COLUMN data bytea; |
|||
UPDATE resource SET data = decode(base64_data, 'base64') WHERE base64_data IS NOT NULL; |
|||
ALTER TABLE resource DROP COLUMN base64_data; |
|||
END IF; |
|||
END; |
|||
$$; |
|||
|
|||
ALTER TABLE resource ADD COLUMN IF NOT EXISTS descriptor varchar; |
|||
ALTER TABLE resource ADD COLUMN IF NOT EXISTS preview bytea; |
|||
ALTER TABLE resource ADD COLUMN IF NOT EXISTS external_id uuid; |
|||
|
|||
CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag); |
|||
|
|||
-- RESOURCES UPDATE END |
|||
@ -0,0 +1,283 @@ |
|||
/** |
|||
* 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.controller; |
|||
|
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import io.swagger.annotations.ApiParam; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.core.io.ByteArrayResource; |
|||
import org.springframework.http.CacheControl; |
|||
import org.springframework.http.HttpHeaders; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
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.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.RequestHeader; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.RequestPart; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
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.TbImageDeleteResult; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
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.common.data.security.Authority; |
|||
import org.thingsboard.server.dao.resource.ImageService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.dao.resource.ImageCacheKey; |
|||
import org.thingsboard.server.service.resource.TbImageService; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
import org.thingsboard.server.service.security.permission.Operation; |
|||
import org.thingsboard.server.service.security.permission.Resource; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
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.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_TEXT_SEARCH_DESCRIPTION; |
|||
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; |
|||
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; |
|||
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; |
|||
|
|||
@Slf4j |
|||
@RestController |
|||
@TbCoreComponent |
|||
@RequiredArgsConstructor |
|||
public class ImageController extends BaseController { |
|||
|
|||
private final ImageService imageService; |
|||
private final TbImageService tbImageService; |
|||
@Value("${cache.image.systemImagesBrowserTtlInMinutes:0}") |
|||
private int systemImagesBrowserTtlInMinutes; |
|||
@Value("${cache.image.tenantImagesBrowserTtlInMinutes:0}") |
|||
private int tenantImagesBrowserTtlInMinutes; |
|||
|
|||
private static final String IMAGE_URL = "/api/images/{type}/{key}"; |
|||
private static final String SYSTEM_IMAGE = "system"; |
|||
private static final String TENANT_IMAGE = "tenant"; |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@PostMapping("/api/image") |
|||
public TbResourceInfo uploadImage(@RequestPart MultipartFile file, |
|||
@RequestPart(required = false) String title) 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(file.getOriginalFilename()); |
|||
if (StringUtils.isNotEmpty(title)) { |
|||
image.setTitle(title); |
|||
} else { |
|||
image.setTitle(file.getOriginalFilename()); |
|||
} |
|||
image.setResourceType(ResourceType.IMAGE); |
|||
ImageDescriptor descriptor = new ImageDescriptor(); |
|||
descriptor.setMediaType(file.getContentType()); |
|||
image.setDescriptorValue(descriptor); |
|||
image.setData(file.getBytes()); |
|||
return tbImageService.save(image, user); |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@PutMapping(IMAGE_URL) |
|||
public TbResourceInfo updateImage(@PathVariable String type, |
|||
@PathVariable String key, |
|||
@RequestPart MultipartFile file) throws Exception { |
|||
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); |
|||
TbResource image = new TbResource(imageInfo); |
|||
image.setData(file.getBytes()); |
|||
image.setFileName(file.getOriginalFilename()); |
|||
image.updateDescriptor(ImageDescriptor.class, descriptor -> { |
|||
descriptor.setMediaType(file.getContentType()); |
|||
return descriptor; |
|||
}); |
|||
return tbImageService.save(image, getCurrentUser()); |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@PutMapping(IMAGE_URL + "/info") |
|||
public TbResourceInfo updateImageInfo(@PathVariable String type, |
|||
@PathVariable String key, |
|||
@RequestBody TbResourceInfo newImageInfo) throws ThingsboardException { |
|||
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); |
|||
imageInfo.setTitle(newImageInfo.getTitle()); |
|||
return tbImageService.save(imageInfo, getCurrentUser()); |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
|||
@GetMapping(value = IMAGE_URL, produces = "image/*") |
|||
public ResponseEntity<ByteArrayResource> downloadImage(@PathVariable String type, |
|||
@PathVariable String key, |
|||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { |
|||
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')") |
|||
@GetMapping(value = IMAGE_URL + "/preview", produces = "image/png") |
|||
public ResponseEntity<ByteArrayResource> downloadImagePreview(@PathVariable String type, |
|||
@PathVariable String key, |
|||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { |
|||
return downloadIfChanged(type, key, etag, true); |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@GetMapping(IMAGE_URL + "/info") |
|||
public TbResourceInfo getImageInfo(@PathVariable String type, |
|||
@PathVariable String key) throws ThingsboardException { |
|||
return checkImageInfo(type, key, Operation.READ); |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@GetMapping("/api/images") |
|||
public PageData<TbResourceInfo> getImages(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) |
|||
@RequestParam int pageSize, |
|||
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) |
|||
@RequestParam int page, |
|||
@ApiParam(value = RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION) |
|||
@RequestParam(required = false) boolean includeSystemImages, |
|||
@ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION) |
|||
@RequestParam(required = false) String textSearch, |
|||
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES) |
|||
@RequestParam(required = false) String sortProperty, |
|||
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) |
|||
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
|||
// PE: generic permission
|
|||
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
|||
TenantId tenantId = getTenantId(); |
|||
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN || !includeSystemImages) { |
|||
return checkNotNull(imageService.getImagesByTenantId(tenantId, pageLink)); |
|||
} else { |
|||
return checkNotNull(imageService.getAllImagesByTenantId(tenantId, pageLink)); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@DeleteMapping(IMAGE_URL) |
|||
public ResponseEntity<TbImageDeleteResult> deleteImage(@PathVariable String type, |
|||
@PathVariable String key, |
|||
@RequestParam(name = "force", required = false) boolean force) throws ThingsboardException { |
|||
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE); |
|||
TbImageDeleteResult result = tbImageService.delete(imageInfo, getCurrentUser(), force); |
|||
return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result); |
|||
} |
|||
|
|||
private ResponseEntity<ByteArrayResource> downloadIfChanged(String type, String key, String etag, boolean preview) throws ThingsboardException, JsonProcessingException { |
|||
ImageCacheKey cacheKey = new ImageCacheKey(getTenantId(type), key, preview); |
|||
if (StringUtils.isNotEmpty(etag)) { |
|||
etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification
|
|||
if (etag.equals(tbImageService.getETag(cacheKey))) { |
|||
return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); |
|||
} |
|||
} |
|||
TenantId tenantId = getTenantId(); |
|||
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); |
|||
String fileName = imageInfo.getFileName(); |
|||
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); |
|||
byte[] data; |
|||
if (preview) { |
|||
descriptor = descriptor.getPreviewDescriptor(); |
|||
data = imageService.getImagePreview(tenantId, imageInfo.getId()); |
|||
} else { |
|||
data = imageService.getImageData(tenantId, imageInfo.getId()); |
|||
} |
|||
tbImageService.putETag(cacheKey, descriptor.getEtag()); |
|||
var result = ResponseEntity.ok() |
|||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName) |
|||
.header("x-filename", fileName) |
|||
.header("Content-Type", descriptor.getMediaType()) |
|||
.contentLength(data.length) |
|||
.eTag(descriptor.getEtag()); |
|||
if (systemImagesBrowserTtlInMinutes > 0 && imageInfo.getTenantId().isSysTenantId()) { |
|||
result.cacheControl(CacheControl.maxAge(systemImagesBrowserTtlInMinutes, TimeUnit.MINUTES)); |
|||
} else if (tenantImagesBrowserTtlInMinutes > 0 && !imageInfo.getTenantId().isSysTenantId()) { |
|||
result.cacheControl(CacheControl.maxAge(tenantImagesBrowserTtlInMinutes, TimeUnit.MINUTES)); |
|||
} else { |
|||
result.cacheControl(CacheControl.noCache()); |
|||
} |
|||
return result.body(new ByteArrayResource(data)); |
|||
} |
|||
|
|||
private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException { |
|||
TenantId tenantId = getTenantId(imageType); |
|||
TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); |
|||
checkEntity(getCurrentUser(), checkNotNull(imageInfo), operation); |
|||
return imageInfo; |
|||
} |
|||
|
|||
private TenantId getTenantId(String imageType) throws ThingsboardException { |
|||
TenantId tenantId; |
|||
if (imageType.equals(TENANT_IMAGE)) { |
|||
tenantId = getTenantId(); |
|||
} else if (imageType.equals(SYSTEM_IMAGE)) { |
|||
tenantId = TenantId.SYS_TENANT_ID; |
|||
} else { |
|||
throw new IllegalArgumentException("Invalid image URL"); |
|||
} |
|||
return tenantId; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
/** |
|||
* 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.executors; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Lazy; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.common.util.ExecutorProvider; |
|||
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
|||
import org.thingsboard.server.queue.util.TbRuleEngineComponent; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
|
|||
@Lazy |
|||
@TbRuleEngineComponent |
|||
@Component |
|||
public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { |
|||
|
|||
@Value("${service.rule_engine.pubsub.executor_thread_pool_size}") |
|||
private Integer threadPoolSize; |
|||
|
|||
/** |
|||
* Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration |
|||
*/ |
|||
private static final int THREADS_PER_CPU = 5; |
|||
private ScheduledExecutorService executor; |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (threadPoolSize == null) { |
|||
threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); |
|||
} |
|||
executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-rule-nodes")); |
|||
} |
|||
|
|||
@Override |
|||
public ScheduledExecutorService getExecutor() { |
|||
return executor; |
|||
} |
|||
|
|||
@PreDestroy |
|||
private void destroy() { |
|||
if (executor != null) { |
|||
executor.shutdownNow(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
/** |
|||
* 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.install.update; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.HasImage; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageDataIterable; |
|||
import org.thingsboard.server.dao.Dao; |
|||
import org.thingsboard.server.dao.asset.AssetProfileDao; |
|||
import org.thingsboard.server.dao.dashboard.DashboardDao; |
|||
import org.thingsboard.server.dao.device.DeviceProfileDao; |
|||
import org.thingsboard.server.dao.resource.ImageService; |
|||
import org.thingsboard.server.dao.widget.WidgetTypeDao; |
|||
import org.thingsboard.server.dao.widget.WidgetsBundleDao; |
|||
|
|||
import java.util.function.BiFunction; |
|||
import java.util.function.Function; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
public class ImagesUpdater { |
|||
private final ImageService imageService; |
|||
private final WidgetsBundleDao widgetsBundleDao; |
|||
private final WidgetTypeDao widgetTypeDao; |
|||
private final DashboardDao dashboardDao; |
|||
private final DeviceProfileDao deviceProfileDao; |
|||
private final AssetProfileDao assetProfileDao; |
|||
|
|||
public void updateWidgetsBundlesImages() { |
|||
log.info("Updating widgets bundles images..."); |
|||
var widgetsBundles = new PageDataIterable<>(widgetsBundleDao::findAllWidgetsBundles, 128); |
|||
updateImages(widgetsBundles, "bundle", imageService::replaceBase64WithImageUrl, widgetsBundleDao); |
|||
} |
|||
|
|||
public void updateWidgetTypesImages() { |
|||
log.info("Updating widget types images..."); |
|||
var widgetTypesIds = new PageDataIterable<>(widgetTypeDao::findAllWidgetTypesIds, 1024); |
|||
updateImages(widgetTypesIds, "widget type", imageService::replaceBase64WithImageUrl, widgetTypeDao); |
|||
} |
|||
|
|||
public void updateDashboardsImages() { |
|||
log.info("Updating dashboards images..."); |
|||
var dashboardsIds = new PageDataIterable<>(dashboardDao::findAllIds, 1024); |
|||
updateImages(dashboardsIds, "dashboard", imageService::replaceBase64WithImageUrl, dashboardDao); |
|||
} |
|||
|
|||
public void createSystemImages(Dashboard defaultDashboard) { |
|||
defaultDashboard.setTenantId(TenantId.SYS_TENANT_ID); |
|||
boolean created = imageService.replaceBase64WithImageUrl(defaultDashboard); |
|||
if (created) { |
|||
log.debug("Created system images for default dashboard '{}'", defaultDashboard.getTitle()); |
|||
} |
|||
} |
|||
|
|||
public void updateDeviceProfilesImages() { |
|||
log.info("Updating device profiles images..."); |
|||
var deviceProfiles = new PageDataIterable<>(deviceProfileDao::findAllWithImages, 256); |
|||
updateImages(deviceProfiles, "device profile", imageService::replaceBase64WithImageUrl, deviceProfileDao); |
|||
} |
|||
|
|||
public void updateAssetProfilesImages() { |
|||
log.info("Updating asset profiles images..."); |
|||
var assetProfiles = new PageDataIterable<>(assetProfileDao::findAllWithImages, 256); |
|||
updateImages(assetProfiles, "asset profile", imageService::replaceBase64WithImageUrl, assetProfileDao); |
|||
} |
|||
|
|||
private <E extends HasImage> void updateImages(Iterable<E> entities, String type, |
|||
BiFunction<E, String, Boolean> updater, Dao<E> dao) { |
|||
int updatedCount = 0; |
|||
int totalCount = 0; |
|||
for (E entity : entities) { |
|||
totalCount++; |
|||
try { |
|||
boolean updated = updater.apply(entity, type); |
|||
if (updated) { |
|||
dao.save(entity.getTenantId(), entity); |
|||
log.debug("[{}][{}] Updated {} images", entity.getTenantId(), entity.getName(), type); |
|||
updatedCount++; |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("[{}][{}] Failed to update {} images", entity.getTenantId(), entity.getName(), type, e); |
|||
} |
|||
if (totalCount % 100 == 0) { |
|||
log.info("Processed {} {}s so far", totalCount, type); |
|||
} |
|||
} |
|||
log.info("Updated {} {}s out of {}", updatedCount, type, totalCount); |
|||
} |
|||
|
|||
private <E extends HasImage> void updateImages(Iterable<? extends EntityId> entitiesIds, String type, |
|||
Function<E, Boolean> updater, Dao<E> dao) { |
|||
int updatedCount = 0; |
|||
int totalCount = 0; |
|||
for (EntityId id : entitiesIds) { |
|||
totalCount++; |
|||
E entity = dao.findById(TenantId.SYS_TENANT_ID, id.getId()); |
|||
try { |
|||
boolean updated = updater.apply(entity); |
|||
if (updated) { |
|||
dao.save(entity.getTenantId(), entity); |
|||
log.debug("[{}][{}] Updated {} images", entity.getTenantId(), entity.getName(), type); |
|||
updatedCount++; |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("[{}][{}] Failed to update {} images", entity.getTenantId(), entity.getName(), type, e); |
|||
} |
|||
if (totalCount % 100 == 0) { |
|||
log.info("Processed {} {}s so far", totalCount, type); |
|||
} |
|||
} |
|||
log.info("Updated {} {}s out of {}", updatedCount, type, totalCount); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,155 @@ |
|||
/** |
|||
* 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.resource; |
|||
|
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.github.benmanes.caffeine.cache.Cache; |
|||
import com.github.benmanes.caffeine.cache.Caffeine; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.cluster.TbClusterService; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.ImageDescriptor; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.TbImageDeleteResult; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.resource.ImageCacheKey; |
|||
import org.thingsboard.server.dao.resource.ImageService; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; |
|||
|
|||
import java.util.Optional; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
@TbCoreComponent |
|||
public class DefaultTbImageService extends AbstractTbEntityService implements TbImageService { |
|||
|
|||
private final TbClusterService clusterService; |
|||
private final ImageService imageService; |
|||
private final Cache<ImageCacheKey, String> etagCache; |
|||
|
|||
public DefaultTbImageService(TbClusterService clusterService, ImageService imageService, |
|||
@Value("${cache.image.etag.timeToLiveInMinutes:44640}") int cacheTtl, |
|||
@Value("${cache.image.etag.maxSize:10000}") int cacheMaxSize) { |
|||
this.clusterService = clusterService; |
|||
this.imageService = imageService; |
|||
this.etagCache = Caffeine.newBuilder() |
|||
.expireAfterAccess(cacheTtl, TimeUnit.MINUTES) |
|||
.maximumSize(cacheMaxSize) |
|||
.build(); |
|||
} |
|||
|
|||
@Override |
|||
public String getETag(ImageCacheKey imageCacheKey) { |
|||
return etagCache.getIfPresent(imageCacheKey); |
|||
} |
|||
|
|||
@Override |
|||
public void putETag(ImageCacheKey imageCacheKey, String etag) { |
|||
etagCache.put(imageCacheKey, etag); |
|||
} |
|||
|
|||
@Override |
|||
public void evictETag(ImageCacheKey imageCacheKey) { |
|||
etagCache.invalidate(imageCacheKey); |
|||
} |
|||
|
|||
@Override |
|||
public TbResourceInfo save(TbResource image, User user) throws Exception { |
|||
ActionType actionType = image.getId() == null ? ActionType.ADDED : ActionType.UPDATED; |
|||
TenantId tenantId = image.getTenantId(); |
|||
try { |
|||
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); |
|||
notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user); |
|||
if (oldEtag.isPresent()) { |
|||
var newEtag = getEtag(savedImage); |
|||
if (newEtag.isPresent() && !oldEtag.get().equals(newEtag.get())) { |
|||
evictETag(new ImageCacheKey(image.getTenantId(), image.getResourceKey(), false)); |
|||
evictETag(new ImageCacheKey(image.getTenantId(), image.getResourceKey(), true)); |
|||
clusterService.broadcastToCore(TransportProtos.ToCoreNotificationMsg.newBuilder() |
|||
.setResourceCacheInvalidateMsg(TransportProtos.ResourceCacheInvalidateMsg.newBuilder() |
|||
.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
|||
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) |
|||
.setResourceKey(image.getResourceKey()) |
|||
.build()) |
|||
.build()); |
|||
} |
|||
} |
|||
return savedImage; |
|||
} catch (Exception e) { |
|||
image.setData(null); |
|||
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), image, actionType, user, e); |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
private Optional<String> getEtag(TbResourceInfo image) throws JsonProcessingException { |
|||
var descriptor = image.getDescriptor(ImageDescriptor.class); |
|||
return Optional.ofNullable(descriptor != null ? descriptor.getEtag() : null); |
|||
} |
|||
|
|||
private Optional<String> getPreviewEtag(TbResourceInfo image) throws JsonProcessingException { |
|||
var descriptor = image.getDescriptor(ImageDescriptor.class); |
|||
descriptor = descriptor != null ? descriptor.getPreviewDescriptor() : null; |
|||
return Optional.ofNullable(descriptor != null ? descriptor.getEtag() : null); |
|||
} |
|||
|
|||
@Override |
|||
public TbResourceInfo save(TbResourceInfo imageInfo, User user) { |
|||
TenantId tenantId = imageInfo.getTenantId(); |
|||
TbResourceId imageId = imageInfo.getId(); |
|||
try { |
|||
imageInfo = imageService.saveImageInfo(imageInfo); |
|||
notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.UPDATED, user); |
|||
return imageInfo; |
|||
} catch (Exception e) { |
|||
notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.UPDATED, user, e); |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public TbImageDeleteResult delete(TbResourceInfo imageInfo, User user, boolean force) { |
|||
TenantId tenantId = imageInfo.getTenantId(); |
|||
TbResourceId imageId = imageInfo.getId(); |
|||
try { |
|||
TbImageDeleteResult result = imageService.deleteImage(imageInfo, force); |
|||
if (result.isSuccess()) { |
|||
notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.DELETED, user, imageId.toString()); |
|||
} |
|||
return result; |
|||
} catch (Exception e) { |
|||
notificationEntityService.logEntityAction(tenantId, imageId, ActionType.DELETED, user, e, imageId.toString()); |
|||
throw e; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
/** |
|||
* 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.resource; |
|||
|
|||
import org.thingsboard.server.common.data.TbImageDeleteResult; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.dao.resource.ImageCacheKey; |
|||
|
|||
public interface TbImageService { |
|||
|
|||
TbResourceInfo save(TbResource image, User user) throws Exception; |
|||
|
|||
TbResourceInfo save(TbResourceInfo imageInfo, User user); |
|||
|
|||
TbImageDeleteResult delete(TbResourceInfo imageInfo, User user, boolean force); |
|||
|
|||
String getETag(ImageCacheKey imageCacheKey); |
|||
|
|||
void putETag(ImageCacheKey imageCacheKey, String etag); |
|||
|
|||
void evictETag(ImageCacheKey imageCacheKey); |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* 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.sync.ie.exporting.impl; |
|||
|
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
|
|||
import java.util.Set; |
|||
|
|||
@Service |
|||
@TbCoreComponent |
|||
public class ResourceExportService extends BaseEntityExportService<TbResourceId, TbResource, EntityExportData<TbResource>> { |
|||
|
|||
@Override |
|||
public Set<EntityType> getSupportedEntityTypes() { |
|||
return Set.of(EntityType.TB_RESOURCE); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
/** |
|||
* 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.sync.ie.importing.impl; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
|||
import org.thingsboard.server.dao.resource.ResourceService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; |
|||
|
|||
@Service |
|||
@TbCoreComponent |
|||
@RequiredArgsConstructor |
|||
public class ResourceImportService extends BaseEntityImportService<TbResourceId, TbResource, EntityExportData<TbResource>> { |
|||
|
|||
private final ResourceService resourceService; |
|||
|
|||
@Override |
|||
protected void setOwner(TenantId tenantId, TbResource resource, IdProvider idProvider) { |
|||
resource.setTenantId(tenantId); |
|||
} |
|||
|
|||
@Override |
|||
protected TbResource prepare(EntitiesImportCtx ctx, TbResource resource, TbResource oldResource, EntityExportData<TbResource> exportData, IdProvider idProvider) { |
|||
return resource; |
|||
} |
|||
|
|||
@Override |
|||
protected TbResource findExistingEntity(EntitiesImportCtx ctx, TbResource resource, IdProvider idProvider) { |
|||
TbResource existingResource = super.findExistingEntity(ctx, resource, idProvider); |
|||
if (existingResource == null && ctx.isFindExistingByName()) { |
|||
existingResource = resourceService.findResourceByTenantIdAndKey(ctx.getTenantId(), resource.getResourceType(), resource.getResourceKey()); |
|||
} |
|||
return existingResource; |
|||
} |
|||
|
|||
@Override |
|||
protected boolean compare(EntitiesImportCtx ctx, EntityExportData<TbResource> exportData, TbResource prepared, TbResource existing) { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
protected TbResource deepCopy(TbResource resource) { |
|||
return new TbResource(resource); |
|||
} |
|||
|
|||
@Override |
|||
protected TbResource saveOrUpdate(EntitiesImportCtx ctx, TbResource resource, EntityExportData<TbResource> exportData, IdProvider idProvider) { |
|||
return resourceService.saveResource(resource); |
|||
} |
|||
|
|||
@Override |
|||
protected void onEntitySaved(User user, TbResource savedResource, TbResource oldResource) throws ThingsboardException { |
|||
super.onEntitySaved(user, savedResource, oldResource); |
|||
clusterService.onResourceChange(savedResource, null); |
|||
} |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.TB_RESOURCE; |
|||
} |
|||
|
|||
} |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,58 @@ |
|||
/** |
|||
* 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.dao.resource; |
|||
|
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.HasImage; |
|||
import org.thingsboard.server.common.data.TbImageDeleteResult; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.TbResourceInfo; |
|||
import org.thingsboard.server.common.data.id.TbResourceId; |
|||
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.common.data.widget.WidgetTypeDetails; |
|||
|
|||
public interface ImageService { |
|||
|
|||
TbResourceInfo saveImage(TbResource image); |
|||
|
|||
TbResourceInfo saveImageInfo(TbResourceInfo imageInfo); |
|||
|
|||
TbResourceInfo getImageInfoByTenantIdAndKey(TenantId tenantId, String key); |
|||
|
|||
PageData<TbResourceInfo> getImagesByTenantId(TenantId tenantId, PageLink pageLink); |
|||
|
|||
PageData<TbResourceInfo> getAllImagesByTenantId(TenantId tenantId, PageLink pageLink); |
|||
|
|||
byte[] getImageData(TenantId tenantId, TbResourceId imageId); |
|||
|
|||
byte[] getImagePreview(TenantId tenantId, TbResourceId imageId); |
|||
|
|||
TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force); |
|||
|
|||
TbResourceInfo findSystemOrTenantImageByEtag(TenantId tenantId, String etag); |
|||
|
|||
boolean replaceBase64WithImageUrl(HasImage entity, String type); |
|||
boolean replaceBase64WithImageUrl(Dashboard dashboard); |
|||
boolean replaceBase64WithImageUrl(WidgetTypeDetails widgetType); |
|||
|
|||
void inlineImage(HasImage entity); |
|||
|
|||
void inlineImages(Dashboard dashboard); |
|||
|
|||
void inlineImages(WidgetTypeDetails widgetTypeDetails); |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* 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; |
|||
|
|||
public interface HasImage extends HasTenantId, HasName { |
|||
|
|||
String getImage(); |
|||
|
|||
void setImage(String image); |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* 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 com.fasterxml.jackson.annotation.JsonInclude; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonInclude(JsonInclude.Include.NON_NULL) |
|||
public class ImageDescriptor { |
|||
private String mediaType; |
|||
private int width; |
|||
private int height; |
|||
private long size; |
|||
private String etag; |
|||
private ImageDescriptor previewDescriptor; |
|||
} |
|||
@ -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; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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 lombok.Builder; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.HasId; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
@Data |
|||
@Builder |
|||
public class TbImageDeleteResult { |
|||
|
|||
private boolean success; |
|||
private Map<String, List<? extends HasId<?>>> references; |
|||
|
|||
} |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,94 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.thingsboard</groupId> |
|||
<version>3.6.2-SNAPSHOT</version> |
|||
<artifactId>common</artifactId> |
|||
</parent> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<artifactId>proto</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>Thingsboard Server Common Protobuf and gRPC structures</name> |
|||
<url>https://thingsboard.io</url> |
|||
|
|||
<properties> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
<main.dir>${basedir}/../..</main.dir> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<artifactId>data</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.thingsboard.common</groupId> |
|||
<artifactId>message</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.google.protobuf</groupId> |
|||
<artifactId>protobuf-java</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.google.protobuf</groupId> |
|||
<artifactId>protobuf-java-util</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
<scope>provided</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.junit.vintage</groupId> |
|||
<artifactId>junit-vintage-engine</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.awaitility</groupId> |
|||
<artifactId>awaitility</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.xolstice.maven.plugins</groupId> |
|||
<artifactId>protobuf-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
<distributionManagement> |
|||
<repository> |
|||
<id>thingsboard-repo-deploy</id> |
|||
<name>ThingsBoard Repo Deployment</name> |
|||
<url>https://repo.thingsboard.io/artifactory/libs-release-public</url> |
|||
</repository> |
|||
</distributionManagement> |
|||
|
|||
</project> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue