Skip to content

Commit 1358217

Browse files
added api validation
1 parent 53f202c commit 1358217

File tree

8 files changed

+335
-8
lines changed

8 files changed

+335
-8
lines changed

src/main/java/edu/stanford/protege/gateway/OwlEntityService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ private void validateEntityUpdate(OWLEntityDto owlEntityDto, String existingProj
201201
callerVersionMatchesLatestVersion(owlEntityDto, existingProjectId, callerHash);
202202
entityIsNotItsOwnParent(owlEntityDto);
203203
linearizationParentsAreOnlyDirectParents(owlEntityDto, existingProjectId);
204-
validatorService.validateOWLEntityDto(owlEntityDto);
204+
validatorService.validateOWLEntityDto(owlEntityDto, existingProjectId);
205205
}
206206

207207
private void callerVersionMatchesLatestVersion(OWLEntityDto owlEntityDto, String existingProjectId, String callerHash) {

src/main/java/edu/stanford/protege/gateway/config/ApplicationBeans.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,14 @@ CommandExecutor<GetFullPostCoordinationConfigurationRequest, GetFullPostCoordina
183183
return new CommandExecutorImpl<>(GetFullPostCoordinationConfigurationResponse.class);
184184
}
185185

186+
@Bean
187+
CommandExecutor<ValidateEntityUpdateRequest, ValidateEntityUpdateResponse> executorForValidateEntityUpdate() {
188+
return new CommandExecutorImpl<>(ValidateEntityUpdateResponse.class);
189+
}
190+
191+
@Bean
192+
CommandExecutor<CheckNonExistentIrisRequest, CheckNonExistentIrisResponse> executorForCheckNonExistentIris() {
193+
return new CommandExecutorImpl<>(CheckNonExistentIrisResponse.class);
194+
}
195+
186196
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package edu.stanford.protege.gateway.ontology.commands;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import edu.stanford.protege.webprotege.common.ProjectId;
6+
import edu.stanford.protege.webprotege.common.Request;
7+
import org.semanticweb.owlapi.model.IRI;
8+
9+
import java.util.Set;
10+
11+
@JsonTypeName(CheckNonExistentIrisRequest.CHANNEL)
12+
public record CheckNonExistentIrisRequest(
13+
@JsonProperty("projectId") ProjectId projectId,
14+
@JsonProperty("iris") Set<IRI> iris
15+
) implements Request<CheckNonExistentIrisResponse> {
16+
17+
public static final String CHANNEL = "webprotege.entities.CheckNonExistentIris";
18+
19+
@Override
20+
public String getChannel() {
21+
return CHANNEL;
22+
}
23+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package edu.stanford.protege.gateway.ontology.commands;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import edu.stanford.protege.webprotege.common.Response;
6+
import org.semanticweb.owlapi.model.IRI;
7+
8+
import java.util.Set;
9+
10+
import static edu.stanford.protege.gateway.ontology.commands.CheckNonExistentIrisRequest.CHANNEL;
11+
12+
@JsonTypeName(CHANNEL)
13+
public record CheckNonExistentIrisResponse(
14+
@JsonProperty("nonExistentIris") Set<IRI> nonExistentIris
15+
) implements Response {
16+
17+
public static CheckNonExistentIrisResponse create(Set<IRI> nonExistentIris) {
18+
return new CheckNonExistentIrisResponse(nonExistentIris);
19+
}
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package edu.stanford.protege.gateway.postcoordination.commands;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import edu.stanford.protege.webprotege.common.ProjectId;
6+
import edu.stanford.protege.webprotege.common.Request;
7+
8+
@JsonTypeName(ValidateEntityUpdateRequest.CHANNEL)
9+
public record ValidateEntityUpdateRequest(@JsonProperty("projectId")
10+
ProjectId projectId,
11+
@JsonProperty("entityCustomScaleValues")
12+
WhoficCustomScalesValues entityCustomScaleValues,
13+
@JsonProperty("entitySpecification")
14+
WhoficEntityPostCoordinationSpecification entitySpecification) implements Request<ValidateEntityUpdateResponse> {
15+
16+
public final static String CHANNEL = "webprotege.postcoordination.ValidateEntityUpdate";
17+
18+
@Override
19+
public String getChannel() {
20+
return CHANNEL;
21+
}
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package edu.stanford.protege.gateway.postcoordination.commands;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonTypeName;
5+
import edu.stanford.protege.webprotege.common.Response;
6+
7+
import java.util.List;
8+
9+
import static edu.stanford.protege.gateway.postcoordination.commands.ValidateEntityUpdateRequest.CHANNEL;
10+
11+
@JsonTypeName(CHANNEL)
12+
public class ValidateEntityUpdateResponse implements Response {
13+
14+
@JsonProperty("errorMessages")
15+
private final List<String> errorMessages;
16+
17+
public ValidateEntityUpdateResponse() {
18+
this.errorMessages = List.of();
19+
}
20+
21+
public ValidateEntityUpdateResponse(List<String> errorMessages) {
22+
this.errorMessages = errorMessages;
23+
}
24+
25+
public List<String> getErrorMessages() {
26+
return errorMessages;
27+
}
28+
}

src/main/java/edu/stanford/protege/gateway/validators/ValidatorService.java

Lines changed: 213 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@
44
import com.google.common.collect.ImmutableSet;
55
import edu.stanford.protege.gateway.EntityIsMissingException;
66
import edu.stanford.protege.gateway.SecurityContextHelper;
7+
import edu.stanford.protege.gateway.ValidationException;
78
import edu.stanford.protege.gateway.dto.BaseExclusionTerm;
9+
import edu.stanford.protege.gateway.dto.BaseIndexTerm;
810
import edu.stanford.protege.gateway.dto.CreateEntityDto;
11+
import edu.stanford.protege.gateway.dto.EntityLanguageTermsDto;
12+
import edu.stanford.protege.gateway.dto.LanguageTerm;
913
import edu.stanford.protege.gateway.dto.OWLEntityDto;
14+
import edu.stanford.protege.gateway.linearization.EntityLinearizationService;
15+
import edu.stanford.protege.gateway.linearization.commands.LinearizationDefinition;
1016
import edu.stanford.protege.gateway.ontology.OntologyService;
1117
import edu.stanford.protege.gateway.ontology.commands.*;
18+
import edu.stanford.protege.gateway.postcoordination.CustomScalesMapper;
19+
import edu.stanford.protege.gateway.postcoordination.SpecificationMapper;
20+
import edu.stanford.protege.gateway.postcoordination.commands.*;
1221
import edu.stanford.protege.webprotege.common.*;
1322
import edu.stanford.protege.webprotege.criteria.EntityTypeIsOneOfCriteria;
1423
import edu.stanford.protege.webprotege.ipc.CommandExecutor;
24+
import edu.stanford.protege.webprotege.ipc.ExecutionContext;
1525
import edu.stanford.protege.webprotege.search.DeprecatedEntitiesTreatment;
16-
import edu.stanford.protege.webprotege.search.EntitySearchResult;
17-
import edu.stanford.protege.webprotege.search.PerformEntitySearchAction;
18-
import edu.stanford.protege.webprotege.search.PerformEntitySearchResult;
1926
import org.semanticweb.owlapi.model.EntityType;
2027
import org.semanticweb.owlapi.model.IRI;
2128
import org.slf4j.Logger;
@@ -40,14 +47,29 @@ public class ValidatorService {
4047
private final CommandExecutor<FilterExistingEntitiesRequest, FilterExistingEntitiesResponse> filterExistingEntitiesExecutor;
4148

4249
private final CommandExecutor<GetExistingClassesForApiRequest, GetExistingClassesForApiResponse> getEntitySearchExecutor;
50+
private final CommandExecutor<ValidateEntityUpdateRequest, ValidateEntityUpdateResponse> validateEntityUpdateExecutor;
51+
private final CommandExecutor<GetTablePostCoordinationAxisRequest, GetTablePostCoordinationAxisResponse> tableConfigurationExecutor;
52+
private final CommandExecutor<GetIcatxEntityTypeRequest, GetIcatxEntityTypeResponse> entityTypesExecutor;
53+
private final CommandExecutor<CheckNonExistentIrisRequest, CheckNonExistentIrisResponse> checkNonExistentIrisExecutor;
54+
private final EntityLinearizationService linearizationService;
4355

4456

4557
public ValidatorService(CommandExecutor<GetIsExistingProjectRequest, GetIsExistingProjectResponse> isExistingProjectExecutor,
4658
CommandExecutor<FilterExistingEntitiesRequest, FilterExistingEntitiesResponse> filterExistingEntitiesExecutor,
47-
CommandExecutor<GetExistingClassesForApiRequest, GetExistingClassesForApiResponse> getEntitySearchExecutor) {
59+
CommandExecutor<GetExistingClassesForApiRequest, GetExistingClassesForApiResponse> getEntitySearchExecutor,
60+
CommandExecutor<ValidateEntityUpdateRequest, ValidateEntityUpdateResponse> validateEntityUpdateExecutor,
61+
CommandExecutor<GetTablePostCoordinationAxisRequest, GetTablePostCoordinationAxisResponse> tableConfigurationExecutor,
62+
CommandExecutor<GetIcatxEntityTypeRequest, GetIcatxEntityTypeResponse> entityTypesExecutor,
63+
CommandExecutor<CheckNonExistentIrisRequest, CheckNonExistentIrisResponse> checkNonExistentIrisExecutor,
64+
EntityLinearizationService linearizationService) {
4865
this.isExistingProjectExecutor = isExistingProjectExecutor;
4966
this.filterExistingEntitiesExecutor = filterExistingEntitiesExecutor;
5067
this.getEntitySearchExecutor = getEntitySearchExecutor;
68+
this.validateEntityUpdateExecutor = validateEntityUpdateExecutor;
69+
this.tableConfigurationExecutor = tableConfigurationExecutor;
70+
this.entityTypesExecutor = entityTypesExecutor;
71+
this.checkNonExistentIrisExecutor = checkNonExistentIrisExecutor;
72+
this.linearizationService = linearizationService;
5173
}
5274

5375

@@ -172,14 +194,200 @@ private void validateBaseExclusionTerm(BaseExclusionTerm term) {
172194
}
173195
}
174196

175-
public void validateOWLEntityDto(OWLEntityDto owlEntityDto) {
197+
public void validateOWLEntityDto(OWLEntityDto owlEntityDto, String projectId) {
176198
if (owlEntityDto == null) {
177199
throw new IllegalArgumentException("OWLEntityDto cannot be null");
178200
}
179201

180202
if (owlEntityDto.languageTerms() != null && owlEntityDto.languageTerms().baseExclusionTerms() != null) {
181203
validateBaseExclusionTerms(owlEntityDto.languageTerms().baseExclusionTerms());
182204
}
205+
206+
validateTermIdsExistence(owlEntityDto, projectId);
207+
validateParentsExistence(owlEntityDto, projectId);
208+
209+
if (owlEntityDto.postcoordination() != null) {
210+
validatePostcoordination(owlEntityDto, projectId);
211+
}
212+
}
213+
214+
private void validateParentsExistence(OWLEntityDto owlEntityDto, String projectId) {
215+
if (owlEntityDto.parents() == null || owlEntityDto.parents().isEmpty()) {
216+
return;
217+
}
218+
219+
try {
220+
Set<IRI> parentIris = owlEntityDto.parents().stream()
221+
.filter(parent -> parent != null && !parent.trim().isEmpty())
222+
.map(IRI::create)
223+
.collect(Collectors.toSet());
224+
225+
if (parentIris.isEmpty()) {
226+
return;
227+
}
228+
229+
Set<IRI> nonExistentIris = checkNonExistentIrisExecutor.execute(
230+
new CheckNonExistentIrisRequest(ProjectId.valueOf(projectId), ImmutableSet.copyOf(parentIris)),
231+
SecurityContextHelper.getExecutionContext()
232+
).get().nonExistentIris();
233+
234+
if (!nonExistentIris.isEmpty()) {
235+
List<String> missingParents = nonExistentIris.stream()
236+
.map(IRI::toString)
237+
.collect(Collectors.toList());
238+
throw new IllegalArgumentException("The following parent entities do not exist: " + String.join(", ", missingParents));
239+
}
240+
} catch (InterruptedException | ExecutionException e) {
241+
LOGGER.error("Could not verify if parents are valid!", e);
242+
throw new RuntimeException("Error validating parents existence", e);
243+
}
244+
}
245+
246+
private void validateTermIdsExistence(OWLEntityDto owlEntityDto, String projectId) {
247+
List<String> termIds = collectTermIds(owlEntityDto);
248+
249+
if (termIds.isEmpty()) {
250+
return;
251+
}
252+
253+
try {
254+
Set<IRI> termIdIris = termIds.stream()
255+
.map(IRI::create)
256+
.collect(Collectors.toSet());
257+
258+
Set<IRI> nonExistentIris = checkNonExistentIrisExecutor.execute(
259+
new CheckNonExistentIrisRequest(ProjectId.valueOf(projectId), ImmutableSet.copyOf(termIdIris)),
260+
SecurityContextHelper.getExecutionContext()
261+
).get().nonExistentIris();
262+
263+
if (!nonExistentIris.isEmpty()) {
264+
List<String> missingTermIds = nonExistentIris.stream()
265+
.map(IRI::toString)
266+
.collect(Collectors.toList());
267+
throw new IllegalArgumentException("The following term IDs do not exist: " + String.join(", ", missingTermIds));
268+
}
269+
} catch (InterruptedException | ExecutionException e) {
270+
LOGGER.error("Could not verify if term IDs are valid!", e);
271+
throw new RuntimeException("Error validating term IDs existence", e);
272+
}
273+
}
274+
275+
private List<String> collectTermIds(OWLEntityDto owlEntityDto) {
276+
List<String> termIds = new ArrayList<>();
277+
278+
// Colectare din languageTerms
279+
if (owlEntityDto.languageTerms() != null) {
280+
EntityLanguageTermsDto languageTerms = owlEntityDto.languageTerms();
281+
282+
// Title termId
283+
if (languageTerms.title() != null && languageTerms.title().termId() != null && !languageTerms.title().termId().trim().isEmpty()) {
284+
termIds.add(languageTerms.title().termId());
285+
}
286+
287+
// Definition termId
288+
if (languageTerms.definition() != null && languageTerms.definition().termId() != null && !languageTerms.definition().termId().trim().isEmpty()) {
289+
termIds.add(languageTerms.definition().termId());
290+
}
291+
292+
// LongDefinition termId
293+
if (languageTerms.longDefinition() != null && languageTerms.longDefinition().termId() != null && !languageTerms.longDefinition().termId().trim().isEmpty()) {
294+
termIds.add(languageTerms.longDefinition().termId());
295+
}
296+
297+
// FullySpecifiedName termId
298+
if (languageTerms.fullySpecifiedName() != null && languageTerms.fullySpecifiedName().termId() != null && !languageTerms.fullySpecifiedName().termId().trim().isEmpty()) {
299+
termIds.add(languageTerms.fullySpecifiedName().termId());
300+
}
301+
302+
// BaseIndexTerms termIds
303+
if (languageTerms.baseIndexTerms() != null) {
304+
languageTerms.baseIndexTerms().stream()
305+
.filter(term -> term != null && term.termId() != null && !term.termId().trim().isEmpty())
306+
.forEach(term -> termIds.add(term.termId()));
307+
}
308+
309+
// BaseExclusionTerms termIds și foundationReferences
310+
if (languageTerms.baseExclusionTerms() != null) {
311+
languageTerms.baseExclusionTerms().stream()
312+
.filter(term -> term != null)
313+
.forEach(term -> {
314+
if (term.termId() != null && !term.termId().trim().isEmpty()) {
315+
termIds.add(term.termId());
316+
}
317+
if (term.foundationReference() != null && !term.foundationReference().trim().isEmpty()) {
318+
termIds.add(term.foundationReference());
319+
}
320+
});
321+
}
322+
}
323+
324+
// Colectare din diagnosticCriteria
325+
if (owlEntityDto.diagnosticCriteria() != null && owlEntityDto.diagnosticCriteria().termId() != null && !owlEntityDto.diagnosticCriteria().termId().trim().isEmpty()) {
326+
termIds.add(owlEntityDto.diagnosticCriteria().termId());
327+
}
328+
329+
// Colectare din relatedImpairments
330+
if (owlEntityDto.relatedImpairments() != null) {
331+
owlEntityDto.relatedImpairments().stream()
332+
.filter(term -> term != null && term.termId() != null && !term.termId().trim().isEmpty())
333+
.forEach(term -> termIds.add(term.termId()));
334+
}
335+
336+
return termIds;
337+
}
338+
339+
private void validatePostcoordination(OWLEntityDto owlEntityDto, String projectId) {
340+
try {
341+
ExecutionContext executionContext = SecurityContextHelper.getExecutionContext();
342+
ProjectId projectIdObj = ProjectId.valueOf(projectId);
343+
String entityIri = owlEntityDto.entityIRI();
344+
345+
// Mapare custom scales values
346+
WhoficCustomScalesValues customScalesValues = CustomScalesMapper.mapFromDtoList(
347+
entityIri,
348+
owlEntityDto.postcoordination().scaleCustomizations()
349+
);
350+
351+
// Obținere table configuration
352+
TableConfiguration tableConfiguration = tableConfigurationExecutor.execute(
353+
new GetTablePostCoordinationAxisRequest(IRI.create(entityIri), projectIdObj),
354+
executionContext
355+
).get().tableConfiguration();
356+
357+
// Obținere entity types
358+
List<String> entityTypes = entityTypesExecutor.execute(
359+
GetIcatxEntityTypeRequest.create(IRI.create(entityIri), projectIdObj),
360+
executionContext
361+
).get().icatxEntityTypes();
362+
363+
// Obținere definitions
364+
List<LinearizationDefinition> definitions = linearizationService.getDefinitionList(executionContext);
365+
366+
// Mapare specification
367+
WhoficEntityPostCoordinationSpecification specification = SpecificationMapper.mapFromDtoList(
368+
entityIri,
369+
entityTypes.isEmpty() ? "ICD" : entityTypes.get(0),
370+
owlEntityDto.postcoordination().postcoordinationSpecifications(),
371+
definitions,
372+
tableConfiguration
373+
);
374+
375+
// Validare
376+
ValidateEntityUpdateResponse validationResponse = validateEntityUpdateExecutor.execute(
377+
new ValidateEntityUpdateRequest(projectIdObj, customScalesValues, specification),
378+
executionContext
379+
).get();
380+
381+
if (validationResponse.getErrorMessages() != null && !validationResponse.getErrorMessages().isEmpty()) {
382+
String errorMessage = String.join("; ", validationResponse.getErrorMessages());
383+
throw new ValidationException(errorMessage);
384+
}
385+
} catch (ValidationException e) {
386+
throw e;
387+
} catch (Exception e) {
388+
LOGGER.error("Error validating postcoordination for entity " + owlEntityDto.entityIRI(), e);
389+
throw new RuntimeException("Error validating postcoordination for entity " + owlEntityDto.entityIRI(), e);
390+
}
183391
}
184392

185393
public static boolean hasEscapeCharacters(String input) {

0 commit comments

Comments
 (0)