private Task WriteFunctionsAsync( string outputFolder, IEnumerable <FunctionSchema> functions, CancellationToken cancellationToken) { return(WriteSchemaToFileAsync( Path.Combine(outputFolder, "Functions"), functions.ToArray(), f => f.Name, f => f.Folder, f => CslCommandGenerator.GenerateCreateOrAlterFunctionCommand(f, false), cancellationToken)); }
public void CreateOrAlterFunction(string functionName, IDictionary <string, string> parameters, string body, string databaseName) { try { if (parameters == null) { parameters = new Dictionary <string, string>(); } var command = CslCommandGenerator.GenerateCreateOrAlterFunctionCommand(functionName, null, null, parameters, body); this.client.ExecuteControlCommand(databaseName, command); } catch (Exception e) { throw e; } }
/// <summary> /// Write the function to the file system. /// </summary> /// <param name="functionSchema">The function to write</param> /// <param name="rootFolder">The root folder for all the CSL files</param> /// <returns></returns> public static void WriteToFile(this FunctionSchema functionSchema, string rootFolder) { string filename = functionSchema.Name + ".csl"; // First remove any other files with this name. In the case where you moved an object to a new folder, this will handle cleaning up the old file string[] existingFiles = Directory.GetFiles(rootFolder, filename, SearchOption.AllDirectories); if (existingFiles.Length > 0) { foreach (string file in existingFiles) { try { File.Delete(file); } catch { // It's not the end of the world if this call fails } } } // Now add write the new file to the correct location. string funcFolder = Path.Combine(rootFolder, "Functions"); if (!string.IsNullOrEmpty(functionSchema.Folder)) { string cleanedFolder = string.Join("", functionSchema.Folder.Split(Path.GetInvalidPathChars())); funcFolder = Path.Combine(funcFolder, cleanedFolder); } string destinationFile = Path.Combine(funcFolder, filename); if (!Directory.Exists(funcFolder)) { Directory.CreateDirectory(funcFolder); } File.WriteAllText(destinationFile, CslCommandGenerator.GenerateCreateOrAlterFunctionCommand(functionSchema, true)); }
/// <summary> /// Test out the settings before saving them. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnOk_Click(object sender, EventArgs e) { Cursor lastCursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; SettingsWrapper.TableFieldsOnNewLine = cbTableFieldsOnNewLine.Checked; SettingsWrapper.CreateMergeEnabled = cbCreateMerge.Checked; SettingsWrapper.KustoObjectDropWarning = chkTableDropWarning.Checked; SettingsWrapper.AADAuthority = txtAuthority.Text; // Only check the Kusto settings if they changed if (SettingsWrapper.KustoClusterForTempDatabases != txtKustoCluster.Text || SettingsWrapper.TemporaryKustoDatabase != txtKustoDatabase.Text) { // Allow for multiple ways of specifying a cluster name if (string.IsNullOrEmpty(txtKustoCluster.Text)) { MessageBox.Show($"No Kusto cluster was specified.", "Missing Info", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } string clusterName = QueryEngine.NormalizeClusterName(txtKustoCluster.Text); string databaseName = txtKustoDatabase.Text; if (string.IsNullOrEmpty(databaseName)) { MessageBox.Show($"No Kusto database was specified.", "Missing Info", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // If the required info is present, update the cluster textbox with the modified cluster url txtKustoCluster.Text = clusterName; // Verify connection and permissions by creating and removing a function var connString = new KustoConnectionStringBuilder(clusterName) { FederatedSecurity = true, InitialCatalog = databaseName, Authority = txtAuthority.Text }; var adminClient = KustoClientFactory.CreateCslAdminProvider(connString); try { string functionName = "SyncKustoPermissionsTest" + Guid.NewGuid(); adminClient.ExecuteControlCommand( CslCommandGenerator.GenerateCreateOrAlterFunctionCommand( functionName, "", "", new Dictionary <string, string>(), "{print now()}")); adminClient.ExecuteControlCommand(CslCommandGenerator.GenerateFunctionDropCommand(functionName)); } catch (Exception ex) { if (ex.Message.Contains("403-Forbidden")) { MessageBox.Show($"The current user does not have permission to create a function on cluster('{clusterName}').database('{databaseName}')", "Error Validating Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error); } else if (ex.Message.Contains("failed to resolve the service name")) { MessageBox.Show($"Cluster {clusterName} could not be found.", "Error Validating Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error); } else if (ex.Message.Contains("Kusto client failed to perform authentication")) { MessageBox.Show($"Could not authenticate with AAD. Please verify that the AAD Authority is specified correctly.", "Error Authenticating", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { MessageBox.Show($"Unknown error: {ex.Message}", "Error Validating Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error); } return; } // Verify that the scratch database is empty try { long functionCount = 0; long tableCount = 0; using (var functionReader = adminClient.ExecuteControlCommand(".show functions | count")) { functionReader.Read(); functionCount = functionReader.GetInt64(0); } using (var tableReader = adminClient.ExecuteControlCommand(".show tables | count")) { tableReader.Read(); tableCount = tableReader.GetInt64(0); } if (functionCount != 0 || tableCount != 0) { MessageBox.Show($"Drop all functions and tables in the {txtKustoDatabase.Text} database before specifying this as the temporary database. " + $"This check is performed to reinforce the point that this databse will be wiped every time a comparison is run.", "Error Validating Empty Database", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } catch (Exception ex) { MessageBox.Show($"Unknown error: {ex.Message}", "Error Validating Empty Database", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // Store the settings now that we know they work SettingsWrapper.KustoClusterForTempDatabases = clusterName; SettingsWrapper.TemporaryKustoDatabase = databaseName; } this.Close(); Cursor.Current = lastCursor; }
/// <summary> /// Create a comparison for the node that was clicked on /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void tvComparison_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { string objectName = e.Node.Text; string sourceText = ""; string targetText = ""; if (_sourceSchema.Functions.ContainsKey(objectName) && e.Node.FullPath.StartsWith(_functionTreeNodeText)) { sourceText = CslCommandGenerator.GenerateCreateOrAlterFunctionCommand(_sourceSchema.Functions[objectName], true); } if (_sourceSchema.Tables.ContainsKey(objectName) && e.Node.FullPath.StartsWith(_tablesTreeNodeText)) { sourceText = CslCommandGenerator.GenerateTableCreateCommand(_sourceSchema.Tables[objectName], true); } if (_targetSchema.Functions.ContainsKey(objectName) && e.Node.FullPath.StartsWith(_functionTreeNodeText)) { targetText = CslCommandGenerator.GenerateCreateOrAlterFunctionCommand(_targetSchema.Functions[objectName], true); } if (_targetSchema.Tables.ContainsKey(objectName) && e.Node.FullPath.StartsWith(_tablesTreeNodeText)) { targetText = CslCommandGenerator.GenerateTableCreateCommand(_targetSchema.Tables[objectName], true); } var diffBuilder = new InlineDiffBuilder(new Differ()); DiffPlex.DiffBuilder.Model.DiffPaneModel diff = diffBuilder.BuildDiffModel(targetText, sourceText); rtbSourceText.Clear(); int longestLine = 98; if (diff.Lines.Any()) { longestLine = Math.Max(diff.Lines.Max(l => l.Text.Length), longestLine); } foreach (DiffPlex.DiffBuilder.Model.DiffPiece line in diff.Lines) { switch (line.Type) { case DiffPlex.DiffBuilder.Model.ChangeType.Inserted: rtbSourceText.SelectionBackColor = System.Drawing.Color.Yellow; rtbSourceText.SelectedText = line.Text.PadRight(longestLine); break; case DiffPlex.DiffBuilder.Model.ChangeType.Deleted: rtbSourceText.SelectionBackColor = System.Drawing.Color.Red; rtbSourceText.SelectedText = line.Text.PadRight(longestLine); break; case DiffPlex.DiffBuilder.Model.ChangeType.Imaginary: break; default: rtbSourceText.SelectionBackColor = System.Drawing.Color.White; rtbSourceText.SelectedText = line.Text.PadRight(longestLine); break; } rtbSourceText.SelectedText += "\n"; } }
/// <summary> /// Test out the settings before saving them. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnOk_Click(object sender, EventArgs e) { Cursor lastCursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; // Allow for multiple ways of specifying a cluster name string clusterName = txtKustoCluster.Text; if (string.IsNullOrEmpty(clusterName)) { MessageBox.Show($"No Kusto cluster was specified.", "Missing Info", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } else if (!clusterName.EndsWith(".kusto.windows.net")) { clusterName = $"https://{clusterName}.kusto.windows.net"; } string databaseName = txtKustoDatabase.Text; if (string.IsNullOrEmpty(databaseName)) { MessageBox.Show($"No Kusto database was specified.", "Missing Info", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // If the required info is present, update the cluster textbox with the modified cluster url txtKustoCluster.Text = clusterName; // Verify connection and permissions by creating and removing a function try { var connString = new KustoConnectionStringBuilder(clusterName) { FederatedSecurity = true, InitialCatalog = databaseName, Authority = txtAuthority.Text }; var adminClient = KustoClientFactory.CreateCslAdminProvider(connString); string functionName = "SyncKustoPermissionsTest"; adminClient.ExecuteControlCommand( CslCommandGenerator.GenerateCreateOrAlterFunctionCommand( functionName, "", "", new Dictionary <string, string>(), "{print now()}")); adminClient.ExecuteControlCommand(CslCommandGenerator.GenerateFunctionDropCommand(functionName)); // Store the settings now that we know they work SettingsWrapper.KustoClusterForTempDatabases = clusterName; SettingsWrapper.TemporaryKustoDatabase = databaseName; SettingsWrapper.AADAuthority = txtAuthority.Text; this.Close(); } catch (Exception ex) { if (ex.Message.Contains("403-Forbidden")) { MessageBox.Show($"The current user does not have permission to create a function on cluster('{clusterName}').database('{databaseName}')", "Error Validating Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error); } else if (ex.Message.Contains("failed to resolve the service name")) { MessageBox.Show($"Cluster {clusterName} could not be found.", "Error Validating Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error); } else if (ex.Message.Contains("Kusto client failed to perform authentication")) { MessageBox.Show($"Could not authenticate with AAD. Please verify that the AAD Authority is specified correctly.", "Error Authenticating", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { MessageBox.Show($"Unknown error: {ex.Message}", "Error Validating Permissions", MessageBoxButtons.OK, MessageBoxIcon.Error); } } Cursor.Current = lastCursor; }
/// <summary> /// Write a function to Kusto /// </summary> /// <param name="functionSchema">The function to write</param> /// <param name="kustoQueryEngine">An initialized query engine for issuing the Kusto command</param> public static void WriteToKusto(this FunctionSchema functionSchema, QueryEngine kustoQueryEngine) { kustoQueryEngine.CreateOrAlterFunctionAsync(CslCommandGenerator.GenerateCreateOrAlterFunctionCommand(functionSchema, true), functionSchema.Name).Wait(); }