diff --git a/code/DeltaKustoLib/CommandModel/CodeHelper.cs b/code/DeltaKustoLib/CommandModel/CodeHelper.cs index deb23e0..f0361ba 100644 --- a/code/DeltaKustoLib/CommandModel/CodeHelper.cs +++ b/code/DeltaKustoLib/CommandModel/CodeHelper.cs @@ -10,6 +10,7 @@ namespace DeltaKustoLib.CommandModel { internal static class CodeHelper { + #region Descendants public static IEnumerable GetAtLeastOneDescendant( this SyntaxElement parent, string descendantNameForExceptionMessage, @@ -95,6 +96,7 @@ public static IReadOnlyList GetImmediateDescendants( return descendants; } + #endregion #region Extract Children public static (C1, C2) ExtractChildren( diff --git a/code/DeltaKustoLib/CommandModel/CommandBase.cs b/code/DeltaKustoLib/CommandModel/CommandBase.cs index 95c4258..e94c8f0 100644 --- a/code/DeltaKustoLib/CommandModel/CommandBase.cs +++ b/code/DeltaKustoLib/CommandModel/CommandBase.cs @@ -189,6 +189,8 @@ public override int GetHashCode() case "DeleteTablePolicyStreamingIngestion": case "DeleteDatabasePolicyStreamingIngestion": return DeleteStreamingIngestionPolicyCommand.FromCode(commandBlock); + case "AlterTablePolicyRowLevelSecurity": + return AlterRowLevelSecurityPolicyCommand.FromCode(commandBlock); #endregion default: diff --git a/code/DeltaKustoLib/CommandModel/EntityName.cs b/code/DeltaKustoLib/CommandModel/EntityName.cs index a89ae08..2732683 100644 --- a/code/DeltaKustoLib/CommandModel/EntityName.cs +++ b/code/DeltaKustoLib/CommandModel/EntityName.cs @@ -41,7 +41,7 @@ public EntityName(string name) } else { - throw new DeltaException($"Unsuppored character for an entity: '{c}'"); + throw new DeltaException($"Unsupported character for an entity: '{c}'"); } } } diff --git a/code/DeltaKustoLib/CommandModel/Policies/AlterRowLevelSecurityPolicyCommand.cs b/code/DeltaKustoLib/CommandModel/Policies/AlterRowLevelSecurityPolicyCommand.cs index 0109297..7b618bb 100644 --- a/code/DeltaKustoLib/CommandModel/Policies/AlterRowLevelSecurityPolicyCommand.cs +++ b/code/DeltaKustoLib/CommandModel/Policies/AlterRowLevelSecurityPolicyCommand.cs @@ -17,7 +17,7 @@ public class AlterRowLevelSecurityPolicyCommand : TableOnlyPolicyCommandBase { public bool IsEnabled { get; } - public string Query { get; } + public QuotedText Query { get; } public override string CommandFriendlyName => ".alter policy row_level_security"; @@ -26,7 +26,7 @@ public class AlterRowLevelSecurityPolicyCommand : TableOnlyPolicyCommandBase public AlterRowLevelSecurityPolicyCommand( EntityName tableName, bool isEnabled, - string query) + QuotedText query) : base(tableName) { IsEnabled = isEnabled; @@ -36,19 +36,28 @@ public AlterRowLevelSecurityPolicyCommand( internal static CommandBase FromCode(SyntaxElement rootElement) { var tableName = rootElement.GetDescendants().Last(); - var policyText = QuotedText.FromLiteral( - rootElement.GetUniqueDescendant( - "AutoDeletePolicy", - e => e.NameInParent == "AutoDeletePolicy")); - var policy = Deserialize(policyText.Text); + var isEnabled = rootElement + .GetDescendants(e => e.Kind == SyntaxKind.IdentifierToken) + .Select(e => e.ToString().Trim()) + .Where(t => t == "enable" || t == "disable") + .Select(t => new bool?(t == "enable")) + .LastOrDefault(); - if (policy == null) + if (isEnabled == null) { throw new DeltaException( - $"Can't extract policy objects from {policyText.ToScript()}"); + "No 'enable' or 'disable' token found in row level security command"); } - return new AlterAutoDeletePolicyCommand(EntityName.FromCode(tableName.Name), policy); + var query = QuotedText.FromLiteral( + rootElement.GetUniqueDescendant( + "Row Level Security", + e => e.NameInParent == "Query")); + + return new AlterRowLevelSecurityPolicyCommand( + EntityName.FromCode(tableName.Name), + isEnabled.Value, + query); } public override string ToScript(ScriptingContext? context) @@ -56,13 +65,11 @@ public override string ToScript(ScriptingContext? context) var builder = new StringBuilder(); builder.Append(".alter table "); - builder.Append(TableName); - builder.Append(" policy auto_delete"); - builder.AppendLine(); - builder.Append("```"); - builder.Append(SerializePolicy()); - builder.AppendLine(); - builder.Append("```"); + builder.Append(TableName.ToScript()); + builder.Append(" policy row_level_security "); + builder.Append(IsEnabled ? "enable" : "disable"); + builder.Append(" "); + builder.AppendLine(Query.ToScript()); return builder.ToString(); } diff --git a/code/DeltaKustoUnitTest/CommandParsing/Policies/AlterRowLevelSecurityPolicyTest.cs b/code/DeltaKustoUnitTest/CommandParsing/Policies/AlterRowLevelSecurityPolicyTest.cs new file mode 100644 index 0000000..a07aafe --- /dev/null +++ b/code/DeltaKustoUnitTest/CommandParsing/Policies/AlterRowLevelSecurityPolicyTest.cs @@ -0,0 +1,61 @@ +using DeltaKustoLib.CommandModel; +using DeltaKustoLib.CommandModel.Policies; +using System; +using System.Linq; +using System.Text.Json; +using Xunit; + +namespace DeltaKustoUnitTest.CommandParsing.Policies +{ + public class AlterRowLevelSecurityPolicyTest : ParsingTestBase + { + [Fact] + public void SimpleTable() + { + TestRowLevelPolicy("A", "MyFunction"); + } + + [Fact] + public void FunkyTable() + { + TestRowLevelPolicy("['A- 1']", "MyTable | where TenantId == 42"); + } + + // Currently not supported by the parser + //[Fact] + //public void DbComposedTableName() + //{ + // TestRowLevelPolicy("mydb.mytable", "MyFunction"); + //} + + // Currently not supported by the parser + //[Fact] + //public void ClusterComposedTableName() + //{ + // TestRowLevelPolicy("mycluster.['my db'].mytable", "MyFunction"); + //} + + private void TestRowLevelPolicy(string tableName, string query) + { + TestRowLevelPolicy(tableName, true, query); + TestRowLevelPolicy(tableName, false, query); + } + + private void TestRowLevelPolicy(string tableName, bool isEnabled, string query) + { + var realTableName = tableName.Split('.').Last(); + var enableToken = isEnabled ? "enable" : "disable"; + var commandText = $@" +.alter table {tableName} policy row_level_security {enableToken} ""{query}"" +"; + var command = ParseOneCommand(commandText); + + Assert.IsType(command); + + var realCommand = (AlterRowLevelSecurityPolicyCommand)command; + + Assert.Equal(isEnabled, realCommand.IsEnabled); + Assert.Equal(query, realCommand.Query.Text); + } + } +} \ No newline at end of file