Merge branch 'feature/mobile-app-bundle' of github.com:thingsboard/thingsboard into feature/mobile-app-bundle

This commit is contained in:
Vladyslav_Prykhodko 2024-11-13 15:22:40 +02:00
commit 01806027b6
9 changed files with 56 additions and 68 deletions

View File

@ -59,6 +59,10 @@ $$
ALTER TABLE mobile_app_oauth2_client RENAME TO mobile_app_bundle_oauth2_client; 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 DROP CONSTRAINT IF EXISTS fk_domain;
ALTER TABLE mobile_app_bundle_oauth2_client RENAME COLUMN mobile_app_id TO mobile_app_bundle_id; ALTER TABLE mobile_app_bundle_oauth2_client RENAME COLUMN mobile_app_id TO mobile_app_bundle_id;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_mobile_app_bundle_oauth2_client_bundle_id') THEN
ALTER TABLE mobile_app_bundle_oauth2_client ADD CONSTRAINT fk_mobile_app_bundle_oauth2_client_bundle_id
FOREIGN KEY (mobile_app_bundle_id) REFERENCES mobile_app_bundle(id) ON DELETE CASCADE;
END IF;
END IF; END IF;
END; END;
$$; $$;

View File

@ -16,7 +16,7 @@
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -54,7 +54,6 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -77,7 +76,7 @@ public class MobileAppController extends BaseController {
private final TbMobileAppService tbMobileAppService; private final TbMobileAppService tbMobileAppService;
@ApiOperation(value = "Get mobile app login info (getLoginMobileInfo)") @ApiOperation(value = "Get mobile app login info (getLoginMobileInfo)")
@GetMapping(value = "/api/noauth/mobile") @GetMapping(value = "/noauth/mobile")
public LoginMobileInfo getLoginMobileInfo(@Parameter(description = "Mobile application package name") public LoginMobileInfo getLoginMobileInfo(@Parameter(description = "Mobile application package name")
@RequestParam String pkgName, @RequestParam String pkgName,
@Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"}))
@ -89,7 +88,7 @@ public class MobileAppController extends BaseController {
@ApiOperation(value = "Get user mobile app basic info (getUserMobileInfo)", notes = AVAILABLE_FOR_ANY_AUTHORIZED_USER) @ApiOperation(value = "Get user mobile app basic info (getUserMobileInfo)", notes = AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/api/mobile") @GetMapping(value = "/mobile")
public UserMobileInfo getUserMobileInfo(@Parameter(description = "Mobile application package name") public UserMobileInfo getUserMobileInfo(@Parameter(description = "Mobile application package name")
@RequestParam String pkgName, @RequestParam String pkgName,
@Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"}))
@ -102,7 +101,7 @@ public class MobileAppController extends BaseController {
} }
@ApiOperation(value = "Get mobile app version info (getMobileVersionInfo)") @ApiOperation(value = "Get mobile app version info (getMobileVersionInfo)")
@GetMapping(value = "/api/mobile/versionInfo") @GetMapping(value = "/mobile/versionInfo")
public MobileAppVersionInfo getMobileVersionInfo(@Parameter(description = "Mobile application package name") public MobileAppVersionInfo getMobileVersionInfo(@Parameter(description = "Mobile application package name")
@RequestParam String pkgName, @RequestParam String pkgName,
@Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"})) @Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"}))
@ -164,15 +163,15 @@ public class MobileAppController extends BaseController {
tbMobileAppService.delete(mobileApp, getCurrentUser()); tbMobileAppService.delete(mobileApp, getCurrentUser());
} }
private List<MobilePage> getVisiblePages(MobileAppBundle mobileAppBundle) throws JsonProcessingException { private JsonNode getVisiblePages(MobileAppBundle mobileAppBundle) throws JsonProcessingException {
if (mobileAppBundle != null && mobileAppBundle.getLayoutConfig() != null) { if (mobileAppBundle != null && mobileAppBundle.getLayoutConfig() != null) {
List<MobilePage> mobilePages = mobileAppBundle.getLayoutConfig().getPages() List<MobilePage> mobilePages = mobileAppBundle.getLayoutConfig().getPages()
.stream() .stream()
.filter(MobilePage::isVisible) .filter(MobilePage::isVisible)
.collect(Collectors.toList()); .collect(Collectors.toList());
return JacksonUtil.readValue(JacksonUtil.writeValueAsViewIgnoringNullFields(mobilePages, Views.Public.class), new TypeReference<>() {}); return JacksonUtil.toJsonNode(JacksonUtil.writeValueAsViewIgnoringNullFields(mobilePages, Views.Public.class));
} else { } else {
return Collections.emptyList(); return JacksonUtil.newArrayNode();
} }
} }

View File

@ -31,17 +31,16 @@ public interface MobileAppBundleService extends EntityDaoService {
MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle); MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle);
void updateOauth2Clients(TenantId tenantId, MobileAppBundleId mobileAppBundleId, List<OAuth2ClientId> oAuth2ClientIds);
MobileAppBundle findMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); MobileAppBundle findMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
PageData<MobileAppBundleInfo> findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink); PageData<MobileAppBundleInfo> findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink);
MobileAppBundleInfo findMobileAppBundleInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); MobileAppBundleInfo findMobileAppBundleInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
void updateOauth2Clients(TenantId tenantId, MobileAppBundleId mobileAppBundleId, List<OAuth2ClientId> oAuth2ClientIds);
MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform); MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform);
void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId); void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
void deleteMobileAppBundlesByTenantId(TenantId tenantId);
} }

