add session-based connection management and React dashboard
Backend: adds JDBC session support, login/status/logout endpoints, and new DTOs (AuthResponse, ConnectionStatusResponse, LoginResult). Frontend replaces the Vite boilerplate with a Dashboard, ServiceCard, and ConnectModal backed by a typed API client. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ extra["springAiVersion"] = "2.0.0-M3"
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-session-jdbc")
|
||||
// implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||
implementation("org.springframework.boot:spring-boot-starter-webmvc")
|
||||
@@ -40,7 +41,8 @@ dependencies {
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-validation-test")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testImplementation("org.wiremock:wiremock-standalone:3.12.0")}
|
||||
testImplementation("org.wiremock:wiremock-standalone:3.12.0")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-session-jdbc-test")}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
@@ -48,6 +50,10 @@ dependencyManagement {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.compilerArgs.add("-parameters")
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.vaessl.app.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
@Value("${vaessl.frontend-url}")
|
||||
private String frontendUrl;
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins(frontendUrl, "http://192.168.1.208:5173", "https://5173.code-server.kasuns.website")
|
||||
.allowedMethods("GET", "POST", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("Content-Type", "Accept")
|
||||
.allowCredentials(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.vaessl.app.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.DefaultCookieSerializer;
|
||||
|
||||
@Configuration
|
||||
public class SessionConfig {
|
||||
|
||||
@Bean
|
||||
public CookieSerializer cookieSerializer() {
|
||||
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
|
||||
serializer.setCookieMaxAge(Integer.MAX_VALUE);
|
||||
serializer.setUseHttpOnlyCookie(true);
|
||||
serializer.setSameSite("Lax");
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,26 @@
|
||||
package com.vaessl.app.connection;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.vaessl.app.dto.AuthResponse;
|
||||
import com.vaessl.app.dto.ConnectionRequest;
|
||||
import com.vaessl.app.dto.ConnectionResponse;
|
||||
import com.vaessl.app.dto.ConnectionStatusResponse;
|
||||
import com.vaessl.app.dto.LoginResult;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@@ -15,10 +28,63 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public class ConnectionController {
|
||||
|
||||
private static final String SUFFIX = "_CONNECTION_ID";
|
||||
|
||||
private final ConnectionService connectionService;
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<ConnectionResponse> loginResponse(@Valid @RequestBody ConnectionRequest request) {
|
||||
return ResponseEntity.ok(connectionService.login(request));
|
||||
public ResponseEntity<AuthResponse> login(
|
||||
@Valid @RequestBody ConnectionRequest request,
|
||||
HttpServletRequest httpReq) {
|
||||
|
||||
LoginResult result = connectionService.login(request);
|
||||
|
||||
HttpSession session = httpReq.getSession(true);
|
||||
session.setAttribute(request.serviceType() + SUFFIX, result.connectionId());
|
||||
|
||||
if (result.expiresAt() != null) {
|
||||
long secs = Instant.now().until(result.expiresAt(), ChronoUnit.SECONDS);
|
||||
session.setMaxInactiveInterval((int) Math.max(secs, 300));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(new AuthResponse(request.serviceType(), result.expiresAt()));
|
||||
}
|
||||
|
||||
@GetMapping("/connections/status")
|
||||
public ResponseEntity<List<ConnectionStatusResponse>> getStatus(HttpServletRequest httpReq) {
|
||||
HttpSession session = httpReq.getSession(false);
|
||||
if (session == null) {
|
||||
return ResponseEntity.ok(List.of());
|
||||
}
|
||||
|
||||
List<ConnectionStatusResponse> statuses = new ArrayList<>();
|
||||
Collections.list(session.getAttributeNames()).stream()
|
||||
.filter(k -> k.endsWith(SUFFIX))
|
||||
.forEach(k -> {
|
||||
String serviceType = k.replace(SUFFIX, "");
|
||||
Long id = (Long) session.getAttribute(k);
|
||||
ConnectionStatusResponse status = connectionService.getConnectionStatus(serviceType, id);
|
||||
if (status != null) statuses.add(status);
|
||||
});
|
||||
|
||||
return ResponseEntity.ok(statuses);
|
||||
}
|
||||
|
||||
@DeleteMapping("/connections/{serviceType}")
|
||||
public ResponseEntity<Void> logout(
|
||||
@PathVariable("serviceType") String serviceType,
|
||||
HttpServletRequest httpReq) {
|
||||
|
||||
HttpSession session = httpReq.getSession(false);
|
||||
if (session != null) {
|
||||
session.removeAttribute(serviceType + SUFFIX);
|
||||
boolean hasMore = Collections.list(session.getAttributeNames()).stream()
|
||||
.anyMatch(k -> k.endsWith(SUFFIX));
|
||||
if (!hasMore) {
|
||||
session.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.vaessl.app.connection;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import com.vaessl.app.dto.ConnectionRequest;
|
||||
import com.vaessl.app.dto.ConnectionResponse;
|
||||
|
||||
@@ -16,4 +18,8 @@ public interface ConnectionProvider {
|
||||
ConnectionEntity connectionToEntity(ConnectionRequest request, ConnectionResponse response);
|
||||
|
||||
void updateToRepository(ConnectionEntity existing, ConnectionResponse response);
|
||||
|
||||
default Instant getTokenExpiry(ConnectionEntity entity) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.vaessl.app.connection;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -8,6 +9,8 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.vaessl.app.dto.ConnectionRequest;
|
||||
import com.vaessl.app.dto.ConnectionResponse;
|
||||
import com.vaessl.app.dto.ConnectionStatusResponse;
|
||||
import com.vaessl.app.dto.LoginResult;
|
||||
import com.vaessl.app.exception.WrongServiceTypeException;
|
||||
|
||||
@Service
|
||||
@@ -23,7 +26,7 @@ public class ConnectionService {
|
||||
this.cRepository = cRepository;
|
||||
}
|
||||
|
||||
public ConnectionResponse login(ConnectionRequest request) {
|
||||
public LoginResult login(ConnectionRequest request) {
|
||||
|
||||
ConnectionProvider provider = providerRegistry.get(request.serviceType());
|
||||
|
||||
@@ -37,13 +40,27 @@ public class ConnectionService {
|
||||
|
||||
ConnectionEntity existing = provider.findUniqueConnectionEntry(request);
|
||||
|
||||
ConnectionEntity saved;
|
||||
if (existing != null) {
|
||||
provider.updateToRepository(existing, response);
|
||||
saved = existing;
|
||||
} else {
|
||||
ConnectionEntity newEntity = provider.connectionToEntity(request, response);
|
||||
cRepository.save(newEntity);
|
||||
saved = cRepository.save(newEntity);
|
||||
}
|
||||
|
||||
return response;
|
||||
return new LoginResult(saved.getId(), response.expiresAt());
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionStatusResponse getConnectionStatus(String serviceType, Long connectionId) {
|
||||
ConnectionEntity entity = cRepository.findById(connectionId).orElse(null);
|
||||
if (entity == null) return null;
|
||||
|
||||
ConnectionProvider provider = providerRegistry.get(serviceType);
|
||||
Instant expiresAt = (provider != null) ? provider.getTokenExpiry(entity) : null;
|
||||
boolean connected = expiresAt == null || expiresAt.isAfter(Instant.now());
|
||||
|
||||
return new ConnectionStatusResponse(serviceType, entity.getAppUrl(),
|
||||
entity.getUsername(), expiresAt, connected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.vaessl.app.connection;
|
||||
|
||||
public enum Endpoint {
|
||||
HOMEBOX_LOGIN("/api/v1/users/login"), LOGIN("/login");
|
||||
HOMEBOX_LOGIN("/api/v1/users/login"),
|
||||
LOGIN("/login"),
|
||||
CONNECTION_STATUS("/connections/status");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
||||
@@ -97,6 +97,11 @@ public class HomeBoxConnectionProvider implements ConnectionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getTokenExpiry(ConnectionEntity entity) {
|
||||
return (entity instanceof HomeboxEntity he) ? he.getExpiresAt() : null;
|
||||
}
|
||||
|
||||
private record HomeboxLoginResponse(String token, String attachmentToken, Instant expiresAt) {
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.vaessl.app.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record AuthResponse(String serviceType, Instant expiresAt) {}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.vaessl.app.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record ConnectionStatusResponse(
|
||||
String serviceType,
|
||||
String appUrl,
|
||||
String username,
|
||||
Instant expiresAt,
|
||||
boolean connected) {}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.vaessl.app.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record LoginResult(Long connectionId, Instant expiresAt) {}
|
||||
@@ -0,0 +1,12 @@
|
||||
{"properties": [
|
||||
{
|
||||
"name": "spring.session.store-type",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'spring.session.store-type'"
|
||||
},
|
||||
{
|
||||
"name": "vaessl.frontend-url",
|
||||
"type": "java.lang.String",
|
||||
"description": "A description for 'vaessl.frontend-url'"
|
||||
}
|
||||
]}
|
||||
@@ -22,6 +22,12 @@ spring:
|
||||
chat:
|
||||
options:
|
||||
model: gpt-4o-mini
|
||||
session:
|
||||
store-type: jdbc
|
||||
jdbc:
|
||||
initialize-schema: always
|
||||
server:
|
||||
servlet:
|
||||
context-path: /api
|
||||
vaessl:
|
||||
frontend-url: ${FRONTEND_URL:http://192.168.1.208:5173/}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.vaessl.app.connection;
|
||||
|
||||
import static com.vaessl.app.connection.Endpoint.*;
|
||||
import static com.vaessl.app.connection.ServiceType.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
|
||||
import jakarta.servlet.http.Cookie;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@WireMockTest
|
||||
class ConnectionControllerTest {
|
||||
|
||||
@Autowired
|
||||
MockMvc mockMvc;
|
||||
|
||||
private static final String TEST_USER = "admin";
|
||||
private static final String TEST_PASS = "pw";
|
||||
private static final String LOGIN_PATH = LOGIN.getValue();
|
||||
private static final String STATUS_PATH = CONNECTION_STATUS.getValue();
|
||||
private static final String LOGOUT_PATH = "/connections/HOMEBOX";
|
||||
|
||||
private static final String VALID_HOMEBOX_RESPONSE = """
|
||||
{
|
||||
"token": "fake-jwt-token",
|
||||
"attachmentToken": "fake-attach",
|
||||
"expiresAt": "2099-01-01T00:00:00Z"
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String EXPIRED_HOMEBOX_RESPONSE = """
|
||||
{
|
||||
"token": "expired-token",
|
||||
"attachmentToken": "fake-attach",
|
||||
"expiresAt": "2000-01-01T00:00:00Z"
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyListWhenNoActiveSession() throws Exception {
|
||||
mockMvc.perform(get(STATUS_PATH))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().json("[]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnConnectionStatusWithConnectedTrueAfterLogin(WireMockRuntimeInfo wm) throws Exception {
|
||||
WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue()).willReturn(WireMock.okJson(VALID_HOMEBOX_RESPONSE)));
|
||||
|
||||
MvcResult loginResult = mockMvc.perform(post(LOGIN_PATH)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(connectionRequestBody(wm)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION");
|
||||
|
||||
mockMvc.perform(get(STATUS_PATH).cookie(sessionCookie))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.length()").value(1))
|
||||
.andExpect(jsonPath("$[0].serviceType").value(HOMEBOX.getValue()))
|
||||
.andExpect(jsonPath("$[0].username").value(TEST_USER))
|
||||
.andExpect(jsonPath("$[0].appUrl").value(wm.getHttpBaseUrl()))
|
||||
.andExpect(jsonPath("$[0].connected").value(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnConnectedFalseWhenStoredTokenIsExpired(WireMockRuntimeInfo wm) throws Exception {
|
||||
WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue()).willReturn(WireMock.okJson(EXPIRED_HOMEBOX_RESPONSE)));
|
||||
|
||||
MvcResult loginResult = mockMvc.perform(post(LOGIN_PATH)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(connectionRequestBody(wm)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION");
|
||||
|
||||
mockMvc.perform(get(STATUS_PATH).cookie(sessionCookie))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].serviceType").value(HOMEBOX.getValue()))
|
||||
.andExpect(jsonPath("$[0].connected").value(false))
|
||||
.andExpect(jsonPath("$[0].expiresAt").value("2000-01-01T00:00:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturn204NoContentOnLogout(WireMockRuntimeInfo wm) throws Exception {
|
||||
WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue()).willReturn(WireMock.okJson(VALID_HOMEBOX_RESPONSE)));
|
||||
|
||||
MvcResult loginResult = mockMvc.perform(post(LOGIN_PATH)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(connectionRequestBody(wm)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION");
|
||||
|
||||
mockMvc.perform(delete(LOGOUT_PATH).cookie(sessionCookie))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyStatusListAfterLogout(WireMockRuntimeInfo wm) throws Exception {
|
||||
WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue()).willReturn(WireMock.okJson(VALID_HOMEBOX_RESPONSE)));
|
||||
|
||||
MvcResult loginResult = mockMvc.perform(post(LOGIN_PATH)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(connectionRequestBody(wm)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION");
|
||||
|
||||
// Verify connected before logout
|
||||
mockMvc.perform(get(STATUS_PATH).cookie(sessionCookie))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.length()").value(1));
|
||||
|
||||
mockMvc.perform(delete(LOGOUT_PATH).cookie(sessionCookie))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
// A new request (no session cookie, as in a fresh browser) returns no connections
|
||||
mockMvc.perform(get(STATUS_PATH))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().json("[]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturn204WhenLogoutCalledWithNoActiveSession() throws Exception {
|
||||
mockMvc.perform(delete(LOGOUT_PATH))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
private String connectionRequestBody(WireMockRuntimeInfo wm) {
|
||||
return """
|
||||
{
|
||||
"appUrl": "%s",
|
||||
"serviceType": "HOMEBOX",
|
||||
"username": "%s",
|
||||
"password": "%s"
|
||||
}
|
||||
""".formatted(wm.getHttpBaseUrl(), TEST_USER, TEST_PASS);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import com.github.tomakehurst.wiremock.http.Fault;
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
|
||||
@@ -41,18 +42,16 @@ class HomeboxIntegrationTest {
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String MOCK_URL = "http://localhost:1234";
|
||||
private static final String TEST_USER = "admin";
|
||||
private static final String TEST_PASS = "pw";
|
||||
|
||||
/**
|
||||
* Returns Token and status code OK when login is successful.
|
||||
*
|
||||
* @param wm
|
||||
* the WiremockRuntimeInfo object
|
||||
* @param wm the WiremockRuntimeInfo object
|
||||
*/
|
||||
@Test
|
||||
void shouldReturnTokenAndStatusOkWhenHomeboxCredentialsAreValid(WireMockRuntimeInfo wm) {
|
||||
void shouldReturnStatusOkWhenHomeboxCredentialsAreValid(WireMockRuntimeInfo wm) {
|
||||
|
||||
stubFor(post(HOMEBOX_LOGIN.getValue())
|
||||
.willReturn(okJson(okJsonHomeboxResponse)));
|
||||
@@ -62,11 +61,8 @@ class HomeboxIntegrationTest {
|
||||
|
||||
DocumentContext documentContext = JsonPath.parse(response.getBody());
|
||||
|
||||
String token = documentContext.read("$.token");
|
||||
assertThat(token).isEqualTo("fake-jwt-token");
|
||||
|
||||
String attachmentToken = documentContext.read("$.extraResponseData.attachmentToken");
|
||||
assertThat(attachmentToken).isEqualTo("fake-attach");
|
||||
String serviceType = documentContext.read("$.serviceType");
|
||||
assertThat(serviceType).isEqualTo("HOMEBOX");
|
||||
|
||||
String expiresAt = documentContext.read("$.expiresAt", String.class);
|
||||
assertThat(expiresAt).isEqualTo("2026-04-26T02:23:13Z");
|
||||
@@ -76,8 +72,7 @@ class HomeboxIntegrationTest {
|
||||
/**
|
||||
* Tests the Unauthorized custom exception.
|
||||
*
|
||||
* @param wm
|
||||
* the WiremockRuntimeInfo object
|
||||
* @param wm the WiremockRuntimeInfo object
|
||||
*/
|
||||
@Test
|
||||
void shouldFailToConnectWhenHomeboxReturnsUnauthorized(WireMockRuntimeInfo wm) {
|
||||
@@ -94,8 +89,7 @@ class HomeboxIntegrationTest {
|
||||
/**
|
||||
* Tests a server error from the external api.
|
||||
*
|
||||
* @param wm
|
||||
* the WiremockRuntimeInfo object
|
||||
* @param wm the WiremockRuntimeInfo object
|
||||
*/
|
||||
@Test
|
||||
void shouldFailToConnectWhenHomeboxReturnsServiceUnavailable(WireMockRuntimeInfo wm) {
|
||||
@@ -113,14 +107,12 @@ class HomeboxIntegrationTest {
|
||||
* Checks when the service is unavailable or the app URL is wrong.
|
||||
*/
|
||||
@Test
|
||||
void shouldReturnServiceUnavailableWhenHomeboxUrlIsWrong() {
|
||||
void shouldReturnServiceUnavailableWhenHomeboxUrlIsWrong(WireMockRuntimeInfo wm) {
|
||||
|
||||
ConnectionRequest badRequest = new ConnectionRequest(
|
||||
MOCK_URL,
|
||||
HOMEBOX.getValue(),
|
||||
TEST_USER, TEST_PASS,
|
||||
false);
|
||||
stubFor(post(HOMEBOX_LOGIN.getValue())
|
||||
.willReturn(aResponse().withFault(Fault.CONNECTION_RESET_BY_PEER)));
|
||||
|
||||
ConnectionRequest badRequest = connectionRequest(wm);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(LOGIN.getValue(), badRequest, String.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
@@ -146,9 +138,9 @@ class HomeboxIntegrationTest {
|
||||
* unsupported.
|
||||
*/
|
||||
@Test
|
||||
void shouldReturnWrongServiceTypeException() {
|
||||
void shouldReturnWrongServiceTypeException(WireMockRuntimeInfo wm) {
|
||||
ConnectionRequest wrongServiceTypeReq = new ConnectionRequest(
|
||||
MOCK_URL,
|
||||
wm.getHttpBaseUrl(),
|
||||
"wrong-service-type",
|
||||
TEST_USER, TEST_PASS,
|
||||
false);
|
||||
@@ -164,8 +156,7 @@ class HomeboxIntegrationTest {
|
||||
* Tests the succesfull persistance of Homebox credential response to the
|
||||
* database.
|
||||
*
|
||||
* @param wm
|
||||
* the WiremockRuntimeInfo object
|
||||
* @param wm the WiremockRuntimeInfo object
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveHomeboxConnectionResponseToDb(WireMockRuntimeInfo wm) {
|
||||
@@ -176,26 +167,25 @@ class HomeboxIntegrationTest {
|
||||
ConnectionRequest request = connectionRequest(wm);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(LOGIN.getValue(), request, String.class);
|
||||
|
||||
DocumentContext responseContext = JsonPath.parse(response.getBody());
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
ConnectionEntity dbEntry = cRepository.findByAppUrlAndUsername(request.appUrl(), request.username());
|
||||
|
||||
assertThat(dbEntry).isNotNull();
|
||||
|
||||
assertThat(dbEntry.getAppUrl()).isEqualTo(request.appUrl());
|
||||
assertThat(dbEntry.getUsername()).isEqualTo(request.username());
|
||||
|
||||
if (dbEntry instanceof HomeboxEntity hbE) {
|
||||
assertThat(hbE.getToken()).isEqualTo(responseContext.read("$.token"));
|
||||
assertThat(hbE.getAttachmentToken()).isEqualTo(responseContext.read("$.extraResponseData.attachmentToken"));
|
||||
assertThat(hbE.getExpiresAt()).isEqualTo(responseContext.read("$.expiresAt"));
|
||||
assertThat(hbE.getToken()).isEqualTo("fake-jwt-token");
|
||||
assertThat(hbE.getAttachmentToken()).isEqualTo("fake-attach");
|
||||
assertThat(hbE.getExpiresAt().toString()).hasToString("2026-04-26T02:23:13Z");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyCredentialsExceptionWhenCredsAreMissing() {
|
||||
ConnectionRequest missingCredentials = new ConnectionRequest(MOCK_URL, HOMEBOX.getValue(), TEST_USER, null,
|
||||
void shouldReturnEmptyCredentialsExceptionWhenCredsAreMissing(WireMockRuntimeInfo wm) {
|
||||
ConnectionRequest missingCredentials = new ConnectionRequest(wm.getHttpBaseUrl(), HOMEBOX.getValue(), TEST_USER,
|
||||
null,
|
||||
false);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(LOGIN.getValue(), missingCredentials,
|
||||
@@ -206,11 +196,10 @@ class HomeboxIntegrationTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a valid connection request with a mock Api throuh
|
||||
* Creates a valid connection request with a mock Api through
|
||||
* WireMockRuntimeInfo.
|
||||
*
|
||||
* @param wm
|
||||
* the WiremockRuntimeInfo object
|
||||
* @param wm the WiremockRuntimeInfo object
|
||||
* @return a mock api connection request.
|
||||
*/
|
||||
private ConnectionRequest connectionRequest(WireMockRuntimeInfo wm) {
|
||||
|
||||
Reference in New Issue
Block a user