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 f9fd1e9181..fd72773b09 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 @@ -21,13 +21,11 @@ CREATE TABLE IF NOT EXISTS mobile_app_bundle ( created_time bigint NOT NULL, tenant_id uuid, title varchar(255), - android_app_id uuid, - ios_app_id uuid, description varchar(1024), + android_app_id uuid UNIQUE, + ios_app_id uuid UNIQUE, 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) ); @@ -43,7 +41,7 @@ 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 + 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; @@ -63,7 +61,7 @@ $$ 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 + 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 @@ -104,6 +102,7 @@ $$ 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 + generatedBundleId := NULL; -- migrate android config SELECT id into androidAppId FROM mobile_app WHERE pkg_name = qrCodeRecord.android_config::jsonb ->> 'appPackage' AND platform_type = 'ANDROID'; IF androidAppId IS NULL THEN @@ -117,6 +116,7 @@ $$ 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; + 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; END IF; -- migrate ios config @@ -137,6 +137,7 @@ $$ END IF; ELSE UPDATE mobile_app SET qr_code_config = qrCodeRecord.ios_config::jsonb || '{"type": "IOS"}'::jsonb WHERE id = iosAppId; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId) WHERE id = qrCodeRecord.id; END IF; END LOOP; END IF; 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 0853cb9eb2..96ddc95dcf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java @@ -64,10 +64,10 @@ public class MobileAppBundleController extends BaseController { private final TbMobileAppBundleService tbMobileAppBundleService; @ApiOperation(value = "Save Or update Mobile app bundle (saveMobileAppBundle)", - notes = "Create or update the Mobile app bundle that represents tha pair of apps for ANDROID and IOS platforms." + + notes = "Create or update the Mobile app bundle that represents tha pair of ANDROID and IOS app and " + + "mobile settings like oauth2 clients, self-registration and layout configuration." + "When creating mobile app bundle, platform generates Mobile App Bundle Id as " + UUID_WIKI_LINK + "The newly created Mobile App Bundle Id will be present in the response. " + - "Specify existing Mobile App Bundle Id to configure application settings like oauth2 settings, self-registration or layout settings. " + "Referencing non-existing Mobile App Bundle Id will cause 'Not Found' error." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PostMapping(value = "/mobile/bundle") 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 b06d280b9a..abe3526bca 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java @@ -40,21 +40,19 @@ import java.util.List; public class MobileV2Controller extends BaseController { @GetMapping(value = "/api/noauth/mobile") - public LoginMobileInfo getMobileUserLoginSettings(@Parameter(description = "Mobile application package name") - @RequestParam String pkgName, - @Parameter(description = "Platform type", - schema = @Schema(allowableValues = {"ANDROID", "IOS"})) - @RequestParam PlatformType platform) { + public LoginMobileInfo getLoginMobileInfo(@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 LoginMobileInfo(oauth2Clients); } @GetMapping(value = "/api/auth/mobile") - public UserMobileInfo getMobileUserSettings(@Parameter(description = "Mobile application package name") - @RequestParam String pkgName, - @Parameter(description = "Platform type", - schema = @Schema(allowableValues = {"ANDROID", "IOS"})) - @RequestParam PlatformType platform) throws ThingsboardException { + public UserMobileInfo getUserMobileInfo(@Parameter(description = "Mobile application package name") + @RequestParam String pkgName, + @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) + @RequestParam PlatformType platform) throws ThingsboardException { SecurityUser securityUser = getCurrentUser(); User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); HomeDashboardInfo homeDashboardInfo = getHomeDashboardInfo(securityUser, user.getAdditionalInfo()); 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 c1ce037a11..12b47ba581 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -113,7 +113,7 @@ public class QrCodeSettingsController extends BaseController { @GetMapping(value = "/.well-known/apple-app-site-association") public ResponseEntity getAppleAppSiteAssociation() { IosQrCodeConfig iosQrCodeConfig = (IosQrCodeConfig) qrCodeSettingService.findAppQrCodeConfig(TenantId.SYS_TENANT_ID, IOS); - if (iosQrCodeConfig != null && iosQrCodeConfig.isEnabled() && iosQrCodeConfig.getAppId() != null) { + if (iosQrCodeConfig != null && iosQrCodeConfig.isEnabled()) { return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, iosQrCodeConfig.getAppId()))); } else { return ResponseEntity.notFound().build(); @@ -139,7 +139,7 @@ public class QrCodeSettingsController extends BaseController { public QrCodeSettings getMobileAppSettings() throws ThingsboardException { SecurityUser currentUser = getCurrentUser(); accessControlService.checkPermission(currentUser, Resource.MOBILE_APP_SETTINGS, Operation.READ); - return qrCodeSettingService.getQrCodeSettings(TenantId.SYS_TENANT_ID); + return qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID); } @ApiOperation(value = "Get the deep link to the associated mobile application (getMobileAppDeepLink)", @@ -150,7 +150,7 @@ public class QrCodeSettingsController extends BaseController { String secret = mobileAppSecretService.generateMobileAppSecret(getCurrentUser()); String baseUrl = systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, null, request); String platformDomain = new URI(baseUrl).getHost(); - QrCodeSettings qrCodeSettings = qrCodeSettingService.getQrCodeSettings(TenantId.SYS_TENANT_ID); + QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID); String appDomain; if (!qrCodeSettings.isUseDefaultApp()) { appDomain = platformDomain; @@ -175,7 +175,7 @@ public class QrCodeSettingsController extends BaseController { @GetMapping(value = "/api/noauth/qr") public ResponseEntity getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) { - QrCodeSettings qrCodeSettings = qrCodeSettingService.getQrCodeSettings(TenantId.SYS_TENANT_ID); + QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID); boolean useDefaultApp = qrCodeSettings.isUseDefaultApp(); if (userAgent.contains("Android")) { String googlePlayLink = useDefaultApp ? qrCodeSettings.getDefaultGooglePlayLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), ANDROID); diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java index 18d632345b..3b90def778 100644 --- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -142,7 +142,7 @@ public class SystemInfoController extends BaseController { DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize()); } - systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.getQrCodeSettings(TenantId.SYS_TENANT_ID)) + systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID)) .map(QrCodeSettings::getQrCodeConfig).map(QRCodeConfig::isShowOnHomePage) .orElse(false)); return systemParams; 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 b81ee82ecd..1cc0862fa8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java @@ -58,10 +58,10 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { public void setUp() throws Exception { loginSysAdmin(); - androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID, true); + androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID); androidApp = doPost("/api/mobile/app", androidApp, MobileApp.class); - iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS, true); + iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS); iosApp = doPost("/api/mobile/app", iosApp, MobileApp.class); } @@ -143,13 +143,13 @@ public class MobileAppBundleControllerTest extends AbstractControllerTest { assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppBundleInfo(savedMobileAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), List.of(new OAuth2ClientInfo(savedOAuth2Client)))); } - private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, PlatformType platformType, boolean oauth2Enabled) { - MobileApp MobileApp = new MobileApp(); - MobileApp.setTenantId(tenantId); - MobileApp.setPkgName(mobileAppName); - MobileApp.setPlatformType(platformType); - MobileApp.setAppSecret(StringUtils.randomAlphanumeric(24)); - return MobileApp; + private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, PlatformType platformType) { + MobileApp mobileApp = new MobileApp(); + mobileApp.setTenantId(tenantId); + mobileApp.setPkgName(mobileAppName); + mobileApp.setPlatformType(platformType); + mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24)); + return mobileApp; } } 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 ba1c7ae3ef..af296f89f2 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 @@ -31,8 +31,6 @@ public interface MobileAppBundleService extends EntityDaoService { MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle); - void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); - MobileAppBundle findMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); PageData findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink); @@ -43,5 +41,7 @@ public interface MobileAppBundleService extends EntityDaoService { MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform); + void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); + void deleteMobileAppBundlesByTenantId(TenantId tenantId); } 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 388e9cb623..865b693d48 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 @@ -54,10 +54,10 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements private MobileAppBundleDao mobileAppBundleDao; @Override - public MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileApp) { - log.trace("Executing saveMobileApp [{}]", mobileApp); + public MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle) { + log.trace("Executing saveMobileAppBundle [{}]", mobileAppBundle); try { - MobileAppBundle savedMobileApp = mobileAppBundleDao.save(tenantId, mobileApp); + MobileAppBundle savedMobileApp = mobileAppBundleDao.save(tenantId, mobileAppBundle); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedMobileApp).build()); return savedMobileApp; } catch (Exception e) { @@ -100,7 +100,7 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements @Override public MobileAppBundleInfo findMobileAppBundleInfoById(TenantId tenantId, MobileAppBundleId mobileAppIdBundle) { - log.trace("Executing findMobileAppInfoById [{}] [{}]", tenantId, mobileAppIdBundle); + log.trace("Executing findMobileAppBundleInfoById [{}] [{}]", tenantId, mobileAppIdBundle); MobileAppBundleInfo mobileAppBundleInfo = mobileAppBundleDao.findInfoById(tenantId, mobileAppIdBundle); if (mobileAppBundleInfo != null) { fetchOauth2Clients(mobileAppBundleInfo); @@ -133,7 +133,7 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements @Override public MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform) { - log.trace("Executing findMobileAppBundle, tenantId [{}], pkgName [{}], platform [{}]", tenantId, pkgName, platform); + log.trace("Executing findMobileAppBundleByPkgNameAndPlatform, tenantId [{}], pkgName [{}], platform [{}]", tenantId, pkgName, platform); return mobileAppBundleDao.findByPkgNameAndPlatform(tenantId, pkgName, platform); } 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 48d221af9d..9c5a19c175 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 @@ -22,9 +22,9 @@ import org.thingsboard.server.common.data.oauth2.PlatformType; public interface QrCodeSettingService { - QrCodeSettings saveQrCodeSettings(TenantId tenantId, QrCodeSettings settings); + QrCodeSettings saveQrCodeSettings(TenantId tenantId, QrCodeSettings qrCodeSettings); - QrCodeSettings getQrCodeSettings(TenantId tenantId); + QrCodeSettings findQrCodeSettings(TenantId tenantId); QrCodeConfig findAppQrCodeConfig(TenantId sysTenantId, PlatformType platformType); 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 5e6ae2fcef..0dc672585c 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 @@ -67,7 +67,7 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService qrCodeSettingsDao.findByTenantId(tenantId), true); @@ -77,7 +77,7 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService @Override public MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType) { - switch (platformType) { - case ANDROID: - return DaoUtil.getData(mobileAppRepository.findAndroidAppByBundleId(mobileAppBundleId.getId())); - case IOS: - return DaoUtil.getData(mobileAppRepository.findIOSAppByBundleId(mobileAppBundleId.getId())); - default: - return null; - } + return switch (platformType) { + case ANDROID -> DaoUtil.getData(mobileAppRepository.findAndroidAppByBundleId(mobileAppBundleId.getId())); + case IOS -> DaoUtil.getData(mobileAppRepository.findIOSAppByBundleId(mobileAppBundleId.getId())); + default -> null; + }; } @Override diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 762e4b6670..22a9fcd7c7 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -637,23 +637,22 @@ CREATE TABLE IF NOT EXISTS mobile_app ( platform_type varchar(32), status varchar(32), version_info varchar(16384), - qr_code_config varchar(16384) + qr_code_config varchar(16384), + CONSTRAINT pkg_platform_unique UNIQUE (pkg_name, platform_type) ); 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) + id uuid NOT NULL CONSTRAINT mobile_app_bundle_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid, + title varchar(255), + description varchar(1024), + android_app_id uuid UNIQUE, + ios_app_id uuid UNIQUE, + layout_config varchar(16384), + oauth2_enabled boolean, + 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 (