View File

@ -38,6 +38,4 @@ public interface MobileAppService extends EntityDaoService {
void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId); void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId);
void deleteMobileAppsByTenantId(TenantId tenantId);
} }

View File

@ -15,11 +15,10 @@
*/ */
package org.thingsboard.server.common.data.mobile; package org.thingsboard.server.common.data.mobile;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.HomeDashboardInfo; import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.mobile.layout.MobilePage;
import java.util.List;
public record UserMobileInfo(User user, HomeDashboardInfo homeDashboardInfo, List<MobilePage> pages) { public record UserMobileInfo(User user, HomeDashboardInfo homeDashboardInfo, JsonNode pages) {
} }

View File

@ -78,10 +78,26 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements
} }
@Override @Override
public void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) { public void updateOauth2Clients(TenantId tenantId, MobileAppBundleId mobileAppBundleId, List<OAuth2ClientId> oAuth2ClientIds) {
log.trace("Executing deleteMobileAppBundleById [{}]", mobileAppBundleId.getId()); log.trace("Executing updateOauth2Clients, mobileAppId [{}], oAuth2ClientIds [{}]", mobileAppBundleId, oAuth2ClientIds);
mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId()); Set<MobileAppBundleOauth2Client> newClientList = oAuth2ClientIds.stream()
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build()); .map(clientId -> new MobileAppBundleOauth2Client(mobileAppBundleId, clientId))
.collect(Collectors.toSet());
List<MobileAppBundleOauth2Client> existingClients = mobileAppBundleDao.findOauth2ClientsByMobileAppBundleId(tenantId, mobileAppBundleId);
List<MobileAppBundleOauth2Client> toRemoveList = existingClients.stream()
.filter(client -> !newClientList.contains(client))
.toList();
newClientList.removeIf(existingClients::contains);
for (MobileAppBundleOauth2Client client : toRemoveList) {
mobileAppBundleDao.removeOauth2Client(tenantId, client);
}
for (MobileAppBundleOauth2Client client : newClientList) {
mobileAppBundleDao.addOauth2Client(tenantId, client);
}
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(mobileAppBundleId).created(false).build());
} }
@Override @Override
@ -108,29 +124,6 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements
return mobileAppBundleInfo; return mobileAppBundleInfo;
} }
@Override
public void updateOauth2Clients(TenantId tenantId, MobileAppBundleId mobileAppBundleId, List<OAuth2ClientId> oAuth2ClientIds) {
log.trace("Executing updateOauth2Clients, mobileAppId [{}], oAuth2ClientIds [{}]", mobileAppBundleId, oAuth2ClientIds);
Set<MobileAppBundleOauth2Client> newClientList = oAuth2ClientIds.stream()
.map(clientId -> new MobileAppBundleOauth2Client(mobileAppBundleId, clientId))
.collect(Collectors.toSet());
List<MobileAppBundleOauth2Client> existingClients = mobileAppBundleDao.findOauth2ClientsByMobileAppBundleId(tenantId, mobileAppBundleId);
List<MobileAppBundleOauth2Client> toRemoveList = existingClients.stream()
.filter(client -> !newClientList.contains(client))
.toList();
newClientList.removeIf(existingClients::contains);
for (MobileAppBundleOauth2Client client : toRemoveList) {
mobileAppBundleDao.removeOauth2Client(tenantId, client);
}
for (MobileAppBundleOauth2Client client : newClientList) {
mobileAppBundleDao.addOauth2Client(tenantId, client);
}
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(mobileAppBundleId).created(false).build());
}
@Override @Override
public MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform) { public MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform) {
log.trace("Executing findMobileAppBundleByPkgNameAndPlatform, tenantId [{}], pkgName [{}], platform [{}]", tenantId, pkgName, platform); log.trace("Executing findMobileAppBundleByPkgNameAndPlatform, tenantId [{}], pkgName [{}], platform [{}]", tenantId, pkgName, platform);
@ -138,6 +131,19 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements
return mobileAppBundleDao.findByPkgNameAndPlatform(tenantId, pkgName, platform); return mobileAppBundleDao.findByPkgNameAndPlatform(tenantId, pkgName, platform);
} }
@Override
public void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) {
log.trace("Executing deleteMobileAppBundleById [{}]", mobileAppBundleId.getId());
mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId());
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build());
}
@Override
public void deleteByTenantId(TenantId tenantId) {
log.trace("Executing deleteMobileAppsByTenantId, tenantId [{}]", tenantId);
mobileAppBundleDao.deleteByTenantId(tenantId);
}
@Override @Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) { public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findMobileAppBundleById(tenantId, new MobileAppBundleId(entityId.getId()))); return Optional.ofNullable(findMobileAppBundleById(tenantId, new MobileAppBundleId(entityId.getId())));
@ -149,17 +155,6 @@ public class MobileAppBundleServiceImpl extends AbstractEntityService implements
deleteMobileAppBundleById(tenantId, (MobileAppBundleId) id); deleteMobileAppBundleById(tenantId, (MobileAppBundleId) id);
} }
@Override
public void deleteMobileAppBundlesByTenantId(TenantId tenantId) {
log.trace("Executing deleteMobileAppsByTenantId, tenantId [{}]", tenantId);
mobileAppBundleDao.deleteByTenantId(tenantId);
}
@Override
public void deleteByTenantId(TenantId tenantId) {
deleteMobileAppBundlesByTenantId(tenantId);
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.MOBILE_APP_BUNDLE; return EntityType.MOBILE_APP_BUNDLE;

View File

@ -102,12 +102,6 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
deleteMobileAppById(tenantId, (MobileAppId) id); deleteMobileAppById(tenantId, (MobileAppId) id);
} }
@Override
public void deleteMobileAppsByTenantId(TenantId tenantId) {
log.trace("Executing deleteMobileAppsByTenantId, tenantId [{}]", tenantId);
mobileAppDao.deleteByTenantId(tenantId);
}
@Override @Override
public MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType) { public MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType) {
log.trace("Executing findAndroidQrConfig, tenantId [{}], mobileAppBundleId [{}]", tenantId, mobileAppBundleId); log.trace("Executing findAndroidQrConfig, tenantId [{}], mobileAppBundleId [{}]", tenantId, mobileAppBundleId);
@ -123,7 +117,8 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
@Override @Override
public void deleteByTenantId(TenantId tenantId) { public void deleteByTenantId(TenantId tenantId) {
deleteMobileAppsByTenantId(tenantId); log.trace("Executing deleteByTenantId, tenantId [{}]", tenantId);
mobileAppDao.deleteByTenantId(tenantId);
} }
@Override @Override

View File

@ -51,9 +51,9 @@ public final class MobileAppBundleOauth2ClientEntity implements ToData<MobileApp
super(); super();
} }
public MobileAppBundleOauth2ClientEntity(MobileAppBundleOauth2Client domainOauth2Provider) { public MobileAppBundleOauth2ClientEntity(MobileAppBundleOauth2Client mobileAppBundleOauth2Client) {
mobileAppBundleId = domainOauth2Provider.getMobileAppBundleId().getId(); mobileAppBundleId = mobileAppBundleOauth2Client.getMobileAppBundleId().getId();
oauth2ClientId = domainOauth2Provider.getOAuth2ClientId().getId(); oauth2ClientId = mobileAppBundleOauth2Client.getOAuth2ClientId().getId();
} }
@Override @Override

