Browse Source

upgrade script, optimized queries to db, refactoring

pull/11817/head
dashevchenko 2 years ago
parent
commit
cc64a27050
  1. 129
      application/src/main/data/upgrade/3.8.0/schema_update.sql
  2. 2
      application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java
  3. 22
      application/src/main/java/org/thingsboard/server/controller/MobileAppController.java
  4. 12
      application/src/main/java/org/thingsboard/server/controller/MobileV2Controller.java
  5. 23
      application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java
  6. 30
      application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java
  7. 20
      application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java
  8. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java
  9. 9
      common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java
  10. 7
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/LoginMobileInfo.java
  11. 7
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java
  12. 6
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppBundleInfo.java
  13. 49
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppVersionInfo.java
  14. 1
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/QrCodeConfig.java
  15. 11
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java
  16. 14
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileSessionInfo.java
  17. 9
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java
  18. 56
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java
  19. 12
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java
  20. 8
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java
  21. 17
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java
  22. 2
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  23. 114
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractMobileAppBundleEntity.java
  24. 75
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleEntity.java
  25. 43
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppBundleInfoEntity.java
  26. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java
  27. 16
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppBundleDao.java
  28. 24
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppBundleRepository.java
  29. 22
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java
  30. 12
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  31. 22
      dao/src/main/resources/sql/schema-entities.sql

129
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;
$$;

2
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);
}

22
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<MobileApp> 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<MobileApp> 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);
}

12
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<OAuth2ClientLoginInfo> 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);
}
}

23
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<JsonNode> 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<JsonNode> 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;
}
}

30
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<MobileApp> 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<MobileAppBundleInfo> 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<MobileAppBundleInfo> 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<MobileApp> 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))));
}

20
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<MobileApp> 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<MobileAppBundleInfo> 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<MobileApp> 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=([^&]+)\"");

2
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);
}

9
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<MobileApp> 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);

7
common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileLoginInfo.java → 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<OAuth2ClientLoginInfo> oAuth2ClientLoginInfos;
public record LoginMobileInfo(List<OAuth2ClientLoginInfo> oAuth2ClientLoginInfos) {
}

7
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<MobileAppId> 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;

6
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<OAuth2ClientInfo> 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<OAuth2ClientInfo> oauth2ClientInfos) {
super(mobileApp);
this.androidPkgName = androidPkgName;

49
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;
}

1
common/data/src/main/java/org/thingsboard/server/common/data/mobile/QrCodeConfig.java

@ -33,5 +33,6 @@ public interface QrCodeConfig {
PlatformType getType();
String getStoreLink();
}

11
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<String, MobileSessionInfo> sessions;
import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.User;
public record UserMobileInfo(User user, HomeDashboardInfo homeDashboardInfo, MobileLayoutConfig layoutConfig) {
}

14
common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileUserInfo.java → 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<String, MobileSessionInfo> sessions;
}

9
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<MobileAppBundle> {
PageData<MobileAppBundle> findByTenantId(TenantId tenantId, PageLink pageLink);
PageData<MobileAppBundleInfo> findInfosByTenantId(TenantId tenantId, PageLink pageLink);
MobileAppBundleInfo findInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
List<MobileAppBundleOauth2Client> 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);

56
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<MobileAppBundleInfo> findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findMobileAppInfosByTenantId [{}]", tenantId);
PageData<MobileAppBundle> mobileBundles = mobileAppBundleDao.findByTenantId(tenantId, pageLink);
return mobileBundles.mapData(this::getMobileAppBundleInfo);
log.trace("Executing findMobileAppBundleInfosByTenantId [{}]", tenantId);
PageData<MobileAppBundleInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> clients = oauth2ClientDao.findByMobileAppBundleId(mobileAppBundleInfo.getUuidId()).stream()
.map(OAuth2ClientInfo::new)
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle))
.collect(Collectors.toList());
mobileAppBundleInfo.setOauth2ClientInfos(clients);
}
}

12
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

8
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);

17
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<Tenant
}
@Override
public AndroidQrCodeConfig getAndroidQrCodeConfig(TenantId tenantId) {
log.trace("Executing getAndroidAppConfig for tenant [{}] ", tenantId);
public QrCodeConfig findAppQrCodeConfig(TenantId tenantId, PlatformType platformType) {
log.trace("Executing findAppQrCodeConfig for tenant [{}] ", tenantId);
QrCodeSettings qrCodeSettings = getQrCodeSettings(tenantId);
return qrCodeSettings.getMobileAppBundleId() != null ? mobileAppService.findAndroidQrCodeConfig(tenantId, qrCodeSettings.getMobileAppBundleId()) : null;
}
@Override
public IosQrCodeConfig getIosQrCodeConfig(TenantId tenantId) {
log.trace("Executing getIOSAppConfig for tenant [{}] ", tenantId);
QrCodeSettings qrCodeSettings = getQrCodeSettings(tenantId);
return qrCodeSettings.getMobileAppBundleId() != null ? mobileAppService.findIosQrCodeConfig(tenantId, qrCodeSettings.getMobileAppBundleId()) : null;
return qrCodeSettings.getMobileAppBundleId() != null ? mobileAppService.findByBundleIdAndPlatformType(tenantId, qrCodeSettings.getMobileAppBundleId(), platformType).getQrCodeConfig() : null;
}
@Override

2
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -464,7 +464,7 @@ public class ModelConstants {
public static final String MOBILE_APP_BUNDLE_LAYOUT_CONFIG_PROPERTY = "layout_config";
public static final String MOBILE_APP_BUNDLE_OAUTH2_ENABLED_PROPERTY = "oauth2_enabled";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_TABLE_NAME = "mobile_app_oauth2_client";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_TABLE_NAME = "mobile_app_bundle_oauth2_client";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_CLIENT_ID_PROPERTY = "oauth2_client_id";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_MOBILE_APP_BUNDLE_ID_PROPERTY = "mobile_app_bundle_id";

114
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractMobileAppBundleEntity.java

@ -0,0 +1,114 @@
/**
* 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 com.fasterxml.jackson.databind.JsonNode;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.MappedSuperclass;
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;
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractMobileAppBundleEntity<T extends MobileAppBundle> extends BaseSqlEntity<T> {
@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;
}
}

75
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<MobileAppBundle> {
@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<MobileAppBundle> {
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();
}
}

43
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<MobileAppBundleInfo> {
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);
}
}

5
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<MobileApp> {
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<MobileApp> {
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;
}

16
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<MobileAppBundleEntity,
}
@Override
public PageData<MobileAppBundle> findByTenantId(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData(mobileAppBundleRepository.findByTenantId(tenantId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink)));
public PageData<MobileAppBundleInfo> 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<MobileAppBundleEntity,
}
@Override
public void addOauth2Client(MobileAppBundleOauth2Client mobileAppBundleOauth2Client) {
public void addOauth2Client(TenantId tenantId, MobileAppBundleOauth2Client mobileAppBundleOauth2Client) {
mobileOauth2ProviderRepository.save(new MobileAppBundleOauth2ClientEntity(mobileAppBundleOauth2Client));
}
@Override
public void removeOauth2Client(MobileAppBundleOauth2Client mobileAppBundleOauth2Client) {
public void removeOauth2Client(TenantId tenantId, MobileAppBundleOauth2Client mobileAppBundleOauth2Client) {
mobileOauth2ProviderRepository.deleteById(new MobileAppOauth2ClientCompositeKey(mobileAppBundleOauth2Client.getMobileAppBundleId().getId(),
mobileAppBundleOauth2Client.getOAuth2ClientId().getId()));
}

24
dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppBundleRepository.java

@ -23,13 +23,29 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.dao.model.sql.MobileAppBundleEntity;
import org.thingsboard.server.dao.model.sql.OAuth2ClientEntity;
import org.thingsboard.server.dao.model.sql.MobileAppBundleInfoEntity;
import java.util.List;
import java.util.UUID;
public interface MobileAppBundleRepository extends JpaRepository<MobileAppBundleEntity, UUID> {
@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<MobileAppBundleInfoEntity> 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<MobileAppBundle
@Query("SELECT b FROM MobileAppBundleEntity b WHERE b.tenantId = :tenantId AND " +
"(:searchText is NULL OR ilike(b.title, concat('%', :searchText, '%')) = true)")
Page<MobileAppBundleEntity> findByTenantId(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText,
Pageable pageable);
@Param("searchText") String searchText,
Pageable pageable);
@Transactional
@Modifying

22
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java

@ -37,8 +37,8 @@ public interface OAuth2ClientRepository extends JpaRepository<OAuth2ClientEntity
@Query("SELECT c " +
"FROM OAuth2ClientEntity c " +
"LEFT JOIN DomainOauth2ClientEntity dc on c.id = dc.oauth2ClientId " +
"LEFT JOIN DomainEntity domain on dc.domainId = domain.id " +
"LEFT JOIN DomainOauth2ClientEntity dc ON c.id = dc.oauth2ClientId " +
"LEFT JOIN DomainEntity domain ON dc.domainId = domain.id " +
"WHERE domain.name = :domainName AND domain.oauth2Enabled = true " +
"AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)")
List<OAuth2ClientEntity> findEnabledByDomainNameAndPlatformType(@Param("domainName") String domainName,
@ -46,31 +46,31 @@ public interface OAuth2ClientRepository extends JpaRepository<OAuth2ClientEntity
@Query("SELECT c " +
"FROM OAuth2ClientEntity c " +
"LEFT JOIN MobileAppBundleOauth2ClientEntity ac on c.id = ac.oauth2ClientId " +
"LEFT JOIN MobileAppBundleEntity b on ac.mobileAppBundleId = b.id " +
"LEFT JOIN MobileAppEntity a on (b.androidAppId = a.id or b.iosAppID = a.id) " +
"WHERE a.pkgName = :pkgName AND b.oauth2Enabled = true " +
"LEFT JOIN MobileAppBundleOauth2ClientEntity ac ON c.id = ac.oauth2ClientId " +
"LEFT JOIN MobileAppBundleEntity b ON ac.mobileAppBundleId = b.id " +
"LEFT JOIN MobileAppEntity andApp ON b.androidAppId = andApp.id LEFT JOIN MobileAppEntity iosApp ON b.iosAppID = iosApp.id " +
"WHERE andApp.pkgName = :pkgName OR iosApp.pkgName = :pkgName AND b.oauth2Enabled = true " +
"AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)")
List<OAuth2ClientEntity> 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<OAuth2ClientEntity> 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<OAuth2ClientEntity> 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,

12
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<UserCacheKey, U
public void saveMobileSession(TenantId tenantId, UserId userId, String mobileToken, MobileSessionInfo sessionInfo) {
removeMobileSession(tenantId, mobileToken); // unassigning fcm token from other users, in case we didn't clean up it on log out or mobile app uninstall
UserMobileInfo mobileInfo = findMobileInfo(tenantId, userId).orElseGet(() -> {
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<UserCacheKey, U
@Override
public Map<String, MobileSessionInfo> 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<UserCacheKey, U
}
}
private Optional<UserMobileInfo> findMobileInfo(TenantId tenantId, UserId userId) {
private Optional<UserMobileSessionInfo> 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

22
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 (

Loading…
Cancel
Save