feat(outbound): request + response + test-result DTOs with Bean Validation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
package com.cameleer.server.app.outbound.dto;
|
||||||
|
|
||||||
|
import com.cameleer.server.core.http.TrustMode;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundAuthKind;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundConnection;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundMethod;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record OutboundConnectionDto(
|
||||||
|
UUID id, String name, String description, String url,
|
||||||
|
OutboundMethod method, Map<String, String> defaultHeaders, String defaultBodyTmpl,
|
||||||
|
TrustMode tlsTrustMode, List<String> tlsCaPemPaths,
|
||||||
|
boolean hmacSecretSet,
|
||||||
|
OutboundAuthKind authKind,
|
||||||
|
List<UUID> allowedEnvironmentIds,
|
||||||
|
Instant createdAt, String createdBy, Instant updatedAt, String updatedBy
|
||||||
|
) {
|
||||||
|
public static OutboundConnectionDto from(OutboundConnection c) {
|
||||||
|
return new OutboundConnectionDto(
|
||||||
|
c.id(), c.name(), c.description(), c.url(), c.method(),
|
||||||
|
c.defaultHeaders(), c.defaultBodyTmpl(),
|
||||||
|
c.tlsTrustMode(), c.tlsCaPemPaths(),
|
||||||
|
c.hmacSecretCiphertext() != null,
|
||||||
|
c.auth().kind(), c.allowedEnvironmentIds(),
|
||||||
|
c.createdAt(), c.createdBy(), c.updatedAt(), c.updatedBy());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.cameleer.server.app.outbound.dto;
|
||||||
|
|
||||||
|
import com.cameleer.server.core.http.TrustMode;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundAuth;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundMethod;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record OutboundConnectionRequest(
|
||||||
|
@NotBlank @Size(max = 100) String name,
|
||||||
|
@Size(max = 2000) String description,
|
||||||
|
@NotBlank @Pattern(regexp = "^https://.+", message = "URL must be HTTPS") String url,
|
||||||
|
@NotNull OutboundMethod method,
|
||||||
|
Map<String, String> defaultHeaders,
|
||||||
|
String defaultBodyTmpl,
|
||||||
|
@NotNull TrustMode tlsTrustMode,
|
||||||
|
List<String> tlsCaPemPaths,
|
||||||
|
String hmacSecret,
|
||||||
|
@NotNull @Valid OutboundAuth auth,
|
||||||
|
List<UUID> allowedEnvironmentIds
|
||||||
|
) {
|
||||||
|
public OutboundConnectionRequest {
|
||||||
|
defaultHeaders = defaultHeaders == null ? Map.of() : defaultHeaders;
|
||||||
|
tlsCaPemPaths = tlsCaPemPaths == null ? List.of() : tlsCaPemPaths;
|
||||||
|
allowedEnvironmentIds = allowedEnvironmentIds == null ? List.of() : allowedEnvironmentIds;
|
||||||
|
if (tlsTrustMode == TrustMode.TRUST_PATHS && tlsCaPemPaths.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("tlsCaPemPaths must not be empty when tlsTrustMode = TRUST_PATHS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.cameleer.server.app.outbound.dto;
|
||||||
|
|
||||||
|
public record OutboundConnectionTestResult(
|
||||||
|
int status,
|
||||||
|
long latencyMs,
|
||||||
|
String responseSnippet,
|
||||||
|
String tlsProtocol,
|
||||||
|
String tlsCipherSuite,
|
||||||
|
String peerCertificateSubject,
|
||||||
|
Long peerCertificateExpiresAtEpochMs,
|
||||||
|
String error
|
||||||
|
) {}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.cameleer.server.app.outbound;
|
||||||
|
|
||||||
|
import com.cameleer.server.app.outbound.dto.OutboundConnectionRequest;
|
||||||
|
import com.cameleer.server.core.http.TrustMode;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundAuth;
|
||||||
|
import com.cameleer.server.core.outbound.OutboundMethod;
|
||||||
|
import jakarta.validation.Validation;
|
||||||
|
import jakarta.validation.Validator;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
class OutboundConnectionRequestValidationTest {
|
||||||
|
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validRequestPasses() {
|
||||||
|
var r = new OutboundConnectionRequest("slack-ops", "desc",
|
||||||
|
"https://hooks.slack.com/x", OutboundMethod.POST,
|
||||||
|
Map.of(), null, TrustMode.SYSTEM_DEFAULT, List.of(),
|
||||||
|
null, new OutboundAuth.None(), List.of());
|
||||||
|
assertThat(validator.validate(r)).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void blankNameFails() {
|
||||||
|
var r = new OutboundConnectionRequest(" ", null, "https://x", OutboundMethod.POST,
|
||||||
|
Map.of(), null, TrustMode.SYSTEM_DEFAULT, List.of(), null, new OutboundAuth.None(), List.of());
|
||||||
|
assertThat(validator.validate(r)).anySatisfy(v -> assertThat(v.getPropertyPath().toString()).isEqualTo("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nonHttpsUrlFails() {
|
||||||
|
var r = new OutboundConnectionRequest("n", null, "http://x", OutboundMethod.POST,
|
||||||
|
Map.of(), null, TrustMode.SYSTEM_DEFAULT, List.of(), null, new OutboundAuth.None(), List.of());
|
||||||
|
assertThat(validator.validate(r)).anySatisfy(v -> assertThat(v.getPropertyPath().toString()).isEqualTo("url"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void trustPathsRequiresNonEmptyCaList() {
|
||||||
|
assertThatThrownBy(() -> new OutboundConnectionRequest("n", null, "https://x", OutboundMethod.POST,
|
||||||
|
Map.of(), null, TrustMode.TRUST_PATHS, List.of(), null, new OutboundAuth.None(), List.of()))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user