From d7582223f443e43a241ac11d27e74e5bcbf0c3fa Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 25 Oct 2024 18:39:28 +0300 Subject: [PATCH] moved 'enabled' from StoreInfo to QrCoodeSettings --- .../main/data/upgrade/3.8.1/schema_update.sql | 83 +++++++++++-------- .../controller/QrCodeSettingsController.java | 22 ++--- .../QrCodeSettingsControllerTest.java | 2 - .../housekeeper/HousekeeperServiceTest.java | 44 ++++++++++ .../common/data/mobile/app/StoreInfo.java | 1 - .../mobile/qrCodeSettings/QrCodeSettings.java | 6 +- .../dao/mobile/MobileAppServiceImpl.java | 2 +- .../dao/mobile/QrCodeSettingServiceImpl.java | 4 +- .../server/dao/model/ModelConstants.java | 2 + .../dao/model/sql/QrCodeSettingsEntity.java | 10 +++ .../validator/MobileAppDataValidator.java | 4 +- .../resources/sql/schema-entities-idx.sql | 2 + .../main/resources/sql/schema-entities.sql | 2 +- .../thingsboard/rest/client/RestClient.java | 19 +++-- 14 files changed, 132 insertions(+), 71 deletions(-) diff --git a/application/src/main/data/upgrade/3.8.1/schema_update.sql b/application/src/main/data/upgrade/3.8.1/schema_update.sql index 27e7cd1c0d..35f1c6d7ac 100644 --- a/application/src/main/data/upgrade/3.8.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.8.1/schema_update.sql @@ -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; diff --git a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java index 988b5a9f10..0eeb9de92c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -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 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 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; - } - } diff --git a/application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java index 2607c885b6..a1ab80ff8e 100644 --- a/application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java @@ -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); diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java index d3b1916def..c7f2e37211 100644 --- a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -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; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java index 414e60fc35..9903302592 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.validation.NoXss; @Builder public class StoreInfo { - private boolean enabled; @NoXss private String appId; @NoXss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QrCodeSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QrCodeSettings.java index ce30e9c8ed..9b62da3eb0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QrCodeSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QrCodeSettings.java @@ -44,10 +44,12 @@ public class QrCodeSettings extends BaseData 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; diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java index 9ebda98726..55c2fa0383 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java @@ -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; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java index b97fdeafd0..3f709b32ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java @@ -119,10 +119,10 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService { @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 { 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.setCreatedTime(createdTime); qrCodeSettings.setTenantId(TenantId.fromUUID(tenantId)); qrCodeSettings.setUseDefaultApp(useDefaultApp); + qrCodeSettings.setAndroidEnabled(androidEnabled); + qrCodeSettings.setIosEnabled(iosEnabled); if (mobileAppBundleId != null) { qrCodeSettings.setMobileAppBundleId(new MobileAppBundleId(mobileAppBundleId)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java index 0aa6ea3647..d91ea8251d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java @@ -31,12 +31,12 @@ public class MobileAppDataValidator extends DataValidator { @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"); } diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 6b4f1a6c09..04627e7b0c 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -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); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index e43594c9f0..3cb5ffc761 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -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 ( diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 1ac7dd51e3..a4a8e86b4d 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -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 getTenantOAuth2Clients() { + public PageData getTenantOAuth2Clients() { return restTemplate.exchange( baseURL + "/api/oauth2/client/infos", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference>() { + new ParameterizedTypeReference>() { }).getBody(); } @@ -2138,12 +2139,12 @@ public class RestClient implements Closeable { restTemplate.delete(baseURL + "/api/oauth2/client/{id}", oAuth2ClientId.getId()); } - public List getTenantDomainInfos() { + public PageData getTenantDomainInfos() { return restTemplate.exchange( baseURL + "/api/domain/infos", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference>() { + new ParameterizedTypeReference>() { }).getBody(); } @@ -2172,12 +2173,12 @@ public class RestClient implements Closeable { restTemplate.postForLocation(baseURL + "/api/domain/{id}/oauth2Clients", oauth2ClientIds, domainId.getId()); } - public List getTenantMobileApps() { + public PageData getTenantMobileApps() { return restTemplate.exchange( baseURL + "/api/mobile/app", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference>() { + new ParameterizedTypeReference>() { }).getBody(); } @@ -2202,18 +2203,18 @@ public class RestClient implements Closeable { restTemplate.delete(baseURL + "/api/mobile/app/{id}", mobileAppId.getId()); } - public List getTenantMobileBundleInfos() { + public PageData getTenantMobileBundleInfos() { return restTemplate.exchange( baseURL + "/api/mobile/bundle/infos", HttpMethod.GET, HttpEntity.EMPTY, - new ParameterizedTypeReference>() { + new ParameterizedTypeReference>() { }).getBody(); } public Optional getMobileBundleById(MobileAppBundleId mobileAppBundleId) { try { - ResponseEntity mobileApp = restTemplate.getForEntity(baseURL + "/api/mobile/app/{id}", MobileAppBundle.class, mobileAppBundleId.getId()); + ResponseEntity 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) {