diff --git a/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionEditor.cs b/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionEditor.cs index 971b4bf31a..294f2d8f9c 100644 --- a/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionEditor.cs +++ b/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionEditor.cs @@ -21,6 +21,7 @@ limitations under the License. using Amdocs.Ginger.Plugin.Core; using Ginger.Actions; using Ginger.UserControlsLib.TextEditor.Common; +using GingerCore; using GingerCore.Variables; using ICSharpCode.AvalonEdit.CodeCompletion; using ICSharpCode.AvalonEdit.Highlighting; @@ -193,6 +194,15 @@ private Page GetPageFor(SelectedContentArgs SelectedContentArgs) } } + if(txt.StartsWith("{MockDataExp Fun=")) + { + Mockdata expParams = GingerCore.ValueExpression.GetMockDataDatasetsFunction(txt); + if (expParams != null) + { + p = new ValueExpressionMockDataEditorPage(mContext, SelectedContentArgs,expParams.MockDataDatasets, expParams.Function, expParams.Locale, expParams.MockExpression); + } + } + if (txt.StartsWith("{EnvURL App=")) { //TODO: impl get page for Env @@ -221,17 +231,28 @@ private Page GetPageFor(SelectedContentArgs SelectedContentArgs) public override void UpdateSelectedContent() { - if (p.GetType() == typeof(ValueExpressionVariableEditorPage)) - { - ((ValueExpressionVariableEditorPage)p).UpdateContent(); - } - else if (p.GetType() == typeof(ActDataSourcePage)) + try { - ((ActDataSourcePage)p).UpdateContent(); + if (p.GetType() == typeof(ValueExpressionVariableEditorPage)) + { + ((ValueExpressionVariableEditorPage)p).UpdateContent(); + } + else if (p.GetType() == typeof(ActDataSourcePage)) + { + ((ActDataSourcePage)p).UpdateContent(); + } + else if (p.GetType() == typeof(ValueExpressionFlowDetailsEditorPage)) + { + ((ValueExpressionFlowDetailsEditorPage)p).UpdateContent(); + } + else if (p.GetType() == typeof(ValueExpressionMockDataEditorPage)) + { + ((ValueExpressionMockDataEditorPage)p).UpdateContent(); + } } - else if (p.GetType() == typeof(ValueExpressionFlowDetailsEditorPage)) + catch (Exception ex) { - ((ValueExpressionFlowDetailsEditorPage)p).UpdateContent(); + Reporter.ToLog(eLogLevel.ERROR, "Failed to update the selected content", ex); } } diff --git a/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionMockDataEditorPage.xaml b/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionMockDataEditorPage.xaml new file mode 100644 index 0000000000..88e4e659a0 --- /dev/null +++ b/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionMockDataEditorPage.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionMockDataEditorPage.xaml.cs b/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionMockDataEditorPage.xaml.cs new file mode 100644 index 0000000000..abff7f2d6e --- /dev/null +++ b/Ginger/Ginger/UserControlsLib/TextEditor/ValueExpression/ValueExpressionMockDataEditorPage.xaml.cs @@ -0,0 +1,300 @@ +#region License +/* +Copyright © 2014-2024 European Support Limited + +Licensed under the Apache License, Version 2.0 (the "License") +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#endregion + +using Amdocs.Ginger.Common; +using Bogus; +using Ginger.UserControlsLib.TextEditor.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Windows.Controls; +using GingerCore; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Ginger.UserControlsLib.TextEditor.ValueExpression +{ + /// + /// Interaction logic for ValueExpressionMockDataEditorPage.xaml + /// + public partial class ValueExpressionMockDataEditorPage : Page + { + SelectedContentArgs mSelectedContentArgs; + Context mContext; + string mObj; + List Localelst; + List objClasses; + public ValueExpressionMockDataEditorPage(Context context, SelectedContentArgs SelectedContentArgs, string obj, string function, string Locale, string MockExpression) + { + try + { + InitializeComponent(); + mContext = context; + mSelectedContentArgs = SelectedContentArgs; + mObj = obj; + int datasetStartindex = MockExpression.IndexOf('='); + int datasetendindex = MockExpression.IndexOf('('); + int localestartindex = MockExpression.IndexOf('"'); + int localeendindex = MockExpression.IndexOf(')'); + int functionstartindex = MockExpression.IndexOf('.'); + + Localelst = Localelst ?? GetLocales(); + Type objType; + List methodList; + + Assembly assembly = Assembly.Load("Bogus"); // or load your target assembly + + string namespaceName = "Bogus.DataSets"; + + objClasses = objClasses ?? GetObjectClasses(assembly, namespaceName); + + string objTypeName = mObj.Equals("Randomizer") ? $"Bogus.{mObj}" : $"Bogus.DataSets.{mObj}"; + objType = assembly.GetType(objTypeName); + + methodList = GetMethodsOfType(objType); + int CaretPossition = SelectedContentArgs.GetCaretPosition(); + if ((datasetStartindex < CaretPossition) && (CaretPossition < datasetendindex)) + { + FunctionsList.ItemsSource = objClasses.OrderBy(x => x).ToList(); + } + else if ((localestartindex < CaretPossition) && (CaretPossition < localeendindex) && (!mObj.Equals("Randomizer") || !mObj.Equals("Finance"))) + { + FunctionsList.ItemsSource = Localelst.OrderBy(x => x).ToList(); + } + else + { + FunctionsList.ItemsSource = methodList.OrderBy(x => x).ToList(); + } + } + catch (Exception ex) + { + Reporter.ToLog(eLogLevel.ERROR, "Failed to load ValueExpressionMockDataEditorPage", ex); + } + } + + + private static List GetLocales() + { + return Bogus.Database.GetAllLocales().ToList(); + } + + private static List GetObjectClasses(Assembly assembly, string namespaceName) + { + Type[] types = assembly.GetTypes(); + List objclass = types.Where(t => t.Namespace == namespaceName && typeof(DataSet).IsAssignableFrom(t) && t != typeof(DataSet) && t.FullName.Contains(namespaceName)) + .Select(x => x.Name) + .ToList(); + objclass.Add("Randomizer"); + return objclass; + } + + private static List GetMethodsOfType(Type objType) + { + List methodList = new List(); + + if (objType != null) + { + MethodInfo[] methods = objType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + + if (methods != null) + { + var excludedMethods = GetExcludedMethods(objType.ToString()); + var groupedMethods = methods.GroupBy(m => m.Name); + foreach (var methodGroup in groupedMethods) + { + foreach (MethodInfo method in methodGroup) + { + if (excludedMethods.Contains(method.Name)) + { + continue; + } + string parameters = string.Join(", ", method.GetParameters().Select(p => FormatParameter(p))); + + string functionString = $"{method.Name}({parameters})"; + methodList.Add(functionString); + } + } + } + } + + return methodList; + } + + private static HashSet GetExcludedMethods(string objType) + { + return objType switch + { + "Bogus.Randomizer" => new HashSet { "EnumValues", "WeightedRandom", "ArrayElement", "ArrayElements", "ListItem", "ListItems", "ReplaceSymbols" }, + "Bogus.DataSets.Date" => new HashSet { "BetweenTimeOnly" }, + _ => new HashSet() + }; + } + + private static string FormatParameter(ParameterInfo parameter) + { + string paramType = parameter.ParameterType.Name; + string paramName = parameter.Name; + bool hasDefaultValue = parameter.HasDefaultValue; + object defaultValue = hasDefaultValue ? $"{FormatDefaultValue(parameter.DefaultValue)}" : SetDefaultValue(parameter); + return $"{defaultValue}"; + + } + + private static object SetDefaultValue(ParameterInfo parameter) + { + Type parameterType = parameter.ParameterType; + + return parameterType switch + { + Type type when type == typeof(DateTime) && parameter.Name.Equals("start") => "Past(1)", + Type type when type == typeof(DateTime) && parameter.Name.Equals("end") => "Future(1)", + + Type type when type == typeof(DateOnly) && parameter.Name.Equals("start") => "PastDateOnly(1)", + Type type when type == typeof(DateOnly) && parameter.Name.Equals("end") => "FutureDateOnly(1)", + + Type type when type == typeof(DateTimeOffset) && parameter.Name.Equals("start") => "PastOffset(1)", + Type type when type == typeof(DateTimeOffset) && parameter.Name.Equals("end") => "FutureOffset(1)", + + Type type when type == typeof(Array) => "[1,2,3,4]", + + Type type when type == typeof(List) => "[1,2,3,4,5]", + + Type type when type == typeof(string) => parameter.Name switch + { + "format" => "'12#34'", + "symbol" => "'#'", + _ => "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" + }, + + _ => GetDefaultForType(parameterType) + }; + } + + public static T[] CreateArrayWithDefaultValues(int length, T defaultValue) + { + T[] array = new T[length]; + for (int i = 0; i < length; i++) + { + array[i] = defaultValue; + } + return array; + } + + private static object GetDefaultForType(Type type) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + + private static string FormatDefaultValue(object defaultValue) + { + switch (defaultValue) + { + case null: + return "null"; + case string s: + return $"\"{s}\""; + case char c: + return $"'{c}'"; + case bool b: + return b.ToString().ToLower(); + default: + return defaultValue.ToString(); + } + } + public void UpdateContent() + { + try + { + string selectedItem = (string)FunctionsList.SelectedItem; + string initialText = mSelectedContentArgs.TextEditor.Text.Substring(0, mSelectedContentArgs.StartPos); + + // Parsing the text editor content + string objStr, functions, Locale; + Mockdata expParams = GingerCore.ValueExpression.GetMockDataDatasetsFunction(mSelectedContentArgs.TextEditor.Text); + + string editorText = mSelectedContentArgs.TextEditor.Text; + + int caretPosition = mSelectedContentArgs.GetCaretPosition(); + string resultText = BuildResultText(selectedItem, expParams.MockDataDatasets, expParams.Locale, expParams.Function, caretPosition, expParams.DatasetStartIndex, expParams.DatasetEndIndex, expParams.LocaleStartIndex, expParams.LocaleEndIndex); + + // Append the remaining text and update the text editor content + resultText += editorText.Substring(mSelectedContentArgs.EndPos + 1); + mSelectedContentArgs.TextEditor.Text = resultText; + } + catch(Exception ex) + { + Reporter.ToLog(eLogLevel.ERROR, "Failed to update content in ValueExpressionMockDataEditorPage", ex); + } + } + + static string ExtractSubstring(string text, int startIndex, int length) + { + return text.Substring(startIndex, length); + } + + static string BuildResultText(string selectedItem, string objStr, string locale, string functions, int caretPosition, int datasetStartIndex, int datasetEndIndex, int localeStartIndex, int localeEndIndex) + { + string resultText = string.Empty; + bool isWithinDataset = datasetStartIndex < caretPosition && caretPosition < datasetEndIndex; + bool isWithinLocale = localeStartIndex < caretPosition && caretPosition < localeEndIndex; + + if (objStr.Equals("Randomizer") || objStr.Equals("Finance")) + { + resultText += isWithinDataset + ? GenerateResultText(selectedItem, "Bogus", isWithinDataset) + : "{MockDataExp Fun=" + objStr + "()." + selectedItem + ";}"; + } + else + { + if (isWithinDataset) + { + resultText += GenerateResultText(selectedItem, "Bogus", isWithinDataset, locale); + } + else if (isWithinLocale) + { + resultText += "{MockDataExp Fun=" + objStr + "(@\"" + selectedItem + "\")." + functions + "();}"; + } + else + { + resultText += "{MockDataExp Fun=" + objStr + "(@\"" + locale + "\")." + selectedItem + ";}"; + } + } + + return resultText; + } + + private static string GenerateResultText(string selectedItem, string baseNamespace, bool isWithinDataset, string locale = null) + { + Assembly assembly = Assembly.Load("Bogus"); + string objTypeName = selectedItem.Equals("Randomizer") ? $"{baseNamespace}.{selectedItem}" : $"{baseNamespace}.DataSets.{selectedItem}"; + Type objType = assembly.GetType(objTypeName); + + List methodList = GetMethodsOfType(objType); + string methodCall = methodList.FirstOrDefault(); + + if (locale == null) + { + return "{MockDataExp Fun=" + selectedItem + "()." + methodCall + ";}"; + } + else + { + return "{MockDataExp Fun=" + selectedItem + "(@\"" + locale + "\")." + methodCall + ";}"; + } + } + } +} diff --git a/Ginger/GingerCoreNET/RosLynLib/CodeProcessor.cs b/Ginger/GingerCoreNET/RosLynLib/CodeProcessor.cs index 78bc229c8f..71c514aa7c 100644 --- a/Ginger/GingerCoreNET/RosLynLib/CodeProcessor.cs +++ b/Ginger/GingerCoreNET/RosLynLib/CodeProcessor.cs @@ -343,7 +343,14 @@ public static string GetBogusExpressionEvaluteResult(string expression, out stri /// Handles scenarios with special case like Between function have inbuilt function as parameter and constructs expressions accordingly if (expressionlist[1].Contains("Past") && expressionlist[1].Contains("Future")) { - expressionlist[1] = expressionlist[1].Replace(Parameter[0], $"Result.{Parameter[0]}").Replace(Parameter[1], $"Result.{Parameter[1]}"); + if (expressionlist[1].Contains("BetweenTimeOnly")) + { + expressionlist[1] = expressionlist[1].Replace(Parameter[0], $"TimeOnly.FromDateTime(Result.{Parameter[0]})").Replace(Parameter[1], $"TimeOnly.FromDateTime(Result.{Parameter[1]})"); + } + else + { + expressionlist[1] = expressionlist[1].Replace(Parameter[0], $"Result.{Parameter[0]}").Replace(Parameter[1], $"Result.{Parameter[1]}"); + } } expression = $"var Result = new Bogus.DataSets.{expressionlist[0]}; return Result.{expressionlist[1]}"; } @@ -381,7 +388,7 @@ public static string GetBogusExpressionEvaluteResult(string expression, out stri } catch (Exception e) { - Reporter.ToLog(eLogLevel.DEBUG, expression + System.Environment.NewLine + " not a valid Bogus data generate expression to evaluate", e); + Reporter.ToLog(eLogLevel.ERROR, expression + System.Environment.NewLine + " not a valid Bogus data generate expression to evaluate", e); } return evalresult; } diff --git a/Ginger/GingerCoreNET/ValueExpressionLib/ValueExpression.cs b/Ginger/GingerCoreNET/ValueExpressionLib/ValueExpression.cs index 38d59706b4..e285e7aaa9 100644 --- a/Ginger/GingerCoreNET/ValueExpressionLib/ValueExpression.cs +++ b/Ginger/GingerCoreNET/ValueExpressionLib/ValueExpression.cs @@ -347,7 +347,6 @@ public enum eFlowDetailsObjects { Environment, Runset, Runner, BusinessFlow, ActivitiesGroup, Activity, Action, PreviousBusinessFlow, PreviousActivity, PreviousAction, LastFailedAction, ErrorHandlerOriginActivitiesGroup, ErrorHandlerOriginActivity, ErrorHandlerOriginAction, LastFailedBusinessFlow, LastFailedActivity, Solution } - public static Tuple GetFlowDetailsParams(string flowDetailsExpression) { try @@ -375,6 +374,60 @@ public static Tuple GetFlowDetailsParams(string flo } } + public static Mockdata GetMockDataDatasetsFunction(string MockDataExpression) + { + try + { + string objStr, functions, Locale; + int DatasetStartIndex, DatasetEndIndex, LocaleStartIndex, LocaleEndIndex; + MockdataExpressionExtract(MockDataExpression, out objStr, out functions, out Locale,out DatasetStartIndex,out DatasetEndIndex,out LocaleStartIndex,out LocaleEndIndex); + Mockdata mockdata = new(); + mockdata.MockDataDatasets = objStr; + mockdata.Locale = Locale; + mockdata.Function = functions; + mockdata.MockExpression = MockDataExpression; + mockdata.DatasetStartIndex = DatasetStartIndex; + mockdata.DatasetEndIndex = DatasetEndIndex; + mockdata.LocaleStartIndex = LocaleStartIndex; + mockdata.LocaleEndIndex = LocaleEndIndex; + return mockdata; + } + catch (Exception ex) + { + Reporter.ToLog(eLogLevel.ERROR, string.Format("Failed to evaluate flow details expression '{0}' due to wrong format", MockDataExpression)); + return null; + } + } + /// + /// This method extracts specific parts from the MockDataExpression string. + /// It initializes several string variables and then uses indices to parse substrings, + /// assigning these substrings to the initialized variables. + /// + /// + /// + /// + /// + /// Index of '=' in MockDataExpression. + /// Index of '(' in MockDataExpression. + /// Index of '"' in MockDataExpression. + /// Index of ')' in MockDataExpression. + private static void MockdataExpressionExtract(string MockDataExpression, out string objStr, out string functions, out string Locale, out int DatasetStartIndex, out int DatasetEndIndex, out int LocaleStartIndex, out int LocaleEndIndex) + { + objStr = string.Empty; + functions = string.Empty; + Locale = string.Empty; + DatasetStartIndex = MockDataExpression.IndexOf('='); + DatasetEndIndex = MockDataExpression.IndexOf('('); + LocaleStartIndex = MockDataExpression.IndexOf('"'); + LocaleEndIndex = MockDataExpression.IndexOf(')'); + int functionstartindex = MockDataExpression.IndexOf('.'); + string funsubstring = MockDataExpression.Substring(functionstartindex + 1); + int functionendindex = funsubstring.IndexOf('('); + Locale = MockDataExpression.Substring(LocaleStartIndex + 1, LocaleEndIndex - 2 - LocaleStartIndex); + objStr = MockDataExpression.Substring(DatasetStartIndex + 1, DatasetEndIndex - 1 - DatasetStartIndex); + functions = funsubstring.Substring(0, functionendindex).Replace("\"", "").Trim(); + } + private void ReplaceFlowDetails(string flowDetailsExpression) { eFlowDetailsObjects obj; @@ -1804,4 +1857,26 @@ public static string Calculate(ProjEnvironment ProjEnvironment, BusinessFlow Bus return VE.ValueCalculated; } } + + public class Mockdata + { + public string MockDataDatasets { get; set; } + + public string Locale { get; set; } + + public string Function { get; set; } + + public string MockExpression { get; set; } + + public int DatasetStartIndex { get; set; } + + public int DatasetEndIndex { get; set; } + + public int LocaleStartIndex { get; set; } + + public int LocaleEndIndex { get; set; } + + public int FunctionStartIndex { get; set; } + + } }