From 114687f67f63a6d58429e92da3fc6fd31fbc9450 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Tue, 19 Nov 2024 22:36:24 -0500 Subject: [PATCH] major updates to .NET OMS server --- mocha-dotnet.sln | 19 +- .../Mocha.Oms.Server/Mocha.Oms.Server.csproj | 1 + .../src/app/Mocha.Oms.Server/Program.cs | 661 +++--------------- .../Commands/AttributesCommand.cs | 83 +++ .../Commands/CreateInstanceCommand.cs | 69 ++ .../Commands/ElementCommand.cs | 221 ++++++ .../Commands/InstanceCommand.cs | 73 ++ .../Commands/InstanceDetailsCommand.cs | 40 ++ .../Commands/InstanceListCommand.cs | 169 +++++ .../Commands/PingCommand.cs | 41 ++ .../Commands/RelatonshipsCommand.cs | 120 ++++ .../Commands/TenantedCommand.cs | 55 ++ .../Commands/TenantsListCommand.cs | 111 +++ .../Mocha.Core.UI.Server.csproj | 14 + .../src/lib/Mocha.Core.UI.Server/OmsServer.cs | 239 +++++++ .../Mocha.Core.UI.Server/OmsServerCommand.cs | 43 ++ .../src/lib/Mocha.Core/InstanceKey.cs | 46 +- .../src/lib/Mocha.Core/KnownInstanceGuids.cs | 33 +- .../lib/Mocha.Core/KnownRelationshipGuids.cs | 24 +- .../AssignAttributeMethodImplementation.cs | 18 +- .../BuildAttributeMethodImplementation.cs | 28 +- ...teBooleanExpressionMethodImplementation.cs | 15 +- .../GetAttributeMethodImplementation.cs | 5 + .../GetInstancesMethodImplementation.cs | 39 ++ ...ReferencedAttributeMethodImplementation.cs | 8 +- ...ferencedInstanceSetMethodImplementation.cs | 15 +- .../GetRelationshipMethodImplementation.cs | 2 +- ...ocessRelatedUpdatesMethodImplementation.cs | 34 + ...dateBySystemRoutineMethodImplementation.cs | 36 + mocha-dotnet/src/lib/Mocha.Core/Oms.cs | 337 ++++++++- mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs | 93 +-- .../lib/Mocha.Core/OmsSystemRoutineBuilder.cs | 6 +- .../Mocha.Core/Oop/DeferredInstanceWrapper.cs | 7 +- .../Methods/UpdateBySystemRoutineMethod.cs | 25 + .../Oop/SystemRoutines/SystemUpdateRoutine.cs | 34 + mocha-dotnet/src/lib/Mocha.Core/Response.cs | 30 + .../Mocha.Core/Responses/FailureResponse.cs | 41 ++ .../lib/Mocha.Core/Responses/PageResponse.cs | 35 + .../Mocha.Core/Responses/RedirectResponse.cs | 38 + .../Mocha.Core/SystemRoutineExecutedEvent.cs | 36 + .../Mocha.Core/SystemRoutineExecutingEvent.cs | 37 + .../src/lib/Mocha.Core/UI/JsonRenderer.cs | 141 ++++ .../src/lib/Mocha.Core/UI/Renderer.cs | 131 +++- mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs | 38 +- .../src/lib/Mocha.Core/UI/Widgets/Element.cs | 188 ++++- .../src/lib/Mocha.Core/UI/Widgets/File.cs | 2 +- .../lib/Mocha.Core/UI/Widgets/MonikerList.cs | 31 +- .../src/lib/Mocha.Core/UI/Widgets/Text.cs | 33 +- 48 files changed, 2819 insertions(+), 726 deletions(-) create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/AttributesCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/CreateInstanceCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceDetailsCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceListCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/PingCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/RelatonshipsCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantedCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantsListCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/Mocha.Core.UI.Server.csproj create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServerCommand.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstancesMethodImplementation.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/UpdateBySystemRoutineMethodImplementation.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Oop/Methods/UpdateBySystemRoutineMethod.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Oop/SystemRoutines/SystemUpdateRoutine.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Response.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Responses/FailureResponse.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Responses/PageResponse.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/Responses/RedirectResponse.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutedEvent.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutingEvent.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/UI/JsonRenderer.cs diff --git a/mocha-dotnet.sln b/mocha-dotnet.sln index 7186d3e..a4ad55f 100644 --- a/mocha-dotnet.sln +++ b/mocha-dotnet.sln @@ -41,7 +41,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{B024 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Plugins.Libraries.McxMini", "mocha-dotnet\src\plugins\Mocha.Plugins.Libraries.McxMini\Mocha.Plugins.Libraries.McxMini.csproj", "{6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Oms.Server", "mocha-dotnet\src\app\Mocha.Oms.Server\Mocha.Oms.Server.csproj", "{07828A24-B888-4464-A158-F77AB61F6AD9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Oms.Server", "mocha-dotnet\src\app\Mocha.Oms.Server\Mocha.Oms.Server.csproj", "{EB83EE7A-AFFE-4068-B445-9139F2DEA0F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Core.UI.Server", "mocha-dotnet\src\lib\Mocha.Core.UI.Server\Mocha.Core.UI.Server.csproj", "{4D1E2156-B1B5-4D15-B347-1F0E07C95891}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -73,10 +75,14 @@ Global {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Release|Any CPU.Build.0 = Release|Any CPU - {07828A24-B888-4464-A158-F77AB61F6AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07828A24-B888-4464-A158-F77AB61F6AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07828A24-B888-4464-A158-F77AB61F6AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07828A24-B888-4464-A158-F77AB61F6AD9}.Release|Any CPU.Build.0 = Release|Any CPU + {EB83EE7A-AFFE-4068-B445-9139F2DEA0F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB83EE7A-AFFE-4068-B445-9139F2DEA0F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB83EE7A-AFFE-4068-B445-9139F2DEA0F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB83EE7A-AFFE-4068-B445-9139F2DEA0F6}.Release|Any CPU.Build.0 = Release|Any CPU + {4D1E2156-B1B5-4D15-B347-1F0E07C95891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D1E2156-B1B5-4D15-B347-1F0E07C95891}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D1E2156-B1B5-4D15-B347-1F0E07C95891}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D1E2156-B1B5-4D15-B347-1F0E07C95891}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -98,7 +104,8 @@ Global {20B7D199-322C-4942-85FE-46B90C75E92A} = {27C300F5-5172-4225-A6F7-3503B9007DD8} {B024FD23-7084-4DDF-A185-D58BEE7A006F} = {66EB3261-A473-41C7-8D40-E1B4DC8ED4B3} {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF} = {B024FD23-7084-4DDF-A185-D58BEE7A006F} - {07828A24-B888-4464-A158-F77AB61F6AD9} = {11486802-8136-4958-8B32-FC34630B0306} + {EB83EE7A-AFFE-4068-B445-9139F2DEA0F6} = {11486802-8136-4958-8B32-FC34630B0306} + {4D1E2156-B1B5-4D15-B347-1F0E07C95891} = {A2C401E9-FED4-43BA-A928-566239894CEE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D28A9CF8-0235-4F8F-865F-C460BDCAE16D} diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj b/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj index 3c2342c..9958638 100644 --- a/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj @@ -5,6 +5,7 @@ + diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs index 7c9e528..55c3161 100644 --- a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs @@ -1,4 +1,4 @@ -namespace Mocha.Oms.Server; +namespace Mocha.OMS.Server; using Mocha.Core; @@ -11,9 +11,27 @@ using System.Text.Json; using System.Text.Json.Serialization; 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; +using System.Runtime.CompilerServices; +using Mocha.Core.Responses; +using System.Runtime.Serialization; +using System.Reflection; + +using Mocha.Core.UI.Server; public class Program : WebApplication { + public class OmsSession + { + + } + + public Dictionary _Sessions = new Dictionary(); + protected override int DefaultPort => 4436; public Program() @@ -37,6 +55,7 @@ public class Program : WebApplication protected override void OnStartup(EventArgs e) { // this all has to be done before the Web server is started... + oms.Initialize(); TenantHandle th = oms.CreateTenant("super"); oms.SelectTenant(th); @@ -57,407 +76,63 @@ public class Program : WebApplication LibraryHandle lh2 = oms.LoadLibrary(path + "/net.alcetech.Mocha.Web.mcl"); oms.AddLibraryReference(lh2); + // implement functions to get user IP address + oms.RegisterSystemAttributeRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetIPAddress, delegate (Oms oms2, OmsContext ctx2) + { + // !HACK + return ctx2.GetExtraData("Client.IPAddress"); + }); + oms.SystemRoutineExecuted += oms_SystemRoutineExecuted; + // now we can start the Web server base.OnStartup(e); } + private void oms_SystemRoutineExecuted(object? sender, SystemRoutineExecutedEventArgs e) + { + if (e.SystemRoutine.GlobalIdentifier == KnownInstanceGuids.SystemUpdateRoutines.LoginUser) + { + string token = e.Context.GetWorkData(e.OMS.GetInstance(KnownAttributeGuids.Text.Token)); + _Sessions[token] = new OmsSession(); + } + } + protected override void OnProcessRequest(WebServerProcessRequestEventArgs e) { base.OnProcessRequest(e); + OmsContext ctx = null; + if (!e.Context.Session.ContainsKey("OmsContext")) + { + ctx = oms.CreateContext(); + e.Context.Session["OmsContext"] = ctx; + } + ctx = (OmsContext)e.Context.Session["OmsContext"]; + + Guid clientRequestId = Guid.NewGuid(); + + // if (!e.Context.Request.Headers.ContainsKey("Cookie")) + { + // e.Context.Response.Cookies.Add("zq-sessionid", Guid.NewGuid().ToString("b"), WebCookieScope.Empty, WebCookieSecurity.None, WebCookieSameSite.None); + } + + if (e.Context.Request.Headers.ContainsKey("X-Forwarded-For")) + { + ctx.SetExtraData("Client.IPAddress", e.Context.Request.Headers["X-Forwarded-For"]); + } e.Context.Response.ResponseCode = 404; e.Context.Response.ResponseText = "Not Found"; - + StreamWriter sw = new StreamWriter(e.Context.Response.Stream); + if (e.Context.Request.Path == "/robots.txt") { } else if (e.Context.Request.PathParts.Length > 1) { - if (e.Context.Request.PathParts.Length == 2 && e.Context.Request.PathParts[1] == "ping") - { - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - JsonObject obj = new JsonObject(); - obj.Add("result", JsonValue.Create("success")); - sw.Write(obj.ToJsonString()); - - sw.Flush(); - sw.Close(); - return; - } - - if (e.Context.Request.PathParts[1] == "tenants") - { - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - JsonArray ary = new JsonArray(); - - IEnumerable tenants = oms.GetTenants(); - foreach (string tenant in tenants) - { - ary.Add(JsonValue.Create(tenant)); - } - - if (e.Context.Request.PathParts.Length == 2) - { - JsonObject obj = new JsonObject(); - obj.Add("result", JsonValue.Create("success")); - obj.Add("tenants", ary); - sw.Write(obj.ToJsonString()); - } - else if (e.Context.Request.PathParts.Length > 3) - { - // /tenants/{tenantName}/instances - string tenantName = e.Context.Request.PathParts[2]; - - TenantHandle th = oms.GetTenantByName(tenantName); - if (th == TenantHandle.Empty) - { - e.Context.Response.ResponseCode = 404; - e.Context.Response.ResponseText = "Not Found"; - e.Context.Response.ContentType = "application/json"; - - JsonObject obj = new JsonObject(); - obj.Add("result", JsonValue.Create("failure")); - obj.Add("message", JsonValue.Create("tenant does not exist")); - sw.Write(obj.ToJsonString()); - sw.Flush(); - return; - } - else - { - oms.SelectTenant(th); - } - - - if (e.Context.Request.PathParts[3] == "instances") - { - if (e.Context.Request.PathParts.Length > 4) - { - if (e.Context.Request.PathParts[4] == "create") - { - // usage: - // POST /tenants/{tenantName}/instances/create - // - // parentClassIid=1$1&title=My+New+Class - if (e.Context.Request.Form.ContainsKey("parentClassIid")) - { - string pclass = e.Context.Request.Form["parentClassIid"]; - if (TryParseInstanceRef(pclass, out InstanceHandle ihParentClass)) - { - InstanceHandle ihRet = oms.CreateInstanceOf(ihParentClass); - - if (e.Context.Request.Form.ContainsKey("title")) - { - oms.SetAttributeValue(ihRet, oms.GetInstance(KnownAttributeGuids.Text.Name), e.Context.Request.Form["title"]); - } - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - JsonObject obj = new JsonObject(); - obj.Add("result", JsonValue.Create("success")); - obj.Add("value", InstanceToJson(ihRet)); - sw.Write(obj.ToJsonString()); - } - else - { - e.Context.Response.ResponseCode = 422; - e.Context.Response.ResponseText = "Unprocessable Content"; - e.Context.Response.ContentType = "application/json"; - - JsonObject obj = new JsonObject(); - obj.Add("result", JsonValue.Create("failure")); - obj.Add("message", JsonValue.Create("parent class instance does not exist")); - sw.Write(obj.ToJsonString()); - } - } - } - else if (e.Context.Request.PathParts.Length >= 5) - { - // /tenants/{tenantName}/instances/{instanceKey}/... - TryParseInstanceRef(e.Context.Request.PathParts[4], out InstanceHandle h); - - if (h == InstanceHandle.Empty) - { - e.Context.Response.ResponseCode = 404; - e.Context.Response.ResponseText = "Not Found"; - e.Context.Response.ContentType = "application/json"; - - JsonObject obj = new JsonObject(); - obj.Add("result", "failure"); - obj.Add("message", String.Format("invalid inst id {0} (inst not found)", e.Context.Request.PathParts[4])); - sw.Write(obj.ToJsonString()); - - sw.Flush(); - sw.Close(); - return; - } - - if (e.Context.Request.PathParts.Length >= 6) - { - string command = e.Context.Request.PathParts[5]; - if (command == "attributes") - { - if (e.Context.Request.PathParts.Length > 7) - { - // /tenants/{tenantName}/instances/{iid}/attrbutes/{attkey}/current - TryParseInstanceRef(e.Context.Request.PathParts[6], out InstanceHandle att); - - string value; - if (e.Context.Request.Method == "POST" && e.Context.Request.PathParts[7].Equals("update")) - { - value = e.Context.Request.Form["value"]; - oms.SetAttributeValue(h, att, value); - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "text/plain"; - - sw.Write(value); - sw.Flush(); - sw.Close(); - return; - } - - DateTime dt = DateTime.Now; - if (e.Context.Request.PathParts[7].Equals("current")) - { - } - - value = oms.GetAttributeValue(h, att, String.Empty, dt); - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "text/plain"; - - sw.Write(value); - sw.Flush(); - sw.Close(); - return; - } - - JsonObject obj = new JsonObject(); - - InstanceHandle hAtt = InstanceHandle.Empty; - if (e.Context.Request.PathParts.Length == 7) - { - TryParseInstanceRef(e.Context.Request.PathParts[6], out hAtt); - } - - JsonArray objAttributes = GetAttributesAsJson(h, hAtt); - obj.Add("attributes", objAttributes); - - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - sw.Write(obj.ToJsonString()); - } - else if (command == "relationships") - { - if (e.Context.Request.PathParts.Length >= 7) - { - TryParseInstanceRef(e.Context.Request.PathParts[6], out InstanceHandle rel); - - DateTime dt = DateTime.Now; - if (e.Context.Request.PathParts.Length > 7) - { - if (e.Context.Request.Method == "POST" && e.Context.Request.PathParts[7].Equals("update")) - { - if (Int32.TryParse(e.Context.Request.Form["count"], out int count)) - { - JsonObject obj1 = new JsonObject(); - obj1.Add("result", "success"); - - JsonArray ary1 = new JsonArray(); - - List list = new List(); - for (int i = 0; i < count; i++) - { - string key = String.Format("item{0}", i); - if (e.Context.Request.Form.ContainsKey(key)) - { - Console.Error.WriteLine("debug: trying to add inst " + e.Context.Request.Form[key]); - if (TryParseInstanceRef(e.Context.Request.Form[key], out InstanceHandle inst)) - { - ary1.Add(InstanceToJson(inst)); - list.Add(inst); - } - } - } - - Console.Error.WriteLine("ok assigning relationship"); - oms.AssignRelationship(h, rel, list.ToArray()); - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - obj1.Add("targetInstances", ary1); - - sw.Write(obj1.ToJsonString()); - sw.Flush(); - sw.Close(); - } - - return; - } - - if (e.Context.Request.PathParts[7].Equals("current")) - { - } - } - - IEnumerable rel_insts = oms.GetRelatedInstances(h, rel, dt); - JsonObject obj = new JsonObject(); - - JsonArray a = new JsonArray(); - foreach (InstanceHandle ih in rel_insts) - { - a.Add(InstanceToJson(ih)); - } - - obj.Add("result", JsonValue.Create("success")); - obj.Add("sourceInstance", InstanceToJson(h)); - obj.Add("relationshipInstance", InstanceToJson(rel)); - obj.Add("value", a); - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - sw.Write(obj.ToJsonString()); - sw.Flush(); - sw.Close(); - return; - } - else - { - JsonObject obj = new JsonObject(); - obj.Add("relationships", GetRelationshipsAsJson(h)); - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - sw.Write(obj.ToJsonString()); - } - } - else if (command == "element") - { - Renderer renderer = new Renderer(oms); - if (oms.IsInstanceOf(h, oms.GetInstance(KnownInstanceGuids.Classes.Element))) - { - Widget widget = renderer.Parse(h); - JsonObject obj3 = widget.ToJSONObject(); - - JsonObject obj2 = new JsonObject(); - obj2.Add("widget", JsonValue.Create("root")); - obj2.Add("body", widget.ToJSONObject()); - - JsonObject obj = new JsonObject(); - obj.Add("result", "success"); - obj.Add("value", obj2); - RespondWithJson(sw, e.Context, 200, "OK", obj); - } - } - else - { - JsonObject obj = new JsonObject(); - obj.Add("result", "failure"); - obj.Add("message", String.Format("unknown cmd '{0}'", command)); - RespondWithJson(sw, e.Context, 404, "Not Found", obj); - } - } - else - { - JsonObject obj = new JsonObject(); - obj.Add("instanceKey", JsonValue.Create(oms.GetInstanceKey(h).ToString())); - obj.Add("globalIdentifier", JsonValue.Create(oms.GetGlobalIdentifier(h).ToString("b"))); - obj.Add("text", JsonValue.Create(oms.GetInstanceText(h))); - obj.Add("attributes", GetAttributesAsJson(h)); - obj.Add("relationshps", GetRelationshipsAsJson(h)); - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - sw.Write(obj.ToJsonString()); - } - } - } - else - { - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - JsonObject obj = new JsonObject(); - - Stopwatch watch = new Stopwatch(); - watch.Start(); - - string query = ""; - if (e.Context.Request.Query.ContainsKey("q") && e.Context.Request.Query["q"].Count > 0) - { - query = e.Context.Request.Query["q"][0]; - } - - string parentClassIid = null; - InstanceHandle ihParentClass = InstanceHandle.Empty; - if (e.Context.Request.Query.ContainsKey("parentClassIid") && e.Context.Request.Query["parentClassIid"].Count > 0) - { - parentClassIid = e.Context.Request.Query["parentClassIid"][0]; - TryParseInstanceRef(parentClassIid, out ihParentClass); - } - - IEnumerable ihs = null; - if (ihParentClass != InstanceHandle.Empty) - { - ihs = oms.GetInstancesOf(ihParentClass); - } - else - { - ihs = oms.GetInstances(); - } - - if (!String.IsNullOrEmpty(query)) - { - List list = new List(); - foreach (InstanceHandle ih in ihs) - { - string text = oms.GetInstanceText(ih); - if (text.ToLower().Contains(query.ToLower())) - { - list.Add(ih); - } - } - ihs = list; - } - - e.Context.Response.ResponseCode = 200; - e.Context.Response.ResponseText = "OK"; - e.Context.Response.ContentType = "application/json"; - - JsonArray objInstances = InstancesToJson(ihs); - obj.Add("instances", objInstances); - - watch.Stop(); - obj.Add("time", JsonValue.Create(watch.ElapsedMilliseconds.ToString())); - - sw.Write(obj.ToJsonString()); - } - } - } - } + OmsServer server = new OmsServer(); + server.ProcessRequest(e, oms, ctx, sw); } else { @@ -472,184 +147,56 @@ public class Program : WebApplication sw.Close(); } -/* - private JsonObject? ElementContentToJSON(InstanceHandle content) - { - JsonObject? objC = null; - InstanceHandle contentWidget = oms.GetRelatedInstance(content, oms.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + /* + private JsonObject? ElementContentToJSON(InstanceHandle content) + { + JsonObject? objC = null; + InstanceHandle contentWidget = oms.GetRelatedInstance(content, oms.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); - // Mocha.Core.UI.Widget widget = renderer.BuildFromInstance() - if (oms.IsInstanceOf(contentWidget, oms.GetInstance(KnownInstanceGuids.Classes.Element))) - { - objC = ElementToJSON(contentWidget, content); - } - else if (oms.IsInstanceOf(contentWidget, oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) - { - objC = TextAttributeToJSON(contentWidget, content); - } - else if (oms.IsInstanceOf(contentWidget, oms.GetInstance(KnownInstanceGuids.Classes.File))) - { - objC = FileToJSON(contentWidget, content); - } - return objC; - } + // Mocha.Core.UI.Widget widget = renderer.BuildFromInstance() + if (oms.IsInstanceOf(contentWidget, oms.GetInstance(KnownInstanceGuids.Classes.Element))) + { + objC = ElementToJSON(contentWidget, content); + } + else if (oms.IsInstanceOf(contentWidget, oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) + { + objC = TextAttributeToJSON(contentWidget, content); + } + else if (oms.IsInstanceOf(contentWidget, oms.GetInstance(KnownInstanceGuids.Classes.File))) + { + objC = FileToJSON(contentWidget, content); + } + return objC; + } - private JsonObject? FileToJSON(InstanceHandle contentWidget, InstanceHandle parentElementContent) - { - JsonObject obj3 = new JsonObject(); - InsertCommonProperties(obj3, contentWidget, parentElementContent); + private JsonObject? FileToJSON(InstanceHandle contentWidget, InstanceHandle parentElementContent) + { + JsonObject obj3 = new JsonObject(); + InsertCommonProperties(obj3, contentWidget, parentElementContent); - string value = oms.GetAttributeValue(contentWidget, oms.GetInstance(KnownAttributeGuids.Text.Value)); - if (value != null) - { - obj3.Add("value", JsonValue.Create(value)); - } - return obj3; - } - private JsonObject? TextAttributeToJSON(InstanceHandle contentWidget, InstanceHandle parentElementContent) - { - JsonObject obj3 = new JsonObject(); - InsertCommonProperties(obj3, contentWidget, parentElementContent); + string value = oms.GetAttributeValue(contentWidget, oms.GetInstance(KnownAttributeGuids.Text.Value)); + if (value != null) + { + obj3.Add("value", JsonValue.Create(value)); + } + return obj3; + } + private JsonObject? TextAttributeToJSON(InstanceHandle contentWidget, InstanceHandle parentElementContent) + { + JsonObject obj3 = new JsonObject(); + InsertCommonProperties(obj3, contentWidget, parentElementContent); - string value = oms.GetAttributeValue(contentWidget, oms.GetInstance(KnownAttributeGuids.Text.Value)); - if (value != null) - { - obj3.Add("value", JsonValue.Create(value)); - } - return obj3; - } + string value = oms.GetAttributeValue(contentWidget, oms.GetInstance(KnownAttributeGuids.Text.Value)); + if (value != null) + { + obj3.Add("value", JsonValue.Create(value)); + } + return obj3; + } - */ + */ - private void RespondWithJson(StreamWriter sw, WebContext context, int responseCode, string responseText, JsonObject obj) - { - context.Response.ResponseCode = responseCode; - context.Response.ResponseText = responseText; - context.Response.ContentType = "application/json"; - - sw.Write(obj.ToJsonString()); - } - - private bool TryParseInstanceRef(string instanceKeyOrGuid, out InstanceHandle h) - { - h = InstanceHandle.Empty; - if (InstanceKey.TryParse(instanceKeyOrGuid, out InstanceKey k)) - { - oms.TryGetInstance(k, out h); - return true; - } - else if (Guid.TryParse(instanceKeyOrGuid, out Guid g)) - { - oms.TryGetInstance(g, out h); - return true; - } - else - { - Console.Error.WriteLine("could not parse: {0}", instanceKeyOrGuid); - } - return false; - } - - private JsonNode? InstanceToJson(InstanceHandle ih) - { - InstanceHandle parentClass = oms.GetParentClass(ih); - - JsonObject objInstance = new JsonObject(); - objInstance.Add("iid", JsonValue.Create(oms.GetInstanceKey(ih).ToString())); - objInstance.Add("globalIdentifier", JsonValue.Create(oms.GetGlobalIdentifier(ih).ToString())); - objInstance.Add("text", JsonValue.Create(oms.GetInstanceText(ih))); - - JsonObject objParentClass = new JsonObject(); - objParentClass.Add("iid", JsonValue.Create(oms.GetInstanceKey(parentClass).ToString())); - objParentClass.Add("globalIdentifier", JsonValue.Create(oms.GetGlobalIdentifier(parentClass).ToString())); - objParentClass.Add("text", JsonValue.Create(oms.GetInstanceText(parentClass))); - objInstance.Add("parentClass", objParentClass); - - return objInstance; - } - - private JsonArray GetRelationshipsAsJson(InstanceHandle h, InstanceHandle hRel = default(InstanceHandle)) - { - JsonArray a = new JsonArray(); - IEnumerable rels = oms.GetRelationships(h); - foreach (MochaRelationship rel in rels) - { - if (hRel != InstanceHandle.Empty) - { - if (!rel.RelationshipInstance.Equals(hRel)) - { - continue; - } - } - - JsonObject o = new JsonObject(); - o.Add("iid", JsonValue.Create(oms.GetInstanceKey(rel.RelationshipInstance).ToString())); - - string attrText = oms.GetInstanceText(rel.RelationshipInstance); - o.Add("name", JsonValue.Create(attrText)); - - JsonArray aa = new JsonArray(); - foreach (RelationshipValue val in rel.Values) - { - JsonObject ao = new JsonObject(); - ao.Add("effectiveDate", JsonValue.Create(val.EffectiveDate)); - ao.Add("targetInstances", InstancesToJson(val.TargetInstances)); - aa.Add(ao); - } - o.Add("values", aa); - - a.Add(o); - } - return a; - } - - private JsonArray GetAttributesAsJson(InstanceHandle h, InstanceHandle hAtt = default(InstanceHandle)) - { - JsonArray a = new JsonArray(); - IEnumerable atts = oms.GetAttributes(h); - foreach (MochaAttribute att in atts) - { - if (hAtt != InstanceHandle.Empty) - { - if (!att.AttributeInstance.Equals(hAtt)) - { - continue; - } - } - - string iid = oms.GetInstanceKey(att.AttributeInstance).ToString(); - JsonObject o = new JsonObject(); - o.Add("iid", JsonValue.Create(iid)); - - string attrText = oms.GetInstanceText(att.AttributeInstance); - o.Add("name", JsonValue.Create(attrText)); - - JsonArray aa = new JsonArray(); - foreach (AttributeValue val in att.Values) - { - JsonObject ao = new JsonObject(); - ao.Add("effectiveDate", JsonValue.Create(val.EffectiveDate)); - ao.Add("value", JsonValue.Create(val.Value)); - aa.Add(ao); - } - o.Add("values", aa); - - a.Add(o); - } - return a; - } - - private JsonArray InstancesToJson(IEnumerable ihs) - { - JsonArray array = new JsonArray(); - foreach (InstanceHandle ih in ihs) - { - array.Add(InstanceToJson(ih)); - } - return array; - } - - public static int Main(string[] args) + public static int Main(string[] args) { return (new Program()).Start(); } diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/AttributesCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/AttributesCommand.cs new file mode 100644 index 0000000..3427ed6 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/AttributesCommand.cs @@ -0,0 +1,83 @@ +// 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; +using Mocha.Core.UI; + +namespace Mocha.Core.UI.Server.Commands; + +public class AttributesCommand : InstanceCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{iid}/attributes" }; + + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + if (e.Context.Request.PathParts.Length > 7) + { + // /tenants/{tenantName}/instances/{iid}/attrbutes/{attkey}/current + oms.TryParseInstanceRef(e.Context.Request.PathParts[6], out InstanceHandle att); + + string value; + if (e.Context.Request.Method == "POST" && e.Context.Request.PathParts[7].Equals("update")) + { + value = e.Context.Request.Form["value"]; + oms.SetAttributeValue(ProcessingInstance, att, value); + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "text/plain"; + + sw.Write(value); + return; + } + + DateTime dt = DateTime.Now; + if (e.Context.Request.PathParts[7].Equals("current")) + { + } + + value = oms.GetAttributeValue(ProcessingInstance, att, String.Empty, dt); + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "text/plain"; + + sw.Write(value); + return; + } + + JsonObject obj = new JsonObject(); + + InstanceHandle hAtt = InstanceHandle.Empty; + if (e.Context.Request.PathParts.Length == 7) + { + oms.TryParseInstanceRef(e.Context.Request.PathParts[6], out hAtt); + } + + JsonArray objAttributes = JsonRenderer.GetAttributesAsJson(oms, ProcessingInstance, hAtt); + obj.Add("attributes", objAttributes); + + + 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/Commands/CreateInstanceCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/CreateInstanceCommand.cs new file mode 100644 index 0000000..f836ee4 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/CreateInstanceCommand.cs @@ -0,0 +1,69 @@ +// 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; +using Mocha.Core.UI; + +namespace Mocha.Core.UI.Server.Commands; + +public class CreateInstanceCommand : OmsServerCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/create" }; + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + // usage: + // POST /tenants/{tenantName}/instances/create + // + // parentClassIid=1$1&title=My+New+Class + if (e.Context.Request.Form.ContainsKey("parentClassIid")) + { + string pclass = e.Context.Request.Form["parentClassIid"]; + if (oms.TryParseInstanceRef(pclass, out InstanceHandle ihParentClass)) + { + InstanceHandle ihRet = oms.CreateInstanceOf(ihParentClass); + + if (e.Context.Request.Form.ContainsKey("title")) + { + oms.SetAttributeValue(ihRet, oms.GetInstance(KnownAttributeGuids.Text.Name), e.Context.Request.Form["title"]); + } + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("success")); + obj.Add("value", JsonRenderer.InstanceToJson(oms, ihRet)); + sw.Write(obj.ToJsonString()); + } + else + { + e.Context.Response.ResponseCode = 422; + e.Context.Response.ResponseText = "Unprocessable Content"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("failure")); + obj.Add("message", JsonValue.Create("parent class instance does not exist")); + sw.Write(obj.ToJsonString()); + } + } + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000..ba9f7da --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs @@ -0,0 +1,221 @@ +// 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.Collections.ObjectModel; +using System.Text.Json.Nodes; +using MBS.Web; +using Mocha.Core; +using Mocha.Core.Responses; +using Mocha.Core.UI; + +namespace Mocha.Core.UI.Server.Commands; + +public class ElementCommand : InstanceCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{iid}/element" }; + + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + if (e.Context.Request.Method == "POST") + { + if (oms.IsInstanceOf(ProcessingInstance, oms.GetInstance(KnownInstanceGuids.Classes.Element))) + { + // 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); + if (resp != null) + { + JsonObject obj2 = resp.GetResponse(oms, ctx); + RespondWithJson(oms, sw, e.Context, 200, "OK", obj2); + return; + } + else + { + JsonObject obj2 = new JsonObject(); + obj2.Add("result", "failure"); + obj2.Add("message", "no CT associated with element"); + RespondWithJson(oms, sw, e.Context, 400, "Bad Request", obj2); + return; + } + + foreach (KeyValuePair kvp in e.Context.Request.Form) + { + Console.Error.WriteLine("---> {0} = {1}", kvp.Key, kvp.Value); + } + + JsonObject obj = new JsonObject(); + obj.Add("result", "failure"); + obj.Add("message", "not implemented"); + RespondWithJson(oms, sw, e.Context, 500, "Internal Server Error", obj); + } + } + else + { + Renderer renderer = new Renderer(oms); + if (oms.IsInstanceOf(ProcessingInstance, oms.GetInstance(KnownInstanceGuids.Classes.Element))) + { + Widget widget = renderer.Parse(ctx, ProcessingInstance, InstanceHandle.Empty, InstanceHandle.Empty); + JsonObject obj3 = widget.ToJSONObject(ctx); + + JsonObject obj2 = new JsonObject(); + obj2.Add("widget", JsonValue.Create("root")); + obj2.Add("body", widget.ToJSONObject(ctx)); + + JsonObject obj = new JsonObject(); + obj.Add("result", "success"); + obj.Add("value", obj2); + RespondWithJson(oms, sw, e.Context, 200, "OK", obj); + } + else if (oms.IsInstanceOf(ProcessingInstance, oms.GetInstance(KnownInstanceGuids.Classes.Task))) + { + 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); + JsonObject obj = r.GetResponse(oms, ctx); + + /* + + */ + + RespondWithJson(oms, sw, e.Context, 200, "OK", obj); + } + else + { + // render the default task for the given instance + InstanceHandle parentClass = oms.GetParentClass(ProcessingInstance); + // if (!oms.TryParseInstanceRef())//blahh + InstanceHandle defaultTask = oms.GetRelatedInstance(parentClass, oms.GetInstance(KnownRelationshipGuids.Class__has_default__Task)); + if (defaultTask != InstanceHandle.Empty) + { + 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); + JsonObject obj = r.GetResponse(oms, ctx); + + /* + + */ + + RespondWithJson(oms, sw, e.Context, 200, "OK", obj); + } + } + } + } + + 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 new file mode 100644 index 0000000..16a60fa --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceCommand.cs @@ -0,0 +1,73 @@ +// 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 abstract class InstanceCommand : TenantedCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{iid}" }; + public InstanceHandle ProcessingInstance { get; private set; } + + protected override bool BeforeProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + if (e.Context.Request.PathParts.Length < 5) + return true; + + string iid = e.Context.Request.PathParts[4]; + if (oms.TryParseInstanceRef(iid, out InstanceHandle ih)) + { + ProcessingInstance = ih; + } + + if (ProcessingInstance == InstanceHandle.Empty) + { + e.Context.Response.ResponseCode = 404; + e.Context.Response.ResponseText = "Not Found"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", "failure"); + obj.Add("message", String.Format("invalid inst id {0} (inst not found)", e.Context.Request.PathParts[4])); + sw.Write(obj.ToJsonString()); + return false; + } + return true; + } + + public void RespondWithJson(Core.Oms oms, StreamWriter sw, WebContext context, int responseCode, string responseText, JsonObject obj) + { + context.Response.ResponseCode = responseCode; + context.Response.ResponseText = responseText; + context.Response.ContentType = "application/json"; + + if (context.Session.ContainsKey("OmsContext")) + { + OmsContext ctx = (OmsContext)context.Session["OmsContext"]; + object? loginToken = ctx.GetWorkData(oms.GetInstance(KnownAttributeGuids.Text.Token)); + if (loginToken != null) + { + obj.Add("sessionSecureToken", loginToken.ToString()); + } + } + sw.Write(obj.ToJsonString()); + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000..56c8b6b --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceDetailsCommand.cs @@ -0,0 +1,40 @@ +// 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.UI; + +namespace Mocha.Core.UI.Server.Commands; + +public class InstanceDetailsCommand : InstanceCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances/{instanceKey}" }; + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + JsonObject obj = JsonRenderer.InstanceToJson(oms, ProcessingInstance); + obj.Add("attributes", JsonRenderer.GetAttributesAsJson(oms, ProcessingInstance)); + obj.Add("relationshps", JsonRenderer.GetRelationshipsAsJson(oms, ProcessingInstance)); + + 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/Commands/InstanceListCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceListCommand.cs new file mode 100644 index 0000000..c255063 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/InstanceListCommand.cs @@ -0,0 +1,169 @@ +// 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.Diagnostics; +using System.Text.Json.Nodes; +using MBS.Web; +using Mocha.Core; +using Mocha.Core.UI; + +namespace Mocha.Core.UI.Server.Commands; + +public class InstanceListCommand : TenantedCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants/{tenantName}/instances" }; + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, OmsContext ctx, StreamWriter sw) + { + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + + Stopwatch watch = new Stopwatch(); + watch.Start(); + + string query = ""; + string parentClassIid = null; + List ihParentClasses = new List(); + + if (e.Context.Request.Query.ContainsKey("q") && e.Context.Request.Query["q"].Count > 0) + { + query = e.Context.Request.Query["q"][0]; + } + if (e.Context.Request.Query.ContainsKey("parentClassIid") && e.Context.Request.Query["parentClassIid"].Count > 0) + { + parentClassIid = e.Context.Request.Query["parentClassIid"][0]; + if (oms.TryParseInstanceRef(parentClassIid, out InstanceHandle ihParentClass)) + { + ihParentClasses.Add(ihParentClass); + } + } + + if (e.Context.Request.Query.ContainsKey("m") && e.Context.Request.Query["m"].Contains("typeahead")) + { + if (ihParentClasses.Count == 0) + { + ihParentClasses.Add(oms.GetInstance(KnownInstanceGuids.Classes.Task)); + ihParentClasses.Add(oms.GetInstance(KnownInstanceGuids.Classes.Class)); + ihParentClasses.Add(oms.GetInstance(KnownInstanceGuids.Classes.Report)); + } + } + + Dictionary parms = new Dictionary(); + if (e.Context.Request.Method == "POST") + { + foreach (KeyValuePair kvp in e.Context.Request.Form) + { + if (oms.TryParseInstanceRef(kvp.Key, out InstanceHandle parm)) + { + if (oms.TryParseInstanceRef(kvp.Value, out InstanceHandle parmValue)) + { + parms[parm] = parmValue; + } + else + { + parms[parm] = kvp.Value; + } + } + } + } + + IEnumerable? ihs = null; + List list2 = new List(); + if (ihParentClasses.Count > 0) + { + foreach (InstanceHandle ihParentClass in ihParentClasses) + { + if (oms.IsInstanceOf(ihParentClass, oms.GetInstance(KnownInstanceGuids.Classes.ElementContent))) + { + // this is kind of a hack... but oh well + // if it's an EC, + + var ecinst = oms.GetRelatedInstance(ihParentClass, oms.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + if (ecinst != InstanceHandle.Empty) + { + if (oms.IsInstanceOf(ecinst, oms.GetInstance(KnownInstanceGuids.Classes.Class))) + { + ihs = oms.GetInstancesOf(ecinst); + } + else if (oms.IsInstanceOf(ecinst, oms.GetInstance(KnownInstanceGuids.Classes.Relationship))) + { + var relDestination = oms.GetRelatedInstance(ecinst, oms.GetInstance(KnownRelationshipGuids.Relationship__has_destination__Class)); + ihs = oms.GetInstancesOf(relDestination); + } + } + } + else + { + // should we just assume it is a Class ? + ihs = oms.GetInstancesOf(ihParentClass, true); + } + + if (ihs != null) + { + list2.AddRange(ihs); + } + } + ihs = list2; + } + else + { + ihs = oms.GetInstances(); + } + + List list = new List(); + foreach (InstanceHandle ih in ihs) + { + bool ignore = false; + foreach (KeyValuePair kvp in parms) + { + object? val = oms.UnsafeGetAttributeValue(ih, kvp.Key, null); + if (val == null) val = ""; + + if (!val.ToString().Equals(kvp.Value.ToString())) + { + Console.Error.WriteLine("ignoring '{0}' = '{1}'", val, kvp.Value); + ignore = true; + break; + } + } + + if (ignore) continue; + + if (!String.IsNullOrEmpty(query)) + { + string text = oms.GetInstanceText(ih); + if (!text.ToLower().Contains(query.ToLower())) continue; + } + list.Add(ih); + } + ihs = list; + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonArray objInstances = JsonRenderer.InstancesToJson(oms, ihs); + obj.Add("instances", objInstances); + + watch.Stop(); + obj.Add("time", JsonValue.Create(watch.ElapsedMilliseconds.ToString())); + + sw.Write(obj.ToJsonString()); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/PingCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/PingCommand.cs new file mode 100644 index 0000000..432bee7 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/PingCommand.cs @@ -0,0 +1,41 @@ +// 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 PingCommand : OmsServerCommand +{ + public override IEnumerable UriPatterns => new string[] { "/ping" }; + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, OmsContext ctx, StreamWriter sw) + { + if (e.Context.Request.PathParts.Length == 2 && e.Context.Request.PathParts[1] == "ping") + { + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("success")); + sw.Write(obj.ToJsonString()); + } + + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/RelatonshipsCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/RelatonshipsCommand.cs new file mode 100644 index 0000000..b139277 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/RelatonshipsCommand.cs @@ -0,0 +1,120 @@ +// 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; +using Mocha.Core.UI; + +namespace Mocha.Core.UI.Server.Commands; + +public class RelationshipsCommand : InstanceCommand +{ + public override IEnumerable UriPatterns => new string[] + { + "/tenants/{tenantName}/instances/{iid}/relationships", + "/tenants/{tenantName}/instances/{iid}/relationships/{reliid}" + }; + + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + if (e.Context.Request.PathParts.Length >= 7) + { + oms.TryParseInstanceRef(e.Context.Request.PathParts[6], out InstanceHandle rel); + + DateTime dt = DateTime.Now; + if (e.Context.Request.PathParts.Length > 7) + { + if (e.Context.Request.Method == "POST" && e.Context.Request.PathParts[7].Equals("update")) + { + if (Int32.TryParse(e.Context.Request.Form["count"], out int count)) + { + JsonObject obj1 = new JsonObject(); + obj1.Add("result", "success"); + + JsonArray ary1 = new JsonArray(); + + List list = new List(); + for (int i = 0; i < count; i++) + { + string key = String.Format("item{0}", i); + if (e.Context.Request.Form.ContainsKey(key)) + { + Console.Error.WriteLine("debug: trying to add inst " + e.Context.Request.Form[key]); + if (oms.TryParseInstanceRef(e.Context.Request.Form[key], out InstanceHandle inst)) + { + ary1.Add(JsonRenderer.InstanceToJson(oms, inst)); + list.Add(inst); + } + } + } + + Console.Error.WriteLine("ok assigning relationship"); + oms.AssignRelationship(ProcessingInstance, rel, list.ToArray()); + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + obj1.Add("targetInstances", ary1); + + sw.Write(obj1.ToJsonString()); + } + + return; + } + + if (e.Context.Request.PathParts[7].Equals("current")) + { + } + } + + IEnumerable rel_insts = oms.GetRelatedInstances(ProcessingInstance, rel, dt); + JsonObject obj = new JsonObject(); + + JsonArray a = new JsonArray(); + foreach (InstanceHandle ih in rel_insts) + { + a.Add(JsonRenderer.InstanceToJson(oms, ih)); + } + + obj.Add("result", JsonValue.Create("success")); + obj.Add("sourceInstance", JsonRenderer.InstanceToJson(oms, ProcessingInstance)); + obj.Add("relationshipInstance", JsonRenderer.InstanceToJson(oms, rel)); + obj.Add("value", a); + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + sw.Write(obj.ToJsonString()); + return; + } + else + { + JsonObject obj = new JsonObject(); + obj.Add("relationships", JsonRenderer.GetRelationshipsAsJson(oms, ProcessingInstance)); + + 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/Commands/TenantedCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantedCommand.cs new file mode 100644 index 0000000..df8fe46 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantedCommand.cs @@ -0,0 +1,55 @@ +// 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 abstract class TenantedCommand : OmsServerCommand +{ + protected override bool BeforeProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, OmsContext ctx, StreamWriter sw) + { + if (e.Context.Request.PathParts.Length > 3) + { + // /tenants/{tenantName}/instances + string tenantName = e.Context.Request.PathParts[2]; + + TenantHandle th = oms.GetTenantByName(tenantName); + if (th == TenantHandle.Empty) + { + e.Context.Response.ResponseCode = 404; + e.Context.Response.ResponseText = "Not Found"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("failure")); + obj.Add("message", JsonValue.Create("tenant does not exist")); + sw.Write(obj.ToJsonString()); + sw.Flush(); + return false; + } + else + { + oms.SelectTenant(th); + } + } + + return base.BeforeProcessInternal(e, oms, ctx, sw); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantsListCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantsListCommand.cs new file mode 100644 index 0000000..bfbc8c4 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/TenantsListCommand.cs @@ -0,0 +1,111 @@ +// 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 TenantsListCommand : OmsServerCommand +{ + public override IEnumerable UriPatterns => new string[] { "/tenants" }; + + protected override void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonArray ary = new JsonArray(); + + IEnumerable tenants = oms.GetTenants(); + foreach (string tenant in tenants) + { + ary.Add(JsonValue.Create(tenant)); + } + + if (e.Context.Request.PathParts.Length == 2) + { + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("success")); + obj.Add("tenants", ary); + sw.Write(obj.ToJsonString()); + } + else if (e.Context.Request.PathParts.Length > 3) + { + if (e.Context.Request.PathParts[3] == "instances") + { + if (e.Context.Request.PathParts.Length > 4) + { + if (e.Context.Request.PathParts[4] == "create") + { + + } + else if (e.Context.Request.PathParts.Length >= 5) + { + // + oms.TryParseInstanceRef(e.Context.Request.PathParts[4], out InstanceHandle h); + + if (e.Context.Request.PathParts.Length >= 6) + { + string command = e.Context.Request.PathParts[5]; + if (command == "relatedTasks") + { + /* + InstanceHandle instParent = oms.GetParentClass(ih); + List relatedTasks = new List(); + + $instInstance = $this->getInstanceByGlobalIdentifier(KnownClassGuids::Instance); + $relatedTasks0 = $this->getRelatedInstances($instInstance, KnownRelationshipGuids::Class__has_related__Task); + foreach ($relatedTasks0 as $task) + { + $relatedTasks[] = $task; + } + + $relatedTasks1 = $this->getRelatedInstances($instParent, KnownRelationshipGuids::Class__has_related__Task); + foreach ($relatedTasks1 as $task) + { + $relatedTasks[] = $task; + } + + $superclasses = $this->getRelatedInstances($instParent, KnownRelationshipGuids::Class__has_super__Class); + foreach ($superclasses as $superclass) + { + $relatedTasks2 = $this->getRelatedInstances($superclass, KnownRelationshipGuids::Class__has_related__Task); + foreach ($relatedTasks2 as $task) + { + $relatedTasks[] = $task; + } + } + return $relatedTasks; + */ + } + } + else + { + } + } + } + else + { + + } + } + } + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Mocha.Core.UI.Server.csproj b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Mocha.Core.UI.Server.csproj new file mode 100644 index 0000000..383a342 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Mocha.Core.UI.Server.csproj @@ -0,0 +1,14 @@ + + + + + + + + + net8.0 + enable + enable + + + diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs new file mode 100644 index 0000000..a968dce --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServer.cs @@ -0,0 +1,239 @@ +// 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.Collections.Immutable; +using System.Reflection; +using System.Text.Json.Nodes; +using MBS.Core.Collections.Generic; +using MBS.Web; + +namespace Mocha.Core.UI.Server; + +public class OmsServer +{ + private bool MatchUriPattern(string path, string template, out Dictionary vars) + { + vars = new Dictionary(); + int l = template.Length; + int l2 = path.Length; + + int i = 0; + int j = 0; + bool escape = false; + bool insideVariable = false; + + string? varname = null; + string? varvalue = null; + + while (i < l && j < l2) + { + if (template[i] == '\\') + { + escape = true; + i++; + continue; + } + + if (!escape && (template[i] == '{' || (template[i] == '$' && (template.Length - i) > 0 && template[i + 1] == '('))) + { + insideVariable = true; + varname = ""; + varvalue = ""; + i++; + continue; + } + else if (!escape && (insideVariable && (template[i] == '}' || template[i] == ')'))) + { + insideVariable = false; + i++; + continue; + } + else + { + if (insideVariable) + { + varname += template[i]; + i++; + continue; + } + else + { + if (template[i] == path[j]) + { + // yay, we match + // save the last-known variable, if any... + if (varname != null && varvalue != null) + { + vars[varname] = varvalue; + + // don't forget to reset it + varname = null; + varvalue = null; + } + + // ... and keep going + i++; + j++; + } + else + { + // no match + if (varname != null) + { + if (j + 1 < path.Length) + { + // we are currently reading a variable value + varvalue += path[j]; + j++; + } + else + { + // don't even question it, just return false since we should never reach this point + return false; + } + continue; + } + else + { + // we do not match! + return false; + } + + if (varvalue != null) + { + // we are in a variable + varvalue += path[j]; + j++; + continue; + } + } + } + } + + escape = false; + } + + if (varvalue != null && j < path.Length) + { + while (j < path.Length) + { + varvalue += path[j]; + j++; + } + + vars[varname] = varvalue; + varname = null; + varvalue = null; + } + + if (i < l) + { + // entire template was not parsed, so not a match + return false; + } + return true; + } + + private struct OmsServerCommandEntry + { + public string Uri { get; } + public OmsServerCommand Command { get; } + + public OmsServerCommandEntry(string uri, OmsServerCommand command) + { + Uri = uri; + Command = command; + } + } + + private class OmsServerCommandComparer : IComparer + { + public int Compare(OmsServerCommandEntry x, OmsServerCommandEntry y) + { + return y.Uri.Length.CompareTo(x.Uri.Length); + } + } + + private IEnumerable Flatten(IEnumerable cmds) + { + List list = new List(); + foreach (OmsServerCommand cmd in cmds) + { + foreach (string uri in cmd.UriPatterns) + { + list.Add(new OmsServerCommandEntry(uri, cmd)); + } + } + list.Sort(comparer); + return list; + } + + private OmsServerCommandComparer comparer = new OmsServerCommandComparer(); + private OmsServerCommand? FindCommand(string path) + { + Console.Error.WriteLine("findcommand: for path '{0}'", path); + + OmsServerCommand[] commands = MBS.Core.Reflection.TypeLoader.GetAvailableTypes(new Assembly[] { typeof(OmsServer).Assembly }, true); + IEnumerable commandsFlat = Flatten(commands); + + foreach (OmsServerCommandEntry cmd in commandsFlat) + { + Console.Error.WriteLine("findcommand: checking '{0}'", cmd.Uri); + if (MatchUriPattern(path, cmd.Uri, out Dictionary vars)) + { + Console.Error.WriteLine("OK!"); + + cmd.Command.Variables.AddRange(vars); + return cmd.Command; + } + } + return null; + } + + public void ProcessRequest(WebServerProcessRequestEventArgs e, Oms oms, OmsContext ctx, StreamWriter sw) + { + OmsServerCommand? cmd = FindCommand(e.Context.Request.Path); + if (cmd != null) + { + cmd.Process(e, oms, ctx, sw); + } + else + { + JsonObject obj = new JsonObject(); + obj.Add("result", "failure"); + // obj.Add("message", String.Format("unknown cmd '{0}'", command)); + RespondWithJson(oms, sw, e.Context, 404, "Not Found", obj); + } + } + public void RespondWithJson(Oms oms, StreamWriter sw, WebContext context, int responseCode, string responseText, JsonObject obj) + { + context.Response.ResponseCode = responseCode; + context.Response.ResponseText = responseText; + context.Response.ContentType = "application/json"; + + if (context.Session.ContainsKey("OmsContext")) + { + OmsContext ctx = (OmsContext)context.Session["OmsContext"]; + object? loginToken = ctx.GetWorkData(oms.GetInstance(KnownAttributeGuids.Text.Token)); + if (loginToken != null) + { + obj.Add("sessionSecureToken", loginToken.ToString()); + } + } + sw.Write(obj.ToJsonString()); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServerCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServerCommand.cs new file mode 100644 index 0000000..63b9421 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/OmsServerCommand.cs @@ -0,0 +1,43 @@ +// 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; + +public abstract class OmsServerCommand +{ + + public abstract IEnumerable UriPatterns { get; } + public Dictionary Variables { get; } = new Dictionary(); + + protected virtual bool BeforeProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + return true; + } + protected abstract void ProcessInternal(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw); + + public void Process(WebServerProcessRequestEventArgs e, Core.Oms oms, Core.OmsContext ctx, StreamWriter sw) + { + if (!BeforeProcessInternal(e, oms, ctx, sw)) + return; + + ProcessInternal(e, oms, ctx, sw); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs index 4d9c5c8..4ae1b9c 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs @@ -1,27 +1,10 @@ -// 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 . - -// +// // InstanceKey.cs // // Author: // Michael Becker // -// Copyright (c) 2020 Mike Becker's Software +// Copyright (c) 2020-2024 Mike Becker's Software // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -46,6 +29,8 @@ namespace Mocha.Core private bool _isNotEmpty; public bool IsEmpty { get { return !_isNotEmpty; } } + public bool IsDerived { get; } + public static readonly InstanceKey Empty = new InstanceKey(); public static InstanceKey Parse(string instanceKey) @@ -67,17 +52,30 @@ namespace Mocha.Core return false; } - public InstanceKey(string instanceKey) + public InstanceKey(string? instanceKey) { if (instanceKey == null) { ClassIndex = 0; InstanceIndex = 0; + IsDerived = false; _isNotEmpty = false; } else { + char splitChar = '\0'; if (instanceKey.Contains("$")) + { + splitChar = '$'; + IsDerived = false; + } + else if (instanceKey.Contains("!")) + { + splitChar = '!'; + IsDerived = true; + } + + if (splitChar != '\0') { string[] split = instanceKey.Split(new char[] { '$' }); if (split.Length == 2) @@ -92,7 +90,7 @@ namespace Mocha.Core } } } - throw new ArgumentException("must be a string containing two integers separated by a '$'"); + throw new ArgumentException("must be a string containing two integers separated by a '$' or a '!'"); } public InstanceKey(int classIndex, int instanceIndex) { @@ -103,7 +101,7 @@ namespace Mocha.Core public override bool Equals(object obj) { - return (obj is InstanceKey) && ((InstanceKey)obj).ClassIndex == ClassIndex && ((InstanceKey)obj).InstanceIndex == InstanceIndex && ((InstanceKey)obj).IsEmpty == IsEmpty; + return (obj is InstanceKey) && ((InstanceKey)obj).ClassIndex == ClassIndex && ((InstanceKey)obj).InstanceIndex == InstanceIndex && ((InstanceKey)obj).IsEmpty == IsEmpty && ((InstanceKey)obj).IsDerived == IsDerived; } public static bool operator ==(InstanceKey left, InstanceKey right) { @@ -116,6 +114,10 @@ namespace Mocha.Core public override string ToString() { + if (IsDerived) + { + return String.Format("{0}!{1}", ClassIndex, InstanceIndex); + } return String.Format("{0}${1}", ClassIndex, InstanceIndex); } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs b/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs index 86b8560..33db4a3 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs @@ -112,6 +112,7 @@ namespace Mocha.Core public static Guid SystemRoutine { get; } = new Guid("{6e5265af-f73d-420c-b9d5-cf2fdc90363d}"); public static Guid SystemInstanceSetRoutine { get; } = new Guid("{d17a6d27-da03-4b5d-9256-f67f978f403d}"); public static Guid SystemAttributeRoutine { get; } = new Guid("{117f4b9c-2678-4747-aced-78d93c25dcd7}"); + public static Guid SystemUpdateRoutine { get; } = new Guid("{0dad3e78-084f-4894-a4a3-006aab2035df}"); public static Guid Report { get; } = new Guid("{19D947B6-CE82-4EEE-92EC-A4E01E27F2DB}"); public static Guid ReportColumn { get; } = new Guid("{BEFE99A1-B2EB-4365-A2C9-061C6609037B}"); @@ -242,7 +243,11 @@ namespace Mocha.Core // GSP - Get Instance Set from Parameters Method - 51 // SA - Select Attribute Method - 52 - // PRU - Process Related Updates Method - 54 + /// + /// PRU - Process Related Updates Method [1$54] + /// + /// + public static Guid ProcessRelatedUpdatesMethod { get; } = new Guid("{2953e698-03c5-4752-a1eb-cbbfa8f13905}"); /// /// BUIR - Build UI Response Method [1$62] /// @@ -256,7 +261,11 @@ namespace Mocha.Core /// public static Guid GetAttributeBySystemRoutineMethod { get; } = new Guid("{9d45eb8c-1fb5-4260-a69c-f99a8f7a70b8}"); // GES - Get Element by System Routine Method - 67 - // US - Update by System Routine Method - 68 + /// + /// US - Update by System Routine Method - 68 + /// + /// + public static Guid UpdateBySystemRoutineMethod { get; } = new Guid("{dfdf1541-90c8-415a-b4f3-6e87dd037e12}"); /// /// AA - Asssign Attribute Method - 73 /// @@ -274,19 +283,24 @@ namespace Mocha.Core public static Guid ConditionalSelectAttributeMethod { get; } = new Guid("{d534a369-321e-4c32-bd7f-8ff2017f191e}"); // 13038 // SSC - Conditional Select from Instance Set Method - 13039 public static Guid ConditionalSelectFromInstanceSetMethod { get; } = new Guid("{ffea8e52-06e5-4e95-8c40-da3ba54ce95f}"); // 13039 - - } + } public static class SystemAttributeRoutines { public static Guid GetRuntimeVersion { get; } = new Guid("{dc4e6c8d-936d-457f-90e9-af47e229b80c}"); // public static Guid SystemInstanceSetRoutine { get; } = new Guid("{d17a6d27-da03-4b5d-9256-f67f978f403d}"); + public static Guid GetInstanceText { get; } = new Guid("{b024abd6-1f2b-495a-9da3-c9ce29fb0c2f}"); public static Guid GetRandomNumber { get; } = new Guid("{8f7945da-4cad-49cb-9838-85f3524a5adb}"); - } + public static Guid GetIPAddress { get; } = new Guid("{45411e57-ef6e-44f5-8801-603e567d73d4}"); + } public static class SystemInstanceSetRoutines { // public static Guid SystemInstanceSetRoutine { get; } = new Guid("{d17a6d27-da03-4b5d-9256-f67f978f403d}"); public static Guid GetCurrentUser { get; } = new Guid("{9ee8edda-aa1b-4766-8c31-1331be5ffb41}"); } + public static class SystemUpdateRoutines + { + public static Guid LoginUser { get; } = new Guid("{f36f8f87-0d2b-4e62-b85f-abb7e94d952c}"); + } public static class PromptValueClasses { public static Guid InstancePromptValue { get; } = new Guid("{0A6E2CF2-97B6-4339-B1DA-1DBBFE9806C6}"); @@ -436,6 +450,15 @@ namespace Mocha.Core public static Guid NotExactMatchWithSelectionList { get; } = new Guid("{f08a632a-1f3e-4979-8ec5-17fefc33b865}"); public static Guid ExactMatchWithSelectionList { get; } = new Guid("{93611da1-e21e-4021-9ddd-f7575e0c79ec}"); } + public class LogicalOperators + { + public static Guid EqualTo { get; } = new Guid("{733e1fb4-6366-4813-bebb-1e6a7bf504db}"); + public static Guid NotEqualTo { get; } = new Guid("{c357f835-6b3c-446c-922e-6e867e116fb5}"); + public static Guid GreaterThan { get; } = new Guid("{f79b96b7-3c57-455f-87ce-1a3372be65b8}"); + public static Guid LessThan { get; } = new Guid("{8b7b92d4-6297-4f1d-ba11-7cad9d33f517}"); + public static Guid GreaterThanOrEqualTo { get; } = new Guid("{3a4eb7be-f96b-45ac-b97a-bb8fbfc35923}"); + public static Guid LessThanOrEqualTo { get; } = new Guid("{528e5637-f995-4a6d-93c3-f5e1e62b4517}"); + } public static class Languages { public static Guid English { get; } = new Guid ("{68BB6038-A4B5-4EE1-AAE9-326494942062}"); diff --git a/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs b/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs index 2e477e2..ea47be5 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs @@ -86,7 +86,9 @@ namespace Mocha.Core public static Guid Instance_Attribute_String_Component__has__Attribute { get; } = new Guid("{E15D4277-69FB-4F19-92DB-8D087F361484}"); public static Guid String_Component__has_source__Method { get; } = new Guid("{1ef1c965-e120-48be-b682-aa040573b5fb}"); - public static Guid Class__instance_labeled_by__Return_Attribute_Method_Binding { get; } = new Guid("{F52FC851-D655-48A9-B526-C5FE0D7A29D2}"); + // public static Guid Class__instance_labeled_by__Return_Attribute_Method_Binding { get; } = new Guid("{F52FC851-D655-48A9-B526-C5FE0D7A29D2}"); + public static Guid Class__instances_labeled_by__Executable_returning_Attribute { get; } = new Guid("{c22fc17f-0c92-47dc-9a8b-28db0db68985}"); + public static Guid Class__has_summary__Report_Field { get; } = new Guid("{D11050AD-7376-4AB7-84DE-E8D0336B74D2}"); public static Guid Class__has_related__Task { get; } = new Guid("{4D8670E1-2AF1-4E7C-9C87-C910BD7B319B}"); @@ -141,6 +143,9 @@ namespace Mocha.Core public static Guid Build_Attribute_Method__returns__Attribute { get; } = new Guid("{dadbf0f3-7af0-4387-a6b7-a1724a216d88}"); public static Guid Attribute__returned_by__Build_Attribute_Method { get; } = new Guid("{d5a6062b-2e84-46a1-8f54-da630ef6a48c}"); + public static Guid Build_Attribute_Method__builds_with__Build_Attribute_Method_Component { get; } = new Guid("{b4fad1b8-711c-4e84-82d0-e9a9e41e8aa7}"); + public static Guid Build_Attribute_Method_Component__uses__Executable_returning_Attribute { get; } = new Guid("{9d2acd01-5c6d-4a95-b77e-5261ba109540}"); + public static Guid Get_Attribute_Method__returns__Attribute { get; } = new Guid("{5eca9b3f-be75-4f6e-8495-781480774833}"); public static Guid Attribute__returned_by__Get_Attribute_Method { get; } = new Guid("{e82ace2e-84b7-4912-89ed-7b8efd63bb5d}"); @@ -154,6 +159,7 @@ namespace Mocha.Core public static Guid Get_Instance_Set_by_System_Routine_Method__uses__System_Instance_Set_Routine { get; } = new Guid("{085bd706-eece-4604-ac04-b7af114d1d21}"); public static Guid System_Instance_Set_Routine__used_by__Get_Instance_Set_by_System_Routine_Method { get; } = new Guid("{6fb6534c-2a46-4d6d-b9df-fd581f19efed}"); + public static Guid Update_by_System_Routine_Method__uses__System_Update_Routine { get; } = new Guid("{9424a253-cb40-4c7d-bebd-0d0373458154}"); public static Guid Get_Referenced_Instance_Set_Method__returns__Work_Set { get; } = new Guid("{72057f5b-9b49-497d-852f-cd7e5e258d6c}"); public static Guid Get_Referenced_Instance_Set_Method__uses_reference__Executable_returning_Instance_Set { get; } = new Guid("{2978238f-7cb0-4ba3-8c6f-473df782cfef}"); @@ -319,6 +325,13 @@ namespace Mocha.Core public static Guid Element_Content__has__Layout { get; } = new Guid("{1ab74120-05ea-4aca-b6d3-c7e0133e0c4f}"); + public static Guid Element_Content__built_from__BEM_Process { get; } = new Guid("{3d7094ff-33e5-4800-9e4e-93dde0d1d331}"); + + + public static Guid Element__has__Layout { get; } = new Guid("{11070799-46bd-4201-ba09-6109c68e61ad}"); + public static Guid Layout__for__Element { get; } = new Guid("{05ced0ff-1dc2-41bc-b460-4773da080fc0}"); + + public static Guid Layout__has__Style { get; } = new Guid("{e684bb26-7e78-4a21-b8b4-5a550f3053d5}"); public static Guid Group_Layout__uses__Group_Layout_Option { get; } = new Guid("{5bea01b6-c33e-4f37-a940-579712ab47c7}"); @@ -415,5 +428,12 @@ namespace Mocha.Core public static Guid Route__secured_to__Domain { get; } = new Guid("{2d085cbe-103b-49ea-b6fe-738357e75912}"); public static Guid Domain__secures__Route { get; } = new Guid("{95725379-7b45-4e0c-a04e-b06eead84c60}"); - } + public static Guid Element__processed_by__Process_Related_Updates_Method { get; } = new Guid("{ed9c836a-04a4-4505-8953-3c567e841c66}"); + public static Guid Process_Related_Updates_Method__uses__Executable_for_PUMB { get; } = new Guid("{50e1f14a-d6e5-4c71-b7ab-1755442728dc}"); + + public static Guid BEM_Process__uses_loop__Executable_returning_Instance_Set { get; } = new Guid("{0fb2b538-eacb-418a-b7d8-43a584b85952}"); + + public static Guid Get_Instances_Method__returns__Work_Set { get; } = new Guid("{7d0f93b1-8c93-464e-a44d-d674f910b589}"); + public static Guid Get_Instances_Method__selects_instances_of__Class { get; } = new Guid("{c0b85d90-de8c-44c2-9420-c5e724ccdf2c}"); + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/AssignAttributeMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/AssignAttributeMethodImplementation.cs index 9659cde..3661959 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/AssignAttributeMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/AssignAttributeMethodImplementation.cs @@ -23,8 +23,13 @@ public class AssignAttributeMethodImplementation : MethodImplementation protected override InstanceHandle ExecuteInternal(Oms oms, OmsContext context, InstanceHandle method) { InstanceHandle irForClass = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Method__for__Class)); - InstanceHandle irForInstance = (InstanceHandle) context.GetWorkData(irForClass); + object wdForClass = context.GetWorkData(irForClass); + if (wdForClass == null) + { + throw new NullReferenceException(String.Format("work data not set for `Method.for Class` {0}", oms.GetGlobalIdentifier(irForClass))); + } + InstanceHandle irForInstance = (InstanceHandle)wdForClass; InstanceHandle assignsAttribute = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Assign_Attribute_Method__assigns__Attribute)); if (assignsAttribute == InstanceHandle.Empty) @@ -33,7 +38,16 @@ public class AssignAttributeMethodImplementation : MethodImplementation } InstanceHandle assignsFrom = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Assign_Attribute_Method__uses__Executable_returning_Attribute)); - InstanceHandle assignsFromValue = oms.Execute(context, assignsFrom); + // ! FIXME: these should not be equal?? idk + InstanceHandle assignsFromValue = InstanceHandle.Empty; + if (oms.IsInstanceOf(assignsFrom, oms.GetInstance(KnownInstanceGuids.Classes.Executable))) + { + assignsFromValue = oms.Execute(context, assignsFrom); + } + else + { + assignsFromValue = assignsFrom; + } // InstanceHandle forInstance = (InstanceHandle) context.GetWorkData(irForClass); object? value = context.GetWorkData(assignsFromValue); diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/BuildAttributeMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/BuildAttributeMethodImplementation.cs index 4a5ab0b..3c6f5bb 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/BuildAttributeMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/BuildAttributeMethodImplementation.cs @@ -31,7 +31,33 @@ public class BuildAttributeMethodImplementation : MethodImplementation } // InstanceHandle forInstance = (InstanceHandle) context.GetWorkData(irForClass); - object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); + object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value + + if (value is string) + { + IEnumerable buildsWithRambs = oms.GetRelatedInstances(method, oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method__builds_with__Build_Attribute_Method_Component)); + foreach (InstanceHandle ihComponent in buildsWithRambs) + { + InstanceHandle ihRamb = oms.GetRelatedInstance(ihComponent, oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method_Component__uses__Executable_returning_Attribute)); + object? val = null; + + if (oms.IsInstanceOf(ihRamb, oms.GetInstance(KnownInstanceGuids.Classes.Attribute))) + { + val = null; + } + else if (oms.IsInstanceOf(ihRamb, oms.GetInstance(KnownInstanceGuids.Classes.Executable))) + { + InstanceHandle wd = oms.Execute(context, ihRamb); + val = context.GetWorkData(wd); + } + + if (val is string) + { + value = ((string)value) + (string)val; + } + } + } + context.SetWorkData(returnsAttribute, value); return returnsAttribute; } diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs index b896deb..35fc59f 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/EvaluateBooleanExpressionMethodImplementation.cs @@ -62,15 +62,19 @@ public class EvaluateBooleanExpressionMethodImplementation : MethodImplementatio rsource_value = new InstanceHandle[] { (InstanceHandle)rsource_valueraw }; } - if (oms.IsInstanceOf(target, c_Class)) + if (oms.IsInstanceOf(target, oms.GetInstance(KnownInstanceGuids.Classes.Attribute))) { rtarget_value = new InstanceHandle[] { target }; } - else + 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; @@ -105,9 +109,14 @@ public class EvaluateBooleanExpressionMethodImplementation : MethodImplementatio 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", comparison)); + 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); diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs index 968e53c..db8bfcc 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetAttributeMethodImplementation.cs @@ -35,6 +35,11 @@ public class GetAttributeMethodImplementation : MethodImplementation { throw new NullReferenceException(String.Format("non-static method call without instance reference {0}", oms.GetGlobalIdentifier(irForClass))); } + + if (oms.IsInstanceOf(forInstance.Value, oms.GetInstance(KnownInstanceGuids.Classes.WorkSet))) + { + forInstance = (InstanceHandle?) context.GetWorkData(forInstance.Value); + } object? value = oms.UnsafeGetAttributeValue((InstanceHandle) forInstance, returnsAttribute); context.SetWorkData(returnsAttribute, value); diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstancesMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstancesMethodImplementation.cs new file mode 100644 index 0000000..b2bbf6f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetInstancesMethodImplementation.cs @@ -0,0 +1,39 @@ +// 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 GetInstancesMethodImplementation : MethodImplementation +{ + public override Guid MethodClassGuid => new Guid("{0a379314-9d0f-432d-ae59-63194ab32dd3}"); + + protected override InstanceHandle ExecuteInternal(Oms oms, OmsContext context, InstanceHandle method) + { + InstanceHandle returnsWorkSet = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Get_Instances_Method__returns__Work_Set)); + InstanceHandle selectsInstanceOfClass = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Get_Instances_Method__selects_instances_of__Class)); + + IEnumerable list = new InstanceHandle[0]; + if (selectsInstanceOfClass != InstanceHandle.Empty) + { + list = oms.GetInstancesOf(selectsInstanceOfClass); + } + context.SetWorkData(returnsWorkSet, list); + + return returnsWorkSet; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedAttributeMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedAttributeMethodImplementation.cs index e6b7312..484fe4c 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedAttributeMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedAttributeMethodImplementation.cs @@ -39,9 +39,13 @@ public class GetReferencedAttributeMethodImplementation : MethodImplementation InstanceHandle referenceInstanceSetWS = oms.Execute(context, referenceInstanceSet); object? referenceInstanceSetValue = context.GetWorkData(referenceInstanceSetWS); - if (referenceInstanceSetValue is IReadOnlyCollection) + if (referenceInstanceSetValue is InstanceHandle) + { + referenceInstanceSetValue = new InstanceHandle[] { (InstanceHandle)referenceInstanceSetValue }; + } + + if (referenceInstanceSetValue is IEnumerable list) { - IReadOnlyCollection list = (IReadOnlyCollection)referenceInstanceSetValue; object? retval = null; int avg_c = 0; diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedInstanceSetMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedInstanceSetMethodImplementation.cs index 1c518b8..4baf563 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedInstanceSetMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetReferencedInstanceSetMethodImplementation.cs @@ -48,9 +48,8 @@ public class GetReferencedInstanceSetMethodImplementation : MethodImplementation referenceInstanceSetValue = new InstanceHandle[] { (InstanceHandle)referenceInstanceSetValue }; } - if (referenceInstanceSetValue is IReadOnlyCollection) + if (referenceInstanceSetValue is IEnumerable list) { - IReadOnlyCollection list = (IReadOnlyCollection)referenceInstanceSetValue; object? retval = null; foreach (InstanceHandle inst in list) @@ -62,10 +61,18 @@ public class GetReferencedInstanceSetMethodImplementation : MethodImplementation if (oms.IsInstanceOf(answerInstanceSet, oms.GetInstance(KnownInstanceGuids.Classes.Executable))) { answerInstanceSetWD = oms.Execute(context, answerInstanceSet); + + object? answerInstanceSetValue = context.GetWorkData(answerInstanceSetWD); + retval = answerInstanceSetValue; + } + else if (oms.IsInstanceOf(answerInstanceSet, oms.GetInstance(KnownInstanceGuids.Classes.Relationship))) + { + object? answerInstanceSetValue = oms.GetRelatedInstance(inst, answerInstanceSet); + retval = answerInstanceSetValue; } - object? answerInstanceSetValue = context.GetWorkData(answerInstanceSetWD); - retval = answerInstanceSetValue; + // ! FIXME ! multiple instances not yet supported + break; } context.SetWorkData(returnsWorkSet, retval); } diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetRelationshipMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetRelationshipMethodImplementation.cs index 80d1296..f5ccd4f 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetRelationshipMethodImplementation.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/GetRelationshipMethodImplementation.cs @@ -54,7 +54,7 @@ public class GetRelationshipMethodImplementation : MethodImplementation } else { - context.SetWorkData(returnsRelationship, null); + context.SetWorkData(returnsRelationship, (object)null); } } else diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs new file mode 100644 index 0000000..793e5f4 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessRelatedUpdatesMethodImplementation.cs @@ -0,0 +1,34 @@ +// 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 ProcessRelatedUpdatesMethodImplementation : MethodImplementation +{ + public override Guid MethodClassGuid => KnownInstanceGuids.MethodClasses.ProcessRelatedUpdatesMethod; + protected override InstanceHandle ExecuteInternal(Oms oms, OmsContext context, InstanceHandle method) + { + 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)); + foreach (InstanceHandle pumb in usesExecutableForPUMB) + { + oms.Execute(context, pumb); + } + return InstanceHandle.Empty; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/UpdateBySystemRoutineMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/UpdateBySystemRoutineMethodImplementation.cs new file mode 100644 index 0000000..43f1de1 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/UpdateBySystemRoutineMethodImplementation.cs @@ -0,0 +1,36 @@ +// 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 UpdateBySystemRoutineMethodImplementation : MethodImplementation +{ + public override Guid MethodClassGuid => KnownInstanceGuids.MethodClasses.UpdateBySystemRoutineMethod; + protected override InstanceHandle ExecuteInternal(Oms oms, OmsContext context, InstanceHandle method) + { + InstanceHandle irForClass = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Method__for__Class)); + InstanceHandle usesSystemUpdateRoutine = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Update_by_System_Routine_Method__uses__System_Update_Routine)); + + if (usesSystemUpdateRoutine == InstanceHandle.Empty) + { + throw new InvalidOperationException("no System Update Routine specified for method"); + } + + oms.ExecuteSystemRoutine(context, usesSystemUpdateRoutine); + return InstanceHandle.Empty; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index 1283dfc..6c63e86 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -17,19 +17,26 @@ namespace Mocha.Core; +using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data.SqlTypes; +using System.Diagnostics.Contracts; using System.Net; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; +using System.Security.Cryptography; using System.Text; +using System.Text.Json.Nodes; +using System.Xml.XPath; using MBS.Core; using MBS.Core.Extensibility; using MBS.Core.IO; using Mocha.Core.MethodImplementations; using Mocha.Core.Oop; +using Mocha.Core.Responses; +using Mocha.Core.UI; public abstract class Oms { @@ -65,11 +72,21 @@ public abstract class Oms UpdateSyntacticSugar(); - RegisterSystemRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, new SystemAttributeRoutine(this, KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, delegate (Oms oms, OmsContext context) + RegisterSystemAttributeRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetInstanceText, delegate (Oms oms, OmsContext context) + { + object? val = context.GetWorkData(oms.GetInstance(KnownInstanceGuids.Classes.Instance)); + if (val is InstanceHandle ih) + { + return oms.GetInstanceText(ih); + } + return ""; + }); + + RegisterSystemAttributeRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, delegate (Oms oms, OmsContext context) { return RuntimeVersion.ToString(); - })); - RegisterSystemRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetRandomNumber, new SystemAttributeRoutine(this, KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, delegate (Oms oms, OmsContext context) + }); + RegisterSystemAttributeRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetRandomNumber, delegate (Oms oms, OmsContext context) { object? oSeed = context.GetWorkData(oms.GetInstance(KnownAttributeGuids.Numeric.Seed)); @@ -83,7 +100,64 @@ public abstract class Oms r = new Random(); } return (decimal)r.NextDouble(); - })); + }); + RegisterSystemUpdateRoutine(KnownInstanceGuids.SystemUpdateRoutines.LoginUser, delegate (Oms oms, OmsContext context) + { + // 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}"); + + string userName = context.GetWorkData(oms.GetInstance(userNameGuid)); + string password = context.GetWorkData(oms.GetInstance(passwordGuid)); + + HashAlgorithm hashAlgorithm = SHA512.Create(); + + InstanceHandle clsUser = oms.GetInstance(KnownInstanceGuids.Classes.User); + IEnumerable users = oms.GetInstancesOf(clsUser); + + string? sessionSecureToken = null; + InstanceHandle actualSystemUser = InstanceHandle.Empty; + + foreach (InstanceHandle user in users) + { + string expectedUserName = oms.GetAttributeValue(user, oms.GetInstance(KnownAttributeGuids.Text.UserName)); + if (expectedUserName == null) + continue; + + if (expectedUserName != userName) + continue; + + string expectedPasswordHash = oms.GetAttributeValue(user, oms.GetInstance(KnownAttributeGuids.Text.PasswordHash)); + if (expectedPasswordHash == null) + continue; + + expectedPasswordHash = expectedPasswordHash.ToLowerInvariant(); + string passwordSalt = oms.GetAttributeValue(user, oms.GetInstance(KnownAttributeGuids.Text.PasswordSalt)); + + byte[] hash = hashAlgorithm.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password + passwordSalt)); + string actualPasswordHash = Convert.ToHexString(hash).ToLowerInvariant(); + + if (actualPasswordHash.Equals(expectedPasswordHash)) + { + actualSystemUser = user; + sessionSecureToken = Guid.NewGuid().ToString("B").ToLowerInvariant(); + break; + } + } + + if (sessionSecureToken != null) + { + context.SetWorkData(oms.GetInstance(KnownInstanceGuids.Classes.User), actualSystemUser); + context.SetWorkData(oms.GetInstance(KnownAttributeGuids.Text.Token), sessionSecureToken); + + // FIXME: this should be processed in the PRU + InstanceHandle clsSystemAccountSignon = GetInstance(KnownInstanceGuids.Classes.UserLogin); + InstanceHandle systemAccountSignon = CreateInstanceOf(clsSystemAccountSignon); + AssignRelationship(systemAccountSignon, GetInstance(KnownRelationshipGuids.User_Login__has__User), actualSystemUser); + SetAttributeValue(systemAccountSignon, GetInstance(KnownAttributeGuids.Text.Token), sessionSecureToken); + SetAttributeValue(systemAccountSignon, GetInstance(KnownAttributeGuids.Text.IPAddress), context.GetExtraData("Client.IPAddress")); + } + }); DebugOms = this; } @@ -416,7 +490,7 @@ public abstract class Oms return HasAttributeValueInternal(source, attribute, effectiveDate.GetValueOrDefault(DateTime.Now)); } protected abstract object? GetAttributeValueInternal(InstanceHandle source, InstanceHandle attribute, DateTime effectiveDate); - protected internal object? UnsafeGetAttributeValue(InstanceHandle source, InstanceHandle attribute, DateTime? effectiveDate = null) + public object? UnsafeGetAttributeValue(InstanceHandle source, InstanceHandle attribute, DateTime? effectiveDate = null) { object? val = GetAttributeValueInternal(source, attribute, effectiveDate.GetValueOrDefault(DateTime.Now)); return val; @@ -486,6 +560,12 @@ public abstract class Oms if (!ValidateConstraints) return; + if (value == null || value == "") + { + // all we can do really is verify if it's supposed to be `Not Empty` + return; + } + InstanceHandle a_TextAttribute = GetInstance(KnownInstanceGuids.Classes.TextAttribute); InstanceHandle a_BooleanAttribute = GetInstance(KnownInstanceGuids.Classes.BooleanAttribute); InstanceHandle a_NumericAttribute = GetInstance(KnownInstanceGuids.Classes.NumericAttribute); @@ -608,6 +688,16 @@ public abstract class Oms return value; } + public bool IsSubclassOf(InstanceHandle subclasz, InstanceHandle clasz, bool exactMatch = false) + { + bool value = Object.Equals(subclasz, clasz); + if (!value && !exactMatch) + { + return RecursiveClassHasSuperclass(subclasz, clasz); + } + return value; + } + public string GetAttachmentUrl(InstanceHandle fileInstance) { string accessKey = BuildAccessKeyForOmsAttachment(fileInstance); @@ -927,10 +1017,26 @@ public abstract class Oms AssignRelationship(inheritsFrom, GetInstance(KnownRelationshipGuids.Class__has_sub__Class), classInstance); } - public IReadOnlyCollection GetInstancesOf(InstanceHandle classInstance) + public IReadOnlyCollection GetInstancesOf(InstanceHandle classInstance, bool includeSubclasses = false) { - IReadOnlyCollection insts = GetRelatedInstances(classInstance, GetInstance(KnownRelationshipGuids.Class__has__Instance)); - return insts; + List list = new List(); + list.Add(classInstance); + if (includeSubclasses) + { + IEnumerable subclasses = GetRelatedInstances(classInstance, GetInstance(KnownRelationshipGuids.Class__has_sub__Class)); + list.AddRange(subclasses); + } + return GetInstancesOf(list); + } + public IReadOnlyCollection GetInstancesOf(IEnumerable classInstances) + { + List list = new List(); + foreach (InstanceHandle ih in classInstances) + { + IReadOnlyCollection insts = GetRelatedInstances(ih, GetInstance(KnownRelationshipGuids.Class__has__Instance)); + list.AddRange(insts); + } + return list; } public string GetInstanceText(InstanceWrapper inst) @@ -942,15 +1048,19 @@ public abstract class Oms InstanceHandle parentClass = GetParentClass(inst); InstanceHandle ihLabeledByRAMB = InstanceHandle.Empty; - if (TryGetInstance(KnownRelationshipGuids.Class__instance_labeled_by__Return_Attribute_Method_Binding, out ihLabeledByRAMB)) + if (TryGetInstance(KnownRelationshipGuids.Class__instances_labeled_by__Executable_returning_Attribute, out ihLabeledByRAMB)) { InstanceHandle ramb = GetRelatedInstance(parentClass, ihLabeledByRAMB); - - OmsContext context = CreateContext(); - InstanceHandle wd = Execute(context, ramb); - - string value = context.GetWorkData(wd); - return value; + if (ramb != InstanceHandle.Empty) + { + OmsContext context = CreateContext(); + context.SetWorkData(parentClass, inst); + + InstanceHandle wd = Execute(context, ramb); + + string value = context.GetWorkData(wd); + return value; + } } // FIXME: remove when we have Class@get Instance Text implemented @@ -1042,15 +1152,61 @@ public abstract class Oms } private Dictionary _systemRoutinesByInstance = new Dictionary(); - internal void RegisterSystemRoutine(Guid globalIdentifier, SystemRoutine routine) + + [Obsolete("Please use RegisterSystemAttributeRoutine or RegisterSystemInstanceSetRoutine instead")] + internal void RegisterSystemRoutine(SystemRoutine routine) { - _systemRoutinesByInstance[globalIdentifier] = routine; + _systemRoutinesByInstance[routine.GlobalIdentifier] = routine; + } + + /// + /// Registers a new System Attribute Routine with the given delegate. + /// + /// + /// + /// + public void RegisterSystemAttributeRoutine(Guid globalIdentifier, Func func) + { + _systemRoutinesByInstance[globalIdentifier] = new SystemAttributeRoutine(this, globalIdentifier, func); + } + + /// + /// Registers a new System Instance Set Routine with the given delegate. + /// + /// + /// + /// + public void RegisterSystemInstanceSetRoutine(Guid globalIdentifier, Func> func) + { + _systemRoutinesByInstance[globalIdentifier] = new SystemInstanceSetRoutine(this, globalIdentifier, func); + } + + /// + /// Registers a new System Update Routine with the given delegate. + /// + /// + /// + /// + public void RegisterSystemUpdateRoutine(Guid globalIdentifier, Action func) + { + _systemRoutinesByInstance[globalIdentifier] = new SystemUpdateRoutine(this, globalIdentifier, func); } internal object? ExecuteSystemRoutine(OmsContext context, InstanceHandle handle) { Guid globalIdentifier = this.GetGlobalIdentifier(handle); - object? value = _systemRoutinesByInstance[globalIdentifier].Execute(this, context); + SystemRoutine sr = _systemRoutinesByInstance[globalIdentifier]; + + SystemRoutineExecutingEventArgs ee = new SystemRoutineExecutingEventArgs(this, context, sr, null); + OnSystemRoutineExecuting(ee); + if (ee.Cancel) + { + return ee.ReturnValue; + } + + object? value = sr.Execute(this, context); + + OnSystemRoutineExecuted(new SystemRoutineExecutedEventArgs(this, context, sr, value)); return value; } @@ -1309,7 +1465,7 @@ public abstract class Oms Library lib = new Library(); // _libraries[name] = lh; - if (!File.Exists(filename) && !Directory.Exists(filename)) + if (!System.IO.File.Exists(filename) && !Directory.Exists(filename)) { throw new FileNotFoundException(null, filename); } @@ -1350,8 +1506,19 @@ public abstract class Oms } private Dictionary _libraryFileNames = new Dictionary(); + + public event EventHandler SystemRoutineExecuting; + protected virtual void OnSystemRoutineExecuting(SystemRoutineExecutingEventArgs e) + { + SystemRoutineExecuting?.Invoke(this, e); + } + public event EventHandler SystemRoutineExecuted; + protected virtual void OnSystemRoutineExecuted(SystemRoutineExecutedEventArgs e) + { + SystemRoutineExecuted?.Invoke(this, e); + } - public LibraryHandle LoadLibrary(Type type, string manifestResourceStreamName) + public LibraryHandle LoadLibrary(Type type, string manifestResourceStreamName) { Stream? st = type.Assembly.GetManifestResourceStream(manifestResourceStreamName); if (st != null) @@ -1421,4 +1588,134 @@ public abstract class Oms { return _tenantsByName.Keys; } + + public bool TryParseInstanceRef(string instanceKeyOrGuid, out InstanceHandle h) + { + h = InstanceHandle.Empty; + if (InstanceKey.TryParse(instanceKeyOrGuid, out InstanceKey k)) + { + return TryGetInstance(k, out h); + } + else if (Guid.TryParse(instanceKeyOrGuid, out Guid g)) + { + return TryGetInstance(g, out h); + } + else + { + Console.Error.WriteLine("could not parse: {0}", instanceKeyOrGuid); + } + return false; + } + + public void UpdateElementContents(OmsContext context, InstanceHandle element, ReadOnlyDictionary values) + { + IEnumerable elementContents = GetRelatedInstances(element, GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); + foreach (InstanceHandle elementContent in elementContents) + { + object? value = null; + InstanceHandle instance = GetRelatedInstance(elementContent, GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + InstanceHandle updateWithRWMB = GetRelatedInstance(elementContent, GetInstance(KnownRelationshipGuids.Derived_Element_Content__update_with__Executable_returning_Work_Data)); + if (updateWithRWMB != InstanceHandle.Empty) + { + if (IsInstanceOf(updateWithRWMB, GetInstance(KnownInstanceGuids.Classes.Executable))) + { + value = Execute(context, updateWithRWMB); + } + else + { + value = updateWithRWMB; + } + + if (value is InstanceHandle) + { + context.SetWorkData(instance, (InstanceHandle)value); + } + } + } + + foreach (KeyValuePair kvp in values) + { + object newValue = kvp.Value; + + if (TryParseInstanceRef(kvp.Key, out InstanceHandle parm__IS)) + { + if (IsInstanceOf(parm__IS, GetInstance(KnownInstanceGuids.Classes.ElementContent))) + { + InstanceHandle hasInstance = GetRelatedInstance(parm__IS, GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + if (IsInstanceOf(hasInstance, GetInstance(KnownInstanceGuids.Classes.Class))) + { + if (TryParseInstanceRef(kvp.Value, out InstanceHandle h)) + { + newValue = h; + context.SetWorkData(hasInstance, newValue); + } + } + if (false) + // if (GetAttributeValue(parm__IS, GetInstance(KnownAttributeGuids.Boolean.DoNotUpdateOnSubmit))) + { + // do not update on submit + continue; + } + } + + + context.SetWorkData(parm__IS, newValue); + } + } + } + + public Response ProcessElement(OmsContext context, InstanceHandle initiatingElement, InstanceHandle parmInstance) + { + if (parmInstance != InstanceHandle.Empty) + { + InstanceHandle processedByCt = GetRelatedInstance(initiatingElement, GetInstance(KnownRelationshipGuids.Element__processed_by__Control_Transaction_Method)); + InstanceHandle forClassId = GetRelatedInstance(processedByCt, GetInstance(KnownRelationshipGuids.Method__for__Class)); + + InstanceHandle buildsResponseWithMB = GetRelatedInstance(processedByCt, 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); + } + else + { + Renderer renderer = new Renderer(this); + Widget widget = renderer.Parse(context, initiatingElement, InstanceHandle.Empty, InstanceHandle.Empty); + if (widget == null) + { + return FailureResponse.UnexpectedFailure; + } + + return new PageResponse(delegate (Oms oms, OmsContext ctx) + { + JsonObject obj3 = widget.ToJSONObject(ctx); + + JsonObject obj2 = new JsonObject(); + obj2.Add("widget", JsonValue.Create("root")); + obj2.Add("body", widget.ToJSONObject(ctx)); + + JsonObject obj = new JsonObject(); + obj.Add("result", "success"); + obj.Add("value", obj2); + return obj; + }); + } + 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) + { + label = GetAttributeValue(parentElementContent, GetInstance(KnownAttributeGuids.Text.Label)); + if (label == null) + { + label = GetInstanceText(parentInstance); + } + } + return label; + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs index 02f2d04..c977b23 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs @@ -42,7 +42,7 @@ public class OmsContext if (_WorkData.ContainsKey(parm)) return _WorkData[parm]; - Console.Error.WriteLine("work data not found for parm {0}", Oms.GetGlobalIdentifier(parm)); + // Console.Error.WriteLine("work data not found for parm {0}", Oms.GetGlobalIdentifier(parm)); return null; } @@ -66,39 +66,22 @@ public class OmsContext } public void SetWorkData(InstanceHandle parm, object? value) { - if (Oms.IsInstanceOf(parm, Oms.GetInstance(KnownInstanceGuids.Classes.WorkSet))) + if (value != null) { - bool singular = false; - if (!Oms.TryGetAttributeValue(parm, Oms.GetInstance(KnownAttributeGuids.Boolean.Singular), out singular)) + if (Oms.IsInstanceOf(parm, Oms.GetInstance(KnownInstanceGuids.Classes.WorkSet))) { - singular = false; - } + bool singular = false; + if (!Oms.TryGetAttributeValue(parm, Oms.GetInstance(KnownAttributeGuids.Boolean.Singular), out singular)) + { + singular = false; + } - if (value is InstanceHandle) - { - IReadOnlyCollection irs = Oms.GetRelatedInstances(parm, Oms.GetInstance(KnownRelationshipGuids.Work_Set__has_valid__Class)); - if (irs.Count > 0) + if (value is InstanceHandle) { - InstanceHandle ir = (InstanceHandle)value; - InstanceHandle parentClass = Oms.GetParentClass(ir); - if (!irs.Contains(parentClass)) - { - throw new ArgumentException("instance reference must be an instance of appropriate class"); - } - } - } - else if (value is IEnumerable) - { - IEnumerable insts = (IEnumerable)value; - if (singular && insts.Count() > 1) - { - throw new InvalidOperationException("Singular Work Set must only contain a single InstanceReference or be an array of InstanceReference that contains exactly zero or one item."); - } - IEnumerable irs = Oms.GetRelatedInstances(parm, Oms.GetInstance(KnownRelationshipGuids.Work_Set__has_valid__Class)); - if (irs != null) - { - foreach (InstanceHandle ir in insts) + IReadOnlyCollection irs = Oms.GetRelatedInstances(parm, Oms.GetInstance(KnownRelationshipGuids.Work_Set__has_valid__Class)); + if (irs.Count > 0) { + InstanceHandle ir = (InstanceHandle)value; InstanceHandle parentClass = Oms.GetParentClass(ir); if (!irs.Contains(parentClass)) { @@ -106,22 +89,18 @@ public class OmsContext } } } - } - else if (value is IEnumerable) - { - IEnumerable insts = (IEnumerable)value; - if (singular && insts.Count() > 1) - { - throw new InvalidOperationException("Singular Work Set must only contain a single InstanceReference or be an array of InstanceReference that contains exactly zero or one item."); - } - IEnumerable irs = Oms.GetRelatedInstances(parm, Oms.GetInstance(KnownRelationshipGuids.Work_Set__has_valid__Class)); - if (irs != null) + else if (value is IEnumerable) { + IEnumerable insts = (IEnumerable)value; + if (singular && insts.Count() > 1) + { + throw new InvalidOperationException("Singular Work Set must only contain a single InstanceReference or be an array of InstanceReference that contains exactly zero or one item."); + } + IEnumerable irs = Oms.GetRelatedInstances(parm, Oms.GetInstance(KnownRelationshipGuids.Work_Set__has_valid__Class)); if (irs.Count() > 0) { - foreach (InstanceWrapper iw in insts) + foreach (InstanceHandle ir in insts) { - InstanceHandle ir = iw.Handle; InstanceHandle parentClass = Oms.GetParentClass(ir); if (!irs.Contains(parentClass)) { @@ -130,10 +109,34 @@ public class OmsContext } } } - } - else - { - throw new ArgumentException(String.Format("cannot assign literal data '{0}' to a Work Set", value)); + else if (value is IEnumerable) + { + IEnumerable insts = (IEnumerable)value; + if (singular && insts.Count() > 1) + { + throw new InvalidOperationException("Singular Work Set must only contain a single InstanceReference or be an array of InstanceReference that contains exactly zero or one item."); + } + IEnumerable irs = Oms.GetRelatedInstances(parm, Oms.GetInstance(KnownRelationshipGuids.Work_Set__has_valid__Class)); + if (irs != null) + { + if (irs.Count() > 0) + { + foreach (InstanceWrapper iw in insts) + { + InstanceHandle ir = iw.Handle; + InstanceHandle parentClass = Oms.GetParentClass(ir); + if (!irs.Contains(parentClass)) + { + throw new ArgumentException("instance reference must be an instance of appropriate class"); + } + } + } + } + } + else + { + throw new ArgumentException(String.Format("cannot assign literal data '{0}' to a Work Set", value)); + } } } _WorkData[parm] = value; diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsSystemRoutineBuilder.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsSystemRoutineBuilder.cs index 586a499..aab0a4a 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsSystemRoutineBuilder.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsSystemRoutineBuilder.cs @@ -32,7 +32,7 @@ public class OmsSystemRoutineBuilder InstanceHandle handle = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.SystemAttributeRoutine), globalIdentifier); SystemAttributeRoutine routine = new SystemAttributeRoutine(Oms, globalIdentifier, func); - Oms.RegisterSystemRoutine(globalIdentifier, routine); + Oms.RegisterSystemRoutine(routine); return routine; } public SystemAttributeRoutine CreateSystemAttributeRoutine(Guid globalIdentifier, Func func) @@ -40,7 +40,7 @@ public class OmsSystemRoutineBuilder InstanceHandle handle = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.SystemAttributeRoutine), globalIdentifier); SystemAttributeRoutine routine = new SystemAttributeRoutine(Oms, globalIdentifier, func); - Oms.RegisterSystemRoutine(globalIdentifier, routine); + Oms.RegisterSystemRoutine(routine); return routine; } public SystemInstanceSetRoutine CreateSystemInstanceSetRoutine(Guid globalIdentifier, Func> func) @@ -48,7 +48,7 @@ public class OmsSystemRoutineBuilder InstanceHandle handle = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.SystemInstanceSetRoutine), globalIdentifier); SystemInstanceSetRoutine routine = new SystemInstanceSetRoutine(Oms, globalIdentifier, func); - Oms.RegisterSystemRoutine(globalIdentifier, routine); + Oms.RegisterSystemRoutine(routine); return routine; } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oop/DeferredInstanceWrapper.cs b/mocha-dotnet/src/lib/Mocha.Core/Oop/DeferredInstanceWrapper.cs index b4eeb13..8fb86d3 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oop/DeferredInstanceWrapper.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oop/DeferredInstanceWrapper.cs @@ -20,15 +20,16 @@ namespace Mocha.Core.Oop; public abstract class DeferredInstanceWrapper : InstanceWrapper { private Oms oms; - private Guid globalIdentifier; + + public Guid GlobalIdentifier { get; private set; } public DeferredInstanceWrapper(Oms oms, Guid globalIdentifier) { this.oms = oms; - this.globalIdentifier = globalIdentifier; + GlobalIdentifier = globalIdentifier; } protected override InstanceHandle GetHandleInternal() { - return oms.GetInstance(globalIdentifier); + return oms.GetInstance(GlobalIdentifier); } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oop/Methods/UpdateBySystemRoutineMethod.cs b/mocha-dotnet/src/lib/Mocha.Core/Oop/Methods/UpdateBySystemRoutineMethod.cs new file mode 100644 index 0000000..e998e31 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Oop/Methods/UpdateBySystemRoutineMethod.cs @@ -0,0 +1,25 @@ +// 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.Oop.Methods; + +public class UpdateBySystemRoutineMethod : Method +{ + public override Guid ClassId => KnownInstanceGuids.MethodClasses.UpdateBySystemRoutineMethod; + internal UpdateBySystemRoutineMethod(InstanceHandle handle) : base(handle) { } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oop/SystemRoutines/SystemUpdateRoutine.cs b/mocha-dotnet/src/lib/Mocha.Core/Oop/SystemRoutines/SystemUpdateRoutine.cs new file mode 100644 index 0000000..d54600f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Oop/SystemRoutines/SystemUpdateRoutine.cs @@ -0,0 +1,34 @@ +// 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.Oop; + +public class SystemUpdateRoutine : SystemRoutine +{ + public override Guid ClassId => KnownInstanceGuids.Classes.SystemUpdateRoutine; + private Action Func { get; } + internal SystemUpdateRoutine(Oms oms, Guid globalIdentifier, Action func) : base(oms, globalIdentifier) + { + Func = func; + } + + protected override object? ExecuteInternal(Oms oms, OmsContext context) + { + Func?.Invoke(oms, context); + return null; + } +} diff --git a/mocha-dotnet/src/lib/Mocha.Core/Response.cs b/mocha-dotnet/src/lib/Mocha.Core/Response.cs new file mode 100644 index 0000000..66f85be --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Response.cs @@ -0,0 +1,30 @@ +// 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 Mocha.Core.Responses; + +namespace Mocha.Core; + +public abstract class Response +{ + protected abstract JsonObject GetResponseInternal(Oms oms, OmsContext context); + public JsonObject GetResponse(Oms oms, OmsContext context) + { + return GetResponseInternal(oms, context); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Responses/FailureResponse.cs b/mocha-dotnet/src/lib/Mocha.Core/Responses/FailureResponse.cs new file mode 100644 index 0000000..888b1a3 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Responses/FailureResponse.cs @@ -0,0 +1,41 @@ +// 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; + +namespace Mocha.Core.Responses; + +public sealed class FailureResponse : Response +{ + public static Response NotImplemented { get; } = new FailureResponse("The method or operation is not implemented."); + public static Response UnexpectedFailure { get; } = new FailureResponse("An unexpected error occurred."); + + public FailureResponse(string message) + { + Message = message; + } + + public string Message { get; } = String.Empty; + + protected override JsonObject GetResponseInternal(Oms oms, OmsContext context) + { + JsonObject obj = new JsonObject(); + obj.Add("result", "failure"); + obj.Add("message", Message); + return obj; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Responses/PageResponse.cs b/mocha-dotnet/src/lib/Mocha.Core/Responses/PageResponse.cs new file mode 100644 index 0000000..b99753e --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Responses/PageResponse.cs @@ -0,0 +1,35 @@ +// 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; + +namespace Mocha.Core.Responses; + +public class PageResponse : Response +{ + private Func _func; + public PageResponse(Func func) + { + _func = func; + } + + protected override JsonObject GetResponseInternal(Oms oms, OmsContext context) + { + JsonObject obj = _func(oms, context); + return obj; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Responses/RedirectResponse.cs b/mocha-dotnet/src/lib/Mocha.Core/Responses/RedirectResponse.cs new file mode 100644 index 0000000..1f498cd --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Responses/RedirectResponse.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 . + +using System.Text.Json.Nodes; + +namespace Mocha.Core.Responses; + +public class RedirectResponse : Response +{ + public RedirectResponse(string destinationUrl) + { + DestinationUrl = destinationUrl; + } + public string DestinationUrl { get; } + + protected override JsonObject GetResponseInternal(Oms oms, OmsContext context) + { + JsonObject obj = new JsonObject(); + obj.Add("result", "success"); + obj.Add("type", "redirect"); + obj.Add("destinationUrl", DestinationUrl); + return obj; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutedEvent.cs b/mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutedEvent.cs new file mode 100644 index 0000000..308324f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutedEvent.cs @@ -0,0 +1,36 @@ +// 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 Mocha.Core.Oop; + +namespace Mocha.Core; + +public class SystemRoutineExecutedEventArgs : EventArgs +{ + public Oms OMS { get; } + public OmsContext Context { get; } + public SystemRoutine SystemRoutine { get; } + public object? ReturnValue { get; } + + internal SystemRoutineExecutedEventArgs(Oms oms, OmsContext context, SystemRoutine systemRoutine, object? returnValue) + { + OMS = oms; + Context = context; + SystemRoutine = systemRoutine; + ReturnValue = returnValue; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutingEvent.cs b/mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutingEvent.cs new file mode 100644 index 0000000..e8ef866 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/SystemRoutineExecutingEvent.cs @@ -0,0 +1,37 @@ +// 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.ComponentModel; +using Mocha.Core.Oop; + +namespace Mocha.Core; + +public class SystemRoutineExecutingEventArgs : CancelEventArgs +{ + public Oms OMS { get; } + public OmsContext Context { get; } + public SystemRoutine SystemRoutine { get; } + public object? ReturnValue { get; set; } + + internal SystemRoutineExecutingEventArgs(Oms oms, OmsContext context, SystemRoutine systemRoutine, object? returnValue) + { + OMS = oms; + Context = context; + SystemRoutine = systemRoutine; + ReturnValue = returnValue; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/JsonRenderer.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/JsonRenderer.cs new file mode 100644 index 0000000..8fc0b37 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/JsonRenderer.cs @@ -0,0 +1,141 @@ +// 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; + +namespace Mocha.Core.UI; + +public class JsonRenderer : Renderer +{ + public JsonRenderer(Oms oms) : base(oms) + { + } + + public JsonArray InstancesToJson(IEnumerable ihs) + { + return JsonRenderer.InstancesToJson(OMS, ihs); + } + + public static JsonArray InstancesToJson(Oms oms, IEnumerable ihs) + { + JsonArray array = new JsonArray(); + foreach (InstanceHandle ih in ihs) + { + array.Add(InstanceToJson(oms, ih)); + } + return array; + } + + public JsonObject InstanceToJson(InstanceHandle ih) + { + return JsonRenderer.InstanceToJson(OMS, ih); + } + + public static JsonObject InstanceToJson(Oms oms, InstanceHandle ih, bool includeParentClass = true) + { + JsonObject objInstance = new JsonObject(); + objInstance.Add("iid", JsonValue.Create(oms.GetInstanceKey(ih).ToString())); + objInstance.Add("globalIdentifier", JsonValue.Create(oms.GetGlobalIdentifier(ih).ToString())); + objInstance.Add("text", JsonValue.Create(oms.GetInstanceText(ih))); + + if (includeParentClass) + { + InstanceHandle parentClass = oms.GetParentClass(ih); + + JsonObject objParentClass = new JsonObject(); + objParentClass.Add("iid", JsonValue.Create(oms.GetInstanceKey(parentClass).ToString())); + objParentClass.Add("globalIdentifier", JsonValue.Create(oms.GetGlobalIdentifier(parentClass).ToString())); + objParentClass.Add("text", JsonValue.Create(oms.GetInstanceText(parentClass))); + + InstanceHandle defaultTask = oms.GetRelatedInstance(parentClass, oms.GetInstance(KnownRelationshipGuids.Class__has_default__Task)); + objParentClass.Add("defaultTask", InstanceToJson(oms, defaultTask, false)); + + objInstance.Add("parentClass", objParentClass); + } + return objInstance; + } + public static JsonArray GetAttributesAsJson(Core.Oms oms, InstanceHandle h, InstanceHandle hAtt = default(InstanceHandle)) + { + JsonArray a = new JsonArray(); + IEnumerable atts = oms.GetAttributes(h); + foreach (MochaAttribute att in atts) + { + if (hAtt != InstanceHandle.Empty) + { + if (!att.AttributeInstance.Equals(hAtt)) + { + continue; + } + } + + string iid = oms.GetInstanceKey(att.AttributeInstance).ToString(); + JsonObject o = new JsonObject(); + o.Add("iid", JsonValue.Create(iid)); + + string attrText = oms.GetInstanceText(att.AttributeInstance); + o.Add("name", JsonValue.Create(attrText)); + + JsonArray aa = new JsonArray(); + foreach (AttributeValue val in att.Values) + { + JsonObject ao = new JsonObject(); + ao.Add("effectiveDate", JsonValue.Create(val.EffectiveDate)); + ao.Add("value", JsonValue.Create(val.Value)); + aa.Add(ao); + } + o.Add("values", aa); + + a.Add(o); + } + return a; + } + public static JsonArray GetRelationshipsAsJson(Oms oms, InstanceHandle h, InstanceHandle hRel = default(InstanceHandle)) + { + JsonArray a = new JsonArray(); + IEnumerable rels = oms.GetRelationships(h); + foreach (MochaRelationship rel in rels) + { + if (hRel != InstanceHandle.Empty) + { + if (!rel.RelationshipInstance.Equals(hRel)) + { + continue; + } + } + + JsonObject o = new JsonObject(); + o.Add("iid", JsonValue.Create(oms.GetInstanceKey(rel.RelationshipInstance).ToString())); + + string attrText = oms.GetInstanceText(rel.RelationshipInstance); + o.Add("name", JsonValue.Create(attrText)); + + JsonArray aa = new JsonArray(); + foreach (RelationshipValue val in rel.Values) + { + JsonObject ao = new JsonObject(); + ao.Add("effectiveDate", JsonValue.Create(val.EffectiveDate)); + ao.Add("targetInstances", JsonRenderer.InstancesToJson(oms, val.TargetInstances)); + aa.Add(ao); + } + o.Add("values", aa); + + a.Add(o); + } + return a; + } + +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Renderer.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Renderer.cs index ad398ae..ed8fca8 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Renderer.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Renderer.cs @@ -27,26 +27,49 @@ public class Renderer OMS = oms; } - - public Widget Parse(InstanceHandle h) + public Widget Parse(OmsContext context, InstanceHandle instance, InstanceHandle parentElementContent, InstanceHandle parentElement) { - return Parse(h, InstanceHandle.Empty); + return Parse(context, instance, parentElementContent, parentElement, InstanceHandle.Empty); } - public Widget Parse(InstanceHandle h, InstanceHandle parentElementContent) + public Widget Parse(OmsContext context, InstanceHandle instance, InstanceHandle parentElementContent, InstanceHandle parentElement, InstanceHandle processingInstance) { - Widget[] availableWidgets = MBS.Core.Reflection.TypeLoader.GetAvailableTypes(); + Widget[] availableWidgets = MBS.Core.Reflection.TypeLoader.GetAvailableTypes(new System.Reflection.Assembly[] { typeof(Widget).Assembly }, true); + Console.Error.WriteLine("got {0} available widgets", availableWidgets.Length); - InstanceHandle parentClassInstance = OMS.GetParentClass(h); + InstanceHandle parentClassInstance = OMS.GetParentClass(instance); Guid gid = OMS.GetGlobalIdentifier(parentClassInstance); + + InstanceHandle layoutInstance = OMS.GetRelatedInstance(parentElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Layout)); + Guid gidLayout = OMS.GetGlobalIdentifier(layoutInstance); Widget? useWidget = null; foreach (Widget w in availableWidgets) { - if (w.SupportedClassGuids.Contains(gid)) + if (instance != InstanceHandle.Empty) { - useWidget = w; - break; + foreach (Guid gid2 in w.SupportedClassGuids) + { + if (OMS.IsSubclassOf(OMS.GetInstance(gid), OMS.GetInstance(gid2))) + { + useWidget = w; + break; + } + } } + + if (layoutInstance != InstanceHandle.Empty) + { + foreach (Guid gid2 in w.SupportedLayoutGuids) + { + if (OMS.IsSubclassOf(OMS.GetInstance(gid), OMS.GetInstance(gid2))) + { + useWidget = w; + break; + } + } + } + if (useWidget != null) + break; } if (useWidget == null) @@ -55,16 +78,98 @@ public class Renderer return null; } + object? value = null; + InstanceHandle updateWithRWMB = OMS.GetRelatedInstance(parentElementContent, OMS.GetInstance(KnownRelationshipGuids.Derived_Element_Content__update_with__Executable_returning_Work_Data)); + if (updateWithRWMB != InstanceHandle.Empty) + { + if (OMS.IsInstanceOf(updateWithRWMB, OMS.GetInstance(KnownInstanceGuids.Classes.Executable))) + { + value = OMS.Execute(context, updateWithRWMB); + } + else + { + value = updateWithRWMB; + } + } + else + { + if (processingInstance != InstanceHandle.Empty) + { + InstanceHandle ecinst = OMS.GetRelatedInstance(parentElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + if (OMS.IsInstanceOf(processingInstance, ecinst)) + { + value = processingInstance; + } + else if (OMS.IsInstanceOf(ecinst, OMS.GetInstance(KnownInstanceGuids.Classes.Attribute))) + { + value = OMS.UnsafeGetAttributeValue(processingInstance, ecinst); + } + else + { + if (OMS.IsInstanceOf(ecinst, OMS.GetInstance(KnownInstanceGuids.Classes.Relationship))) + { + value = OMS.GetRelatedInstances(processingInstance, ecinst); + } + else if (OMS.IsInstanceOf(ecinst, OMS.GetInstance(KnownInstanceGuids.Classes.Class))) + { + value = context.GetWorkData(ecinst); + } + else + { + // executable returning instance set? + InstanceHandle workSet = OMS.Execute(context, ecinst); + value = context.GetWorkData(workSet); + } + } + } + } + + if (useWidget is MonikerList) + { + if (value is InstanceHandle) + { + ((MonikerList)useWidget).SelectedInstances.Add((InstanceHandle)value); + context.SetWorkData(instance, (InstanceHandle)value); + } + else if (value is IEnumerable ee) + { + ((MonikerList)useWidget).SelectedInstances.AddRange(ee); + } + } + useWidget.OMS = OMS; - useWidget.ParentInstance = h; + + if (parentElement != InstanceHandle.Empty) + { + IEnumerable elementContents = OMS.GetRelatedInstances(parentElement, OMS.GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); + InstanceHandle firstElementContent = elementContents.First(); + if (firstElementContent != parentElementContent) + { + InstanceHandle firstElementContentInstance = OMS.GetRelatedInstance(firstElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + + // InstanceHandle realInstance = OMS.GetInstancesOf(firstElementContentInstance).First(); + object realInstance = context.GetWorkData(firstElementContentInstance); + if (realInstance is InstanceHandle) + { + useWidget.TargetInstance = (InstanceHandle)realInstance; + } + } + } + else + { + useWidget.TargetInstance = processingInstance; + } + + useWidget.ParentInstance = instance; useWidget.ParentElementContent = parentElementContent; useWidget.Renderer = this; return useWidget; } - public Widget ParseElementContent(InstanceHandle content) + public Widget ParseElementContent(OmsContext context, InstanceHandle element, InstanceHandle content) { - InstanceHandle h = OMS.GetRelatedInstance(content, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); - return Parse(h, content); + InstanceHandle elementContentInstance = OMS.GetRelatedInstance(content, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + return Parse(context, elementContentInstance, content, element); } + } \ 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 5211ed7..8cfd752 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widget.cs @@ -21,21 +21,33 @@ namespace Mocha.Core.UI; public abstract class Widget { - protected abstract void RenderJSONInternal(JsonObject obj); + protected abstract void RenderJSONInternal(OmsContext context, JsonObject obj); protected abstract string GetWidgetName(); - + + /// + /// The instance on which to retrieve attributes. If empty, the Parent Instance is used. + /// + /// + public InstanceHandle TargetInstance { get; internal set; } = InstanceHandle.Empty; + /// + /// The instance of the Attribute to retrieve. + /// + /// public InstanceHandle ParentInstance { get; internal set; } = InstanceHandle.Empty; public InstanceHandle ParentElementContent { get; internal set; } = InstanceHandle.Empty; - public JsonObject ToJSONObject() + public bool? Enabled { get; set; } = null; + + public JsonObject ToJSONObject(OmsContext context) { JsonObject obj = new JsonObject(); InsertCommonProperties(obj); - RenderJSONInternal(obj); + RenderJSONInternal(context, obj); return obj; } public virtual Guid[] SupportedClassGuids { get; } = new Guid[0]; + public virtual Guid[] SupportedLayoutGuids { get; } = new Guid[0]; public Oms OMS { get; internal set; } public Renderer Renderer { get; internal set; } @@ -45,6 +57,12 @@ public abstract class Widget return displayOptions; } + protected virtual bool ShouldBeEnabled() + { + IEnumerable displayOptions = GetDisplayOptions(); + return !displayOptions.Contains(OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.NotEnterable)); + } + private void InsertCommonProperties(JsonObject obj) { obj.Add("widget", GetWidgetName()); @@ -56,18 +74,10 @@ public abstract class Widget if (!displayOptions.Contains(OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.DoNotShowLabel))) { - string label = OMS.GetAttributeValue(ParentElementContent, OMS.GetInstance(KnownAttributeGuids.Text.LabelOverride)); - if (label == null) - { - label = OMS.GetAttributeValue(ParentElementContent, OMS.GetInstance(KnownAttributeGuids.Text.Label)); - if (label == null) - { - label = OMS.GetInstanceText(ParentInstance); - } - } + string label = OMS.GetLabelForUIElement(ParentElementContent); obj.Add("label", label); } - if (displayOptions.Contains(OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.NotEnterable))) + if (!ShouldBeEnabled()) { obj.Add("enabled", false); } 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 faebf5e..fb0c3bc 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Element.cs @@ -24,7 +24,45 @@ public class Element : Widget { protected override string GetWidgetName() { - InstanceHandle layout = OMS.GetRelatedInstance(ParentElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Layout)); + IEnumerable displayOptions = OMS.GetRelatedInstances(ParentElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Element_Content_Display_Option)); + InstanceHandle doSingular = OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.Singular); + InstanceHandle doShowSubelementsVertically = OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.ShowSubelementsVertically); + + InstanceHandle layout = OMS.GetRelatedInstance(ParentInstance, OMS.GetInstance(KnownRelationshipGuids.Element__has__Layout)); + if (layout == InstanceHandle.Empty) + { + Console.Error.WriteLine("!!! FIXME !!! Replace `Element Content.has Layout` with `Element.has Layout`"); + layout = OMS.GetRelatedInstance(ParentElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Layout)); + } + + if (layout == InstanceHandle.Empty) + { + // if we hae `Show Subelements Vertically`, we are a vbox + if (displayOptions.Contains(doShowSubelementsVertically)) + { + return "vbox"; + } + else + { + // if we have no layout, and we have multiple instances, we are a grid view + if (!displayOptions.Contains(doSingular)) + { + if (ParentElementContent != InstanceHandle.Empty) + { + if (OMS.GetRelatedInstances(ParentInstance, OMS.GetInstance(KnownRelationshipGuids.Element__has__Element_Content)).Count() > 1) + { + Console.Error.WriteLine("FIXME: 'grid' returned for Element {0}", OMS.GetGlobalIdentifier(ParentInstance)); + return "grid"; + } + } + } + + // otherwise, we are a hbox + // return "hbox"; + return "vbox"; + } + } + if (OMS.IsInstanceOf(layout, OMS.GetInstance(KnownInstanceGuids.Classes.GroupLayout))) { InstanceHandle ihGLOFieldSet = OMS.GetInstance(KnownInstanceGuids.GroupLayoutOptions.FieldSet); @@ -39,29 +77,149 @@ public class Element : Widget public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.Element }; - protected override void RenderJSONInternal(JsonObject obj) + protected override void RenderJSONInternal(OmsContext context, JsonObject obj) { - JsonArray objA = new JsonArray(); + IEnumerable displayOptions = OMS.GetRelatedInstances(ParentElementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Element_Content_Display_Option)); + InstanceHandle doSingular = OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.Singular); + + InstanceHandle doDoNotShow = OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.DoNotShow); + InstanceHandle doDoNotShowIfEmpty = OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.DoNotShowIfEmpty); IEnumerable contents = OMS.GetRelatedInstances(ParentInstance, OMS.GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); - foreach (InstanceHandle content in contents) + + if (!displayOptions.Contains(doSingular) && ParentElementContent != InstanceHandle.Empty && contents.Count() > 1) { - Widget widget = Renderer.ParseElementContent(content); - if (widget == null) + JsonArray objColumns = new JsonArray(); + int i = 1; + foreach (InstanceHandle ec in contents) { - JsonObject objC = new JsonObject(); - objC.Add("widget", JsonValue.Create("error")); - objC.Add("text", JsonValue.Create("could not create EC")); - objC.Add("ecid", JsonValue.Create(OMS.GetInstanceKey(content).ToString())); - objC.Add("iid", JsonValue.Create(OMS.GetInstanceKey(OMS.GetRelatedInstance(content, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance))).ToString())); - objA.Add(objC); + IEnumerable ecDisplayOptions = OMS.GetRelatedInstances(ec, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Element_Content_Display_Option)); + InstanceHandle inst = OMS.GetRelatedInstance(ec, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + + JsonObject objColumn = new JsonObject(); + objColumn.Add("widget", "column"); + objColumn.Add("ecid", OMS.GetInstanceKey(ec).ToString()); + objColumn.Add("iid", OMS.GetInstanceKey(inst).ToString()); + objColumn.Add("enabled", false); + objColumn.Add("label", OMS.GetLabelForUIElement(ec)); + objColumn.Add("columnId", i.ToString()); + if (ecDisplayOptions.Contains(doDoNotShow)) + { + objColumn.Add("visible", false); + } + objColumns.Add(objColumn); + + i++; } - else + obj.Add("columns", objColumns); + + obj.Add("compareWorkboxId", OMS.GetInstanceKey(ParentElementContent).ToString()); + + int deepRowCount = 0; + obj.Add("deepRowCount", deepRowCount); + + int subtotalRowCount = 0; + obj.Add("subtotalRowCount", subtotalRowCount); + + obj.Add("gridType", "LIST"); + + JsonArray? ary = BuildElement(context, ParentElementContent); + if (ary != null) { - objA.Add(widget.ToJSONObject()); + int rowCount = ary.Count; + obj.Add("rowCount", rowCount); + obj.Add("rows", ary); } } + else + { + JsonArray objA = new JsonArray(); - obj.Add("children", objA); + foreach (InstanceHandle content in contents) + { + Widget widget = Renderer.ParseElementContent(context, ParentInstance, content); + if (widget == null) + { + JsonObject objC = new JsonObject(); + objC.Add("widget", JsonValue.Create("error")); + objC.Add("text", JsonValue.Create("could not create EC")); + objC.Add("ecid", JsonValue.Create(OMS.GetInstanceKey(content).ToString())); + objC.Add("iid", JsonValue.Create(OMS.GetInstanceKey(OMS.GetRelatedInstance(content, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance))).ToString())); + objA.Add(objC); + } + else + { + objA.Add(widget.ToJSONObject(context)); + } + } + + obj.Add("children", objA); + } } + + private JsonArray? BuildElement(OmsContext context, InstanceHandle elementContent) + { + InstanceHandle bemProcess = OMS.GetRelatedInstance(elementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__built_from__BEM_Process)); + InstanceHandle parentInstance = OMS.GetRelatedInstance(elementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + + IEnumerable parentECDisplayOptions = OMS.GetRelatedInstances(elementContent, OMS.GetInstance(KnownRelationshipGuids.Element_Content__has__Element_Content_Display_Option)); + bool disabled = parentECDisplayOptions.Contains(OMS.GetInstance(KnownInstanceGuids.ElementContentDisplayOptions.NotEnterable)); + + IEnumerable contents = OMS.GetRelatedInstances(parentInstance, OMS.GetInstance(KnownRelationshipGuids.Element__has__Element_Content)); + InstanceHandle loopExecutable = OMS.GetRelatedInstance(bemProcess, OMS.GetInstance(KnownRelationshipGuids.BEM_Process__uses_loop__Executable_returning_Instance_Set)); + + int gridId = 468; + + InstanceHandle workSet = OMS.Execute(context, loopExecutable); + object? obj = context.GetWorkData(workSet); + if (obj is IEnumerable list) + { + 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; + } + + return null; + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/File.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/File.cs index 78de9d1..b59a46f 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/File.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/File.cs @@ -25,7 +25,7 @@ public class File : Widget protected override string GetWidgetName() => "image"; public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.File }; - protected override void RenderJSONInternal(JsonObject obj) + protected override void RenderJSONInternal(OmsContext context, JsonObject obj) { obj.Add("target", OMS.BuildAccessKeyForOmsAttachment(ParentInstance)); if (ParentElementContent != null) diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/MonikerList.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/MonikerList.cs index 2a67e8c..155bfe2 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/MonikerList.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/MonikerList.cs @@ -22,9 +22,36 @@ namespace Mocha.Core.UI; public class MonikerList : Widget { protected override string GetWidgetName() => "monikerList"; - public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.WorkSet }; + public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.WorkSet, KnownInstanceGuids.Classes.Class, KnownInstanceGuids.Classes.ReturnInstanceSetMethodBinding }; - protected override void RenderJSONInternal(JsonObject obj) + public List SelectedInstances { get; } = new List(); + + protected override void RenderJSONInternal(OmsContext context, JsonObject obj) { + JsonArray aryInstances = new JsonArray(); + foreach (InstanceHandle ih in SelectedInstances) + { + JsonObject obj2 = new JsonObject(); + obj2.Add("widget", "moniker"); + obj2.Add("instanceId", OMS.GetInstanceKey(ih).ToString()); + obj2.Add("text", OMS.GetInstanceText(ih)); + obj2.Add("rt", true); // related task? + obj2.Add("pv", true); // preview? + obj2.Add("v", true); // view? + aryInstances.Add(obj2); + } + obj.Add("instances", aryInstances); + if (SelectedInstances.Any()) + { + obj.Add("selfUriTemplate", String.Format("/{0}/inst/{1}/{2}", OMS.GetTenantName(OMS.CurrentTenant), OMS.GetInstanceKey(ParentInstance).ToString(), OMS.GetInstanceKey(SelectedInstances.First()).ToString())); + obj.Add("relatedTasksUriTemplate", String.Format("/{0}/inst/{1}/rel-tasks", OMS.GetTenantName(OMS.CurrentTenant), OMS.GetInstanceKey(SelectedInstances.First()).ToString())); + } + obj.Add("text", OMS.GetInstanceText(ParentInstance)); + obj.Add("singular", true); } + + protected override bool ShouldBeEnabled() + { + return base.ShouldBeEnabled() && !(OMS.IsInstanceOf(ParentInstance, OMS.GetInstance(KnownInstanceGuids.Classes.ReturnInstanceSetMethodBinding))); + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs index 389cbfe..ceabef4 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs @@ -34,16 +34,39 @@ public class Text : Widget } return "text"; } - public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.TextAttribute }; + public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.TextAttribute, KnownInstanceGuids.Classes.ExecutableReturningAttribute }; - protected override void RenderJSONInternal(JsonObject obj) + protected override void RenderJSONInternal(OmsContext context, JsonObject obj) { - string value = OMS.GetAttributeValue(ParentInstance, OMS.GetInstance(KnownAttributeGuids.Text.Value)); - if (value != null) + string? value = null; + + if (OMS.IsInstanceOf(ParentInstance, OMS.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) { - obj.Add("value", value); + if (TargetInstance != InstanceHandle.Empty) + { + // first check our target instance for the attribute. this MAY be empty + value = OMS.GetAttributeValue(TargetInstance, ParentInstance); + } + } + else + { + InstanceHandle workData = OMS.Execute(context, ParentInstance); + object? val = context.GetWorkData(workData); + value = val?.ToString(); } + if (value == null) + { + // no value specified for the target instance, so get default value of the Text Attribute + value = OMS.GetAttributeValue(ParentInstance, OMS.GetInstance(KnownAttributeGuids.Text.Value)); + } + + + if (value != null) + { + // if we finally have a value, use it + obj.Add("value", value); + } if (OMS.TryGetAttributeValue(ParentInstance, OMS.GetInstance(KnownAttributeGuids.Numeric.MaximumLength), out decimal maximumLength)) { obj.Add("maximumLength", maximumLength);