1- using Kusto . Language ;
2- using KustoSchemaTools . Model ;
1+ using KustoSchemaTools . Model ;
32using Microsoft . Extensions . Logging ;
43
54namespace KustoSchemaTools . Changes
65{
76 public class DatabaseChanges
87 {
9- public static List < IChange > GenerateChanges ( Database oldState , Database newState , string name , Microsoft . Extensions . Logging . ILogger log )
8+ public static List < IChange > GenerateChanges ( Database oldState , Database newState , string name , ILogger log )
109 {
1110
1211 var result = new List < IChange > ( ) ;
@@ -35,7 +34,67 @@ public static List<IChange> GenerateChanges(Database oldState, Database newState
3534 result . Add ( c ) ;
3635 }
3736 }
37+ if ( oldState == null )
38+ {
39+ oldState = new Database ( ) ;
40+ }
41+
42+ result . AddRange ( GeneratePermissionChanges ( oldState , newState , name , log ) ) ;
43+
44+ result . AddRange ( GenerateDeletions ( oldState , newState . Deletions , log ) ) ;
45+
46+ result . AddRange ( GenerateScriptCompareChanges ( oldState , newState , db => db . Tables , nameof ( newState . Tables ) , log ) ) ;
47+ result . AddRange ( GenerateScriptCompareChanges ( oldState , newState , db => db . MaterializedViews , nameof ( newState . MaterializedViews ) , log ) ) ;
48+ result . AddRange ( GenerateScriptCompareChanges ( oldState , newState , db => db . ContinuousExports , nameof ( newState . MaterializedViews ) , log ) ) ;
49+ result . AddRange ( GenerateScriptCompareChanges ( oldState , newState , db => db . Functions , nameof ( newState . MaterializedViews ) , log ) ) ;
50+ result . AddRange ( GenerateScriptCompareChanges ( oldState , newState , db => db . ExternalTables , nameof ( newState . ExternalTables ) , log ) ) ;
51+
52+ if ( newState . EntityGroups . Any ( ) )
53+ {
54+ List < IChange > changes = GenerateEntityGroupChanges ( oldState , newState , name ) ;
55+ if ( changes . Any ( ) )
56+ {
57+ log . LogInformation ( $ "Detected changes for Entity Groups: { changes . Count } ") ;
58+ result . Add ( new Heading ( "Entity Groups" ) ) ;
59+ result . AddRange ( changes ) ;
60+ }
61+ }
62+
3863
64+ return result ;
65+ }
66+
67+ private static IEnumerable < IChange > GenerateDeletions ( Database oldState , Deletions deletions , ILogger log )
68+ {
69+ var scripts = new List < IChange > ( ) ;
70+ scripts . AddRange ( deletions . Tables . Where ( oldState . Tables . ContainsKey ) . Select ( itm => GenerateDeletionChange ( itm , "table" ) ) ) ;
71+ var colDel = deletions . Columns
72+ . Select ( itm => itm . Split ( '.' ) )
73+ . Select ( itm => new { Table = itm [ 0 ] , Column = itm [ 1 ] } )
74+ . Where ( itm => oldState . Tables . ContainsKey ( itm . Table ) && oldState . Tables [ itm . Table ] . Columns . ContainsKey ( itm . Column ) )
75+ . Select ( itm => GenerateDeletionChange ( $ "{ itm . Table } .{ itm . Column } ", "column" ) )
76+ . ToList ( ) ;
77+ scripts . AddRange ( colDel ) ;
78+ scripts . AddRange ( deletions . Functions . Where ( oldState . Functions . ContainsKey ) . Select ( itm => GenerateDeletionChange ( itm , "function" ) ) ) ;
79+ scripts . AddRange ( deletions . ExternalTables . Where ( oldState . ExternalTables . ContainsKey ) . Select ( itm => GenerateDeletionChange ( itm , "external table" ) ) ) ;
80+ scripts . AddRange ( deletions . MaterializedViews . Where ( oldState . MaterializedViews . ContainsKey ) . Select ( itm => GenerateDeletionChange ( itm , "materialized-view" ) ) ) ;
81+ scripts . AddRange ( deletions . ContinuousExports . Where ( oldState . ContinuousExports . ContainsKey ) . Select ( itm => GenerateDeletionChange ( itm , "continuous-export" ) ) ) ;
82+
83+ if ( scripts . Any ( ) )
84+ {
85+ scripts . Insert ( 0 , new Heading ( "Deletions" ) ) ;
86+ }
87+ return scripts ;
88+ }
89+
90+ public static IChange GenerateDeletionChange ( string entityName , string entityType )
91+ {
92+ return new DeletionChange ( entityName , entityType ) ;
93+ }
94+
95+ private static List < IChange > GeneratePermissionChanges ( Database oldState , Database newState , string name , ILogger log )
96+ {
97+ var result = new List < IChange > ( ) ;
3998 var permissionChanges = new List < IChange >
4099 {
41100 new PermissionChange ( name , "Admins" , oldState . Admins , newState . Admins ) ,
@@ -46,189 +105,66 @@ public static List<IChange> GenerateChanges(Database oldState, Database newState
46105 new PermissionChange ( name , "Ingestors" , oldState . Ingestors , newState . Ingestors ) ,
47106 } . Where ( itm => itm . Scripts . Any ( ) ) . ToList ( ) ;
48107
49- if ( permissionChanges . Any ( ) )
108+ if ( permissionChanges . Any ( ) )
50109 {
51110 log . LogInformation ( $ "Detected { permissionChanges . Count } permission changes") ;
52111 result . Add ( new Heading ( "Permissions" ) ) ;
53- result . AddRange ( permissionChanges ) ;
112+
54113 }
55114
56- if ( newState . Tables . Any ( ) )
57- {
58- var tmp = new List < IChange > ( ) ;
59- var existingTables = oldState ? . Tables ?? new Dictionary < string , Table > ( ) ;
60- log . LogInformation ( $ "Existing tables: { string . Join ( ", " , existingTables . Keys ) } ") ;
61-
62- foreach ( var table in newState . Tables )
63- {
64- if ( existingTables . ContainsKey ( table . Key ) )
65- {
66- var change = new ScriptCompareChange ( table . Key , existingTables [ table . Key ] , table . Value ) ;
67- log . LogInformation ( $ "Table { table . Key } exists, created { change . Scripts . Count } script to apply the diffs") ;
68- tmp . Add ( change ) ;
69- }
70- else if ( table . Value . Columns ? . Count > 0 )
71- {
72- var change = new ScriptCompareChange ( table . Key , null , table . Value ) ;
73- log . LogInformation ( $ "Table { table . Key } doesn't exist, created { change . Scripts . Count } scripts to create the table") ;
74- tmp . Add ( change ) ;
75- }
76- }
77- var changes = tmp . Where ( itm => itm . Scripts . Any ( ) ) . ToList ( ) ;
78- if ( changes . Any ( ) )
79- {
80- log . LogInformation ( $ "Detected changes for Tables: { changes . Count } changes with { changes . SelectMany ( itm => itm . Scripts ) . Count ( ) } scripts") ;
81- result . Add ( new Heading ( "Tables" ) ) ;
82- result . AddRange ( changes ) ;
83- }
84- }
115+ return result ;
116+ }
85117
86- if ( newState . MaterializedViews . Any ( ) )
118+ private static List < IChange > GenerateEntityGroupChanges ( Database oldState , Database newState , string name )
119+ {
120+ var changes = new List < IChange > ( ) ;
121+ var existingEntityGroups = oldState ? . EntityGroups ?? new Dictionary < string , List < Entity > > ( ) ;
122+ foreach ( var group in newState . EntityGroups )
87123 {
88- var tmp = new List < IChange > ( ) ;
89- var existingMaterializedViews = oldState ? . MaterializedViews ?? new Dictionary < string , MaterializedView > ( ) ;
90- log . LogInformation ( $ "Existing materialized views: { string . Join ( ", " , existingMaterializedViews . Keys ) } ") ;
91-
92- foreach ( var view in newState . MaterializedViews )
93- {
94- if ( existingMaterializedViews . ContainsKey ( view . Key ) )
95- {
96- var change = new ScriptCompareChange ( view . Key , existingMaterializedViews [ view . Key ] , view . Value ) ;
97- log . LogInformation ( $ "Materialized view { view . Key } exists, created { change . Scripts . Count } script to apply the diffs") ;
98- tmp . Add ( change ) ;
99- }
100- else
101- {
102- var change = new ScriptCompareChange ( view . Key , null , view . Value ) ;
103- log . LogInformation ( $ "Materialized view { view . Key } doesn't exist, created { change . Scripts . Count } scripts to create the view") ;
104- tmp . Add ( change ) ;
105- }
106- }
107- var changes = tmp . Where ( itm => itm . Scripts . Any ( ) ) . ToList ( ) ;
108- if ( changes . Any ( ) )
124+ var existing = existingEntityGroups . ContainsKey ( group . Key ) ? existingEntityGroups [ group . Key ] : null ;
125+ var change = new EntityGroupChange ( name , group . Key , existing , group . Value ) ;
126+ if ( change . Scripts . Any ( ) )
109127 {
110- log . LogInformation ( $ "Detected changes for Materialized Views: { changes . Count } changes with { changes . SelectMany ( itm => itm . Scripts ) . Count ( ) } scripts") ;
111- result . Add ( new Heading ( "Materialized Views" ) ) ;
112- result . AddRange ( changes ) ;
128+ changes . Add ( change ) ;
113129 }
114130 }
115131
116- if ( newState . ContinuousExports . Any ( ) )
117- {
118- var tmp = new List < IChange > ( ) ;
119- var existingContinuousExports = oldState ? . ContinuousExports ?? new Dictionary < string , ContinuousExport > ( ) ;
120- log . LogInformation ( $ "Existing materialized views: { string . Join ( ", " , existingContinuousExports . Keys ) } ") ;
132+ return changes ;
133+ }
121134
122- foreach ( var view in newState . ContinuousExports )
123- {
124- if ( existingContinuousExports . ContainsKey ( view . Key ) )
125- {
126- var change = new ScriptCompareChange ( view . Key , existingContinuousExports [ view . Key ] , view . Value ) ;
127- log . LogInformation ( $ "Continuous Exports { view . Key } exists, created { change . Scripts . Count } script to apply the diffs") ;
128- tmp . Add ( change ) ;
129- }
130- else
131- {
132- var change = new ScriptCompareChange ( view . Key , null , view . Value ) ;
133- log . LogInformation ( $ "Continuous Exports { view . Key } doesn't exist, created { change . Scripts . Count } scripts to create the view") ;
134- tmp . Add ( change ) ;
135- }
136- }
137- var changes = tmp . Where ( itm => itm . Scripts . Any ( ) ) . ToList ( ) ;
138- if ( changes . Any ( ) )
139- {
140- log . LogInformation ( $ "Detected changes for Continuous Exports: { changes . Count } changes with { changes . SelectMany ( itm => itm . Scripts ) . Count ( ) } scripts") ;
141- result . Add ( new Heading ( "Continuous Exports " ) ) ;
142- result . AddRange ( changes ) ;
143- }
144- }
135+ private static List < IChange > GenerateScriptCompareChanges < T > ( Database oldState , Database newState , Func < Database , Dictionary < string , T > > entitySelector , string entityName , ILogger log ) where T : IKustoBaseEntity
136+ {
137+ var tmp = new List < IChange > ( ) ;
138+ var existing = entitySelector ( oldState ) ?? new Dictionary < string , T > ( ) ;
139+ var newItems = entitySelector ( newState ) ?? new Dictionary < string , T > ( ) ;
145140
146- if ( newState . Functions . Any ( ) )
147- {
148- var tmp = new List < IChange > ( ) ;
149- var existingFunctions = oldState ? . Functions ?? new Dictionary < string , Function > ( ) ;
150- log . LogInformation ( $ "Existing functions: { string . Join ( ", " , existingFunctions . Keys ) } ") ;
151141
152- foreach ( var function in newState . Functions )
153- {
154- if ( existingFunctions . ContainsKey ( function . Key ) )
155- {
156- var existingFunction = existingFunctions [ function . Key ] ;
157- var change = new ScriptCompareChange ( function . Key , existingFunction , function . Value ) ;
158- log . LogInformation ( $ "Function { function . Key } exists, created { change . Scripts . Count } script to apply the diffs") ;
159- tmp . Add ( change ) ;
160- }
161- else
162- {
163- var change = new ScriptCompareChange ( function . Key , null , function . Value ) ;
164- log . LogInformation ( $ "Function { function . Key } doesn't exist, created { change . Scripts . Count } scripts to create the function") ;
165- tmp . Add ( change ) ;
166- }
167- }
168- var changes = tmp . Where ( itm => itm . Scripts . Any ( ) ) . ToList ( ) ;
169- if ( changes . Any ( ) )
170- {
171- log . LogInformation ( $ "Detected changes for Functions: { changes . Count } changes with { changes . SelectMany ( itm => itm . Scripts ) . Count ( ) } scripts") ;
172- result . Add ( new Heading ( "Functions" ) ) ;
173- result . AddRange ( changes ) ;
174- }
175- }
142+ log . LogInformation ( $ "Existing { entityName } : { string . Join ( ", " , existing . Keys ) } ") ;
176143
177- if ( newState . EntityGroups . Any ( ) )
144+ foreach ( var item in newItems )
178145 {
179- var changes = new List < IChange > ( ) ;
180- var existingEntityGroups = oldState ? . EntityGroups ?? new Dictionary < string , List < Entity > > ( ) ;
181- foreach ( var group in newState . EntityGroups )
146+ if ( existing . ContainsKey ( item . Key ) )
182147 {
183- var existing = existingEntityGroups . ContainsKey ( group . Key ) ? existingEntityGroups [ group . Key ] : null ;
184- var change = new EntityGroupChange ( name , group . Key , existing , group . Value ) ;
185- if ( change . Scripts . Any ( ) )
186- {
187- changes . Add ( change ) ;
188- }
148+ var change = new ScriptCompareChange ( item . Key , existing [ item . Key ] , item . Value ) ;
149+ log . LogInformation ( $ "{ item . Key } already exists, created { change . Scripts . Count } script to apply the diffs") ;
150+ tmp . Add ( change ) ;
189151 }
190- if ( changes . Any ( ) )
152+ else
191153 {
192- log . LogInformation ( $ "Detected changes for Entity Groups: { changes . Count } " ) ;
193- result . Add ( new Heading ( "Entity Groups" ) ) ;
194- result . AddRange ( changes ) ;
154+ var change = new ScriptCompareChange ( item . Key , null , item . Value ) ;
155+ log . LogInformation ( $ " { item . Key } doesn't exist, created { change . Scripts . Count } scripts to create it." ) ;
156+ tmp . Add ( change ) ;
195157 }
196158 }
197159
198- if ( newState . ExternalTables . Any ( ) )
199- {
200- var tmp = new List < IChange > ( ) ;
201- var existingExternalTable = oldState ? . ExternalTables ?? new Dictionary < string , ExternalTable > ( ) ;
202- log . LogInformation ( $ "Existing functions: { string . Join ( ", " , existingExternalTable . Keys ) } ") ;
203-
204- foreach ( var extTable in newState . ExternalTables )
205- {
206- if ( existingExternalTable . ContainsKey ( extTable . Key ) )
207- {
208- var existingFunction = existingExternalTable [ extTable . Key ] ;
209- var change = new ScriptCompareChange ( extTable . Key , existingFunction , extTable . Value ) ;
210- log . LogInformation ( $ "Function { extTable . Key } exists, created { change . Scripts . Count } script to apply the diffs") ;
211- tmp . Add ( change ) ;
212- }
213- else
214- {
215- var change = new ScriptCompareChange ( extTable . Key , null , extTable . Value ) ;
216- log . LogInformation ( $ "Function { extTable . Key } doesn't exist, created { change . Scripts . Count } scripts to create the function") ;
217- tmp . Add ( change ) ;
218- }
219- }
220- var changes = tmp . Where ( itm => itm . Scripts . Any ( ) ) . ToList ( ) ;
221- if ( changes . Any ( ) )
222- {
223- log . LogInformation ( $ "Detected changes for Functions: { changes . Count } changes with { changes . SelectMany ( itm => itm . Scripts ) . Count ( ) } scripts") ;
224- result . Add ( new Heading ( "Functions" ) ) ;
225- result . AddRange ( changes ) ;
226- }
160+ tmp = tmp . Where ( itm => itm . Scripts ? . Any ( ) == true ) . ToList ( ) ;
227161
162+ if ( tmp . Count > 0 )
163+ {
164+ tmp . Insert ( 0 , new Heading ( entityName ) ) ;
228165 }
229166
230-
231- return result ;
167+ return tmp ;
232168 }
233169 }
234170
0 commit comments