RED-7886 - Endpoint for rule validation #360

Merged
corina.olariu.ext1 merged 3 commits from RED-7886-dryrun into master 2024-02-23 10:21:54 +01:00
5 changed files with 162 additions and 27 deletions

View File

@ -17,6 +17,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@ -57,27 +58,38 @@ public class RulesController implements RulesResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<?> upload(@RequestBody RulesUploadRequestModel rules) {
public ResponseEntity<DroolsSyntaxValidationResponse> upload(@RequestBody RulesUploadRequestModel rules) {
RulesUploadRequest rulesUploadRequest = RulesUploadRequest.fromModel(rules);
DroolsSyntaxValidationResponse droolsSyntaxValidationResponse = DroolsSyntaxValidationResponse.builder()
.build();
DroolsSyntaxValidationResponse droolsSyntaxValidationResponse = DroolsSyntaxValidationResponse.builder().build();
try {
DroolsSyntaxValidation droolsSyntaxValidation = rulesValidationService.validateRules(rulesUploadRequest.getRuleFileType(), rulesUploadRequest.getRules());
var rulesSyntaxWarningMessages = droolsSyntaxValidation.getDroolsSyntaxDeprecatedWarnings()
.stream()
.map(warningMessage -> RuleSyntaxWarningMessage.builder().line(warningMessage.getLine()).column(warningMessage.getColumn()).message(warningMessage.getMessage())
.map(warningMessage -> RuleSyntaxWarningMessage.builder()
.line(warningMessage.getLine())
.column(warningMessage.getColumn())
.message(warningMessage.getMessage())
.build())
.collect(Collectors.toList());
droolsSyntaxValidationResponse.setRulesSyntaxWarningMessages(rulesSyntaxWarningMessages);
if (!droolsSyntaxValidation.isCompiled()) {
var rulesSyntaxErrorMessages = droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
.stream()
.map(errorMessage -> RuleSyntaxErrorMessage.builder().line(errorMessage.getLine()).column(errorMessage.getColumn()).message(errorMessage.getMessage())
.map(errorMessage -> RuleSyntaxErrorMessage.builder()
.line(errorMessage.getLine())
.column(errorMessage.getColumn())
.message(errorMessage.getMessage())
.build())
.toList();
droolsSyntaxValidationResponse.setRulesSyntaxErrorMessages(rulesSyntaxErrorMessages);
return new ResponseEntity<>(droolsSyntaxValidationResponse, HttpStatus.BAD_REQUEST);
if (!rules.isDryRun()) {
return new ResponseEntity<>(droolsSyntaxValidationResponse, HttpStatus.BAD_REQUEST);
}
}
if (rules.isDryRun()) {
return ResponseEntity.ok(droolsSyntaxValidationResponse);
}
} catch (FeignException e) {
if (e.status() == HttpStatus.BAD_REQUEST.value()) {
@ -117,20 +129,23 @@ public class RulesController implements RulesResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<?> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestPart(name = "file") MultipartFile file) {
public ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@RequestPart(name = "file") MultipartFile file) {
return uploadFile(dossierTemplateId, RuleFileType.ENTITY, file);
return uploadFile(dossierTemplateId, RuleFileType.ENTITY, dryRun, file);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<?> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestPart(name = "file") MultipartFile file) {
public ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@RequestPart(name = "file") MultipartFile file) {
try {
return upload(new RulesUploadRequestModel(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId, ruleFileType));
return upload(new RulesUploadRequestModel(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId, ruleFileType, dryRun));
} catch (IOException e) {
throw new FileUploadException("Could not upload file.", e);
}

View File

@ -7,12 +7,14 @@ 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.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsSyntaxValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequestModel;
@ -33,6 +35,8 @@ public interface RulesResource {
String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType";
String RULE_FILE_TYPE_PATH_VARIABLE = "/{ruleFileType}";
String DRY_RUN_PARAMETER = "dryRun";
/**
* Upload rules to be used by redaction service.
@ -42,8 +46,8 @@ public interface RulesResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = RULES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Takes object containing string or rules as argument, which will be used by the redaction service.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Rules upload successful."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<?> upload(@RequestBody RulesUploadRequestModel rules);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<DroolsSyntaxValidationResponse> upload(@RequestBody RulesUploadRequestModel rules);
@ResponseBody
@ -70,17 +74,20 @@ public interface RulesResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + UPLOAD_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Takes object containing string or rules as argument, which will be used by the redaction service.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Rules upload successful."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<?> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + RULE_FILE_TYPE_PATH_VARIABLE + UPLOAD_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Takes object containing string or rules as argument, which will be used by the redaction service.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Rules upload successful."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<?> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done"), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ResponseBody

View File

@ -1,18 +1,31 @@
package com.iqser.red.service.peristence.v1.server.integration.tests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import com.iqser.red.service.peristence.v1.server.integration.client.RulesClient;
import com.iqser.red.service.peristence.v1.server.integration.client.VersionClient;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsSyntaxValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequestModel;
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxDeprecatedWarnings;
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxErrorMessage;
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation;
import com.iqser.red.service.redaction.v1.model.RuleValidationModel;
import feign.FeignException;
import software.amazon.awssdk.http.HttpStatusCode;
public class RulesTest extends AbstractPersistenceServerServiceTest {
@ -31,16 +44,104 @@ public class RulesTest extends AbstractPersistenceServerServiceTest {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
rulesClient.upload(new RulesUploadRequestModel("lorem ipsum", dossierTemplate.getId(), RuleFileType.ENTITY));
var response = rulesClient.upload(new RulesUploadRequestModel("lorem ipsum", dossierTemplate.getId(), RuleFileType.ENTITY, false));
assertThat(response.getStatusCode().value()).isEqualTo(HttpStatus.SC_OK);
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId()))
.get(dossierTemplate.getId())
.getRulesVersion()).isEqualTo(3); //1. beim Anlegen des DossierTemplates; 2. bei provideTestTemplate(), damit es ACTIVE ist
.get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(3); //1. beim Anlegen des DossierTemplates; 2. bei provideTestTemplate(), damit es ACTIVE ist
assertThat(rulesClient.download(dossierTemplate.getId()).getRules()).isEqualTo("lorem ipsum");
rulesClient.upload(new RulesUploadRequestModel("lorem ipsum dolor sit amet", dossierTemplate.getId(), RuleFileType.ENTITY));
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId())).get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(4);
response = rulesClient.upload(new RulesUploadRequestModel("lorem ipsum dolor sit amet", dossierTemplate.getId(), RuleFileType.ENTITY, false));
assertThat(response.getStatusCode().value()).isEqualTo(HttpStatusCode.OK);
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId()))
.get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(4);
assertThat(rulesClient.download(dossierTemplate.getId()).getRules()).isEqualTo("lorem ipsum dolor sit amet");
}
@Test
public void testRulesWithRunDrySetTrue() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
List<DroolsSyntaxDeprecatedWarnings> warningMessages = new ArrayList<>();
warningMessages.add(DroolsSyntaxDeprecatedWarnings.builder().line(1).column(0).message("this function is deprecated").build());
List<DroolsSyntaxErrorMessage> errorMessages = new ArrayList<>();
errorMessages.add(DroolsSyntaxErrorMessage.builder().line(1).column(0).message("error message").build());
var request = new RulesUploadRequestModel("lorem ipsum", dossierTemplate.getId(), RuleFileType.ENTITY, true);
// case 1: dry-run true, no error messages just warning messages
when(redactionClient.testRules(new RuleValidationModel(request.getRuleFileType().name(), request.getRules()))).thenReturn(DroolsSyntaxValidation.builder()
.droolsSyntaxErrorMessages(Collections.emptyList())
.droolsSyntaxDeprecatedWarnings(
warningMessages)
.build());
ResponseEntity<DroolsSyntaxValidationResponse> response = rulesClient.upload(request);
assertThat(response.getStatusCode().value()).isEqualTo(HttpStatus.SC_OK);
assertThat(response.getBody().getRulesSyntaxWarningMessages()).isNotEmpty();
assertThat(response.getBody().getRulesSyntaxErrorMessages()).isEmpty();
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId()))
.get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(2); //1. beim Anlegen des DossierTemplates; 2. bei provideTestTemplate()
// case 2: dry-run true, error messages and warning messages
when(redactionClient.testRules(new RuleValidationModel(request.getRuleFileType().name(), request.getRules()))).thenReturn(DroolsSyntaxValidation.builder()
.droolsSyntaxErrorMessages(errorMessages)
.droolsSyntaxDeprecatedWarnings(
warningMessages)
.build());
response = rulesClient.upload(request);
assertThat(response.getStatusCode().value()).isEqualTo(HttpStatus.SC_OK);
assertThat(response.getBody().getRulesSyntaxWarningMessages()).isNotEmpty();
assertThat(response.getBody().getRulesSyntaxErrorMessages()).isNotEmpty();
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId()))
.get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(2); //1. beim Anlegen des DossierTemplates; 2. bei provideTestTemplate()
}
@Test
public void testRulesWithRunDrySetFalse() {
var dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();
List<DroolsSyntaxDeprecatedWarnings> warningMessages = new ArrayList<>();
warningMessages.add(DroolsSyntaxDeprecatedWarnings.builder().line(1).column(0).message("this function is deprecated").build());
List<DroolsSyntaxErrorMessage> errorMessages = new ArrayList<>();
errorMessages.add(DroolsSyntaxErrorMessage.builder().line(1).column(0).message("error message").build());
var request = new RulesUploadRequestModel("lorem ipsum", dossierTemplate.getId(), RuleFileType.ENTITY, false);
// case 1: dry-run false, error messages and warning messages
when(redactionClient.testRules(new RuleValidationModel(request.getRuleFileType().name(), request.getRules()))).thenReturn(DroolsSyntaxValidation.builder()
.droolsSyntaxErrorMessages(errorMessages)
.droolsSyntaxDeprecatedWarnings(
warningMessages)
.build());
try {
rulesClient.upload(request);
} catch(FeignException e) {
assertThat(e.status()).isEqualTo(HttpStatus.SC_BAD_REQUEST);
}
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId()))
.get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(2); //1. beim Anlegen des DossierTemplates; 2. bei provideTestTemplate()
// case 2: dry-run false, no error messages just warning messages
when(redactionClient.testRules(new RuleValidationModel(request.getRuleFileType().name(), request.getRules()))).thenReturn(DroolsSyntaxValidation.builder()
.droolsSyntaxErrorMessages(Collections.emptyList())
.droolsSyntaxDeprecatedWarnings(
warningMessages)
.build());
ResponseEntity<DroolsSyntaxValidationResponse> response = rulesClient.upload(request);
assertThat(response.getStatusCode().value()).isEqualTo(HttpStatus.SC_OK);
assertThat(response.getBody().getRulesSyntaxWarningMessages()).isNotEmpty();
assertThat(response.getBody().getRulesSyntaxErrorMessages()).isEmpty();
assertThat(versionClient.getVersions(List.of(dossierTemplate.getId()))
.get(dossierTemplate.getId()).getRulesVersion()).isEqualTo(3); //1. beim Anlegen des DossierTemplates; 2. bei provideTestTemplate()
}
}

View File

@ -162,7 +162,7 @@ public abstract class AbstractPersistenceServerServiceTest {
@Autowired
protected ApplicationConfigRepository applicationConfigRepository;
@MockBean
private RedactionClient redactionClient;
protected RedactionClient redactionClient;
@Autowired
protected PrometheusMeterRegistry prometheusMeterRegistry;
@MockBean
@ -247,7 +247,7 @@ public abstract class AbstractPersistenceServerServiceTest {
when(amqpAdmin.getQueueInfo(Mockito.any())).thenReturn(null);
when(entityLogService.getEntityLog(Mockito.any(), Mockito.any())).thenReturn(new EntityLog(1, 1, Lists.newArrayList(), null, 0, 0, 0, 0));
when(entityLogService.getEntityLog(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyBoolean())).thenReturn(new EntityLog(1, 1, Lists.newArrayList(), null, 0, 0, 0, 0));
when(redactionClient.testRules(Mockito.any())).thenReturn(DroolsSyntaxValidation.builder().droolsSyntaxErrorMessages(Collections.emptyList()).build());
when(redactionClient.testRules(Mockito.any())).thenReturn(DroolsSyntaxValidation.builder().droolsSyntaxErrorMessages(Collections.emptyList()).droolsSyntaxDeprecatedWarnings(Collections.emptyList()).build());
}

View File

@ -25,4 +25,16 @@ public class RulesUploadRequestModel {
@Schema(description = "The Rule file type of these rules", allowableValues = {"ENTITY", "COMPONENT"})
private RuleFileType ruleFileType = RuleFileType.ENTITY;
@Schema(description = "The dry-run parameter if set to true the file is not saved (only the validation results are returned", defaultValue = "false")
private boolean dryRun;
public RulesUploadRequestModel(@NonNull String rules, @NonNull String dossierTemplateId, RuleFileType ruleFileType) {
this.rules = rules;
this.dossierTemplateId = dossierTemplateId;
this.ruleFileType = ruleFileType;
this.dryRun = false;
}
}