2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2024 Ericsson
4 * Modifications Copyright (C) 2024 OpenInfra Foundation Europe
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
21 package org.oran.smo.teiv.pgsqlgenerator.schema.data;
23 import static org.oran.smo.teiv.pgsqlgenerator.Constants.ALTER;
24 import static org.oran.smo.teiv.pgsqlgenerator.Constants.CREATE;
25 import static org.oran.smo.teiv.pgsqlgenerator.Constants.DEFAULT;
26 import static org.oran.smo.teiv.pgsqlgenerator.Constants.INDEX;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.LinkedHashMap;
32 import java.util.List;
34 import java.util.Objects;
36 import org.oran.smo.teiv.pgsqlgenerator.IndexType;
37 import org.oran.smo.teiv.pgsqlgenerator.PostgresIndex;
38 import org.springframework.stereotype.Component;
40 import org.oran.smo.teiv.pgsqlgenerator.Column;
41 import org.oran.smo.teiv.pgsqlgenerator.Table;
44 public class ModelComparator {
46 private Map<String, List<Table>> identifiedChangesToModels;
49 * Identifies differences between baseline and generated models.
51 * @param tablesFromModelService
52 * Model information from Model Service
53 * @param tablesFromBaselineSql
54 * Model information from the baseline
55 * @return A map with identified changes to models
57 public Map<String, List<Table>> identifyDifferencesInBaselineAndGenerated(List<Table> tablesFromModelService,
58 List<Table> tablesFromBaselineSql) {
59 //TODO: Throw error if there is table from model service doesn't contain any info in baseline
60 identifiedChangesToModels = identifiedModelChangeMapping();
62 List<String> tableNamesOfBaseline = extractTableNames(tablesFromBaselineSql);
63 List<String> tableNameOfModelSvc = extractTableNames(tablesFromModelService);
65 if (!tableNameOfModelSvc.equals(tableNamesOfBaseline)) {
66 storeNewTables(tablesFromModelService, tableNamesOfBaseline, tableNameOfModelSvc);
68 compareAndStoreChangesToColumns(tablesFromModelService, tablesFromBaselineSql);
70 return Collections.unmodifiableMap(identifiedChangesToModels);
73 private Map<String, List<Table>> identifiedModelChangeMapping() {
74 Map<String, List<Table>> storeIdentifiedChangesToModels = new LinkedHashMap<>();
75 storeIdentifiedChangesToModels.put(DEFAULT, new ArrayList<>());
76 storeIdentifiedChangesToModels.put(INDEX, new ArrayList<>());
77 storeIdentifiedChangesToModels.put(ALTER, new ArrayList<>());
78 storeIdentifiedChangesToModels.put(CREATE, new ArrayList<>());
79 return storeIdentifiedChangesToModels;
83 * Check if all tables in extracted data from module service are same as what's in baseline schema Store identified with
86 private void storeNewTables(List<Table> tablesFromModelService, List<String> tableNamesOfBaseline,
87 List<String> tableNameOfGenerated) {
88 List<String> differences = tableNameOfGenerated.stream().filter(element -> !tableNamesOfBaseline.contains(element))
90 differences.forEach(tableName -> tablesFromModelService.stream().filter(table -> table.getName().equals(tableName))
91 .findFirst().ifPresent(table -> identifiedChangesToModels.get(CREATE).add(Table.builder().name(table
92 .getName()).columns(table.getColumns()).build())));
96 * Compare columns of each table from module service with columns of each table from baseline schema
98 private void compareAndStoreChangesToColumns(List<Table> tablesFromModelService, List<Table> tablesFromBaselineSql) {
99 tablesFromModelService.forEach(tableFromModelService -> {
100 tablesFromBaselineSql.stream().filter(baselineTable -> tableFromModelService.getName().equals(baselineTable
101 .getName())).findFirst().ifPresent(baselineTable -> {
103 List<Column> columnsInBaseline = new ArrayList<>(baselineTable.getColumns());
104 List<Column> columnsFromModuleSvc = new ArrayList<>(tableFromModelService.getColumns());
106 columnsInBaseline.sort(Comparator.comparing(Column::getName));
107 columnsFromModuleSvc.sort(Comparator.comparing(Column::getName));
109 // Check for new columns in table
110 if (columnsFromModuleSvc.size() > columnsInBaseline.size()) {
111 storeNewColumns(tableFromModelService.getName(), columnsInBaseline, columnsFromModuleSvc);
113 detectAndStoreDefaultValueChanges(tableFromModelService.getName(), columnsInBaseline,
114 columnsFromModuleSvc);
115 detectAndStoreNewlyAddedIndex(tableFromModelService.getName(), columnsInBaseline,
116 columnsFromModuleSvc);
121 private List<String> extractTableNames(List<Table> tables) {
122 return tables.stream().map(Table::getName).sorted().toList();
126 * Check if new columns are introduced by comparing data from module service and baseline schema
128 private void storeNewColumns(String tableName, List<Column> columnsInBaseline, List<Column> columnsFromModuleSvc) {
129 List<Column> newColumns = columnsFromModuleSvc.stream().filter(columnInGenerated -> !getListOfAllColumns(
130 columnsInBaseline).contains(columnInGenerated.getName())).toList();
131 identifiedChangesToModels.get(ALTER).add(Table.builder().name(tableName).columns(newColumns.stream().map(
132 column -> Column.builder().name(column.getName()).dataType(column.getDataType()).postgresConstraints(column
133 .getPostgresConstraints()).defaultValue(column.getDefaultValue()).postgresIndexList(column
134 .getPostgresIndexList()).build()).toList()).build());
138 * Check if default values for all columns in tables from module service are same as what's in baseline schema Store
139 * identified with a "DEFAULT" key
141 private void detectAndStoreDefaultValueChanges(String tableName, List<Column> columnsInBaseline,
142 List<Column> columnsFromModuleSvc) {
143 List<Column> list = new ArrayList<>();
144 columnsInBaseline.forEach(columnInBaseline -> {
145 columnsFromModuleSvc.forEach(columnInGenerated -> {
146 if (columnInGenerated.getName().equals(columnInBaseline.getName()) && !Objects.equals(columnInGenerated
147 .getDefaultValue(), columnInBaseline.getDefaultValue())) {
148 list.add(columnInGenerated);
152 if (!list.isEmpty()) {
153 identifiedChangesToModels.get(DEFAULT).add(Table.builder().name(tableName).columns(list).build());
157 private void detectAndStoreNewlyAddedIndex(String tableName, List<Column> columnsInBaseline,
158 List<Column> columnsFromModuleSvc) {
159 List<Column> columnList = new ArrayList<>();
160 columnsInBaseline.forEach(columnInBaseline -> columnsFromModuleSvc.forEach(columnInGenerated -> {
161 if (columnInGenerated.getName().equals(columnInBaseline.getName())) {
162 List<IndexType> indexInBaselineSchema = columnInBaseline.getPostgresIndexList().stream().map(
163 PostgresIndex::getIndexType).toList();
164 List<PostgresIndex> postgresIndexList = new ArrayList<>();
165 columnInGenerated.getPostgresIndexList().forEach(postgresIndex -> {
166 if (!indexInBaselineSchema.contains(postgresIndex.getIndexType())) {
167 postgresIndexList.add(postgresIndex);
170 if (!postgresIndexList.isEmpty()) {
171 columnInGenerated.setPostgresIndexList(postgresIndexList);
172 columnList.add(columnInGenerated);
176 if (!columnList.isEmpty()) {
177 identifiedChangesToModels.get(INDEX).add(Table.builder().name(tableName).columns(columnList).build());
181 private List<String> getListOfAllColumns(List<Column> columns) {
182 List<String> allColumns = new ArrayList<>();
183 for (Column col : columns) {
184 allColumns.add(col.getName().replace("\"", ""));