Browse Source

added id, createdTime to mobile app setting entity, added MobileAppSettingsValidator

pull/10591/head
dashevchenko 2 years ago
parent
commit
0003caf4fb
  1. 4
      application/src/main/data/upgrade/3.6.4/schema_update.sql
  2. 27
      application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java
  3. 85
      application/src/test/java/org/thingsboard/server/controller/MobileApplicationControllerTest.java
  4. 37
      common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppSettingsId.java
  5. 13
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java
  6. 30
      dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java
  7. 5
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsDao.java
  8. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  9. 44
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppSettingsEntity.java
  10. 51
      dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppSettingsDataValidator.java
  11. 22
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppSettingsDao.java
  12. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppSettingsRepository.java
  13. 4
      dao/src/main/resources/sql/schema-entities.sql

4
application/src/main/data/upgrade/3.6.4/schema_update.sql

@ -138,7 +138,9 @@ DELETE FROM asset_profile WHERE name ='TbServiceQueue';
-- MOBILE APP SETTINGS TABLE CREATE START
CREATE TABLE IF NOT EXISTS mobile_app_settings (
tenant_id UUID NOT NULL,
id uuid NOT NULL CONSTRAINT mobile_app_settings_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid UNIQUE NOT NULL,
use_default_app boolean,
android_config VARCHAR(1000),
ios_config VARCHAR(1000),

27
application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java

@ -20,17 +20,17 @@ import io.swagger.v3.oas.annotations.Parameter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.AndroidConfig;
import org.thingsboard.server.common.data.mobile.IosConfig;
@ -81,10 +81,13 @@ public class MobileApplicationController extends BaseController {
" }\n" +
"}";
public static final String ANDROID_APPLICATION_STORE_LINK = "https://play.google.com/store/apps/details?id=org.thingsboard.demo.app";
public static final String APPLE_APPLICATION_STORE_LINK = "https://apps.apple.com/us/app/thingsboard-live/id1594355695";
public static final String SECRET = "secret";
public static final String SECRET_PARAM_DESCRIPTION = "A string value representing short-live secret key";
public static final String DEFAULT_APP_DOMAIN = "demo.thingsboard.io";
public static final String DEEP_LINK_PATTERN = "https://%s/api/noauth/qr?secret=%s&ttl=%s";
private final SystemSecurityService systemSecurityService;
private final MobileAppSecretService mobileAppSecretService;
private final MobileAppSettingsService mobileAppSettingsService;
@ -143,7 +146,7 @@ public class MobileApplicationController extends BaseController {
@GetMapping(value = "/api/mobile/deepLink", produces = "text/plain")
public String getMobileAppDeepLink(HttpServletRequest request) throws ThingsboardException, URISyntaxException {
String secret = mobileAppSecretService.generateMobileAppSecret(getCurrentUser());
String baseUrl = systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
String baseUrl = systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, null, request);
String platformDomain = new URI(baseUrl).getHost();
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
String appDomain;
@ -156,7 +159,7 @@ public class MobileApplicationController extends BaseController {
if (!appDomain.equals(platformDomain)) {
deepLink = deepLink + "&host=" + baseUrl;
}
return deepLink;
return "\"" + deepLink + "\"";
}
@ApiOperation(value = "Get User Token (getUserTokenByMobileSecret)",
@ -167,4 +170,20 @@ public class MobileApplicationController extends BaseController {
checkParameter(SECRET, secret);
return mobileAppSecretService.getJwtPair(secret);
}
@GetMapping(value = "/api/noauth/qr")
public ResponseEntity<?> getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) {
if (userAgent.contains("Android")) {
return ResponseEntity.status(HttpStatus.FOUND)
.header("Location", ANDROID_APPLICATION_STORE_LINK)
.build();
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) {
return ResponseEntity.status(HttpStatus.FOUND)
.header("Location", APPLE_APPLICATION_STORE_LINK)
.build();
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.build();
}
}
}

85
application/src/test/java/org/thingsboard/server/controller/MobileApplicationControllerTest.java

@ -31,6 +31,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
@ -48,10 +49,10 @@ public class MobileApplicationControllerTest extends AbstractControllerTest {
public void setUp() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
QRCodeConfig qrCodeConfig = new QRCodeConfig();
qrCodeConfig.setQrCodeLabel(TEST_LABEL);
MobileAppSettings mobileAppSettings = new MobileAppSettings();
mobileAppSettings.setUseDefaultApp(true);
AndroidConfig androidConfig = AndroidConfig.builder()
.appPackage(ANDROID_PACKAGE_NAME)
@ -67,7 +68,8 @@ public class MobileApplicationControllerTest extends AbstractControllerTest {
mobileAppSettings.setIosConfig(iosConfig);
mobileAppSettings.setQrCodeConfig(qrCodeConfig);
doPost("/api/mobile/app/settings", mobileAppSettings);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
@ -86,6 +88,79 @@ public class MobileApplicationControllerTest extends AbstractControllerTest {
assertThat(updatedMobileAppSettings.isUseDefaultApp()).isFalse();
}
@Test
public void testShouldNotSaveMobileAppSettingsWithoutRequiredConfig() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(false);
mobileAppSettings.setAndroidConfig(null);
mobileAppSettings.setIosConfig(null);
mobileAppSettings.setQrCodeConfig(null);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Android/ios settings are required to use custom application!")));
mobileAppSettings.setAndroidConfig(AndroidConfig.builder().enabled(false).build());
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Android/ios settings are required to use custom application!")));
mobileAppSettings.setIosConfig(IosConfig.builder().enabled(false).build());
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Qr code config is required!")));
mobileAppSettings.setQrCodeConfig(QRCodeConfig.builder().showOnHomePage(false).build());
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testShouldNotSaveMobileAppSettingsWithoutRequiredAndroidConf() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
AndroidConfig androidConfig = AndroidConfig.builder()
.enabled(true)
.appPackage(null)
.sha256CertFingerprints(null)
.build();
mobileAppSettings.setAndroidConfig(androidConfig);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Application package and sha256 cert fingerprints are required for enabled android settings!")));
androidConfig.setAppPackage("test_app_package");
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Application package and sha256 cert fingerprints are required for enabled android settings!")));
androidConfig.setSha256CertFingerprints("test_sha_256");
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testShouldNotSaveMobileAppSettingsWithoutRequiredIosConf() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
IosConfig iosConfig = IosConfig.builder()
.enabled(true)
.appId(null)
.build();
mobileAppSettings.setIosConfig(iosConfig);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Application id is required for enabled ios settings!")));
iosConfig.setAppId("test_app_id");
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testGetApplicationAssociations() throws Exception {
JsonNode assetLinks = doGet("/.well-known/assetlinks.json", JsonNode.class);
@ -101,7 +176,7 @@ public class MobileApplicationControllerTest extends AbstractControllerTest {
loginSysAdmin();
String deepLink = doGet("/api/mobile/deepLink", String.class);
Pattern expectedPattern = Pattern.compile("https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)&host=([^&]+)");
Pattern expectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)&host=([^&]+)\"");
Matcher parsedDeepLink = expectedPattern.matcher(deepLink);
assertThat(parsedDeepLink.matches()).isTrue();
String appHost = parsedDeepLink.group(1);
@ -138,7 +213,7 @@ public class MobileApplicationControllerTest extends AbstractControllerTest {
doPost("/api/mobile/app/settings", mobileAppSettings);
String customAppDeepLink = doGet("/api/mobile/deepLink", String.class);
Pattern customAppExpectedPattern = Pattern.compile("https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)");
Pattern customAppExpectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)\"");
Matcher customAppParsedDeepLink = customAppExpectedPattern.matcher(customAppDeepLink);
assertThat(customAppParsedDeepLink.matches()).isTrue();
assertThat(customAppParsedDeepLink.group(1)).isEqualTo("localhost");
@ -154,7 +229,5 @@ public class MobileApplicationControllerTest extends AbstractControllerTest {
Matcher customerCustomAppParsedDeepLink = customAppExpectedPattern.matcher(customerCustomAppDeepLink);
assertThat(customerCustomAppParsedDeepLink.matches()).isTrue();
assertThat(customerCustomAppParsedDeepLink.group(1)).isEqualTo("localhost");
}
}

37
common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppSettingsId.java

@ -0,0 +1,37 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.UUID;
@Schema
public class MobileAppSettingsId extends UUIDBased {
private static final long serialVersionUID = 1L;
@JsonCreator
public MobileAppSettingsId(@JsonProperty("id") UUID id) {
super(id);
}
public static MobileAppSettingsId fromString(String mobileAppSettingsId) {
return new MobileAppSettingsId(UUID.fromString(mobileAppSettingsId));
}
}

13
common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java

@ -17,12 +17,13 @@ package org.thingsboard.server.common.data.mobile;
import jakarta.validation.Valid;
import lombok.Data;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.MobileAppSettingsId;
import org.thingsboard.server.common.data.id.TenantId;
import java.io.Serializable;
@Data
public class MobileAppSettings implements Serializable {
public class MobileAppSettings extends BaseData<MobileAppSettingsId> implements HasTenantId {
private static final long serialVersionUID = 2628323657987010348L;
@ -35,4 +36,10 @@ public class MobileAppSettings implements Serializable {
@Valid
private QRCodeConfig qrCodeConfig;
public MobileAppSettings() {
}
public MobileAppSettings(MobileAppSettingsId id) {
super(id);
}
}

30
dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.mobile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.id.TenantId;
@ -26,24 +27,41 @@ import org.thingsboard.server.common.data.mobile.BadgeStyle;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.dao.entity.AbstractCachedService;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
@RequiredArgsConstructor
public class BaseMobileAppSettingsService extends AbstractCachedService<TenantId, MobileAppSettings, MobileAppSettingsEvictEvent> implements MobileAppSettingsService {
public class BaseMobileAppSettingsService extends AbstractCachedEntityService<TenantId, MobileAppSettings, MobileAppSettingsEvictEvent> implements MobileAppSettingsService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String DEFAULT_QR_CODE_LABEL = "Scan to connect or download mobile app";
private final MobileAppSettingsDao mobileAppSettingsDao;
private final DataValidator<MobileAppSettings> mobileAppSettingsDataValidator;
@Override
public MobileAppSettings saveMobileAppSettings(TenantId tenantId, MobileAppSettings settings) {
MobileAppSettings mobileAppSettings = mobileAppSettingsDao.save(tenantId, settings);
publishEvictEvent(new MobileAppSettingsEvictEvent(tenantId));
return mobileAppSettings;
public MobileAppSettings saveMobileAppSettings(TenantId tenantId, MobileAppSettings mobileAppSettings) {
mobileAppSettingsDataValidator.validate(mobileAppSettings, s -> tenantId);
try {
MobileAppSettings savedMobileAppSettings = mobileAppSettingsDao.save(tenantId, mobileAppSettings);
publishEvictEvent(new MobileAppSettingsEvictEvent(tenantId));
return savedMobileAppSettings;
} catch (Exception exception) {
if (mobileAppSettings != null) {
handleEvictEvent(new MobileAppSettingsEvictEvent(tenantId));
}
ConstraintViolationException e = extractConstraintViolationException(exception).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("mobile_app_settings_tenant_id_key")) {
throw new DataValidationException("Mobile application for specified tenant already exists!");
} else {
throw exception;
}
}
}
@Override

5
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsDao.java

@ -17,11 +17,10 @@ package org.thingsboard.server.dao.mobile;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.dao.Dao;
public interface MobileAppSettingsDao {
MobileAppSettings save(TenantId tenantId, MobileAppSettings appSettings);
public interface MobileAppSettingsDao extends Dao<MobileAppSettings> {
MobileAppSettings findByTenantId(TenantId tenantId);

4
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -665,8 +665,8 @@ public class ModelConstants {
public static final String MOBILE_APP_SETTINGS_TABLE_NAME = "mobile_app_settings";
public static final String MOBILE_APP_SETTINGS_USE_DEFAULT_APP_PROPERTY = "use_default_app";
public static final String MOBILE_APP_SETTINGS_ANDROID_CONFIG_PROPERTY = "android_config";
public static final String MOBILE_APP_IOS_CONFIG_PROPERTY = "ios_config";
public static final String MOBILE_APP_QR_CODE_CONFIG_PROPERTY = "qr_code_config";
public static final String MOBILE_APP_SETTINGS_IOS_CONFIG_PROPERTY = "ios_config";
public static final String MOBILE_APP_SETTINGS_QR_CODE_CONFIG_PROPERTY = "qr_code_config";
protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN};

44
dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppSettingsEntity.java

@ -19,30 +19,27 @@ import com.fasterxml.jackson.databind.JsonNode;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.MobileAppSettingsId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.AndroidConfig;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.ToData;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
import java.io.Serializable;
import java.util.UUID;
@Data
@NoArgsConstructor
@Entity
@Table(name = ModelConstants.MOBILE_APP_SETTINGS_TABLE_NAME)
public class MobileAppSettingsEntity implements ToData<MobileAppSettings>, Serializable {
public class MobileAppSettingsEntity extends BaseSqlEntity<MobileAppSettings> {
@Id
@Column(name = ModelConstants.TENANT_ID_COLUMN, columnDefinition = "uuid")
protected UUID tenantId;
@ -54,41 +51,34 @@ public class MobileAppSettingsEntity implements ToData<MobileAppSettings>, Seria
private JsonNode androidConfig;
@Convert(converter = JsonConverter.class)
@Column(name = ModelConstants.MOBILE_APP_IOS_CONFIG_PROPERTY)
@Column(name = ModelConstants.MOBILE_APP_SETTINGS_IOS_CONFIG_PROPERTY)
private JsonNode iosConfig;
@Convert(converter = JsonConverter.class)
@Column(name = ModelConstants.MOBILE_APP_QR_CODE_CONFIG_PROPERTY)
@Column(name = ModelConstants.MOBILE_APP_SETTINGS_QR_CODE_CONFIG_PROPERTY)
private JsonNode qrCodeConfig;
public MobileAppSettingsEntity(MobileAppSettings mobileAppSettings) {
if (mobileAppSettings.getId() != null) {
this.setId(mobileAppSettings.getId().getId());
}
this.setCreatedTime(mobileAppSettings.getCreatedTime());
this.tenantId = mobileAppSettings.getTenantId().getId();
this.useDefaultApp = mobileAppSettings.isUseDefaultApp();
if (mobileAppSettings.getAndroidConfig() != null) {
this.androidConfig = JacksonUtil.valueToTree(mobileAppSettings.getAndroidConfig());
}
if (mobileAppSettings.getIosConfig() != null) {
this.iosConfig = JacksonUtil.valueToTree(mobileAppSettings.getIosConfig());
}
if (mobileAppSettings.getQrCodeConfig() != null) {
this.qrCodeConfig = JacksonUtil.valueToTree(mobileAppSettings.getQrCodeConfig());
}
this.androidConfig = toJson(mobileAppSettings.getAndroidConfig());
this.iosConfig = toJson(mobileAppSettings.getIosConfig());
this.qrCodeConfig = toJson(mobileAppSettings.getQrCodeConfig());
}
@Override
public MobileAppSettings toData() {
MobileAppSettings mobileAppSettings = new MobileAppSettings();
MobileAppSettings mobileAppSettings = new MobileAppSettings(new MobileAppSettingsId(getUuid()));
mobileAppSettings.setCreatedTime(createdTime);
mobileAppSettings.setTenantId(TenantId.fromUUID(tenantId));
mobileAppSettings.setUseDefaultApp(useDefaultApp);
if (qrCodeConfig != null) {
mobileAppSettings.setAndroidConfig(JacksonUtil.convertValue(androidConfig, AndroidConfig.class));
}
if (qrCodeConfig != null) {
mobileAppSettings.setIosConfig(JacksonUtil.convertValue(iosConfig, IosConfig.class));
}
if (qrCodeConfig != null) {
mobileAppSettings.setQrCodeConfig(JacksonUtil.convertValue(qrCodeConfig, QRCodeConfig.class));
}
mobileAppSettings.setAndroidConfig(fromJson(androidConfig, AndroidConfig.class));
mobileAppSettings.setIosConfig(fromJson(iosConfig, IosConfig.class));
mobileAppSettings.setQrCodeConfig(fromJson(qrCodeConfig, QRCodeConfig.class));
return mobileAppSettings;
}
}

51
dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppSettingsDataValidator.java

@ -0,0 +1,51 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.service.validator;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.AndroidConfig;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
@Component
@AllArgsConstructor
public class MobileAppSettingsDataValidator extends DataValidator<MobileAppSettings> {
@Override
protected void validateDataImpl(TenantId tenantId, MobileAppSettings mobileAppSettings) {
AndroidConfig androidConfig = mobileAppSettings.getAndroidConfig();
IosConfig iosConfig = mobileAppSettings.getIosConfig();
QRCodeConfig qrCodeConfig = mobileAppSettings.getQrCodeConfig();
if (!mobileAppSettings.isUseDefaultApp() && (androidConfig == null || iosConfig == null)) {
throw new DataValidationException("Android/ios settings are required to use custom application!");
}
if (qrCodeConfig == null) {
throw new DataValidationException("Qr code config is required!");
}
if (androidConfig != null && androidConfig.isEnabled() &&
(androidConfig.getAppPackage() == null || androidConfig.getSha256CertFingerprints() == null)) {
throw new DataValidationException("Application package and sha256 cert fingerprints are required for enabled android settings!");
}
if (iosConfig != null && iosConfig.isEnabled() && iosConfig.getAppId() == null) {
throw new DataValidationException("Application id is required for enabled ios settings!");
}
}
}

22
dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppDao.java → dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppSettingsDao.java

@ -17,32 +17,31 @@ package org.thingsboard.server.dao.sql.mobile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.mobile.MobileAppSettingsDao;
import org.thingsboard.server.dao.model.sql.MobileAppSettingsEntity;
import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.UUID;
@Component
@Slf4j
@SqlDao
public class JpaMobileAppDao extends JpaAbstractDaoListeningExecutorService implements MobileAppSettingsDao {
public class JpaMobileAppSettingsDao extends JpaAbstractDao<MobileAppSettingsEntity, MobileAppSettings> implements MobileAppSettingsDao {
@Autowired
private MobileAppSettingsRepository mobileAppSettingsRepository;
@Override
public MobileAppSettings save(TenantId tenantId, MobileAppSettings mobileAppSettings) {
return DaoUtil.getData(mobileAppSettingsRepository.save(new MobileAppSettingsEntity(mobileAppSettings)));
}
@Override
public MobileAppSettings findByTenantId(TenantId tenantId) {
return DaoUtil.getData(mobileAppSettingsRepository.findById(tenantId.getId()));
return DaoUtil.getData(mobileAppSettingsRepository.findByTenantId(tenantId.getId()));
}
@Override
@ -50,4 +49,13 @@ public class JpaMobileAppDao extends JpaAbstractDaoListeningExecutorService impl
mobileAppSettingsRepository.deleteByTenantId(tenantId.getId());
}
@Override
protected Class<MobileAppSettingsEntity> getEntityClass() {
return MobileAppSettingsEntity.class;
}
@Override
protected JpaRepository<MobileAppSettingsEntity, UUID> getRepository() {
return mobileAppSettingsRepository;
}
}

2
dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppSettingsRepository.java

@ -27,6 +27,8 @@ import java.util.UUID;
public interface MobileAppSettingsRepository extends JpaRepository<MobileAppSettingsEntity, UUID> {
MobileAppSettingsEntity findByTenantId(@Param("tenantId") UUID tenantId);
@Transactional
@Modifying
@Query("DELETE FROM MobileAppSettingsEntity r WHERE r.tenantId = :tenantId")

4
dao/src/main/resources/sql/schema-entities.sql

@ -896,7 +896,9 @@ CREATE TABLE IF NOT EXISTS queue_stats (
);
CREATE TABLE IF NOT EXISTS mobile_app_settings (
tenant_id UUID NOT NULL,
id uuid NOT NULL CONSTRAINT mobile_app_settings_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid UNIQUE NOT NULL,
use_default_app boolean,
android_config VARCHAR(1000),
ios_config VARCHAR(1000),

Loading…
Cancel
Save