committed by
GitHub
112 changed files with 4427 additions and 199 deletions
@ -0,0 +1,66 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2021 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. |
||||
|
-- |
||||
|
|
||||
|
CREATE TABLE IF NOT EXISTS resource ( |
||||
|
id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
tenant_id uuid NOT NULL, |
||||
|
title varchar(255) NOT NULL, |
||||
|
resource_type varchar(32) NOT NULL, |
||||
|
resource_key varchar(255) NOT NULL, |
||||
|
search_text varchar(255), |
||||
|
file_name varchar(255) NOT NULL, |
||||
|
data varchar, |
||||
|
CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) |
||||
|
); |
||||
|
|
||||
|
CREATE TABLE IF NOT EXISTS firmware ( |
||||
|
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
tenant_id uuid NOT NULL, |
||||
|
title varchar(255) NOT NULL, |
||||
|
version varchar(255) NOT NULL, |
||||
|
file_name varchar(255), |
||||
|
content_type varchar(255), |
||||
|
checksum_algorithm varchar(32), |
||||
|
checksum varchar(1020), |
||||
|
data bytea, |
||||
|
additional_info varchar, |
||||
|
search_text varchar(255), |
||||
|
CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) |
||||
|
); |
||||
|
|
||||
|
ALTER TABLE device_profile |
||||
|
ADD COLUMN IF NOT EXISTS firmware_id uuid; |
||||
|
|
||||
|
ALTER TABLE device |
||||
|
ADD COLUMN IF NOT EXISTS firmware_id uuid; |
||||
|
|
||||
|
DO $$ |
||||
|
BEGIN |
||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN |
||||
|
ALTER TABLE device_profile |
||||
|
ADD CONSTRAINT fk_firmware_device_profile |
||||
|
FOREIGN KEY (firmware_id) REFERENCES firmware(id); |
||||
|
END IF; |
||||
|
|
||||
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN |
||||
|
ALTER TABLE device |
||||
|
ADD CONSTRAINT fk_firmware_device |
||||
|
FOREIGN KEY (firmware_id) REFERENCES firmware(id); |
||||
|
END IF; |
||||
|
END; |
||||
|
$$; |
||||
@ -0,0 +1,195 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.google.common.hash.Hashing; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.core.io.ByteArrayResource; |
||||
|
import org.springframework.http.HttpHeaders; |
||||
|
import org.springframework.http.ResponseEntity; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
import java.nio.ByteBuffer; |
||||
|
|
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
public class FirmwareController extends BaseController { |
||||
|
|
||||
|
public static final String FIRMWARE_ID = "firmwareId"; |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmware/{firmwareId}/download", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public ResponseEntity<org.springframework.core.io.Resource> downloadFirmware(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { |
||||
|
checkParameter(FIRMWARE_ID, strFirmwareId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
||||
|
Firmware firmware = checkFirmwareId(firmwareId, Operation.READ); |
||||
|
|
||||
|
ByteArrayResource resource = new ByteArrayResource(firmware.getData().array()); |
||||
|
return ResponseEntity.ok() |
||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmware.getFileName()) |
||||
|
.header("x-filename", firmware.getFileName()) |
||||
|
.contentLength(resource.contentLength()) |
||||
|
.contentType(parseMediaType(firmware.getContentType())) |
||||
|
.body(resource); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { |
||||
|
checkParameter(FIRMWARE_ID, strFirmwareId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
||||
|
return checkFirmwareInfoId(firmwareId, Operation.READ); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public Firmware getFirmwareById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { |
||||
|
checkParameter(FIRMWARE_ID, strFirmwareId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
||||
|
return checkFirmwareId(firmwareId, Operation.READ); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmware", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException { |
||||
|
firmwareInfo.setTenantId(getTenantId()); |
||||
|
checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE); |
||||
|
try { |
||||
|
return firmwareService.saveFirmwareInfo(firmwareInfo); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Firmware saveFirmwareData(@PathVariable(FIRMWARE_ID) String strFirmwareId, |
||||
|
@RequestParam(required = false) String checksum, |
||||
|
@RequestParam(required = false) String checksumAlgorithm, |
||||
|
@RequestBody MultipartFile file) throws ThingsboardException { |
||||
|
checkParameter(FIRMWARE_ID, strFirmwareId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
||||
|
FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.READ); |
||||
|
|
||||
|
Firmware firmware = new Firmware(firmwareId); |
||||
|
firmware.setCreatedTime(info.getCreatedTime()); |
||||
|
firmware.setTenantId(getTenantId()); |
||||
|
firmware.setTitle(info.getTitle()); |
||||
|
firmware.setVersion(info.getVersion()); |
||||
|
firmware.setAdditionalInfo(info.getAdditionalInfo()); |
||||
|
|
||||
|
if (StringUtils.isEmpty(checksumAlgorithm)) { |
||||
|
checksumAlgorithm = "sha256"; |
||||
|
checksum = Hashing.sha256().hashBytes(file.getBytes()).toString(); |
||||
|
} |
||||
|
|
||||
|
firmware.setChecksumAlgorithm(checksumAlgorithm); |
||||
|
firmware.setChecksum(checksum); |
||||
|
firmware.setFileName(file.getOriginalFilename()); |
||||
|
firmware.setContentType(file.getContentType()); |
||||
|
firmware.setData(ByteBuffer.wrap(file.getBytes())); |
||||
|
return firmwareService.saveFirmware(firmware); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmwares", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(firmwareService.findTenantFirmwaresByTenantId(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData, |
||||
|
@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE) |
||||
|
@ResponseBody |
||||
|
public void deleteResource(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { |
||||
|
checkParameter(FIRMWARE_ID, strFirmwareId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); |
||||
|
checkFirmwareInfoId(firmwareId, Operation.DELETE); |
||||
|
firmwareService.deleteFirmware(getTenantId(), firmwareId); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,171 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.firmware; |
||||
|
|
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.LongDataEntry; |
||||
|
import org.thingsboard.server.common.data.kv.StringDataEntry; |
||||
|
import org.thingsboard.server.common.data.kv.TsKvEntry; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.device.DeviceProfileService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.firmware.FirmwareService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import javax.annotation.Nullable; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.function.Consumer; |
||||
|
|
||||
|
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM; |
||||
|
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM; |
||||
|
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE; |
||||
|
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE; |
||||
|
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
public class DefaultFirmwareStateService implements FirmwareStateService { |
||||
|
|
||||
|
private final FirmwareService firmwareService; |
||||
|
private final DeviceService deviceService; |
||||
|
private final DeviceProfileService deviceProfileService; |
||||
|
private final RuleEngineTelemetryService telemetryService; |
||||
|
|
||||
|
public DefaultFirmwareStateService(FirmwareService firmwareService, DeviceService deviceService, DeviceProfileService deviceProfileService, RuleEngineTelemetryService telemetryService) { |
||||
|
this.firmwareService = firmwareService; |
||||
|
this.deviceService = deviceService; |
||||
|
this.deviceProfileService = deviceProfileService; |
||||
|
this.telemetryService = telemetryService; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void update(Device device, boolean created) { |
||||
|
FirmwareId firmwareId = device.getFirmwareId(); |
||||
|
if (firmwareId == null) { |
||||
|
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); |
||||
|
firmwareId = deviceProfile.getFirmwareId(); |
||||
|
} |
||||
|
|
||||
|
if (firmwareId == null) { |
||||
|
if (!created) { |
||||
|
remove(device); |
||||
|
} |
||||
|
} else { |
||||
|
update(device, firmwareService.findFirmwareById(device.getTenantId(), firmwareId), System.currentTimeMillis()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void update(DeviceProfile deviceProfile) { |
||||
|
TenantId tenantId = deviceProfile.getTenantId(); |
||||
|
|
||||
|
Consumer<Device> updateConsumer; |
||||
|
if (deviceProfile.getFirmwareId() != null) { |
||||
|
Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId()); |
||||
|
long ts = System.currentTimeMillis(); |
||||
|
updateConsumer = d -> update(d, firmware, ts); |
||||
|
} else { |
||||
|
updateConsumer = this::remove; |
||||
|
} |
||||
|
|
||||
|
PageLink pageLink = new PageLink(100); |
||||
|
PageData<Device> pageData; |
||||
|
do { |
||||
|
//TODO: create a query which will return devices without firmware
|
||||
|
pageData = deviceService.findDevicesByTenantIdAndType(tenantId, deviceProfile.getName(), pageLink); |
||||
|
|
||||
|
pageData.getData().stream().filter(d -> d.getFirmwareId() == null).forEach(updateConsumer); |
||||
|
|
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
} |
||||
|
|
||||
|
private void update(Device device, Firmware firmware, long ts) { |
||||
|
TenantId tenantId = device.getTenantId(); |
||||
|
DeviceId deviceId = device.getId(); |
||||
|
|
||||
|
List<TsKvEntry> telemetry = new ArrayList<>(); |
||||
|
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle()))); |
||||
|
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); |
||||
|
|
||||
|
telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Void tmp) { |
||||
|
log.trace("[{}] Success save telemetry with target firmware for device!", deviceId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("[{}] Failed to save telemetry with target firmware for device!", deviceId, t); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
List<AttributeKvEntry> attributes = new ArrayList<>(); |
||||
|
|
||||
|
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle()))); |
||||
|
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion()))); |
||||
|
|
||||
|
attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, (long) firmware.getData().array().length))); |
||||
|
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm()))); |
||||
|
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum()))); |
||||
|
telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Void tmp) { |
||||
|
log.trace("[{}] Success save attributes with target firmware!", deviceId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("[{}] Failed to save attributes with target firmware!", deviceId, t); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private void remove(Device device) { |
||||
|
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, |
||||
|
Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM), |
||||
|
new FutureCallback<>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Void tmp) { |
||||
|
log.trace("[{}] Success remove target firmware attributes!", device.getId()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.firmware; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
|
||||
|
public interface FirmwareStateService { |
||||
|
|
||||
|
void update(Device device, boolean created); |
||||
|
|
||||
|
void update(DeviceProfile deviceProfile); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,302 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.type.TypeReference; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.springframework.mock.web.MockMultipartFile; |
||||
|
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; |
||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
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 java.nio.ByteBuffer; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { |
||||
|
|
||||
|
private IdComparator<FirmwareInfo> idComparator = new IdComparator<>(); |
||||
|
|
||||
|
public static final String TITLE = "My firmware"; |
||||
|
private static final String FILE_NAME = "filename.txt"; |
||||
|
private static final String VERSION = "v1.0"; |
||||
|
private static final String CONTENT_TYPE = "text/plain"; |
||||
|
private static final String CHECKSUM_ALGORITHM = "sha256"; |
||||
|
private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; |
||||
|
private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); |
||||
|
|
||||
|
private Tenant savedTenant; |
||||
|
private User tenantAdmin; |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
Tenant tenant = new Tenant(); |
||||
|
tenant.setTitle("My tenant"); |
||||
|
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
||||
|
Assert.assertNotNull(savedTenant); |
||||
|
|
||||
|
tenantAdmin = new User(); |
||||
|
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
||||
|
tenantAdmin.setTenantId(savedTenant.getId()); |
||||
|
tenantAdmin.setEmail("tenant2@thingsboard.org"); |
||||
|
tenantAdmin.setFirstName("Joe"); |
||||
|
tenantAdmin.setLastName("Downs"); |
||||
|
|
||||
|
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveFirmware() throws Exception { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
Assert.assertNotNull(savedFirmwareInfo); |
||||
|
Assert.assertNotNull(savedFirmwareInfo.getId()); |
||||
|
Assert.assertTrue(savedFirmwareInfo.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(savedTenant.getId(), savedFirmwareInfo.getTenantId()); |
||||
|
Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); |
||||
|
Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion()); |
||||
|
|
||||
|
savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); |
||||
|
|
||||
|
save(savedFirmwareInfo); |
||||
|
|
||||
|
FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); |
||||
|
Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveFirmwareData() throws Exception { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
Assert.assertNotNull(savedFirmwareInfo); |
||||
|
Assert.assertNotNull(savedFirmwareInfo.getId()); |
||||
|
Assert.assertTrue(savedFirmwareInfo.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(savedTenant.getId(), savedFirmwareInfo.getTenantId()); |
||||
|
Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); |
||||
|
Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion()); |
||||
|
|
||||
|
savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); |
||||
|
|
||||
|
save(savedFirmwareInfo); |
||||
|
|
||||
|
FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); |
||||
|
Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); |
||||
|
|
||||
|
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); |
||||
|
|
||||
|
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); |
||||
|
|
||||
|
Assert.assertEquals(FILE_NAME, savedFirmware.getFileName()); |
||||
|
Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testUpdateFirmwareFromDifferentTenant() throws Exception { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
loginDifferentTenant(); |
||||
|
doPost("/api/firmware", savedFirmwareInfo, FirmwareInfo.class, status().isForbidden()); |
||||
|
deleteDifferentTenant(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindFirmwareInfoById() throws Exception { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
FirmwareInfo foundFirmware = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); |
||||
|
Assert.assertNotNull(foundFirmware); |
||||
|
Assert.assertEquals(savedFirmwareInfo, foundFirmware); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindFirmwareById() throws Exception { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); |
||||
|
|
||||
|
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); |
||||
|
|
||||
|
Firmware foundFirmware = doGet("/api/firmware/" + savedFirmwareInfo.getId().getId().toString(), Firmware.class); |
||||
|
Assert.assertNotNull(foundFirmware); |
||||
|
Assert.assertEquals(savedFirmware, foundFirmware); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteFirmware() throws Exception { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
doDelete("/api/firmware/" + savedFirmwareInfo.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
|
||||
|
doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString()) |
||||
|
.andExpect(status().isNotFound()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantFirmwares() throws Exception { |
||||
|
List<FirmwareInfo> firmwares = new ArrayList<>(); |
||||
|
for (int i = 0; i < 165; i++) { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION + i); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
if (i > 100) { |
||||
|
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); |
||||
|
|
||||
|
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); |
||||
|
firmwares.add(new FirmwareInfo(savedFirmware)); |
||||
|
} else { |
||||
|
firmwares.add(savedFirmwareInfo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
List<FirmwareInfo> loadedFirmwares = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(24); |
||||
|
PageData<FirmwareInfo> pageData; |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/firmwares?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
loadedFirmwares.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(firmwares, idComparator); |
||||
|
Collections.sort(loadedFirmwares, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(firmwares, loadedFirmwares); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantFirmwaresByHasData() throws Exception { |
||||
|
List<FirmwareInfo> firmwaresWithData = new ArrayList<>(); |
||||
|
List<FirmwareInfo> firmwaresWithoutData = new ArrayList<>(); |
||||
|
|
||||
|
for (int i = 0; i < 165; i++) { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION + i); |
||||
|
|
||||
|
FirmwareInfo savedFirmwareInfo = save(firmwareInfo); |
||||
|
|
||||
|
if (i > 100) { |
||||
|
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); |
||||
|
|
||||
|
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); |
||||
|
firmwaresWithData.add(new FirmwareInfo(savedFirmware)); |
||||
|
} else { |
||||
|
firmwaresWithoutData.add(savedFirmwareInfo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
List<FirmwareInfo> loadedFirmwaresWithData = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(24); |
||||
|
PageData<FirmwareInfo> pageData; |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/firmwares/true?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
loadedFirmwaresWithData.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>(); |
||||
|
pageLink = new PageLink(24); |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/firmwares/false?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
loadedFirmwaresWithoutData.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(firmwaresWithData, idComparator); |
||||
|
Collections.sort(firmwaresWithoutData, idComparator); |
||||
|
Collections.sort(loadedFirmwaresWithData, idComparator); |
||||
|
Collections.sort(loadedFirmwaresWithoutData, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(firmwaresWithData, loadedFirmwaresWithData); |
||||
|
Assert.assertEquals(firmwaresWithoutData, loadedFirmwaresWithoutData); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private FirmwareInfo save(FirmwareInfo firmwareInfo) throws Exception { |
||||
|
return doPost("/api/firmware", firmwareInfo, FirmwareInfo.class); |
||||
|
} |
||||
|
|
||||
|
protected Firmware savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { |
||||
|
MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); |
||||
|
postRequest.file(content); |
||||
|
setJwtToken(postRequest); |
||||
|
return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), Firmware.class); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.sql; |
||||
|
|
||||
|
import org.thingsboard.server.controller.BaseFirmwareControllerTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class FirmwareControllerSqlTest extends BaseFirmwareControllerTest { |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
|
|
||||
|
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; |
||||
|
|
||||
|
public abstract class AbstractRedisFirmwareCache { |
||||
|
|
||||
|
protected final RedisConnectionFactory redisConnectionFactory; |
||||
|
|
||||
|
protected AbstractRedisFirmwareCache(RedisConnectionFactory redisConnectionFactory) { |
||||
|
this.redisConnectionFactory = redisConnectionFactory; |
||||
|
} |
||||
|
|
||||
|
protected byte[] toFirmwareCacheKey(String key) { |
||||
|
return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
||||
|
import org.springframework.cache.CacheManager; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; |
||||
|
|
||||
|
@Service |
||||
|
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='caffeine')") |
||||
|
public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { |
||||
|
|
||||
|
private final CacheManager cacheManager; |
||||
|
|
||||
|
public CaffeineFirmwareCacheReader(CacheManager cacheManager) { |
||||
|
this.cacheManager = cacheManager; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public byte[] get(String key) { |
||||
|
return get(key, 0, 0); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public byte[] get(String key, int chunkSize, int chunk) { |
||||
|
byte[] data = cacheManager.getCache(FIRMWARE_CACHE).get(key, byte[].class); |
||||
|
|
||||
|
if (chunkSize < 1) { |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
if (data != null && data.length > 0) { |
||||
|
int startIndex = chunkSize * chunk; |
||||
|
|
||||
|
int size = Math.min(data.length - startIndex, chunkSize); |
||||
|
|
||||
|
if (startIndex < data.length && size > 0) { |
||||
|
byte[] result = new byte[size]; |
||||
|
System.arraycopy(data, startIndex, result, 0, size); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
return new byte[0]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
||||
|
import org.springframework.cache.CacheManager; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; |
||||
|
|
||||
|
@Service |
||||
|
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='caffeine')") |
||||
|
public class CaffeineFirmwareCacheWriter implements FirmwareCacheWriter { |
||||
|
|
||||
|
private final CacheManager cacheManager; |
||||
|
|
||||
|
public CaffeineFirmwareCacheWriter(CacheManager cacheManager) { |
||||
|
this.cacheManager = cacheManager; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void put(String key, byte[] value) { |
||||
|
cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
public interface FirmwareCacheReader { |
||||
|
byte[] get(String key); |
||||
|
|
||||
|
byte[] get(String key, int chunkSize, int chunk); |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
public interface FirmwareCacheWriter { |
||||
|
void put(String key, byte[] value); |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
||||
|
import org.springframework.data.redis.connection.RedisConnection; |
||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && '${cache.type:null}'=='redis'") |
||||
|
public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache implements FirmwareCacheReader { |
||||
|
|
||||
|
public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) { |
||||
|
super(redisConnectionFactory); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public byte[] get(String key) { |
||||
|
return get(key, 0, 0); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public byte[] get(String key, int chunkSize, int chunk) { |
||||
|
try (RedisConnection connection = redisConnectionFactory.getConnection()) { |
||||
|
if (chunkSize == 0) { |
||||
|
return connection.get(toFirmwareCacheKey(key)); |
||||
|
} |
||||
|
|
||||
|
int startIndex = chunkSize * chunk; |
||||
|
int endIndex = startIndex + chunkSize - 1; |
||||
|
return connection.getRange(toFirmwareCacheKey(key), startIndex, endIndex); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.cache.firmware; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
||||
|
import org.springframework.data.redis.connection.RedisConnection; |
||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='core') && '${cache.type:null}'=='redis'") |
||||
|
public class RedisFirmwareCacheWriter extends AbstractRedisFirmwareCache implements FirmwareCacheWriter { |
||||
|
|
||||
|
public RedisFirmwareCacheWriter(RedisConnectionFactory redisConnectionFactory) { |
||||
|
super(redisConnectionFactory); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void put(String key, byte[] value) { |
||||
|
try (RedisConnection connection = redisConnectionFactory.getConnection()) { |
||||
|
connection.set(toFirmwareCacheKey(key), value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.firmware; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
|
||||
|
public interface FirmwareService { |
||||
|
|
||||
|
FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo); |
||||
|
|
||||
|
Firmware saveFirmware(Firmware firmware); |
||||
|
|
||||
|
Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId); |
||||
|
|
||||
|
FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId); |
||||
|
|
||||
|
PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); |
||||
|
|
||||
|
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); |
||||
|
|
||||
|
void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); |
||||
|
|
||||
|
void deleteFirmwaresByTenantId(TenantId tenantId); |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
|
||||
|
import java.nio.ByteBuffer; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class Firmware extends FirmwareInfo { |
||||
|
|
||||
|
private static final long serialVersionUID = 3091601761339422546L; |
||||
|
|
||||
|
private String fileName; |
||||
|
|
||||
|
private String contentType; |
||||
|
|
||||
|
private String checksumAlgorithm; |
||||
|
|
||||
|
private String checksum; |
||||
|
|
||||
|
private transient ByteBuffer data; |
||||
|
|
||||
|
public Firmware() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public Firmware(FirmwareId id) { |
||||
|
super(id); |
||||
|
} |
||||
|
|
||||
|
public Firmware(Firmware firmware) { |
||||
|
super(firmware); |
||||
|
this.fileName = firmware.getFileName(); |
||||
|
this.contentType = firmware.getContentType(); |
||||
|
this.data = firmware.getData(); |
||||
|
this.checksumAlgorithm = firmware.getChecksumAlgorithm(); |
||||
|
this.checksum = firmware.getChecksum(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasTenantId { |
||||
|
|
||||
|
private static final long serialVersionUID = 3168391583570815419L; |
||||
|
|
||||
|
private TenantId tenantId; |
||||
|
private String title; |
||||
|
private String version; |
||||
|
private boolean hasData; |
||||
|
|
||||
|
public FirmwareInfo() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public FirmwareInfo(FirmwareId id) { |
||||
|
super(id); |
||||
|
} |
||||
|
|
||||
|
public FirmwareInfo(FirmwareInfo firmwareInfo) { |
||||
|
super(firmwareInfo); |
||||
|
this.tenantId = firmwareInfo.getTenantId(); |
||||
|
this.title = firmwareInfo.getTitle(); |
||||
|
this.version = firmwareInfo.getVersion(); |
||||
|
this.hasData = firmwareInfo.isHasData(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getSearchText() { |
||||
|
return title; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.id; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public class FirmwareId extends UUIDBased implements EntityId { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@JsonCreator |
||||
|
public FirmwareId(@JsonProperty("id") UUID id) { |
||||
|
super(id); |
||||
|
} |
||||
|
|
||||
|
public static FirmwareId fromString(String firmwareId) { |
||||
|
return new FirmwareId(UUID.fromString(firmwareId)); |
||||
|
} |
||||
|
|
||||
|
@JsonIgnore |
||||
|
@Override |
||||
|
public EntityType getEntityType() { |
||||
|
return EntityType.FIRMWARE; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,317 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.firmware; |
||||
|
|
||||
|
import com.google.common.hash.HashFunction; |
||||
|
import com.google.common.hash.Hashing; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.hibernate.exception.ConstraintViolationException; |
||||
|
import org.springframework.cache.Cache; |
||||
|
import org.springframework.cache.CacheManager; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.exception.DataValidationException; |
||||
|
import org.thingsboard.server.dao.service.DataValidator; |
||||
|
import org.thingsboard.server.dao.service.PaginatedRemover; |
||||
|
import org.thingsboard.server.dao.tenant.TenantDao; |
||||
|
|
||||
|
import java.nio.ByteBuffer; |
||||
|
import java.util.Collections; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; |
||||
|
import static org.thingsboard.server.dao.service.Validator.validateId; |
||||
|
import static org.thingsboard.server.dao.service.Validator.validatePageLink; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class BaseFirmwareService implements FirmwareService { |
||||
|
public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId "; |
||||
|
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; |
||||
|
|
||||
|
private final TenantDao tenantDao; |
||||
|
private final FirmwareDao firmwareDao; |
||||
|
private final FirmwareInfoDao firmwareInfoDao; |
||||
|
private final CacheManager cacheManager; |
||||
|
|
||||
|
public BaseFirmwareService(TenantDao tenantDao, FirmwareDao firmwareDao, FirmwareInfoDao firmwareInfoDao, CacheManager cacheManager) { |
||||
|
this.tenantDao = tenantDao; |
||||
|
this.firmwareDao = firmwareDao; |
||||
|
this.firmwareInfoDao = firmwareInfoDao; |
||||
|
this.cacheManager = cacheManager; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) { |
||||
|
log.trace("Executing saveFirmwareInfo [{}]", firmwareInfo); |
||||
|
firmwareInfoValidator.validate(firmwareInfo, FirmwareInfo::getTenantId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = firmwareInfo.getId(); |
||||
|
if (firmwareId != null) { |
||||
|
cacheManager.getCache(FIRMWARE_CACHE).evict(firmwareId.toString()); |
||||
|
} |
||||
|
return firmwareInfoDao.save(firmwareInfo.getTenantId(), firmwareInfo); |
||||
|
} catch (Exception t) { |
||||
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
||||
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) { |
||||
|
throw new DataValidationException("Firmware with such title and version already exists!"); |
||||
|
} else { |
||||
|
throw t; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Firmware saveFirmware(Firmware firmware) { |
||||
|
log.trace("Executing saveFirmware [{}]", firmware); |
||||
|
firmwareValidator.validate(firmware, FirmwareInfo::getTenantId); |
||||
|
try { |
||||
|
FirmwareId firmwareId = firmware.getId(); |
||||
|
if (firmwareId != null) { |
||||
|
cacheManager.getCache(FIRMWARE_CACHE).evict(firmwareId.toString()); |
||||
|
} |
||||
|
return firmwareDao.save(firmware.getTenantId(), firmware); |
||||
|
} catch (Exception t) { |
||||
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
||||
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) { |
||||
|
throw new DataValidationException("Firmware with such title and version already exists!"); |
||||
|
} else { |
||||
|
throw t; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId) { |
||||
|
log.trace("Executing findFirmwareById [{}]", firmwareId); |
||||
|
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); |
||||
|
return firmwareDao.findById(tenantId, firmwareId.getId()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) { |
||||
|
log.trace("Executing findFirmwareInfoById [{}]", firmwareId); |
||||
|
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); |
||||
|
return firmwareInfoDao.findById(tenantId, firmwareId.getId()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { |
||||
|
log.trace("Executing findTenantFirmwaresByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
validatePageLink(pageLink); |
||||
|
return firmwareInfoDao.findFirmwareInfoByTenantId(tenantId, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { |
||||
|
log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
validatePageLink(pageLink); |
||||
|
return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) { |
||||
|
log.trace("Executing deleteFirmware [{}]", firmwareId); |
||||
|
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); |
||||
|
try { |
||||
|
Cache cache = cacheManager.getCache(FIRMWARE_CACHE); |
||||
|
cache.evict(Collections.singletonList(firmwareId)); |
||||
|
firmwareDao.removeById(tenantId, firmwareId.getId()); |
||||
|
} catch (Exception t) { |
||||
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
||||
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device")) { |
||||
|
throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); |
||||
|
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { |
||||
|
throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); |
||||
|
} else { |
||||
|
throw t; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deleteFirmwaresByTenantId(TenantId tenantId) { |
||||
|
log.trace("Executing deleteFirmwaresByTenantId, tenantId [{}]", tenantId); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
tenantFirmwareRemover.removeEntities(tenantId, tenantId); |
||||
|
} |
||||
|
|
||||
|
private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() { |
||||
|
|
||||
|
@Override |
||||
|
protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) { |
||||
|
if (firmware.getTenantId() == null) { |
||||
|
throw new DataValidationException("Firmware should be assigned to tenant!"); |
||||
|
} else { |
||||
|
Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId()); |
||||
|
if (tenant == null) { |
||||
|
throw new DataValidationException("Firmware is referencing to non-existent tenant!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getTitle())) { |
||||
|
throw new DataValidationException("Firmware title should be specified!"); |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getVersion())) { |
||||
|
throw new DataValidationException("Firmware version should be specified!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { |
||||
|
FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); |
||||
|
|
||||
|
if (!firmwareOld.getTitle().equals(firmware.getTitle())) { |
||||
|
throw new DataValidationException("Updating firmware title is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (!firmwareOld.getVersion().equals(firmware.getVersion())) { |
||||
|
throw new DataValidationException("Updating firmware version is prohibited!"); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
private DataValidator<Firmware> firmwareValidator = new DataValidator<>() { |
||||
|
|
||||
|
@Override |
||||
|
protected void validateDataImpl(TenantId tenantId, Firmware firmware) { |
||||
|
if (firmware.getTenantId() == null) { |
||||
|
throw new DataValidationException("Firmware should be assigned to tenant!"); |
||||
|
} else { |
||||
|
Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId()); |
||||
|
if (tenant == null) { |
||||
|
throw new DataValidationException("Firmware is referencing to non-existent tenant!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getTitle())) { |
||||
|
throw new DataValidationException("Firmware title should be specified!"); |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getVersion())) { |
||||
|
throw new DataValidationException("Firmware version should be specified!"); |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getFileName())) { |
||||
|
throw new DataValidationException("Firmware file name should be specified!"); |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getContentType())) { |
||||
|
throw new DataValidationException("Firmware content type should be specified!"); |
||||
|
} |
||||
|
|
||||
|
ByteBuffer data = firmware.getData(); |
||||
|
if (data == null || !data.hasArray() || data.array().length == 0) { |
||||
|
throw new DataValidationException("Firmware data should be specified!"); |
||||
|
} |
||||
|
|
||||
|
if (StringUtils.isEmpty(firmware.getChecksumAlgorithm())) { |
||||
|
throw new DataValidationException("Firmware checksum algorithm should be specified!"); |
||||
|
} |
||||
|
if (StringUtils.isEmpty(firmware.getChecksum())) { |
||||
|
throw new DataValidationException("Firmware checksum should be specified!"); |
||||
|
} |
||||
|
|
||||
|
HashFunction hashFunction; |
||||
|
switch (firmware.getChecksumAlgorithm()) { |
||||
|
case "sha256": |
||||
|
hashFunction = Hashing.sha256(); |
||||
|
break; |
||||
|
case "md5": |
||||
|
hashFunction = Hashing.md5(); |
||||
|
break; |
||||
|
case "crc32": |
||||
|
hashFunction = Hashing.crc32(); |
||||
|
break; |
||||
|
default: |
||||
|
throw new DataValidationException("Unknown checksum algorithm!"); |
||||
|
} |
||||
|
|
||||
|
String currentChecksum = hashFunction.hashBytes(data.array()).toString(); |
||||
|
|
||||
|
if (!currentChecksum.equals(firmware.getChecksum())) { |
||||
|
throw new DataValidationException("Wrong firmware file!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void validateUpdate(TenantId tenantId, Firmware firmware) { |
||||
|
Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); |
||||
|
|
||||
|
if (!firmwareOld.getTitle().equals(firmware.getTitle())) { |
||||
|
throw new DataValidationException("Updating firmware title is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (!firmwareOld.getVersion().equals(firmware.getVersion())) { |
||||
|
throw new DataValidationException("Updating firmware version is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (firmwareOld.getFileName() != null && !firmwareOld.getFileName().equals(firmware.getFileName())) { |
||||
|
throw new DataValidationException("Updating firmware file name is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (firmwareOld.getContentType() != null && !firmwareOld.getContentType().equals(firmware.getContentType())) { |
||||
|
throw new DataValidationException("Updating firmware content type is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (firmwareOld.getChecksumAlgorithm() != null && !firmwareOld.getChecksumAlgorithm().equals(firmware.getChecksumAlgorithm())) { |
||||
|
throw new DataValidationException("Updating firmware content type is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (firmwareOld.getChecksum() != null && !firmwareOld.getChecksum().equals(firmware.getChecksum())) { |
||||
|
throw new DataValidationException("Updating firmware content type is prohibited!"); |
||||
|
} |
||||
|
|
||||
|
if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { |
||||
|
throw new DataValidationException("Updating firmware data is prohibited!"); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover = |
||||
|
new PaginatedRemover<>() { |
||||
|
|
||||
|
@Override |
||||
|
protected PageData<FirmwareInfo> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { |
||||
|
return firmwareInfoDao.findFirmwareInfoByTenantId(id, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void removeEntity(TenantId tenantId, FirmwareInfo entity) { |
||||
|
deleteFirmware(tenantId, entity.getId()); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
protected Optional<ConstraintViolationException> extractConstraintViolationException(Exception t) { |
||||
|
if (t instanceof ConstraintViolationException) { |
||||
|
return Optional.of((ConstraintViolationException) t); |
||||
|
} else if (t.getCause() instanceof ConstraintViolationException) { |
||||
|
return Optional.of((ConstraintViolationException) (t.getCause())); |
||||
|
} else { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.firmware; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.dao.Dao; |
||||
|
|
||||
|
public interface FirmwareDao extends Dao<Firmware> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.firmware; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.Dao; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public interface FirmwareInfoDao extends Dao<FirmwareInfo> { |
||||
|
|
||||
|
PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); |
||||
|
|
||||
|
PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,132 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.model.sql; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.hibernate.annotations.Type; |
||||
|
import org.hibernate.annotations.TypeDef; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.model.BaseSqlEntity; |
||||
|
import org.thingsboard.server.dao.model.ModelConstants; |
||||
|
import org.thingsboard.server.dao.model.SearchTextEntity; |
||||
|
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.Table; |
||||
|
import java.nio.ByteBuffer; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Entity |
||||
|
@TypeDef(name = "json", typeClass = JsonStringType.class) |
||||
|
@Table(name = FIRMWARE_TABLE_NAME) |
||||
|
public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTextEntity<Firmware> { |
||||
|
|
||||
|
@Column(name = FIRMWARE_TENANT_ID_COLUMN) |
||||
|
private UUID tenantId; |
||||
|
|
||||
|
@Column(name = FIRMWARE_TITLE_COLUMN) |
||||
|
private String title; |
||||
|
|
||||
|
@Column(name = FIRMWARE_VERSION_COLUMN) |
||||
|
private String version; |
||||
|
|
||||
|
@Column(name = FIRMWARE_FILE_NAME_COLUMN) |
||||
|
private String fileName; |
||||
|
|
||||
|
@Column(name = FIRMWARE_CONTENT_TYPE_COLUMN) |
||||
|
private String contentType; |
||||
|
|
||||
|
@Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN) |
||||
|
private String checksumAlgorithm; |
||||
|
|
||||
|
@Column(name = FIRMWARE_CHECKSUM_COLUMN) |
||||
|
private String checksum; |
||||
|
|
||||
|
@Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY") |
||||
|
private byte[] data; |
||||
|
|
||||
|
@Type(type = "json") |
||||
|
@Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN) |
||||
|
private JsonNode additionalInfo; |
||||
|
|
||||
|
@Column(name = SEARCH_TEXT_PROPERTY) |
||||
|
private String searchText; |
||||
|
|
||||
|
public FirmwareEntity() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public FirmwareEntity(Firmware firmware) { |
||||
|
this.createdTime = firmware.getCreatedTime(); |
||||
|
this.setUuid(firmware.getUuidId()); |
||||
|
this.tenantId = firmware.getTenantId().getId(); |
||||
|
this.title = firmware.getTitle(); |
||||
|
this.version = firmware.getVersion(); |
||||
|
this.fileName = firmware.getFileName(); |
||||
|
this.contentType = firmware.getContentType(); |
||||
|
this.checksumAlgorithm = firmware.getChecksumAlgorithm(); |
||||
|
this.checksum = firmware.getChecksum(); |
||||
|
this.data = firmware.getData().array(); |
||||
|
this.additionalInfo = firmware.getAdditionalInfo(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getSearchTextSource() { |
||||
|
return title; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void setSearchText(String searchText) { |
||||
|
this.searchText = searchText; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Firmware toData() { |
||||
|
Firmware firmware = new Firmware(new FirmwareId(id)); |
||||
|
firmware.setCreatedTime(createdTime); |
||||
|
firmware.setTenantId(new TenantId(tenantId)); |
||||
|
firmware.setTitle(title); |
||||
|
firmware.setVersion(version); |
||||
|
firmware.setFileName(fileName); |
||||
|
firmware.setContentType(contentType); |
||||
|
firmware.setChecksumAlgorithm(checksumAlgorithm); |
||||
|
firmware.setChecksum(checksum); |
||||
|
if (data != null) { |
||||
|
firmware.setData(ByteBuffer.wrap(data)); |
||||
|
firmware.setHasData(true); |
||||
|
} |
||||
|
firmware.setAdditionalInfo(additionalInfo); |
||||
|
return firmware; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,116 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.model.sql; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.hibernate.annotations.Type; |
||||
|
import org.hibernate.annotations.TypeDef; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.id.FirmwareId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.model.BaseSqlEntity; |
||||
|
import org.thingsboard.server.dao.model.ModelConstants; |
||||
|
import org.thingsboard.server.dao.model.SearchTextEntity; |
||||
|
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.persistence.Transient; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; |
||||
|
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Entity |
||||
|
@TypeDef(name = "json", typeClass = JsonStringType.class) |
||||
|
@Table(name = FIRMWARE_TABLE_NAME) |
||||
|
public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements SearchTextEntity<FirmwareInfo> { |
||||
|
|
||||
|
@Column(name = FIRMWARE_TENANT_ID_COLUMN) |
||||
|
private UUID tenantId; |
||||
|
|
||||
|
@Column(name = FIRMWARE_TITLE_COLUMN) |
||||
|
private String title; |
||||
|
|
||||
|
@Column(name = FIRMWARE_VERSION_COLUMN) |
||||
|
private String version; |
||||
|
|
||||
|
@Type(type = "json") |
||||
|
@Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN) |
||||
|
private JsonNode additionalInfo; |
||||
|
|
||||
|
@Column(name = SEARCH_TEXT_PROPERTY) |
||||
|
private String searchText; |
||||
|
|
||||
|
// @Column(name = FIRMWARE_HAS_DATA_PROPERTY, insertable = false, updatable = false)
|
||||
|
@Transient |
||||
|
private boolean hasData; |
||||
|
|
||||
|
public FirmwareInfoEntity() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public FirmwareInfoEntity(FirmwareInfo firmware) { |
||||
|
this.createdTime = firmware.getCreatedTime(); |
||||
|
this.setUuid(firmware.getUuidId()); |
||||
|
this.tenantId = firmware.getTenantId().getId(); |
||||
|
this.title = firmware.getTitle(); |
||||
|
this.version = firmware.getVersion(); |
||||
|
this.additionalInfo = firmware.getAdditionalInfo(); |
||||
|
} |
||||
|
|
||||
|
public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, Object additionalInfo, boolean hasData) { |
||||
|
this.id = id; |
||||
|
this.createdTime = createdTime; |
||||
|
this.tenantId = tenantId; |
||||
|
this.title = title; |
||||
|
this.version = version; |
||||
|
this.hasData = hasData; |
||||
|
this.additionalInfo = JacksonUtil.convertValue(additionalInfo, JsonNode.class); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getSearchTextSource() { |
||||
|
return title; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void setSearchText(String searchText) { |
||||
|
this.searchText = searchText; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public FirmwareInfo toData() { |
||||
|
FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); |
||||
|
firmware.setCreatedTime(createdTime); |
||||
|
firmware.setTenantId(new TenantId(tenantId)); |
||||
|
firmware.setTitle(title); |
||||
|
firmware.setVersion(version); |
||||
|
firmware.setAdditionalInfo(additionalInfo); |
||||
|
firmware.setHasData(hasData); |
||||
|
return firmware; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.sql.firmware; |
||||
|
|
||||
|
import org.springframework.data.domain.Page; |
||||
|
import org.springframework.data.domain.Pageable; |
||||
|
import org.springframework.data.jpa.repository.Query; |
||||
|
import org.springframework.data.repository.CrudRepository; |
||||
|
import org.springframework.data.repository.query.Param; |
||||
|
import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> { |
||||
|
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + |
||||
|
"f.tenantId = :tenantId " + |
||||
|
"AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") |
||||
|
Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, |
||||
|
@Param("searchText") String searchText, |
||||
|
Pageable pageable); |
||||
|
|
||||
|
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + |
||||
|
"f.tenantId = :tenantId " + |
||||
|
"AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + |
||||
|
"AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") |
||||
|
Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId, |
||||
|
@Param("hasData") boolean hasData, |
||||
|
@Param("searchText") String searchText, |
||||
|
Pageable pageable); |
||||
|
|
||||
|
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") |
||||
|
FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.sql.firmware; |
||||
|
|
||||
|
import org.springframework.data.repository.CrudRepository; |
||||
|
import org.thingsboard.server.dao.model.sql.FirmwareEntity; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public interface FirmwareRepository extends CrudRepository<FirmwareEntity, UUID> { |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.sql.firmware; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.data.repository.CrudRepository; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.dao.firmware.FirmwareDao; |
||||
|
import org.thingsboard.server.dao.model.sql.FirmwareEntity; |
||||
|
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class JpaFirmwareDao extends JpaAbstractSearchTextDao<FirmwareEntity, Firmware> implements FirmwareDao { |
||||
|
|
||||
|
@Autowired |
||||
|
private FirmwareRepository firmwareRepository; |
||||
|
|
||||
|
@Override |
||||
|
protected Class<FirmwareEntity> getEntityClass() { |
||||
|
return FirmwareEntity.class; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected CrudRepository<FirmwareEntity, UUID> getCrudRepository() { |
||||
|
return firmwareRepository; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.sql.firmware; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.data.repository.CrudRepository; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.DaoUtil; |
||||
|
import org.thingsboard.server.dao.firmware.FirmwareInfoDao; |
||||
|
import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; |
||||
|
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
||||
|
|
||||
|
import java.util.Objects; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEntity, FirmwareInfo> implements FirmwareInfoDao { |
||||
|
|
||||
|
@Autowired |
||||
|
private FirmwareInfoRepository firmwareInfoRepository; |
||||
|
|
||||
|
@Override |
||||
|
protected Class<FirmwareInfoEntity> getEntityClass() { |
||||
|
return FirmwareInfoEntity.class; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected CrudRepository<FirmwareInfoEntity, UUID> getCrudRepository() { |
||||
|
return firmwareInfoRepository; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public FirmwareInfo findById(TenantId tenantId, UUID id) { |
||||
|
return DaoUtil.getData(firmwareInfoRepository.findFirmwareInfoById(id)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public FirmwareInfo save(TenantId tenantId, FirmwareInfo firmwareInfo) { |
||||
|
FirmwareInfo savedFirmware = super.save(tenantId, firmwareInfo); |
||||
|
if (firmwareInfo.getId() == null) { |
||||
|
return savedFirmware; |
||||
|
} else { |
||||
|
return findById(tenantId, savedFirmware.getId().getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink) { |
||||
|
return DaoUtil.toPageData(firmwareInfoRepository |
||||
|
.findAllByTenantId( |
||||
|
tenantId.getId(), |
||||
|
Objects.toString(pageLink.getTextSearch(), ""), |
||||
|
DaoUtil.toPageable(pageLink))); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { |
||||
|
return DaoUtil.toPageData(firmwareInfoRepository |
||||
|
.findAllByTenantIdAndHasData( |
||||
|
tenantId.getId(), |
||||
|
hasData, |
||||
|
Objects.toString(pageLink.getTextSearch(), ""), |
||||
|
DaoUtil.toPageable(pageLink))); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,481 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.service; |
||||
|
|
||||
|
import com.datastax.oss.driver.api.core.uuid.Uuids; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.Firmware; |
||||
|
import org.thingsboard.server.common.data.FirmwareInfo; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.exception.DataValidationException; |
||||
|
|
||||
|
import java.nio.ByteBuffer; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
||||
|
|
||||
|
public static final String TITLE = "My firmware"; |
||||
|
private static final String FILE_NAME = "filename.txt"; |
||||
|
private static final String VERSION = "v1.0"; |
||||
|
private static final String CONTENT_TYPE = "text/plain"; |
||||
|
private static final String CHECKSUM_ALGORITHM = "sha256"; |
||||
|
private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; |
||||
|
private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); |
||||
|
|
||||
|
private IdComparator<FirmwareInfo> idComparator = new IdComparator<>(); |
||||
|
|
||||
|
private TenantId tenantId; |
||||
|
|
||||
|
@Before |
||||
|
public void before() { |
||||
|
Tenant tenant = new Tenant(); |
||||
|
tenant.setTitle("My tenant"); |
||||
|
Tenant savedTenant = tenantService.saveTenant(tenant); |
||||
|
Assert.assertNotNull(savedTenant); |
||||
|
tenantId = savedTenant.getId(); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void after() { |
||||
|
tenantService.deleteTenant(tenantId); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveFirmware() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
Firmware savedFirmware = firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
Assert.assertNotNull(savedFirmware); |
||||
|
Assert.assertNotNull(savedFirmware.getId()); |
||||
|
Assert.assertTrue(savedFirmware.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(firmware.getTenantId(), savedFirmware.getTenantId()); |
||||
|
Assert.assertEquals(firmware.getTitle(), savedFirmware.getTitle()); |
||||
|
Assert.assertEquals(firmware.getFileName(), savedFirmware.getFileName()); |
||||
|
Assert.assertEquals(firmware.getContentType(), savedFirmware.getContentType()); |
||||
|
Assert.assertEquals(firmware.getData(), savedFirmware.getData()); |
||||
|
|
||||
|
savedFirmware.setAdditionalInfo(JacksonUtil.newObjectNode()); |
||||
|
firmwareService.saveFirmware(savedFirmware); |
||||
|
|
||||
|
Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); |
||||
|
Assert.assertEquals(foundFirmware.getTitle(), savedFirmware.getTitle()); |
||||
|
|
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveFirmwareInfoAndUpdateWithData() { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTenantId(tenantId); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); |
||||
|
|
||||
|
Assert.assertNotNull(savedFirmwareInfo); |
||||
|
Assert.assertNotNull(savedFirmwareInfo.getId()); |
||||
|
Assert.assertTrue(savedFirmwareInfo.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(firmwareInfo.getTenantId(), savedFirmwareInfo.getTenantId()); |
||||
|
Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); |
||||
|
|
||||
|
Firmware firmware = new Firmware(savedFirmwareInfo.getId()); |
||||
|
firmware.setCreatedTime(firmwareInfo.getCreatedTime()); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
|
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); |
||||
|
firmwareService.saveFirmwareInfo(savedFirmwareInfo); |
||||
|
|
||||
|
Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, firmware.getId()); |
||||
|
firmware.setAdditionalInfo(JacksonUtil.newObjectNode()); |
||||
|
|
||||
|
Assert.assertEquals(foundFirmware.getTitle(), firmware.getTitle()); |
||||
|
Assert.assertTrue(foundFirmware.isHasData()); |
||||
|
|
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithEmptyTenant() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithEmptyTitle() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithEmptyFileName() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithEmptyContentType() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithEmptyData() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithInvalidTenant() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(new TenantId(Uuids.timeBased())); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithEmptyChecksum() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(new TenantId(Uuids.timeBased())); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareInfoWithExistingTitleAndVersion() { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTenantId(tenantId); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION); |
||||
|
firmwareService.saveFirmwareInfo(firmwareInfo); |
||||
|
|
||||
|
FirmwareInfo newFirmwareInfo = new FirmwareInfo(); |
||||
|
newFirmwareInfo.setTenantId(tenantId); |
||||
|
newFirmwareInfo.setTitle(TITLE); |
||||
|
newFirmwareInfo.setVersion(VERSION); |
||||
|
firmwareService.saveFirmwareInfo(newFirmwareInfo); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testSaveFirmwareWithExistingTitleAndVersion() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
Firmware newFirmware = new Firmware(); |
||||
|
newFirmware.setTenantId(tenantId); |
||||
|
newFirmware.setTitle(TITLE); |
||||
|
newFirmware.setVersion(VERSION); |
||||
|
newFirmware.setFileName(FILE_NAME); |
||||
|
newFirmware.setContentType(CONTENT_TYPE); |
||||
|
newFirmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(newFirmware); |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testDeleteFirmwareWithReferenceByDevice() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
Firmware savedFirmware = firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
Device device = new Device(); |
||||
|
device.setTenantId(tenantId); |
||||
|
device.setName("My device"); |
||||
|
device.setType("default"); |
||||
|
device.setFirmwareId(savedFirmware.getId()); |
||||
|
Device savedDevice = deviceService.saveDevice(device); |
||||
|
|
||||
|
try { |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} finally { |
||||
|
deviceService.deleteDevice(tenantId, savedDevice.getId()); |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Test(expected = DataValidationException.class) |
||||
|
public void testDeleteFirmwareWithReferenceByDeviceProfile() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
Firmware savedFirmware = firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); |
||||
|
deviceProfile.setFirmwareId(savedFirmware.getId()); |
||||
|
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); |
||||
|
|
||||
|
try { |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} finally { |
||||
|
deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindFirmwareById() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
Firmware savedFirmware = firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); |
||||
|
Assert.assertNotNull(foundFirmware); |
||||
|
Assert.assertEquals(savedFirmware, foundFirmware); |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindFirmwareInfoById() { |
||||
|
FirmwareInfo firmware = new FirmwareInfo(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); |
||||
|
|
||||
|
FirmwareInfo foundFirmware = firmwareService.findFirmwareInfoById(tenantId, savedFirmware.getId()); |
||||
|
Assert.assertNotNull(foundFirmware); |
||||
|
Assert.assertEquals(savedFirmware, foundFirmware); |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteFirmware() { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
Firmware savedFirmware = firmwareService.saveFirmware(firmware); |
||||
|
|
||||
|
Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); |
||||
|
Assert.assertNotNull(foundFirmware); |
||||
|
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
||||
|
foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); |
||||
|
Assert.assertNull(foundFirmware); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantFirmwaresByTenantId() { |
||||
|
List<FirmwareInfo> firmwares = new ArrayList<>(); |
||||
|
for (int i = 0; i < 165; i++) { |
||||
|
Firmware firmware = new Firmware(); |
||||
|
firmware.setTenantId(tenantId); |
||||
|
firmware.setTitle(TITLE); |
||||
|
firmware.setVersion(VERSION + i); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
|
||||
|
FirmwareInfo info = new FirmwareInfo(firmwareService.saveFirmware(firmware)); |
||||
|
info.setHasData(true); |
||||
|
firmwares.add(info); |
||||
|
} |
||||
|
|
||||
|
List<FirmwareInfo> loadedFirmwares = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(16); |
||||
|
PageData<FirmwareInfo> pageData; |
||||
|
do { |
||||
|
pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); |
||||
|
loadedFirmwares.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(firmwares, idComparator); |
||||
|
Collections.sort(loadedFirmwares, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(firmwares, loadedFirmwares); |
||||
|
|
||||
|
firmwareService.deleteFirmwaresByTenantId(tenantId); |
||||
|
|
||||
|
pageLink = new PageLink(31); |
||||
|
pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertTrue(pageData.getData().isEmpty()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantFirmwaresByTenantIdAndHasData() { |
||||
|
List<FirmwareInfo> firmwares = new ArrayList<>(); |
||||
|
for (int i = 0; i < 165; i++) { |
||||
|
FirmwareInfo firmwareInfo = new FirmwareInfo(); |
||||
|
firmwareInfo.setTenantId(tenantId); |
||||
|
firmwareInfo.setTitle(TITLE); |
||||
|
firmwareInfo.setVersion(VERSION + i); |
||||
|
firmwares.add(firmwareService.saveFirmwareInfo(firmwareInfo)); |
||||
|
} |
||||
|
|
||||
|
List<FirmwareInfo> loadedFirmwares = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(16); |
||||
|
PageData<FirmwareInfo> pageData; |
||||
|
do { |
||||
|
pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink); |
||||
|
loadedFirmwares.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(firmwares, idComparator); |
||||
|
Collections.sort(loadedFirmwares, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(firmwares, loadedFirmwares); |
||||
|
|
||||
|
firmwares.forEach(f -> { |
||||
|
Firmware firmware = new Firmware(f.getId()); |
||||
|
firmware.setCreatedTime(f.getCreatedTime()); |
||||
|
firmware.setTenantId(f.getTenantId()); |
||||
|
firmware.setTitle(f.getTitle()); |
||||
|
firmware.setVersion(f.getVersion()); |
||||
|
firmware.setFileName(FILE_NAME); |
||||
|
firmware.setContentType(CONTENT_TYPE); |
||||
|
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
||||
|
firmware.setChecksum(CHECKSUM); |
||||
|
firmware.setData(DATA); |
||||
|
firmwareService.saveFirmware(firmware); |
||||
|
f.setHasData(true); |
||||
|
}); |
||||
|
|
||||
|
loadedFirmwares = new ArrayList<>(); |
||||
|
pageLink = new PageLink(16); |
||||
|
do { |
||||
|
pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink); |
||||
|
loadedFirmwares.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(firmwares, idComparator); |
||||
|
Collections.sort(loadedFirmwares, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(firmwares, loadedFirmwares); |
||||
|
|
||||
|
firmwareService.deleteFirmwaresByTenantId(tenantId); |
||||
|
|
||||
|
pageLink = new PageLink(31); |
||||
|
pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertTrue(pageData.getData().isEmpty()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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.service.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.BaseFirmwareServiceTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class FirmwareServiceSqlTest extends BaseFirmwareServiceTest { |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
import { PageLink } from '@shared/models/page/page-link'; |
||||
|
import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { PageData } from '@shared/models/page/page-data'; |
||||
|
import { Firmware, FirmwareInfo } from '@shared/models/firmware.models'; |
||||
|
import { catchError, map, mergeMap } from 'rxjs/operators'; |
||||
|
import { deepClone, isDefinedAndNotNull } from '@core/utils'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class FirmwareService { |
||||
|
constructor( |
||||
|
private http: HttpClient |
||||
|
) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> { |
||||
|
let url = `/api/firmwares`; |
||||
|
if (isDefinedAndNotNull(hasData)) { |
||||
|
url += `/${hasData}`; |
||||
|
} |
||||
|
url += `${pageLink.toQuery()}`; |
||||
|
return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config)); |
||||
|
} |
||||
|
|
||||
|
public getFirmware(firmwareId: string, config?: RequestConfig): Observable<Firmware> { |
||||
|
return this.http.get<Firmware>(`/api/firmware/${firmwareId}`, defaultHttpOptionsFromConfig(config)); |
||||
|
} |
||||
|
|
||||
|
public getFirmwareInfo(firmwareId: string, config?: RequestConfig): Observable<FirmwareInfo> { |
||||
|
return this.http.get<FirmwareInfo>(`/api/firmware/info/${firmwareId}`, defaultHttpOptionsFromConfig(config)); |
||||
|
} |
||||
|
|
||||
|
public downloadFirmware(firmwareId: string): Observable<any> { |
||||
|
return this.http.get(`/api/firmware/${firmwareId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( |
||||
|
map((response) => { |
||||
|
const headers = response.headers; |
||||
|
const filename = headers.get('x-filename'); |
||||
|
const contentType = headers.get('content-type'); |
||||
|
const linkElement = document.createElement('a'); |
||||
|
try { |
||||
|
const blob = new Blob([response.body], { type: contentType }); |
||||
|
const url = URL.createObjectURL(blob); |
||||
|
linkElement.setAttribute('href', url); |
||||
|
linkElement.setAttribute('download', filename); |
||||
|
const clickEvent = new MouseEvent('click', |
||||
|
{ |
||||
|
view: window, |
||||
|
bubbles: true, |
||||
|
cancelable: false |
||||
|
} |
||||
|
); |
||||
|
linkElement.dispatchEvent(clickEvent); |
||||
|
return null; |
||||
|
} catch (e) { |
||||
|
throw e; |
||||
|
} |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public saveFirmware(firmware: Firmware, config?: RequestConfig): Observable<Firmware> { |
||||
|
if (!firmware.file) { |
||||
|
return this.saveFirmwareInfo(firmware, config); |
||||
|
} |
||||
|
const firmwareInfo = deepClone(firmware); |
||||
|
delete firmwareInfo.file; |
||||
|
delete firmwareInfo.checksum; |
||||
|
delete firmwareInfo.checksumAlgorithm; |
||||
|
return this.saveFirmwareInfo(firmwareInfo, config).pipe( |
||||
|
mergeMap(res => { |
||||
|
return this.uploadFirmwareFile(res.id.id, firmware.file, firmware.checksumAlgorithm, firmware.checksum).pipe( |
||||
|
catchError(() => this.deleteFirmware(res.id.id)) |
||||
|
); |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public saveFirmwareInfo(firmware: FirmwareInfo, config?: RequestConfig): Observable<Firmware> { |
||||
|
return this.http.post<Firmware>('/api/firmware', firmware, defaultHttpOptionsFromConfig(config)); |
||||
|
} |
||||
|
|
||||
|
public uploadFirmwareFile(firmwareId: string, file: File, checksumAlgorithm?: string, |
||||
|
checksum?: string, config?: RequestConfig): Observable<any> { |
||||
|
if (!config) { |
||||
|
config = {}; |
||||
|
} |
||||
|
const formData = new FormData(); |
||||
|
formData.append('file', file); |
||||
|
let url = `/api/firmware/${firmwareId}`; |
||||
|
if (checksumAlgorithm && checksum) { |
||||
|
url += `?checksumAlgorithm=${checksumAlgorithm}&checksum=${checksum}`; |
||||
|
} |
||||
|
return this.http.post(url, formData, |
||||
|
defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); |
||||
|
} |
||||
|
|
||||
|
public deleteFirmware(firmwareId: string, config?: RequestConfig) { |
||||
|
return this.http.delete(`/api/firmware/${firmwareId}`, defaultHttpOptionsFromConfig(config)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2021 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. |
||||
|
|
||||
|
--> |
||||
|
<mat-form-field [formGroup]="firmwareFormGroup" class="mat-block"> |
||||
|
<input matInput type="text" placeholder="{{ placeholderText | translate }}" |
||||
|
#firmwareInput |
||||
|
formControlName="firmwareId" |
||||
|
(focusin)="onFocus()" |
||||
|
[required]="required" |
||||
|
[matAutocomplete]="firmwareAutocomplete"> |
||||
|
<button *ngIf="firmwareFormGroup.get('firmwareId').value && !disabled" |
||||
|
type="button" |
||||
|
matSuffix mat-button mat-icon-button aria-label="Clear" |
||||
|
(click)="clear()"> |
||||
|
<mat-icon class="material-icons">close</mat-icon> |
||||
|
</button> |
||||
|
<mat-autocomplete class="tb-autocomplete" |
||||
|
#firmwareAutocomplete="matAutocomplete" |
||||
|
[displayWith]="displayFirmwareFn"> |
||||
|
<mat-option *ngFor="let firmware of filteredFirmwares | async" [value]="firmware"> |
||||
|
<span [innerHTML]="this.firmwareTitleText(firmware) | highlight:searchText"></span> |
||||
|
</mat-option> |
||||
|
<mat-option *ngIf="!(filteredFirmwares | async)?.length" [value]="null" class="tb-not-found"> |
||||
|
<div class="tb-not-found-content" (click)="$event.stopPropagation()"> |
||||
|
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty"> |
||||
|
<span translate>firmware.no-firmware-text</span> |
||||
|
</div> |
||||
|
<ng-template #searchNotEmpty> |
||||
|
<span> |
||||
|
{{ translate.get('firmware.no-firmware-matching', |
||||
|
{entity: truncate.transform(searchText, true, 6, '...')}) | async }} |
||||
|
</span> |
||||
|
</ng-template> |
||||
|
</div> |
||||
|
</mat-option> |
||||
|
</mat-autocomplete> |
||||
|
<mat-error *ngIf="firmwareFormGroup.get('firmwareId').hasError('required')"> |
||||
|
{{ requiredErrorText | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
@ -0,0 +1,236 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; |
||||
|
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { map, mergeMap, share, tap } from 'rxjs/operators'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
||||
|
import { EntityId } from '@shared/models/id/entity-id'; |
||||
|
import { EntityType } from '@shared/models/entity-type.models'; |
||||
|
import { BaseData } from '@shared/models/base-data'; |
||||
|
import { EntityService } from '@core/http/entity.service'; |
||||
|
import { TruncatePipe } from '@shared/pipe/truncate.pipe'; |
||||
|
import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; |
||||
|
import { FirmwareInfo } from '@shared/models/firmware.models'; |
||||
|
import { FirmwareService } from '@core/http/firmware.service'; |
||||
|
import { PageLink } from '@shared/models/page/page-link'; |
||||
|
import { Direction } from '@shared/models/page/sort-order'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-firmware-autocomplete', |
||||
|
templateUrl: './firmware-autocomplete.component.html', |
||||
|
styleUrls: [], |
||||
|
providers: [{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => FirmwareAutocompleteComponent), |
||||
|
multi: true |
||||
|
}] |
||||
|
}) |
||||
|
export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnInit { |
||||
|
|
||||
|
firmwareFormGroup: FormGroup; |
||||
|
|
||||
|
modelValue: string | null; |
||||
|
|
||||
|
@Input() |
||||
|
labelText: string; |
||||
|
|
||||
|
@Input() |
||||
|
requiredText: string; |
||||
|
|
||||
|
@Input() |
||||
|
useFullEntityId = false; |
||||
|
|
||||
|
private requiredValue: boolean; |
||||
|
|
||||
|
get required(): boolean { |
||||
|
return this.requiredValue; |
||||
|
} |
||||
|
|
||||
|
@Input() |
||||
|
set required(value: boolean) { |
||||
|
this.requiredValue = coerceBooleanProperty(value); |
||||
|
} |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@ViewChild('firmwareInput', {static: true}) firmwareInput: ElementRef; |
||||
|
@ViewChild('firmwareInput', {read: MatAutocompleteTrigger}) firmwareAutocomplete: MatAutocompleteTrigger; |
||||
|
|
||||
|
filteredFirmwares: Observable<Array<FirmwareInfo>>; |
||||
|
|
||||
|
searchText = ''; |
||||
|
|
||||
|
private dirty = false; |
||||
|
|
||||
|
private propagateChange = (v: any) => { }; |
||||
|
|
||||
|
constructor(private store: Store<AppState>, |
||||
|
public translate: TranslateService, |
||||
|
public truncate: TruncatePipe, |
||||
|
private entityService: EntityService, |
||||
|
private firmwareService: FirmwareService, |
||||
|
private fb: FormBuilder) { |
||||
|
this.firmwareFormGroup = this.fb.group({ |
||||
|
firmwareId: [null] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.filteredFirmwares = this.firmwareFormGroup.get('firmwareId').valueChanges |
||||
|
.pipe( |
||||
|
tap(value => { |
||||
|
let modelValue; |
||||
|
if (typeof value === 'string' || !value) { |
||||
|
modelValue = null; |
||||
|
} else { |
||||
|
modelValue = this.useFullEntityId ? value.id : value.id.id; |
||||
|
} |
||||
|
this.updateView(modelValue); |
||||
|
if (value === null) { |
||||
|
this.clear(); |
||||
|
} |
||||
|
}), |
||||
|
map(value => value ? (typeof value === 'string' ? value : value.title) : ''), |
||||
|
mergeMap(name => this.fetchFirmware(name)), |
||||
|
share() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
ngAfterViewInit(): void { |
||||
|
} |
||||
|
|
||||
|
getCurrentEntity(): BaseData<EntityId> | null { |
||||
|
const currentRuleChain = this.firmwareFormGroup.get('firmwareId').value; |
||||
|
if (currentRuleChain && typeof currentRuleChain !== 'string') { |
||||
|
return currentRuleChain as BaseData<EntityId>; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (this.disabled) { |
||||
|
this.firmwareFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.firmwareFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
textIsNotEmpty(text: string): boolean { |
||||
|
return (text && text.length > 0); |
||||
|
} |
||||
|
|
||||
|
writeValue(value: string | EntityId | null): void { |
||||
|
this.searchText = ''; |
||||
|
if (value != null && value !== '') { |
||||
|
let firmwareId = ''; |
||||
|
if (typeof value === 'string') { |
||||
|
firmwareId = value; |
||||
|
} else if (value.entityType && value.id) { |
||||
|
firmwareId = value.id; |
||||
|
} |
||||
|
if (firmwareId !== '') { |
||||
|
this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe( |
||||
|
(entity) => { |
||||
|
this.modelValue = entity.id.id; |
||||
|
this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false}); |
||||
|
}, |
||||
|
() => { |
||||
|
this.modelValue = null; |
||||
|
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); |
||||
|
if (value !== null) { |
||||
|
this.propagateChange(this.modelValue); |
||||
|
} |
||||
|
} |
||||
|
); |
||||
|
} else { |
||||
|
this.modelValue = null; |
||||
|
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); |
||||
|
} |
||||
|
} else { |
||||
|
this.modelValue = null; |
||||
|
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); |
||||
|
} |
||||
|
this.dirty = true; |
||||
|
} |
||||
|
|
||||
|
onFocus() { |
||||
|
if (this.dirty) { |
||||
|
this.firmwareFormGroup.get('firmwareId').updateValueAndValidity({onlySelf: true, emitEvent: true}); |
||||
|
this.dirty = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
reset() { |
||||
|
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); |
||||
|
} |
||||
|
|
||||
|
updateView(value: string | null) { |
||||
|
if (this.modelValue !== value) { |
||||
|
this.modelValue = value; |
||||
|
this.propagateChange(this.modelValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
displayFirmwareFn(firmware?: FirmwareInfo): string | undefined { |
||||
|
return firmware ? `${firmware.title} (${firmware.version})` : undefined; |
||||
|
} |
||||
|
|
||||
|
fetchFirmware(searchText?: string): Observable<Array<FirmwareInfo>> { |
||||
|
this.searchText = searchText; |
||||
|
const pageLink = new PageLink(50, 0, searchText, { |
||||
|
property: 'title', |
||||
|
direction: Direction.ASC |
||||
|
}); |
||||
|
return this.firmwareService.getFirmwares(pageLink, true, {ignoreLoading: true}).pipe( |
||||
|
map((data) => data && data.data.length ? data.data : null) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
clear() { |
||||
|
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: true}); |
||||
|
setTimeout(() => { |
||||
|
this.firmwareInput.nativeElement.blur(); |
||||
|
this.firmwareInput.nativeElement.focus(); |
||||
|
}, 0); |
||||
|
} |
||||
|
|
||||
|
get placeholderText(): string { |
||||
|
return this.labelText || 'firmware.firmware'; |
||||
|
} |
||||
|
|
||||
|
get requiredErrorText(): string { |
||||
|
return this.requiredText || 'firmware.firmware-required'; |
||||
|
} |
||||
|
|
||||
|
firmwareTitleText(firmware: FirmwareInfo): string { |
||||
|
return `${firmware.title} (${firmware.version})`; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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.
|
||||
|
///
|
||||
|
|
||||
|
import { RouterModule, Routes } from '@angular/router'; |
||||
|
import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; |
||||
|
import { Authority } from '@shared/models/authority.enum'; |
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { FirmwareTableConfigResolve } from '@home/pages/firmware/firmware-table-config.resolve'; |
||||
|
|
||||
|
const routes: Routes = [ |
||||
|
{ |
||||
|
path: 'firmwares', |
||||
|
component: EntitiesTableComponent, |
||||
|
data: { |
||||
|
auth: [Authority.TENANT_ADMIN], |
||||
|
title: 'firmware.firmware', |
||||
|
breadcrumb: { |
||||
|
label: 'firmware.firmware', |
||||
|
icon: 'memory' |
||||
|
} |
||||
|
}, |
||||
|
resolve: { |
||||
|
entitiesTableConfig: FirmwareTableConfigResolve |
||||
|
} |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
@NgModule({ |
||||
|
imports: [RouterModule.forChild(routes)], |
||||
|
exports: [RouterModule], |
||||
|
providers: [ |
||||
|
FirmwareTableConfigResolve |
||||
|
] |
||||
|
}) |
||||
|
export class FirmwareRoutingModule{ } |
||||
@ -0,0 +1,99 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { Resolve } from '@angular/router'; |
||||
|
import { |
||||
|
DateEntityTableColumn, |
||||
|
EntityTableColumn, |
||||
|
EntityTableConfig |
||||
|
} from '@home/models/entity/entities-table-config.models'; |
||||
|
import { Firmware, FirmwareInfo } from '@shared/models/firmware.models'; |
||||
|
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { DatePipe } from '@angular/common'; |
||||
|
import { FirmwareService } from '@core/http/firmware.service'; |
||||
|
import { PageLink } from '@shared/models/page/page-link'; |
||||
|
import { FirmwaresComponent } from '@home/pages/firmware/firmwares.component'; |
||||
|
import { EntityAction } from '@home/models/entity/entity-component.models'; |
||||
|
import { DeviceInfo } from '@shared/models/device.models'; |
||||
|
|
||||
|
@Injectable() |
||||
|
export class FirmwareTableConfigResolve implements Resolve<EntityTableConfig<Firmware, PageLink, FirmwareInfo>> { |
||||
|
|
||||
|
private readonly config: EntityTableConfig<Firmware, PageLink, FirmwareInfo> = new EntityTableConfig<Firmware, PageLink, FirmwareInfo>(); |
||||
|
|
||||
|
constructor(private translate: TranslateService, |
||||
|
private datePipe: DatePipe, |
||||
|
private firmwareService: FirmwareService) { |
||||
|
this.config.entityType = EntityType.FIRMWARE; |
||||
|
this.config.entityComponent = FirmwaresComponent; |
||||
|
this.config.entityTranslations = entityTypeTranslations.get(EntityType.FIRMWARE); |
||||
|
this.config.entityResources = entityTypeResources.get(EntityType.FIRMWARE); |
||||
|
|
||||
|
this.config.entityTitle = (firmware) => firmware ? firmware.title : ''; |
||||
|
|
||||
|
this.config.columns.push( |
||||
|
new DateEntityTableColumn<FirmwareInfo>('createdTime', 'common.created-time', this.datePipe, '150px'), |
||||
|
new EntityTableColumn<FirmwareInfo>('title', 'firmware.title', '50%'), |
||||
|
new EntityTableColumn<FirmwareInfo>('version', 'firmware.version', '50%') |
||||
|
); |
||||
|
|
||||
|
this.config.cellActionDescriptors.push( |
||||
|
{ |
||||
|
name: this.translate.instant('firmware.export'), |
||||
|
icon: 'file_download', |
||||
|
isEnabled: (firmware) => firmware.hasData, |
||||
|
onAction: ($event, entity) => this.exportFirmware($event, entity) |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
this.config.deleteEntityTitle = firmware => this.translate.instant('firmware.delete-firmware-title', |
||||
|
{ firmwareTitle: firmware.title }); |
||||
|
this.config.deleteEntityContent = () => this.translate.instant('firmware.delete-firmware-text'); |
||||
|
this.config.deleteEntitiesTitle = count => this.translate.instant('firmware.delete-firmwares-title', {count}); |
||||
|
this.config.deleteEntitiesContent = () => this.translate.instant('firmware.delete-firmwares-text'); |
||||
|
|
||||
|
this.config.entitiesFetchFunction = pageLink => this.firmwareService.getFirmwares(pageLink); |
||||
|
this.config.loadEntity = id => this.firmwareService.getFirmwareInfo(id.id); |
||||
|
this.config.saveEntity = firmware => this.firmwareService.saveFirmware(firmware); |
||||
|
this.config.deleteEntity = id => this.firmwareService.deleteFirmware(id.id); |
||||
|
|
||||
|
this.config.onEntityAction = action => this.onFirmwareAction(action); |
||||
|
} |
||||
|
|
||||
|
resolve(): EntityTableConfig<Firmware, PageLink, FirmwareInfo> { |
||||
|
this.config.tableTitle = this.translate.instant('firmware.firmware'); |
||||
|
return this.config; |
||||
|
} |
||||
|
|
||||
|
exportFirmware($event: Event, firmware: FirmwareInfo) { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
this.firmwareService.downloadFirmware(firmware.id.id).subscribe(); |
||||
|
} |
||||
|
|
||||
|
onFirmwareAction(action: EntityAction<FirmwareInfo>): boolean { |
||||
|
switch (action.action) { |
||||
|
case 'uploadFirmware': |
||||
|
this.exportFirmware(action.event, action.entity); |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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.
|
||||
|
///
|
||||
|
|
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { SharedModule } from '@shared/shared.module'; |
||||
|
import { HomeComponentsModule } from '@home/components/home-components.module'; |
||||
|
import { FirmwareRoutingModule } from '@home/pages/firmware/firmware-routing.module'; |
||||
|
import { FirmwaresComponent } from '@home/pages/firmware/firmwares.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [ |
||||
|
FirmwaresComponent |
||||
|
], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
SharedModule, |
||||
|
HomeComponentsModule, |
||||
|
FirmwareRoutingModule |
||||
|
] |
||||
|
}) |
||||
|
export class FirmwareModule { } |
||||
@ -0,0 +1,97 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2021 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. |
||||
|
|
||||
|
--> |
||||
|
<div class="tb-details-buttons" fxLayout.xs="column"> |
||||
|
<button mat-raised-button color="primary" fxFlex.xs |
||||
|
[disabled]="(isLoading$ | async) || !entity?.hasData" |
||||
|
(click)="onEntityAction($event, 'uploadFirmware')" |
||||
|
[fxShow]="!isEdit"> |
||||
|
{{'firmware.export' | translate }} |
||||
|
</button> |
||||
|
<button mat-raised-button color="primary" fxFlex.xs |
||||
|
[disabled]="(isLoading$ | async)" |
||||
|
(click)="onEntityAction($event, 'delete')" |
||||
|
[fxShow]="!hideDelete() && !isEdit"> |
||||
|
{{'resource.delete' | translate }} |
||||
|
</button> |
||||
|
<div fxLayout="row" fxLayout.xs="column"> |
||||
|
<button mat-raised-button |
||||
|
ngxClipboard |
||||
|
(cbOnSuccess)="onFirmwareIdCopied($event)" |
||||
|
[cbContent]="entity?.id?.id" |
||||
|
[fxShow]="!isEdit"> |
||||
|
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> |
||||
|
<span translate>firmware.copyId</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="mat-padding" fxLayout="column"> |
||||
|
<form [formGroup]="entityForm"> |
||||
|
<fieldset [disabled]="(isLoading$ | async) || !isEdit"> |
||||
|
<mat-hint class="tb-hint" translate *ngIf="isAdd">firmware.warning-after-save-no-edit</mat-hint> |
||||
|
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column"> |
||||
|
<mat-form-field class="mat-block" fxFlex="45"> |
||||
|
<mat-label translate>firmware.title</mat-label> |
||||
|
<input matInput formControlName="title" type="text" required> |
||||
|
<mat-error *ngIf="entityForm.get('title').hasError('required')"> |
||||
|
{{ 'firmware.title-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="mat-block" fxFlex> |
||||
|
<mat-label translate>firmware.version</mat-label> |
||||
|
<input matInput formControlName="version" type="text" required> |
||||
|
<mat-error *ngIf="entityForm.get('version').hasError('required')"> |
||||
|
{{ 'firmware.version-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<section *ngIf="isAdd" style="padding-bottom: 8px"> |
||||
|
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column"> |
||||
|
<mat-form-field class="mat-block" fxFlex="45"> |
||||
|
<mat-label translate>firmware.checksum-algorithm</mat-label> |
||||
|
<mat-select formControlName="checksumAlgorithm"> |
||||
|
<mat-option [value]=null></mat-option> |
||||
|
<mat-option *ngFor="let checksumAlgorithm of checksumAlgorithms" [value]="checksumAlgorithm"> |
||||
|
{{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="mat-block" fxFlex> |
||||
|
<mat-label translate>firmware.checksum</mat-label> |
||||
|
<input matInput formControlName="checksum" type="text" |
||||
|
[required]="entityForm.get('checksumAlgorithm').value != null"> |
||||
|
<mat-error *ngIf="entityForm.get('checksumAlgorithm').hasError('required')"> |
||||
|
{{ 'firmware.checksum-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<tb-file-input |
||||
|
formControlName="file" |
||||
|
workFromFileObj="true" |
||||
|
required |
||||
|
dropLabel="{{'resource.drop-file' | translate}}"> |
||||
|
</tb-file-input> |
||||
|
</section> |
||||
|
<div formGroupName="additionalInfo"> |
||||
|
<mat-form-field class="mat-block"> |
||||
|
<mat-label translate>firmware.description</mat-label> |
||||
|
<textarea matInput formControlName="description" rows="2"></textarea> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</fieldset> |
||||
|
</form> |
||||
|
</div> |
||||
@ -0,0 +1,124 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { EntityComponent } from '@home/components/entity/entity.component'; |
||||
|
import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, Firmware } from '@shared/models/firmware.models'; |
||||
|
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators'; |
||||
|
import { ActionNotificationShow } from '@core/notification/notification.actions'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-firmware', |
||||
|
templateUrl: './firmwares.component.html' |
||||
|
}) |
||||
|
export class FirmwaresComponent extends EntityComponent<Firmware> implements OnInit, OnDestroy { |
||||
|
|
||||
|
private destroy$ = new Subject(); |
||||
|
|
||||
|
checksumAlgorithms = Object.values(ChecksumAlgorithm); |
||||
|
checksumAlgorithmTranslationMap = ChecksumAlgorithmTranslationMap; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
protected translate: TranslateService, |
||||
|
@Inject('entity') protected entityValue: Firmware, |
||||
|
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Firmware>, |
||||
|
public fb: FormBuilder) { |
||||
|
super(store, fb, entityValue, entitiesTableConfigValue); |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
super.ngOnInit(); |
||||
|
if (this.isAdd) { |
||||
|
this.entityForm.get('checksumAlgorithm').valueChanges.pipe( |
||||
|
map(algorithm => !!algorithm), |
||||
|
distinctUntilChanged(), |
||||
|
takeUntil(this.destroy$) |
||||
|
).subscribe( |
||||
|
setAlgorithm => { |
||||
|
if (setAlgorithm) { |
||||
|
this.entityForm.get('checksum').setValidators([Validators.maxLength(1020), Validators.required]); |
||||
|
} else { |
||||
|
this.entityForm.get('checksum').clearValidators(); |
||||
|
} |
||||
|
this.entityForm.get('checksum').updateValueAndValidity({emitEvent: false}); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy() { |
||||
|
super.ngOnDestroy(); |
||||
|
this.destroy$.next(); |
||||
|
this.destroy$.complete(); |
||||
|
} |
||||
|
|
||||
|
hideDelete() { |
||||
|
if (this.entitiesTableConfig) { |
||||
|
return !this.entitiesTableConfig.deleteEnabled(this.entity); |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
buildForm(entity: Firmware): FormGroup { |
||||
|
const form = this.fb.group({ |
||||
|
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], |
||||
|
version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]], |
||||
|
additionalInfo: this.fb.group( |
||||
|
{ |
||||
|
description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], |
||||
|
} |
||||
|
) |
||||
|
}); |
||||
|
if (this.isAdd) { |
||||
|
form.addControl('checksumAlgorithm', this.fb.control(null)); |
||||
|
form.addControl('checksum', this.fb.control('', Validators.maxLength(1020))); |
||||
|
form.addControl('file', this.fb.control(null, Validators.required)); |
||||
|
} |
||||
|
return form; |
||||
|
} |
||||
|
|
||||
|
updateForm(entity: Firmware) { |
||||
|
if (this.isEdit) { |
||||
|
this.entityForm.get('title').disable({emitEvent: false}); |
||||
|
this.entityForm.get('version').disable({emitEvent: false}); |
||||
|
} |
||||
|
this.entityForm.patchValue({ |
||||
|
title: entity.title, |
||||
|
version: entity.version, |
||||
|
additionalInfo: { |
||||
|
description: entity.additionalInfo ? entity.additionalInfo.description : '' |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onFirmwareIdCopied($event) { |
||||
|
this.store.dispatch(new ActionNotificationShow( |
||||
|
{ |
||||
|
message: this.translate.instant('firmware.idCopiedMessage'), |
||||
|
type: 'success', |
||||
|
duration: 750, |
||||
|
verticalPosition: 'bottom', |
||||
|
horizontalPosition: 'right' |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue