moved 'enabled' from StoreInfo to QrCoodeSettings

This commit is contained in:
dashevchenko 2024-10-25 18:39:28 +03:00
parent c6396e4074
commit d7582223f4
14 changed files with 132 additions and 71 deletions

View File

@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS mobile_app_bundle (
CONSTRAINT fk_android_app_id FOREIGN KEY (android_app_id) REFERENCES mobile_app(id),
CONSTRAINT fk_ios_app_id FOREIGN KEY (ios_app_id) REFERENCES mobile_app(id)
);
CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id);
ALTER TABLE mobile_app ADD COLUMN IF NOT EXISTS platform_type varchar(32),
ADD COLUMN IF NOT EXISTS status varchar(32),
@ -82,31 +83,34 @@ $$
-- duplicate app for iOS platform type
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, app_secret, platform_type, status)
VALUES (iosAppId, (extract(epoch from now()) * 1000), mobileAppRecord.tenant_id, mobileAppRecord.pkg_name, mobileAppRecord.app_secret, 'IOS', mobileAppRecord.status)
VALUES (iosAppId, mobileAppRecord.created_time, mobileAppRecord.tenant_id, mobileAppRecord.pkg_name, mobileAppRecord.app_secret, 'IOS', mobileAppRecord.status)
ON CONFLICT DO NOTHING;
-- create bundle for android and iOS app
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id, ios_app_id, oauth2_enabled)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), mobileAppRecord.tenant_id,
'Autogenerated for ' || mobileAppRecord.pkg_name, mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled)
VALUES (generatedBundleId, mobileAppRecord.created_time, mobileAppRecord.tenant_id,
mobileAppRecord.pkg_name || ' (autogenerated)', mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled)
ON CONFLICT DO NOTHING;
UPDATE mobile_app_bundle_oauth2_client SET mobile_app_bundle_id = generatedBundleId WHERE mobile_app_bundle_id = mobileAppRecord.id;
END LOOP;
END IF;
ALTER TABLE mobile_app DROP COLUMN IF EXISTS oauth2_enabled;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'pkg_name_platform_unique') THEN
ALTER TABLE mobile_app ADD CONSTRAINT pkg_name_platform_unique UNIQUE (pkg_name, platform_type);
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'mobile_app_pkg_name_platform_unq_key') THEN
ALTER TABLE mobile_app ADD CONSTRAINT mobile_app_pkg_name_platform_unq_key UNIQUE (pkg_name, platform_type);
END IF;
END;
$$;
ALTER TABLE IF EXISTS mobile_app_settings RENAME TO qr_code_settings;
ALTER TABLE qr_code_settings ADD COLUMN IF NOT EXISTS mobile_app_bundle_id uuid;
ALTER TABLE qr_code_settings ADD COLUMN IF NOT EXISTS mobile_app_bundle_id uuid,
ADD COLUMN IF NOT EXISTS android_enabled boolean,
ADD COLUMN IF NOT EXISTS ios_enabled boolean;
-- migrate mobile apps from qr code settings to mobile_app, create mobile app bundle for the pair of apps
DO
$$
DECLARE
androidPkgName varchar;
iosPkgName varchar;
androidAppId uuid;
iosAppId uuid;
@ -119,40 +123,49 @@ $$
LOOP
generatedBundleId := NULL;
-- migrate android config
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = qrCodeRecord.android_config::jsonb ->> 'appPackage' AND platform_type = 'ANDROID';
IF androidAppId IS NULL THEN
androidAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
qrCodeRecord.android_config::jsonb ->> 'appPackage', 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage');
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, 'Autogenerated for qr code', androidAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.id;
ELSE
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' WHERE id = androidAppId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId) WHERE id = qrCodeRecord.id;
IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN
androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage';
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID';
IF androidAppId IS NULL THEN
androidAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
androidPkgName, 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled');
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
ELSE
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId),
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
END IF;
END IF;
-- migrate ios config
iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1);
SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS';
IF iosAppId IS NULL THEN
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (iosAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
iosPkgName, 'IOS', 'DRAFT', qrCodeRecord.ios_config);
IF generatedBundleId IS NULL THEN
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, 'Autogenerated for qr code', iosAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.id;
IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN
iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1);
SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS';
IF iosAppId IS NULL THEN
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (iosAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
iosPkgName, 'IOS', 'DRAFT', qrCodeRecord.ios_config);
IF generatedBundleId IS NULL THEN
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
ELSE
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
END IF;
ELSE
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId),
ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id;
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId;
END IF;
ELSE
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config WHERE id = iosAppId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId) WHERE id = qrCodeRecord.id;
END IF;
END LOOP;
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key;

View File

@ -95,14 +95,13 @@ public class QrCodeSettingsController extends BaseController {
private final SystemSecurityService systemSecurityService;
private final MobileAppSecretService mobileAppSecretService;
private final QrCodeSettingService qrCodeSettingService;
private final MobileAppService mobileAppService;
@ApiOperation(value = "Get associated android applications (getAssetLinks)")
@GetMapping(value = "/.well-known/assetlinks.json")
public ResponseEntity<JsonNode> getAssetLinks() {
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, ANDROID);
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
if (storeInfo != null && storeInfo.isEnabled() && storeInfo.getSha256CertFingerprints() != null) {
if (storeInfo != null && storeInfo.getSha256CertFingerprints() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, mobileApp.getPkgName(), storeInfo.getSha256CertFingerprints())));
} else {
return ResponseEntity.notFound().build();
@ -114,7 +113,7 @@ public class QrCodeSettingsController extends BaseController {
public ResponseEntity<JsonNode> getAppleAppSiteAssociation() {
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, IOS);
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
if (storeInfo != null && storeInfo.isEnabled() && storeInfo.getAppId() != null) {
if (storeInfo != null && storeInfo.getAppId() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, storeInfo.getAppId())));
} else {
return ResponseEntity.notFound().build();
@ -177,14 +176,13 @@ public class QrCodeSettingsController extends BaseController {
@GetMapping(value = "/api/noauth/qr")
public ResponseEntity<?> getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) {
QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID);
boolean useDefaultApp = qrCodeSettings.isUseDefaultApp();
if (userAgent.contains("Android")) {
String googlePlayLink = useDefaultApp ? qrCodeSettings.getGooglePlayLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), ANDROID);
if (userAgent.contains("Android") && qrCodeSettings.isAndroidEnabled()) {
String googlePlayLink = qrCodeSettings.getGooglePlayLink();
return ResponseEntity.status(HttpStatus.FOUND)
.header("Location", googlePlayLink)
.build();
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) {
String appStoreLink = useDefaultApp ? qrCodeSettings.getAppStoreLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), IOS);
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad") && qrCodeSettings.isIosEnabled()) {
String appStoreLink = qrCodeSettings.getAppStoreLink();
return ResponseEntity.status(HttpStatus.FOUND)
.header("Location", appStoreLink)
.build();
@ -193,12 +191,4 @@ public class QrCodeSettingsController extends BaseController {
}
}
private String getStoreLink(MobileAppBundleId mobileAppBundleId, PlatformType platformType) {
if (mobileAppBundleId == null) {
return null;
}
MobileApp mobileApp = mobileAppService.findByBundleIdAndPlatformType(TenantId.SYS_TENANT_ID, mobileAppBundleId, platformType);
return (mobileApp != null && mobileApp.getStoreInfo() != null) ? mobileApp.getStoreInfo().getStoreLink() : null;
}
}

View File

@ -70,7 +70,6 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
StoreInfo androidStoreInfo = StoreInfo.builder()
.sha256CertFingerprints(ANDROID_APP_SHA256)
.storeLink(ANDROID_STORE_LINK)
.enabled(true)
.build();
androidApp.setStoreInfo(androidStoreInfo);
MobileApp savedAndroidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
@ -78,7 +77,6 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
MobileApp iosApp = validMobileApp( "my.ios.package", PlatformType.IOS);
StoreInfo iosQrCodeConfig = StoreInfo.builder()
.appId(APPLE_APP_ID)
.enabled(true)
.storeLink(IOS_STORE_LINK)
.build();
iosApp.setStoreInfo(iosQrCodeConfig);

View File

@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
@ -45,6 +46,9 @@ import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
@ -54,7 +58,12 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
import org.thingsboard.server.common.data.mobile.app.MobileAppStatus;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -233,6 +242,27 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
tenantId = differentTenantId;
createRelatedData(tenantId);
MobileApp androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID);
androidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
MobileAppId androidAppId = androidApp.getId();
MobileApp iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS);
iosApp = doPost("/api/mobile/app", iosApp, MobileApp.class);
MobileAppId iosAppId = androidApp.getId();
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
OAuth2ClientId oAuth2ClientId = savedOAuth2Client.getId();
MobileAppBundle mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
mobileAppBundle.setAndroidAppId(androidApp.getId());
mobileAppBundle.setIosAppId(iosApp.getId());
MobileAppBundle savedAppBundle = doPost("/api/mobile/bundle?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileAppBundle, MobileAppBundle.class);
MobileAppBundleId appBundleId = savedAppBundle.getId();
createDifferentTenantCustomer();
createRelatedData(differentTenantCustomerId);
loginDifferentTenant();
@ -279,6 +309,10 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
verifyNoRelatedData(userId);
verifyNoRelatedData(differentTenantCustomerId);
verifyNoRelatedData(tenantApiUsageState.getId());
verifyNoRelatedData(androidAppId);
verifyNoRelatedData(iosAppId);
verifyNoRelatedData(oAuth2ClientId);
verifyNoRelatedData(appBundleId);
verifyNoRelatedData(tenantId);
});
}
@ -483,4 +517,14 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId);
}
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setTenantId(tenantId);
mobileApp.setStatus(MobileAppStatus.DRAFT);
mobileApp.setPkgName(mobileAppName);
mobileApp.setPlatformType(platformType);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
return mobileApp;
}
}

