added platformType request param to /mobile/app API endpoint

This commit is contained in:
dashevchenko 2024-10-10 12:32:17 +03:00
parent 3a243c821a
commit 06ad4bdc43
13 changed files with 125 additions and 39 deletions

View File

@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.MobileAppId;
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.config.annotations.ApiOperation;
@ -76,7 +77,9 @@ 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> getTenantMobileApps(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
public PageData<MobileApp> getTenantMobileApps(@Parameter(description = "Platform type: ANDROID or IOS")
@RequestParam(required = false) PlatformType platformType,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ -88,7 +91,7 @@ public class MobileAppController extends BaseController {
@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);
return mobileAppService.findMobileAppsByTenantId(getTenantId(), platformType, pageLink);
}
@ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH)

View File

@ -308,7 +308,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
jwtSettingsService.saveJwtSettings(jwtSettings);
}
List<MobileApp> mobiles = mobileAppDao.findByTenantId(TenantId.SYS_TENANT_ID, new PageLink(Integer.MAX_VALUE,0)).getData();
List<MobileApp> mobiles = mobileAppDao.findByTenantId(TenantId.SYS_TENANT_ID, null, new PageLink(Integer.MAX_VALUE,0)).getData();
if (CollectionUtils.isNotEmpty(mobiles)) {
mobiles.stream()
.filter(mobileApp -> !validateKeyLength(mobileApp.getAppSecret()))

View File

@ -21,10 +21,10 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.StringUtils;
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.service.DaoSqlTest;
@ -59,7 +59,7 @@ public class MobileAppControllerTest extends AbstractControllerTest {
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(pageData.getData()).isEmpty();
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package");
MobileApp mobileApp = validMobileApp("my.test.package", PlatformType.ANDROID);
MobileApp savedMobileApp = doPost("/api/mobile/app", mobileApp, MobileApp.class);
PageData<MobileApp> pageData2 = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
@ -76,7 +76,7 @@ public class MobileAppControllerTest extends AbstractControllerTest {
@Test
public void testSaveMobileAppWithShortAppSecret() throws Exception {
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "mobileApp.ce");
MobileApp mobileApp = validMobileApp( "mobileApp.ce", PlatformType.ANDROID);
mobileApp.setAppSecret("short");
doPost("/api/mobile/app", mobileApp)
.andExpect(status().isBadRequest())
@ -85,7 +85,7 @@ public class MobileAppControllerTest extends AbstractControllerTest {
@Test
public void testShouldNotSaveMobileAppWithWrongQrCodeConf() throws Exception {
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "mobileApp.ce");
MobileApp mobileApp = validMobileApp("mobileApp.ce", PlatformType.ANDROID);
AndroidQrCodeConfig androidQrCodeConfig = AndroidQrCodeConfig.builder()
.enabled(true)
.appPackage(null)
@ -110,7 +110,7 @@ public class MobileAppControllerTest extends AbstractControllerTest {
@Test
public void testShouldNotSaveMobileAppWithWrongIosConf() throws Exception {
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "mobileApp.ce");
MobileApp mobileApp = validMobileApp("mobileApp.ce", PlatformType.ANDROID);
IosQrCodeConfig iosQrCodeConfig = IosQrCodeConfig.builder()
.enabled(true)
.appId(null)
@ -127,12 +127,27 @@ public class MobileAppControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName) {
MobileApp MobileApp = new MobileApp();
MobileApp.setTenantId(tenantId);
MobileApp.setPkgName(mobileAppName);
MobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
return MobileApp;
@Test
public void testGetTenantAppsByPlatformTypeSaveMobileApp() throws Exception {
MobileApp androidApp = doPost("/api/mobile/app", validMobileApp("android.1", PlatformType.ANDROID), MobileApp.class);
MobileApp androidApp2 = doPost("/api/mobile/app", validMobileApp("android.2", PlatformType.ANDROID), MobileApp.class);
MobileApp iosApp = doPost("/api/mobile/app", validMobileApp("ios.1", PlatformType.IOS), MobileApp.class);
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(pageData.getData()).hasSize(3);
assertThat(pageData.getData()).containsExactlyInAnyOrder(androidApp, androidApp2, iosApp);
PageData<MobileApp> androidPageData = doGetTypedWithPageLink("/api/mobile/app?platformType=ANDROID&", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(androidPageData.getData()).hasSize(2);
assertThat(androidPageData.getData()).containsExactlyInAnyOrder(androidApp, androidApp2);
}
private MobileApp validMobileApp(String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setPkgName(mobileAppName);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
mobileApp.setPlatformType(platformType);
return mobileApp;
}
}

View File

@ -23,14 +23,13 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.server.common.data.StringUtils;
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.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.MobileAppBundleInfo;
import org.thingsboard.server.common.data.mobile.QrCodeSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.common.data.mobile.QrCodeSettings;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -68,7 +67,7 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
public void setUp() throws Exception {
loginSysAdmin();
MobileApp androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID, true);
MobileApp androidApp = validMobileApp( "my.android.package", PlatformType.ANDROID);
AndroidQrCodeConfig androidQrCodeConfig = AndroidQrCodeConfig.builder()
.appPackage(ANDROID_PACKAGE_NAME)
.sha256CertFingerprints(ANDROID_APP_SHA256)
@ -78,7 +77,7 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
androidApp.setQrCodeConfig(androidQrCodeConfig);
MobileApp savedAndroidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
MobileApp iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS, true);
MobileApp iosApp = validMobileApp( "my.ios.package", PlatformType.IOS);
IosQrCodeConfig iosQrCodeConfig = IosQrCodeConfig.builder()
.appId(APPLE_APP_ID)
.enabled(true)
@ -101,7 +100,7 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
qrCodeSettings.setMobileAppBundleId(null);
qrCodeSettings.setQrCodeConfig(qrCodeConfig);
doPost("/api/qr/settings", qrCodeSettings)
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isOk());
}
@ -249,12 +248,12 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
assertThat(customerCustomAppParsedDeepLink.group(1)).isEqualTo("localhost");
}
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(String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setTenantId(tenantId);
mobileApp.setPkgName(mobileAppName);
mobileApp.setPlatformType(platformType);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
return mobileApp;
}
}

View File

@ -30,7 +30,7 @@ public interface MobileAppService extends EntityDaoService {
MobileApp findMobileAppById(TenantId tenantId, MobileAppId mobileAppId);
PageData<MobileApp> findMobileAppsByTenantId(TenantId tenantId, PageLink pageLink);
PageData<MobileApp> findMobileAppsByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink);
MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType);

View File

@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ -47,8 +48,9 @@ public class MobileApp extends BaseData<MobileAppId> implements HasTenantId, Has
@Length(fieldName = "appSecret", min = 16, max = 2048, message = "must be at least 16 and max 2048 characters")
private String appSecret;
@Schema(description = "Application platform type: ANDROID or IOS", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private PlatformType platformType;
@Schema(description = "Application status: PUBLISHED, DEPRECATED, SUSPENDED", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "Application status: PUBLISHED, DEPRECATED, SUSPENDED, DRAFT", requiredMode = Schema.RequiredMode.REQUIRED)
private MobileAppStatus status;
@Schema(description = "Application version info")
@Valid

View File

@ -36,6 +36,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.Comparator;
import java.util.List;
@ -52,10 +53,14 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements
private OAuth2ClientDao oauth2ClientDao;
@Autowired
private MobileAppBundleDao mobileAppBundleDao;
@Autowired
private DataValidator<MobileAppBundle> mobileAppBundleDataValidator;
@Override
public MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle) {
log.trace("Executing saveMobileAppBundle [{}]", mobileAppBundle);
mobileAppBundleDataValidator.validate(mobileAppBundle, b -> tenantId);
try {
MobileAppBundle savedMobileApp = mobileAppBundleDao.save(tenantId, mobileAppBundle);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedMobileApp).build());

View File

@ -27,7 +27,7 @@ public interface MobileAppDao extends Dao<MobileApp> {
MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType);
PageData<MobileApp> findByTenantId(TenantId tenantId, PageLink pageLink);
PageData<MobileApp> findByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink);
void deleteByTenantId(TenantId tenantId);

View File

@ -71,9 +71,9 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
}
@Override
public PageData<MobileApp> findMobileAppsByTenantId(TenantId tenantId, PageLink pageLink) {
public PageData<MobileApp> findMobileAppsByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink) {
log.trace("Executing findMobileAppInfosByTenantId [{}]", tenantId);
return mobileAppDao.findByTenantId(tenantId, pageLink);
return mobileAppDao.findByTenantId(tenantId, platformType, pageLink);
}
@Override

View File

