diff --git a/api/src/main/java/com/databasir/api/DocumentController.java b/api/src/main/java/com/databasir/api/DocumentController.java index 94f9cb8..0ac6895 100644 --- a/api/src/main/java/com/databasir/api/DocumentController.java +++ b/api/src/main/java/com/databasir/api/DocumentController.java @@ -2,7 +2,6 @@ package com.databasir.api; import com.databasir.api.common.LoginUserContext; import com.databasir.common.JsonData; -import com.databasir.core.diff.data.RootDiff; import com.databasir.core.domain.document.data.*; import com.databasir.core.domain.document.generator.DocumentFileType; import com.databasir.core.domain.document.service.DocumentService; @@ -20,6 +19,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import javax.validation.Valid; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; @@ -43,13 +43,6 @@ public class DocumentController { return JsonData.ok(taskIdOpt); } - @GetMapping(Routes.Document.DIFF) - public JsonData diff(@PathVariable Integer projectId, - @RequestParam(name = "originalVersion") Long originalVersion, - @RequestParam(required = false) Long currentVersion) { - return JsonData.ok(documentService.diff(projectId, originalVersion, currentVersion)); - } - @GetMapping(Routes.Document.GET_ONE) public JsonData getByProjectId(@PathVariable Integer projectId, @RequestParam(required = false) Long version) { @@ -85,16 +78,17 @@ public class DocumentController { @GetMapping(Routes.Document.GET_SIMPLE_ONE) public JsonData getSimpleByProjectId(@PathVariable Integer projectId, @RequestParam(required = false) - Long version) { - return JsonData.ok(documentService.getSimpleOneByProjectId(projectId, version)); + Long version, + @RequestParam(required = false) + Long originalVersion) { + return JsonData.ok(documentService.getSimpleOneByProjectId(projectId, version, originalVersion)); } @PostMapping(Routes.Document.GET_TABLE_DETAIL) - public JsonData> getTableDocument( - @PathVariable Integer projectId, - @PathVariable Integer documentId, - @RequestBody List tableIds) { - return JsonData.ok(documentService.getTableDetails(projectId, documentId, tableIds)); + public JsonData> getTableDocument(@PathVariable Integer projectId, + @PathVariable Integer documentId, + @RequestBody @Valid TableDocumentRequest request) { + return JsonData.ok(documentService.getTableDetails(projectId, documentId, request)); } @GetMapping(Routes.Document.LIST_TABLES) diff --git a/core/src/main/java/com/databasir/core/domain/document/comparator/DiffResult.java b/core/src/main/java/com/databasir/core/domain/document/comparator/DiffResult.java new file mode 100644 index 0000000..254bc3f --- /dev/null +++ b/core/src/main/java/com/databasir/core/domain/document/comparator/DiffResult.java @@ -0,0 +1,15 @@ +package com.databasir.core.domain.document.comparator; + +import com.databasir.core.diff.data.DiffType; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +public class DiffResult { + + private final String id; + + private final DiffType diffType; + +} diff --git a/core/src/main/java/com/databasir/core/domain/document/comparator/DocumentDiffs.java b/core/src/main/java/com/databasir/core/domain/document/comparator/DocumentDiffs.java new file mode 100644 index 0000000..162a886 --- /dev/null +++ b/core/src/main/java/com/databasir/core/domain/document/comparator/DocumentDiffs.java @@ -0,0 +1,189 @@ +package com.databasir.core.domain.document.comparator; + +import com.databasir.core.diff.data.DiffType; +import com.databasir.core.domain.document.data.DatabaseDocumentSimpleResponse.TableData; +import com.databasir.core.meta.data.*; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Slf4j +public class DocumentDiffs { + + public static List tableDiff(DatabaseMeta original, DatabaseMeta current) { + return tableDiff(original.getTables(), current.getTables()); + } + + public static List tableDiff(List original, List current) { + Map originalTablesMap = toMap(original, TableMeta::getName); + Map currentTablesMap = toMap(current, TableMeta::getName); + var added = added(originalTablesMap, currentTablesMap) + .stream() + .map(curr -> { + TableDiffResult result = new TableDiffResult(curr.getName(), DiffType.ADDED); + List columnDiffs = + doDiff(Collections.emptyList(), curr.getColumns(), ColumnMeta::getName); + List indexDiffs = + doDiff(Collections.emptyList(), curr.getIndexes(), IndexMeta::getName); + List triggerDiffs = + doDiff(Collections.emptyList(), curr.getTriggers(), TriggerMeta::getName); + List fkDiffs = + doDiff(Collections.emptyList(), + curr.getForeignKeys(), + fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq()); + result.setColumnDiffResults(columnDiffs); + result.setIndexDiffResults(indexDiffs); + result.setTriggerDiffResults(triggerDiffs); + result.setForeignKeyDiffResults(fkDiffs); + return result; + }) + .collect(Collectors.toList()); + var removed = removed(originalTablesMap, currentTablesMap) + .stream() + .map(old -> { + TableDiffResult result = new TableDiffResult(old.getName(), DiffType.REMOVED); + List columnDiffs = + doDiff(old.getColumns(), Collections.emptyList(), ColumnMeta::getName); + List indexDiffs = + doDiff(old.getIndexes(), Collections.emptyList(), IndexMeta::getName); + List triggerDiffs = + doDiff(old.getTriggers(), Collections.emptyList(), TriggerMeta::getName); + List fkDiffs = + doDiff(old.getForeignKeys(), + Collections.emptyList(), + fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq()); + result.setColumnDiffResults(columnDiffs); + result.setIndexDiffResults(indexDiffs); + result.setTriggerDiffResults(triggerDiffs); + result.setForeignKeyDiffResults(fkDiffs); + return result; + }) + .collect(Collectors.toList()); + List sameOrModified = currentTablesMap.entrySet() + .stream() + .filter(entry -> originalTablesMap.containsKey(entry.getKey())) + .map(entry -> { + String tableName = entry.getKey(); + TableMeta currentTable = entry.getValue(); + TableMeta originalTable = originalTablesMap.get(tableName); + + List columnDiffs = + doDiff(originalTable.getColumns(), currentTable.getColumns(), ColumnMeta::getName); + List indexDiffs = + doDiff(originalTable.getIndexes(), currentTable.getIndexes(), IndexMeta::getName); + List triggerDiffs = + doDiff(originalTable.getTriggers(), currentTable.getTriggers(), TriggerMeta::getName); + List fkDiffs = + doDiff(originalTable.getForeignKeys(), + currentTable.getForeignKeys(), + fk -> fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq()); + + DiffType diffType = Objects.equals(currentTable, originalTable) ? DiffType.NONE : DiffType.MODIFIED; + TableDiffResult diffResult = new TableDiffResult(tableName, diffType); + diffResult.setColumnDiffResults(columnDiffs); + diffResult.setIndexDiffResults(indexDiffs); + diffResult.setTriggerDiffResults(triggerDiffs); + diffResult.setForeignKeyDiffResults(fkDiffs); + return diffResult; + }) + .collect(Collectors.toList()); + + List all = new ArrayList<>(16); + all.addAll(sameOrModified); + all.addAll(added); + all.addAll(removed); + return all; + } + + private static List doDiff(List original, List current, Function idMapping) { + Map originalColumnMap = toMap(original, idMapping); + Map currentColumnMap = toMap(current, idMapping); + BiFunction mapping = (meta, diffType) -> { + return new DiffResult(idMapping.apply(meta), diffType); + }; + List added = added(originalColumnMap, currentColumnMap) + .stream() + .map(col -> mapping.apply(col, DiffType.ADDED)) + .collect(Collectors.toList()); + List removed = removed(originalColumnMap, currentColumnMap) + .stream() + .map(col -> mapping.apply(col, DiffType.REMOVED)) + .collect(Collectors.toList()); + List modified = modified(originalColumnMap, currentColumnMap) + .stream() + .map(col -> mapping.apply(col, DiffType.MODIFIED)) + .collect(Collectors.toList()); + List same = same(originalColumnMap, currentColumnMap) + .stream() + .map(col -> mapping.apply(col, DiffType.NONE)) + .collect(Collectors.toList()); + List columnResults = new ArrayList<>(); + columnResults.addAll(same); + columnResults.addAll(added); + columnResults.addAll(modified); + columnResults.addAll(removed); + return columnResults; + } + + private static Map toMap(List content, Function idMapping) { + return content + .stream() + .collect(Collectors.toMap(idMapping, Function.identity(), (a, b) -> { + log.warn("Duplicate key, origin = {}, current = {}", a, b); + return a; + })); + } + + private static List added(Map originalMapById, + Map currentMapById) { + return currentMapById.entrySet() + .stream() + .filter(entry -> !originalMapById.containsKey(entry.getKey())) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } + + private static List removed(Map originalMapById, + Map currentMapById) { + return originalMapById.entrySet() + .stream() + .filter(entry -> !currentMapById.containsKey(entry.getKey())) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } + + private static List modified(Map originalMapById, + Map currentMapById) { + return modified(originalMapById, currentMapById, Objects::equals); + } + + private static List modified(Map originalMapById, + Map currentMapById, + BiFunction sameFunction) { + return currentMapById.entrySet() + .stream() + .filter(entry -> originalMapById.containsKey(entry.getKey())) + .filter(entry -> !sameFunction.apply(entry.getValue(), originalMapById.get(entry.getKey()))) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } + + private static List same(Map originalMapById, + Map currentMapById) { + return same(originalMapById, currentMapById, Objects::equals); + } + + private static List same(Map originalMapById, + Map currentMapById, + BiFunction sameFunction) { + return currentMapById.entrySet() + .stream() + .filter(entry -> originalMapById.containsKey(entry.getKey())) + .filter(entry -> sameFunction.apply(entry.getValue(), originalMapById.get(entry.getKey()))) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + } +} diff --git a/core/src/main/java/com/databasir/core/domain/document/comparator/TableDiffResult.java b/core/src/main/java/com/databasir/core/domain/document/comparator/TableDiffResult.java new file mode 100644 index 0000000..1c27e2d --- /dev/null +++ b/core/src/main/java/com/databasir/core/domain/document/comparator/TableDiffResult.java @@ -0,0 +1,25 @@ +package com.databasir.core.domain.document.comparator; + +import com.databasir.core.diff.data.DiffType; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.util.Collection; +import java.util.Collections; + +@Data +@RequiredArgsConstructor +public class TableDiffResult { + + private final String id; + + private final DiffType diffType; + + private Collection columnDiffResults = Collections.emptyList(); + + private Collection indexDiffResults = Collections.emptyList(); + + private Collection triggerDiffResults = Collections.emptyList(); + + private Collection foreignKeyDiffResults = Collections.emptyList(); +} diff --git a/core/src/main/java/com/databasir/core/domain/document/converter/DatabaseMetaConverter.java b/core/src/main/java/com/databasir/core/domain/document/converter/DatabaseMetaConverter.java index 65c0b07..220dc7b 100644 --- a/core/src/main/java/com/databasir/core/domain/document/converter/DatabaseMetaConverter.java +++ b/core/src/main/java/com/databasir/core/domain/document/converter/DatabaseMetaConverter.java @@ -1,5 +1,6 @@ package com.databasir.core.domain.document.converter; +import com.databasir.core.domain.document.data.TableDocumentResponse; import com.databasir.core.infrastructure.converter.JsonConverter; import com.databasir.core.meta.data.ColumnMeta; import com.databasir.core.meta.data.DatabaseMeta; @@ -64,6 +65,10 @@ public interface DatabaseMetaConverter { @Mapping(target = "columnNames", source = "pojo.columnNameArray") IndexMeta of(TableIndexDocumentPojo pojo); + List of(List table); + + TableMeta of(TableDocumentResponse table); + default Map> groupBy(List content, Function idMapping) { return content.stream() .collect(Collectors.groupingBy(idMapping)); diff --git a/core/src/main/java/com/databasir/core/domain/document/converter/DocumentPojoConverter.java b/core/src/main/java/com/databasir/core/domain/document/converter/DocumentPojoConverter.java index 9d35e98..f18b199 100644 --- a/core/src/main/java/com/databasir/core/domain/document/converter/DocumentPojoConverter.java +++ b/core/src/main/java/com/databasir/core/domain/document/converter/DocumentPojoConverter.java @@ -1,10 +1,7 @@ package com.databasir.core.domain.document.converter; import com.databasir.core.infrastructure.converter.JsonConverter; -import com.databasir.core.meta.data.ColumnMeta; -import com.databasir.core.meta.data.ForeignKeyMeta; -import com.databasir.core.meta.data.IndexMeta; -import com.databasir.core.meta.data.TriggerMeta; +import com.databasir.core.meta.data.*; import com.databasir.dao.tables.pojos.*; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -20,7 +17,7 @@ public interface DocumentPojoConverter { @Mapping(target = "schemaName", source = "meta.schemaName") @Mapping(target = "isArchive", constant = "false") DatabaseDocumentPojo toDatabasePojo(Integer projectId, - com.databasir.core.meta.data.DatabaseMeta meta, + DatabaseMeta meta, Long version); TableDocumentPojo toTablePojo(Integer databaseDocumentId, diff --git a/core/src/main/java/com/databasir/core/domain/document/converter/DocumentSimpleResponseConverter.java b/core/src/main/java/com/databasir/core/domain/document/converter/DocumentSimpleResponseConverter.java index 8abf891..5c5e4f9 100644 --- a/core/src/main/java/com/databasir/core/domain/document/converter/DocumentSimpleResponseConverter.java +++ b/core/src/main/java/com/databasir/core/domain/document/converter/DocumentSimpleResponseConverter.java @@ -1,5 +1,6 @@ package com.databasir.core.domain.document.converter; +import com.databasir.core.diff.data.DiffType; import com.databasir.core.domain.document.data.DatabaseDocumentSimpleResponse; import com.databasir.core.infrastructure.converter.JsonConverter; import com.databasir.dao.tables.pojos.DatabaseDocumentPojo; @@ -19,7 +20,8 @@ public interface DocumentSimpleResponseConverter { @Mapping(target = "createAt", source = "databaseDocument.createAt") @Mapping(target = "documentVersion", source = "databaseDocument.version") DatabaseDocumentSimpleResponse of(DatabaseDocumentPojo databaseDocument, - List tables); + List tables, + DiffType diffType); DatabaseDocumentSimpleResponse.TableData of(TableDocumentPojo tables, Integer discussionCount, diff --git a/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentResponse.java b/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentResponse.java index ddd42c6..602bf61 100644 --- a/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentResponse.java +++ b/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentResponse.java @@ -1,5 +1,6 @@ package com.databasir.core.domain.document.data; +import com.databasir.core.diff.data.DiffType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -32,4 +33,6 @@ public class DatabaseDocumentResponse { private LocalDateTime createAt; + private DiffType diffType; + } diff --git a/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentSimpleResponse.java b/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentSimpleResponse.java index 38e7bfe..7fe1946 100644 --- a/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentSimpleResponse.java +++ b/core/src/main/java/com/databasir/core/domain/document/data/DatabaseDocumentSimpleResponse.java @@ -1,5 +1,6 @@ package com.databasir.core.domain.document.data; +import com.databasir.core.diff.data.DiffType; import lombok.Data; import java.time.LocalDateTime; @@ -25,8 +26,10 @@ public class DatabaseDocumentSimpleResponse { private LocalDateTime createAt; + private DiffType diffType; + @Data - public static class TableData { + public static class TableData implements DiffAble { private Integer id; @@ -39,5 +42,9 @@ public class DatabaseDocumentSimpleResponse { private Integer discussionCount; private String description; + + private DiffType diffType; + + private TableData original; } } diff --git a/core/src/main/java/com/databasir/core/domain/document/data/DiffAble.java b/core/src/main/java/com/databasir/core/domain/document/data/DiffAble.java new file mode 100644 index 0000000..50ce3c7 --- /dev/null +++ b/core/src/main/java/com/databasir/core/domain/document/data/DiffAble.java @@ -0,0 +1,11 @@ +package com.databasir.core.domain.document.data; + +import com.databasir.core.diff.data.DiffType; + +public interface DiffAble { + + void setDiffType(DiffType diffType); + + void setOriginal(T t); +} + diff --git a/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentRequest.java b/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentRequest.java new file mode 100644 index 0000000..0228d92 --- /dev/null +++ b/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentRequest.java @@ -0,0 +1,16 @@ +package com.databasir.core.domain.document.data; + +import lombok.Data; + +import java.util.Collection; +import java.util.Collections; + +@Data +public class TableDocumentRequest { + + private Collection tableIds = Collections.emptyList(); + + private Long originalVersion; + + private Long currentVersion; +} diff --git a/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentResponse.java b/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentResponse.java index ab7db5b..5f10bd3 100644 --- a/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentResponse.java +++ b/core/src/main/java/com/databasir/core/domain/document/data/TableDocumentResponse.java @@ -1,5 +1,6 @@ package com.databasir.core.domain.document.data; +import com.databasir.core.diff.data.DiffType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,7 +14,7 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor @Builder -public class TableDocumentResponse { +public class TableDocumentResponse implements DiffAble { private Integer id; @@ -41,11 +42,15 @@ public class TableDocumentResponse { private LocalDateTime createAt; + private DiffType diffType; + + private TableDocumentResponse original; + @Data @NoArgsConstructor @AllArgsConstructor @Builder - public static class ColumnDocumentResponse { + public static class ColumnDocumentResponse implements DiffAble { private Integer id; private String name; @@ -71,13 +76,17 @@ public class TableDocumentResponse { private Integer discussionCount; private LocalDateTime createAt; + + private DiffType diffType; + + private ColumnDocumentResponse original; } @Data @NoArgsConstructor @AllArgsConstructor @Builder - public static class IndexDocumentResponse { + public static class IndexDocumentResponse implements DiffAble { private Integer id; @@ -89,13 +98,17 @@ public class TableDocumentResponse { private List columnNames = new ArrayList<>(); private LocalDateTime createAt; + + private DiffType diffType; + + private IndexDocumentResponse original; } @Data @NoArgsConstructor @AllArgsConstructor @Builder - public static class ForeignKeyDocumentResponse { + public static class ForeignKeyDocumentResponse implements DiffAble { private Integer id; @@ -105,6 +118,8 @@ public class TableDocumentResponse { private String fkColumnName; + private Integer keySeq; + private String pkName; private String pkTableName; @@ -116,13 +131,17 @@ public class TableDocumentResponse { private String deleteRule; private LocalDateTime createAt; + + private DiffType diffType; + + private ForeignKeyDocumentResponse original; } @Data @NoArgsConstructor @AllArgsConstructor @Builder - public static class TriggerDocumentResponse { + public static class TriggerDocumentResponse implements DiffAble { private Integer id; @@ -137,5 +156,9 @@ public class TableDocumentResponse { private String triggerCreateAt; private LocalDateTime createAt; + + private DiffType diffType; + + private TriggerDocumentResponse original; } } \ No newline at end of file diff --git a/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java b/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java index 9d8d94b..beb6961 100644 --- a/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java +++ b/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java @@ -7,8 +7,12 @@ import com.databasir.core.diff.Diffs; import com.databasir.core.diff.data.DiffType; import com.databasir.core.diff.data.RootDiff; import com.databasir.core.domain.DomainErrors; +import com.databasir.core.domain.document.comparator.DiffResult; +import com.databasir.core.domain.document.comparator.DocumentDiffs; +import com.databasir.core.domain.document.comparator.TableDiffResult; import com.databasir.core.domain.document.converter.*; import com.databasir.core.domain.document.data.*; +import com.databasir.core.domain.document.data.TableDocumentResponse.ForeignKeyDocumentResponse; import com.databasir.core.domain.document.event.DocumentUpdated; import com.databasir.core.domain.document.generator.DocumentFileGenerator; import com.databasir.core.domain.document.generator.DocumentFileType; @@ -16,6 +20,7 @@ import com.databasir.core.infrastructure.connection.DatabaseConnectionService; import com.databasir.core.infrastructure.converter.JsonConverter; import com.databasir.core.infrastructure.event.EventPublisher; import com.databasir.core.meta.data.DatabaseMeta; +import com.databasir.core.meta.data.TableMeta; import com.databasir.dao.impl.*; import com.databasir.dao.tables.pojos.*; import com.databasir.dao.value.DocumentDiscussionCountPojo; @@ -32,12 +37,12 @@ import org.springframework.util.CollectionUtils; import java.io.OutputStream; import java.sql.Connection; import java.sql.SQLException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; +import static java.util.Collections.emptyList; + @Service @RequiredArgsConstructor @Slf4j @@ -51,8 +56,6 @@ public class DocumentService { private final DataSourcePropertyDao dataSourcePropertyDao; - private final SysKeyDao sysKeyDao; - private final DatabaseConnectionService databaseConnectionService; private final DatabaseDocumentDao databaseDocumentDao; @@ -199,13 +202,16 @@ public class DocumentService { projectId, meta.getDatabaseName(), version); } - public Optional getSimpleOneByProjectId(Integer projectId, Long version) { + public Optional getSimpleOneByProjectId(Integer projectId, + Long version, + Long originalVersion) { Optional documentOption; if (version == null) { documentOption = databaseDocumentDao.selectNotArchivedByProjectId(projectId); } else { documentOption = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, version); } + return documentOption.map(document -> { Integer id = document.getId(); var tables = tableDocumentDao.selectByDatabaseDocumentId(id); @@ -213,7 +219,7 @@ public class DocumentService { documentDiscussionDao.selectTableDiscussionCount(projectId) .stream() .collect(Collectors.toMap(d -> d.getTableName(), d -> d.getCount(), (a, b) -> a)); - Map descriptionMapByTableName = + var descriptionMapByTableName = documentDescriptionDao.selectTableDescriptionByProjectId(projectId) .stream() .collect(Collectors.toMap(d -> d.getTableName(), d -> d.getContent(), (a, b) -> a)); @@ -222,10 +228,70 @@ public class DocumentService { discussionCountMapByTableName, descriptionMapByTableName ); - return documentSimpleResponseConverter.of(document, tableMetas); + + // if original version is not null mean version diff enabled + if (originalVersion != null) { + var originalDocument = + databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, originalVersion) + .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); + var originalTables = tableDocumentDao.selectByDatabaseDocumentId(originalDocument.getId()); + var originalTableMetas = documentSimpleResponseConverter.of( + originalTables, + discussionCountMapByTableName, + descriptionMapByTableName + ); + var originalMap = originalTableMetas.stream() + .collect(Collectors.toMap(t -> t.getName(), Function.identity(), (a, b) -> a)); + var currentMap = tableMetas.stream() + .collect(Collectors.toMap(t -> t.getName(), Function.identity(), (a, b) -> a)); + List diffResults = tableDiffs(projectId, originalVersion, version); + + List result = new ArrayList<>(); + for (TableDiffResult diffResult : diffResults) { + var cur = currentMap.get(diffResult.getId()); + var org = originalMap.get(diffResult.getId()); + if (diffResult.getDiffType() == DiffType.ADDED) { + cur.setDiffType(DiffType.ADDED); + result.add(cur); + } else if (diffResult.getDiffType() == DiffType.MODIFIED) { + cur.setDiffType(DiffType.MODIFIED); + cur.setOriginal(org); + result.add(cur); + } else if (diffResult.getDiffType() == DiffType.REMOVED) { + org.setDiffType(DiffType.REMOVED); + result.add(org); + } else { + cur.setDiffType(DiffType.NONE); + result.add(cur); + } + } + result.sort(Comparator.comparing(DatabaseDocumentSimpleResponse.TableData::getName)); + DiffType diffType = result.stream() + .anyMatch(t -> t.getDiffType() == DiffType.MODIFIED) ? DiffType.MODIFIED : DiffType.NONE; + return documentSimpleResponseConverter.of(document, result, diffType); + } else { + tableMetas.sort(Comparator.comparing(DatabaseDocumentSimpleResponse.TableData::getName)); + return documentSimpleResponseConverter.of(document, tableMetas, DiffType.NONE); + } }); } + public List tableDiffs(Integer projectId, Long originalVersion, Long currentVersion) { + var original = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, originalVersion) + .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); + DatabaseDocumentPojo current; + if (currentVersion == null) { + current = databaseDocumentDao.selectNotArchivedByProjectId(projectId) + .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); + } else { + current = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, currentVersion) + .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); + } + DatabaseMeta currMeta = retrieveOriginalDatabaseMeta(current); + DatabaseMeta originalMeta = retrieveOriginalDatabaseMeta(original); + return DocumentDiffs.tableDiff(originalMeta, currMeta); + } + public Optional getOneByProjectId(Integer projectId, Long version) { Optional documentOption; @@ -252,11 +318,11 @@ public class DocumentService { var tableDocumentResponseList = tables.stream() .map(table -> { Integer tableId = table.getId(); - var subColumns = columnsGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); - var subIndexes = indexesGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); - var subTriggers = triggersGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); + var subColumns = columnsGroupByTableMetaId.getOrDefault(tableId, emptyList()); + var subIndexes = indexesGroupByTableMetaId.getOrDefault(tableId, emptyList()); + var subTriggers = triggersGroupByTableMetaId.getOrDefault(tableId, emptyList()); var subForeignKeys = - foreignKeysGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); + foreignKeysGroupByTableMetaId.getOrDefault(tableId, emptyList()); return documentResponseConverter.of( table, subColumns, @@ -284,10 +350,10 @@ public class DocumentService { public List getTableDetails(Integer projectId, Integer databaseDocumentId, - List tableIds) { + Collection tableIds) { // maybe deleted if (CollectionUtils.isEmpty(tableIds) || !projectDao.existsById(projectId)) { - return Collections.emptyList(); + return emptyList(); } var tables = tableDocumentDao.selectByDatabaseDocumentIdAndIdIn(databaseDocumentId, tableIds); @@ -338,10 +404,10 @@ public class DocumentService { return tables.stream() .map(table -> { Integer tableId = table.getId(); - var subColumns = columnsGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); - var subIndexes = indexesGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); - var subForeignKeys = foreignKeysGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); - var subTriggers = triggersGroupByTableMetaId.getOrDefault(tableId, Collections.emptyList()); + var subColumns = columnsGroupByTableMetaId.getOrDefault(tableId, emptyList()); + var subIndexes = indexesGroupByTableMetaId.getOrDefault(tableId, emptyList()); + var subForeignKeys = foreignKeysGroupByTableMetaId.getOrDefault(tableId, emptyList()); + var subTriggers = triggersGroupByTableMetaId.getOrDefault(tableId, emptyList()); var discussionCount = discussionCountMapByJoinName.get(table.getName()); var description = descriptionMapByJoinName.get(table.getName()); var columnResponses = documentResponseConverter.of( @@ -362,6 +428,87 @@ public class DocumentService { .collect(Collectors.toList()); } + public List getTableDetails(Integer projectId, + Integer databaseDocumentId, + TableDocumentRequest request) { + // maybe deleted + if (CollectionUtils.isEmpty(request.getTableIds()) || !projectDao.existsById(projectId)) { + return emptyList(); + } + var current = this.getTableDetails(projectId, databaseDocumentId, request.getTableIds()); + if (request.getOriginalVersion() != null) { + DatabaseDocumentPojo doc = + databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, request.getOriginalVersion()) + .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); + List tableNames = current.stream().map(t -> t.getName()).distinct().collect(Collectors.toList()); + List originalTableIds = + tableDocumentDao.selectTableIdsByDatabaseDocumentIdAndTableNameIn(doc.getId(), tableNames); + var original = this.getTableDetails(projectId, doc.getId(), originalTableIds); + Map currentMapByName = current.stream() + .collect(Collectors.toMap(TableDocumentResponse::getName, Function.identity(), (a, b) -> a)); + Map originalMapByName = original.stream() + .collect(Collectors.toMap(TableDocumentResponse::getName, Function.identity(), (a, b) -> a)); + List currentMeta = databaseMetaConverter.of(current); + List originalMeta = databaseMetaConverter.of(original); + List diffs = DocumentDiffs.tableDiff(originalMeta, currentMeta); + return diffs.stream() + .map(diff -> { + if (diff.getDiffType() == DiffType.ADDED) { + TableDocumentResponse c = currentMapByName.get(diff.getId()); + c.setDiffType(DiffType.ADDED); + var cols = + diff(diff.getColumnDiffResults(), emptyList(), c.getColumns(), i -> i.getName()); + c.setColumns(cols); + var indexes = + diff(diff.getIndexDiffResults(), emptyList(), c.getIndexes(), i -> i.getName()); + c.setIndexes(indexes); + var foreignKeys = foreignKeyDiff(diff.getForeignKeyDiffResults(), + emptyList(), c.getForeignKeys()); + c.setForeignKeys(foreignKeys); + var triggers = + diff(diff.getTriggerDiffResults(), emptyList(), c.getTriggers(), t -> t.getName()); + c.setTriggers(triggers); + return c; + } + if (diff.getDiffType() == DiffType.REMOVED) { + TableDocumentResponse t = originalMapByName.get(diff.getId()); + t.setDiffType(DiffType.REMOVED); + return t; + } + if (diff.getDiffType() == DiffType.MODIFIED) { + TableDocumentResponse c = currentMapByName.get(diff.getId()); + TableDocumentResponse o = originalMapByName.get(diff.getId()); + c.setDiffType(DiffType.MODIFIED); + c.setOriginal(o); + var cols = + diff(diff.getColumnDiffResults(), o.getColumns(), c.getColumns(), + col -> col.getName()); + c.setColumns(cols); + var indexes = + diff(diff.getIndexDiffResults(), o.getIndexes(), c.getIndexes(), i -> i.getName()); + c.setIndexes(indexes); + var foreignKeys = foreignKeyDiff(diff.getForeignKeyDiffResults(), + o.getForeignKeys(), c.getForeignKeys()); + c.setForeignKeys(foreignKeys); + var triggers = + diff(diff.getTriggerDiffResults(), o.getTriggers(), c.getTriggers(), + t -> t.getName()); + c.setTriggers(triggers); + return c; + } + TableDocumentResponse t = currentMapByName.get(diff.getId()); + t.setDiffType(DiffType.NONE); + return t; + }) + .sorted(Comparator.comparing(TableDocumentResponse::getName)) + .collect(Collectors.toList()); + + } else { + current.sort(Comparator.comparing(TableDocumentResponse::getName)); + return current; + } + } + public void export(Integer projectId, Long version, DocumentFileType type, @@ -379,22 +526,6 @@ public class DocumentService { }); } - public RootDiff diff(Integer projectId, Long originalVersion, Long currentVersion) { - var original = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, originalVersion) - .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); - DatabaseDocumentPojo current; - if (currentVersion == null) { - current = databaseDocumentDao.selectNotArchivedByProjectId(projectId) - .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); - } else { - current = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, currentVersion) - .orElseThrow(DomainErrors.DOCUMENT_VERSION_IS_INVALID::exception); - } - DatabaseMeta currMeta = retrieveOriginalDatabaseMeta(current); - DatabaseMeta originalMeta = retrieveOriginalDatabaseMeta(original); - return Diffs.diff(originalMeta, currMeta); - } - public List getTableAndColumns(Integer projectId, Long version) { Optional documentOption; if (version == null) { @@ -403,7 +534,7 @@ public class DocumentService { documentOption = databaseDocumentDao.selectOptionalByProjectIdAndVersion(projectId, version); } if (documentOption.isEmpty()) { - return Collections.emptyList(); + return emptyList(); } else { DatabaseDocumentPojo databaseDoc = documentOption.get(); var tables = tableDocumentDao.selectByDatabaseDocumentId(databaseDoc.getId()); @@ -413,4 +544,46 @@ public class DocumentService { return tableResponseConverter.from(tables, columnMapByTableId); } } + + private List diff(Collection diffs, + Collection original, + Collection current, + Function idMapping) { + var currentMapByName = current.stream() + .collect(Collectors.toMap(idMapping, Function.identity(), (a, b) -> a)); + var originalMapByName = original.stream() + .collect(Collectors.toMap(idMapping, Function.identity(), (a, b) -> a)); + return diffs.stream().map(diff -> { + if (diff.getDiffType() == DiffType.ADDED) { + var t = currentMapByName.get(diff.getId()); + t.setDiffType(DiffType.ADDED); + return t; + } + if (diff.getDiffType() == DiffType.REMOVED) { + var t = originalMapByName.get(diff.getId()); + t.setDiffType(DiffType.REMOVED); + return t; + } + if (diff.getDiffType() == DiffType.MODIFIED) { + var c = currentMapByName.get(diff.getId()); + var o = originalMapByName.get(diff.getId()); + c.setDiffType(DiffType.MODIFIED); + c.setOriginal(o); + return c; + } + var t = currentMapByName.get(diff.getId()); + t.setDiffType(DiffType.NONE); + return t; + }) + .collect(Collectors.toList()); + } + + private List foreignKeyDiff(Collection diffs, + Collection original, + Collection current) { + Function idMapping = fk -> { + return fk.getFkTableName() + "." + fk.getFkColumnName() + "." + fk.getKeySeq(); + }; + return diff(diffs, original, current, idMapping); + } } diff --git a/core/src/main/java/com/databasir/core/infrastructure/event/subscriber/SystemStartedEventSubscriber.java b/core/src/main/java/com/databasir/core/infrastructure/event/subscriber/SystemStartedEventSubscriber.java index 4e809c8..3ec3e2a 100644 --- a/core/src/main/java/com/databasir/core/infrastructure/event/subscriber/SystemStartedEventSubscriber.java +++ b/core/src/main/java/com/databasir/core/infrastructure/event/subscriber/SystemStartedEventSubscriber.java @@ -61,7 +61,7 @@ public class SystemStartedEventSubscriber { DocumentTemplatePropertyPojo pojo = new DocumentTemplatePropertyPojo(); pojo.setType(type); pojo.setKey(key); - pojo.setDefaultValue(fieldChineseMap.get(key)); + pojo.setDefaultValue(fieldChineseMap.getOrDefault(key, def)); return pojo; }; // table field name; diff --git a/dao/src/main/java/com/databasir/dao/impl/TableColumnDocumentDao.java b/dao/src/main/java/com/databasir/dao/impl/TableColumnDocumentDao.java index 6552b1c..b387498 100644 --- a/dao/src/main/java/com/databasir/dao/impl/TableColumnDocumentDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/TableColumnDocumentDao.java @@ -6,6 +6,7 @@ import org.jooq.DSLContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -30,7 +31,7 @@ public class TableColumnDocumentDao extends BaseDao { } public List selectByDatabaseDocumentIdAndTableIdIn(Integer schemaDocumentId, - List tableIdIn) { + Collection tableIdIn) { if (tableIdIn == null || tableIdIn.isEmpty()) { return Collections.emptyList(); } diff --git a/dao/src/main/java/com/databasir/dao/impl/TableDocumentDao.java b/dao/src/main/java/com/databasir/dao/impl/TableDocumentDao.java index 12abef7..90416ac 100644 --- a/dao/src/main/java/com/databasir/dao/impl/TableDocumentDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/TableDocumentDao.java @@ -7,6 +7,7 @@ import org.jooq.DSLContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -32,7 +33,7 @@ public class TableDocumentDao extends BaseDao { } public List selectByDatabaseDocumentIdAndIdIn(Integer databaseDocumentId, - List idList) { + Collection idList) { if (idList == null || idList.isEmpty()) { return Collections.emptyList(); } @@ -60,4 +61,16 @@ public class TableDocumentDao extends BaseDao { .and(TABLE_DOCUMENT.ID.eq(id))) .fetchOptionalInto(TableDocumentPojo.class); } + + public List selectTableIdsByDatabaseDocumentIdAndTableNameIn(Integer databaseDocumentId, + Collection tableNames) { + if (tableNames == null || tableNames.isEmpty()) { + return Collections.emptyList(); + } + return getDslContext() + .select(TABLE_DOCUMENT.ID).from(TABLE_DOCUMENT) + .where(TABLE_DOCUMENT.DATABASE_DOCUMENT_ID.eq(databaseDocumentId) + .and(TABLE_DOCUMENT.NAME.in(tableNames))) + .fetchInto(Integer.class); + } } diff --git a/dao/src/main/java/com/databasir/dao/impl/TableForeignKeyDocumentDao.java b/dao/src/main/java/com/databasir/dao/impl/TableForeignKeyDocumentDao.java index 330f571..72b985b 100644 --- a/dao/src/main/java/com/databasir/dao/impl/TableForeignKeyDocumentDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/TableForeignKeyDocumentDao.java @@ -6,6 +6,7 @@ import org.jooq.DSLContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -30,7 +31,7 @@ public class TableForeignKeyDocumentDao extends BaseDao selectByDatabaseDocumentIdAndTableIdIn(Integer databaseDocumentId, - List tableIdIn) { + Collection tableIdIn) { if (tableIdIn == null || tableIdIn.isEmpty()) { return Collections.emptyList(); } diff --git a/dao/src/main/java/com/databasir/dao/impl/TableIndexDocumentDao.java b/dao/src/main/java/com/databasir/dao/impl/TableIndexDocumentDao.java index 5d905dc..5de3920 100644 --- a/dao/src/main/java/com/databasir/dao/impl/TableIndexDocumentDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/TableIndexDocumentDao.java @@ -6,6 +6,7 @@ import org.jooq.DSLContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -30,7 +31,7 @@ public class TableIndexDocumentDao extends BaseDao { } public List selectByDatabaseDocumentIdAndIdIn(Integer documentId, - List tableIdIn) { + Collection tableIdIn) { if (tableIdIn == null || tableIdIn.isEmpty()) { return Collections.emptyList(); } diff --git a/dao/src/main/java/com/databasir/dao/impl/TableTriggerDocumentDao.java b/dao/src/main/java/com/databasir/dao/impl/TableTriggerDocumentDao.java index bd7b03e..5b32ae9 100644 --- a/dao/src/main/java/com/databasir/dao/impl/TableTriggerDocumentDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/TableTriggerDocumentDao.java @@ -6,6 +6,7 @@ import org.jooq.DSLContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -30,7 +31,7 @@ public class TableTriggerDocumentDao extends BaseDao { } public List selectByDatabaseDocumentIdAndIdIn(Integer documentId, - List tableIdIn) { + Collection tableIdIn) { if (tableIdIn == null || tableIdIn.isEmpty()) { return Collections.emptyList(); }