feat(01-03): add test infrastructure, protocol version interceptor, and app bootstrap
- AbstractClickHouseIT base class with Testcontainers ClickHouse and schema init - ProtocolVersionInterceptor validates X-Cameleer-Protocol-Version:1 on data/agent paths - WebConfig registers interceptor with path patterns, excludes health/docs - Cameleer3ServerApplication with @EnableScheduling and component scanning - application-test.yml with small buffer config for tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package com.cameleer3.server.app;
|
||||
|
||||
import com.cameleer3.server.app.config.IngestionConfig;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* Main entry point for the Cameleer3 Server application.
|
||||
* <p>
|
||||
* Scans {@code com.cameleer3.server.app} and {@code com.cameleer3.server.core} packages.
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = {
|
||||
"com.cameleer3.server.app",
|
||||
"com.cameleer3.server.core"
|
||||
})
|
||||
@EnableScheduling
|
||||
@EnableConfigurationProperties(IngestionConfig.class)
|
||||
public class Cameleer3ServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Cameleer3ServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.cameleer3.server.app.config;
|
||||
|
||||
import com.cameleer3.server.app.interceptor.ProtocolVersionInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web MVC configuration.
|
||||
* <p>
|
||||
* Registers the {@link ProtocolVersionInterceptor} on data and agent endpoint paths,
|
||||
* excluding health, API docs, and Swagger UI paths that do not require protocol versioning.
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
private final ProtocolVersionInterceptor protocolVersionInterceptor;
|
||||
|
||||
public WebConfig(ProtocolVersionInterceptor protocolVersionInterceptor) {
|
||||
this.protocolVersionInterceptor = protocolVersionInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(protocolVersionInterceptor)
|
||||
.addPathPatterns("/api/v1/data/**", "/api/v1/agents/**")
|
||||
.excludePathPatterns(
|
||||
"/api/v1/health",
|
||||
"/api/v1/api-docs/**",
|
||||
"/api/v1/swagger-ui/**",
|
||||
"/api/v1/swagger-ui.html"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.cameleer3.server.app.interceptor;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Validates that all requests to data and agent endpoints include the
|
||||
* {@code X-Cameleer-Protocol-Version} header with value {@code "1"}.
|
||||
* <p>
|
||||
* Requests missing the header or using an unsupported version receive a 400 response
|
||||
* with a JSON error body.
|
||||
*/
|
||||
@Component
|
||||
public class ProtocolVersionInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final String HEADER_NAME = "X-Cameleer-Protocol-Version";
|
||||
private static final String SUPPORTED_VERSION = "1";
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public ProtocolVersionInterceptor(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
String version = request.getHeader(HEADER_NAME);
|
||||
|
||||
if (version == null || !SUPPORTED_VERSION.equals(version)) {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
objectMapper.writeValue(response.getWriter(),
|
||||
Map.of("error", "Missing or unsupported X-Cameleer-Protocol-Version header"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user