View File

@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.validation.NoXss;
@Builder
public class StoreInfo {
private boolean enabled;
@NoXss
private String appId;
@NoXss

View File

@ -44,10 +44,12 @@ public class QrCodeSettings extends BaseData<QrCodeSettingsId> implements HasTen
@Valid
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "QR code config configuration.")
private QRCodeConfig qrCodeConfig;
@Schema(description = "Indicates if google play link is available", example = "true")
private boolean androidEnabled;
@Schema(description = "Indicates if apple store link is available", example = "true")
private boolean iosEnabled;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String googlePlayLink;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String appStoreLink;

View File

@ -56,7 +56,7 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
return savedMobileApp;
} catch (Exception e) {
checkConstraintViolation(e,
Map.of("mobile_app_unq_key", "Mobile app with such package already exists!"));
Map.of("mobile_app_pkg_name_platform_unq_key", "Mobile app with such package name and platform already exists!"));
throw e;
}
}

View File

@ -119,10 +119,10 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService<Tenant
} else {
MobileApp androidApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), ANDROID);
MobileApp iosApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), IOS);
if (androidApp != null && androidApp.getStoreInfo() != null && androidApp.getStoreInfo().isEnabled()) {
if (androidApp != null && androidApp.getStoreInfo() != null) {
qrCodeSettings.setGooglePlayLink(androidApp.getStoreInfo().getStoreLink());
}
if (iosApp != null && iosApp.getStoreInfo() != null && iosApp.getStoreInfo().isEnabled()) {
if (iosApp != null && iosApp.getStoreInfo() != null) {
qrCodeSettings.setAppStoreLink(iosApp.getStoreInfo().getStoreLink());
}
}

View File

@ -701,6 +701,8 @@ public class ModelConstants {
*/
public static final String QR_CODE_SETTINGS_TABLE_NAME = "qr_code_settings";
public static final String QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY = "use_default_app";
public static final String QR_CODE_SETTINGS_ANDROID_ENABLED_PROPERTY = "android_enabled";
public static final String QR_CODE_SETTINGS_IOS_ENABLED_PROPERTY = "ios_enabled";
public static final String QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY = "mobile_app_bundle_id";
public static final String QR_CODE_SETTINGS_CONFIG_PROPERTY = "qr_code_config";

View File

@ -47,6 +47,12 @@ public class QrCodeSettingsEntity extends BaseSqlEntity<QrCodeSettings> {
@Column(name = ModelConstants.QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY)
private boolean useDefaultApp;
@Column(name = ModelConstants.QR_CODE_SETTINGS_ANDROID_ENABLED_PROPERTY)
private boolean androidEnabled;
@Column(name = ModelConstants.QR_CODE_SETTINGS_IOS_ENABLED_PROPERTY)
private boolean iosEnabled;
@Column(name = ModelConstants.QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY)
private UUID mobileAppBundleId;
@ -59,6 +65,8 @@ public class QrCodeSettingsEntity extends BaseSqlEntity<QrCodeSettings> {
this.setCreatedTime(qrCodeSettings.getCreatedTime());
this.tenantId = qrCodeSettings.getTenantId().getId();
this.useDefaultApp = qrCodeSettings.isUseDefaultApp();
this.androidEnabled = qrCodeSettings.isAndroidEnabled();
this.iosEnabled = qrCodeSettings.isIosEnabled();
if (qrCodeSettings.getMobileAppBundleId() != null) {
this.mobileAppBundleId = qrCodeSettings.getMobileAppBundleId().getId();
}
@ -71,6 +79,8 @@ public class QrCodeSettingsEntity extends BaseSqlEntity<QrCodeSettings> {
qrCodeSettings.setCreatedTime(createdTime);
qrCodeSettings.setTenantId(TenantId.fromUUID(tenantId));
qrCodeSettings.setUseDefaultApp(useDefaultApp);
qrCodeSettings.setAndroidEnabled(androidEnabled);
qrCodeSettings.setIosEnabled(iosEnabled);
if (mobileAppBundleId != null) {
qrCodeSettings.setMobileAppBundleId(new MobileAppBundleId(mobileAppBundleId));
}

View File

@ -31,12 +31,12 @@ public class MobileAppDataValidator extends DataValidator<MobileApp> {
@Override
protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) {
if (mobileApp.getPlatformType() == PlatformType.ANDROID) {
if (mobileApp.getStoreInfo() != null && mobileApp.getStoreInfo().isEnabled() &&
if (mobileApp.getStoreInfo() != null &&
(mobileApp.getStoreInfo().getSha256CertFingerprints() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
throw new DataValidationException("Sha256CertFingerprints and store link are required");
}
} else if (mobileApp.getPlatformType() == PlatformType.IOS) {
if (mobileApp.getStoreInfo() != null && mobileApp.getStoreInfo().isEnabled() &&
if (mobileApp.getStoreInfo() != null &&
(mobileApp.getStoreInfo().getAppId() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
throw new DataValidationException("AppId and store link are required");
}

View File

@ -127,3 +127,5 @@ CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag);
CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag);
CREATE INDEX IF NOT EXISTS idx_resource_type_public_resource_key ON resource(resource_type, public_resource_key);
CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id);

View File

@ -628,7 +628,7 @@ CREATE TABLE IF NOT EXISTS mobile_app (
status varchar(32),
version_info varchar(100000),
store_info varchar(16384),
CONSTRAINT pkg_name_platform_unique UNIQUE (pkg_name, platform_type)
CONSTRAINT mobile_app_pkg_name_platform_unq_key UNIQUE (pkg_name, platform_type)
);
CREATE TABLE IF NOT EXISTS mobile_app_bundle (

View File

@ -126,6 +126,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
@ -2108,12 +2109,12 @@ public class RestClient implements Closeable {
}, params).getBody();
}
public List<OAuth2ClientInfo> getTenantOAuth2Clients() {
public PageData<OAuth2ClientInfo> getTenantOAuth2Clients() {
return restTemplate.exchange(
baseURL + "/api/oauth2/client/infos",
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<List<OAuth2ClientInfo>>() {
new ParameterizedTypeReference<PageData<OAuth2ClientInfo>>() {
}).getBody();
}
@ -2138,12 +2139,12 @@ public class RestClient implements Closeable {
restTemplate.delete(baseURL + "/api/oauth2/client/{id}", oAuth2ClientId.getId());
}
public List<DomainInfo> getTenantDomainInfos() {
public PageData<DomainInfo> getTenantDomainInfos() {
return restTemplate.exchange(
baseURL + "/api/domain/infos",
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<List<DomainInfo>>() {
new ParameterizedTypeReference<PageData<DomainInfo>>() {
}).getBody();
}
@ -2172,12 +2173,12 @@ public class RestClient implements Closeable {
restTemplate.postForLocation(baseURL + "/api/domain/{id}/oauth2Clients", oauth2ClientIds, domainId.getId());
}
public List<DomainInfo> getTenantMobileApps() {
public PageData<MobileApp> getTenantMobileApps() {
return restTemplate.exchange(
baseURL + "/api/mobile/app",
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<List<DomainInfo>>() {
new ParameterizedTypeReference<PageData<MobileApp>>() {
}).getBody();
}
@ -2202,18 +2203,18 @@ public class RestClient implements Closeable {
restTemplate.delete(baseURL + "/api/mobile/app/{id}", mobileAppId.getId());
}
public List<DomainInfo> getTenantMobileBundleInfos() {
public PageData<MobileAppBundleInfo> getTenantMobileBundleInfos() {
return restTemplate.exchange(
baseURL + "/api/mobile/bundle/infos",
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<List<DomainInfo>>() {
new ParameterizedTypeReference<PageData<MobileAppBundleInfo>>() {
}).getBody();
}
public Optional<MobileAppBundle> getMobileBundleById(MobileAppBundleId mobileAppBundleId) {
try {
ResponseEntity<MobileAppBundle> mobileApp = restTemplate.getForEntity(baseURL + "/api/mobile/app/{id}", MobileAppBundle.class, mobileAppBundleId.getId());
ResponseEntity<MobileAppBundle> mobileApp = restTemplate.getForEntity(baseURL + "/api/mobile/bundle/{id}", MobileAppBundle.class, mobileAppBundleId.getId());
return Optional.ofNullable(mobileApp.getBody());
} catch (HttpClientErrorException exception) {
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {