Skip to content
Open
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
7 changes: 7 additions & 0 deletions monitor-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,12 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.monitor.commons;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@Getter
public abstract class AbstractProcessConfig implements ProcessConfig {
@JsonProperty(required = true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I don't really understand the logic here: why do we make it required but also add a null check:
        this.modificationUuids = modificationUuids != null ? List.copyOf(modificationUuids) : null;
  • Why do we need an abstraction and can't continue using the record?

private final List<UUID> modificationUuids;

private final String owner;

private final Instant creationDate;

private final Instant lastModificationDate;

private final String lastModifiedBy;

protected AbstractProcessConfig(
List<UUID> modificationUuids,
String owner,
Instant creationDate,
Instant lastModificationDate,
String lastModifiedBy
) {
this.modificationUuids = modificationUuids != null ? List.copyOf(modificationUuids) : null;
this.owner = owner;
this.creationDate = creationDate;
this.lastModificationDate = lastModificationDate;
this.lastModifiedBy = lastModifiedBy;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.monitor.commons;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
public final class Constants {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the logic for now is to put in monitor-commons class that are commons to server and worker.
I see that these constants are only used in the server. So I would put this file in server/Constants.java


private Constants() {
}

public static final String HEADER_USER_ID = "userId";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
*/
package org.gridsuite.monitor.commons;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

Expand All @@ -25,5 +27,17 @@
public interface ProcessConfig {
ProcessType processType();

List<UUID> modificationUuids();
List<UUID> getModificationUuids();

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is readonly to avoid API requests with this data, right?

String getOwner();

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
Instant getCreationDate();

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
Instant getLastModificationDate();

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String getLastModifiedBy();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,43 @@
*/
package org.gridsuite.monitor.commons;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

/**
* @author Antoine Bouhours <antoine.bouhours at rte-france.com>
*/
public record SecurityAnalysisConfig(
UUID parametersUuid,
List<String> contingencies,
List<UUID> modificationUuids
) implements ProcessConfig {
@Getter
@EqualsAndHashCode(callSuper = true)
public class SecurityAnalysisConfig extends AbstractProcessConfig {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment for the record vs class and null/required

@JsonProperty(required = true)
private final UUID parametersUuid;

@JsonProperty(required = true)
private final List<String> contingencies;

public SecurityAnalysisConfig(
UUID parametersUuid,
List<String> contingencies,
List<UUID> modificationUuids,
String owner,
Instant creationDate,
Instant lastModificationDate,
String lastModifiedBy
) {
super(modificationUuids, owner, creationDate, lastModificationDate, lastModifiedBy);
this.parametersUuid = parametersUuid;
this.contingencies = contingencies != null ? List.copyOf(contingencies) : null;
}
Comment on lines +20 to +41
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if -parameters compiler flag is configured in any pom.xml or build.gradle
echo "=== Checking for -parameters compiler flag ==="
rg -n '\-parameters' --type xml --type gradle 2>/dev/null || echo "No -parameters flag found"

echo ""
echo "=== Checking pom.xml for maven-compiler-plugin configuration ==="
fd 'pom.xml' -x cat {} \; | head -200

echo ""
echo "=== Checking for build.gradle ==="
fd 'build.gradle' -x cat {} \; 2>/dev/null | head -150

echo ""
echo "=== Checking for jackson-module-parameter-names dependency ==="
rg -n 'jackson-module-parameter-names' 2>/dev/null || echo "No jackson-module-parameter-names found"

echo ""
echo "=== Check parent POM and Spring Boot version ==="
rg -n 'spring-boot' --type xml -A2 -B2 2>/dev/null | head -50

Repository: gridsuite/monitor-core

Length of output: 10878


Add Jackson deserialization support before deploying the record-to-class migration.

The class lacks the necessary configuration for Jackson to deserialize it. It has a multi-arg constructor without @JsonCreator annotation or @JsonProperty on constructor parameters, no no-arg constructor, and neither the -parameters compiler flag nor the jackson-module-parameter-names dependency is configured. When this class is deserialized via @JsonSubTypes polymorphism, Jackson will fail at runtime with a deserialization error.

Add one of the following before merging:

  • Add the -parameters compiler flag to pom.xml and explicitly declare jackson-module-parameter-names as a dependency, OR
  • Annotate the constructor with @JsonCreator and each parameter with @JsonProperty, OR
  • Add a no-arg constructor (if immutability is not required)
🤖 Prompt for AI Agents
In
`@monitor-commons/src/main/java/org/gridsuite/monitor/commons/SecurityAnalysisConfig.java`
around lines 20 - 41, The SecurityAnalysisConfig class has a multi-arg
constructor that Jackson cannot map during polymorphic deserialization; annotate
the constructor SecurityAnalysisConfig(...) with `@JsonCreator` and add
`@JsonProperty` on each constructor parameter (parametersUuid, contingencies,
modificationUuids, owner, creationDate, lastModificationDate, lastModifiedBy) so
Jackson can bind JSON to the constructor, and keep the existing final fields and
List.copyOf handling.


@Override
public ProcessType processType() {
return ProcessType.SECURITY_ANALYSIS;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) 2026, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.monitor.commons;

import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
class SecurityAnalysisConfigTest {

@Test
void createSecurityAnalysisConfig() {
UUID parametersUuid = UUID.randomUUID();
UUID modificationUuid = UUID.randomUUID();
List<String> contingencies = List.of("contingency1", "contingency2");
String owner = "user1";
Instant creationDate = Instant.now();
Instant lastModificationDate = Instant.now().plusSeconds(60);
String lastModifiedBy = "user2";

SecurityAnalysisConfig config = new SecurityAnalysisConfig(
parametersUuid,
contingencies,
List.of(modificationUuid),
owner,
creationDate,
lastModificationDate,
lastModifiedBy
);

assertThat(config.getParametersUuid()).isEqualTo(parametersUuid);
assertThat(config.getContingencies()).isEqualTo(contingencies);
assertThat(config.getModificationUuids()).containsExactly(modificationUuid);
assertThat(config.getOwner()).isEqualTo(owner);
assertThat(config.getCreationDate()).isEqualTo(creationDate);
assertThat(config.getLastModificationDate()).isEqualTo(lastModificationDate);
assertThat(config.getLastModifiedBy()).isEqualTo(lastModifiedBy);
assertThat(config.processType()).isEqualTo(ProcessType.SECURITY_ANALYSIS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.List;
import java.util.UUID;

import static org.gridsuite.monitor.commons.Constants.HEADER_USER_ID;

/**
* @author Antoine Bouhours <antoine.bouhours at rte-france.com>
*/
Expand All @@ -33,8 +35,6 @@ public class MonitorController {

private final MonitorService monitorService;

public static final String HEADER_USER_ID = "userId";

public MonitorController(MonitorService monitorService) {
this.monitorService = monitorService;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;
import java.util.UUID;

import static org.gridsuite.monitor.commons.Constants.HEADER_USER_ID;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
Expand All @@ -46,8 +49,9 @@ public ProcessConfigController(ProcessConfigService processConfigService) {
@Operation(summary = "Create process config")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "process config was created")})
public ResponseEntity<UUID> createProcessConfig(@RequestBody ProcessConfig processConfig) {
return ResponseEntity.ok().body(processConfigService.createProcessConfig(processConfig));
public ResponseEntity<UUID> createProcessConfig(@RequestBody ProcessConfig processConfig,
@RequestHeader(HEADER_USER_ID) String userId) {
return ResponseEntity.ok().body(processConfigService.createProcessConfig(processConfig, userId));
}

@GetMapping(value = "/{uuid}", produces = MediaType.APPLICATION_JSON_VALUE)
Expand All @@ -68,8 +72,9 @@ public ResponseEntity<ProcessConfig> getProcessConfig(
@ApiResponse(responseCode = "404", description = "process config was not found")})
public ResponseEntity<Void> updateProcessConfig(
@Parameter(description = "process config UUID") @PathVariable("uuid") UUID processConfigUuid,
@RequestBody ProcessConfig processConfig) {
return processConfigService.updateProcessConfig(processConfigUuid, processConfig) ?
@RequestBody ProcessConfig processConfig,
@RequestHeader(HEADER_USER_ID) String userId) {
return processConfigService.updateProcessConfig(processConfigUuid, processConfig, userId) ?
ResponseEntity.ok().build() :
ResponseEntity.notFound().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import org.gridsuite.monitor.commons.ProcessType;

import java.util.List;
import java.util.UUID;
import java.time.Instant;

import static jakarta.persistence.DiscriminatorType.STRING;

Expand All @@ -43,6 +45,7 @@
@AllArgsConstructor
@Getter
@Setter
@SuperBuilder
public abstract class AbstractProcessConfigEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Expand All @@ -57,6 +60,18 @@ public abstract class AbstractProcessConfigEntity {
@OrderColumn(name = "pos_modifications")
private List<UUID> modificationUuids;

@Column(name = "owner", length = 80, nullable = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we usually don't constrain in the bd but it see that it's consistent with directory server

private String owner;

@Column(name = "creation_date", nullable = false)
private Instant creationDate;

@Column(name = "last_modification_date", nullable = false)
private Instant lastModificationDate;

@Column(name = "last_modified_by", length = 80, nullable = false)
private String lastModifiedBy;

public abstract ProcessType getType();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import org.gridsuite.monitor.commons.ProcessType;

import java.util.List;
Expand All @@ -37,6 +38,7 @@
@AllArgsConstructor
@Getter
@Setter
@SuperBuilder
public class SecurityAnalysisConfigEntity extends AbstractProcessConfigEntity {
@Column(name = "parameters_uuid")
private UUID parametersUuid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,45 @@
import org.gridsuite.monitor.commons.SecurityAnalysisConfig;
import org.gridsuite.monitor.server.entities.SecurityAnalysisConfigEntity;

import java.time.Instant;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
*/
@SuppressWarnings("checkstyle:HideUtilityClassConstructor")
public class SecurityAnalysisConfigMapper {
public static SecurityAnalysisConfigEntity toEntity(SecurityAnalysisConfig dto) {
public static SecurityAnalysisConfigEntity toEntity(SecurityAnalysisConfig dto, String owner) {
SecurityAnalysisConfigEntity entity = new SecurityAnalysisConfigEntity();
update(entity, dto);
entity.setOwner(owner);
entity.setLastModifiedBy(owner);
Instant now = Instant.now();
entity.setCreationDate(now);

entity.setLastModificationDate(now);
entity.setParametersUuid(dto.getParametersUuid());
entity.setContingencies(dto.getContingencies());
entity.setModificationUuids(dto.getModificationUuids());
return entity;
}

public static SecurityAnalysisConfig toDto(SecurityAnalysisConfigEntity entity) {
return new SecurityAnalysisConfig(
entity.getParametersUuid(),
entity.getContingencies(),
entity.getModificationUuids()
entity.getModificationUuids(),
entity.getOwner(),
entity.getCreationDate(),
entity.getLastModificationDate(),
entity.getLastModifiedBy()
);
}

public static void update(SecurityAnalysisConfigEntity entity, SecurityAnalysisConfig dto) {
entity.setParametersUuid(dto.parametersUuid());
entity.setContingencies(dto.contingencies());
entity.setModificationUuids(dto.modificationUuids());
public static void update(SecurityAnalysisConfigEntity entity, SecurityAnalysisConfig dto, String userId) {
entity.setLastModifiedBy(userId);
entity.setLastModificationDate(Instant.now());

entity.setParametersUuid(dto.getParametersUuid());
entity.setContingencies(dto.getContingencies());
entity.setModificationUuids(dto.getModificationUuids());
}
}
Loading