moved 'enabled' from StoreInfo to QrCoodeSettings
This commit is contained in:
parent
c6396e4074
commit
d7582223f4
@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS mobile_app_bundle (
|
|||||||
CONSTRAINT fk_android_app_id FOREIGN KEY (android_app_id) REFERENCES mobile_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)
|
CONSTRAINT fk_ios_app_id FOREIGN KEY (ios_app_id) REFERENCES mobile_app(id)
|
||||||
);
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id);
|
||||||
|
|
||||||
ALTER TABLE mobile_app ADD COLUMN IF NOT EXISTS platform_type varchar(32),
|
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 status varchar(32),
|
||||||
@ -82,31 +83,34 @@ $$
|
|||||||
-- duplicate app for iOS platform type
|
-- duplicate app for iOS platform type
|
||||||
iosAppId := uuid_generate_v4();
|
iosAppId := uuid_generate_v4();
|
||||||
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, app_secret, platform_type, status)
|
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)
|
VALUES (iosAppId, mobileAppRecord.created_time, mobileAppRecord.tenant_id, mobileAppRecord.pkg_name, mobileAppRecord.app_secret, 'IOS', mobileAppRecord.status)
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
-- create bundle for android and iOS app
|
-- create bundle for android and iOS app
|
||||||
generatedBundleId := uuid_generate_v4();
|
generatedBundleId := uuid_generate_v4();
|
||||||
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id, ios_app_id, oauth2_enabled)
|
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,
|
VALUES (generatedBundleId, mobileAppRecord.created_time, mobileAppRecord.tenant_id,
|
||||||
'Autogenerated for ' || mobileAppRecord.pkg_name, mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled)
|
mobileAppRecord.pkg_name || ' (autogenerated)', mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled)
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
UPDATE mobile_app_bundle_oauth2_client SET mobile_app_bundle_id = generatedBundleId WHERE mobile_app_bundle_id = mobileAppRecord.id;
|
UPDATE mobile_app_bundle_oauth2_client SET mobile_app_bundle_id = generatedBundleId WHERE mobile_app_bundle_id = mobileAppRecord.id;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END IF;
|
END IF;
|
||||||
ALTER TABLE mobile_app DROP COLUMN IF EXISTS oauth2_enabled;
|
ALTER TABLE mobile_app DROP COLUMN IF EXISTS oauth2_enabled;
|
||||||
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'pkg_name_platform_unique') THEN
|
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'mobile_app_pkg_name_platform_unq_key') THEN
|
||||||
ALTER TABLE mobile_app ADD CONSTRAINT pkg_name_platform_unique UNIQUE (pkg_name, platform_type);
|
ALTER TABLE mobile_app ADD CONSTRAINT mobile_app_pkg_name_platform_unq_key UNIQUE (pkg_name, platform_type);
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS mobile_app_settings RENAME TO qr_code_settings;
|
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;
|
ALTER TABLE qr_code_settings ADD COLUMN IF NOT EXISTS mobile_app_bundle_id uuid,
|
||||||
|
ADD COLUMN IF NOT EXISTS android_enabled boolean,
|
||||||
|
ADD COLUMN IF NOT EXISTS ios_enabled boolean;
|
||||||
|
|
||||||
-- migrate mobile apps from qr code settings to mobile_app, create mobile app bundle for the pair of apps
|
-- migrate mobile apps from qr code settings to mobile_app, create mobile app bundle for the pair of apps
|
||||||
DO
|
DO
|
||||||
$$
|
$$
|
||||||
DECLARE
|
DECLARE
|
||||||
|
androidPkgName varchar;
|
||||||
iosPkgName varchar;
|
iosPkgName varchar;
|
||||||
androidAppId uuid;
|
androidAppId uuid;
|
||||||
iosAppId uuid;
|
iosAppId uuid;
|
||||||
@ -119,22 +123,28 @@ $$
|
|||||||
LOOP
|
LOOP
|
||||||
generatedBundleId := NULL;
|
generatedBundleId := NULL;
|
||||||
-- migrate android config
|
-- migrate android config
|
||||||
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = qrCodeRecord.android_config::jsonb ->> 'appPackage' AND platform_type = 'ANDROID';
|
IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN
|
||||||
|
androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage';
|
||||||
|
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID';
|
||||||
IF androidAppId IS NULL THEN
|
IF androidAppId IS NULL THEN
|
||||||
androidAppId := uuid_generate_v4();
|
androidAppId := uuid_generate_v4();
|
||||||
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
|
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
|
||||||
VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
|
VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
|
||||||
qrCodeRecord.android_config::jsonb ->> 'appPackage', 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage');
|
androidPkgName, 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled');
|
||||||
generatedBundleId := uuid_generate_v4();
|
generatedBundleId := uuid_generate_v4();
|
||||||
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
|
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
|
||||||
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, 'Autogenerated for qr code', androidAppId);
|
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId);
|
||||||
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.id;
|
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
|
||||||
|
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' WHERE id = androidAppId;
|
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId;
|
||||||
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId) WHERE id = qrCodeRecord.id;
|
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId),
|
||||||
|
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- migrate ios config
|
-- migrate ios config
|
||||||
|
IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN
|
||||||
iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1);
|
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';
|
SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS';
|
||||||
IF iosAppId IS NULL THEN
|
IF iosAppId IS NULL THEN
|
||||||
@ -145,14 +155,17 @@ $$
|
|||||||
IF generatedBundleId IS NULL THEN
|
IF generatedBundleId IS NULL THEN
|
||||||
generatedBundleId := uuid_generate_v4();
|
generatedBundleId := uuid_generate_v4();
|
||||||
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
|
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
|
||||||
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, 'Autogenerated for qr code', iosAppId);
|
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId);
|
||||||
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId WHERE id = qrCodeRecord.id;
|
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
|
||||||
|
ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
|
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
|
||||||
END IF;
|
END IF;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config WHERE id = iosAppId;
|
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId),
|
||||||
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId) WHERE id = qrCodeRecord.id;
|
ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id;
|
||||||
|
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId;
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key;
|
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key;
|
||||||
|
|||||||
@ -95,14 +95,13 @@ public class QrCodeSettingsController extends BaseController {
|
|||||||
private final SystemSecurityService systemSecurityService;
|
private final SystemSecurityService systemSecurityService;
|
||||||
private final MobileAppSecretService mobileAppSecretService;
|
private final MobileAppSecretService mobileAppSecretService;
|
||||||
private final QrCodeSettingService qrCodeSettingService;
|
private final QrCodeSettingService qrCodeSettingService;
|
||||||
private final MobileAppService mobileAppService;
|
|
||||||
|
|
||||||
@ApiOperation(value = "Get associated android applications (getAssetLinks)")
|
@ApiOperation(value = "Get associated android applications (getAssetLinks)")
|
||||||
@GetMapping(value = "/.well-known/assetlinks.json")
|
@GetMapping(value = "/.well-known/assetlinks.json")
|
||||||
public ResponseEntity<JsonNode> getAssetLinks() {
|
public ResponseEntity<JsonNode> getAssetLinks() {
|
||||||
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, ANDROID);
|
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, ANDROID);
|
||||||
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
|
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
|
||||||
if (storeInfo != null && storeInfo.isEnabled() && storeInfo.getSha256CertFingerprints() != null) {
|
if (storeInfo != null && storeInfo.getSha256CertFingerprints() != null) {
|
||||||
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, mobileApp.getPkgName(), storeInfo.getSha256CertFingerprints())));
|
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, mobileApp.getPkgName(), storeInfo.getSha256CertFingerprints())));
|
||||||
} else {
|
} else {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
@ -114,7 +113,7 @@ public class QrCodeSettingsController extends BaseController {
|
|||||||
public ResponseEntity<JsonNode> getAppleAppSiteAssociation() {
|
public ResponseEntity<JsonNode> getAppleAppSiteAssociation() {
|
||||||
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, IOS);
|
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, IOS);
|
||||||
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
|
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
|
||||||
if (storeInfo != null && storeInfo.isEnabled() && storeInfo.getAppId() != null) {
|
if (storeInfo != null && storeInfo.getAppId() != null) {
|
||||||
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, storeInfo.getAppId())));
|
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, storeInfo.getAppId())));
|
||||||
} else {
|
} else {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
@ -177,14 +176,13 @@ public class QrCodeSettingsController extends BaseController {
|
|||||||
@GetMapping(value = "/api/noauth/qr")
|
@GetMapping(value = "/api/noauth/qr")
|
||||||
public ResponseEntity<?> getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) {
|
public ResponseEntity<?> getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) {
|
||||||
QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID);
|
QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID);
|
||||||
boolean useDefaultApp = qrCodeSettings.isUseDefaultApp();
|
if (userAgent.contains("Android") && qrCodeSettings.isAndroidEnabled()) {
|
||||||
if (userAgent.contains("Android")) {
|
String googlePlayLink = qrCodeSettings.getGooglePlayLink();
|
||||||
String googlePlayLink = useDefaultApp ? qrCodeSettings.getGooglePlayLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), ANDROID);
|
|
||||||
return ResponseEntity.status(HttpStatus.FOUND)
|
return ResponseEntity.status(HttpStatus.FOUND)
|
||||||
.header("Location", googlePlayLink)
|
.header("Location", googlePlayLink)
|
||||||
.build();
|
.build();
|
||||||
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) {
|
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad") && qrCodeSettings.isIosEnabled()) {
|
||||||
String appStoreLink = useDefaultApp ? qrCodeSettings.getAppStoreLink() : getStoreLink(qrCodeSettings.getMobileAppBundleId(), IOS);
|
String appStoreLink = qrCodeSettings.getAppStoreLink();
|
||||||
return ResponseEntity.status(HttpStatus.FOUND)
|
return ResponseEntity.status(HttpStatus.FOUND)
|
||||||
.header("Location", appStoreLink)
|
.header("Location", appStoreLink)
|
||||||
.build();
|
.build();
|
||||||
@ -193,12 +191,4 @@ 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.getStoreInfo() != null) ? mobileApp.getStoreInfo().getStoreLink() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,6 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
|
|||||||
StoreInfo androidStoreInfo = StoreInfo.builder()
|
StoreInfo androidStoreInfo = StoreInfo.builder()
|
||||||
.sha256CertFingerprints(ANDROID_APP_SHA256)
|
.sha256CertFingerprints(ANDROID_APP_SHA256)
|
||||||
.storeLink(ANDROID_STORE_LINK)
|
.storeLink(ANDROID_STORE_LINK)
|
||||||
.enabled(true)
|
|
||||||
.build();
|
.build();
|
||||||
androidApp.setStoreInfo(androidStoreInfo);
|
androidApp.setStoreInfo(androidStoreInfo);
|
||||||
MobileApp savedAndroidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
|
MobileApp savedAndroidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
|
||||||
@ -78,7 +77,6 @@ public class QrCodeSettingsControllerTest extends AbstractControllerTest {
|
|||||||
MobileApp iosApp = validMobileApp( "my.ios.package", PlatformType.IOS);
|
MobileApp iosApp = validMobileApp( "my.ios.package", PlatformType.IOS);
|
||||||
StoreInfo iosQrCodeConfig = StoreInfo.builder()
|
StoreInfo iosQrCodeConfig = StoreInfo.builder()
|
||||||
.appId(APPLE_APP_ID)
|
.appId(APPLE_APP_ID)
|
||||||
.enabled(true)
|
|
||||||
.storeLink(IOS_STORE_LINK)
|
.storeLink(IOS_STORE_LINK)
|
||||||
.build();
|
.build();
|
||||||
iosApp.setStoreInfo(iosQrCodeConfig);
|
iosApp.setStoreInfo(iosQrCodeConfig);
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
|
|||||||
import org.thingsboard.server.common.data.AttributeScope;
|
import org.thingsboard.server.common.data.AttributeScope;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.EventInfo;
|
import org.thingsboard.server.common.data.EventInfo;
|
||||||
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
import org.thingsboard.server.common.data.alarm.EntityAlarm;
|
import org.thingsboard.server.common.data.alarm.EntityAlarm;
|
||||||
@ -45,6 +46,9 @@ import org.thingsboard.server.common.data.id.AlarmId;
|
|||||||
import org.thingsboard.server.common.data.id.AssetId;
|
import org.thingsboard.server.common.data.id.AssetId;
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
import org.thingsboard.server.common.data.id.MobileAppBundleId;
|
||||||
|
import org.thingsboard.server.common.data.id.MobileAppId;
|
||||||
|
import org.thingsboard.server.common.data.id.OAuth2ClientId;
|
||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||||
import org.thingsboard.server.common.data.id.RuleNodeId;
|
import org.thingsboard.server.common.data.id.RuleNodeId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
@ -54,7 +58,12 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
|
|||||||
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
|
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
|
||||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
||||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||||
|
import org.thingsboard.server.common.data.mobile.app.MobileApp;
|
||||||
|
import org.thingsboard.server.common.data.mobile.app.MobileAppStatus;
|
||||||
|
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
|
||||||
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
|
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
|
||||||
|
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
|
||||||
|
import org.thingsboard.server.common.data.oauth2.PlatformType;
|
||||||
import org.thingsboard.server.common.data.page.TimePageLink;
|
import org.thingsboard.server.common.data.page.TimePageLink;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||||
@ -233,6 +242,27 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
|
|||||||
tenantId = differentTenantId;
|
tenantId = differentTenantId;
|
||||||
|
|
||||||
createRelatedData(tenantId);
|
createRelatedData(tenantId);
|
||||||
|
|
||||||
|
MobileApp androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID);
|
||||||
|
androidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
|
||||||
|
MobileAppId androidAppId = androidApp.getId();
|
||||||
|
|
||||||
|
MobileApp iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS);
|
||||||
|
iosApp = doPost("/api/mobile/app", iosApp, MobileApp.class);
|
||||||
|
MobileAppId iosAppId = androidApp.getId();
|
||||||
|
|
||||||
|
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
|
||||||
|
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
|
||||||
|
OAuth2ClientId oAuth2ClientId = savedOAuth2Client.getId();
|
||||||
|
|
||||||
|
MobileAppBundle mobileAppBundle = new MobileAppBundle();
|
||||||
|
mobileAppBundle.setTitle("Test bundle");
|
||||||
|
mobileAppBundle.setAndroidAppId(androidApp.getId());
|
||||||
|
mobileAppBundle.setIosAppId(iosApp.getId());
|
||||||
|
|
||||||
|
MobileAppBundle savedAppBundle = doPost("/api/mobile/bundle?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileAppBundle, MobileAppBundle.class);
|
||||||
|
MobileAppBundleId appBundleId = savedAppBundle.getId();
|
||||||
|
|
||||||
createDifferentTenantCustomer();
|
createDifferentTenantCustomer();
|
||||||
createRelatedData(differentTenantCustomerId);
|
createRelatedData(differentTenantCustomerId);
|
||||||
loginDifferentTenant();
|
loginDifferentTenant();
|
||||||
@ -279,6 +309,10 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
|
|||||||
verifyNoRelatedData(userId);
|
verifyNoRelatedData(userId);
|
||||||
verifyNoRelatedData(differentTenantCustomerId);
|
verifyNoRelatedData(differentTenantCustomerId);
|
||||||
verifyNoRelatedData(tenantApiUsageState.getId());
|
verifyNoRelatedData(tenantApiUsageState.getId());
|
||||||
|
verifyNoRelatedData(androidAppId);
|
||||||
|
verifyNoRelatedData(iosAppId);
|
||||||
|
verifyNoRelatedData(oAuth2ClientId);
|
||||||
|
verifyNoRelatedData(appBundleId);
|
||||||
verifyNoRelatedData(tenantId);
|
verifyNoRelatedData(tenantId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -483,4 +517,14 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
|
|||||||
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId);
|
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, PlatformType platformType) {
|
||||||
|
MobileApp mobileApp = new MobileApp();
|
||||||
|
mobileApp.setTenantId(tenantId);
|
||||||
|
mobileApp.setStatus(MobileAppStatus.DRAFT);
|
||||||
|
mobileApp.setPkgName(mobileAppName);
|
||||||
|
mobileApp.setPlatformType(platformType);
|
||||||
|
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
|
||||||
|
return mobileApp;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.validation.NoXss;
|
|||||||
@Builder
|
@Builder
|
||||||
public class StoreInfo {
|
public class StoreInfo {
|
||||||
|
|
||||||
private boolean enabled;
|
|
||||||
@NoXss
|
@NoXss
|
||||||
private String appId;
|
private String appId;
|
||||||
@NoXss
|
@NoXss
|
||||||
|
|||||||
@ -44,10 +44,12 @@ public class QrCodeSettings extends BaseData<QrCodeSettingsId> implements HasTen
|
|||||||
@Valid
|
@Valid
|
||||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "QR code config configuration.")
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "QR code config configuration.")
|
||||||
private QRCodeConfig qrCodeConfig;
|
private QRCodeConfig qrCodeConfig;
|
||||||
|
@Schema(description = "Indicates if google play link is available", example = "true")
|
||||||
|
private boolean androidEnabled;
|
||||||
|
@Schema(description = "Indicates if apple store link is available", example = "true")
|
||||||
|
private boolean iosEnabled;
|
||||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
private String googlePlayLink;
|
private String googlePlayLink;
|
||||||
|
|
||||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
private String appStoreLink;
|
private String appStoreLink;
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
|
|||||||
return savedMobileApp;
|
return savedMobileApp;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
checkConstraintViolation(e,
|
checkConstraintViolation(e,
|
||||||
Map.of("mobile_app_unq_key", "Mobile app with such package already exists!"));
|
Map.of("mobile_app_pkg_name_platform_unq_key", "Mobile app with such package name and platform already exists!"));
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -119,10 +119,10 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService<Tenant
|
|||||||
} else {
|
} else {
|
||||||
MobileApp androidApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), ANDROID);
|
MobileApp androidApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), ANDROID);
|
||||||
MobileApp iosApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), IOS);
|
MobileApp iosApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), IOS);
|
||||||
if (androidApp != null && androidApp.getStoreInfo() != null && androidApp.getStoreInfo().isEnabled()) {
|
if (androidApp != null && androidApp.getStoreInfo() != null) {
|
||||||
qrCodeSettings.setGooglePlayLink(androidApp.getStoreInfo().getStoreLink());
|
qrCodeSettings.setGooglePlayLink(androidApp.getStoreInfo().getStoreLink());
|
||||||
}
|
}
|
||||||
if (iosApp != null && iosApp.getStoreInfo() != null && iosApp.getStoreInfo().isEnabled()) {
|
if (iosApp != null && iosApp.getStoreInfo() != null) {
|
||||||
qrCodeSettings.setAppStoreLink(iosApp.getStoreInfo().getStoreLink());
|
qrCodeSettings.setAppStoreLink(iosApp.getStoreInfo().getStoreLink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -701,6 +701,8 @@ public class ModelConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String QR_CODE_SETTINGS_TABLE_NAME = "qr_code_settings";
|
public static final String QR_CODE_SETTINGS_TABLE_NAME = "qr_code_settings";
|
||||||
public static final String QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY = "use_default_app";
|
public static final String QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY = "use_default_app";
|
||||||
|
public static final String QR_CODE_SETTINGS_ANDROID_ENABLED_PROPERTY = "android_enabled";
|
||||||
|
public static final String QR_CODE_SETTINGS_IOS_ENABLED_PROPERTY = "ios_enabled";
|
||||||
public static final String QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY = "mobile_app_bundle_id";
|
public static final String QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY = "mobile_app_bundle_id";
|
||||||
public static final String QR_CODE_SETTINGS_CONFIG_PROPERTY = "qr_code_config";
|
public static final String QR_CODE_SETTINGS_CONFIG_PROPERTY = "qr_code_config";
|
||||||
|
|
||||||
|
|||||||
@ -47,6 +47,12 @@ public class QrCodeSettingsEntity extends BaseSqlEntity<QrCodeSettings> {
|
|||||||
@Column(name = ModelConstants.QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY)
|
@Column(name = ModelConstants.QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY)
|
||||||
private boolean useDefaultApp;
|
private boolean useDefaultApp;
|
||||||
|
|
||||||
|
@Column(name = ModelConstants.QR_CODE_SETTINGS_ANDROID_ENABLED_PROPERTY)
|
||||||
|
private boolean androidEnabled;
|
||||||
|
|
||||||
|
@Column(name = ModelConstants.QR_CODE_SETTINGS_IOS_ENABLED_PROPERTY)
|
||||||
|
private boolean iosEnabled;
|
||||||
|
|
||||||
@Column(name = ModelConstants.QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY)
|
@Column(name = ModelConstants.QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY)
|
||||||
private UUID mobileAppBundleId;
|
private UUID mobileAppBundleId;
|
||||||
|
|
||||||
@ -59,6 +65,8 @@ public class QrCodeSettingsEntity extends BaseSqlEntity<QrCodeSettings> {
|
|||||||
this.setCreatedTime(qrCodeSettings.getCreatedTime());
|
this.setCreatedTime(qrCodeSettings.getCreatedTime());
|
||||||
this.tenantId = qrCodeSettings.getTenantId().getId();
|
this.tenantId = qrCodeSettings.getTenantId().getId();
|
||||||
this.useDefaultApp = qrCodeSettings.isUseDefaultApp();
|
this.useDefaultApp = qrCodeSettings.isUseDefaultApp();
|
||||||
|
this.androidEnabled = qrCodeSettings.isAndroidEnabled();
|
||||||
|
this.iosEnabled = qrCodeSettings.isIosEnabled();
|
||||||
if (qrCodeSettings.getMobileAppBundleId() != null) {
|
if (qrCodeSettings.getMobileAppBundleId() != null) {
|
||||||
this.mobileAppBundleId = qrCodeSettings.getMobileAppBundleId().getId();
|
this.mobileAppBundleId = qrCodeSettings.getMobileAppBundleId().getId();
|
||||||
}
|
}
|
||||||
@ -71,6 +79,8 @@ public class QrCodeSettingsEntity extends BaseSqlEntity<QrCodeSettings> {
|
|||||||
qrCodeSettings.setCreatedTime(createdTime);
|
qrCodeSettings.setCreatedTime(createdTime);
|
||||||
qrCodeSettings.setTenantId(TenantId.fromUUID(tenantId));
|
qrCodeSettings.setTenantId(TenantId.fromUUID(tenantId));
|
||||||
qrCodeSettings.setUseDefaultApp(useDefaultApp);
|
qrCodeSettings.setUseDefaultApp(useDefaultApp);
|
||||||
|
qrCodeSettings.setAndroidEnabled(androidEnabled);
|
||||||
|
qrCodeSettings.setIosEnabled(iosEnabled);
|
||||||
if (mobileAppBundleId != null) {
|
if (mobileAppBundleId != null) {
|
||||||
qrCodeSettings.setMobileAppBundleId(new MobileAppBundleId(mobileAppBundleId));
|
qrCodeSettings.setMobileAppBundleId(new MobileAppBundleId(mobileAppBundleId));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,12 +31,12 @@ public class MobileAppDataValidator extends DataValidator<MobileApp> {
|
|||||||
@Override
|
@Override
|
||||||
protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) {
|
protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) {
|
||||||
if (mobileApp.getPlatformType() == PlatformType.ANDROID) {
|
if (mobileApp.getPlatformType() == PlatformType.ANDROID) {
|
||||||
if (mobileApp.getStoreInfo() != null && mobileApp.getStoreInfo().isEnabled() &&
|
if (mobileApp.getStoreInfo() != null &&
|
||||||
(mobileApp.getStoreInfo().getSha256CertFingerprints() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
|
(mobileApp.getStoreInfo().getSha256CertFingerprints() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
|
||||||
throw new DataValidationException("Sha256CertFingerprints and store link are required");
|
throw new DataValidationException("Sha256CertFingerprints and store link are required");
|
||||||
}
|
}
|
||||||
} else if (mobileApp.getPlatformType() == PlatformType.IOS) {
|
} else if (mobileApp.getPlatformType() == PlatformType.IOS) {
|
||||||
if (mobileApp.getStoreInfo() != null && mobileApp.getStoreInfo().isEnabled() &&
|
if (mobileApp.getStoreInfo() != null &&
|
||||||
(mobileApp.getStoreInfo().getAppId() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
|
(mobileApp.getStoreInfo().getAppId() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
|
||||||
throw new DataValidationException("AppId and store link are required");
|
throw new DataValidationException("AppId and store link are required");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,3 +127,5 @@ CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag);
|
|||||||
CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag);
|
CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_resource_type_public_resource_key ON resource(resource_type, public_resource_key);
|
CREATE INDEX IF NOT EXISTS idx_resource_type_public_resource_key ON resource(resource_type, public_resource_key);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id);
|
||||||
|
|||||||
@ -628,7 +628,7 @@ CREATE TABLE IF NOT EXISTS mobile_app (
|
|||||||
status varchar(32),
|
status varchar(32),
|
||||||
version_info varchar(100000),
|
version_info varchar(100000),
|
||||||
store_info varchar(16384),
|
store_info varchar(16384),
|
||||||
CONSTRAINT pkg_name_platform_unique UNIQUE (pkg_name, platform_type)
|
CONSTRAINT mobile_app_pkg_name_platform_unq_key UNIQUE (pkg_name, platform_type)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS mobile_app_bundle (
|
CREATE TABLE IF NOT EXISTS mobile_app_bundle (
|
||||||
|
|||||||
@ -126,6 +126,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
|||||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||||||
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.bundle.MobileAppBundle;
|
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
|
||||||
|
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
|
||||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
|
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
|
||||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
|
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
|
||||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
|
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
|
||||||
@ -2108,12 +2109,12 @@ public class RestClient implements Closeable {
|
|||||||
}, params).getBody();
|
}, params).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<OAuth2ClientInfo> getTenantOAuth2Clients() {
|
public PageData<OAuth2ClientInfo> getTenantOAuth2Clients() {
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
baseURL + "/api/oauth2/client/infos",
|
baseURL + "/api/oauth2/client/infos",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
HttpEntity.EMPTY,
|
HttpEntity.EMPTY,
|
||||||
new ParameterizedTypeReference<List<OAuth2ClientInfo>>() {
|
new ParameterizedTypeReference<PageData<OAuth2ClientInfo>>() {
|
||||||
}).getBody();
|
}).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2138,12 +2139,12 @@ public class RestClient implements Closeable {
|
|||||||
restTemplate.delete(baseURL + "/api/oauth2/client/{id}", oAuth2ClientId.getId());
|
restTemplate.delete(baseURL + "/api/oauth2/client/{id}", oAuth2ClientId.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DomainInfo> getTenantDomainInfos() {
|
public PageData<DomainInfo> getTenantDomainInfos() {
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
baseURL + "/api/domain/infos",
|
baseURL + "/api/domain/infos",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
HttpEntity.EMPTY,
|
HttpEntity.EMPTY,
|
||||||
new ParameterizedTypeReference<List<DomainInfo>>() {
|
new ParameterizedTypeReference<PageData<DomainInfo>>() {
|
||||||
}).getBody();
|
}).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2172,12 +2173,12 @@ public class RestClient implements Closeable {
|
|||||||
restTemplate.postForLocation(baseURL + "/api/domain/{id}/oauth2Clients", oauth2ClientIds, domainId.getId());
|
restTemplate.postForLocation(baseURL + "/api/domain/{id}/oauth2Clients", oauth2ClientIds, domainId.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DomainInfo> getTenantMobileApps() {
|
public PageData<MobileApp> getTenantMobileApps() {
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
baseURL + "/api/mobile/app",
|
baseURL + "/api/mobile/app",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
HttpEntity.EMPTY,
|
HttpEntity.EMPTY,
|
||||||
new ParameterizedTypeReference<List<DomainInfo>>() {
|
new ParameterizedTypeReference<PageData<MobileApp>>() {
|
||||||
}).getBody();
|
}).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2202,18 +2203,18 @@ public class RestClient implements Closeable {
|
|||||||
restTemplate.delete(baseURL + "/api/mobile/app/{id}", mobileAppId.getId());
|
restTemplate.delete(baseURL + "/api/mobile/app/{id}", mobileAppId.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DomainInfo> getTenantMobileBundleInfos() {
|
public PageData<MobileAppBundleInfo> getTenantMobileBundleInfos() {
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
baseURL + "/api/mobile/bundle/infos",
|
baseURL + "/api/mobile/bundle/infos",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
HttpEntity.EMPTY,
|
HttpEntity.EMPTY,
|
||||||
new ParameterizedTypeReference<List<DomainInfo>>() {
|
new ParameterizedTypeReference<PageData<MobileAppBundleInfo>>() {
|
||||||
}).getBody();
|
}).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<MobileAppBundle> getMobileBundleById(MobileAppBundleId mobileAppBundleId) {
|
public Optional<MobileAppBundle> getMobileBundleById(MobileAppBundleId mobileAppBundleId) {
|
||||||
try {
|
try {
|
||||||
ResponseEntity<MobileAppBundle> mobileApp = restTemplate.getForEntity(baseURL + "/api/mobile/app/{id}", MobileAppBundle.class, mobileAppBundleId.getId());
|
ResponseEntity<MobileAppBundle> mobileApp = restTemplate.getForEntity(baseURL + "/api/mobile/bundle/{id}", MobileAppBundle.class, mobileAppBundleId.getId());
|
||||||
return Optional.ofNullable(mobileApp.getBody());
|
return Optional.ofNullable(mobileApp.getBody());
|
||||||
} catch (HttpClientErrorException exception) {
|
} catch (HttpClientErrorException exception) {
|
||||||
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
|
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user