diff --git a/application/src/main/data/upgrade/3.8.0/schema_update.sql b/application/src/main/data/upgrade/3.8.0/schema_update.sql index 6b87dc6dde..f9fd1e9181 100644 --- a/application/src/main/data/upgrade/3.8.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.8.0/schema_update.sql @@ -14,3 +14,132 @@ -- limitations under the License. -- +-- CREATE MOBILE APP BUNDLES FROM EXISTING APPS + +CREATE TABLE IF NOT EXISTS mobile_app_bundle ( + id uuid NOT NULL CONSTRAINT mobile_app_bundle_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid, + title varchar(255), + android_app_id uuid, + ios_app_id uuid, + description varchar(1024), + layout_config varchar(16384), + oauth2_enabled boolean, + CONSTRAINT android_app_id_unq_key UNIQUE (android_app_id), + CONSTRAINT ios_app_id_unq_key UNIQUE (ios_app_id), + 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) +); + +ALTER TABLE mobile_app ADD COLUMN IF NOT EXISTS platform_type varchar(32), + ADD COLUMN IF NOT EXISTS status varchar(32), + ADD COLUMN IF NOT EXISTS version_info varchar(16384), + ADD COLUMN IF NOT EXISTS qr_code_config varchar(16384), + DROP CONSTRAINT IF EXISTS mobile_app_pkg_name_key; + +-- rename mobile_app_oauth2_client to mobile_app_bundle_oauth2_client +DO +$$ + BEGIN + -- in case of running the upgrade script a second time + IF EXISTS(SELECT * FROM information_schema.tables WHERE table_name='mobile_app_oauth2_client') THEN + ALTER TABLE mobile_app_oauth2_client RENAME TO mobile_app_bundle_oauth2_client; + ALTER TABLE mobile_app_bundle_oauth2_client DROP CONSTRAINT IF EXISTS fk_domain; + ALTER TABLE mobile_app_bundle_oauth2_client RENAME COLUMN mobile_app_id TO mobile_app_bundle_id; + END IF; + END; +$$; + + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- duplicate each mobile app and create mobile app bundle for the pair of android and ios app +DO +$$ + DECLARE + generatedBundleId uuid; + iosAppId uuid; + mobileAppRecord RECORD; + BEGIN + -- in case of running the upgrade script a second time + IF EXISTS(SELECT * FROM information_schema.columns WHERE table_name='mobile_app' and column_name='oauth2_enabled') THEN + UPDATE mobile_app SET platform_type = 'ANDROID' WHERE platform_type IS NULL; + UPDATE mobile_app SET status = 'PUBLISHED' WHERE mobile_app.status IS NULL; + FOR mobileAppRecord IN SELECT * FROM mobile_app + LOOP + -- 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); + -- 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, + 'App bundle ' || mobileAppRecord.pkg_name, mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled); + 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_platform_unique') THEN + ALTER TABLE mobile_app ADD CONSTRAINT pkg_platform_unique 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; + +-- migrate mobile apps from qr code settings to mobile_app, create mobile app bundle for the pair of apps +DO +$$ + DECLARE + iosPkgName varchar; + androidAppId uuid; + iosAppId uuid; + generatedBundleId uuid; + qrCodeRecord RECORD; + BEGIN + -- in case of running the upgrade script a second time + IF EXISTS(SELECT * FROM information_schema.columns WHERE table_name = 'qr_code_settings' and column_name = 'android_config') THEN + FOR qrCodeRecord IN SELECT * FROM qr_code_settings + LOOP + -- 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, qr_code_config) + VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, + qrCodeRecord.android_config::jsonb ->> 'appPackage', 'ANDROID', 'PUBLISHED', qrCodeRecord.android_config::jsonb || '{"type": "ANDROID"}'::jsonb); + 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, 'App bundle for qr code', androidAppId); + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.id; + ELSE + UPDATE mobile_app SET qr_code_config = qrCodeRecord.android_config::jsonb || '{"type": "ANDROID"}'::jsonb WHERE id = androidAppId; + 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, qr_code_config) + VALUES (iosAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, + iosPkgName, 'IOS', 'PUBLISHED', qrCodeRecord.ios_config::jsonb || '{"type": "IOS"}'::jsonb); + 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, 'App bundle for qr code', iosAppId); + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.id; + ELSE + UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId; + END IF; + ELSE + UPDATE mobile_app SET qr_code_config = qrCodeRecord.ios_config::jsonb || '{"type": "IOS"}'::jsonb WHERE id = iosAppId; + END IF; + END LOOP; + END IF; + ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config; + END; +$$; diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java b/application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java index f54314bb9a..0853cb9eb2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java @@ -106,7 +106,7 @@ public class MobileAppBundleController extends BaseController { @RequestParam(required = false) String sortProperty, @Parameter(description = SORT_ORDER_DESCRIPTION) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.MOBILE_APP, Operation.READ); + accessControlService.checkPermission(getCurrentUser(), Resource.MOBILE_APP_BUNDLE, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return mobileAppBundleService.findMobileAppBundleInfosByTenantId(getTenantId(), pageLink); } diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java b/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java index b19267d44a..4fe2d4cd2a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java @@ -76,16 +76,16 @@ public class MobileAppController extends BaseController { @ApiOperation(value = "Get mobile app infos (getTenantMobileAppInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @GetMapping(value = "/mobile/app") - public PageData getTenantMobileAppInfos(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) - @RequestParam int pageSize, - @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) - @RequestParam int page, - @Parameter(description = "Case-insensitive 'substring' filter based on app's name") - @RequestParam(required = false) String textSearch, - @Parameter(description = SORT_PROPERTY_DESCRIPTION) - @RequestParam(required = false) String sortProperty, - @Parameter(description = SORT_ORDER_DESCRIPTION) - @RequestParam(required = false) String sortOrder) throws ThingsboardException { + public PageData getTenantMobileApps(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @Parameter(description = "Case-insensitive 'substring' filter based on app's name") + @RequestParam(required = false) String textSearch, + @Parameter(description = SORT_PROPERTY_DESCRIPTION) + @RequestParam(required = false) String sortProperty, + @Parameter(description = SORT_ORDER_DESCRIPTION) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.MOBILE_APP, Operation.READ); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return mobileAppService.findMobileAppsByTenantId(getTenantId(), pageLink); @@ -94,7 +94,7 @@ public class MobileAppController extends BaseController { @ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @GetMapping(value = "/mobile/app/{id}") - public MobileApp getMobileAppInfoById(@PathVariable UUID id) throws ThingsboardException { + public MobileApp getMobileAppById(@PathVariable UUID id) throws ThingsboardException { MobileAppId mobileAppId = new MobileAppId(id); return checkEntityId(mobileAppId, mobileAppService::findMobileAppById, Operation.READ); } diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java index 1f8cb1da77..b06d280b9a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.HomeDashboardInfo; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.mobile.MobileAppBundle; -import org.thingsboard.server.common.data.mobile.MobileLoginInfo; -import org.thingsboard.server.common.data.mobile.MobileUserInfo; +import org.thingsboard.server.common.data.mobile.LoginMobileInfo; +import org.thingsboard.server.common.data.mobile.UserMobileInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -40,17 +40,17 @@ import java.util.List; public class MobileV2Controller extends BaseController { @GetMapping(value = "/api/noauth/mobile") - public MobileLoginInfo getMobileUserLoginSettings(@Parameter(description = "Mobile application package name") + public LoginMobileInfo getMobileUserLoginSettings(@Parameter(description = "Mobile application package name") @RequestParam String pkgName, @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) @RequestParam PlatformType platform) { List oauth2Clients = oAuth2ClientService.findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(pkgName, platform); - return new MobileLoginInfo(oauth2Clients); + return new LoginMobileInfo(oauth2Clients); } @GetMapping(value = "/api/auth/mobile") - public MobileUserInfo getMobileUserSettings(@Parameter(description = "Mobile application package name") + public UserMobileInfo getMobileUserSettings(@Parameter(description = "Mobile application package name") @RequestParam String pkgName, @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) @@ -59,7 +59,7 @@ public class MobileV2Controller extends BaseController { User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); HomeDashboardInfo homeDashboardInfo = getHomeDashboardInfo(securityUser, user.getAdditionalInfo()); MobileAppBundle mobileAppBundle = mobileAppBundleService.findMobileAppBundleByPkgNameAndPlatform(securityUser.getTenantId(), pkgName, platform); - return new MobileUserInfo(user, homeDashboardInfo, mobileAppBundle != null ? mobileAppBundle.getLayoutConfig() : null); + return new UserMobileInfo(user, homeDashboardInfo, mobileAppBundle != null ? mobileAppBundle.getLayoutConfig() : null); } } 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 1de38b5671..c1ce037a11 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -31,10 +31,13 @@ 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.MobileAppBundleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.AndroidQrCodeConfig; import org.thingsboard.server.common.data.mobile.IosQrCodeConfig; +import org.thingsboard.server.common.data.mobile.MobileApp; import org.thingsboard.server.common.data.mobile.QrCodeSettings; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.dao.mobile.MobileAppService; @@ -49,6 +52,8 @@ import org.thingsboard.server.service.security.system.SystemSecurityService; import java.net.URI; import java.net.URISyntaxException; +import static org.thingsboard.server.common.data.oauth2.PlatformType.ANDROID; +import static org.thingsboard.server.common.data.oauth2.PlatformType.IOS; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; @@ -96,7 +101,7 @@ public class QrCodeSettingsController extends BaseController { @ApiOperation(value = "Get associated android applications (getAssetLinks)") @GetMapping(value = "/.well-known/assetlinks.json") public ResponseEntity getAssetLinks() { - AndroidQrCodeConfig androidQrConfig = qrCodeSettingService.getAndroidQrCodeConfig(TenantId.SYS_TENANT_ID); + AndroidQrCodeConfig androidQrConfig = (AndroidQrCodeConfig) qrCodeSettingService.findAppQrCodeConfig(TenantId.SYS_TENANT_ID, ANDROID); if (androidQrConfig != null && androidQrConfig.isEnabled()) { return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, androidQrConfig.getAppPackage(), androidQrConfig.getSha256CertFingerprints()))); } else { @@ -107,7 +112,7 @@ public class QrCodeSettingsController extends BaseController { @ApiOperation(value = "Get associated ios applications (getAppleAppSiteAssociation)") @GetMapping(value = "/.well-known/apple-app-site-association") public ResponseEntity getAppleAppSiteAssociation() { - IosQrCodeConfig iosQrCodeConfig = qrCodeSettingService.getIosQrCodeConfig(TenantId.SYS_TENANT_ID); + IosQrCodeConfig iosQrCodeConfig = (IosQrCodeConfig) qrCodeSettingService.findAppQrCodeConfig(TenantId.SYS_TENANT_ID, IOS); if (iosQrCodeConfig != null && iosQrCodeConfig.isEnabled() && iosQrCodeConfig.getAppId() != null) { return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, iosQrCodeConfig.getAppId()))); } else { @@ -173,14 +178,12 @@ public class QrCodeSettingsController extends BaseController { QrCodeSettings qrCodeSettings = qrCodeSettingService.getQrCodeSettings(TenantId.SYS_TENANT_ID); boolean useDefaultApp = qrCodeSettings.isUseDefaultApp(); if (userAgent.contains("Android")) { - String googlePlayLink = useDefaultApp ? qrCodeSettings.getDefaultGooglePlayLink() : - mobileAppService.findAndroidQrCodeConfig(TenantId.SYS_TENANT_ID, qrCodeSettings.getMobileAppBundleId()).getStoreLink(); + String googlePlayLink = useDefaultApp ? qrCodeSettings.getDefaultGooglePlayLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), ANDROID); return ResponseEntity.status(HttpStatus.FOUND) .header("Location", googlePlayLink) .build(); } else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) { - String appStoreLink = useDefaultApp ? qrCodeSettings.getDefaultAppStoreLink() : - mobileAppService.findIosQrCodeConfig(TenantId.SYS_TENANT_ID, qrCodeSettings.getMobileAppBundleId()).getStoreLink(); + String appStoreLink = useDefaultApp ? qrCodeSettings.getDefaultAppStoreLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), IOS); return ResponseEntity.status(HttpStatus.FOUND) .header("Location", appStoreLink) .build(); @@ -189,4 +192,12 @@ 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.getQrCodeConfig() != null) ? mobileApp.getQrCodeConfig().getStoreLink() : null; + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java index 26c1a207e2..b81ee82ecd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java @@ -32,7 +32,10 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -64,15 +67,15 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { @After public void tearDown() throws Exception { - PageData pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0)); - for (MobileApp mobileApp : pageData.getData()) { - doDelete("/api/mobile/app/" + mobileApp.getId().getId()) + PageData pageData2 = doGetTypedWithPageLink("/api/mobile/bundle/infos?", PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF, new PageLink(10, 0)); + for (MobileAppBundleInfo appBundleInfo : pageData2.getData()) { + doDelete("/api/mobile/bundle/" + appBundleInfo.getId().getId()) .andExpect(status().isOk()); } - PageData pageData2 = doGetTypedWithPageLink("/api/mobile/bundle?", PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF, new PageLink(10, 0)); - for (MobileAppBundleInfo appBundleInfo : pageData2.getData()) { - doDelete("/api/mobile/bundle/" + appBundleInfo.getId().getId()) + PageData pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0)); + for (MobileApp mobileApp : pageData.getData()) { + doDelete("/api/mobile/app/" + mobileApp.getId().getId()) .andExpect(status().isOk()); } @@ -84,7 +87,7 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { } @Test - public void testSaveMobileApp() throws Exception { + public void testSaveMobileAppBundle() { MobileAppBundle mobileAppBundle = new MobileAppBundle(); mobileAppBundle.setTitle("Test bundle"); mobileAppBundle.setAndroidAppId(androidApp.getId()); @@ -97,7 +100,7 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { @Test - public void testUpdateMobileAppOauth2Clients() throws Exception { + public void testUpdateMobileAppBundleOauth2Clients() throws Exception { MobileAppBundle mobileAppBundle = new MobileAppBundle(); mobileAppBundle.setTitle("Test bundle"); mobileAppBundle.setAndroidAppId(androidApp.getId()); @@ -113,11 +116,14 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { doPut("/api/mobile/bundle/" + savedAppBundle.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId())); - MobileAppBundleInfo retrievedMobileAppInfo = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedAppBundle.getId().getId()); - assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppBundleInfo(savedAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), List.of(new OAuth2ClientInfo(oAuth2Client)))); + MobileAppBundleInfo retrievedMobileAppBundleInfo = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedAppBundle.getId().getId()); + assertThat(retrievedMobileAppBundleInfo).isEqualTo(new MobileAppBundleInfo(savedAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), + Stream.of(new OAuth2ClientInfo(savedOAuth2Client), new OAuth2ClientInfo(savedOAuth2Client2)) + .sorted(Comparator.comparing(OAuth2ClientInfo::getTitle)).collect(Collectors.toList()) + )); doPut("/api/mobile/bundle/" + savedAppBundle.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId())); - MobileAppBundleInfo retrievedMobileAppInfo2 = doGet("/api/mobileApp/info/{id}", MobileAppBundleInfo.class, savedOAuth2Client.getId().getId()); + MobileAppBundleInfo retrievedMobileAppInfo2 = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedAppBundle.getId().getId()); assertThat(retrievedMobileAppInfo2).isEqualTo(new MobileAppBundleInfo(savedAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), List.of(new OAuth2ClientInfo(savedOAuth2Client2)))); } @@ -133,7 +139,7 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { MobileAppBundle savedMobileAppBundle = doPost("/api/mobile/bundle?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileAppBundle, MobileAppBundle.class); - MobileAppBundleInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppBundleInfo.class, savedMobileAppBundle.getId().getId()); + MobileAppBundleInfo retrievedMobileAppInfo = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedMobileAppBundle.getId().getId()); assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppBundleInfo(savedMobileAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), List.of(new OAuth2ClientInfo(savedOAuth2Client)))); } 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 2ed4933d2b..88949a7e11 100644 --- a/application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java @@ -107,17 +107,17 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest { @After public void tearDown() throws Exception { - PageData pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0)); - for (MobileApp mobileApp : pageData.getData()) { - doDelete("/api/mobile/app/" + mobileApp.getId().getId()) - .andExpect(status().isOk()); - } - + loginSysAdmin(); PageData pageData2 = doGetTypedWithPageLink("/api/mobile/bundle/infos?", PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF, new PageLink(10, 0)); for (MobileAppBundleInfo appBundleInfo : pageData2.getData()) { doDelete("/api/mobile/bundle/" + appBundleInfo.getId().getId()) .andExpect(status().isOk()); } + PageData pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0)); + for (MobileApp mobileApp : pageData.getData()) { + doDelete("/api/mobile/app/" + mobileApp.getId().getId()) + .andExpect(status().isOk()); + } } @Test @@ -148,7 +148,7 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest { doPost("/api/qr/settings", qrCodeSettings) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Bundle required to use custom application!"))); + .andExpect(statusReason(containsString("Mobile app bundle is required to use custom application!"))); qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId()); doPost("/api/qr/settings", qrCodeSettings) @@ -175,7 +175,8 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest { public void testGetApplicationAssociations() throws Exception { loginSysAdmin(); QrCodeSettings qrCodeSettings = doGet("/api/qr/settings", QrCodeSettings.class); - qrCodeSettings.setUseDefaultApp(false); + qrCodeSettings.setUseDefaultApp(true); + qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId()); doPost("/api/qr/settings", qrCodeSettings) .andExpect(status().isOk()); @@ -226,7 +227,8 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest { loginSysAdmin(); QrCodeSettings qrCodeSettings = doGet("/api/qr/settings", QrCodeSettings.class); qrCodeSettings.setUseDefaultApp(false); - doPost("/api/mobile/app/settings", qrCodeSettings); + qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId()); + doPost("/api/qr/settings", qrCodeSettings); String customAppDeepLink = doGet("/api/qr/deepLink", String.class); Pattern customAppExpectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)\""); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java index 45e404d4af..ba1c7ae3ef 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java @@ -43,5 +43,5 @@ public interface MobileAppBundleService extends EntityDaoService { MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform); - void deleteMobileAppsByTenantId(TenantId tenantId); + void deleteMobileAppBundlesByTenantId(TenantId tenantId); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java index 57be235577..01ebe81d30 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java @@ -18,9 +18,8 @@ package org.thingsboard.server.dao.mobile; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.MobileAppId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.mobile.AndroidQrCodeConfig; -import org.thingsboard.server.common.data.mobile.IosQrCodeConfig; import org.thingsboard.server.common.data.mobile.MobileApp; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.EntityDaoService; @@ -29,15 +28,13 @@ public interface MobileAppService extends EntityDaoService { MobileApp saveMobileApp(TenantId tenantId, MobileApp mobileApp); - void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId); - MobileApp findMobileAppById(TenantId tenantId, MobileAppId mobileAppId); PageData findMobileAppsByTenantId(TenantId tenantId, PageLink pageLink); - AndroidQrCodeConfig findAndroidQrCodeConfig(TenantId tenantId, MobileAppBundleId mobileAppBundleId); + MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType); - IosQrCodeConfig findIosQrCodeConfig(TenantId tenantId, MobileAppBundleId mobileAppBundleId); + void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId); void deleteMobileAppsByTenantId(TenantId tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileLoginInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/LoginMobileInfo.java similarity index 82% rename from common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileLoginInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/mobile/LoginMobileInfo.java index 2d9717f279..f146291123 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileLoginInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/LoginMobileInfo.java @@ -15,14 +15,9 @@ */ package org.thingsboard.server.common.data.mobile; -import lombok.AllArgsConstructor; -import lombok.Data; import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo; import java.util.List; -@Data -@AllArgsConstructor -public class MobileLoginInfo { - List oAuth2ClientLoginInfos; +public record LoginMobileInfo(List oAuth2ClientLoginInfos) { } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java index 018d79b965..5e28815908 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java @@ -16,7 +16,6 @@ package org.thingsboard.server.common.data.mobile; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; @@ -31,7 +30,6 @@ import org.thingsboard.server.common.data.id.MobileAppId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.validation.Length; -import org.thingsboard.server.common.data.validation.NoXss; @EqualsAndHashCode(callSuper = true) @Data @@ -53,9 +51,8 @@ public class MobileApp extends BaseData implements HasTenantId, Has @Schema(description = "Application status: PUBLISHED, DEPRECATED, SUSPENDED", requiredMode = Schema.RequiredMode.REQUIRED) private MobileAppStatus status; @Schema(description = "Application version info") - @NoXss - @Length(fieldName = "versionInfo", max = 16384) - private JsonNode versionInfo; + @Valid + private MobileAppVersionInfo versionInfo; @Schema(description = "Application qr code configuration") @Valid private QrCodeConfig qrCodeConfig; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppBundleInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppBundleInfo.java index 64f3396d56..ec076c1007 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppBundleInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppBundleInfo.java @@ -35,6 +35,12 @@ public class MobileAppBundleInfo extends MobileAppBundle { @Schema(description = "List of available oauth2 clients") private List oauth2ClientInfos; + public MobileAppBundleInfo(MobileAppBundle mobileApp, String androidPkgName, String iosPkgName) { + super(mobileApp); + this.androidPkgName = androidPkgName; + this.iosPkgName = iosPkgName; + } + public MobileAppBundleInfo(MobileAppBundle mobileApp, String androidPkgName, String iosPkgName, List oauth2ClientInfos) { super(mobileApp); this.androidPkgName = androidPkgName; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppVersionInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppVersionInfo.java new file mode 100644 index 0000000000..948955f315 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppVersionInfo.java @@ -0,0 +1,49 @@ +/** + * 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.mobile; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.validation.Length; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class MobileAppVersionInfo { + + @Schema(description = "Minimum supported version") + @Length(fieldName = "minVersion", max = 20) + private String minVersion; + + @Schema(description = "Release notes of minimum supported version") + @Length(fieldName = "minVersionReleaseNotes", max = 10000) + private String minVersionReleaseNotes; + + @Schema(description = "Latest supported version") + @Length(fieldName = "latestVersion", max = 20) + private String latestVersion; + + @Schema(description = "Release notes of latest supported version") + @Length(fieldName = "latestVersionReleaseNotes", max = 10000) + private String latestVersionReleaseNotes; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/QrCodeConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/QrCodeConfig.java index 01f4eaa98a..167b2c6a91 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/QrCodeConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/QrCodeConfig.java @@ -33,5 +33,6 @@ public interface QrCodeConfig { PlatformType getType(); + String getStoreLink(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java index 21f72dd4db..671e9ec271 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java @@ -15,13 +15,8 @@ */ package org.thingsboard.server.common.data.mobile; -import lombok.Data; - -import java.util.Map; - -@Data -public class UserMobileInfo { - - private Map sessions; +import org.thingsboard.server.common.data.HomeDashboardInfo; +import org.thingsboard.server.common.data.User; +public record UserMobileInfo(User user, HomeDashboardInfo homeDashboardInfo, MobileLayoutConfig layoutConfig) { } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileUserInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileSessionInfo.java similarity index 70% rename from common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileUserInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileSessionInfo.java index 1d078ab6d1..414059a92c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileUserInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileSessionInfo.java @@ -15,15 +15,13 @@ */ package org.thingsboard.server.common.data.mobile; -import lombok.AllArgsConstructor; import lombok.Data; -import org.thingsboard.server.common.data.HomeDashboardInfo; -import org.thingsboard.server.common.data.User; + +import java.util.Map; @Data -@AllArgsConstructor -public class MobileUserInfo { - User user; - HomeDashboardInfo homeDashboardInfo; - MobileLayoutConfig layoutConfig; +public class UserMobileSessionInfo { + + private Map sessions; + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java index b376a0096f..7b801a3c30 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.mobile; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.MobileAppBundle; +import org.thingsboard.server.common.data.mobile.MobileAppBundleInfo; import org.thingsboard.server.common.data.mobile.MobileAppBundleOauth2Client; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; @@ -28,13 +29,15 @@ import java.util.List; public interface MobileAppBundleDao extends Dao { - PageData findByTenantId(TenantId tenantId, PageLink pageLink); + PageData findInfosByTenantId(TenantId tenantId, PageLink pageLink); + + MobileAppBundleInfo findInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); List findOauth2ClientsByMobileAppBundleId(TenantId tenantId, MobileAppBundleId mobileAppBundleId); - void addOauth2Client(MobileAppBundleOauth2Client mobileAppBundleOauth2Client); + void addOauth2Client(TenantId tenantId, MobileAppBundleOauth2Client mobileAppBundleOauth2Client); - void removeOauth2Client(MobileAppBundleOauth2Client mobileAppBundleOauth2Client); + void removeOauth2Client(TenantId tenantId, MobileAppBundleOauth2Client mobileAppBundleOauth2Client); MobileAppBundle findByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform); diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java index b67ad2cac3..388e9cb623 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java @@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.OAuth2ClientId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.mobile.MobileApp; import org.thingsboard.server.common.data.mobile.MobileAppBundle; import org.thingsboard.server.common.data.mobile.MobileAppBundleInfo; import org.thingsboard.server.common.data.mobile.MobileAppBundleOauth2Client; @@ -53,8 +52,6 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements private OAuth2ClientDao oauth2ClientDao; @Autowired private MobileAppBundleDao mobileAppBundleDao; - @Autowired - private MobileAppService mobileAppService; @Override public MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileApp) { @@ -73,9 +70,18 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements @Override public void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) { - log.trace("Executing deleteMobileAppById [{}]", mobileAppBundleId.getId()); + log.trace("Executing deleteMobileAppBundleById [{}]", mobileAppBundleId.getId()); mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId()); eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build()); + + try { + mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build()); + } catch (Exception e) { + checkConstraintViolation(e, "fk_android_app_id", "The mobile app referenced by the mobile bundle cannot be deleted!", + "fk_ios_app_id", "The mobile app referenced by the mobile bundle cannot be deleted!"); + throw e; + } } @Override @@ -86,19 +92,20 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements @Override public PageData findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findMobileAppInfosByTenantId [{}]", tenantId); - PageData mobileBundles = mobileAppBundleDao.findByTenantId(tenantId, pageLink); - return mobileBundles.mapData(this::getMobileAppBundleInfo); + log.trace("Executing findMobileAppBundleInfosByTenantId [{}]", tenantId); + PageData mobileBundles = mobileAppBundleDao.findInfosByTenantId(tenantId, pageLink); + mobileBundles.getData().forEach(this::fetchOauth2Clients); + return mobileBundles; } @Override public MobileAppBundleInfo findMobileAppBundleInfoById(TenantId tenantId, MobileAppBundleId mobileAppIdBundle) { log.trace("Executing findMobileAppInfoById [{}] [{}]", tenantId, mobileAppIdBundle); - MobileAppBundle mobileAppBundle = mobileAppBundleDao.findById(tenantId, mobileAppIdBundle.getId()); - if (mobileAppBundle == null) { - return null; + MobileAppBundleInfo mobileAppBundleInfo = mobileAppBundleDao.findInfoById(tenantId, mobileAppIdBundle); + if (mobileAppBundleInfo != null) { + fetchOauth2Clients(mobileAppBundleInfo); } - return getMobileAppBundleInfo(mobileAppBundle); + return mobileAppBundleInfo; } @Override @@ -115,10 +122,10 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements newClientList.removeIf(existingClients::contains); for (MobileAppBundleOauth2Client client : toRemoveList) { - mobileAppBundleDao.removeOauth2Client(client); + mobileAppBundleDao.removeOauth2Client(tenantId, client); } for (MobileAppBundleOauth2Client client : newClientList) { - mobileAppBundleDao.addOauth2Client(client); + mobileAppBundleDao.addOauth2Client(tenantId, client); } eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) .entityId(mobileAppBundleId).created(false).build()); @@ -142,29 +149,26 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements } @Override - public void deleteMobileAppsByTenantId(TenantId tenantId) { + public void deleteMobileAppBundlesByTenantId(TenantId tenantId) { log.trace("Executing deleteMobileAppsByTenantId, tenantId [{}]", tenantId); mobileAppBundleDao.deleteByTenantId(tenantId); } @Override public void deleteByTenantId(TenantId tenantId) { - deleteMobileAppsByTenantId(tenantId); - } - - private MobileAppBundleInfo getMobileAppBundleInfo(MobileAppBundle mobileAppBundle) { - List clients = oauth2ClientDao.findByMobileAppBundleId(mobileAppBundle.getUuidId()).stream() - .map(OAuth2ClientInfo::new) - .sorted(Comparator.comparing(OAuth2ClientInfo::getTitle)) - .collect(Collectors.toList()); - MobileApp androidApp = mobileAppService.findMobileAppById(mobileAppBundle.getTenantId(), mobileAppBundle.getAndroidAppId()); - MobileApp iosApp = mobileAppService.findMobileAppById(mobileAppBundle.getTenantId(), mobileAppBundle.getIosAppId()); - return new MobileAppBundleInfo(mobileAppBundle, androidApp != null ? androidApp.getPkgName() : null, - iosApp != null ? iosApp.getPkgName() : null, clients); + deleteMobileAppBundlesByTenantId(tenantId); } @Override public EntityType getEntityType() { return EntityType.MOBILE_APP_BUNDLE; } + + private void fetchOauth2Clients(MobileAppBundleInfo mobileAppBundleInfo) { + List clients = oauth2ClientDao.findByMobileAppBundleId(mobileAppBundleInfo.getUuidId()).stream() + .map(OAuth2ClientInfo::new) + .sorted(Comparator.comparing(OAuth2ClientInfo::getTitle)) + .collect(Collectors.toList()); + mobileAppBundleInfo.setOauth2ClientInfos(clients); + } } 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 2b96d88497..a471b30589 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 @@ -97,17 +97,9 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil } @Override - public AndroidQrCodeConfig findAndroidQrCodeConfig(TenantId tenantId, MobileAppBundleId mobileAppBundleId) { + public MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType) { log.trace("Executing findAndroidQrConfig, tenantId [{}], mobileAppBundleId [{}]", tenantId, mobileAppBundleId); - MobileApp mobileApp = mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, PlatformType.ANDROID); - return mobileApp != null ? JacksonUtil.convertValue(mobileApp.getQrCodeConfig(), AndroidQrCodeConfig.class) : null; - } - - @Override - public IosQrCodeConfig findIosQrCodeConfig(TenantId tenantId, MobileAppBundleId mobileAppBundleId) { - log.trace("Executing findAndroidQrConfig, tenantId [{}], mobileAppBundleId [{}]", tenantId, mobileAppBundleId); - MobileApp mobileApp = mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, PlatformType.IOS); - return mobileApp != null ? JacksonUtil.convertValue(mobileApp.getQrCodeConfig(), IosQrCodeConfig.class) : null; + return mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, platformType); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java index ae46e4039b..48d221af9d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java @@ -16,9 +16,9 @@ package org.thingsboard.server.dao.mobile; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.mobile.AndroidQrCodeConfig; -import org.thingsboard.server.common.data.mobile.IosQrCodeConfig; +import org.thingsboard.server.common.data.mobile.QrCodeConfig; import org.thingsboard.server.common.data.mobile.QrCodeSettings; +import org.thingsboard.server.common.data.oauth2.PlatformType; public interface QrCodeSettingService { @@ -26,9 +26,7 @@ public interface QrCodeSettingService { QrCodeSettings getQrCodeSettings(TenantId tenantId); - AndroidQrCodeConfig getAndroidQrCodeConfig(TenantId sysTenantId); - - IosQrCodeConfig getIosQrCodeConfig(TenantId sysTenantId); + QrCodeConfig findAppQrCodeConfig(TenantId sysTenantId, PlatformType platformType); void deleteByTenantId(TenantId tenantId); 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 bda099bcc0..5e6ae2fcef 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 @@ -21,11 +21,11 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.mobile.AndroidQrCodeConfig; import org.thingsboard.server.common.data.mobile.BadgePosition; -import org.thingsboard.server.common.data.mobile.IosQrCodeConfig; +import org.thingsboard.server.common.data.mobile.QrCodeConfig; import org.thingsboard.server.common.data.mobile.QrCodeSettings; import org.thingsboard.server.common.data.mobile.QRCodeConfig; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.service.DataValidator; @@ -75,17 +75,10 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService extends BaseSqlEntity { + + @Column(name = TENANT_ID_COLUMN) + private UUID tenantId; + + @Column(name = ModelConstants.MOBILE_APP_BUNDLE_TITLE_PROPERTY) + private String title; + + @Column(name = ModelConstants.MOBILE_APP_BUNDLE_DESCRIPTION_PROPERTY) + private String description; + + @Column(name = ModelConstants.MOBILE_APP_BUNDLE_ANDROID_APP_ID_PROPERTY) + private UUID androidAppId; + + @Column(name = ModelConstants.MOBILE_APP_BUNDLE_IOS_APP_ID_PROPERTY) + private UUID iosAppID; + + @Convert(converter = JsonConverter.class) + @Column(name = ModelConstants.MOBILE_APP_BUNDLE_LAYOUT_CONFIG_PROPERTY) + private JsonNode layoutConfig; + + @Column(name = ModelConstants.MOBILE_APP_BUNDLE_OAUTH2_ENABLED_PROPERTY) + private Boolean oauth2Enabled; + + public AbstractMobileAppBundleEntity() { + super(); + } + + public AbstractMobileAppBundleEntity(MobileAppBundleEntity mobileAppBundleEntity) { + super(mobileAppBundleEntity); + this.tenantId = mobileAppBundleEntity.getTenantId(); + this.title = mobileAppBundleEntity.getTitle(); + this.description = mobileAppBundleEntity.getDescription(); + this.androidAppId = mobileAppBundleEntity.getAndroidAppId(); + this.iosAppID = mobileAppBundleEntity.getIosAppID(); + this.layoutConfig = mobileAppBundleEntity.getLayoutConfig(); + this.oauth2Enabled = mobileAppBundleEntity.getOauth2Enabled(); + } + + public AbstractMobileAppBundleEntity(T mobileAppBundle) { + super(mobileAppBundle); + if (mobileAppBundle.getTenantId() != null) { + this.tenantId = mobileAppBundle.getTenantId().getId(); + } + this.title = mobileAppBundle.getTitle(); + this.description = mobileAppBundle.getDescription(); + if (mobileAppBundle.getAndroidAppId() != null) { + this.androidAppId = mobileAppBundle.getAndroidAppId().getId(); + } + if (mobileAppBundle.getIosAppId() != null) { + this.iosAppID = mobileAppBundle.getIosAppId().getId(); + } + this.layoutConfig = toJson(mobileAppBundle.getLayoutConfig()); + this.oauth2Enabled = mobileAppBundle.getOauth2Enabled(); + } + + protected MobileAppBundle toMobileAppBundle() { + MobileAppBundle mobileAppBundle = new MobileAppBundle(new MobileAppBundleId(id)); + mobileAppBundle.setCreatedTime(createdTime); + mobileAppBundle.setTitle(title); + mobileAppBundle.setDescription(description); + if (tenantId != null) { + mobileAppBundle.setTenantId(TenantId.fromUUID(tenantId)); + } + if (androidAppId != null) { + mobileAppBundle.setAndroidAppId(new MobileAppId(androidAppId)); + } + if (iosAppID != null) { + mobileAppBundle.setIosAppId(new MobileAppId(iosAppID)); + } + mobileAppBundle.setLayoutConfig(fromJson(layoutConfig, MobileLayoutConfig.class)); + mobileAppBundle.setOauth2Enabled(oauth2Enabled); + return mobileAppBundle; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleEntity.java index 030d995203..66b5a98457 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleEntity.java @@ -15,93 +15,30 @@ */ package org.thingsboard.server.dao.model.sql; -import com.fasterxml.jackson.databind.JsonNode; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.MobileAppBundleId; -import org.thingsboard.server.common.data.id.MobileAppId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.MobileAppBundle; -import org.thingsboard.server.common.data.mobile.MobileLayoutConfig; -import org.thingsboard.server.dao.model.BaseSqlEntity; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.util.mapping.JsonConverter; -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_BUNDLE_TABLE_NAME; @Data @EqualsAndHashCode(callSuper = true) @Entity -@Table(name = ModelConstants.MOBILE_APP_BUNDLE_TABLE_NAME) -public class MobileAppBundleEntity extends BaseSqlEntity { - - @Column(name = TENANT_ID_COLUMN) - private UUID tenantId; - - @Column(name = ModelConstants.MOBILE_APP_BUNDLE_TITLE_PROPERTY) - private String title; - - @Column(name = ModelConstants.MOBILE_APP_BUNDLE_DESCRIPTION_PROPERTY) - private String description; - - @Column(name = ModelConstants.MOBILE_APP_BUNDLE_ANDROID_APP_ID_PROPERTY) - private UUID androidAppId; - - @Column(name = ModelConstants.MOBILE_APP_BUNDLE_IOS_APP_ID_PROPERTY) - private UUID iosAppID; - - @Convert(converter = JsonConverter.class) - @Column(name = ModelConstants.MOBILE_APP_BUNDLE_LAYOUT_CONFIG_PROPERTY) - private JsonNode layoutConfig; - - @Column(name = ModelConstants.MOBILE_APP_BUNDLE_OAUTH2_ENABLED_PROPERTY) - private Boolean oauth2Enabled; +@Table(name = MOBILE_APP_BUNDLE_TABLE_NAME) +public final class MobileAppBundleEntity extends AbstractMobileAppBundleEntity { public MobileAppBundleEntity() { super(); } - public MobileAppBundleEntity(MobileAppBundle mobile) { - super(mobile); - if (mobile.getTenantId() != null) { - this.tenantId = mobile.getTenantId().getId(); - } - this.title = mobile.getTitle(); - this.description = mobile.getDescription(); - if (mobile.getAndroidAppId() != null) { - this.androidAppId = mobile.getAndroidAppId().getId(); - } - if (mobile.getIosAppId() != null) { - this.iosAppID = mobile.getIosAppId().getId(); - } - this.layoutConfig = toJson(mobile.getLayoutConfig()); - this.oauth2Enabled = mobile.getOauth2Enabled(); + public MobileAppBundleEntity(MobileAppBundle mobileAppBundle) { + super(mobileAppBundle); } @Override public MobileAppBundle toData() { - MobileAppBundle mobileAppBundle = new MobileAppBundle(); - mobileAppBundle.setId(new MobileAppBundleId(id)); - mobileAppBundle.setCreatedTime(createdTime); - mobileAppBundle.setTitle(title); - mobileAppBundle.setDescription(description); - if (tenantId != null) { - mobileAppBundle.setTenantId(TenantId.fromUUID(tenantId)); - } - if (androidAppId != null) { - mobileAppBundle.setAndroidAppId(new MobileAppId(androidAppId)); - } - if (iosAppID != null) { - mobileAppBundle.setIosAppId(new MobileAppId(iosAppID)); - } - mobileAppBundle.setLayoutConfig(fromJson(layoutConfig, MobileLayoutConfig.class)); - mobileAppBundle.setOauth2Enabled(oauth2Enabled); - return mobileAppBundle; + return super.toMobileAppBundle(); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleInfoEntity.java new file mode 100644 index 0000000000..9248e4f722 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleInfoEntity.java @@ -0,0 +1,43 @@ +/** + * 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.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.mobile.MobileAppBundleInfo; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MobileAppBundleInfoEntity extends AbstractMobileAppBundleEntity { + + private String androidPkgName; + private String iosPkgName; + + public MobileAppBundleInfoEntity() { + super(); + } + + public MobileAppBundleInfoEntity(MobileAppBundleEntity mobileAppBundleEntity, String androidPkgName, String iosPkgName) { + super(mobileAppBundleEntity); + this.androidPkgName = androidPkgName; + this.iosPkgName = iosPkgName; + } + + @Override + public MobileAppBundleInfo toData() { + return new MobileAppBundleInfo(super.toMobileAppBundle(), androidPkgName, iosPkgName); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java index 69d5fe4e6e..8cd42b8d4a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.MobileAppId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.MobileAppStatus; import org.thingsboard.server.common.data.mobile.MobileApp; +import org.thingsboard.server.common.data.mobile.MobileAppVersionInfo; import org.thingsboard.server.common.data.mobile.QrCodeConfig; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -82,7 +83,7 @@ public class MobileAppEntity extends BaseSqlEntity { this.appSecret = mobile.getAppSecret(); this.platformType = mobile.getPlatformType(); this.status = mobile.getStatus(); - this.versionInfo = mobile.getVersionInfo(); + this.versionInfo = toJson(mobile.getVersionInfo()); this.qrCodeConfig = toJson(mobile.getQrCodeConfig()); } @@ -98,7 +99,7 @@ public class MobileAppEntity extends BaseSqlEntity { mobile.setAppSecret(appSecret); mobile.setPlatformType(platformType); mobile.setStatus(status); - mobile.setVersionInfo(versionInfo); + mobile.setVersionInfo(fromJson(versionInfo, MobileAppVersionInfo.class)); mobile.setQrCodeConfig(fromJson(qrCodeConfig, QrCodeConfig.class)); return mobile; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppBundleDao.java index 01ac158863..0d9bc0dbf5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppBundleDao.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mobile.MobileAppBundle; +import org.thingsboard.server.common.data.mobile.MobileAppBundleInfo; import org.thingsboard.server.common.data.mobile.MobileAppBundleOauth2Client; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; @@ -29,8 +30,8 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.mobile.MobileAppBundleDao; import org.thingsboard.server.dao.model.sql.MobileAppBundleEntity; -import org.thingsboard.server.dao.model.sql.MobileAppOauth2ClientCompositeKey; import org.thingsboard.server.dao.model.sql.MobileAppBundleOauth2ClientEntity; +import org.thingsboard.server.dao.model.sql.MobileAppOauth2ClientCompositeKey; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; @@ -56,8 +57,13 @@ public class JpaMobileAppBundleDao extends JpaAbstractDao findByTenantId(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(mobileAppBundleRepository.findByTenantId(tenantId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); + public PageData findInfosByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(mobileAppBundleRepository.findInfoByTenantId(tenantId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); + } + + @Override + public MobileAppBundleInfo findInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) { + return DaoUtil.getData(mobileAppBundleRepository.findInfoById(mobileAppBundleId.getId())); } @Override @@ -66,12 +72,12 @@ public class JpaMobileAppBundleDao extends JpaAbstractDao { + @Query("SELECT new org.thingsboard.server.dao.model.sql.MobileAppBundleInfoEntity(b, andApp.pkgName, iosApp.pkgName) " + + "FROM MobileAppBundleEntity b " + + "LEFT JOIN MobileAppEntity andApp on b.androidAppId = andApp.id " + + "LEFT JOIN MobileAppEntity iosApp on b.iosAppID = iosApp.id " + + "WHERE b.tenantId = :tenantId AND " + + "(:searchText is NULL OR ilike(b.title, concat('%', :searchText, '%')) = true)") + Page findInfoByTenantId(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT new org.thingsboard.server.dao.model.sql.MobileAppBundleInfoEntity(b, andApp.pkgName, iosApp.pkgName) " + + "FROM MobileAppBundleEntity b " + + "LEFT JOIN MobileAppEntity andApp on b.androidAppId = andApp.id " + + "LEFT JOIN MobileAppEntity iosApp on b.iosAppID = iosApp.id " + + "WHERE b.id = :bundleId ") + MobileAppBundleInfoEntity findInfoById(UUID bundleId); + @Query("SELECT b " + "FROM MobileAppBundleEntity b " + "LEFT JOIN MobileAppEntity a on b.androidAppId = a.id or b.iosAppID = a.id " + @@ -40,8 +56,8 @@ public interface MobileAppBundleRepository extends JpaRepository findByTenantId(@Param("tenantId") UUID tenantId, - @Param("searchText") String searchText, - Pageable pageable); + @Param("searchText") String searchText, + Pageable pageable); @Transactional @Modifying diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java index e6a1485a87..788644504a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java @@ -37,8 +37,8 @@ public interface OAuth2ClientRepository extends JpaRepository findEnabledByDomainNameAndPlatformType(@Param("domainName") String domainName, @@ -46,31 +46,31 @@ public interface OAuth2ClientRepository extends JpaRepository findEnabledByPkgNameAndPlatformType(@Param("pkgName") String pkgName, @Param("platformFilter") String platformFilter); @Query("SELECT c " + "FROM OAuth2ClientEntity c " + - "LEFT JOIN DomainOauth2ClientEntity dc on dc.oauth2ClientId = c.id " + + "LEFT JOIN DomainOauth2ClientEntity dc ON dc.oauth2ClientId = c.id " + "WHERE dc.domainId = :domainId ") List findByDomainId(@Param("domainId") UUID domainId); @Query("SELECT c " + "FROM OAuth2ClientEntity c " + - "LEFT JOIN MobileAppBundleOauth2ClientEntity bc on bc.oauth2ClientId = c.id " + + "LEFT JOIN MobileAppBundleOauth2ClientEntity bc ON bc.oauth2ClientId = c.id " + "WHERE bc.mobileAppBundleId = :mobileAppBundleId ") List findByMobileAppBundleId(@Param("mobileAppBundleId") UUID mobileAppBundleId); @Query("SELECT a.appSecret " + "FROM MobileAppEntity a " + - "LEFT JOIN MobileAppBundleEntity b on (b.androidAppId = a.id or b.iosAppID = a.id) " + - "LEFT JOIN MobileAppBundleOauth2ClientEntity bc on bc.mobileAppBundleId = b.id " + - "LEFT JOIN OAuth2ClientEntity c on bc.oauth2ClientId = c.id " + + "LEFT JOIN MobileAppBundleEntity b ON (b.androidAppId = a.id OR b.iosAppID = a.id) " + + "LEFT JOIN MobileAppBundleOauth2ClientEntity bc ON bc.mobileAppBundleId = b.id " + + "LEFT JOIN OAuth2ClientEntity c ON bc.oauth2ClientId = c.id " + "WHERE c.id = :clientId " + "AND a.pkgName = :pkgName") String findAppSecret(@Param("clientId") UUID id, diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index ba0d3d69f3..9cc55169a9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -46,7 +46,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.mobile.MobileSessionInfo; -import org.thingsboard.server.common.data.mobile.UserMobileInfo; +import org.thingsboard.server.common.data.mobile.UserMobileSessionInfo; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; @@ -468,8 +468,8 @@ public class UserServiceImpl extends AbstractCachedEntityService { - UserMobileInfo newMobileInfo = new UserMobileInfo(); + UserMobileSessionInfo mobileInfo = findMobileInfo(tenantId, userId).orElseGet(() -> { + UserMobileSessionInfo newMobileInfo = new UserMobileSessionInfo(); newMobileInfo.setSessions(new HashMap<>()); return newMobileInfo; }); @@ -479,7 +479,7 @@ public class UserServiceImpl extends AbstractCachedEntityService findMobileSessions(TenantId tenantId, UserId userId) { - return findMobileInfo(tenantId, userId).map(UserMobileInfo::getSessions).orElse(Collections.emptyMap()); + return findMobileInfo(tenantId, userId).map(UserMobileSessionInfo::getSessions).orElse(Collections.emptyMap()); } @Override @@ -495,9 +495,9 @@ public class UserServiceImpl extends AbstractCachedEntityService findMobileInfo(TenantId tenantId, UserId userId) { + private Optional findMobileInfo(TenantId tenantId, UserId userId) { return Optional.ofNullable(userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.MOBILE)) - .map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileInfo.class)); + .map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileSessionInfo.class)); } @Override diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 164645c249..762e4b6670 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -641,15 +641,19 @@ CREATE TABLE IF NOT EXISTS mobile_app ( ); CREATE TABLE IF NOT EXISTS mobile_app_bundle ( - id uuid NOT NULL CONSTRAINT mobile_app_bundle_pkey PRIMARY KEY, - created_time bigint NOT NULL, - tenant_id uuid, - title varchar(255), - android_app_id uuid, - ios_app_id uuid, - description varchar(1024), - layout_config varchar(16384), - oauth2_enabled boolean + id uuid NOT NULL CONSTRAINT mobile_app_bundle_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid, + title varchar(255), + android_app_id uuid, + ios_app_id uuid, + description varchar(1024), + layout_config varchar(16384), + oauth2_enabled boolean, + CONSTRAINT android_app_id_unq_key UNIQUE (android_app_id), + CONSTRAINT ios_app_id_unq_key UNIQUE (ios_app_id), + 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 TABLE IF NOT EXISTS domain_oauth2_client (