From 19dcf5da1bde359be2aabe20572cabae8a56e52e Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Tue, 23 Jul 2024 17:58:25 -0400 Subject: [PATCH 01/18] properly set GlobalIdentifier for newly-created instance --- mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs index 6495436..08c4381 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs @@ -87,6 +87,7 @@ public class OmsInstanceList : OmsInstanceList, IList where T : OmsClass { Guid localClassGuid = OmsDatabase.GetGlobalIdentifierForClass(item.GetType()); Guid localInstanceGuid = Guid.NewGuid(); + item.GlobalIdentifier = localInstanceGuid; omsdb.CreateClass(item, localClassGuid, localInstanceGuid); } From 601caf16516cbe55d8e02854edce445c0b796605 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Tue, 17 Dec 2024 00:49:08 -0500 Subject: [PATCH 02/18] update mocha-common --- mocha-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocha-common b/mocha-common index 65c6b69..54d5ab8 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit 65c6b698a3dd1215ccd51de45ea3349f231bbf70 +Subproject commit 54d5ab8b2c37bb9139eb6800631eece9b60ee25a From 3a3e681a90da46ae9eeedc6981a2d572102cdcd0 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Tue, 17 Dec 2024 00:49:22 -0500 Subject: [PATCH 03/18] test ALL the things, all green lights now --- .../src/app/Mocha.Oms.Server/Program.cs | 13 + .../Commands/ElementCommand.cs | 107 +------ .../Commands/InstanceCommand.cs | 2 +- .../src/lib/Mocha.Core.UI.Server/OmsServer.cs | 3 +- .../src/lib/Mocha.Core/KnownInstanceGuids.cs | 7 + ...onalSelectAttributeMethodImplementation.cs | 28 +- ...teBooleanExpressionMethodImplementation.cs | 150 ++++++---- .../GetAttributeMethodImplementation.cs | 13 +- ...eSetBySystemRoutineMethodImplementation.cs | 38 +++ ...ocessRelatedUpdatesMethodImplementation.cs | 5 +- mocha-dotnet/src/lib/Mocha.Core/Oms.cs | 273 ++++++++++++++++-- mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs | 45 +++ mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs | 7 +- .../src/lib/Mocha.Core/UI/Widgets/Element.cs | 2 +- .../MethodTests/CommonTests.cs | 2 +- .../GetSpecifiedInstancesMethodTests.cs | 2 +- .../tests/Mocha.Core.Tests/ModuleTests.cs | 94 ++++++ .../tests/Mocha.Core.Tests/UI/UITests.cs | 73 +++++ 18 files changed, 645 insertions(+), 219 deletions(-) create mode 100644 mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstanceSetBySystemRoutineMethodImplementation.cs create mode 100644 mocha-dotnet/tests/Mocha.Core.Tests/ModuleTests.cs create mode 100644 mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs index 55c3161..4708264 100644 --- a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs @@ -54,6 +54,16 @@ public class Program : WebApplication protected override void OnStartup(EventArgs e) { + string pidfile = "/var/run/mocha/mocha-oms.pid"; + try + { + System.IO.File.WriteAllText(pidfile, System.Environment.ProcessId.ToString()); + } + catch (Exception ex) + { + Console.Error.WriteLine("oms: error: failed to write PID file; graceful control will be unavailable"); + } + // this all has to be done before the Web server is started... oms.Initialize(); @@ -93,6 +103,9 @@ public class Program : WebApplication if (e.SystemRoutine.GlobalIdentifier == KnownInstanceGuids.SystemUpdateRoutines.LoginUser) { string token = e.Context.GetWorkData(e.OMS.GetInstance(KnownAttributeGuids.Text.Token)); + if (token == null) + return; + _Sessions[token] = new OmsSession(); } } diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs index ba9f7da..18e772a 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs @@ -36,7 +36,7 @@ public class ElementCommand : InstanceCommand { // what we should do, is build a transaction with all updated ECs and fields and values // if any update fails, the entire transaction should be rolled back - Response resp = ProcessElement(oms, ctx, ProcessingInstance, InstanceHandle.Empty, e.Context.Request.Form); + Response resp = oms.ProcessElement(ctx, ProcessingInstance, e.Context.Request.Form); if (resp != null) { JsonObject obj2 = resp.GetResponse(oms, ctx); @@ -85,7 +85,7 @@ public class ElementCommand : InstanceCommand InstanceHandle elem = oms.GetRelatedInstance(ProcessingInstance, oms.GetInstance(KnownRelationshipGuids.Task__has_initiating__Element)); // fill out the initiating element and execute the task - Response r = oms.ProcessElement(ctx, elem, InstanceHandle.Empty); + Response r = oms.ProcessElement(ctx, elem, null); JsonObject obj = r.GetResponse(oms, ctx); /* @@ -105,7 +105,7 @@ public class ElementCommand : InstanceCommand InstanceHandle elem = oms.GetRelatedInstance(defaultTask, oms.GetInstance(KnownRelationshipGuids.Task__has_initiating__Element)); // fill out the initiating element and execute the task - Response r = oms.ProcessElement(ctx, elem, ProcessingInstance); + Response r = oms.ProcessElement(ctx, elem, null); JsonObject obj = r.GetResponse(oms, ctx); /* @@ -117,105 +117,4 @@ public class ElementCommand : InstanceCommand } } } - - private Response ProcessElement(Core.Oms oms, OmsContext ctx, InstanceHandle element, InstanceHandle elementContent, ReadOnlyDictionary form, string fqecidPrefix = "") - { - // first we process the related updates - InstanceHandle processedByPru = oms.GetRelatedInstance(element, oms.GetInstance(KnownRelationshipGuids.Element__processed_by__Process_Related_Updates_Method)); - if (processedByPru != InstanceHandle.Empty) - { - // this works... so far - oms.UpdateElementContents(ctx, element, form); - oms.Execute(ctx, processedByPru); - /* - $executesMethod = $oms->getRelatedInstance($usesBRMB, KnownRelationshipGuids::Method_Binding__executes__Method); - if ($executesMethod === null) - { - trigger_error("task_step: 1; uses BRMB " . $usesBRMB . "; executes method is null"); - } - $usesElement = $oms->getRelatedInstance($executesMethod, KnownRelationshipGuids::Build_UI_Response_Method__uses__Executable_returning_Element); - - $this->updateWorkDataWithElementContents($parentElementContents, $element); - - $usesElementContents = $oms->getRelatedInstances($usesElement, KnownRelationshipGuids::Element__has__Element_Content); - if (count($usesElementContents) > 0) - { - $ecInst0 = $oms->getRelatedInstance($usesElementContents[0], KnownRelationshipGuids::Element_Content__has__Instance); - $this->TargetInstance = $this->Context->getWorkData($ecInst0); - - $this->renderTaskStep2($parentElementContents, $usesElement); - } - exit(); - */ - } - - // ... then we process the element contents... - IEnumerable elementContents = oms.GetRelatedInstances(element, oms.GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); - foreach (InstanceHandle elementContent2 in elementContents) - { - Response? resp = ProcessElementContent(oms, ctx, elementContent2, form, fqecidPrefix); - if (resp != null) - { - return resp; - } - } - - // ... finally we run the CT - Control Transaction Method to return the response - InstanceHandle processedByCt = oms.GetRelatedInstance(element, oms.GetInstance(KnownRelationshipGuids.Element__processed_by__Control_Transaction_Method)); - if (processedByCt != InstanceHandle.Empty) - { - string destinationURL = oms.GetAttributeValue(processedByCt, oms.GetInstance(KnownAttributeGuids.Text.TargetURL)); - InstanceHandle usesBuildResponseMethodBinding = oms.GetRelatedInstance(processedByCt, oms.GetInstance(KnownRelationshipGuids.Control_Transaction_Method__uses__Build_Response_Method_Binding)); - if (usesBuildResponseMethodBinding != InstanceHandle.Empty) - { - - } - else if (destinationURL != null) - { - return new RedirectResponse(destinationURL); - } - - /* - $executesMethod = $oms->getRelatedInstance($usesBRMB, KnownRelationshipGuids::Method_Binding__executes__Method); - if ($executesMethod === null) - { - trigger_error("task_step: 1; uses BRMB " . $usesBRMB . "; executes method is null"); - } - $usesElement = $oms->getRelatedInstance($executesMethod, KnownRelationshipGuids::Build_UI_Response_Method__uses__Executable_returning_Element); - - $this->updateWorkDataWithElementContents($parentElementContents, $element); - - $usesElementContents = $oms->getRelatedInstances($usesElement, KnownRelationshipGuids::Element__has__Element_Content); - if (count($usesElementContents) > 0) - { - $ecInst0 = $oms->getRelatedInstance($usesElementContents[0], KnownRelationshipGuids::Element_Content__has__Instance); - $this->TargetInstance = $this->Context->getWorkData($ecInst0); - - $this->renderTaskStep2($parentElementContents, $usesElement); - } - exit(); - */ - return FailureResponse.NotImplemented; - } - return FailureResponse.UnexpectedFailure; - } - - private Response? ProcessElementContent(Core.Oms oms, OmsContext context, InstanceHandle elementContent, ReadOnlyDictionary form, string fqecidPrefix) - { - InstanceHandle elementContentInstance = oms.GetRelatedInstance(elementContent, oms.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); - if (oms.IsInstanceOf(elementContentInstance, oms.GetInstance(KnownInstanceGuids.Classes.Element))) - { - return ProcessElement(oms, context, elementContentInstance, elementContent, form, fqecidPrefix + oms.GetInstanceKey(elementContentInstance).ToString() + ":"); - } - else if (oms.IsInstanceOf(elementContentInstance, oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) - { - string key = fqecidPrefix + oms.GetInstanceKey(elementContent); - string text = oms.GetInstanceText(elementContentInstance); - if (form.ContainsKey(key)) - { - string value = form[key]; - } - } - return null; - } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceCommand.cs index 16a60fa..4e865e8 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceCommand.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceCommand.cs @@ -61,7 +61,7 @@ public abstract class InstanceCommand : TenantedCommand if (context.Session.ContainsKey("OmsContext")) { OmsContext ctx = (OmsContext)context.Session["OmsContext"]; - object? loginToken = ctx.GetWorkData(oms.GetInstance(KnownAttributeGuids.Text.Token)); + string? loginToken = ctx.GetGlobal(oms.GetInstance(KnownAttributeGuids.Text.Token)); if (loginToken != null) { obj.Add("sessionSecureToken", loginToken.ToString()); diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs index a968dce..4b64beb 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs @@ -218,6 +218,7 @@ public class OmsServer // obj.Add("message", String.Format("unknown cmd '{0}'", command)); RespondWithJson(oms, sw, e.Context, 404, "Not Found", obj); } + e.Handled = true; } public void RespondWithJson(Oms oms, StreamWriter sw, WebContext context, int responseCode, string responseText, JsonObject obj) { @@ -228,7 +229,7 @@ public class OmsServer if (context.Session.ContainsKey("OmsContext")) { OmsContext ctx = (OmsContext)context.Session["OmsContext"]; - object? loginToken = ctx.GetWorkData(oms.GetInstance(KnownAttributeGuids.Text.Token)); + string? loginToken = ctx.GetGlobal(oms.GetInstance(KnownAttributeGuids.Text.Token)); if (loginToken != null) { obj.Add("sessionSecureToken", loginToken.ToString()); diff --git a/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs b/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs index 33db4a3..07c17ab 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs @@ -481,5 +481,12 @@ namespace Mocha.Core public static Guid Far { get; } = new Guid("{f19d16c7-99ff-48a3-b86a-7db8f400d869}"); public static Guid Center { get; } = new Guid("{63179b92-9a6a-495b-a823-f45e353be9d8}"); } + + public static class Modules + { + public static Guid MochaBaseSystem { get; } = new Guid("{3ffd3a31-208c-49c9-905d-2a69362902ca}"); + public static Guid OOP { get; } = new Guid("{818dfe8d-96ea-4339-8d18-a0e9a5f50908}"); + public static Guid OOPMethodExamples { get; } = new Guid("{5017c102-c154-4ef0-b117-e42bd1c756c1}"); + } } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ConditionalSelectAttributeMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ConditionalSelectAttributeMethodImplementation.cs index 08b6d5d..b579260 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ConditionalSelectAttributeMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ConditionalSelectAttributeMethodImplementation.cs @@ -34,32 +34,8 @@ public class ConditionalSelectAttributeMethodImplementation : MethodImplementati IEnumerable trueConditions = oms.GetRelatedInstances(cas, oms.GetInstance(KnownRelationshipGuids.Condition_Group__has_true_condition__Executable_returning_Work_Data)); IEnumerable falseConditions = oms.GetRelatedInstances(cas, oms.GetInstance(KnownRelationshipGuids.Condition_Group__has_false_condition__Executable_returning_Work_Data)); bool useAnyCondition = oms.GetAttributeValue(cas, oms.GetInstance(KnownAttributeGuids.Boolean.UseAnyCondition)); - if (trueConditions != null) - { - foreach (InstanceHandle trueCondition in trueConditions) - { - InstanceHandle ret = oms.Execute(context, trueCondition); - object? retval = context.GetWorkData(ret); - if (retval is bool && ((bool)retval)) - { - value = true; - break; - } - } - } - if (falseConditions != null) - { - foreach (InstanceHandle falseCondition in falseConditions) - { - InstanceHandle ret = oms.Execute(context, falseCondition); - object? retval = context.GetWorkData(ret); - if (retval is bool && (!(bool)retval)) - { - value = true; - break; - } - } - } + + value = oms.EvaluateConditions(context, trueConditions, falseConditions, useAnyCondition); if (value) { diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs index 35fc59f..949da2d 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs @@ -45,80 +45,120 @@ public class EvaluateBooleanExpressionMethodImplementation : MethodImplementatio InstanceHandle source = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Evaluate_Boolean_Expression_Method__has_source__Executable_returning_Work_Data)); InstanceHandle comparison = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Evaluate_Boolean_Expression_Method__uses__Boolean_Operator)); - InstanceHandle target = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Evaluate_Boolean_Expression_Method__has_target__Executable_returning_Work_Data)); - - InstanceHandle[] rtarget_value = new InstanceHandle[0]; + Console.Error.WriteLine("EBE - source {0}", oms.GetGlobalIdentifier(source)); + InstanceHandle rsource = oms.Execute(context, source); object? rsource_valueraw = context.GetWorkData(rsource); - InstanceHandle[] rsource_value = new InstanceHandle[0]; - if (rsource_valueraw is InstanceHandle[]) - { - rsource_value = (InstanceHandle[])rsource_valueraw; - } - else if (rsource_valueraw is InstanceHandle) - { - rsource_value = new InstanceHandle[] { (InstanceHandle)rsource_valueraw }; - } - - if (oms.IsInstanceOf(target, oms.GetInstance(KnownInstanceGuids.Classes.Attribute))) - { - rtarget_value = new InstanceHandle[] { target }; - } - else if (oms.IsInstanceOf(target, oms.GetInstance(KnownInstanceGuids.Classes.Executable))) - { - InstanceHandle rtarget = oms.Execute(context, target); - object? rtarget_valueraw = context.GetWorkData(rtarget); - } - else - { - rtarget_value = new InstanceHandle[] { target }; - } - bool value = false; - - if (comparison == oms.GetInstance(KnownInstanceGuids.RelationalOperators.InSelectionList)) + if (comparison == oms.GetInstance(KnownInstanceGuids.RelationalOperators.IsNotEmpty)) { - if (rtarget_value.ContainsAny(rsource_value)) + if (rsource_valueraw is InstanceHandle) { - value = true; + Console.Error.WriteLine("EBE - is not empty - {0} ?", oms.GetGlobalIdentifier((InstanceHandle)rsource_valueraw)); + + value = ((InstanceHandle)rsource_valueraw) != InstanceHandle.Empty; } - } - else if (comparison == oms.GetInstance(KnownInstanceGuids.RelationalOperators.ExactMatchWithSelectionList)) - { - if (rtarget_value.Length == rsource_value.Length) + else if (rsource_valueraw is IEnumerable) { - List l1 = new List(rsource_value); - List l2 = new List(rtarget_value); - l1.Sort(); - l2.Sort(); - - value = true; - for (int i = 0; i < l1.Count; i++) - { - if (!l1[i].Equals(l2[i])) - { - value = false; - break; - } - } + Console.Error.WriteLine("EBE - is not empty - count = {0} ?", ((IEnumerable)rsource_valueraw).Count()); + + value = ((IEnumerable)rsource_valueraw).Any(); } else { - value = false; + Console.Error.WriteLine("EBE - is not empty - {0} ?", rsource_valueraw); + + value = rsource_valueraw != null; } } - else if (comparison == oms.GetInstance(KnownInstanceGuids.LogicalOperators.EqualTo)) + else if (comparison == oms.GetInstance(KnownInstanceGuids.RelationalOperators.IsEmpty)) { - // ! FIXME ! Implement This ! - value = false; + if (rsource_valueraw is InstanceHandle) + { + value = ((InstanceHandle)rsource_valueraw) == InstanceHandle.Empty; + } + else if (rsource_valueraw is IEnumerable) + { + value = !((IEnumerable)rsource_valueraw).Any(); + } + else + { + value = rsource_valueraw == null; + } } else { - throw new NotImplementedException(String.Format("relational operator '{0}' not implemented @@ {1}, {2}", comparison, oms.GetGlobalIdentifier(method).ToString("B"), context.StackTrace.ToString())); + InstanceHandle target = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Evaluate_Boolean_Expression_Method__has_target__Executable_returning_Work_Data)); + InstanceHandle[] rtarget_value = new InstanceHandle[0]; + + InstanceHandle[] rsource_value = new InstanceHandle[0]; + if (rsource_valueraw is InstanceHandle[]) + { + rsource_value = (InstanceHandle[])rsource_valueraw; + } + else if (rsource_valueraw is InstanceHandle) + { + rsource_value = new InstanceHandle[] { (InstanceHandle)rsource_valueraw }; + } + + if (oms.IsInstanceOf(target, oms.GetInstance(KnownInstanceGuids.Classes.Attribute))) + { + rtarget_value = new InstanceHandle[] { target }; + } + else if (oms.IsInstanceOf(target, oms.GetInstance(KnownInstanceGuids.Classes.Executable))) + { + InstanceHandle rtarget = oms.Execute(context, target); + object? rtarget_valueraw = context.GetWorkData(rtarget); + } + else + { + rtarget_value = new InstanceHandle[] { target }; + } + + if (comparison == oms.GetInstance(KnownInstanceGuids.RelationalOperators.InSelectionList)) + { + if (rtarget_value.ContainsAny(rsource_value)) + { + value = true; + } + } + else if (comparison == oms.GetInstance(KnownInstanceGuids.RelationalOperators.ExactMatchWithSelectionList)) + { + if (rtarget_value.Length == rsource_value.Length) + { + List l1 = new List(rsource_value); + List l2 = new List(rtarget_value); + l1.Sort(); + l2.Sort(); + + value = true; + for (int i = 0; i < l1.Count; i++) + { + if (!l1[i].Equals(l2[i])) + { + value = false; + break; + } + } + } + else + { + value = false; + } + } + else if (comparison == oms.GetInstance(KnownInstanceGuids.LogicalOperators.EqualTo)) + { + // ! FIXME ! Implement This ! + value = false; + } + else + { + throw new NotImplementedException(String.Format("relational operator '{0}' not implemented @@ {1}, {2}", comparison, oms.GetGlobalIdentifier(method).ToString("B"), context.StackTrace.ToString())); + } } - + context.SetWorkData(returnsAttribute, value); return returnsAttribute; } diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs index db8bfcc..7560eab 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs @@ -40,9 +40,16 @@ public class GetAttributeMethodImplementation : MethodImplementation { forInstance = (InstanceHandle?) context.GetWorkData(forInstance.Value); } - - object? value = oms.UnsafeGetAttributeValue((InstanceHandle) forInstance, returnsAttribute); - context.SetWorkData(returnsAttribute, value); + + if (forInstance is InstanceHandle forInstanceHandle) + { + object? value = oms.UnsafeGetAttributeValue((InstanceHandle)forInstanceHandle, returnsAttribute); + context.SetWorkData(returnsAttribute, value); + } + else + { + Console.Error.WriteLine("no for instance IS"); + } return returnsAttribute; } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstanceSetBySystemRoutineMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstanceSetBySystemRoutineMethodImplementation.cs new file mode 100644 index 0000000..6a7c466 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstanceSetBySystemRoutineMethodImplementation.cs @@ -0,0 +1,38 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of Mocha.NET. +// +// Mocha.NET is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Mocha.NET is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Mocha.NET. If not, see . + +namespace Mocha.Core.MethodImplementations; + +public class GetInstanceSetBySystemRoutineMethodImplementation : MethodImplementation +{ + public override Guid MethodClassGuid => KnownInstanceGuids.MethodClasses.GetInstanceSetBySystemRoutineMethod; + protected override InstanceHandle ExecuteInternal(Oms oms, OmsContext context, InstanceHandle method) + { + InstanceHandle irForClass = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Method__for__Class)); + InstanceHandle returnsWorkSet = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Get_Instance_Set_by_System_Routine_Method__returns__Work_Set)); + InstanceHandle usesSystemInstanceSetRoutine = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Get_Instance_Set_by_System_Routine_Method__uses__System_Instance_Set_Routine)); + + if (returnsWorkSet == InstanceHandle.Empty) + { + throw new InvalidOperationException("no return Attribute specified for method"); + } + + object? value = oms.ExecuteSystemRoutine(context, usesSystemInstanceSetRoutine); + context.SetWorkData(returnsWorkSet, value); + return returnsWorkSet; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs index 793e5f4..9f2e506 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs @@ -25,9 +25,12 @@ public class ProcessRelatedUpdatesMethodImplementation : MethodImplementation InstanceHandle forClass = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Method__for__Class)); IEnumerable usesExecutableForPUMB = oms.GetRelatedInstances(method, oms.GetInstance(KnownRelationshipGuids.Process_Related_Updates_Method__uses__Executable_for_PUMB)); + Console.Error.WriteLine("---> has {0} PUMBs", usesExecutableForPUMB.Count()); + foreach (InstanceHandle pumb in usesExecutableForPUMB) { - oms.Execute(context, pumb); + Console.Error.WriteLine("---> executing PUMB {0}", pumb.GlobalIdentifier); + oms.Execute(context, pumb) ; } return InstanceHandle.Empty; } diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index 6c63e86..19cdcba 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -21,6 +21,7 @@ using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data.SqlTypes; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Net; using System.Numerics; @@ -72,6 +73,37 @@ public abstract class Oms UpdateSyntacticSugar(); + RegisterSystemInstanceSetRoutine(KnownInstanceGuids.SystemInstanceSetRoutines.GetCurrentUser, delegate (Oms oms, OmsContext context) + { + Console.Error.WriteLine("oms: executing system instance set routine {0}", KnownInstanceGuids.SystemUpdateRoutines.LoginUser); + + InstanceHandle a_Token = oms.GetInstance(KnownAttributeGuids.Text.Token); + string token = context.GetGlobal(a_Token); + if (token != null) + { + IEnumerable userLogins = oms.GetInstancesOf(oms.GetInstance(KnownInstanceGuids.Classes.UserLogin)); + InstanceHandle r_User_Login__has__User = oms.GetInstance(KnownRelationshipGuids.User_Login__has__User); + + foreach (InstanceHandle userLogin in userLogins) + { + string compareToken = oms.GetAttributeValue(userLogin, a_Token); + Console.Error.WriteLine("oms: token {0} == {1} ???", token, compareToken); + + if (compareToken.Equals(token)) + { + InstanceHandle user = oms.GetRelatedInstance(userLogin, r_User_Login__has__User); + Console.Error.WriteLine("yes, returning {0}", user.GlobalIdentifier); + + return new InstanceHandle[] { user }; + } + } + } + else + { + Console.Error.WriteLine("oms: error: login token not set"); + } + return new InstanceHandle[0]; + }); RegisterSystemAttributeRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetInstanceText, delegate (Oms oms, OmsContext context) { object? val = context.GetWorkData(oms.GetInstance(KnownInstanceGuids.Classes.Instance)); @@ -103,6 +135,8 @@ public abstract class Oms }); RegisterSystemUpdateRoutine(KnownInstanceGuids.SystemUpdateRoutines.LoginUser, delegate (Oms oms, OmsContext context) { + Console.Error.WriteLine("oms: executing system update routine {0}", KnownInstanceGuids.SystemUpdateRoutines.LoginUser); + // FIXME: these are ECs for now, they should probably be Text Attributes (or parms?) passed into the US method Guid userNameGuid = new Guid("{c67f305e-bd4d-4628-816b-55fb85ea1b67}"); Guid passwordGuid = new Guid("{51b51be3-44fd-48f1-971f-682aee0a6132}"); @@ -120,10 +154,14 @@ public abstract class Oms foreach (InstanceHandle user in users) { + Console.Error.WriteLine("looking at user {0}", user.GlobalIdentifier); + string expectedUserName = oms.GetAttributeValue(user, oms.GetInstance(KnownAttributeGuids.Text.UserName)); if (expectedUserName == null) continue; + Console.Error.WriteLine("... expected user name '{0}', actual user name '{1}'", expectedUserName, userName); + if (expectedUserName != userName) continue; @@ -137,6 +175,8 @@ public abstract class Oms byte[] hash = hashAlgorithm.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password + passwordSalt)); string actualPasswordHash = Convert.ToHexString(hash).ToLowerInvariant(); + Console.Error.WriteLine("... expected password hash '{0}'\n... actual password hash '{1}'", expectedPasswordHash, actualPasswordHash); + if (actualPasswordHash.Equals(expectedPasswordHash)) { actualSystemUser = user; @@ -147,8 +187,12 @@ public abstract class Oms if (sessionSecureToken != null) { + Console.Error.WriteLine("!! success !! set secure token {0}", sessionSecureToken); + Console.Error.WriteLine("!! success !! current system user is {0}", actualSystemUser.GlobalIdentifier); + context.SetWorkData(oms.GetInstance(KnownInstanceGuids.Classes.User), actualSystemUser); context.SetWorkData(oms.GetInstance(KnownAttributeGuids.Text.Token), sessionSecureToken); + context.SetGlobal(oms.GetInstance(KnownAttributeGuids.Text.Token), sessionSecureToken); // FIXME: this should be processed in the PRU InstanceHandle clsSystemAccountSignon = GetInstance(KnownInstanceGuids.Classes.UserLogin); @@ -905,7 +949,8 @@ public abstract class Oms InstanceHandle parentClass = GetParentClass(methodOrMethodBinding); Guid parentClassId = GetGlobalIdentifier(parentClass); - context.StackTrace.Push(methodOrMethodBinding); + context.Push(methodOrMethodBinding); + // context.StackTrace.Push(methodOrMethodBinding); InstanceHandle? retval = null; @@ -949,7 +994,14 @@ public abstract class Oms } else { - context.StackTrace.Pop(); + // context.StackTrace.Pop(); + IDictionary dict = context.GetAllWorkData(); + context.Pop(); + + if (dict.ContainsKey(retval.Value)) + { + context.SetWorkData(retval.Value, dict[retval.Value]); + } return retval.Value; } } @@ -965,7 +1017,7 @@ public abstract class Oms return new OmsContext(this); } - public IReadOnlyCollection ExecuteStaticMethodReturningInstanceSet(OmsContext context, ReturnInstanceSetMethodBinding methodBinding) + public IEnumerable ExecuteStaticMethodReturningInstanceSet(OmsContext context, ReturnInstanceSetMethodBinding methodBinding) { InstanceHandle method = GetRelatedInstance(methodBinding.Handle, GetInstance(KnownRelationshipGuids.Method_Binding__executes__Method)); if (TryGetAttributeValue(method, GetInstance(KnownAttributeGuids.Boolean.Static), out bool is_static)) @@ -975,10 +1027,14 @@ public abstract class Oms InstanceHandle result = Execute(context, methodBinding); object? value = context.GetWorkData(result); - if (value is IReadOnlyCollection res) + if (value is IEnumerable res) { return res; } + else if (value is InstanceHandle res_singular) + { + return new InstanceHandle[] { res_singular }; + } } } throw new InvalidOperationException("cannot call a non-static Method in a static context"); @@ -1607,7 +1663,7 @@ public abstract class Oms return false; } - public void UpdateElementContents(OmsContext context, InstanceHandle element, ReadOnlyDictionary values) + private void UpdateElementContents(OmsContext context, InstanceHandle element, IDictionary values) { IEnumerable elementContents = GetRelatedInstances(element, GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); foreach (InstanceHandle elementContent in elementContents) @@ -1664,24 +1720,139 @@ public abstract class Oms } } - public Response ProcessElement(OmsContext context, InstanceHandle initiatingElement, InstanceHandle parmInstance) + public string GetLabelForUIElement(InstanceHandle parentElementContent) { - if (parmInstance != InstanceHandle.Empty) + InstanceHandle parentInstance = GetRelatedInstance(parentElementContent, GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + string label = GetAttributeValue(parentElementContent, GetInstance(KnownAttributeGuids.Text.LabelOverride)); + if (label == null) { - InstanceHandle processedByCt = GetRelatedInstance(initiatingElement, GetInstance(KnownRelationshipGuids.Element__processed_by__Control_Transaction_Method)); - InstanceHandle forClassId = GetRelatedInstance(processedByCt, GetInstance(KnownRelationshipGuids.Method__for__Class)); + label = GetAttributeValue(parentElementContent, GetInstance(KnownAttributeGuids.Text.Label)); + if (label == null) + { + label = GetInstanceText(parentInstance); + } + } + return label; + } - InstanceHandle buildsResponseWithMB = GetRelatedInstance(processedByCt, GetInstance(KnownRelationshipGuids.Control_Transaction_Method__uses__Build_Response_Method_Binding)); + + + public Response ProcessElement(OmsContext ctx, InstanceHandle element, IDictionary? form = null, string fqecidPrefix = "") + { + if (form != null) + { + /* + InstanceHandle processedByCt2 = GetRelatedInstance(element, GetInstance(KnownRelationshipGuids.Element__processed_by__Control_Transaction_Method)); + InstanceHandle forClassId = GetRelatedInstance(processedByCt2, GetInstance(KnownRelationshipGuids.Method__for__Class)); + + InstanceHandle buildsResponseWithMB = GetRelatedInstance(processedByCt2, GetInstance(KnownRelationshipGuids.Control_Transaction_Method__uses__Build_Response_Method_Binding)); InstanceHandle executesMethod = GetRelatedInstance(buildsResponseWithMB, GetInstance(KnownRelationshipGuids.Method_Binding__executes__Method)); InstanceHandle usesExecutableReturningElement = GetRelatedInstance(executesMethod, GetInstance(KnownRelationshipGuids.Build_UI_Response_Method__uses__Executable_returning_Element)); - context.SetWorkData(forClassId, parmInstance); - return ProcessElement(context, usesExecutableReturningElement, InstanceHandle.Empty); + ctx.SetWorkData(forClassId, processingInstance); + return ProcessElement(ctx, usesExecutableReturningElement, InstanceHandle.Empty, form); + */ + + // first we process the related updates + InstanceHandle processedByPru = GetRelatedInstance(element, GetInstance(KnownRelationshipGuids.Element__processed_by__Process_Related_Updates_Method)); + if (processedByPru != InstanceHandle.Empty) + { + // this works... so far + Console.Error.WriteLine("oms: processing element with PRU {0}", processedByPru.GlobalIdentifier); + // hack + UpdateElementContents(ctx, element, form); + Execute(ctx, processedByPru); + /* + $executesMethod = $oms->getRelatedInstance($usesBRMB, KnownRelationshipGuids::Method_Binding__executes__Method); + if ($executesMethod === null) + { + trigger_error("task_step: 1; uses BRMB " . $usesBRMB . "; executes method is null"); + } + $usesElement = $oms->getRelatedInstance($executesMethod, KnownRelationshipGuids::Build_UI_Response_Method__uses__Executable_returning_Element); + + $this->updateWorkDataWithElementContents($parentElementContents, $element); + + $usesElementContents = $oms->getRelatedInstances($usesElement, KnownRelationshipGuids::Element__has__Element_Content); + if (count($usesElementContents) > 0) + { + $ecInst0 = $oms->getRelatedInstance($usesElementContents[0], KnownRelationshipGuids::Element_Content__has__Instance); + $this->TargetInstance = $this->Context->getWorkData($ecInst0); + + $this->renderTaskStep2($parentElementContents, $usesElement); + } + exit(); + */ + } + + // ... then we process the element contents... + IEnumerable elementContents = GetRelatedInstances(element, GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); + foreach (InstanceHandle elementContent2 in elementContents) + { + Response? resp = ProcessElementContent(ctx, elementContent2, form, fqecidPrefix); + if (resp != null) + { + return resp; + } + } + + // ... finally we run the CT - Control Transaction Method to return the response + InstanceHandle processedByCt = GetRelatedInstance(element, GetInstance(KnownRelationshipGuids.Element__processed_by__Control_Transaction_Method)); + if (processedByCt != InstanceHandle.Empty) + { + Console.Error.WriteLine("oms: processing element with CT {0}", processedByCt.GlobalIdentifier); + // hack + IEnumerable usesBuildResponseMethodBindings = GetRelatedInstances(processedByCt, GetInstance(KnownRelationshipGuids.Control_Transaction_Method__uses__Build_Response_Method_Binding)); + Console.Error.WriteLine("---> has {0} BRMBs", usesBuildResponseMethodBindings.Count()); + + foreach (InstanceHandle usesBuildResponseMethodBinding in usesBuildResponseMethodBindings) + { + Console.Error.WriteLine("oms: evaluating BRMB {0}", usesBuildResponseMethodBinding.GlobalIdentifier); + + IEnumerable trueConditions = GetRelatedInstances(usesBuildResponseMethodBinding, GetInstance(KnownRelationshipGuids.Condition_Group__has_true_condition__Executable_returning_Work_Data)); + IEnumerable falseConditions = GetRelatedInstances(usesBuildResponseMethodBinding, GetInstance(KnownRelationshipGuids.Condition_Group__has_false_condition__Executable_returning_Work_Data)); + bool useAnyCondition = GetAttributeValue(usesBuildResponseMethodBinding, GetInstance(KnownAttributeGuids.Boolean.UseAnyCondition)); + + if (EvaluateConditions(ctx, trueConditions, falseConditions, useAnyCondition)) + { + InstanceHandle executesMethod = GetRelatedInstance(usesBuildResponseMethodBinding, GetInstance(KnownRelationshipGuids.Method_Binding__executes__Method)); + Console.Error.WriteLine("----> ok, executing method {0}", executesMethod.GlobalIdentifier); + + // execute BRMB + string targetURL = GetAttributeValue(executesMethod, GetInstance(KnownAttributeGuids.Text.TargetURL)); + if (targetURL != null) + { + return new RedirectResponse(targetURL); + } + } + } + + /* + $executesMethod = $oms->getRelatedInstance($usesBRMB, KnownRelationshipGuids::Method_Binding__executes__Method); + if ($executesMethod === null) + { + trigger_error("task_step: 1; uses BRMB " . $usesBRMB . "; executes method is null"); + } + $usesElement = $oms->getRelatedInstance($executesMethod, KnownRelationshipGuids::Build_UI_Response_Method__uses__Executable_returning_Element); + + $this->updateWorkDataWithElementContents($parentElementContents, $element); + + $usesElementContents = $oms->getRelatedInstances($usesElement, KnownRelationshipGuids::Element__has__Element_Content); + if (count($usesElementContents) > 0) + { + $ecInst0 = $oms->getRelatedInstance($usesElementContents[0], KnownRelationshipGuids::Element_Content__has__Instance); + $this->TargetInstance = $this->Context->getWorkData($ecInst0); + + $this->renderTaskStep2($parentElementContents, $usesElement); + } + exit(); + */ + return FailureResponse.NotImplemented; + } } else { Renderer renderer = new Renderer(this); - Widget widget = renderer.Parse(context, initiatingElement, InstanceHandle.Empty, InstanceHandle.Empty); + Widget widget = renderer.Parse(ctx, element, InstanceHandle.Empty, InstanceHandle.Empty); if (widget == null) { return FailureResponse.UnexpectedFailure; @@ -1704,18 +1875,74 @@ public abstract class Oms return FailureResponse.UnexpectedFailure; } - public string GetLabelForUIElement(InstanceHandle parentElementContent) - { - InstanceHandle parentInstance = GetRelatedInstance(parentElementContent, GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); - string label = GetAttributeValue(parentElementContent, GetInstance(KnownAttributeGuids.Text.LabelOverride)); - if (label == null) + private Response? ProcessElementContent(OmsContext context, InstanceHandle elementContent, IDictionary form, string fqecidPrefix) + { + InstanceHandle elementContentInstance = GetRelatedInstance(elementContent, GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + if (IsInstanceOf(elementContentInstance, GetInstance(KnownInstanceGuids.Classes.Element))) { - label = GetAttributeValue(parentElementContent, GetInstance(KnownAttributeGuids.Text.Label)); - if (label == null) + return ProcessElement(context, elementContentInstance, form, fqecidPrefix + GetInstanceKey(elementContentInstance).ToString() + ":"); + } + else if (IsInstanceOf(elementContentInstance, GetInstance(KnownInstanceGuids.Classes.TextAttribute))) + { + string key = fqecidPrefix + GetInstanceKey(elementContent); + string text = GetInstanceText(elementContentInstance); + if (form.ContainsKey(key)) { - label = GetInstanceText(parentInstance); + string value = form[key]; } } - return label; - } + return null; + } + + private HashAlgorithm hashAlgorithm = System.Security.Cryptography.SHA512.Create(); + + public InstanceHandle CreateUser(string username, string password) + { + InstanceHandle c_User = GetInstance(KnownInstanceGuids.Classes.User); + InstanceHandle a_UserName = GetInstance(KnownAttributeGuids.Text.UserName); + InstanceHandle a_PasswordSalt = GetInstance(KnownAttributeGuids.Text.PasswordSalt); + InstanceHandle a_PasswordHash = GetInstance(KnownAttributeGuids.Text.PasswordHash); + + string salt = Guid.NewGuid().ToString("N").ToLowerInvariant(); + string hash = Convert.ToHexString(hashAlgorithm.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password + salt))).ToLowerInvariant(); + + InstanceHandle i_User = CreateInstanceOf(c_User); + SetAttributeValue(i_User, a_UserName, username); + SetAttributeValue(i_User, a_PasswordSalt, salt); + SetAttributeValue(i_User, a_PasswordHash, hash); + + return i_User; + } + + public bool EvaluateConditions(OmsContext context, IEnumerable trueConditions, IEnumerable falseConditions, bool useAnyCondition) + { + bool value = false; + if (trueConditions != null) + { + foreach (InstanceHandle trueCondition in trueConditions) + { + InstanceHandle ret = Execute(context, trueCondition); + object? retval = context.GetWorkData(ret); + if (retval is bool && ((bool)retval)) + { + value = true; + break; + } + } + } + if (falseConditions != null) + { + foreach (InstanceHandle falseCondition in falseConditions) + { + InstanceHandle ret = Execute(context, falseCondition); + object? retval = context.GetWorkData(ret); + if (retval is bool && (!(bool)retval)) + { + value = true; + break; + } + } + } + return value; + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs index c977b23..1749816 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs @@ -25,6 +25,7 @@ public class OmsContext { public Oms Oms { get; } public OmsStackTrace StackTrace { get; } + private Stack> WorkDataSets = new Stack>(); internal OmsContext(Oms oms) { @@ -33,6 +34,7 @@ public class OmsContext } private Dictionary _WorkData = new Dictionary(); + private Dictionary _GlobalData = new Dictionary(); public object? GetWorkData(WorkSet parm) { return GetWorkData(parm.Handle); @@ -42,6 +44,12 @@ public class OmsContext if (_WorkData.ContainsKey(parm)) return _WorkData[parm]; + foreach (Dictionary set in WorkDataSets) + { + if (set.ContainsKey(parm)) + return set[parm]; + } + // Console.Error.WriteLine("work data not found for parm {0}", Oms.GetGlobalIdentifier(parm)); return null; } @@ -108,6 +116,8 @@ public class OmsContext } } } + + value = ((IEnumerable)value).FirstOrDefault(); } else if (value is IEnumerable) { @@ -141,4 +151,39 @@ public class OmsContext } _WorkData[parm] = value; } + + public void Push(InstanceHandle methodOrMethodBinding) + { + StackTrace.Push(methodOrMethodBinding); + WorkDataSets.Push(_WorkData); + + _WorkData = new Dictionary(); + } + public InstanceHandle Pop() + { + InstanceHandle methodOrMethodBinding = StackTrace.Pop(); + _WorkData = WorkDataSets.Pop(); + return methodOrMethodBinding; + } + + public IDictionary GetAllWorkData() + { + return _WorkData; + } + + public T GetGlobal(InstanceHandle parm, T defaultValue = default(T)) + { + if (_GlobalData.ContainsKey(parm)) + { + if (_GlobalData[parm] is T) + { + return (T)(_GlobalData[parm]); + } + } + return defaultValue; + } + public void SetGlobal(InstanceHandle parm, object? value) + { + _GlobalData[parm] = value; + } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs index 8cfd752..04b1274 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs @@ -74,8 +74,11 @@ public abstract class Widget if (!displayOptions.Contains(OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.DoNotShowLabel))) { - string label = OMS.GetLabelForUIElement(ParentElementContent); - obj.Add("label", label); + if (!OMS.IsInstanceOf(ParentInstance, OMS.GetInstance(KnownInstanceGuids.Classes.Element))) + { + string label = OMS.GetLabelForUIElement(ParentElementContent); + obj.Add("label", label); + } } if (!ShouldBeEnabled()) { diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs index fb0c3bc..687e469 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs @@ -59,7 +59,7 @@ public class Element : Widget // otherwise, we are a hbox // return "hbox"; - return "vbox"; + return "fieldSet"; } } diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/CommonTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/CommonTests.cs index 406b029..7ba0621 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/CommonTests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/CommonTests.cs @@ -41,7 +41,7 @@ public class CommonTests : MethodTestsBase Assert.That(delegate () { - IReadOnlyCollection specifiedInstances = Oms.ExecuteStaticMethodReturningInstanceSet(context, dummyMethodRsmb); + IEnumerable specifiedInstances = Oms.ExecuteStaticMethodReturningInstanceSet(context, dummyMethodRsmb); }, Throws.InvalidOperationException); } diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/GetSpecifiedInstancesMethodTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/GetSpecifiedInstancesMethodTests.cs index 71d9bf3..71d4b34 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/GetSpecifiedInstancesMethodTests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/MethodTests/GetSpecifiedInstancesMethodTests.cs @@ -44,7 +44,7 @@ public class GetSpecifiedInstancesMethodTests : MethodTestsBase nom = Oms.GetInstanceText(gsiMethodRsmb); OmsContext context = Oms.CreateContext(); - IReadOnlyCollection specifiedInstances = Oms.ExecuteStaticMethodReturningInstanceSet(context, gsiMethodRsmb); + IEnumerable specifiedInstances = Oms.ExecuteStaticMethodReturningInstanceSet(context, gsiMethodRsmb); Assert.That(specifiedInstances, Contains.Item(irTestClassInstance)); } diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/ModuleTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/ModuleTests.cs new file mode 100644 index 0000000..9317c35 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/ModuleTests.cs @@ -0,0 +1,94 @@ +using System; +using Mocha.Core.Oop; + +namespace Mocha.Core.Tests; + +[TestFixture] +public class ModuleTests : OmsTestsBase +{ + [Test] + public void ToplevelModuleInstanceText() + { + InstanceHandle ihMochaBaseSystem = Oms.GetInstance(KnownInstanceGuids.Modules.MochaBaseSystem); + + string text = Oms.GetInstanceText(ihMochaBaseSystem); + Assert.That(text, Is.EqualTo("Mocha Base System")); + } + [Test] + public void SublevelModuleInstanceText() + { + InstanceHandle ihOOPMethodExamples = Oms.GetInstance(KnownInstanceGuids.Modules.OOPMethodExamples); + string text = Oms.GetInstanceText(ihOOPMethodExamples); + Assert.That(text, Is.EqualTo("Mocha Base System:OOP:OOP Method Examples")); + } + + [Test] + public void TopLevelModuleName() + { + InstanceHandle ihMochaBaseSystem = Oms.GetInstance(KnownInstanceGuids.Modules.MochaBaseSystem); + + InstanceHandle c_Module = Oms.GetInstance(KnownInstanceGuids.Classes.Module); + Method Module__get__Name = Oms.GetMethod(c_Module, "get", "Name"); + Assert.That(Module__get__Name, Is.Not.Null); + // + OmsContext context = Oms.CreateContext(); + InstanceHandle ret = Oms.Execute(context, Module__get__Name, ihMochaBaseSystem); + object? val = context.GetWorkData(ret); + Assert.That(val, Is.TypeOf().And.EqualTo("Mocha Base System")); + } + + [Test] + public void OOPMethodExamples_ParentModuleIs_OOP() + { + InstanceHandle ihOOPMethodExamples = Oms.GetInstance(KnownInstanceGuids.Modules.OOPMethodExamples); + InstanceHandle ihOOP = Oms.GetInstance(KnownInstanceGuids.Modules.OOP); + + InstanceHandle c_Module = Oms.GetInstance(KnownInstanceGuids.Classes.Module); + Method Module__get__Parent_Module = Oms.GetMethod(c_Module, "get", "Parent Module"); + Assert.That(Module__get__Parent_Module, Is.Not.Null); + + OmsContext context = Oms.CreateContext(); + InstanceHandle ret = Oms.Execute(context, Module__get__Parent_Module, ihOOPMethodExamples); + object? val = context.GetWorkData(ret); + + Assert.That(val, Is.TypeOf().And.EqualTo(ihOOP)); + } + + [Test] + public void MochaBaseSystem_ParentModuleIs_Empty() + { + InstanceHandle ihMochaBaseSystem = Oms.GetInstance(KnownInstanceGuids.Modules.MochaBaseSystem); + + InstanceHandle c_Module = Oms.GetInstance(KnownInstanceGuids.Classes.Module); + Method Module__get__Parent_Module = Oms.GetMethod(c_Module, "get", "Parent Module"); + Assert.That(Module__get__Parent_Module, Is.Not.Null); + + OmsContext context = Oms.CreateContext(); + InstanceHandle ret = Oms.Execute(context, Module__get__Parent_Module, ihMochaBaseSystem); + object? val = context.GetWorkData(ret); + + Assert.That(val, Is.Null.Or.EqualTo(InstanceHandle.Empty)); + } + + [Test] + public void Module__get__Fully_Qualified_Name__method_components() + { + InstanceHandle ihMochaBaseSystem = Oms.GetInstance(KnownInstanceGuids.Modules.MochaBaseSystem); + + InstanceHandle c_Module = Oms.GetInstance(KnownInstanceGuids.Classes.Module); + InstanceHandle method = Oms.GetInstance(new Guid("{0ab85e0b-5577-475a-b914-4578ffb928d7}")); + + IEnumerable ms = Oms.GetRelatedInstances(method, Oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method__builds_with__Build_Attribute_Method_Component)); + + InstanceHandle m2 = Oms.GetInstance(new Guid("{27b070c9-7130-414b-960d-511f2836ef06}")); + + IEnumerable ms2 = Oms.GetRelatedInstances(method, Oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method__builds_with__Build_Attribute_Method_Component)); + + OmsContext context = Oms.CreateContext(); + InstanceHandle ret = Oms.Execute(context, method, ihMochaBaseSystem); + object? val = context.GetWorkData(ret); + + + } + +} diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs new file mode 100644 index 0000000..11fbcaa --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs @@ -0,0 +1,73 @@ +using System; +using System.Text.Json.Nodes; + +namespace Mocha.Core.Tests.UI; + +[TestFixture] +public class UITests : OmsTestsBase +{ + + [Test] + public void LoginPageElement() + { + InstanceHandle loginPage = Oms.GetInstance(KnownInstanceGuids.Pages.LoginPage); + + OmsContext context = Oms.CreateContext(); + Response pageOutput = Oms.ProcessElement(context, loginPage); + + JsonObject json = pageOutput.GetResponse(Oms, context); + + Assert.That(json["result"].GetValue(), Is.EqualTo("success")); + Assert.That(json["value"]["body"]["widget"].GetValue(), Is.EqualTo("fieldSet")); + } + + [Test] + public void LoginPageElementSubmit() + { + Oms.CreateUser("testing", "testing"); + + InstanceHandle loginPage = Oms.GetInstance(KnownInstanceGuids.Pages.LoginPage); + + InstanceHandle loginPageSubedit = Oms.GetInstance(new Guid("{2b7d4481-b7c2-4e26-a917-e3ff7c367a8a}")); + OmsContext context = Oms.CreateContext(); + + Dictionary dict = new Dictionary(); + dict["{c67f305e-bd4d-4628-816b-55fb85ea1b67}"] = "testing"; + dict["{51b51be3-44fd-48f1-971f-682aee0a6132}"] = "testing"; + + Response pageOutput = Oms.ProcessElement(context, loginPageSubedit, dict); + JsonObject json = pageOutput.GetResponse(Oms, context); + + string s_Token = context.GetGlobal(Oms.GetInstance(KnownAttributeGuids.Text.Token)); + Assert.That(s_Token, Is.Not.Empty); + + Assert.That(json["result"].GetValue(), Is.EqualTo("success")); + Assert.That(json["type"].GetValue(), Is.EqualTo("redirect")); + Assert.That(json["destinationUrl"].GetValue(), Is.EqualTo("~/d/home.htmld")); + } + + [Test] + public void LoginPageElementSubmitBadPassword() + { + InstanceHandle loginPage = Oms.GetInstance(KnownInstanceGuids.Pages.LoginPage); + + InstanceHandle loginPageSubedit = Oms.GetInstance(new Guid("{2b7d4481-b7c2-4e26-a917-e3ff7c367a8a}")); + OmsContext context = Oms.CreateContext(); + + Dictionary dict = new(); + dict["{c67f305e-bd4d-4628-816b-55fb85ea1b67}"] = "superuser"; + dict["{51b51be3-44fd-48f1-971f-682aee0a6132}"] = "testing"; + + Response pageOutput = Oms.ProcessElement(context, loginPageSubedit, dict); + + object i_User = context.GetWorkData(Oms.GetInstance(KnownInstanceGuids.Classes.User)); + Assert.That(i_User, Is.Null); + + JsonObject json = pageOutput.GetResponse(Oms, context); + + // ??? should this return a result=failure? + Assert.That(json["result"].GetValue(), Is.EqualTo("success")); + Assert.That(json["type"].GetValue(), Is.EqualTo("redirect")); + Assert.That(json["destinationUrl"].GetValue(), Does.StartWith("~/d/logout.htmld")); + } +} From 784e04fd58560f898353077234a358e8d6bf7ce3 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Thu, 19 Dec 2024 08:35:07 -0500 Subject: [PATCH 04/18] preliminary implementation of derived instances --- mocha-common | 2 +- .../src/lib/Mocha.Core/InstanceKey.cs | 122 ++++++++++++++++-- .../src/lib/Mocha.Core/KnownAttributeGuids.cs | 1 + mocha-dotnet/src/lib/Mocha.Core/Oms.cs | 90 ++++++++++++- .../Mocha.Core.Tests/DerivedInstanceTests.cs | 54 ++++++++ 5 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs diff --git a/mocha-common b/mocha-common index 54d5ab8..cdb956e 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit 54d5ab8b2c37bb9139eb6800631eece9b60ee25a +Subproject commit cdb956eab2cc70bebb94803486e96230e83c465e diff --git a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs index 4ae1b9c..2d9438d 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs @@ -19,6 +19,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; +using MBS.Core; + namespace Mocha.Core { public struct InstanceKey @@ -29,7 +31,25 @@ namespace Mocha.Core private bool _isNotEmpty; public bool IsEmpty { get { return !_isNotEmpty; } } - public bool IsDerived { get; } + public bool IsDerived { get { return _derivedDataString != null; } } + + internal Oms? _oms; + internal InstanceHandle _inst; + internal InstanceHandle _parentClassInstance; + private string? _derivedDataString = null; + internal void SetDerivedData(Oms oms, InstanceHandle inst, Dictionary derivedData) + { + _oms = oms; + _inst = inst; + _parentClassInstance = oms.GetParentClass(inst); + + Dictionary _derivedData = new Dictionary(); + foreach (KeyValuePair kvp in derivedData) + { + _derivedData[kvp.Key] = kvp.Value; + } + _derivedDataString = GetDerivedDataString(_derivedData); + } public static readonly InstanceKey Empty = new InstanceKey(); @@ -58,7 +78,6 @@ namespace Mocha.Core { ClassIndex = 0; InstanceIndex = 0; - IsDerived = false; _isNotEmpty = false; } else @@ -67,32 +86,50 @@ namespace Mocha.Core if (instanceKey.Contains("$")) { splitChar = '$'; - IsDerived = false; } else if (instanceKey.Contains("!")) { splitChar = '!'; - IsDerived = true; } if (splitChar != '\0') { - string[] split = instanceKey.Split(new char[] { '$' }); + string[] split = instanceKey.Split(new char[] { splitChar }); if (split.Length == 2) { - if (Int32.TryParse(split[0], out int ci) && Int32.TryParse(split[1], out int ii)) + if (splitChar == '!') { - ClassIndex = ci; - InstanceIndex = ii; - _isNotEmpty = true; + if (Int32.TryParse(split[0], out int ci)) + { + ClassIndex = ci; + InstanceIndex = -1; + SetDerivedDataString(split[1]); + _isNotEmpty = true; + } return; } + else + { + if (Int32.TryParse(split[0], out int ci) && Int32.TryParse(split[1], out int ii)) + { + ClassIndex = ci; + InstanceIndex = ii; + _isNotEmpty = true; + return; + } + } } } } throw new ArgumentException("must be a string containing two integers separated by a '$' or a '!'"); } - public InstanceKey(int classIndex, int instanceIndex) + + private void SetDerivedDataString(string base64String) + { + _derivedDataString = base64String; + } + + public InstanceKey(int classIndex, int instanceIndex) { ClassIndex = classIndex; InstanceIndex = instanceIndex; @@ -116,9 +153,70 @@ namespace Mocha.Core { if (IsDerived) { - return String.Format("{0}!{1}", ClassIndex, InstanceIndex); + return String.Format("{0}!{1}", ClassIndex, _derivedDataString); } return String.Format("{0}${1}", ClassIndex, InstanceIndex); } - } + + internal string GetDerivedDataString(Dictionary? derivedData = null) + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + + if (derivedData == null) + { + derivedData = GetDerivedData(); + } + + foreach (KeyValuePair kvp in derivedData) + { + if (kvp.Value is string) + { + string value = (string)kvp.Value; + bw.Write((int)value.Length); + bw.Write((string)value); + } + } + + bw.Flush(); + bw.Close(); + byte[] data = ms.ToArray(); + + string b64 = Convert.ToBase64String(data); + return b64; + } + + internal Dictionary? GetDerivedData() + { + Dictionary derivedData = new Dictionary(); + + InstanceKey ikParentClass = new InstanceKey(1, ClassIndex); + _parentClassInstance = _oms.GetInstance(ikParentClass); + + if (_derivedDataString != null) + { + byte[] data = Convert.FromBase64String(_derivedDataString); + MemoryStream ms = new MemoryStream(data); + BinaryReader br = new BinaryReader(ms); + + IEnumerable attributes = _oms.GetRelatedInstances(_parentClassInstance, _oms.GetInstance(KnownRelationshipGuids.Class__has__Attribute)); + foreach (InstanceHandle att in attributes) + { + if (_oms.IsInstanceOf(att, _oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) + { + if (br.BaseStream.EndOfStream()) + { + break; + } + + int length = br.ReadInt32(); + string value = br.ReadString(); + derivedData[att] = value; + } + } + } + + return derivedData; + } + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/KnownAttributeGuids.cs b/mocha-dotnet/src/lib/Mocha.Core/KnownAttributeGuids.cs index b82073c..b713f57 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/KnownAttributeGuids.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/KnownAttributeGuids.cs @@ -60,6 +60,7 @@ namespace Mocha.Core public static Guid UseAnyCondition { get; } = new Guid("{31a8a2c2-1f55-4dfe-b177-427a2219ef8c}"); public static Guid ValidateOnlyOnSubmit { get; } = new Guid("{400fcd8e-823b-4f4a-aa38-b444f763259b}"); public static Guid UserIsLoggedIn { get; } = new Guid("{8e93d9f3-a897-4c97-935c-b3427f90633b}"); + public static Guid Derived { get; } = new Guid("{66991ca1-ef08-4f30-846c-4984c2a3139d}"); } public static class Numeric { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index 19cdcba..da9156d 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -301,6 +301,15 @@ public abstract class Oms { return new InstanceKey(1, 1); } + if (IsDerivedInstance(instance)) + { + InstanceHandle parent = GetParentClass(instance); + InstanceKey ikParent = GetInstanceKeyInternal(parent); + + InstanceKey ik = new InstanceKey(ikParent.InstanceIndex, -1); + ik.SetDerivedData(this, instance, GetDerivedData(instance)); + return ik; + } return GetInstanceKeyInternal(instance); } @@ -388,8 +397,42 @@ public abstract class Oms } throw new KeyNotFoundException(String.Format("inst not found: {0}", globalIdentifier)); } + + private Dictionary _derivedInstances = new Dictionary(); public InstanceHandle GetInstance(InstanceKey ik) { + if (ik.IsDerived) + { + ik._oms = this; + InstanceHandle inst; + if (!_derivedInstances.ContainsKey(ik.ToString())) + { + InstanceHandle pclass = GetInstance(new InstanceKey(1, ik.ClassIndex)); + + inst = InstanceHandle.Create(); + _derivedInstances[ik.ToString()] = inst; + + SetParentClass(inst, pclass); + ik._inst = inst; + ik._parentClassInstance = pclass; + + _derivedData[inst] = new Dictionary(); + IEnumerable>? kvps = ik.GetDerivedData(); + if (kvps != null) + { + foreach (KeyValuePair kvp in kvps) + { + _derivedData[inst][kvp.Key] = kvp.Value; + } + } + } + else + { + inst = _derivedInstances[ik.ToString()]; + } + return inst; + } + if (TryGetInstance(ik, out InstanceHandle ih)) { return ih; @@ -584,8 +627,43 @@ public abstract class Oms value = default(T); return false; } - public T GetAttributeValue(InstanceHandle source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null) + + private Dictionary?> _derivedData = new Dictionary?>(); + public Dictionary? GetDerivedData(InstanceHandle source) { + if (IsDerivedInstance(source)) + { + if (!_derivedData.ContainsKey(source)) + { + _derivedData[source] = new Dictionary(); + } + return _derivedData[source]; + } + return null; + } + + public bool IsDerivedInstance(InstanceHandle source) + { + // !!! we cannot use GetAttributeValue here !!! + InstanceHandle pclass = GetParentClass(source); + object? o = GetAttributeValueInternal(pclass, GetInstance(KnownAttributeGuids.Boolean.Derived), DateTime.Now); + if (o is bool b && b) + { + return true; + } + return false; + } + + public T GetAttributeValue(InstanceHandle source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null) + { + Dictionary? derivedData = GetDerivedData(source); + if (derivedData != null) + { + if (derivedData.ContainsKey(attribute)) + { + return (T)derivedData[attribute]; + } + } if (TryGetAttributeValue(source, attribute, effectiveDate, out T value)) { return value; @@ -596,6 +674,16 @@ public abstract class Oms public void SetAttributeValue(InstanceHandle source, InstanceHandle attribute, object value, DateTime? effectiveDate = null) { ValidateConstraintsForAttribute(source, attribute, value); + + if (IsDerivedInstance(source)) + { + if (!_derivedData.ContainsKey(source)) + { + _derivedData[source] = new Dictionary(); + } + _derivedData[source][attribute] = value; + return; + } SetAttributeValueInternal(source, attribute, value, effectiveDate.GetValueOrDefault(DateTime.Now)); } diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs new file mode 100644 index 0000000..0455344 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs @@ -0,0 +1,54 @@ +using System; +using Newtonsoft.Json.Serialization; + +namespace Mocha.Core.Tests; + +public class DerivedInstanceTests : OmsTestsBase +{ + InstanceHandle TEST_CLASS_DERIVED; + protected override void AfterSetup() + { + base.AfterSetup(); + + TEST_CLASS_DERIVED = Oms.CreateClass("Test Class (Derived)"); + Oms.SetAttributeValue(TEST_CLASS_DERIVED, Oms.GetInstance(KnownAttributeGuids.Boolean.Derived), true); + + // Derived classes are represented only by InstanceKeys with the form {ClassId}!{DerivedData}, where + // {DerivedData} is a Base64 string encoding all the attributes and relationships associated with the + // derived instance. + + // ? Should we be concerned with memory leaks given the lazy implementation of GetInstance on a derived + // ? InstanceKey essentially allocates a new InstanceHandle each time it's called ??? + Oms.AddAttribute(TEST_CLASS_DERIVED, Oms.GetInstance(KnownAttributeGuids.Text.Name)); + } + + const string TEST_DERIVED_VALUE = "Test Class Derived #1"; + + [Test] + public void Derived_Instance_Test_get_Text_Attribute_Value() + { + InstanceHandle iTestClassDerived = Oms.CreateInstanceOf(TEST_CLASS_DERIVED); + Oms.SetAttributeValue(iTestClassDerived, Oms.GetInstance(KnownAttributeGuids.Text.Name), TEST_DERIVED_VALUE); + + InstanceKey ik = Oms.GetInstanceKey(iTestClassDerived); + + InstanceHandle h2 = Oms.GetInstance(ik); + Dictionary? dict = Oms.GetDerivedData(h2); + + string attVName = Oms.GetAttributeValue(h2, Oms.GetInstance(KnownAttributeGuids.Text.Name)); + Assert.That(attVName, Is.EqualTo(TEST_DERIVED_VALUE)); + } + + [Test] + public void Derived_Instance_Test__instance_key_Parse() + { + string ikStr = "154!FQAAABVUZXN0IENsYXNzIERlcml2ZWQgIzE="; + InstanceKey ik = InstanceKey.Parse(ikStr); + + InstanceHandle ih = Oms.GetInstance(ik); + + string attVName = Oms.GetAttributeValue(ih, Oms.GetInstance(KnownAttributeGuids.Text.Name)); + Assert.That(attVName, Is.EqualTo(TEST_DERIVED_VALUE)); + } + +} From e574830852844d498da9be6250a72a814234af93 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:19:09 -0500 Subject: [PATCH 05/18] update mocha-common --- mocha-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocha-common b/mocha-common index cdb956e..7760508 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit cdb956eab2cc70bebb94803486e96230e83c465e +Subproject commit 7760508bad6f9dbd94c70ca1fb3a5fb6a3838eb4 From 92b757edc37d8ebc5aeaa4b64e98b1299618a920 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:21:14 -0500 Subject: [PATCH 06/18] add convenience function for derived instances so we don't have to allocate an InstanceHandle --- mocha-dotnet/src/lib/Mocha.Core/Oms.cs | 18 ++++++++++++++++++ .../Mocha.Core.Tests/DerivedInstanceTests.cs | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index da9156d..e1edfa4 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -654,6 +654,24 @@ public abstract class Oms return false; } + public T GetAttributeValue(InstanceKey source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null) + { + if (source.IsDerived) + { + source._oms = this; + Dictionary derivedData = source.GetDerivedData(); + if (derivedData.ContainsKey(attribute)) + { + return (T) derivedData[attribute]; + } + return defaultValue; + } + else + { + InstanceHandle hSource = GetInstance(source); + return GetAttributeValue(hSource, attribute, defaultValue, effectiveDate); + } + } public T GetAttributeValue(InstanceHandle source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null) { Dictionary? derivedData = GetDerivedData(source); diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs index 0455344..d38b49f 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs @@ -47,7 +47,7 @@ public class DerivedInstanceTests : OmsTestsBase InstanceHandle ih = Oms.GetInstance(ik); - string attVName = Oms.GetAttributeValue(ih, Oms.GetInstance(KnownAttributeGuids.Text.Name)); + string attVName = Oms.GetAttributeValue(ik, Oms.GetInstance(KnownAttributeGuids.Text.Name)); Assert.That(attVName, Is.EqualTo(TEST_DERIVED_VALUE)); } From 8cdc7730cff32869d0d76f8f376a3388c6ae5a88 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:21:51 -0500 Subject: [PATCH 07/18] not sure why this was here; we shouldn't be forcing singular returns for a nonsingular work set --- mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs index 1749816..52413e3 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs @@ -116,8 +116,7 @@ public class OmsContext } } } - - value = ((IEnumerable)value).FirstOrDefault(); + // value = ((IEnumerable)value).FirstOrDefault(); } else if (value is IEnumerable) { From 1cdfb21bddb40f8e1ddc38a488e0f3b8b886e109 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:22:35 -0500 Subject: [PATCH 08/18] provide the title of the page in the resulting JSON --- .../lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs index 18e772a..9f89b65 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs @@ -75,6 +75,15 @@ public class ElementCommand : InstanceCommand obj2.Add("widget", JsonValue.Create("root")); obj2.Add("body", widget.ToJSONObject(ctx)); + JsonObject objTitle = new JsonObject(); + string label = oms.GetAttributeValue(widget.ParentInstance, oms.GetInstance(KnownAttributeGuids.Text.Label)); + if (label == null) + { + label = oms.GetInstanceText(widget.ParentInstance); + } + objTitle.Add("label", label); + obj2.Add("title", objTitle); + JsonObject obj = new JsonObject(); obj.Add("result", "success"); obj.Add("value", obj2); From 7beeffd9ad96a0ff93a7c72ce65b11f26a5f37bd Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:23:28 -0500 Subject: [PATCH 09/18] support returning multiple instances from elements built by Build Element Methods --- .../src/lib/Mocha.Core/UI/Widgets/Element.cs | 102 ++++++++++-------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs index 687e469..e8cd10b 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs @@ -172,54 +172,64 @@ public class Element : Widget InstanceHandle workSet = OMS.Execute(context, loopExecutable); object? obj = context.GetWorkData(workSet); - if (obj is IEnumerable list) + + IEnumerable list; + if (obj is IEnumerable list2) { - JsonArray ary = new JsonArray(); - foreach (InstanceHandle ih in list) - { - JsonObject jo = new JsonObject(); - jo.Add("widget", "row"); - jo.Add("ecid", ""); - jo.Add("iid", ""); - - JsonObject cellMap = new JsonObject(); - int i = 1; - foreach (InstanceHandle ec in contents) - { - InstanceHandle ecinst = OMS.GetRelatedInstance(ec, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); - Widget parsed = Renderer.Parse(context, ecinst, ec, InstanceHandle.Empty, ih); - - JsonObject cell; - if (parsed == null) - { - cell = new JsonObject(); - cell.Add("widget", JsonValue.Create("error")); - cell.Add("text", JsonValue.Create("could not create EC")); - cell.Add("ecid", JsonValue.Create(OMS.GetInstanceKey(ec).ToString())); - cell.Add("iid", JsonValue.Create(OMS.GetInstanceKey(OMS.GetRelatedInstance(ec, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance))).ToString())); - } - else - { - cell = parsed.ToJSONObject(context); - if (disabled) - { - if (cell.ContainsKey("enabled")) - { - cell.Remove("enabled"); - } - cell.Add("enabled", false); - } - } - - cellMap.Add(String.Format("{0}.{1}", gridId, i), cell); - i++; - } - jo.Add("cellsMap", cellMap); - ary.Add(jo); - } - return ary; + list = list2; } + else if (obj is InstanceHandle) + { + list = new InstanceHandle[] { (InstanceHandle)obj }; + } + else + { + list = new InstanceHandle[0]; + } + + JsonArray ary = new JsonArray(); + foreach (InstanceHandle ih in list) + { + JsonObject jo = new JsonObject(); + jo.Add("widget", "row"); + jo.Add("ecid", ""); + jo.Add("iid", ""); - return null; + JsonObject cellMap = new JsonObject(); + int i = 1; + foreach (InstanceHandle ec in contents) + { + InstanceHandle ecinst = OMS.GetRelatedInstance(ec, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + Widget parsed = Renderer.Parse(context, ecinst, ec, InstanceHandle.Empty, ih); + + JsonObject cell; + if (parsed == null) + { + cell = new JsonObject(); + cell.Add("widget", JsonValue.Create("error")); + cell.Add("text", JsonValue.Create("could not create EC")); + cell.Add("ecid", JsonValue.Create(OMS.GetInstanceKey(ec).ToString())); + cell.Add("iid", JsonValue.Create(OMS.GetInstanceKey(OMS.GetRelatedInstance(ec, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance))).ToString())); + } + else + { + cell = parsed.ToJSONObject(context); + if (disabled) + { + if (cell.ContainsKey("enabled")) + { + cell.Remove("enabled"); + } + cell.Add("enabled", false); + } + } + + cellMap.Add(String.Format("{0}.{1}", gridId, i), cell); + i++; + } + jo.Add("cellsMap", cellMap); + ary.Add(jo); + } + return ary; } } From d4b41e14ff94163210e17cae5b059a6117ff18bd Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:23:54 -0500 Subject: [PATCH 10/18] preliminary implementation of preview widgets and related tasks in the .NET OMS --- .../Commands/InstanceDetailsCommand.cs | 2 +- .../Commands/InstancePreviewCommand.cs | 135 ++++++++++++++++++ .../src/lib/Mocha.Core.UI.Server/OmsServer.cs | 4 + 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceDetailsCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceDetailsCommand.cs index 56c8b6b..9b40c82 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceDetailsCommand.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceDetailsCommand.cs @@ -23,7 +23,7 @@ namespace Mocha.Core.UI.Server.Commands; public class InstanceDetailsCommand : InstanceCommand { - public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{instanceKey}" }; + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{iid}" }; protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) { JsonObject obj = JsonRenderer.InstanceToJson(oms, ProcessingInstance); diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs new file mode 100644 index 0000000..6b64c77 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs @@ -0,0 +1,135 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of Mocha.NET. +// +// Mocha.NET is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Mocha.NET is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Mocha.NET. If not, see . + +using System.Text.Json.Nodes; +using MBS.Web; +using Mocha.Core; + +namespace Mocha.Core.UI.Server.Commands; + +public class InstancePreviewCommand : InstanceCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{iid}/preview" }; + + private struct RelatedTask + { + public InstanceKey InstanceKey; + public string Label; + } + + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + JsonObject obj = new JsonObject(); + obj.Add("result", "success"); + + JsonObject objPreview = new JsonObject(); + objPreview.Add("widget", "root"); + obj.Add("preview", objPreview); + + JsonObject objRelatedTasks = new JsonObject(); + objRelatedTasks.Add("widget", "relatedTasks"); + + Dictionary> relatedTasks = new Dictionary>(); + + InstanceHandle parentClass = oms.GetParentClass(ProcessingInstance); + IEnumerable relatedTasksIH = oms.GetRelatedInstances(parentClass, oms.GetInstance(KnownRelationshipGuids.Class__has_related__Task)); + foreach (InstanceHandle ih in relatedTasksIH) + { + InstanceHandle taskCategory = oms.GetRelatedInstance(ih, oms.GetInstance(KnownRelationshipGuids.Task__has__Task_Category)); + string categoryTitle = oms.GetInstanceText(taskCategory); + if (!relatedTasks.ContainsKey(categoryTitle)) + { + relatedTasks[categoryTitle] = new List(); + } + + relatedTasks[categoryTitle].Add(new RelatedTask() { InstanceKey = oms.GetInstanceKey(ih), Label = oms.GetInstanceText(ih) }); + } + + /* + + relatedTasks["Preferences"] = new List(); + relatedTasks["Preferences"].Add(new RelatedTask() { InstanceKey = new InstanceKey(1924, 264), Label = "Export Preferences" }); + + relatedTasks["System Account"] = new List(); + */ + + InstanceKey ik = oms.GetInstanceKey(ProcessingInstance); + + JsonArray aryTaskGroups = new JsonArray(); + foreach (KeyValuePair> kvp in relatedTasks) + { + JsonObject objTaskGroup = new JsonObject(); + objTaskGroup.Add("widget", "relatedTaskGroup"); + objTaskGroup.Add("renderDifferently", false); + + JsonArray aryTaskGroups1 = new JsonArray(); + { + JsonObject objTaskGroup1 = new JsonObject(); + objTaskGroup1.Add("widget", "relatedTaskGroup"); + objTaskGroup1.Add("label", kvp.Key); + objTaskGroup1.Add("renderDifferently", false); + + JsonArray aryTaskGroups2 = new JsonArray(); + foreach (RelatedTask task in kvp.Value) + { + JsonObject objTaskGroup2 = new JsonObject(); + objTaskGroup2.Add("widget", "relatedTaskGroup"); + + JsonArray aryTasks = new JsonArray(); + JsonObject objTask = new JsonObject(); + objTask.Add("widget", "relatedTask"); + objTask.Add("uri", String.Format("/{0}/inst/{1}/rel-task/{2}", "super", ik, task.InstanceKey)); + objTask.Add("view", true); + objTask.Add("webService", false); + objTask.Add("iid", task.InstanceKey.ToString()); + objTask.Add("label", task.Label); + aryTasks.Add(objTask); + + objTaskGroup2.Add("tasks", aryTasks); + objTaskGroup2.Add("renderDifferently", false); + + aryTaskGroups2.Add(objTaskGroup2); + } + + objTaskGroup1.Add("taskGroups", aryTaskGroups2); + aryTaskGroups1.Add(objTaskGroup1); + } + + objTaskGroup.Add("taskGroups", aryTaskGroups1); + + aryTaskGroups.Add(objTaskGroup); + } + + objRelatedTasks.Add("taskGroups", aryTaskGroups); + + JsonObject objInstance = new JsonObject(); + objInstance.Add("instanceId", ik.ToString()); + objInstance.Add("label", oms.GetInstanceText(parentClass)); + objRelatedTasks.Add("instance", objInstance); + + obj.Add("relatedTasks", objRelatedTasks); + + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + sw.Write(obj.ToJsonString()); + } + + +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs index 4b64beb..151865b 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs @@ -170,6 +170,10 @@ public class OmsServer private IEnumerable Flatten(IEnumerable cmds) { + // !FIXME! this returns weird results if the length of the one string is longer + // ! ! INCLUDING length of {variable} names, when these should be replaced + // ! ! by a single char '?' (like in Phast) to avoid this issue + List list = new List(); foreach (OmsServerCommand cmd in cmds) { From f30c87dc8fb351215b22343f3ddc20f555c266d4 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 21 Dec 2024 00:29:48 -0500 Subject: [PATCH 11/18] also show related tasks for `Instance` ; i.e., all instances regardless of parent class --- .../Commands/InstancePreviewCommand.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs index 6b64c77..8b4add4 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstancePreviewCommand.cs @@ -46,19 +46,14 @@ public class InstancePreviewCommand : InstanceCommand Dictionary> relatedTasks = new Dictionary>(); InstanceHandle parentClass = oms.GetParentClass(ProcessingInstance); + + InstanceHandle c_Instance = oms.GetInstance(KnownInstanceGuids.Classes.Instance); + IEnumerable relatedTasksINSTANCE = oms.GetRelatedInstances(c_Instance, oms.GetInstance(KnownRelationshipGuids.Class__has_related__Task)); + IEnumerable relatedTasksIH = oms.GetRelatedInstances(parentClass, oms.GetInstance(KnownRelationshipGuids.Class__has_related__Task)); - foreach (InstanceHandle ih in relatedTasksIH) - { - InstanceHandle taskCategory = oms.GetRelatedInstance(ih, oms.GetInstance(KnownRelationshipGuids.Task__has__Task_Category)); - string categoryTitle = oms.GetInstanceText(taskCategory); - if (!relatedTasks.ContainsKey(categoryTitle)) - { - relatedTasks[categoryTitle] = new List(); - } - - relatedTasks[categoryTitle].Add(new RelatedTask() { InstanceKey = oms.GetInstanceKey(ih), Label = oms.GetInstanceText(ih) }); - } - + AddRelatedTasks(oms, relatedTasks, relatedTasksINSTANCE); + AddRelatedTasks(oms, relatedTasks, relatedTasksIH); + /* relatedTasks["Preferences"] = new List(); @@ -131,5 +126,18 @@ public class InstancePreviewCommand : InstanceCommand sw.Write(obj.ToJsonString()); } + private void AddRelatedTasks(Oms oms, Dictionary> relatedTasks, IEnumerable relatedTasksIH) + { + foreach (InstanceHandle ih in relatedTasksIH) + { + InstanceHandle taskCategory = oms.GetRelatedInstance(ih, oms.GetInstance(KnownRelationshipGuids.Task__has__Task_Category)); + string categoryTitle = oms.GetInstanceText(taskCategory); + if (!relatedTasks.ContainsKey(categoryTitle)) + { + relatedTasks[categoryTitle] = new List(); + } + relatedTasks[categoryTitle].Add(new RelatedTask() { InstanceKey = oms.GetInstanceKey(ih), Label = oms.GetInstanceText(ih) }); + } + } } \ No newline at end of file From 70bfea6b54a2b7db81a648427bf2824f1562f38f Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 17:00:22 -0500 Subject: [PATCH 12/18] update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4bbb548..8e1b336 100644 --- a/.gitignore +++ b/.gitignore @@ -418,3 +418,6 @@ FodyWeavers.xsd # Mocha CUP output directory output +# Backups (made by Notepad++) +*.bak + From c0932c6cb86396cc849e67f8d7a371815281a964 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 17:00:51 -0500 Subject: [PATCH 13/18] add documentation comment --- mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs | 4 ++++ mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs index 4708264..fe7c346 100644 --- a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs @@ -23,6 +23,10 @@ using System.Reflection; using Mocha.Core.UI.Server; +/// +/// Provides the entry point for a simple application which starts a Web server +/// and provisions one or more Mocha tenants from a configuration file. +/// public class Program : WebApplication { public class OmsSession diff --git a/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs b/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs index ef412f8..d41b534 100644 --- a/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs +++ b/mocha-dotnet/src/app/Mocha.ServerApplication/Program.cs @@ -26,6 +26,9 @@ using Mocha.Core.Oop.Methods; namespace Mocha.ServerApplication; +/// Provides the entry point for a simple application which starts a Web server +/// and provisions one or more Mocha tenants from a configuration file. +/// public class Program : MochaWebApplication { protected override int DefaultPort => 10020; From df25f1cac01b35e9bc901efae15f920be87c79d7 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 22:26:33 -0500 Subject: [PATCH 14/18] update mocha-common --- mocha-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocha-common b/mocha-common index 7760508..569e302 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit 7760508bad6f9dbd94c70ca1fb3a5fb6a3838eb4 +Subproject commit 569e302f3000e236ba562f17bc83d9e70a54fbf5 From c4ea69407bb4fb616b893c63f1ffcaeadad2f1d4 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 22:28:40 -0500 Subject: [PATCH 15/18] fix some cross-platform issues on Windows --- .../src/app/Mocha.Oms.Server/Program.cs | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs index fe7c346..3be68a3 100644 --- a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs @@ -13,6 +13,7 @@ using System.Text.Json.Nodes; using Mocha.Core.UI; using Mocha.Core.Oop; using MBS.Core; + using MBS.Core.Collections.Generic; using System.Collections.ObjectModel; @@ -57,7 +58,7 @@ public class Program : WebApplication } protected override void OnStartup(EventArgs e) - { + { string pidfile = "/var/run/mocha/mocha-oms.pid"; try { @@ -74,22 +75,39 @@ public class Program : WebApplication TenantHandle th = oms.CreateTenant("super"); oms.SelectTenant(th); - string path = "/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-common/mocha-common/output"; - if (!System.IO.Directory.Exists(path)) + string path; + if (System.Environment.OSVersion.Platform == PlatformID.Win32NT) { - path = "/usr/share/mocha/system"; + path = System.IO.Path.Combine(new string[] + { + System.Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), + "Mocha" + }); + } + else + { + path = "/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-common/mocha-common/output"; if (!System.IO.Directory.Exists(path)) { - Console.Error.WriteLine("path not found: " + path); + path = "/usr/share/mocha/system"; } } - LibraryHandle lh = oms.LoadLibrary(path + "/net.alcetech.Mocha.System.mcl"); - oms.AddLibraryReference(lh); - - LibraryHandle lh2 = oms.LoadLibrary(path + "/net.alcetech.Mocha.Web.mcl"); - oms.AddLibraryReference(lh2); + if (!System.IO.Directory.Exists(path)) + { + Console.Error.WriteLine("path not found: " + path); + return; + } + if (!TryLoadLibrary(oms, path, "net.alcetech.Mocha.System.mcl")) + { + return; + } + if (!TryLoadLibrary(oms, path, "net.alcetech.Mocha.Web.mcl")) + { + return; + } + // implement functions to get user IP address oms.RegisterSystemAttributeRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetIPAddress, delegate (Oms oms2, OmsContext ctx2) { @@ -100,7 +118,26 @@ public class Program : WebApplication // now we can start the Web server base.OnStartup(e); - } + } + + private bool TryLoadLibrary(Oms oms, string path, string fileName) + { + string fullyQualifiedFileName = System.IO.Path.Combine(new string[] { path, fileName }); + if (!System.IO.File.Exists(fullyQualifiedFileName)) + { + Console.WriteLine("file not found: {0}", fullyQualifiedFileName); + return false; + } + + LibraryHandle lh = oms.LoadLibrary(fullyQualifiedFileName); + if (lh == LibraryHandle.Empty) + { + return false; + } + + oms.AddLibraryReference(lh); + return true; + } private void oms_SystemRoutineExecuted(object? sender, SystemRoutineExecutedEventArgs e) { From 65ccf405d73ff3aec8d3414a2c07e98fd571b01a Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 22:31:00 -0500 Subject: [PATCH 16/18] add ctor(Type) --- .../lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs index 415649d..b3b837a 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs @@ -20,4 +20,8 @@ namespace Mocha.Core.Modeling; public class OmsRelationshipTargetAttribute : OmsNativeRelationship { public OmsRelationshipTargetAttribute(string targetClassGlobalIdentifier) : base(KnownRelationshipGuids.Relationship__has_destination__Class, targetClassGlobalIdentifier) { } + public OmsRelationshipTargetAttribute(Type targetClassType) : base(KnownRelationshipGuids.Relationship__has_destination__Class, null) + { + + } } \ No newline at end of file From 07ed23ecd8a4dde18e59491646993b100f7d8245 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 22:32:22 -0500 Subject: [PATCH 17/18] not sure if this works or not --- .../ImplicitAttributes/TextAttribute.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs new file mode 100644 index 0000000..6c0c71c --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs @@ -0,0 +1,27 @@ +namespace Mocha.Core.Modeling.ImplicitAttributes; + +public class TextAttribute +{ + private OmsDatabase omsdb; + public TextAttribute(OmsDatabase omsdb) + { + this.omsdb = omsdb; + } + + private InstanceHandle sourceInstance; + private InstanceHandle attributeInstance; + + private string GetAttributeValueStr() + { + return omsdb.Oms.GetAttributeValue(sourceInstance, attributeInstance); + } + + public static implicit operator string(TextAttribute source) + { + return source.GetAttributeValueStr(); + } + public static implicit operator TextAttribute(string source) + { + return null; + } +} \ No newline at end of file From 66c5ede6e0432c88a1a16a2a33b61c6a212afe57 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sun, 22 Dec 2024 22:33:12 -0500 Subject: [PATCH 18/18] remove assets folder which should be symlinked on demand depending on the OS --- mocha-dotnet/src/app/Mocha.ServerApplication/assets | 1 - 1 file changed, 1 deletion(-) delete mode 120000 mocha-dotnet/src/app/Mocha.ServerApplication/assets diff --git a/mocha-dotnet/src/app/Mocha.ServerApplication/assets b/mocha-dotnet/src/app/Mocha.ServerApplication/assets deleted file mode 120000 index 17d0247..0000000 --- a/mocha-dotnet/src/app/Mocha.ServerApplication/assets +++ /dev/null @@ -1 +0,0 @@ -/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-dotnet/src/app/Mocha.ServerApplication/bin/Debug/net8.0/assets \ No newline at end of file