Skip to content

Commit 666f7bd

Browse files
committed
feat(validation): Handling
1 parent 357cf21 commit 666f7bd

5 files changed

Lines changed: 59 additions & 11 deletions

File tree

src/main/java/ai/labs/eddi/configs/ingestion/IRestRagIngestionSourceStore.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import ai.labs.eddi.configs.ingestion.model.RagIngestionSource;
1010
import jakarta.annotation.security.RolesAllowed;
1111
import jakarta.ws.rs.*;
12-
import jakarta.ws.rs.core.MediaType;
1312
import jakarta.ws.rs.core.Response;
1413
import org.eclipse.microprofile.openapi.annotations.Operation;
1514
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;

src/main/java/ai/labs/eddi/configs/ingestion/model/RagIngestionSource.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,11 @@ public record RagIngestionSource(
6969
type = "web";
7070
}
7171

72-
// Ensure sourceConfig is not null for web type
73-
if (sourceConfig == null && "web".equals(type)) {
74-
sourceConfig = new WebSourceConfig(null, null, null);
75-
}
76-
7772
// Set defaults for other nullable nested configs
7873
if (ingestionSettings == null) {
7974
ingestionSettings = new IngestionSettings();
8075
}
76+
8177
if (schedule == null) {
8278
schedule = new Schedule();
8379
}

src/main/java/ai/labs/eddi/configs/ingestion/model/WebSourceConfig.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package ai.labs.eddi.configs.ingestion.model;
66

7+
import ai.labs.eddi.modules.llm.tools.UrlValidationUtils;
78
import java.util.ArrayList;
89
import java.util.List;
910

@@ -40,6 +41,21 @@ public record WebSourceConfig(
4041
}
4142
}
4243