View File

@ -21,9 +21,8 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.app.MobileApp;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QRCodeConfig; import org.thingsboard.server.common.data.mobile.app.MobileAppStatus;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings; import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
import org.thingsboard.server.common.data.mobile.app.StoreInfo;
import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.mobile.MobileAppDao; import org.thingsboard.server.dao.mobile.MobileAppDao;
@ -45,13 +44,13 @@ public class QrCodeSettingsDataValidator extends DataValidator<QrCodeSettings> {
if (!qrCodeSettings.isUseDefaultApp()) { if (!qrCodeSettings.isUseDefaultApp()) {
if (qrCodeSettings.isAndroidEnabled()) { if (qrCodeSettings.isAndroidEnabled()) {
MobileApp androidApp = mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, PlatformType.ANDROID); MobileApp androidApp = mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, PlatformType.ANDROID);
if (androidApp != null && androidApp.getStoreInfo() == null) { if (androidApp != null && androidApp.getStatus() != MobileAppStatus.PUBLISHED) {
throw new DataValidationException("The mobile app bundle references an Android app that has not been published!"); throw new DataValidationException("The mobile app bundle references an Android app that has not been published!");
} }
} }
if (qrCodeSettings.isIosEnabled()) { if (qrCodeSettings.isIosEnabled()) {
MobileApp iosApp = mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, PlatformType.IOS); MobileApp iosApp = mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, PlatformType.IOS);
if (iosApp != null && iosApp.getStoreInfo() == null) { if (iosApp != null && iosApp.getStatus() != MobileAppStatus.PUBLISHED) {
throw new DataValidationException("The mobile app bundle references an iOS app that has not been published!"); throw new DataValidationException("The mobile app bundle references an iOS app that has not been published!");
} }
} }