@ -0,0 +1,60 @@
/**
* 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.service.validator;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.MobileAppId;
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.oauth2.PlatformType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.mobile.MobileAppDao;
import org.thingsboard.server.dao.service.DataValidator;
@Component
@AllArgsConstructor
public class MobileAppBundleDataValidator extends DataValidator<MobileAppBundle> {
@Autowired
private MobileAppDao mobileAppDao;
@Override
protected void validateDataImpl(TenantId tenantId, MobileAppBundle mobileAppBundle) {
MobileAppId androidAppId = mobileAppBundle.getAndroidAppId();
if (androidAppId != null) {
MobileApp androidApp = mobileAppDao.findById(tenantId, androidAppId.getId());
if (androidApp == null) {
throw new DataValidationException("Mobile app bundle refers to non-existing android app!");
}
if (androidApp.getPlatformType() != PlatformType.ANDROID) {
throw new DataValidationException("Mobile app bundle refers to wrong android app! Platform type of specified app is " + androidApp.getPlatformType());
}
}
MobileAppId iosAppId = mobileAppBundle.getIosAppId();
if (iosAppId != null) {
MobileApp iosApp = mobileAppDao.findById(tenantId, iosAppId.getId());
if (iosApp == null) {
throw new DataValidationException("Mobile app bundle refers to non-existing ios app!");
}
if (iosApp.getPlatformType() != PlatformType.IOS) {
throw new DataValidationException("Mobile app bundle refers to wrong ios app! Platform type of specified app is " + iosApp.getPlatformType());
}
}
}
}

View File

@ -60,8 +60,8 @@ public class JpaMobileAppDao extends JpaAbstractDao<MobileAppEntity, MobileApp>
}
@Override
public PageData<MobileApp> findByTenantId(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData(mobileAppRepository.findByTenantId(tenantId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink)));
public PageData<MobileApp> findByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink) {
return DaoUtil.toPageData(mobileAppRepository.findByTenantId(tenantId.getId(), platformType, pageLink.getTextSearch(), DaoUtil.toPageable(pageLink)));
}
@Override

View File

@ -30,8 +30,10 @@ import java.util.UUID;
public interface MobileAppRepository extends JpaRepository<MobileAppEntity, UUID> {
@Query("SELECT a FROM MobileAppEntity a WHERE a.tenantId = :tenantId AND " +
"(:platformType is NULL OR a.platformType = :platformType) AND" +
"(:searchText is NULL OR ilike(a.pkgName, concat('%', :searchText, '%')) = true)")
Page<MobileAppEntity> findByTenantId(@Param("tenantId") UUID tenantId,
@Param("platformType") PlatformType platformType,
@Param("searchText") String searchText,
Pageable pageable);

View File

@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
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.mobile.MobileAppService;
@ -48,7 +49,7 @@ public class MobileAppServiceTest extends AbstractServiceTest {
@Test
public void testSaveMobileApp() {
MobileApp MobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "mobileApp.ce", true);
MobileApp MobileApp = validMobileApp("mobileApp.ce", PlatformType.IOS);
MobileApp savedMobileApp = mobileAppService.saveMobileApp(SYSTEM_TENANT_ID, MobileApp);
MobileApp retrievedMobileApp = mobileAppService.findMobileAppById(savedMobileApp.getTenantId(), savedMobileApp.getId());
@ -70,20 +71,19 @@ public class MobileAppServiceTest extends AbstractServiceTest {
public void testGetTenantMobileApps() {
List<MobileApp> mobileApps = new ArrayList<>();
for (int i = 0; i < 5; i++) {
MobileApp oAuth2Client = validMobileApp(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5), true);
MobileApp oAuth2Client = validMobileApp(StringUtils.randomAlphabetic(5), PlatformType.ANDROID);
MobileApp savedOauth2Client = mobileAppService.saveMobileApp(SYSTEM_TENANT_ID, oAuth2Client);
mobileApps.add(savedOauth2Client);
}
PageData<MobileApp> retrieved = mobileAppService.findMobileAppsByTenantId(TenantId.SYS_TENANT_ID, new PageLink(10, 0));
PageData<MobileApp> retrieved = mobileAppService.findMobileAppsByTenantId(TenantId.SYS_TENANT_ID, null, new PageLink(10, 0));
assertThat(retrieved.getData()).containsOnlyOnceElementsOf(mobileApps);
}
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, boolean oauth2Enabled) {
private MobileApp validMobileApp(String mobileAppName, PlatformType platformType) {
MobileApp MobileApp = new MobileApp();
MobileApp.setTenantId(tenantId);
MobileApp.setPkgName(mobileAppName);
MobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
MobileApp.setPlatformType(platformType);
return MobileApp;
}
}