refactored connection classes to be more generic and accept credentials of different apps.
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
package com.vaessl.app.connection;
|
||||||
|
|
||||||
|
import com.vaessl.app.dto.ConnectionRequest;
|
||||||
|
import com.vaessl.app.dto.ConnectionResponse;
|
||||||
|
|
||||||
|
public interface ConnectionProvider {
|
||||||
|
|
||||||
|
String getServiceType();
|
||||||
|
|
||||||
|
ConnectionResponse authenticate (ConnectionRequest connectionRequest);
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.vaessl.app.connection;
|
package com.vaessl.app.connection;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestClient;
|
|
||||||
|
|
||||||
import com.vaessl.app.dto.ConnectionRequest;
|
import com.vaessl.app.dto.ConnectionRequest;
|
||||||
import com.vaessl.app.dto.ConnectionResponse;
|
import com.vaessl.app.dto.ConnectionResponse;
|
||||||
@@ -9,20 +12,20 @@ import com.vaessl.app.dto.ConnectionResponse;
|
|||||||
@Service
|
@Service
|
||||||
public class ConnectionService {
|
public class ConnectionService {
|
||||||
|
|
||||||
private final RestClient.Builder restClientBuilder;
|
private final Map<String, ConnectionProvider> providerRegistry;
|
||||||
|
|
||||||
public ConnectionService(RestClient.Builder restClientBuilder) {
|
public ConnectionService(List<ConnectionProvider> providers) {
|
||||||
this.restClientBuilder = restClientBuilder;
|
this.providerRegistry = providers.stream()
|
||||||
|
.collect(Collectors.toMap(ConnectionProvider::getServiceType, p -> p));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionResponse login(ConnectionRequest request) {
|
public ConnectionResponse login(ConnectionRequest request) {
|
||||||
// TODO: Look into Map<String, RestClient> to cache restclient requests.
|
ConnectionProvider provider = providerRegistry.get(request.serviceType().toUpperCase());
|
||||||
return restClientBuilder.baseUrl(request.appUrl())
|
|
||||||
.build()
|
if (provider == null) {
|
||||||
.post()
|
throw new IllegalArgumentException("Unknown provider: " + request.serviceType());
|
||||||
.uri("/api/v1/users/login")
|
}
|
||||||
.body(request)
|
|
||||||
.retrieve()
|
return provider.authenticate(request);
|
||||||
.body(ConnectionResponse.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.vaessl.app.connection;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum Endpoints {
|
||||||
|
HOMEBOX_LOGIN("/api/v1/users/login");
|
||||||
|
|
||||||
|
private final String endpoint;
|
||||||
|
|
||||||
|
Endpoints(String endpoint){
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.vaessl.app.connection;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
|
|
||||||
|
import com.vaessl.app.dto.ConnectionRequest;
|
||||||
|
import com.vaessl.app.dto.ConnectionResponse;
|
||||||
|
|
||||||
|
import static com.vaessl.app.connection.Endpoints.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class HomeBoxConnectionProvider implements ConnectionProvider {
|
||||||
|
|
||||||
|
private final RestClient.Builder restClientBuilder;
|
||||||
|
|
||||||
|
public HomeBoxConnectionProvider(RestClient.Builder restClientBuilder) {
|
||||||
|
this.restClientBuilder = restClientBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServiceType() {
|
||||||
|
return "HOMEBOX";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionResponse authenticate(ConnectionRequest connectionRequest) {
|
||||||
|
Map<String, Object> homeboxPayload = Map.of("username", connectionRequest.credentials().get("username"),
|
||||||
|
"password", connectionRequest.credentials().get("password"), "stayLoggedIn",
|
||||||
|
connectionRequest.stayLoggedIn());
|
||||||
|
|
||||||
|
return restClientBuilder.baseUrl(connectionRequest.appUrl())
|
||||||
|
.build()
|
||||||
|
.post()
|
||||||
|
.uri(HOMEBOX_LOGIN.getEndpoint())
|
||||||
|
.body(homeboxPayload)
|
||||||
|
.retrieve()
|
||||||
|
.body(ConnectionResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
package com.vaessl.app.dto;
|
package com.vaessl.app.dto;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
public record ConnectionRequest(
|
public record ConnectionRequest(
|
||||||
@NotBlank(message = "App URL is mandatory") String appUrl,
|
@NotBlank(message = "App URL is mandatory") String appUrl,
|
||||||
@NotBlank(message = "Username is mandatory") String username,
|
@NotBlank String serviceType,
|
||||||
@NotBlank(message = "Password is mandatory") String password) {
|
@NotEmpty Map <String, String> credentials,
|
||||||
|
boolean stayLoggedIn) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import lombok.Getter;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum ErrorMessages {
|
public enum ErrorMessages {
|
||||||
|
|
||||||
BAD_REQUEST_EMPTY_FIELDS(HttpStatus.BAD_REQUEST, "Fields must not be empty."), UNAUTHORIZED_WRONG_LOGIN(
|
BAD_REQUEST_EMPTY_FIELDS(HttpStatus.BAD_REQUEST, "Fields must not be empty."), UNAUTHORIZED_WRONG_LOGIN(
|
||||||
HttpStatus.UNAUTHORIZED, "Invalid username or password."), SERVICE_UNAVAILABLE_UNREACHABLE_URL(
|
HttpStatus.UNAUTHORIZED, "Invalid username or password."), SERVICE_UNAVAILABLE_UNREACHABLE_URL(
|
||||||
HttpStatus.SERVICE_UNAVAILABLE, "The target URL is unreachable."), SERVER_ERROR_GENERAL(
|
HttpStatus.SERVICE_UNAVAILABLE, "The target URL is unreachable."), SERVER_ERROR_GENERAL(
|
||||||
|
|||||||
+27
-14
@@ -16,25 +16,27 @@ import com.jayway.jsonpath.JsonPath;
|
|||||||
import com.vaessl.app.dto.ConnectionRequest;
|
import com.vaessl.app.dto.ConnectionRequest;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static com.vaessl.app.connection.Endpoints.*;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@AutoConfigureTestRestTemplate
|
@AutoConfigureTestRestTemplate
|
||||||
@WireMockTest
|
@WireMockTest
|
||||||
public class ConnectionIntegrationTest {
|
public class HomeboxIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
TestRestTemplate restTemplate;
|
TestRestTemplate restTemplate;
|
||||||
|
|
||||||
private static final String API_LOGIN = "/api/v1/users/login";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns Token and status code OK when login is successful.
|
* Returns Token and status code OK when login is successful.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnTokenAndStatusOkWhenCredentialsAreValid(WireMockRuntimeInfo wm) {
|
void shouldReturnTokenAndStatusOkWhenHomeboxCredentialsAreValid(WireMockRuntimeInfo wm) {
|
||||||
|
|
||||||
stubFor(post(API_LOGIN)
|
stubFor(post(HOMEBOX_LOGIN.getEndpoint())
|
||||||
.willReturn(okJson("""
|
.willReturn(okJson("""
|
||||||
{
|
{
|
||||||
"token": "fake-jwt-token",
|
"token": "fake-jwt-token",
|
||||||
@@ -64,7 +66,7 @@ public class ConnectionIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldFailToConnectWhenHomeboxReturnsUnauthorized(WireMockRuntimeInfo wm) {
|
void shouldFailToConnectWhenHomeboxReturnsUnauthorized(WireMockRuntimeInfo wm) {
|
||||||
|
|
||||||
stubFor(post(API_LOGIN).willReturn(unauthorized()));
|
stubFor(post(HOMEBOX_LOGIN.getEndpoint()).willReturn(unauthorized()));
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.postForEntity("/login", connectionRequest(wm), String.class);
|
ResponseEntity<String> response = restTemplate.postForEntity("/login", connectionRequest(wm), String.class);
|
||||||
|
|
||||||
@@ -78,7 +80,7 @@ public class ConnectionIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldFailToConnectWhenHomeboxReturnsServiceUnavailable(WireMockRuntimeInfo wm) {
|
void shouldFailToConnectWhenHomeboxReturnsServiceUnavailable(WireMockRuntimeInfo wm) {
|
||||||
|
|
||||||
stubFor(post(API_LOGIN).willReturn(serviceUnavailable()));
|
stubFor(post(HOMEBOX_LOGIN.getEndpoint()).willReturn(serviceUnavailable()));
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.postForEntity("/login", connectionRequest(wm), String.class);
|
ResponseEntity<String> response = restTemplate.postForEntity("/login", connectionRequest(wm), String.class);
|
||||||
|
|
||||||
@@ -90,15 +92,18 @@ public class ConnectionIntegrationTest {
|
|||||||
* Checks when the service is unavailable or the app URL is wrong.
|
* Checks when the service is unavailable or the app URL is wrong.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnServiceUnavailableWhenUrlIsCompletelyWrong() {
|
void shouldReturnServiceUnavailableWhenHomeboxUrlIsWrong() {
|
||||||
|
|
||||||
ConnectionRequest badRequest = new ConnectionRequest(
|
ConnectionRequest badRequest = new ConnectionRequest(
|
||||||
"http://localhost:1234",
|
"http://localhost:1234",
|
||||||
"user",
|
"HOMEBOX",
|
||||||
"pass");
|
Map.of("username", "myUser", "password", "myPass"),
|
||||||
|
false);
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.postForEntity("/login", badRequest, String.class);
|
ResponseEntity<String> response = restTemplate.postForEntity("/login", badRequest, String.class);
|
||||||
|
|
||||||
|
System.out.println("RESPONSE: " + response.getBody());
|
||||||
|
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||||
assertThat(response.getBody()).contains("The target URL is unreachable.");
|
assertThat(response.getBody()).contains("The target URL is unreachable.");
|
||||||
}
|
}
|
||||||
@@ -107,9 +112,9 @@ public class ConnectionIntegrationTest {
|
|||||||
* Checks if any login fields are empty since all of them are mandatory.
|
* Checks if any login fields are empty since all of them are mandatory.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnBadRequestWhenFieldsAreEmpty() {
|
void shouldReturnBadRequestWhenHomeboxFieldsAreEmpty() {
|
||||||
|
|
||||||
ConnectionRequest emtpyRequest = new ConnectionRequest("", "", "");
|
ConnectionRequest emtpyRequest = new ConnectionRequest("", "", Map.of(), false);
|
||||||
|
|
||||||
ResponseEntity<String> response = restTemplate.postForEntity("/login", emtpyRequest, String.class);
|
ResponseEntity<String> response = restTemplate.postForEntity("/login", emtpyRequest, String.class);
|
||||||
|
|
||||||
@@ -117,7 +122,15 @@ public class ConnectionIntegrationTest {
|
|||||||
assertThat(response.getBody()).contains("Fields must not be empty.");
|
assertThat(response.getBody()).contains("Fields must not be empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConnectionRequest connectionRequest(WireMockRuntimeInfo wireMockRuntimeInfo) {
|
/**
|
||||||
return new ConnectionRequest(wireMockRuntimeInfo.getHttpBaseUrl(), "username", "password");
|
* Creates a valid connection request with a mock Api throuh
|
||||||
|
* WireMockRuntimeInfo.
|
||||||
|
*
|
||||||
|
* @param wm
|
||||||
|
* @return a mock api connection request.
|
||||||
|
*/
|
||||||
|
private ConnectionRequest connectionRequest(WireMockRuntimeInfo wm) {
|
||||||
|
return new ConnectionRequest(wm.getHttpBaseUrl(), "HOMEBOX", Map.of("username", "admin", "password", "pw"),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user