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 <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort fields for {@link ActionRest}.
|
* Sort & search fields for actions.
|
||||||
*/
|
*/
|
||||||
public enum ActionFields implements FieldNameProvider, FieldValueConverter<ActionFields> {
|
public enum ActionFields implements FieldNameProvider, FieldValueConverter<ActionFields> {
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.sdk;
|
|||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
|
import feign.RequestInterceptor;
|
||||||
import feign.codec.Decoder;
|
import feign.codec.Decoder;
|
||||||
import feign.codec.Encoder;
|
import feign.codec.Encoder;
|
||||||
import feign.codec.ErrorDecoder;
|
import feign.codec.ErrorDecoder;
|
||||||
@@ -22,13 +23,45 @@ import org.springframework.util.ObjectUtils;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Builder
|
@Builder
|
||||||
public class HawkbitClient {
|
public class HawkbitClient {
|
||||||
|
|
||||||
private static final String AUTHORIZATION = "Authorization";
|
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<Tenant, Controller, RequestInterceptor> 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;
|
private final HawkbitServer hawkBitServerProperties;
|
||||||
|
|
||||||
@@ -37,14 +70,31 @@ public class HawkbitClient {
|
|||||||
private final Decoder decoder;
|
private final Decoder decoder;
|
||||||
private final Contract contract;
|
private final Contract contract;
|
||||||
|
|
||||||
|
private final ErrorDecoder errorDecoder;
|
||||||
|
private final BiFunction<Tenant, Controller, RequestInterceptor> requestInterceptorFn;
|
||||||
|
|
||||||
public HawkbitClient(
|
public HawkbitClient(
|
||||||
final HawkbitServer hawkBitServerProperties,
|
final HawkbitServer hawkBitServerProperties,
|
||||||
final Client client, final Encoder encoder, final Decoder decoder, final Contract contract) {
|
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<Tenant, Controller, RequestInterceptor> requestInterceptorFn) {
|
||||||
this.hawkBitServerProperties = hawkBitServerProperties;
|
this.hawkBitServerProperties = hawkBitServerProperties;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.encoder = encoder;
|
this.encoder = encoder;
|
||||||
this.decoder = decoder;
|
this.decoder = decoder;
|
||||||
this.contract = contract;
|
this.contract = contract;
|
||||||
|
|
||||||
|
this.errorDecoder = errorDecoder == null ? DEFAULT_ERROR_DECODER : errorDecoder;
|
||||||
|
this.requestInterceptorFn = requestInterceptorFn == null ? DEFAULT_REQUEST_INTERCEPTOR_FN : requestInterceptorFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T mgmtService(final Class<T> serviceType, final Tenant tenantProperties) {
|
public <T> T mgmtService(final Class<T> serviceType, final Tenant tenantProperties) {
|
||||||
@@ -54,41 +104,16 @@ public class HawkbitClient {
|
|||||||
return service(serviceType, tenantProperties, controller);
|
return service(serviceType, tenantProperties, controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T service(final Class<T> serviceType, final Tenant tenantProperties, final Controller controller) {
|
private <T> T service(final Class<T> serviceType, final Tenant tenant, final Controller controller) {
|
||||||
return Feign.builder().client(client)
|
return Feign.builder().client(client)
|
||||||
.encoder(encoder)
|
.encoder(encoder)
|
||||||
.decoder(decoder)
|
.decoder(decoder)
|
||||||
.errorDecoder((methodKey, response) -> {
|
.errorDecoder(errorDecoder)
|
||||||
final Exception e = DEFAULT_ERROR_DECODER.decode(methodKey, response);
|
|
||||||
log.trace("REST API call failed!", e);
|
|
||||||
return e;
|
|
||||||
})
|
|
||||||
.contract(contract)
|
.contract(contract)
|
||||||
.requestInterceptor(controller == null ?
|
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
|
||||||
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());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.target(serviceType,
|
.target(serviceType,
|
||||||
controller == null ?
|
controller == null ?
|
||||||
hawkBitServerProperties.getMgmtUrl() :
|
hawkBitServerProperties.getMgmtUrl() :
|
||||||
hawkBitServerProperties.getDdiUrl());
|
hawkBitServerProperties.getDdiUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||||
import org.springframework.hateoas.config.WebConverters;
|
import org.springframework.hateoas.config.WebConverters;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -38,6 +39,7 @@ import java.util.LinkedHashMap;
|
|||||||
@EnableConfigurationProperties({ HawkbitServer.class, Tenant.class})
|
@EnableConfigurationProperties({ HawkbitServer.class, Tenant.class})
|
||||||
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
|
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
|
||||||
@Import(FeignClientsConfiguration.class)
|
@Import(FeignClientsConfiguration.class)
|
||||||
|
@PropertySource("classpath:/hawkbit-sdk-defaults.properties")
|
||||||
public class HawkbitSDKConfigurtion {
|
public class HawkbitSDKConfigurtion {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.eclipse.hawkbit.sdk.HawkbitSDKConfigurtion
|
||||||
@@ -8,5 +8,4 @@
|
|||||||
# SPDX-License-Identifier: EPL-2.0
|
# SPDX-License-Identifier: EPL-2.0
|
||||||
#
|
#
|
||||||
|
|
||||||
spring.cloud.openfeign.httpclient.hc5.enabled=true
|
spring.cloud.openfeign.httpclient.hc5.enabled=true
|
||||||
|
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hawkbit-sdk-test</artifactId>
|
<artifactId>hawkbit-sdk-demo</artifactId>
|
||||||
<name>hawkBit :: SDK :: Test / Example</name>
|
<name>hawkBit :: SDK :: Test / Example</name>
|
||||||
<description>Test / Example of how SDK could be used to for devices and for Mgmt API access</description>
|
<description>Test / Example of how SDK could be used to for devices and for Mgmt API access</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-shell.version>3.1.5</spring-shell.version>
|
<spring-shell.version>3.1.5</spring-shell.version>
|
||||||
|
<spring.app.class>org.eclipse.hawkbit.sdk.demo.multidevice.MultiDeviceApp</spring.app.class>
|
||||||
|
<start-class>${spring.app.class}</start-class>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -45,4 +47,30 @@
|
|||||||
<version>${spring-shell.version}</version>
|
<version>${spring-shell.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${baseDir}</outputDirectory>
|
||||||
|
<layout>JAR</layout>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -93,8 +93,10 @@ public class SetupHelper {
|
|||||||
securityTargetToken = randomToken();
|
securityTargetToken = randomToken();
|
||||||
mgmtTargetRestApi.updateTarget(controllerId,
|
mgmtTargetRestApi.updateTarget(controllerId,
|
||||||
new MgmtTargetRequestBody().setSecurityToken(securityTargetToken));
|
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)
|
// update target's with the security token (since it doesn't match)
|
||||||
mgmtTargetRestApi.updateTarget(controllerId,
|
mgmtTargetRestApi.updateTarget(controllerId,
|
||||||
new MgmtTargetRequestBody().setSecurityToken(securityTargetToken));
|
new MgmtTargetRequestBody().setSecurityToken(securityTargetToken));
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Import({ HawkbitSDKConfigurtion.class})
|
|
||||||
public class DeviceApp {
|
public class DeviceApp {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Import({ HawkbitSDKConfigurtion.class})
|
|
||||||
public class MultiDeviceApp {
|
public class MultiDeviceApp {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
spring.main.web-application-type=none
|
spring.main.web-application-type=none
|
||||||
spring.cloud.openfeign.httpclient.hc5.enabled=true
|
|
||||||
|
|
||||||
logging.level.org.eclipse.hawkbit=DEBUG
|
logging.level.org.eclipse.hawkbit=DEBUG
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user