From 8ea3fdb5e7f2ad77e7dead7eb84d24380ce06273 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Wed, 21 Feb 2024 15:44:27 +0200 Subject: [PATCH] SDK AutoConfig & improvements & fixes (#1663) * SDK autoconfiguration added * Option for custom error decoder and request interceptor added * Fixed authentication for targets with security token Signed-off-by: Marinov Avgustin --- .../hawkbit/repository/ActionFields.java | 2 +- .../eclipse/hawkbit/sdk/HawkbitClient.java | 85 ++++++++++++------- .../hawkbit/sdk/HawkbitSDKConfigurtion.java | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...erties => hawkbit-sdk-defaults.properties} | 3 +- hawkbit-sdk/hawkbit-sdk-demo/pom.xml | 30 ++++++- .../eclipse/hawkbit/sdk/demo/SetupHelper.java | 4 +- .../hawkbit/sdk/demo/device/DeviceApp.java | 1 - .../sdk/demo/multidevice/MultiDeviceApp.java | 1 - .../src/main/resources/application.properties | 1 - 10 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/{application.properties => hawkbit-sdk-defaults.properties} (83%) diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java index 6692be0aa..66638f260 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/repository/ActionFields.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; /** - * Sort fields for {@link ActionRest}. + * Sort & search fields for actions. */ public enum ActionFields implements FieldNameProvider, FieldValueConverter { diff --git a/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitClient.java b/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitClient.java index f674b178f..5dbf54f86 100644 --- a/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitClient.java +++ b/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitClient.java @@ -12,6 +12,7 @@ package org.eclipse.hawkbit.sdk; import feign.Client; import feign.Contract; import feign.Feign; +import feign.RequestInterceptor; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; @@ -22,13 +23,45 @@ import org.springframework.util.ObjectUtils; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Objects; +import java.util.function.BiFunction; @Slf4j @Builder public class HawkbitClient { private static final String AUTHORIZATION = "Authorization"; - private static final ErrorDecoder DEFAULT_ERROR_DECODER = new ErrorDecoder.Default(); + private static final ErrorDecoder DEFAULT_ERROR_DECODER_0 = new ErrorDecoder.Default(); + + public static final ErrorDecoder DEFAULT_ERROR_DECODER = (methodKey, response) -> { + final Exception e = DEFAULT_ERROR_DECODER_0.decode(methodKey, response); + log.trace("REST API call failed!", e); + return e; + }; + + public static final BiFunction DEFAULT_REQUEST_INTERCEPTOR_FN = + (tenant, controller) -> + controller == null ? + template -> { + template.header( + AUTHORIZATION, + + "Basic " + + Base64.getEncoder() + .encodeToString( + (Objects.requireNonNull(tenant.getUsername(), "User is null!") + + ":" + + Objects.requireNonNull(tenant.getPassword(),"Password is not available!")) + .getBytes(StandardCharsets.ISO_8859_1))); + } : + template -> { + if (ObjectUtils.isEmpty(tenant.getGatewayToken())) { + if (!ObjectUtils.isEmpty(controller.getSecurityToken())) { + template.header(AUTHORIZATION, "TargetToken " + controller.getSecurityToken()); + } // else do not sent authentication + } else { + template.header(AUTHORIZATION, "GatewayToken " + tenant.getGatewayToken()); + } + }; private final HawkbitServer hawkBitServerProperties; @@ -37,14 +70,31 @@ public class HawkbitClient { private final Decoder decoder; private final Contract contract; + private final ErrorDecoder errorDecoder; + private final BiFunction requestInterceptorFn; + public HawkbitClient( final HawkbitServer hawkBitServerProperties, final Client client, final Encoder encoder, final Decoder decoder, final Contract contract) { + this(hawkBitServerProperties, client, encoder, decoder, contract, null, null); + } + + /** + * Customizers gets default ones and could + */ + public HawkbitClient( + final HawkbitServer hawkBitServerProperties, + final Client client, final Encoder encoder, final Decoder decoder, final Contract contract, + final ErrorDecoder errorDecoder, + final BiFunction requestInterceptorFn) { this.hawkBitServerProperties = hawkBitServerProperties; this.client = client; this.encoder = encoder; this.decoder = decoder; this.contract = contract; + + this.errorDecoder = errorDecoder == null ? DEFAULT_ERROR_DECODER : errorDecoder; + this.requestInterceptorFn = requestInterceptorFn == null ? DEFAULT_REQUEST_INTERCEPTOR_FN : requestInterceptorFn; } public T mgmtService(final Class serviceType, final Tenant tenantProperties) { @@ -54,41 +104,16 @@ public class HawkbitClient { return service(serviceType, tenantProperties, controller); } - private T service(final Class serviceType, final Tenant tenantProperties, final Controller controller) { + private T service(final Class serviceType, final Tenant tenant, final Controller controller) { return Feign.builder().client(client) .encoder(encoder) .decoder(decoder) - .errorDecoder((methodKey, response) -> { - final Exception e = DEFAULT_ERROR_DECODER.decode(methodKey, response); - log.trace("REST API call failed!", e); - return e; - }) + .errorDecoder(errorDecoder) .contract(contract) - .requestInterceptor(controller == null ? - template -> { - template.header(AUTHORIZATION, - "Basic " + - Base64.getEncoder() - .encodeToString( - (Objects.requireNonNull(tenantProperties.getUsername(), - "User is null!") + - ":" + - Objects.requireNonNull(tenantProperties.getPassword(), - "Password is not available!")) - .getBytes(StandardCharsets.ISO_8859_1))); - } : - template -> { - if (ObjectUtils.isEmpty(tenantProperties.getGatewayToken())) { - if (!ObjectUtils.isEmpty(controller.getSecurityToken())) { - template.header(AUTHORIZATION, "TargetToken " + controller.getSecurityToken()); - } // else do not sent authentication - } else { - template.header(AUTHORIZATION, "GatewayToken " + tenantProperties.getGatewayToken()); - } - }) + .requestInterceptor(requestInterceptorFn.apply(tenant, controller)) .target(serviceType, controller == null ? hawkBitServerProperties.getMgmtUrl() : hawkBitServerProperties.getDdiUrl()); } -} +} \ No newline at end of file diff --git a/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitSDKConfigurtion.java b/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitSDKConfigurtion.java index c0641e6c3..f187036a3 100644 --- a/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitSDKConfigurtion.java +++ b/hawkbit-sdk/hawkbit-sdk-commons/src/main/java/org/eclipse/hawkbit/sdk/HawkbitSDKConfigurtion.java @@ -25,6 +25,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.PropertySource; import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.WebConverters; import org.springframework.http.MediaType; @@ -38,6 +39,7 @@ import java.util.LinkedHashMap; @EnableConfigurationProperties({ HawkbitServer.class, Tenant.class}) @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL) @Import(FeignClientsConfiguration.class) +@PropertySource("classpath:/hawkbit-sdk-defaults.properties") public class HawkbitSDKConfigurtion { /** diff --git a/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..f02f927ae --- /dev/null +++ b/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.eclipse.hawkbit.sdk.HawkbitSDKConfigurtion \ No newline at end of file diff --git a/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/application.properties b/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/hawkbit-sdk-defaults.properties similarity index 83% rename from hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/application.properties rename to hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/hawkbit-sdk-defaults.properties index 8b73eecab..f10ea79ed 100644 --- a/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/application.properties +++ b/hawkbit-sdk/hawkbit-sdk-commons/src/main/resources/hawkbit-sdk-defaults.properties @@ -8,5 +8,4 @@ # SPDX-License-Identifier: EPL-2.0 # -spring.cloud.openfeign.httpclient.hc5.enabled=true - +spring.cloud.openfeign.httpclient.hc5.enabled=true \ No newline at end of file diff --git a/hawkbit-sdk/hawkbit-sdk-demo/pom.xml b/hawkbit-sdk/hawkbit-sdk-demo/pom.xml index 3912a1024..7b229a2f2 100644 --- a/hawkbit-sdk/hawkbit-sdk-demo/pom.xml +++ b/hawkbit-sdk/hawkbit-sdk-demo/pom.xml @@ -19,12 +19,14 @@ ${revision} - hawkbit-sdk-test + hawkbit-sdk-demo hawkBit :: SDK :: Test / Example Test / Example of how SDK could be used to for devices and for Mgmt API access 3.1.5 + org.eclipse.hawkbit.sdk.demo.multidevice.MultiDeviceApp + ${spring.app.class} @@ -45,4 +47,30 @@ ${spring-shell.version} + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + ${baseDir} + JAR + + + + + + + + + src/main/resources + + + diff --git a/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/SetupHelper.java b/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/SetupHelper.java index ec4442589..e38ca60e2 100644 --- a/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/SetupHelper.java +++ b/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/SetupHelper.java @@ -93,8 +93,10 @@ public class SetupHelper { securityTargetToken = randomToken(); mgmtTargetRestApi.updateTarget(controllerId, new MgmtTargetRequestBody().setSecurityToken(securityTargetToken)); + } else { + securityTargetToken = target.getSecurityToken(); } - } else if (!securityTargetToken.equals(target.getSecurityToken())){ + } else if (!securityTargetToken.equals(target.getSecurityToken())) { // update target's with the security token (since it doesn't match) mgmtTargetRestApi.updateTarget(controllerId, new MgmtTargetRequestBody().setSecurityToken(securityTargetToken)); diff --git a/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/device/DeviceApp.java b/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/device/DeviceApp.java index c87bb831f..0bae91371 100644 --- a/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/device/DeviceApp.java +++ b/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/device/DeviceApp.java @@ -40,7 +40,6 @@ import java.util.concurrent.ScheduledExecutorService; */ @Slf4j @SpringBootApplication -@Import({ HawkbitSDKConfigurtion.class}) public class DeviceApp { public static void main(String[] args) { diff --git a/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/multidevice/MultiDeviceApp.java b/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/multidevice/MultiDeviceApp.java index d9b55d11a..81fd9d57b 100644 --- a/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/multidevice/MultiDeviceApp.java +++ b/hawkbit-sdk/hawkbit-sdk-demo/src/main/java/org/eclipse/hawkbit/sdk/demo/multidevice/MultiDeviceApp.java @@ -41,7 +41,6 @@ import java.util.concurrent.ScheduledExecutorService; */ @Slf4j @SpringBootApplication -@Import({ HawkbitSDKConfigurtion.class}) public class MultiDeviceApp { public static void main(String[] args) { diff --git a/hawkbit-sdk/hawkbit-sdk-demo/src/main/resources/application.properties b/hawkbit-sdk/hawkbit-sdk-demo/src/main/resources/application.properties index cc3b6310f..ecd3ddf61 100644 --- a/hawkbit-sdk/hawkbit-sdk-demo/src/main/resources/application.properties +++ b/hawkbit-sdk/hawkbit-sdk-demo/src/main/resources/application.properties @@ -9,7 +9,6 @@ # spring.main.web-application-type=none -spring.cloud.openfeign.httpclient.hc5.enabled=true logging.level.org.eclipse.hawkbit=DEBUG