diff --git a/product/roundhouse.console/Program.cs b/product/roundhouse.console/Program.cs index 14f833c3..711bfaa2 100644 --- a/product/roundhouse.console/Program.cs +++ b/product/roundhouse.console/Program.cs @@ -7,6 +7,7 @@ using roundhouse.folders; using roundhouse.infrastructure; using roundhouse.infrastructure.app; +using roundhouse.infrastructure.app.tokens; using roundhouse.infrastructure.app.logging; using roundhouse.infrastructure.commandline.options; using roundhouse.infrastructure.containers; @@ -323,6 +324,10 @@ private static void parse_arguments_and_set_up_configuration(ConfigurationProper .Add("rcm=|recoverymode=", "RecoveryMode - This instructs RH to set the database recovery mode to Simple|Full|NoChange. Defaults to NoChange.", option => configuration.RecoveryMode = (RecoveryMode)Enum.Parse(typeof(RecoveryMode), option, true)) + //user tokens + .Add("ut=|usertokens=", + "UserTokens - This is a list of key/value pairs used in scripts: the token '{{SomeToken}}' will be replace by 'value123' if 'ut=SomeToken=value123' is provided. Separate multiple tokens with ;", + option => configuration.UserTokens = UserTokenParser.Parse(option)) //debug .Add("debug", "Debug - This instructs RH to write out all messages. Defaults to false.", diff --git a/product/roundhouse.tasks/Roundhouse.cs b/product/roundhouse.tasks/Roundhouse.cs index c0cf2a20..ab210dac 100644 --- a/product/roundhouse.tasks/Roundhouse.cs +++ b/product/roundhouse.tasks/Roundhouse.cs @@ -1,6 +1,8 @@ -namespace roundhouse.tasks + +namespace roundhouse.tasks { using System; + using System.Collections.Generic; using databases; using folders; using infrastructure.app; @@ -190,6 +192,7 @@ public string RecoveryMode public System.Text.Encoding FileEncoding { get; set; } public bool DisableOutput { get; set; } + public Dictionary UserTokens { get; set; } public bool Initialize { get; set; } public string ConfigurationFile { get; set; } diff --git a/product/roundhouse.tests/TestExtensions.cs b/product/roundhouse.tests/TestExtensions.cs index d9e1f7b4..1bccaac4 100644 --- a/product/roundhouse.tests/TestExtensions.cs +++ b/product/roundhouse.tests/TestExtensions.cs @@ -1,5 +1,7 @@ using System; using NUnit.Framework; +using System.Collections; +using System.Collections.Generic; namespace roundhouse.tests { @@ -29,6 +31,15 @@ public static void should_throw_an(this Action a) where T: Exception { Assert.Throws(() => a()); } + + public static void should_only_contain(this IDictionary dictionary, params KeyValuePair[] values) + { + Assert.That(dictionary, Has.Count.EqualTo(values.Length)); + foreach(var value in values) + { + Assert.That(dictionary, Contains.Item(value)); + } + } } } \ No newline at end of file diff --git a/product/roundhouse.tests/infrastructure.app/tokens/TokenReplacerSpecs.cs b/product/roundhouse.tests/infrastructure.app/tokens/TokenReplacerSpecs.cs index f86f6915..6bdc49eb 100644 --- a/product/roundhouse.tests/infrastructure.app/tokens/TokenReplacerSpecs.cs +++ b/product/roundhouse.tests/infrastructure.app/tokens/TokenReplacerSpecs.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace roundhouse.tests.infrastructure.app.tokens { using consoles; @@ -14,7 +16,15 @@ public abstract class concern_for_TokenReplacer : TinySpec public override void Context() { - configuration = new DefaultConfiguration { DatabaseName = database_name }; + configuration = new DefaultConfiguration + { + DatabaseName = database_name, + UserTokens = new Dictionary() + { + { "UserId", "123" }, + { "UserName", "Some Name" } + } + }; } } @@ -71,6 +81,39 @@ public void if_given_a_value_that_does_not_exist_should_return_the_value_with_or { TokenReplacer.replace_tokens(configuration, "ALTER DATABASE {{DataBase}}").should_be_equal_to("ALTER DATABASE {{DataBase}}"); } + [Observation] + public void if_given_userid_and_username_should_replace_with_the_user_tokens_from_the_configuration() + { + TokenReplacer.replace_tokens(configuration, "SELECT * FROM Users WHERE UserId = {{UserId}} OR UserName = '{{UserName}}'") + .should_be_equal_to("SELECT * FROM Users WHERE UserId = "+configuration.UserTokens["UserId"]+" OR UserName = '"+configuration.UserTokens["UserName"]+"'"); + } } + [Concern(typeof(TokenReplacer))] + public class when_replacing_tokens_in_sql_files_using_user_tokens_from_configuration : TinySpec + { + protected static object result; + protected static ConfigurationPropertyHolder configuration; + + public override void Context() + { + configuration = new DefaultConfiguration + { + UserTokens = new Dictionary() + { + { "UserId", "123" }, + { "UserName", "Some Name" } + } + }; + } + + public override void Because() {} + + [Observation] + public void if_given_bracket_bracket_DatabaseName_bracket_bracket_should_replace_with_the_DatabaseName_from_the_configuration() + { + TokenReplacer.replace_tokens(configuration, "SELECT * FROM Users WHERE UserId = {{UserId}} OR UserName = '{{UserName}}'") + .should_be_equal_to("SELECT * FROM Users WHERE UserId = " + configuration.UserTokens["UserId"] + " OR UserName = '" + configuration.UserTokens["UserName"] + "'"); + } +} } } \ No newline at end of file diff --git a/product/roundhouse.tests/infrastructure.app/tokens/UserTokenParserSpecs.cs b/product/roundhouse.tests/infrastructure.app/tokens/UserTokenParserSpecs.cs new file mode 100644 index 00000000..e7324ae5 --- /dev/null +++ b/product/roundhouse.tests/infrastructure.app/tokens/UserTokenParserSpecs.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using roundhouse.infrastructure.app; + +namespace roundhouse.tests.infrastructure.app.tokens +{ + using roundhouse.infrastructure.app.tokens; + using System.IO; + + public class UserTokenParserSpecs + { + [Concern(typeof(UserTokenParser))] + public class when_parsing_from_text : TinySpec + { + protected static object result; + + public override void Context() + { + } + + public override void Because() + { + } + + [Observation] + public void if_given_keyvalues_should_parse_to_dictionary() + { + var dictionary = UserTokenParser.Parse("UserId=123;UserName=Some Name"); + dictionary.should_be_an_instance_of>(); + dictionary.should_only_contain( + new KeyValuePair("UserId", "123"), + new KeyValuePair("UserName", "Some Name")); + } + + [Observation] + public void if_given_filepath_with_keyvalues_should_parse_to_dictionary() + { + var filename = Path.GetTempFileName() + ".txt"; + File.WriteAllText(filename, "UserId=123" + Environment.NewLine + "UserName=Some Name"); + try + { + var dictionary = UserTokenParser.Parse(filename); + dictionary.should_be_an_instance_of>(); + dictionary.should_only_contain( + new KeyValuePair("UserId", "123"), + new KeyValuePair("UserName", "Some Name")); + } + finally + { + File.Delete(filename); + } + } + [Observation] + public void if_given_wrong_syntax_text_without_equals_sign_should_throw_format_exception() + { + Action action = () => + { + var dictionary = UserTokenParser.Parse("UserId123User&NameSome Name"); + }; + action.should_throw_an(); + } + [Observation] + public void if_given_empty_text_should_throw_argument_null_exception() + { + Action action = () => + { + var dictionary = UserTokenParser.Parse(""); + }; + action.should_throw_an(); + } + } + + } +} \ No newline at end of file diff --git a/product/roundhouse.tests/roundhouse.tests.csproj b/product/roundhouse.tests/roundhouse.tests.csproj index fa0b2aed..05a81406 100644 --- a/product/roundhouse.tests/roundhouse.tests.csproj +++ b/product/roundhouse.tests/roundhouse.tests.csproj @@ -112,6 +112,7 @@ + diff --git a/product/roundhouse/consoles/DefaultConfiguration.cs b/product/roundhouse/consoles/DefaultConfiguration.cs index 84f9046a..0de0ba01 100644 --- a/product/roundhouse/consoles/DefaultConfiguration.cs +++ b/product/roundhouse/consoles/DefaultConfiguration.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace roundhouse.consoles { using System; @@ -65,6 +67,7 @@ public sealed class DefaultConfiguration : ConfigurationPropertyHolder public bool DisableTokenReplacement { get; set; } public bool SearchAllSubdirectoriesInsteadOfTraverse { get; set; } public bool DisableOutput { get; set; } + public Dictionary UserTokens { get; set; } public bool Initialize { get; set; } public string ConfigurationFile { get; set; } public System.Text.Encoding FileEncoding { get; set; } diff --git a/product/roundhouse/infrastructure.app/ConfigurationPropertyHolder.cs b/product/roundhouse/infrastructure.app/ConfigurationPropertyHolder.cs index ad1bcec9..3a813c75 100644 --- a/product/roundhouse/infrastructure.app/ConfigurationPropertyHolder.cs +++ b/product/roundhouse/infrastructure.app/ConfigurationPropertyHolder.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using roundhouse.infrastructure.logging; namespace roundhouse.infrastructure.app @@ -65,6 +66,7 @@ public interface ConfigurationPropertyHolder bool DisableTokenReplacement { get; set; } bool SearchAllSubdirectoriesInsteadOfTraverse { get; set; } bool DisableOutput { get; set; } + Dictionary UserTokens { get; set; } System.Text.Encoding FileEncoding { get; set; } string ConfigurationFile { get; set; } } diff --git a/product/roundhouse/infrastructure.app/UserTokenParser.cs b/product/roundhouse/infrastructure.app/UserTokenParser.cs new file mode 100644 index 00000000..54f66da1 --- /dev/null +++ b/product/roundhouse/infrastructure.app/UserTokenParser.cs @@ -0,0 +1,30 @@ +namespace roundhouse.infrastructure.app.tokens +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + + public class UserTokenParser + { + public static Dictionary Parse(string option) + { + if (String.IsNullOrEmpty(option)) + throw new ArgumentNullException("option"); + + var textToParse = option; + var pairs = textToParse.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); + + if (pairs.Length == 1 && File.Exists(textToParse)) + { + textToParse = File.ReadAllText(textToParse); + pairs = textToParse.Split(new string[] { ";",Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + } + + if (pairs.Any(p => !p.Contains("="))) throw new FormatException("Wrong format"); + + return pairs.ToDictionary(p => p.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[0], + p => p.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[1]); + } + } +} diff --git a/product/roundhouse/infrastructure.app/tokens/TokenReplacer.cs b/product/roundhouse/infrastructure.app/tokens/TokenReplacer.cs index d4ffe708..d62e4baa 100644 --- a/product/roundhouse/infrastructure.app/tokens/TokenReplacer.cs +++ b/product/roundhouse/infrastructure.app/tokens/TokenReplacer.cs @@ -36,7 +36,19 @@ private static IDictionary create_dictionary_from_configuration( Dictionary property_dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var property in configuration.GetType().GetProperties()) { - property_dictionary.Add(property.Name, property.GetValue(configuration, null).to_string()); + if (property.Name == "UserTokens") + { + var user_tokens = property.GetValue(configuration, null) as Dictionary; + if (user_tokens == null) + continue; + + foreach (var user_token in user_tokens) + { + property_dictionary[user_token.Key] = user_token.Value; + } + } + else property_dictionary.Add(property.Name, property.GetValue(configuration, null).to_string()); + } return property_dictionary; diff --git a/product/roundhouse/roundhouse.csproj b/product/roundhouse/roundhouse.csproj index 1609a492..3cee1198 100644 --- a/product/roundhouse/roundhouse.csproj +++ b/product/roundhouse/roundhouse.csproj @@ -135,6 +135,7 @@ +