|
1
|
|
|
<?php |
|
2
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
3
|
|
|
namespace SetBased\Audit\Command; |
|
4
|
|
|
|
|
5
|
|
|
use SetBased\Audit\Columns; |
|
6
|
|
|
use SetBased\Audit\MySql\Command\AuditCommand; |
|
7
|
|
|
use SetBased\Audit\MySql\DataLayer; |
|
8
|
|
|
use SetBased\Stratum\MySql\StaticDataLayer; |
|
9
|
|
|
use SetBased\Stratum\Style\StratumStyle; |
|
10
|
|
|
use Symfony\Component\Console\Formatter\OutputFormatterStyle; |
|
11
|
|
|
use Symfony\Component\Console\Helper\Table; |
|
12
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
|
13
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
|
14
|
|
|
use Symfony\Component\Console\Input\InputOption; |
|
15
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
|
16
|
|
|
|
|
17
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
18
|
|
|
/** |
|
19
|
|
|
* Command for comparing data tables with audit tables. |
|
20
|
|
|
*/ |
|
21
|
|
|
class DiffCommand extends AuditCommand |
|
22
|
|
|
{ |
|
23
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
24
|
|
|
/** |
|
25
|
|
|
* Array with columns for each table. |
|
26
|
|
|
* array [ |
|
27
|
|
|
* table_name [ |
|
28
|
|
|
* column [ |
|
29
|
|
|
* data table type, |
|
30
|
|
|
* audit table type |
|
31
|
|
|
* ], |
|
32
|
|
|
* ... |
|
33
|
|
|
* ] |
|
34
|
|
|
* ] |
|
35
|
|
|
* |
|
36
|
|
|
* @var array[] |
|
37
|
|
|
*/ |
|
38
|
|
|
private $diffColumns; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* If set all tables and columns are shown. |
|
42
|
|
|
* |
|
43
|
|
|
* @var string |
|
44
|
|
|
*/ |
|
45
|
|
|
private $full; |
|
46
|
|
|
|
|
47
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
48
|
|
|
/** |
|
49
|
|
|
* Check full full and return array without new or obsolete columns if full not set. |
|
50
|
|
|
* |
|
51
|
|
|
* @param array[] $columns The metadata of the columns of a table. |
|
52
|
|
|
* |
|
53
|
|
|
* @return array[] |
|
54
|
|
|
*/ |
|
55
|
|
|
private static function removeMatchingColumns($columns) |
|
56
|
|
|
{ |
|
57
|
|
|
$cleaned = []; |
|
58
|
|
|
foreach ($columns as $column) |
|
59
|
|
|
{ |
|
60
|
|
|
if (($column['data_table_type']!=$column['audit_table_type'] && $column['audit_table_type']!=$column['config_type']) || ($column['audit_table_type']!=$column['config_type'] && !empty($column['config_type']))) |
|
61
|
|
|
{ |
|
62
|
|
|
$cleaned[] = $column; |
|
63
|
|
|
} |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
return $cleaned; |
|
67
|
|
|
} |
|
68
|
|
|
|
|
69
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
70
|
|
|
/** |
|
71
|
|
|
* {@inheritdoc} |
|
72
|
|
|
*/ |
|
73
|
|
|
protected function configure() |
|
74
|
|
|
{ |
|
75
|
|
|
$this->setName('diff') |
|
76
|
|
|
->setDescription('Compares data tables and audit tables') |
|
77
|
|
|
->addArgument('config file', InputArgument::OPTIONAL, 'The audit configuration file', 'etc/audit.json') |
|
78
|
|
|
->addOption('full', 'f', InputOption::VALUE_NONE, 'Show all columns'); |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
82
|
|
|
/** |
|
83
|
|
|
* {@inheritdoc} |
|
84
|
|
|
*/ |
|
85
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
|
86
|
|
|
{ |
|
87
|
|
|
$this->io = new StratumStyle($input, $output); |
|
88
|
|
|
|
|
89
|
|
|
// Style for column names with miss matched column types. |
|
90
|
|
|
$style = new OutputFormatterStyle(null, 'red'); |
|
91
|
|
|
$output->getFormatter()->setStyle('mm_column', $style); |
|
92
|
|
|
|
|
93
|
|
|
// Style for column types of columns with miss matched column types. |
|
94
|
|
|
$style = new OutputFormatterStyle('yellow'); |
|
95
|
|
|
$output->getFormatter()->setStyle('mm_type', $style); |
|
96
|
|
|
|
|
97
|
|
|
// Style for obsolete tables. |
|
98
|
|
|
$style = new OutputFormatterStyle('yellow'); |
|
99
|
|
|
$output->getFormatter()->setStyle('obsolete_table', $style); |
|
100
|
|
|
|
|
101
|
|
|
// Style for missing tables. |
|
102
|
|
|
$style = new OutputFormatterStyle('red'); |
|
103
|
|
|
$output->getFormatter()->setStyle('miss_table', $style); |
|
104
|
|
|
|
|
105
|
|
|
$this->configFileName = $input->getArgument('config file'); |
|
106
|
|
|
$this->readConfigFile(); |
|
107
|
|
|
|
|
108
|
|
|
$this->full = $input->getOption('full'); |
|
109
|
|
|
|
|
110
|
|
|
$this->connect($this->config); |
|
111
|
|
|
|
|
112
|
|
|
$this->listOfTables(); |
|
113
|
|
|
|
|
114
|
|
|
$this->getDiff(); |
|
115
|
|
|
$this->printDiff($output); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
119
|
|
|
/** |
|
120
|
|
|
* Add highlighting to columns. |
|
121
|
|
|
* |
|
122
|
|
|
* @param array[] $columns The metadata of the columns. |
|
123
|
|
|
* |
|
124
|
|
|
* @return array[] |
|
125
|
|
|
*/ |
|
126
|
|
|
private function addHighlighting($columns) |
|
127
|
|
|
{ |
|
128
|
|
|
$styledColumns = []; |
|
129
|
|
|
foreach ($columns as $column) |
|
130
|
|
|
{ |
|
131
|
|
|
$styledColumn = $column; |
|
132
|
|
|
// Highlighting for data table column types and audit. |
|
133
|
|
|
if (!empty($column['data_table_type'])) |
|
134
|
|
|
{ |
|
135
|
|
|
if (isset($column['data_table_type']) && !isset($column['audit_table_type'])) |
|
136
|
|
|
{ |
|
137
|
|
|
$styledColumn['column_name'] = sprintf('<mm_column>%s</>', $styledColumn['column_name']); |
|
138
|
|
|
$styledColumn['data_table_type'] = sprintf('<mm_type>%s</>', $styledColumn['data_table_type']); |
|
139
|
|
|
} |
|
140
|
|
|
else if (!isset($column['data_table_type']) && isset($column['audit_table_type'])) |
|
141
|
|
|
{ |
|
142
|
|
|
$styledColumn['audit_table_type'] = sprintf('<mm_type>%s</>', $styledColumn['audit_table_type']); |
|
143
|
|
|
} |
|
144
|
|
View Code Duplication |
else if (strcmp($column['data_table_type'], $column['audit_table_type']) && !isset($configType)) |
|
|
|
|
|
|
145
|
|
|
{ |
|
146
|
|
|
$styledColumn['column_name'] = sprintf('<mm_column>%s</>', $styledColumn['column_name']); |
|
147
|
|
|
$styledColumn['data_table_type'] = sprintf('<mm_type>%s</>', $styledColumn['data_table_type']); |
|
148
|
|
|
$styledColumn['audit_table_type'] = sprintf('<mm_type>%s</>', $styledColumn['audit_table_type']); |
|
149
|
|
|
} |
|
150
|
|
|
} |
|
151
|
|
|
else |
|
152
|
|
|
{ |
|
153
|
|
|
// Highlighting for audit table column types and audit_columns in config file. |
|
154
|
|
|
$searchColumn = StaticDataLayer::searchInRowSet('column_name', $styledColumn['column_name'], $this->config['audit_columns']); |
|
155
|
|
|
if (isset($searchColumn)) |
|
156
|
|
|
{ |
|
157
|
|
|
$configType = $this->config['audit_columns'][$searchColumn]['column_type']; |
|
158
|
|
|
if (isset($configType) && !isset($column['audit_table_type'])) |
|
159
|
|
|
{ |
|
160
|
|
|
$styledColumn['column_name'] = sprintf('<mm_column>%s</>', $styledColumn['column_name']); |
|
161
|
|
|
$styledColumn['config_type'] = sprintf('<mm_type>%s</>', $styledColumn['config_type']); |
|
162
|
|
|
} |
|
163
|
|
|
else if (!isset($configType) && isset($column['audit_table_type'])) |
|
164
|
|
|
{ |
|
165
|
|
|
$styledColumn['audit_table_type'] = sprintf('<mm_type>%s</>', $column['audit_table_type']); |
|
166
|
|
|
} |
|
167
|
|
View Code Duplication |
else if (strcmp($configType, $column['audit_table_type'])) |
|
|
|
|
|
|
168
|
|
|
{ |
|
169
|
|
|
$styledColumn['column_name'] = sprintf('<mm_column>%s</>', $styledColumn['column_name']); |
|
170
|
|
|
$styledColumn['audit_table_type'] = sprintf('<mm_type>%s</>', $column['audit_table_type']); |
|
171
|
|
|
$styledColumn['config_type'] = sprintf('<mm_type>%s</>', $styledColumn['config_type']); |
|
172
|
|
|
} |
|
173
|
|
|
} |
|
174
|
|
|
} |
|
175
|
|
|
$styledColumns[] = $styledColumn; |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
return $styledColumns; |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
182
|
|
|
/** |
|
183
|
|
|
* Get the difference between data and audit tables. |
|
184
|
|
|
* |
|
185
|
|
|
* @param Columns $dataColumns The table columns from data schema. |
|
186
|
|
|
* @param Columns $auditColumns The table columns from audit schema. |
|
187
|
|
|
* |
|
188
|
|
|
* @return array[] |
|
189
|
|
|
*/ |
|
190
|
|
|
private function createDiffArray($dataColumns, $auditColumns) |
|
191
|
|
|
{ |
|
192
|
|
|
$columns = []; |
|
193
|
|
|
|
|
194
|
|
|
foreach ($dataColumns->getColumns() as $column) |
|
195
|
|
|
{ |
|
196
|
|
|
$columns[$column['column_name']] = ['column_name' => $column['column_name'], |
|
197
|
|
|
'data_table_type' => $column['column_type'], |
|
198
|
|
|
'audit_table_type' => null, |
|
199
|
|
|
'config_type' => null]; |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
foreach ($auditColumns->getColumns() as $column) |
|
203
|
|
|
{ |
|
204
|
|
|
$data_table_type = isset($columns[$column['column_name']]) ? $columns[$column['column_name']]['data_table_type'] : null; |
|
205
|
|
|
|
|
206
|
|
|
$columns[$column['column_name']] = ['column_name' => $column['column_name'], |
|
207
|
|
|
'data_table_type' => $data_table_type, |
|
208
|
|
|
'audit_table_type' => $column['column_type'], |
|
209
|
|
|
'config_type' => null]; |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
foreach ($this->config['audit_columns'] as $column) |
|
213
|
|
|
{ |
|
214
|
|
|
$data_table_type = isset($columns[$column['column_name']]) ? $columns[$column['column_name']]['data_table_type'] : null; |
|
215
|
|
|
$audit_table_type = isset($columns[$column['column_name']]) ? $columns[$column['column_name']]['audit_table_type'] : null; |
|
216
|
|
|
|
|
217
|
|
|
$columns[$column['column_name']] = ['column_name' => $column['column_name'], |
|
218
|
|
|
'data_table_type' => $data_table_type, |
|
219
|
|
|
'audit_table_type' => $audit_table_type, |
|
220
|
|
|
'config_type' => $column['column_type']]; |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
return $columns; |
|
224
|
|
|
} |
|
225
|
|
|
|
|
226
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
227
|
|
|
/** |
|
228
|
|
|
* Computes the difference between data and audit tables. |
|
229
|
|
|
*/ |
|
230
|
|
|
private function getDiff() |
|
231
|
|
|
{ |
|
232
|
|
|
foreach ($this->dataSchemaTables as $table) |
|
233
|
|
|
{ |
|
234
|
|
|
if ($this->config['tables'][$table['table_name']]['audit']) |
|
235
|
|
|
{ |
|
236
|
|
|
$res = StaticDataLayer::searchInRowSet('table_name', $table['table_name'], $this->auditSchemaTables); |
|
237
|
|
|
if (isset($res)) |
|
238
|
|
|
{ |
|
239
|
|
|
$dataColumns = new Columns(DataLayer::getTableColumns($this->config['database']['data_schema'], $table['table_name'])); |
|
240
|
|
|
$auditColumns = DataLayer::getTableColumns($this->config['database']['audit_schema'], $table['table_name']); |
|
241
|
|
|
$auditColumns = $this->addNotNull($auditColumns); |
|
242
|
|
|
$auditColumns = new Columns($auditColumns); |
|
243
|
|
|
|
|
244
|
|
|
$this->diffColumns[$table['table_name']] = $this->createDiffArray($dataColumns, $auditColumns); |
|
245
|
|
|
} |
|
246
|
|
|
} |
|
247
|
|
|
} |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
251
|
|
|
/** |
|
252
|
|
|
* Add not null to audit columns if it not nullable. |
|
253
|
|
|
* |
|
254
|
|
|
* @param array $theColumns Audit columns. |
|
255
|
|
|
* |
|
256
|
|
|
* @return array |
|
257
|
|
|
*/ |
|
258
|
|
|
private function addNotNull($theColumns) |
|
259
|
|
|
{ |
|
260
|
|
|
$modifiedColumns = []; |
|
261
|
|
|
foreach ($theColumns as $column) |
|
262
|
|
|
{ |
|
263
|
|
|
$modifiedColumn = $column; |
|
264
|
|
|
$auditColumn = StaticDataLayer::searchInRowSet('column_name', $modifiedColumn['column_name'], $this->config['audit_columns']); |
|
265
|
|
|
if (isset($auditColumn)) |
|
266
|
|
|
{ |
|
267
|
|
|
if ($modifiedColumn['is_nullable']==='NO') |
|
268
|
|
|
{ |
|
269
|
|
|
$modifiedColumn['column_type'] = sprintf('%s not null', $modifiedColumn['column_type']); |
|
270
|
|
|
} |
|
271
|
|
|
} |
|
272
|
|
|
$modifiedColumns[] = $modifiedColumn; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
return $modifiedColumns; |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
279
|
|
|
/** |
|
280
|
|
|
* Writes the difference between the audit tables and metadata tables to the output. |
|
281
|
|
|
* |
|
282
|
|
|
* @param OutputInterface $output The output. |
|
283
|
|
|
*/ |
|
284
|
|
|
private function diffTables($output) |
|
285
|
|
|
{ |
|
286
|
|
|
foreach ($this->config['tables'] as $tableName => $table) |
|
287
|
|
|
{ |
|
288
|
|
|
$res = StaticDataLayer::searchInRowSet('table_name', $tableName, $this->auditSchemaTables); |
|
289
|
|
|
if ($table['audit'] && !isset($res)) |
|
290
|
|
|
{ |
|
291
|
|
|
$output->writeln(sprintf('<miss_table>%s</>', $tableName)); |
|
292
|
|
|
} |
|
293
|
|
|
else if (!$table['audit'] && isset($res)) |
|
294
|
|
|
{ |
|
295
|
|
|
$output->writeln(sprintf('<obsolete_table>%s</>', $tableName)); |
|
296
|
|
|
} |
|
297
|
|
|
} |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
301
|
|
|
/** |
|
302
|
|
|
* Writes the difference between the audit and data tables to the output. |
|
303
|
|
|
* |
|
304
|
|
|
* @param OutputInterface $output The output. |
|
305
|
|
|
*/ |
|
306
|
|
|
private function printDiff($output) |
|
307
|
|
|
{ |
|
308
|
|
|
$first = true; |
|
309
|
|
|
if (isset($this->diffColumns)) |
|
310
|
|
|
{ |
|
311
|
|
|
foreach ($this->diffColumns as $tableName => $columns) |
|
312
|
|
|
{ |
|
313
|
|
|
// Remove matching columns unless the full option is used. |
|
314
|
|
|
if (!$this->full) |
|
315
|
|
|
{ |
|
316
|
|
|
$columns = self::removeMatchingColumns($columns); |
|
317
|
|
|
} |
|
318
|
|
|
|
|
319
|
|
|
if (!empty($columns)) |
|
320
|
|
|
{ |
|
321
|
|
|
// Add an empty line between tables. |
|
322
|
|
|
if ($first) |
|
323
|
|
|
{ |
|
324
|
|
|
$first = false; |
|
325
|
|
|
} |
|
326
|
|
|
else |
|
327
|
|
|
{ |
|
328
|
|
|
$output->writeln(''); |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
// Write table name. |
|
332
|
|
|
$output->writeln($tableName); |
|
333
|
|
|
|
|
334
|
|
|
// Write table with columns. |
|
335
|
|
|
$columns = $this->addHighlighting($columns); |
|
336
|
|
|
$table = new Table($output); |
|
337
|
|
|
$table->setHeaders(['column name', 'data table type', 'audit table type', 'config']) |
|
338
|
|
|
->setRows($columns); |
|
339
|
|
|
$table->render(); |
|
340
|
|
|
} |
|
341
|
|
|
} |
|
342
|
|
|
} |
|
343
|
|
|
$this->diffTables($output); |
|
344
|
|
|
} |
|
345
|
|
|
|
|
346
|
|
|
//-------------------------------------------------------------------------------------------------------------------- |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
|
//---------------------------------------------------------------------------------------------------------------------- |
|
350
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.