44+
/**
45+
* Validates this configuration and throws {@link IllegalArgumentException} if
46+
* any field is invalid. Call this before using the config for crawling.
47+
*/
48+
public void validate() {
49+
if (!UrlValidationUtils.isValidHttpUrl(startUrl)) {
50+
throw new IllegalArgumentException(
51+
startUrl == null || startUrl.isBlank()
52+
? "startUrl must not be null or blank"
53+
: "startUrl must be a valid http or https URL, got: " + startUrl);
54+
}
55+
scope.validate();
56+
crawlSettings.validate();
57+
}
58+
4359
/**
4460
* Scope constraints for the web crawler. Controls how far and where the crawler
4561
* will follow links.
@@ -90,6 +106,15 @@ public record Scope(
90106
public Scope() {
91107
this(true, "/", 3, 200, new ArrayList<>());
92108
}
109+
110+
void validate() {
111+
if (maxPages > 10_000) {
112+
throw new IllegalArgumentException("maxPages must not exceed 10,000, got: " + maxPages);
113+
}
114+
if (maxDepth > 20) {
115+
throw new IllegalArgumentException("maxDepth must not exceed 20, got: " + maxDepth);
116+
}
117+
}
93118
}
94119

95120
/**
@@ -128,5 +153,14 @@ public record CrawlSettings(
128153
public CrawlSettings() {
129154
this(500, 15, "EDDI-Crawler/1.0");
130155
}
156+
157+
void validate() {
158+
if (requestDelayMs > 30_000) {
159+
throw new IllegalArgumentException("requestDelayMs must not exceed 30,000, got: " + requestDelayMs);
160+
}
161+
if (timeoutSeconds > 300) {
162+
throw new IllegalArgumentException("timeoutSeconds must not exceed 300, got: " + timeoutSeconds);
163+
}
164+
}
131165
}
132166
}

src/main/java/ai/labs/eddi/configs/ingestion/rest/RestRagIngestionSourceStore.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import ai.labs.eddi.configs.ingestion.IRagIngestionSourceStore;
99
import ai.labs.eddi.configs.ingestion.IRestRagIngestionSourceStore;
1010
import ai.labs.eddi.configs.ingestion.model.RagIngestionSource;
11+
import ai.labs.eddi.configs.ingestion.model.WebSourceConfig;
1112
import ai.labs.eddi.configs.rest.RestVersionInfo;
1213
import ai.labs.eddi.configs.schema.IJsonSchemaCreator;
1314
import ai.labs.eddi.configs.descriptors.model.DocumentDescriptor;
@@ -48,7 +49,6 @@ public class RestRagIngestionSourceStore implements IRestRagIngestionSourceStore
4849
private final IRagIngestionSourceStore sourceStore;
4950
private final IScheduleStore scheduleStore;
5051
private final IJsonSchemaCreator jsonSchemaCreator;
51-
private final IDocumentDescriptorStore documentDescriptorStore;
5252
private final RagIngestionService ingestionService;
5353
private final IContentHashStore contentHashTracker;
5454
private final RestVersionInfo<RagIngestionSource> restVersionInfo;
@@ -64,7 +64,6 @@ public RestRagIngestionSourceStore(
6464
this.sourceStore = sourceStore;
6565
this.scheduleStore = scheduleStore;
6666
this.jsonSchemaCreator = jsonSchemaCreator;
67-
this.documentDescriptorStore = documentDescriptorStore;
6867
this.ingestionService = ingestionService;
6968
this.contentHashTracker = contentHashTracker;
7069
this.restVersionInfo = new RestVersionInfo<>(resourceURI, sourceStore, documentDescriptorStore);
@@ -100,6 +99,8 @@ public RagIngestionSource readIngestionSource(String id, Integer version) {
10099

101100
@Override
102101
public Response updateIngestionSource(String id, Integer version, RagIngestionSource source) {
102+
validateSource(source);
103+
103104
Response response = restVersionInfo.update(id, version, source);
104105

105106
// Update associated schedule
@@ -116,6 +117,8 @@ public Response updateIngestionSource(String id, Integer version, RagIngestionSo
116117

117118
@Override
118119
public Response createIngestionSource(RagIngestionSource source) {
120+
validateSource(source);
121+
119122
IResourceStore.IResourceId resourceId = restVersionInfo.createDocument(source);
120123

121124
// Create associated schedule
@@ -194,6 +197,18 @@ public IResourceStore.IResourceId getCurrentResourceId(String id) throws IResour
194197
return sourceStore.getCurrentResourceId(id);
195198
}
196199

200+
// --- Source validation ---
201+
202+
private void validateSource(RagIngestionSource source) {
203+
if ("web".equals(source.type())) {
204+
if (source.sourceConfig() instanceof WebSourceConfig webConfig) {
205+
webConfig.validate();
206+
} else if (source.sourceConfig() == null) {
207+
throw new IllegalArgumentException("sourceConfig is required for web-type ingestion sources");
208+
}
209+
}
210+
}
211+
197212
// --- Schedule management ---
198213

199214
private void createScheduleForSource(String sourceId, RagIngestionSource source) throws Exception {

src/main/java/ai/labs/eddi/engine/exception/IllegalArgumentExceptionMapper.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@
44
*/
55
package ai.labs.eddi.engine.exception;
66

7+
import jakarta.ws.rs.core.MediaType;
78
import jakarta.ws.rs.core.Response;
89
import jakarta.ws.rs.ext.ExceptionMapper;
910
import jakarta.ws.rs.ext.Provider;
1011

12+
import java.util.Map;
13+
1114
/**
1215
* @author ginccc
1316
*/
1417
@Provider
1518
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
1619
@Override
1720
public Response toResponse(IllegalArgumentException exception) {
18-
Response.ResponseBuilder response = Response.status(Response.Status.BAD_REQUEST);
19-
response.entity(exception.getLocalizedMessage());
20-
return response.build();
21+
return Response.status(Response.Status.BAD_REQUEST)
22+
.entity(Map.of("message", exception.getLocalizedMessage()))
23+
.type(MediaType.APPLICATION_JSON)
24+
.build();
2125
}
2226
}

0 commit comments

Comments
 (0)