moved 'enabled' from StoreInfo to QrCoodeSettings
This commit is contained in:
		
							parent
							
								
									c6396e4074
								
							
						
					
					
						commit
						d7582223f4
					
				@ -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,22 +123,28 @@ $$
 | 
			
		||||
            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 (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,
 | 
			
		||||
                            qrCodeRecord.android_config::jsonb ->> 'appPackage', 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage');
 | 
			
		||||
                                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, 'Autogenerated for qr code', androidAppId);
 | 
			
		||||
                    UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.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' 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;
 | 
			
		||||
                        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
 | 
			
		||||
                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
 | 
			
		||||
@ -145,14 +155,17 @@ $$
 | 
			
		||||
                        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;
 | 
			
		||||
                            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 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;
 | 
			
		||||
                        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;
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
@Builder
 | 
			
		||||
public class StoreInfo {
 | 
			
		||||
 | 
			
		||||
    private boolean enabled;
 | 
			
		||||
    @NoXss
 | 
			
		||||
    private String appId;
 | 
			
		||||
    @NoXss
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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 (
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user