Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
@Service
public class SecurityAnalysisStoppedPublisherService {

public static final String CANCEL_MESSAGE = "Security analysis has canceled";
public static final String FAIL_MESSAGE = "Security analysis has failed";

private static final String CATEGORY_BROKER_OUTPUT = SecurityAnalysisStoppedPublisherService.class.getName()
+ ".output-broker-messages";

Expand All @@ -33,11 +36,20 @@ public Supplier<Flux<Message<String>>> publishStopped() {
return () -> stoppedMessagePublisher.log(CATEGORY_BROKER_OUTPUT, Level.FINE);
}

public void publish(UUID resultUuid, String receiver) {
public void publishCancel(UUID resultUuid, String receiver) {
publish(resultUuid, receiver, CANCEL_MESSAGE);
}

public void publishFail(UUID resultUuid, String receiver, String causeMessage) {
publish(resultUuid, receiver, FAIL_MESSAGE + " : " + causeMessage);
}

public void publish(UUID resultUuid, String receiver, String stopMessage) {
stoppedMessagePublisher.onNext(MessageBuilder
.withPayload("")
.setHeader("resultUuid", resultUuid.toString())
.setHeader("receiver", receiver)
.setHeader("message", stopMessage)
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisStoppedPublisherService.CANCEL_MESSAGE;
import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisStoppedPublisherService.FAIL_MESSAGE;

/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
Expand Down Expand Up @@ -168,14 +171,17 @@ public Consumer<Message<String>> consumeRun() {
LOGGER.info("Security analysis complete (resultUuid='{}')", resultContext.getResultUuid());
} else { // result not available : stop computation request
if (cancelComputationRequests.get(resultContext.getResultUuid()) != null) {
stoppedPublisherService.publish(resultContext.getResultUuid(), cancelComputationRequests.get(resultContext.getResultUuid()).getReceiver());
stoppedPublisherService.publishCancel(resultContext.getResultUuid(), cancelComputationRequests.get(resultContext.getResultUuid()).getReceiver());
}
}
})
.doOnError(throwable -> {
.onErrorResume(throwable -> {
if (!(throwable instanceof CancellationException)) {
LOGGER.error(throwable.toString(), throwable);
LOGGER.error(FAIL_MESSAGE, throwable);
stoppedPublisherService.publishFail(resultContext.getResultUuid(), resultContext.getRunContext().getReceiver(), throwable.getMessage());
return resultRepository.delete(resultContext.getResultUuid()).then(Mono.empty());
}
return Mono.empty();
})
.doFinally(s -> {
futures.remove(resultContext.getResultUuid());
Expand All @@ -202,8 +208,8 @@ public Consumer<Message<String>> consumeCancel() {

resultRepository.delete(cancelContext.getResultUuid())
.doOnSuccess(unused -> {
stoppedPublisherService.publish(cancelContext.getResultUuid(), cancelContext.getReceiver());
LOGGER.info("Security analysis stopped (resultUuid='{}')", cancelContext.getResultUuid());
stoppedPublisherService.publishCancel(cancelContext.getResultUuid(), cancelContext.getReceiver());
LOGGER.info(CANCEL_MESSAGE + " (resultUuid='{}')", cancelContext.getResultUuid());
})
.doOnError(throwable -> LOGGER.error(throwable.toString(), throwable))
.subscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.powsybl.network.store.client.PreloadingStrategy;
import org.gridsuite.securityanalysis.server.service.ActionsService;
import org.gridsuite.securityanalysis.server.service.SecurityAnalysisConfigService;
import org.gridsuite.securityanalysis.server.service.SecurityAnalysisService;
import org.gridsuite.securityanalysis.server.service.UuidGeneratorService;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -37,7 +36,10 @@

import static com.powsybl.network.store.model.NetworkStoreApi.VERSION;
import static org.gridsuite.securityanalysis.server.SecurityAnalysisFactoryMock.*;
import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisStoppedPublisherService.CANCEL_MESSAGE;
import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisStoppedPublisherService.FAIL_MESSAGE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.BDDMockito.given;

/**
Expand All @@ -59,6 +61,8 @@ public class SecurityAnalysisControllerTest extends AbstractEmbeddedCassandraSet

private static final String EXPECTED_FILTERED_JSON = "{\"version\":\"1.0\",\"preContingencyResult\":{\"computationOk\":true,\"limitViolations\":[{\"subjectId\":\"l3\",\"limitType\":\"CURRENT\",\"acceptableDuration\":1200,\"limit\":10.0,\"limitReduction\":1.0,\"value\":11.0,\"side\":\"ONE\"}],\"actionsTaken\":[]},\"postContingencyResults\":[{\"contingency\":{\"id\":\"l1\",\"elements\":[{\"id\":\"l1\",\"type\":\"BRANCH\"}]},\"limitViolationsResult\":{\"computationOk\":true,\"limitViolations\":[],\"actionsTaken\":[]}},{\"contingency\":{\"id\":\"l2\",\"elements\":[{\"id\":\"l2\",\"type\":\"BRANCH\"}]},\"limitViolationsResult\":{\"computationOk\":true,\"limitViolations\":[],\"actionsTaken\":[]}}]}";

private static final String ERROR_MESSAGE = "Error message test";

@Autowired
private OutputDestination output;

Expand All @@ -74,9 +78,6 @@ public class SecurityAnalysisControllerTest extends AbstractEmbeddedCassandraSet
@MockBean
private UuidGeneratorService uuidGeneratorService;

@Autowired
private SecurityAnalysisService securityAnalysisService;

@Autowired
private SecurityAnalysisConfigService configService;

Expand All @@ -96,12 +97,18 @@ public void setUp() {
.willReturn(Flux.fromIterable(SecurityAnalysisFactoryMock.CONTINGENCIES));
given(actionsService.getContingencyList(CONTINGENCY_LIST2_NAME, NETWORK_UUID))
.willReturn(Flux.fromIterable(SecurityAnalysisFactoryMock.CONTINGENCIES));
given(actionsService.getContingencyList(CONTINGENCY_LIST_ERROR_NAME, NETWORK_UUID))
.willReturn(Flux.fromIterable(SecurityAnalysisFactoryMock.CONTINGENCIES).thenMany(Flux.error(new RuntimeException(ERROR_MESSAGE))));

// UUID service mocking to always generate the same result UUID
given(uuidGeneratorService.generate()).willReturn(RESULT_UUID);

// mock the powsybl security analysis service
configService.setSecurityAnalysisFactoryClass(SecurityAnalysisFactoryMock.class.getName());

// purge messages
while (output.receive(1000) != null) {
}
}

@Test
Expand Down Expand Up @@ -244,14 +251,59 @@ public void stopTest() {
.expectBody(UUID.class)
.isEqualTo(RESULT_UUID);

Message<byte[]> message = output.receive(1000, "sa.run.destination");
assertEquals(NETWORK_UUID.toString(), message.getHeaders().get("networkUuid"));
assertEquals(RESULT_UUID.toString(), message.getHeaders().get("resultUuid"));
assertEquals(CONTINGENCY_LIST_NAME, message.getHeaders().get("contingencyListNames"));
assertEquals("me", message.getHeaders().get("receiver"));

webTestClient.put()
.uri("/" + VERSION + "/results/" + RESULT_UUID + "/stop"
+ "?receiver=me")
.exchange()
.expectStatus().isOk();

Message<byte[]> cancelMessage = output.receive(1000, "sa.cancel.destination");
assertEquals(RESULT_UUID.toString(), cancelMessage.getHeaders().get("resultUuid"));
assertEquals("me", cancelMessage.getHeaders().get("receiver"));
message = output.receive(1000, "sa.cancel.destination");
assertEquals(RESULT_UUID.toString(), message.getHeaders().get("resultUuid"));
assertEquals("me", message.getHeaders().get("receiver"));

message = output.receive(1000, "sa.stopped.destination");
assertEquals(RESULT_UUID.toString(), message.getHeaders().get("resultUuid"));
assertEquals("me", message.getHeaders().get("receiver"));
assertEquals(CANCEL_MESSAGE, message.getHeaders().get("message"));

assertNull(output.receive(1000));
}

@Test
public void runTestWithError() {
webTestClient.post()
.uri("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?contingencyListName=" + CONTINGENCY_LIST_ERROR_NAME
+ "&receiver=me")
.exchange()
.expectStatus().isOk() // Because fully asynchronous (just publish a message)
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody(UUID.class)
.isEqualTo(RESULT_UUID);

Message<byte[]> message = output.receive(1000, "sa.run.destination");
assertEquals(NETWORK_UUID.toString(), message.getHeaders().get("networkUuid"));
assertEquals(RESULT_UUID.toString(), message.getHeaders().get("resultUuid"));
assertEquals(CONTINGENCY_LIST_ERROR_NAME, message.getHeaders().get("contingencyListNames"));
assertEquals("me", message.getHeaders().get("receiver"));

// Message stopped has been sent
message = output.receive(1000, "sa.stopped.destination");
assertEquals(RESULT_UUID.toString(), message.getHeaders().get("resultUuid"));
assertEquals("me", message.getHeaders().get("receiver"));
assertEquals(FAIL_MESSAGE + " : " + ERROR_MESSAGE, message.getHeaders().get("message"));

assertNull(output.receive(1000));

// No result
webTestClient.get()
.uri("/" + VERSION + "/results/" + RESULT_UUID)
.exchange()
.expectStatus().isNotFound();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class SecurityAnalysisFactoryMock implements SecurityAnalysisFactory {

static final String CONTINGENCY_LIST_NAME = "list1";
static final String CONTINGENCY_LIST2_NAME = "list2";
static final String CONTINGENCY_LIST_ERROR_NAME = "listError";

static final List<Contingency> CONTINGENCIES = List.of(new Contingency("l1", new BranchContingency("l1")),
new Contingency("l2", new BranchContingency("l2")));
Expand Down