diff --git a/src/main/java/org/gridsuite/studyconfig/server/controller/SpreadsheetConfigController.java b/src/main/java/org/gridsuite/studyconfig/server/controller/SpreadsheetConfigController.java index d0e273b..2af9535 100644 --- a/src/main/java/org/gridsuite/studyconfig/server/controller/SpreadsheetConfigController.java +++ b/src/main/java/org/gridsuite/studyconfig/server/controller/SpreadsheetConfigController.java @@ -15,10 +15,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.gridsuite.studyconfig.server.StudyConfigApi; -import org.gridsuite.studyconfig.server.dto.ColumnInfos; -import org.gridsuite.studyconfig.server.dto.GlobalFilterInfos; -import org.gridsuite.studyconfig.server.dto.MetadataInfos; -import org.gridsuite.studyconfig.server.dto.SpreadsheetConfigInfos; +import org.gridsuite.studyconfig.server.dto.*; import org.gridsuite.studyconfig.server.service.SpreadsheetConfigService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -179,6 +176,20 @@ public ResponseEntity reorderColumns( return ResponseEntity.noContent().build(); } + @PutMapping("/{id}/columns/states") + @Operation(summary = "Update column states", + description = "Updates the visibility and order of columns in a spreadsheet configuration") + @ApiResponse(responseCode = "204", description = "Column states updated successfully") + @ApiResponse(responseCode = "404", description = "Spreadsheet configuration not found") + @ApiResponse(responseCode = "400", description = "Invalid column state data") + public ResponseEntity updateColumnStates( + @Parameter(description = "ID of the spreadsheet config") @PathVariable UUID id, + @Parameter(description = "List of column state updates") + @Valid @RequestBody List columnStates) { + spreadsheetConfigService.updateColumnStates(id, columnStates); + return ResponseEntity.noContent().build(); + } + @PostMapping("/{id}/global-filters") @Operation(summary = "Set global filters", description = "Replaces all existing global filters with the provided list for a spreadsheet configuration") diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnInfos.java b/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnInfos.java index 797bc06..0268194 100644 --- a/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnInfos.java +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnInfos.java @@ -51,5 +51,14 @@ public record ColumnInfos( String filterValue, @Schema(description = "Filter tolerance for numeric comparisons") - Double filterTolerance -) { } + Double filterTolerance, + + @Schema(description = "Column visibility", defaultValue = "true") + Boolean visible +) { + public ColumnInfos { + if (visible == null) { + visible = true; + } + } +} diff --git a/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnStateUpdateInfos.java b/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnStateUpdateInfos.java new file mode 100644 index 0000000..3268233 --- /dev/null +++ b/src/main/java/org/gridsuite/studyconfig/server/dto/ColumnStateUpdateInfos.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2025, 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.studyconfig.server.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +import java.util.UUID; + +/** + * DTO for updating column state (visibility and order) + * @author Achour BERRAHMA + */ +@Schema(name = "ColumnStateUpdateDto", description = "Column state update information") +public record ColumnStateUpdateInfos( + + @NotNull(message = "Column UUID is mandatory") + @Schema(description = "Column UUID") + UUID columnId, + + @NotNull(message = "Visible state is mandatory") + @Schema(description = "Column visibility state") + Boolean visible, + + @NotNull(message = "Order is mandatory") + @Schema(description = "New position in the column order (0-based index)") + Integer order + +) { } diff --git a/src/main/java/org/gridsuite/studyconfig/server/entities/ColumnEntity.java b/src/main/java/org/gridsuite/studyconfig/server/entities/ColumnEntity.java index 9522f45..ce83203 100644 --- a/src/main/java/org/gridsuite/studyconfig/server/entities/ColumnEntity.java +++ b/src/main/java/org/gridsuite/studyconfig/server/entities/ColumnEntity.java @@ -61,4 +61,7 @@ public class ColumnEntity { @Column(name = "filter_tolerance") private Double filterTolerance; + @Column(name = "visible", nullable = false) + @Builder.Default + private boolean visible = true; } diff --git a/src/main/java/org/gridsuite/studyconfig/server/mapper/SpreadsheetConfigMapper.java b/src/main/java/org/gridsuite/studyconfig/server/mapper/SpreadsheetConfigMapper.java index 6106fd1..1c008ff 100644 --- a/src/main/java/org/gridsuite/studyconfig/server/mapper/SpreadsheetConfigMapper.java +++ b/src/main/java/org/gridsuite/studyconfig/server/mapper/SpreadsheetConfigMapper.java @@ -73,7 +73,8 @@ public static ColumnInfos toColumnDto(ColumnEntity entity) { entity.getFilterDataType(), entity.getFilterType(), entity.getFilterValue(), - entity.getFilterTolerance() + entity.getFilterTolerance(), + entity.isVisible() ); } @@ -89,6 +90,7 @@ public static ColumnEntity toColumnEntity(ColumnInfos dto) { .filterType(dto.filterType()) .filterValue(dto.filterValue()) .filterTolerance(dto.filterTolerance()) + .visible(dto.visible()) .build(); } diff --git a/src/main/java/org/gridsuite/studyconfig/server/service/SpreadsheetConfigService.java b/src/main/java/org/gridsuite/studyconfig/server/service/SpreadsheetConfigService.java index 5a97e1f..6018c24 100644 --- a/src/main/java/org/gridsuite/studyconfig/server/service/SpreadsheetConfigService.java +++ b/src/main/java/org/gridsuite/studyconfig/server/service/SpreadsheetConfigService.java @@ -333,6 +333,7 @@ public void updateColumn(UUID id, UUID columnId, ColumnInfos dto) { columnEntity.setFilterType(dto.filterType()); columnEntity.setFilterValue(dto.filterValue()); columnEntity.setFilterTolerance(dto.filterTolerance()); + columnEntity.setVisible(dto.visible()); spreadsheetConfigRepository.save(entity); } @@ -352,11 +353,35 @@ public void reorderColumns(UUID id, List columnOrder) { SpreadsheetConfigEntity entity = findEntityById(id); List columns = entity.getColumns(); - columns.sort((c1, c2) -> { - int idx1 = columnOrder.indexOf(c1.getUuid()); - int idx2 = columnOrder.indexOf(c2.getUuid()); - return Integer.compare(idx1, idx2); - }); + reorderColumns(columnOrder, columns); + } + + private static void reorderColumns(List columnOrder, List columns) { + columns.sort(Comparator.comparingInt(column -> columnOrder.indexOf(column.getUuid()))); + } + + @Transactional + public void updateColumnStates(UUID id, List columnStates) { + SpreadsheetConfigEntity entity = findEntityById(id); + List columns = entity.getColumns(); + + Map columnMap = columns.stream() + .collect(Collectors.toMap(ColumnEntity::getUuid, column -> column)); + + for (ColumnStateUpdateInfos state : columnStates) { + ColumnEntity column = columnMap.get(state.columnId()); + if (column == null) { + throw new EntityNotFoundException(COLUMN_NOT_FOUND + state.columnId()); + } + column.setVisible(state.visible()); + } + + // Reorder columns based on the provided states + List orderedColumnIds = columnStates.stream() + .sorted(Comparator.comparingInt(ColumnStateUpdateInfos::order)) + .map(ColumnStateUpdateInfos::columnId) + .toList(); + reorderColumns(orderedColumnIds, columns); } private SpreadsheetConfigCollectionInfos readDefaultSpreadsheetConfigCollection() throws IOException { diff --git a/src/main/resources/db/changelog/changesets/changelog_20250606T123216Z.xml b/src/main/resources/db/changelog/changesets/changelog_20250606T123216Z.xml new file mode 100644 index 0000000..3037bb0 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20250606T123216Z.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 4f68682..f98db5e 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -53,3 +53,6 @@ databaseChangeLog: - include: file: changesets/changelog_20250612T140000Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20250606T123216Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/studyconfig/server/DtoConverterTest.java b/src/test/java/org/gridsuite/studyconfig/server/DtoConverterTest.java index 99147f3..5f09418 100644 --- a/src/test/java/org/gridsuite/studyconfig/server/DtoConverterTest.java +++ b/src/test/java/org/gridsuite/studyconfig/server/DtoConverterTest.java @@ -39,13 +39,14 @@ void testConversionToDtoOfSpreadsheetConfig() { .sheetType(SheetType.BATTERY) .columns(Arrays.asList( ColumnEntity.builder() - .name("Column1") - .formula("A+B") - .id("id1") - .filterDataType("text") - .filterType("contains") - .filterValue("test") - .build(), + .name("Column1") + .formula("A+B") + .id("id1") + .filterDataType("text") + .filterType("contains") + .filterValue("test") + .visible(false) + .build(), ColumnEntity.builder().name("Column2").formula("C*D").id("id2").build() )) .globalFilters(Arrays.asList( @@ -75,6 +76,7 @@ void testConversionToDtoOfSpreadsheetConfig() { assertThat(d.columns().get(0).filterDataType()).isEqualTo("text"); assertThat(d.columns().get(0).filterType()).isEqualTo("contains"); assertThat(d.columns().get(0).filterValue()).isEqualTo("test"); + assertThat(d.columns().get(0).visible()).isFalse(); assertThat(d.columns().get(1).name()).isEqualTo("Column2"); assertThat(d.columns().get(1).formula()).isEqualTo("C*D"); @@ -82,6 +84,8 @@ void testConversionToDtoOfSpreadsheetConfig() { assertThat(d.columns().get(1).filterDataType()).isNull(); assertThat(d.columns().get(1).filterType()).isNull(); assertThat(d.columns().get(1).filterValue()).isNull(); + assertThat(d.columns().get(1).visible()).isTrue(); + // Global filters assertions assertThat(d.globalFilters()).hasSize(2); assertThat(d.globalFilters().get(0).label()).isEqualTo("GlobalFilter1"); @@ -99,9 +103,9 @@ void testConversionToEntityOfSpreadsheetConfig() { SheetType.BUS, Arrays.asList( new ColumnInfos(null, "Column1", ColumnType.NUMBER, 1, "X+Y", "[\"col1\", \"col2\"]", "id1", - "number", "greaterThan", "100", 0.5), + "number", "greaterThan", "100", 0.5, true), new ColumnInfos(null, "Column2", ColumnType.NUMBER, 2, "Z*W", "[\"col1\"]", "id2", - null, null, null, null) + null, null, null, null, true) ), List.of( GlobalFilterInfos.builder().uuid(filterId).filterType("country").label("GlobalFilter1").recent(false).build() @@ -126,6 +130,7 @@ void testConversionToEntityOfSpreadsheetConfig() { assertThat(e.getColumns().get(0).getFilterType()).isEqualTo("greaterThan"); assertThat(e.getColumns().get(0).getFilterValue()).isEqualTo("100"); assertThat(e.getColumns().get(0).getFilterTolerance()).isEqualTo(0.5); + assertThat(e.getColumns().get(0).isVisible()).isTrue(); assertThat(e.getColumns().get(1).getName()).isEqualTo("Column2"); assertThat(e.getColumns().get(1).getFormula()).isEqualTo("Z*W"); @@ -135,6 +140,7 @@ void testConversionToEntityOfSpreadsheetConfig() { assertThat(e.getColumns().get(1).getFilterType()).isNull(); assertThat(e.getColumns().get(1).getFilterValue()).isNull(); assertThat(e.getColumns().get(1).getFilterTolerance()).isNull(); + assertThat(e.getColumns().get(1).isVisible()).isTrue(); // Global filter assertions assertThat(e.getGlobalFilters()).hasSize(1); @@ -170,6 +176,7 @@ void testConversionToDtoOfColumnWithFilter() { assertThat(d.filterType()).isEqualTo("startsWith"); assertThat(d.filterValue()).isEqualTo("prefix"); assertThat(d.filterTolerance()).isNull(); + assertThat(d.visible()).isTrue(); }); } @@ -186,7 +193,8 @@ void testConversionToEntityOfColumnWithFilter() { "number", "lessThan", "50.5", - 0.1); + 0.1, + true); ColumnEntity column = SpreadsheetConfigMapper.toColumnEntity(dto); @@ -201,6 +209,7 @@ void testConversionToEntityOfColumnWithFilter() { assertThat(e.getFilterType()).isEqualTo("lessThan"); assertThat(e.getFilterValue()).isEqualTo("50.5"); assertThat(e.getFilterTolerance()).isEqualTo(0.1); + assertThat(e.isVisible()).isTrue(); }); } @@ -217,7 +226,8 @@ void testConversionOfColumnWithoutFilter() { null, null, null, - null); + null, + true); ColumnEntity entity = SpreadsheetConfigMapper.toColumnEntity(dto); ColumnInfos convertedDto = SpreadsheetConfigMapper.toColumnDto(entity); diff --git a/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigCollectionIntegrationTest.java b/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigCollectionIntegrationTest.java index 65c7b47..987bcf6 100644 --- a/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigCollectionIntegrationTest.java +++ b/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigCollectionIntegrationTest.java @@ -265,7 +265,7 @@ void testAddSpreadsheetConfigToCollection() throws Exception { UUID collectionUuid = postSpreadsheetConfigCollection(initialCollection); List columnInfos = List.of( - new ColumnInfos(null, "new_col", ColumnType.NUMBER, 1, "formula", "[\"dep\"]", "idNew", null, null, null, null) + new ColumnInfos(null, "new_col", ColumnType.NUMBER, 1, "formula", "[\"dep\"]", "idNew", null, null, null, null, true) ); SpreadsheetConfigInfos newConfig = new SpreadsheetConfigInfos(null, "NewSheet", SheetType.BATTERY, columnInfos, null); @@ -402,8 +402,8 @@ void testReplaceAllSpreadsheetConfigs() throws Exception { private List createSpreadsheetConfigs() { List columnInfos = Arrays.asList( - new ColumnInfos(null, "cust_a", ColumnType.NUMBER, 1, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", null, null, null, null), - new ColumnInfos(null, "cust_b", ColumnType.TEXT, null, "var_minP + 1", null, "idB", null, null, null, null) + new ColumnInfos(null, "cust_a", ColumnType.NUMBER, 1, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", null, null, null, null, true), + new ColumnInfos(null, "cust_b", ColumnType.TEXT, null, "var_minP + 1", null, "idB", null, null, null, null, true) ); return List.of( @@ -415,13 +415,13 @@ private List createSpreadsheetConfigs() { private List createSpreadsheetConfigsWithFilters() { List columnsConfig1 = Arrays.asList( new ColumnInfos(null, "id", ColumnType.TEXT, null, "id", "[\"id\"]", "id", - "text", "equals", "test-value", null), + "text", "equals", "test-value", null, true), new ColumnInfos(null, "name", ColumnType.TEXT, null, "name", "[\"name\"]", "name", - "text", "contains", "name-value", null), + "text", "contains", "name-value", null, true), new ColumnInfos(null, "country1", ColumnType.ENUM, null, "country1", "[\"country1\"]", "country1", - null, null, null, null), + null, null, null, null, true), new ColumnInfos(null, "voltage", ColumnType.NUMBER, 1, "voltage", "[\"voltage\"]", "voltage", - "number", "greaterThan", "100", 0.5) + "number", "greaterThan", "100", 0.5, true) ); List globalFiltersConfig1 = Arrays.asList( @@ -431,11 +431,11 @@ private List createSpreadsheetConfigsWithFilters() { List columnsConfig2 = Arrays.asList( new ColumnInfos(null, "id", ColumnType.TEXT, null, "id", "[\"id\"]", "id", - "text", "contains", "other-value", null), + "text", "contains", "other-value", null, true), new ColumnInfos(null, "type", ColumnType.ENUM, null, "type", "[\"type\"]", "type", - null, null, null, null), + null, null, null, null, true), new ColumnInfos(null, "power", ColumnType.NUMBER, 1, "power", "[\"power\"]", "power", - "number", "lessThan", "50", 0.1) + "number", "lessThan", "50", 0.1, true) ); List globalFiltersConfig2 = List.of( @@ -450,10 +450,10 @@ private List createSpreadsheetConfigsWithFilters() { private List createUpdatedSpreadsheetConfigs() { List columnInfos = Arrays.asList( - new ColumnInfos(null, "cust_a", ColumnType.NUMBER, 1, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", null, null, null, null), - new ColumnInfos(null, "cust_b", ColumnType.TEXT, null, "var_minP + 2", null, "idB", null, null, null, null), - new ColumnInfos(null, "cust_c", ColumnType.ENUM, null, "cust_b + 2", "[\"cust_b\"]", "idC", null, null, null, null), - new ColumnInfos(null, "cust_d", ColumnType.NUMBER, 0, "5 + 1", null, "idD", null, null, null, null) + new ColumnInfos(null, "cust_a", ColumnType.NUMBER, 1, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", null, null, null, null, true), + new ColumnInfos(null, "cust_b", ColumnType.TEXT, null, "var_minP + 2", null, "idB", null, null, null, null, true), + new ColumnInfos(null, "cust_c", ColumnType.ENUM, null, "cust_b + 2", "[\"cust_b\"]", "idC", null, null, null, null, true), + new ColumnInfos(null, "cust_d", ColumnType.NUMBER, 0, "5 + 1", null, "idD", null, null, null, null, true) ); return List.of( @@ -466,9 +466,9 @@ private List createUpdatedSpreadsheetConfigs() { private List createUpdatedSpreadsheetConfigsWithFilters() { List columnsConfig1 = Arrays.asList( new ColumnInfos(null, "id", ColumnType.TEXT, null, "id", "[\"id\"]", "id", - "text", "startsWith", "new-prefix", null), + "text", "startsWith", "new-prefix", null, true), new ColumnInfos(null, "updated", ColumnType.TEXT, null, "updated", "[\"updated\"]", "updated", - null, null, null, null) + null, null, null, null, true) ); List globalFiltersConfig1 = Arrays.asList( @@ -479,9 +479,9 @@ private List createUpdatedSpreadsheetConfigsWithFilters( List columnsConfig2 = Arrays.asList( new ColumnInfos(null, "id", ColumnType.TEXT, null, "id", "[\"id\"]", "id", - "text", "endsWith", "suffix", null), + "text", "endsWith", "suffix", null, true), new ColumnInfos(null, "other", ColumnType.NUMBER, 2, "other", "[\"other\"]", "other", - "number", "between", "10,20", null) + "number", "between", "10,20", null, true) ); List globalFiltersConfig2 = List.of( @@ -490,9 +490,9 @@ private List createUpdatedSpreadsheetConfigsWithFilters( List columnsConfig3 = Arrays.asList( new ColumnInfos(null, "id", ColumnType.TEXT, null, "id", "[\"id\"]", "id", - "text", "contains", "middle", null), + "text", "contains", "middle", null, true), new ColumnInfos(null, "third", ColumnType.BOOLEAN, null, "third", "[\"third\"]", "third", - "boolean", "equals", "true", null) + "boolean", "equals", "true", null, true) ); List globalFiltersConfig3 = List.of( diff --git a/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigIntegrationTest.java b/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigIntegrationTest.java index 8455a22..4246591 100644 --- a/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigIntegrationTest.java +++ b/src/test/java/org/gridsuite/studyconfig/server/SpreadsheetConfigIntegrationTest.java @@ -11,10 +11,7 @@ import org.gridsuite.studyconfig.server.constants.ColumnType; import org.gridsuite.studyconfig.server.constants.SheetType; -import org.gridsuite.studyconfig.server.dto.ColumnInfos; -import org.gridsuite.studyconfig.server.dto.GlobalFilterInfos; -import org.gridsuite.studyconfig.server.dto.MetadataInfos; -import org.gridsuite.studyconfig.server.dto.SpreadsheetConfigInfos; +import org.gridsuite.studyconfig.server.dto.*; import org.gridsuite.studyconfig.server.repositories.SpreadsheetConfigRepository; import org.gridsuite.studyconfig.server.service.SpreadsheetConfigService; import org.junit.jupiter.api.AfterEach; @@ -241,7 +238,7 @@ void testCreateColumn() throws Exception { SpreadsheetConfigInfos config = new SpreadsheetConfigInfos(null, "Battery", SheetType.BATTERY, List.of(), null); UUID configId = saveAndReturnId(config); - ColumnInfos columnToCreate = new ColumnInfos(null, "new_column", ColumnType.NUMBER, 2, "x + 1", "[\"x\"]", "newId", null, null, null, null); + ColumnInfos columnToCreate = new ColumnInfos(null, "new_column", ColumnType.NUMBER, 2, "x + 1", "[\"x\"]", "newId", null, null, null, null, true); MvcResult result = mockMvc.perform(post(URI_SPREADSHEET_CONFIG_GET_PUT + configId + URI_COLUMN_BASE) .content(mapper.writeValueAsString(columnToCreate)) @@ -259,7 +256,7 @@ void testCreateColumn() throws Exception { // Create a column with a filter ColumnInfos columnWithFilter = new ColumnInfos(null, "new_column_with_filter", ColumnType.NUMBER, 2, "x + 1", "[\"x\"]", "newId", - "text", "equals", "test-value", null); + "text", "equals", "test-value", null, true); MvcResult resultWithFilter = mockMvc.perform(post(URI_SPREADSHEET_CONFIG_GET_PUT + configId + URI_COLUMN_BASE) .content(mapper.writeValueAsString(columnWithFilter)) @@ -284,7 +281,7 @@ void testUpdateColumn() throws Exception { UUID columnId = savedConfig.columns().get(0).uuid(); ColumnInfos columnUpdate = new ColumnInfos(columnId, "updated_column", ColumnType.TEXT, null, "new_formula", "[]", "updatedId", - "text", "equals", "updated-value", null); + "text", "equals", "updated-value", null, true); mockMvc.perform(put(URI_SPREADSHEET_CONFIG_GET_PUT + configId + URI_COLUMN_BASE + "/" + columnId) .content(mapper.writeValueAsString(columnUpdate)) @@ -359,6 +356,100 @@ void testReorderColumns() throws Exception { } } + @Test + void testUpdateColumnStates() throws Exception { + // Create config with multiple columns + List columns = Arrays.asList( + new ColumnInfos(null, "col1", ColumnType.TEXT, null, "formula1", null, "id1", null, null, null, null, true), + new ColumnInfos(null, "col2", ColumnType.NUMBER, 2, "formula2", null, "id2", null, null, null, null, true), + new ColumnInfos(null, "col3", ColumnType.BOOLEAN, null, "formula3", null, "id3", null, null, null, null, false) + ); + + SpreadsheetConfigInfos config = new SpreadsheetConfigInfos(null, "TestConfig", SheetType.BATTERY, columns, null); + UUID configId = saveAndReturnId(config); + + // Get the saved config to retrieve column UUIDs + SpreadsheetConfigInfos savedConfig = getSpreadsheetConfig(configId); + List savedColumns = savedConfig.columns(); + assertThat(savedColumns).hasSize(3); + + // Prepare column state updates: reorder and change visibility + List stateUpdates = Arrays.asList( + new ColumnStateUpdateInfos(savedColumns.get(0).uuid(), false, 2), // Hide and move to position 2 + new ColumnStateUpdateInfos(savedColumns.get(1).uuid(), true, 1), // Keep visible, move to position 1 + new ColumnStateUpdateInfos(savedColumns.get(2).uuid(), true, 0) // Show and move to position 0 + ); + + // Call the update column states endpoint + mockMvc.perform(put(URI_SPREADSHEET_CONFIG_GET_PUT + configId + URI_COLUMN_BASE + "/states") + .content(mapper.writeValueAsString(stateUpdates)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + // Verify the updates + SpreadsheetConfigInfos updatedConfig = getSpreadsheetConfig(configId); + List updatedColumns = updatedConfig.columns(); + + // Check visibility updates + ColumnInfos firstColumn = updatedColumns.stream() + .filter(col -> col.uuid().equals(savedColumns.get(0).uuid())) + .findFirst().orElseThrow(); + assertThat(firstColumn.visible()).isFalse(); + + ColumnInfos thirdColumn = updatedColumns.stream() + .filter(col -> col.uuid().equals(savedColumns.get(2).uuid())) + .findFirst().orElseThrow(); + assertThat(thirdColumn.visible()).isTrue(); + + // Check order: should be col3, col2, col1 + assertThat(updatedColumns.get(0).uuid()).isEqualTo(savedColumns.get(2).uuid()); + assertThat(updatedColumns.get(1).uuid()).isEqualTo(savedColumns.get(1).uuid()); + assertThat(updatedColumns.get(2).uuid()).isEqualTo(savedColumns.get(0).uuid()); + } + + @Test + void testUpdateColumnStatesInvalidColumn() throws Exception { + SpreadsheetConfigInfos config = new SpreadsheetConfigInfos(null, "TestConfig", SheetType.BATTERY, createColumns(), null); + UUID configId = saveAndReturnId(config); + + UUID nonExistentColumnId = UUID.randomUUID(); + List stateUpdates = List.of( + new ColumnStateUpdateInfos(nonExistentColumnId, false, 0) + ); + + mockMvc.perform(put(URI_SPREADSHEET_CONFIG_GET_PUT + configId + URI_COLUMN_BASE + "/states") + .content(mapper.writeValueAsString(stateUpdates)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + } + + @Test + void testUpdateColumnStatesNonExistentConfig() throws Exception { + UUID nonExistentConfigId = UUID.randomUUID(); + List stateUpdates = List.of( + new ColumnStateUpdateInfos(UUID.randomUUID(), true, 0) + ); + + mockMvc.perform(put(URI_SPREADSHEET_CONFIG_GET_PUT + nonExistentConfigId + URI_COLUMN_BASE + "/states") + .content(mapper.writeValueAsString(stateUpdates)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + } + + @Test + void testUpdateColumnStatesWithInvalidData() throws Exception { + SpreadsheetConfigInfos config = new SpreadsheetConfigInfos(null, "TestConfig", SheetType.BATTERY, createColumns(), null); + UUID configId = saveAndReturnId(config); + + // Missing required fields (columnId and visible are null) + String invalidJson = "[{\"columnId\": null, \"visible\": null, \"order\": 0}]"; + + mockMvc.perform(put(URI_SPREADSHEET_CONFIG_GET_PUT + configId + URI_COLUMN_BASE + "/states") + .content(invalidJson) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); + } + @Test void testRenameSpreadsheetConfig() throws Exception { SpreadsheetConfigInfos configToRename = new SpreadsheetConfigInfos(null, "OriginalName", SheetType.BATTERY, createColumns(), List.of()); @@ -433,42 +524,42 @@ void testSetGlobalFiltersToNonExistentConfig() throws Exception { private List createColumns() { return Arrays.asList( - new ColumnInfos(null, "cust_a", ColumnType.BOOLEAN, null, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", null, null, null, null), - new ColumnInfos(null, "cust_b", ColumnType.NUMBER, 0, "var_minP + 1", null, "idB", null, null, null, null), - new ColumnInfos(null, "cust_c", ColumnType.NUMBER, 2, "cust_b + 1", "[\"cust_b\"]", "idC", null, null, null, null), - new ColumnInfos(null, "cust_d", ColumnType.TEXT, null, "5 + 2", null, "idD", null, null, null, null) + new ColumnInfos(null, "cust_a", ColumnType.BOOLEAN, null, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", null, null, null, null, true), + new ColumnInfos(null, "cust_b", ColumnType.NUMBER, 0, "var_minP + 1", null, "idB", null, null, null, null, true), + new ColumnInfos(null, "cust_c", ColumnType.NUMBER, 2, "cust_b + 1", "[\"cust_b\"]", "idC", null, null, null, null, true), + new ColumnInfos(null, "cust_d", ColumnType.TEXT, null, "5 + 2", null, "idD", null, null, null, null, true) ); } private List createColumnsWithFilters() { return Arrays.asList( new ColumnInfos(null, "cust_a", ColumnType.BOOLEAN, null, "cust_b + cust_c", "[\"cust_b\", \"cust_c\"]", "idA", - "text", "equals", "test-value", null), + "text", "equals", "test-value", null, true), new ColumnInfos(null, "cust_b", ColumnType.NUMBER, 0, "var_minP + 1", null, "idB", - "number", "greaterThan", "100", 0.5), + "number", "greaterThan", "100", 0.5, true), new ColumnInfos(null, "cust_c", ColumnType.NUMBER, 2, "cust_b + 1", "[\"cust_b\"]", "idC", - "text", "startsWith", "prefix", null), + "text", "startsWith", "prefix", null, true), new ColumnInfos(null, "cust_d", ColumnType.TEXT, null, "5 + 2", null, "idD", - null, null, null, null) + null, null, null, null, true) ); } private List createUpdatedColumns() { return Arrays.asList( - new ColumnInfos(null, "cust_x", ColumnType.BOOLEAN, null, "cust_y * 2", "[\"cust_y\"]", "idX", null, null, null, null), - new ColumnInfos(null, "cust_y", ColumnType.NUMBER, 1, "var_maxP - 1", null, "idY", null, null, null, null), - new ColumnInfos(null, "cust_z", ColumnType.NUMBER, 0, "cust_x / 2", "[\"cust_x\"]", "idZ", null, null, null, null) + new ColumnInfos(null, "cust_x", ColumnType.BOOLEAN, null, "cust_y * 2", "[\"cust_y\"]", "idX", null, null, null, null, true), + new ColumnInfos(null, "cust_y", ColumnType.NUMBER, 1, "var_maxP - 1", null, "idY", null, null, null, null, true), + new ColumnInfos(null, "cust_z", ColumnType.NUMBER, 0, "cust_x / 2", "[\"cust_x\"]", "idZ", null, null, null, null, true) ); } private List createUpdatedColumnsWithFilters() { return Arrays.asList( new ColumnInfos(null, "cust_x", ColumnType.BOOLEAN, null, "cust_y * 2", "[\"cust_y\"]", "idX", - "text", "contains", "updated-value", null), + "text", "contains", "updated-value", null, true), new ColumnInfos(null, "cust_y", ColumnType.NUMBER, 1, "var_maxP - 1", null, "idY", - "number", "lessThan", "50", 0.1), + "number", "lessThan", "50", 0.1, true), new ColumnInfos(null, "cust_z", ColumnType.NUMBER, 0, "cust_x / 2", "[\"cust_x\"]", "idZ", - null, null, null, null) // No filter on this column + null, null, null, null, true) // No filter on this column ); }