fa0423dc587dfc8ee8f4138c5d2768e3a6c35d02
[smo/teiv.git] /
1 /*
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
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21 package org.oran.smo.teiv.pgsqlgenerator.schema.data;
22
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
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Objects;
34
35 import org.springframework.stereotype.Component;
36
37 import org.oran.smo.teiv.pgsqlgenerator.Column;
38 import org.oran.smo.teiv.pgsqlgenerator.Table;
39
40 @Component
41 public class ModelComparator {
42
43     private Map<String, List<Table>> identifiedChangesToModels;
44
45     /**
46      * Identifies differences between baseline and generated models.
47      *
48      * @param tablesFromModelService
49      *     Model information from Model Service
50      * @param tablesFromBaselineSql
51      *     Model information from the baseline
52      * @return A map with identified changes to models
53      */
54     public Map<String, List<Table>> identifyDifferencesInBaselineAndGenerated(List<Table> tablesFromModelService,
55             List<Table> tablesFromBaselineSql) {
56         //TODO: Throw error if there is table from model service doesn't contain any info in baseline
57         identifiedChangesToModels = identifiedModelChangeMapping();
58
59         List<String> tableNamesOfBaseline = extractTableNames(tablesFromBaselineSql);
60         List<String> tableNameOfModelSvc = extractTableNames(tablesFromModelService);
61
62         if (!tableNameOfModelSvc.equals(tableNamesOfBaseline)) {
63             storeNewTables(tablesFromModelService, tableNamesOfBaseline, tableNameOfModelSvc);
64         }
65         compareAndStoreChangesToColumns(tablesFromModelService, tablesFromBaselineSql);
66
67         return Collections.unmodifiableMap(identifiedChangesToModels);
68     }
69
70     private Map<String, List<Table>> identifiedModelChangeMapping() {
71         Map<String, List<Table>> storeIdentifiedChangesToModels = new HashMap<>();
72         storeIdentifiedChangesToModels.put(CREATE, new ArrayList<>());
73         storeIdentifiedChangesToModels.put(ALTER, new ArrayList<>());
74         storeIdentifiedChangesToModels.put(DEFAULT, new ArrayList<>());
75         return storeIdentifiedChangesToModels;
76     }
77
78     /**
79      * Check if all tables in extracted data from module service are same as what's in baseline schema Store identified with
80      * a "CREATE" key
81      */
82     private void storeNewTables(List<Table> tablesFromModelService, List<String> tableNamesOfBaseline,
83             List<String> tableNameOfGenerated) {
84         List<String> differences = tableNameOfGenerated.stream().filter(element -> !tableNamesOfBaseline.contains(element))
85                 .toList();
86         differences.forEach(tableName -> tablesFromModelService.stream().filter(table -> table.getName().equals(tableName))
87                 .findFirst().ifPresent(table -> identifiedChangesToModels.get(CREATE).add(Table.builder().name(table
88                         .getName()).columns(table.getColumns()).build())));
89     }
90
91     /**
92      * Compare columns of each table from module service with columns of each table from baseline schema
93      */
94     private void compareAndStoreChangesToColumns(List<Table> tablesFromModelService, List<Table> tablesFromBaselineSql) {
95         tablesFromModelService.forEach(tableFromModelService -> {
96             tablesFromBaselineSql.stream().filter(baselineTable -> tableFromModelService.getName().equals(baselineTable
97                     .getName())).findFirst().ifPresent(baselineTable -> {
98
99                         List<Column> columnsInBaseline = new ArrayList<>(baselineTable.getColumns());
100                         List<Column> columnsFromModuleSvc = new ArrayList<>(tableFromModelService.getColumns());
101
102                         columnsInBaseline.sort(Comparator.comparing(Column::getName));
103                         columnsFromModuleSvc.sort(Comparator.comparing(Column::getName));
104
105                         // Check for new columns in table
106                         if (columnsFromModuleSvc.size() > columnsInBaseline.size()) {
107                             storeNewColumns(tableFromModelService.getName(), columnsInBaseline, columnsFromModuleSvc);
108                         }
109                         detectAndStoreDefaultValueChanges(tableFromModelService.getName(), columnsInBaseline,
110                                 columnsFromModuleSvc);
111                     });
112         });
113     }
114
115     private List<String> extractTableNames(List<Table> tables) {
116         return tables.stream().map(Table::getName).sorted().toList();
117     }
118
119     /**
120      * Check if new columns are introduced by comparing data from module service and baseline schema
121      */
122     private void storeNewColumns(String tableName, List<Column> columnsInBaseline, List<Column> columnsFromModuleSvc) {
123         List<Column> newColumns = columnsFromModuleSvc.stream().filter(columnInGenerated -> !getListOfAllColumns(
124                 columnsInBaseline).contains(columnInGenerated.getName())).toList();
125         identifiedChangesToModels.get(ALTER).add(Table.builder().name(tableName).columns(newColumns.stream().map(
126                 column -> Column.builder().name(column.getName()).dataType(column.getDataType()).postgresConstraints(column
127                         .getPostgresConstraints()).build()).toList()).build());
128         List<Column> columnsWithDefaultValues = newColumns.stream().filter(columnIdentified -> columnIdentified
129                 .getDefaultValue() != null).toList();
130         if (!columnsWithDefaultValues.isEmpty()) {
131             identifiedChangesToModels.get(DEFAULT).add(Table.builder().name(tableName).columns(columnsWithDefaultValues)
132                     .build());
133         }
134     }
135
136     /**
137      * Check if default values for all columns in tables from module service are same as what's in baseline schema Store
138      * identified with a "DEFAULT" key
139      */
140     private void detectAndStoreDefaultValueChanges(String tableName, List<Column> columnsInBaseline,
141             List<Column> columnsFromModuleSvc) {
142         List<Column> list = new ArrayList<>();
143         columnsInBaseline.forEach(columnInBaseline -> {
144             columnsFromModuleSvc.forEach(columnInGenerated -> {
145                 if (columnInGenerated.getName().equals(columnInBaseline.getName()) && !Objects.equals(columnInGenerated
146                         .getDefaultValue(), columnInBaseline.getDefaultValue())) {
147                     list.add(columnInGenerated);
148                 }
149             });
150         });
151         if (!list.isEmpty()) {
152             identifiedChangesToModels.get(DEFAULT).add(Table.builder().name(tableName).columns(list).build());
153         }
154     }
155
156     private List<String> getListOfAllColumns(List<Column> columns) {
157         List<String> allColumns = new ArrayList<>();
158         for (Column col : columns) {
159             allColumns.add(col.getName().replace("\"", ""));
160         }
161         return allColumns;
162     }
163
164 }