diff --git a/Esiur.Stores.MongoDB/MongoDBStore.cs b/Esiur.Stores.MongoDB/MongoDBStore.cs index 93d0526..733bd43 100644 --- a/Esiur.Stores.MongoDB/MongoDBStore.cs +++ b/Esiur.Stores.MongoDB/MongoDBStore.cs @@ -52,10 +52,10 @@ namespace Esiur.Stores.MongoDB [Public] - public event ResourceEventHanlder ResourceAdded; + public event ResourceEventHanlder ResourceAdded; [Public] - public event ResourceEventHanlder ResourceRemoved; + public event ResourceEventHanlder ResourceRemoved; int count = 0; @@ -225,7 +225,7 @@ namespace Esiur.Stores.MongoDB var doc = value.AsBsonDocument; if (doc["type"] == 0) { - return Warehouse.Get(doc["link"].AsString); + return Warehouse.Get(doc["link"].AsString); } // structure else if (doc["type"] == 1) { @@ -383,7 +383,7 @@ namespace Esiur.Stores.MongoDB foreach (var pt in template.Properties) { - var rt = pt.Info.GetValue(resource, null); + var rt = pt.PropertyInfo.GetValue(resource, null); values.Add(pt.Name, new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) }, @@ -612,7 +612,7 @@ namespace Esiur.Stores.MongoDB var pi = resource.GetType().GetProperty(pt.Name); #endif */ - var rt = pt.Info.GetValue(resource, null); + var rt = pt.PropertyInfo.GetValue(resource, null); values.Add(pt.Name, new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) }, @@ -841,7 +841,7 @@ namespace Esiur.Stores.MongoDB foreach (var child in children) { - var r = Warehouse.Get(child); + var r = Warehouse.Get(child); if (r is AsyncReply) rt.Add(r);// (AsyncReply)r); } @@ -873,7 +873,7 @@ namespace Esiur.Stores.MongoDB foreach (var parent in parents) { - var r = Warehouse.Get(parent); + var r = Warehouse.Get(parent); if (r is AsyncReply) rt.Add(r);// (AsyncReply)r); } diff --git a/Esiur/Core/AsyncReply.cs b/Esiur/Core/AsyncReply.cs index 5e463e2..3269d54 100644 --- a/Esiur/Core/AsyncReply.cs +++ b/Esiur/Core/AsyncReply.cs @@ -84,6 +84,8 @@ namespace Esiur.Core if (Debug) Console.WriteLine($"AsyncReply: {Id} Wait ended"); + if (exception != null) + throw exception; return result; } diff --git a/Esiur/Data/ResourceJsonConverter.cs b/Esiur/Data/ResourceJsonConverter.cs index efbbd09..c8798cd 100644 --- a/Esiur/Data/ResourceJsonConverter.cs +++ b/Esiur/Data/ResourceJsonConverter.cs @@ -53,10 +53,10 @@ namespace Esiur.Data foreach (var pt in resource.Instance.Template.Properties) { - var rt = pt.Info.GetValue(resource, null); + var rt = pt.PropertyInfo.GetValue(resource, null); if (rt is DistributedPropertyContext) continue; - + writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name); if (rt is IResource) diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index 87d95d2..e7afce7 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -7,12 +7,12 @@ https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE http://www.esiur.com true - 1.7.1 + 1.8.1 https://github.com/esiur/esiur-dotnet Ahmed Kh. Zamil - 1.7.1 + 1.8.1.0 Esiur Foundation - 1.7.1 + 1.8.1.0 Esiur Esiur Esiur @@ -57,6 +57,7 @@ + @@ -65,7 +66,7 @@ - + @@ -76,6 +77,8 @@ + + \ No newline at end of file diff --git a/Esiur/Net/IIP/DistributedConnection.cs b/Esiur/Net/IIP/DistributedConnection.cs index dc8e1bd..ad175e8 100644 --- a/Esiur/Net/IIP/DistributedConnection.cs +++ b/Esiur/Net/IIP/DistributedConnection.cs @@ -458,7 +458,7 @@ namespace Esiur.Net.IIP case IIPPacketAction.QueryLink: IIPRequestQueryResources(packet.CallbackId, packet.ResourceLink); break; - + case IIPPacketAction.ResourceChildren: IIPRequestResourceChildren(packet.CallbackId, packet.ResourceId); break; @@ -470,6 +470,10 @@ namespace Esiur.Net.IIP IIPRequestInquireResourceHistory(packet.CallbackId, packet.ResourceId, packet.FromDate, packet.ToDate); break; + case IIPPacketAction.LinkTemplates: + IIPRequestLinkTemplates(packet.CallbackId, packet.ResourceLink); + break; + // Invoke case IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments: IIPRequestInvokeFunctionArrayArguments(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content); @@ -559,6 +563,7 @@ namespace Esiur.Net.IIP case IIPPacketAction.ResourceChildren: case IIPPacketAction.ResourceParents: case IIPPacketAction.ResourceHistory: + case IIPPacketAction.LinkTemplates: IIPReply(packet.CallbackId, packet.Content); break; @@ -699,10 +704,12 @@ namespace Esiur.Net.IIP else { //Console.WriteLine("User not found"); - SendParams().AddUInt8(0xc0) + SendParams() + .AddUInt8(0xc0) .AddUInt8((byte)ExceptionCode.UserOrTokenNotFound) .AddUInt16(15) - .AddString("Token not found").Done(); + .AddString("Token not found") + .Done(); } }); } @@ -710,12 +717,49 @@ namespace Esiur.Net.IIP { var errMsg = DC.ToBytes(ex.Message); + SendParams() + .AddUInt8(0xc0) + .AddUInt8((byte)ExceptionCode.GeneralFailure) + .AddUInt16((ushort)errMsg.Length) + .AddUInt8Array(errMsg) + .Done(); + } + } + else if (authPacket.RemoteMethod == AuthenticationMethod.None && authPacket.LocalMethod == AuthenticationMethod.None) + { + try + { + // Check if guests are allowed + if (Server.Membership.GuestsAllowed) + { + session.RemoteAuthentication.Username = "g-" + Global.GenerateCode(); + session.RemoteAuthentication.Domain = authPacket.Domain; + readyToEstablish = true; + SendParams() + .AddUInt8(0x80) + .Done(); + } + else + { + SendParams() + .AddUInt8(0xc0) + .AddUInt8((byte)ExceptionCode.AccessDenied) + .AddUInt16(18) + .AddString("Guests not allowed") + .Done(); + } + } + catch (Exception ex) + { + var errMsg = DC.ToBytes(ex.Message); + SendParams().AddUInt8(0xc0) .AddUInt8((byte)ExceptionCode.GeneralFailure) .AddUInt16((ushort)errMsg.Length) .AddUInt8Array(errMsg).Done(); } } + } else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Action) { @@ -808,7 +852,7 @@ namespace Esiur.Net.IIP Server?.Membership.Login(session); loginDate = DateTime.Now; - + }).Error(x => { openReply?.TriggerError(x); @@ -832,22 +876,32 @@ namespace Esiur.Net.IIP { if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Acknowledge) { - remoteNonce = authPacket.RemoteNonce; + if (authPacket.LocalMethod == AuthenticationMethod.None) + { + SendParams() + .AddUInt8(0x20) + .AddUInt16(0) + .Done(); + } + else if (authPacket.LocalMethod == AuthenticationMethod.Credentials + || authPacket.LocalMethod == AuthenticationMethod.Token) + { + remoteNonce = authPacket.RemoteNonce; - // send our hash - var hashFunc = SHA256.Create(); - //var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localPassword, localNonce, remoteNonce)); - var localHash = hashFunc.ComputeHash(new BinaryList() - .AddUInt8Array(localPasswordOrToken) - .AddUInt8Array(localNonce) - .AddUInt8Array(remoteNonce) - .ToArray()); - - SendParams() - .AddUInt8(0) - .AddUInt8Array(localHash) - .Done(); + // send our hash + var hashFunc = SHA256.Create(); + //var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localPassword, localNonce, remoteNonce)); + var localHash = hashFunc.ComputeHash(new BinaryList() + .AddUInt8Array(localPasswordOrToken) + .AddUInt8Array(localNonce) + .AddUInt8Array(remoteNonce) + .ToArray()); + SendParams() + .AddUInt8(0) + .AddUInt8Array(localHash) + .Done(); + } //SendParams((byte)0, localHash); } else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Action) @@ -867,7 +921,6 @@ namespace Esiur.Net.IIP if (remoteHash.SequenceEqual(authPacket.Hash)) { // send establish request - //SendParams((byte)0x20, (ushort)0); SendParams() .AddUInt8(0x20) .AddUInt16(0) @@ -981,32 +1034,29 @@ namespace Esiur.Net.IIP { if (trigger == ResourceTrigger.Open) { + if (this.Server != null) + return new AsyncReply(true); + + var host = Instance.Name.Split(':'); + + var address = host[0]; + var port = ushort.Parse(host[1]); + // assign domain from hostname if not provided + var domain = Domain != null ? Domain : address; + if (Username != null // Instance.Attributes.ContainsKey("username") && Password != null)/// Instance.Attributes.ContainsKey("password")) { - // assign domain from hostname if not provided - - var host = Instance.Name.Split(':'); - - var address = host[0]; - var port = ushort.Parse(host[1]); - - var domain = Domain != null ? Domain : address; - - return Connect(AuthenticationMethod.Credentials, null, address, port, Username, 0, DC.ToBytes(Password), domain); - } else if (Token != null) { - var host = Instance.Name.Split(':'); - - var address = host[0]; - var port = ushort.Parse(host[1]); - - var domain = Domain != null ? Domain : address; - return Connect(AuthenticationMethod.Token, null, address, port, null, TokenIndex, DC.ToBytes(Token), domain); + } + else + { + + return Connect(AuthenticationMethod.None, null, address, port, null, 0, null, domain); } } diff --git a/Esiur/Net/IIP/DistributedConnectionProtocol.cs b/Esiur/Net/IIP/DistributedConnectionProtocol.cs index 0f20ee2..fca4484 100644 --- a/Esiur/Net/IIP/DistributedConnectionProtocol.cs +++ b/Esiur/Net/IIP/DistributedConnectionProtocol.cs @@ -498,8 +498,8 @@ namespace Esiur.Net.IIP //r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred; //r.Instance.ResourceModified -= Instance_PropertyModified; //r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed; - - + + // r.Instance.Children.OnAdd -= Children_OnAdd; // r.Instance.Children.OnRemoved -= Children_OnRemoved; @@ -800,16 +800,16 @@ namespace Esiur.Net.IIP // create the resource var resource = Activator.CreateInstance(type, args) as IResource; - Warehouse.Put( name, resource, store as IStore, parent).Then(ok => - { - SendReply(IIPPacket.IIPPacketAction.CreateResource, callback) - .AddUInt32(resource.Instance.Id) - .Done(); + Warehouse.Put(name, resource, store as IStore, parent).Then(ok => + { + SendReply(IIPPacket.IIPPacketAction.CreateResource, callback) + .AddUInt32(resource.Instance.Id) + .Done(); - }).Error(x => - { - SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.AddToStoreFailed); - }); + }).Error(x => + { + SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.AddToStoreFailed); + }); }); }); @@ -1077,6 +1077,50 @@ namespace Esiur.Net.IIP } + void IIPRequestLinkTemplates(uint callback, string resourceLink) + { + Console.WriteLine("IIPRequestLinkTemplates " + DateTime.UtcNow); + Action queryCallback = (r) => + { + if (r == null) + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); + else + { + var list = r.Where(x => x.Instance.Applicable(session, ActionType.ViewTemplate, null) != Ruling.Denied).ToArray(); + + if (list.Length == 0) + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); + else + { + // get all templates related to this resource + + var msg = new BinaryList(); + + var templates = new List(); + foreach (var resource in list) + templates.AddRange(ResourceTemplate.GetRuntimeTypes(resource.Instance.Template).Where(x => !templates.Contains(x))); + + foreach(var t in templates) + { + msg.AddInt32(t.Content.Length) + .AddUInt8Array(t.Content); + } + + // digggg + SendReply(IIPPacket.IIPPacketAction.LinkTemplates, callback) + .AddInt32(msg.Length) + .AddUInt8Array(msg.ToArray()) + .Done(); + } + } + }; + + if (Server?.EntryPoint != null) + Server.EntryPoint.Query(resourceLink, this).Then(queryCallback); + else + Warehouse.Query(resourceLink).Then(queryCallback); + } + void IIPRequestTemplateFromClassName(uint callback, string className) { Warehouse.GetTemplate(className).Then((t) => @@ -1096,19 +1140,18 @@ namespace Esiur.Net.IIP void IIPRequestTemplateFromClassId(uint callback, Guid classId) { - Warehouse.GetTemplate(classId).Then((t) => + var t = Warehouse.GetTemplate(classId); + + if (t != null) + SendReply(IIPPacket.IIPPacketAction.TemplateFromClassId, callback) + .AddInt32(t.Content.Length) + .AddUInt8Array(t.Content) + .Done(); + else { - if (t != null) - SendReply(IIPPacket.IIPPacketAction.TemplateFromClassId, callback) - .AddInt32(t.Content.Length) - .AddUInt8Array(t.Content) - .Done(); - else - { - // reply failed - SendError(ErrorType.Management, callback, (ushort)ExceptionCode.TemplateNotFound); - } - }); + // reply failed + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.TemplateNotFound); + } } @@ -1165,7 +1208,7 @@ namespace Esiur.Net.IIP } [Attribute] - public ExceptionLevel ExceptionLevel { get; set; } + public ExceptionLevel ExceptionLevel { get; set; } = ExceptionLevel.Code | ExceptionLevel.Message | ExceptionLevel.Source | ExceptionLevel.Trace; private Tuple SummerizeException(Exception ex) @@ -1477,17 +1520,17 @@ namespace Esiur.Net.IIP .AddUInt8Array(Codec.Compose(res, this)) .Done(); - }).Error(ex => - { - var (code, msg) = SummerizeException(ex); - SendError(ErrorType.Exception, callback, code, msg); - }).Progress((pt, pv, pm) => - { - SendProgress(callback, pv, pm); - }).Chunk(v => - { - SendChunk(callback, v); - }); + }).Error(ex => + { + var (code, msg) = SummerizeException(ex); + SendError(ErrorType.Exception, callback, code, msg); + }).Progress((pt, pv, pm) => + { + SendProgress(callback, pv, pm); + }).Chunk(v => + { + SendChunk(callback, v); + }); } else { @@ -1539,7 +1582,7 @@ namespace Esiur.Net.IIP } else { - lock(subscriptionsLock) + lock (subscriptionsLock) { if (!subscriptions.ContainsKey(r)) { @@ -1711,51 +1754,51 @@ namespace Esiur.Net.IIP }); } -// void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age) -// { -// Warehouse.GetById(resourceId).Then((r) => -// { -// if (r != null) -// { -// var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); -// if (pt != null) -// { -// if (r.Instance.GetAge(index) > age) -// { -//#if NETSTANDARD -// var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); -//#else -// var pi = r.GetType().GetProperty(pt.Name); -//#endif -// if (pi != null) -// { -// SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) -// .AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) -// .Done(); -// } -// else -// { -// // pt found, pi not found, this should never happen -// } -// } -// else -// { -// SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) -// .AddUInt8((byte)DataType.NotModified) -// .Done(); -// } -// } -// else -// { -// // pt not found -// } -// } -// else -// { -// // resource not found -// } -// }); -// } + // void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age) + // { + // Warehouse.GetById(resourceId).Then((r) => + // { + // if (r != null) + // { + // var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); + // if (pt != null) + // { + // if (r.Instance.GetAge(index) > age) + // { + //#if NETSTANDARD + // var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); + //#else + // var pi = r.GetType().GetProperty(pt.Name); + //#endif + // if (pi != null) + // { + // SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) + // .AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) + // .Done(); + // } + // else + // { + // // pt found, pi not found, this should never happen + // } + // } + // else + // { + // SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) + // .AddUInt8((byte)DataType.NotModified) + // .Done(); + // } + // } + // else + // { + // // pt not found + // } + // } + // else + // { + // // resource not found + // } + // }); + // } void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content) { @@ -1791,7 +1834,7 @@ namespace Esiur.Net.IIP var pi = r.GetType().GetProperty(pt.Name); #endif*/ - var pi = pt.Info; + var pi = pt.PropertyInfo; if (pi != null) { @@ -2091,6 +2134,43 @@ namespace Esiur.Net.IIP return new AsyncReply(null); } + + public AsyncReply GetLinkTemplates(string link) + { + var reply = new AsyncReply(); + + var l = DC.ToBytes(link); + + SendRequest(IIPPacket.IIPPacketAction.LinkTemplates) + .AddUInt16((ushort)l.Length) + .AddUInt8Array(l) + .Done() + .Then((rt) => + { + + var templates = new List(); + // parse templates + + var data = (byte[])rt[0]; + //var offset = 0; + for (uint offset = 0; offset < data.Length;) + { + var cs = data.GetUInt32(offset); + offset += 4; + templates.Add(ResourceTemplate.Parse(data, offset, cs)); + offset += cs; + } + + reply.Trigger(templates.ToArray()); + + }).Error((ex) => + { + reply.TriggerError(ex); + }); + + return reply; + } + /// /// Fetch a resource from the other end /// @@ -2125,7 +2205,19 @@ namespace Esiur.Net.IIP .Then((rt) => { - var dr = resource ?? new DistributedResource(this, id, (ulong)rt[1], (string)rt[2]); + DistributedResource dr; + + if (resource == null) + { + var template = Warehouse.GetTemplate((Guid)rt[0]); + if (template?.RuntimeType != null) + dr = Activator.CreateInstance(template.RuntimeType, this, id, (ulong)rt[1], (string)rt[2]) as DistributedResource; + else + dr = new DistributedResource(this, id, (ulong)rt[1], (string)rt[2]); + } + else + dr = resource; + GetTemplate((Guid)rt[0]).Then((tmp) => { @@ -2140,7 +2232,7 @@ namespace Esiur.Net.IIP resourceRequests.Remove(id); reply.Trigger(dr); }); - }).Error(ex=>reply.TriggerError(ex)); + }).Error(ex => reply.TriggerError(ex)); } else { @@ -2149,7 +2241,7 @@ namespace Esiur.Net.IIP dr._Attach(ar); resourceRequests.Remove(id); reply.Trigger(dr); - }).Error(ex=>reply.TriggerError(ex)); + }).Error(ex => reply.TriggerError(ex)); } }).Error((ex) => @@ -2422,9 +2514,9 @@ namespace Esiur.Net.IIP private void UnsubscribeAll() { - lock(subscriptionsLock) + lock (subscriptionsLock) { - foreach(var resource in subscriptions.Keys) + foreach (var resource in subscriptions.Keys) { resource.Instance.ResourceEventOccurred -= Instance_EventOccurred; resource.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred; @@ -2491,7 +2583,7 @@ namespace Esiur.Net.IIP if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) return; - + // compose the packet SendEvent(IIPPacket.IIPPacketEvent.EventOccurred) .AddUInt32(resource.Instance.Id) diff --git a/Esiur/Net/IIP/DistributedResource.cs b/Esiur/Net/IIP/DistributedResource.cs index d50791e..c13301a 100644 --- a/Esiur/Net/IIP/DistributedResource.cs +++ b/Esiur/Net/IIP/DistributedResource.cs @@ -67,7 +67,7 @@ namespace Esiur.Net.IIP string link; //ulong age; //ulong[] ages; - object[] properties; + protected object[] properties; internal List parents = new List(); internal List children = new List(); @@ -210,13 +210,16 @@ namespace Esiur.Net.IIP return true; } - internal void _EmitEventByIndex(byte index, object args) + + protected internal virtual void _EmitEventByIndex(byte index, object args) { var et = Instance.Template.GetEventTemplateByIndex(index); events[index]?.Invoke(this, args); Instance.EmitResourceEvent(et.Name, args); } + + public AsyncReply _InvokeByNamedArguments(byte index, Structure namedArgs) { if (destroyed) @@ -332,7 +335,7 @@ namespace Esiur.Net.IIP /// /// Zero-based property index. /// Value - internal object _Get(byte index) + protected internal object _Get(byte index) { if (index >= properties.Length) return null; @@ -383,7 +386,7 @@ namespace Esiur.Net.IIP /// Zero-based property index. /// Value /// Indicator when the property is set. - internal AsyncReply _Set(byte index, object value) + protected internal AsyncReply _Set(byte index, object value) { if (index >= properties.Length) return null; diff --git a/Esiur/Net/IIP/DistributedResourceEvent.cs b/Esiur/Net/IIP/DistributedResourceEvent.cs index 5a03bf7..c419b47 100644 --- a/Esiur/Net/IIP/DistributedResourceEvent.cs +++ b/Esiur/Net/IIP/DistributedResourceEvent.cs @@ -30,5 +30,5 @@ using System.Threading.Tasks; namespace Esiur.Net.IIP { - public delegate void DistributedResourceEvent(DistributedResource sender, object arguments); + public delegate void DistributedResourceEvent(DistributedResource sender, object argument); } diff --git a/Esiur/Net/Packets/IIPAuthPacket.cs b/Esiur/Net/Packets/IIPAuthPacket.cs index 916c3f0..bc1c72d 100644 --- a/Esiur/Net/Packets/IIPAuthPacket.cs +++ b/Esiur/Net/Packets/IIPAuthPacket.cs @@ -344,12 +344,15 @@ namespace Esiur.Net.Packets LocalMethod = (AuthenticationMethod)((data[offset] >> 2) & 0x3); var encrypt = ((data[offset++] & 0x2) == 0x2); - if (NotEnough(offset, ends, 1)) - return -dataLengthNeeded; - - - if (RemoteMethod == AuthenticationMethod.Credentials - || RemoteMethod == AuthenticationMethod.Token) + if (RemoteMethod == AuthenticationMethod.None) + { + if (LocalMethod == AuthenticationMethod.None) + { + // do nothing + } + } + else if (RemoteMethod == AuthenticationMethod.Credentials + || RemoteMethod == AuthenticationMethod.Token) { if (LocalMethod == AuthenticationMethod.None) { diff --git a/Esiur/Net/Packets/IIPPacket.cs b/Esiur/Net/Packets/IIPPacket.cs index 182e29a..7f3c9c3 100644 --- a/Esiur/Net/Packets/IIPPacket.cs +++ b/Esiur/Net/Packets/IIPPacket.cs @@ -107,6 +107,7 @@ namespace Esiur.Net.Packets ResourceHistory, ResourceChildren, ResourceParents, + LinkTemplates, // Request Invoke InvokeFunctionArrayArguments = 0x10, @@ -484,7 +485,8 @@ namespace Esiur.Net.Packets ResourceId = data.GetUInt32(offset); offset += 4; } - else if (Action == IIPPacketAction.QueryLink) + else if (Action == IIPPacketAction.QueryLink + || Action == IIPPacketAction.LinkTemplates) { if (NotEnough(offset, ends, 2)) return -dataLengthNeeded; @@ -691,6 +693,7 @@ namespace Esiur.Net.Packets || Action == IIPPacketAction.ResourceChildren || Action == IIPPacketAction.ResourceParents || Action == IIPPacketAction.ResourceHistory + || Action == IIPPacketAction.LinkTemplates // Attribute || Action == IIPPacketAction.GetAllAttributes || Action == IIPPacketAction.GetAttributes) diff --git a/Esiur/Proxy/ResourceGenerator.cs b/Esiur/Proxy/ResourceGenerator.cs index 87882de..d297e9a 100644 --- a/Esiur/Proxy/ResourceGenerator.cs +++ b/Esiur/Proxy/ResourceGenerator.cs @@ -6,43 +6,247 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Linq; +using System.Text.RegularExpressions; +using Esiur.Net.IIP; +using Esiur.Resource; +using Esiur.Resource.Template; +using Esiur.Data; +using System.IO; +using Esiur.Core; namespace Esiur.Proxy { [Generator] public class ResourceGenerator : ISourceGenerator { + + + private static Regex urlRegex = new Regex(@"^(?:([\S]*)://([^/]*)/?)"); + + private KeyList cache = new(); + // private List inProgress = new(); + public void Initialize(GeneratorInitializationContext context) { // Register receiver + context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver()); } + string GetTypeName(TemplateDataType templateDataType, ResourceTemplate[] templates) + { + + if (templateDataType.Type == DataType.Resource) + return templates.First(x => x.ClassId == templateDataType.TypeGuid).ClassName; + else if (templateDataType.Type == DataType.ResourceArray) + return templates.First(x => x.ClassId == templateDataType.TypeGuid).ClassName + "[]"; + + var name = templateDataType.Type switch + { + DataType.Bool => "bool", + DataType.BoolArray => "bool[]", + DataType.Char => "char", + DataType.CharArray => "char[]", + DataType.DateTime => "DateTime", + DataType.DateTimeArray => "DateTime[]", + DataType.Decimal => "decimal", + DataType.DecimalArray => "decimal[]", + DataType.Float32 => "float", + DataType.Float32Array => "float[]", + DataType.Float64 => "double", + DataType.Float64Array => "double[]", + DataType.Int16 => "short", + DataType.Int16Array => "short[]", + DataType.Int32 => "int", + DataType.Int32Array => "int[]", + DataType.Int64 => "long", + DataType.Int64Array => "long[]", + DataType.Int8 => "sbyte", + DataType.Int8Array => "sbyte[]", + DataType.String => "string", + DataType.StringArray => "string[]", + DataType.Structure => "Structure", + DataType.StructureArray => "Structure[]", + DataType.UInt16 => "ushort", + DataType.UInt16Array => "ushort[]", + DataType.UInt32 => "uint", + DataType.UInt32Array => "uint[]", + DataType.UInt64 => "ulong", + DataType.UInt64Array => "ulong[]", + DataType.UInt8 => "byte", + DataType.UInt8Array => "byte[]", + DataType.VarArray => "object[]", + DataType.Void => "object", + _ => "object" + }; + + return name; + } + + void ReportError(GeneratorExecutionContext context, string title, string msg, string category) + { + context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("MySG001", title, msg, category, DiagnosticSeverity.Error, true), Location.None)); + } + + string GenerateClass(ResourceTemplate template, ResourceTemplate[] templates) + { + var cls = template.ClassName.Split('.'); + + var nameSpace = string.Join(".", cls.Take(cls.Length - 1)); + var className = cls.Last(); + + var rt = new StringBuilder(); + + rt.AppendLine("using System;\r\nusing Esiur.Resource;\r\nusing Esiur.Core;\r\nusing Esiur.Data;\r\nusing Esiur.Net.IIP;"); + rt.AppendLine($"namespace { nameSpace} {{"); + rt.AppendLine($"public class {className} : DistributedResource {{"); + + rt.AppendLine($"public {className}(DistributedConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) {{}}"); + rt.AppendLine($"public {className}() {{}}"); + + foreach (var f in template.Functions) + { + var rtTypeName = GetTypeName(f.ReturnType, templates); + rt.Append($"public AsyncReply<{rtTypeName}> {f.Name}("); + rt.Append(string.Join(",", f.Arguments.Select(x => GetTypeName(x.Type, templates) + " " + x.Name))); + + rt.AppendLine(") {"); + rt.AppendLine($"var rt = new AsyncReply<{rtTypeName}>();"); + rt.AppendLine($"_InvokeByArrayArguments({f.Index}, new object[] {{ { string.Join(", ", f.Arguments.Select(x => x.Name)) } }})"); + rt.AppendLine($".Then(x => rt.Trigger(({rtTypeName})x))"); + rt.AppendLine($".Error(x => rt.TriggerError(x))"); + rt.AppendLine($".Chunk(x => rt.TriggerChunk(x));"); + rt.AppendLine("return rt; }"); + } + + foreach (var p in template.Properties) + { + var ptTypeName = GetTypeName(p.ValueType, templates); + rt.AppendLine($"public {ptTypeName} {p.Name} {{"); + rt.AppendLine($"get => ({ptTypeName})properties[{p.Index}];"); + rt.AppendLine($"set => _Set({p.Index}, value);"); + rt.AppendLine("}"); + } + + if (template.Events.Length > 0) + { + rt.AppendLine("protected override void _EmitEventByIndex(byte index, object args) {"); + rt.AppendLine("switch (index) {"); + + var eventsList = new StringBuilder(); + + foreach (var e in template.Events) + { + var etTypeName = GetTypeName(e.ArgumentType, templates); + rt.AppendLine($"case {e.Index}: {e.Name}?.Invoke(({etTypeName})args); break;"); + eventsList.AppendLine($"public event ResourceEventHanlder<{etTypeName}> {e.Name};"); + } + + rt.AppendLine("}}"); + + rt.AppendLine(eventsList.ToString()); + + } + + rt.AppendLine("\r\n}\r\n}"); + + return rt.ToString(); + } + + + void GenerateModel(GeneratorExecutionContext context, ResourceTemplate[] templates) + { + foreach (var tmp in templates) + { + var source = GenerateClass(tmp, templates); + //File.WriteAllText($@"C:\gen\{tmp.ClassName}.cs", source); + context.AddSource(tmp.ClassName + "_esiur.cs", source); + } + + // generate info class + + var gen = "using System; \r\n namespace Esiur { public static class Generated { public static Type[] Types {get;} = new Type[]{ " + + string.Join(",", templates.Select(x => $"typeof({x.ClassName})")) + + " }; \r\n } \r\n}"; + + //File.WriteAllText($@"C:\gen\Esiur.Generated.cs", gen); + + context.AddSource("Esiur.Generated.cs", gen); + + } + public void Execute(GeneratorExecutionContext context) { if (!(context.SyntaxContextReceiver is ResourceGeneratorReceiver receiver)) return; - try + //if (receiver.Imports.Count > 0 && !Debugger.IsAttached) + //{ + // Debugger.Launch(); + //} + + foreach (var path in receiver.Imports) { - //#if DEBUG - // if (!Debugger.IsAttached) - // { - // Debugger.Launch(); - // } - //#endif + if (!urlRegex.IsMatch(path)) + continue; - //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0); - foreach (var ci in receiver.Classes) + //File.WriteAllLines("C:\\gen\\ref.log", context.Compilation.ReferencedAssemblyNames.Select(x => x.ToString())); + + if (cache.Contains(path)) { + GenerateModel(context, cache[path]); + continue; + } + + // Syncronization + //if (inProgress.Contains(path)) + // continue; + + //inProgress.Add(path); + + var url = urlRegex.Split(path); + + + try + { + var con = Warehouse.Get(url[1] + "://" + url[2]).Wait(20000); + var templates = con.GetLinkTemplates(url[3]).Wait(60000); + + cache[path] = templates; + + // make sources + GenerateModel(context, templates); + + } + catch (Exception ex) + { + ReportError(context, ex.Source, ex.Message, "Esiur"); + System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n"); + } + + //inProgress.Remove(path); + } + + + //#if DEBUG + + //#endif + + //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0); + + foreach (var ci in receiver.Classes.Values) + { + try + { + var code = @$"using Esiur.Resource; using Esiur.Core; namespace { ci.ClassSymbol.ContainingNamespace.ToDisplayString() } {{ "; - if (ci.ImplementInterface) + if (ci.HasInterface) code += $"public partial class {ci.Name} {{"; else { @@ -52,7 +256,7 @@ public event DestroyedEvent OnDestroy; public virtual void Destroy() {{ OnDestroy?.Invoke(this); }} "; - if (!ci.ImplementTrigger) + if (!ci.HasTrigger) code += "public AsyncReply Trigger(ResourceTrigger trigger) => new AsyncReply(true);\r\n"; } @@ -70,11 +274,12 @@ public virtual void Destroy() {{ OnDestroy?.Invoke(this); }} //System.IO.File.WriteAllText("c:\\gen\\" + ci.Name + "_esiur.cs", code); context.AddSource(ci.Name + "_esiur.cs", code); + + } + catch (Exception ex) + { + System.IO.File.AppendAllText("c:\\gen\\error.log", ci.Name + " " + ex.ToString() + "\r\n"); } - } - catch //(Exception ex) - { - //System.IO.File.AppendAllText("c:\\gen\\error.log", ex.ToString() + "\r\n"); } } } diff --git a/Esiur/Proxy/ResourceGeneratorClassInfo.cs b/Esiur/Proxy/ResourceGeneratorClassInfo.cs index 9d95eb2..2f68aa2 100644 --- a/Esiur/Proxy/ResourceGeneratorClassInfo.cs +++ b/Esiur/Proxy/ResourceGeneratorClassInfo.cs @@ -9,10 +9,10 @@ namespace Esiur.Proxy public struct ResourceGeneratorClassInfo { public string Name { get; set; } - public bool ImplementInterface { get; set; } + public bool HasInterface { get; set; } - public bool ImplementTrigger { get; set; } - public IFieldSymbol[] Fields { get; set; } + public bool HasTrigger { get; set; } + public List Fields { get; set; } public ITypeSymbol ClassSymbol { get; set; } public ClassDeclarationSyntax ClassDeclaration { get; set; } diff --git a/Esiur/Proxy/ResourceGeneratorReceiver.cs b/Esiur/Proxy/ResourceGeneratorReceiver.cs index 37a39fe..edc8557 100644 --- a/Esiur/Proxy/ResourceGeneratorReceiver.cs +++ b/Esiur/Proxy/ResourceGeneratorReceiver.cs @@ -11,7 +11,9 @@ namespace Esiur.Proxy public class ResourceGeneratorReceiver : ISyntaxContextReceiver { - public List Classes { get; } = new(); + public Dictionary Classes { get; } = new(); + + public List Imports { get; } = new (); public void OnVisitSyntaxNode(GeneratorSyntaxContext context) { @@ -20,8 +22,21 @@ namespace Esiur.Proxy { var cds = context.Node as ClassDeclarationSyntax; var cls = context.SemanticModel.GetDeclaredSymbol(cds) as ITypeSymbol; + var attrs = cls.GetAttributes(); - if (cls.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute")) + var imports = attrs.Where(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ImportAttribute"); + + foreach (var import in imports) + { + // Debugger.Launch(); + + var url = import.ConstructorArguments.First().Value.ToString(); + + if (!Imports.Contains(url)) + Imports.Add(url); + } + + if (attrs.Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute")) { @@ -46,16 +61,33 @@ namespace Esiur.Proxy // get fields - Classes.Add(new ResourceGeneratorClassInfo() - { - Name = cls.Name, - ClassDeclaration = cds, - ClassSymbol = cls, - Fields = fields, - ImplementInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"), - ImplementTrigger = hasTrigger - }); + var fullName = cls.ContainingAssembly + "." + cls.Name; + + // Partial class check + if (Classes.ContainsKey(fullName)) + { + // append fields + var c = Classes[fullName]; + c.Fields.AddRange(fields); + if (!c.HasInterface) + c.HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"); + if (!c.HasTrigger) + c.HasTrigger = hasTrigger; + } else + { + Classes.Add(fullName, new ResourceGeneratorClassInfo() + { + Name = cls.Name, + ClassDeclaration = cds, + ClassSymbol = cls, + Fields = fields.ToList(), + HasInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"), + HasTrigger = hasTrigger + }); + } + + return; } } diff --git a/Esiur/Resource/ImportAttribute.cs b/Esiur/Resource/ImportAttribute.cs new file mode 100644 index 0000000..037c0c8 --- /dev/null +++ b/Esiur/Resource/ImportAttribute.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + [AttributeUsage(AttributeTargets.Class)] + public class ImportAttribute:Attribute + { + public ImportAttribute(string url) + { + + } + } +} diff --git a/Esiur/Resource/Instance.cs b/Esiur/Resource/Instance.cs index 017773f..231e3d1 100644 --- a/Esiur/Resource/Instance.cs +++ b/Esiur/Resource/Instance.cs @@ -185,8 +185,8 @@ namespace Esiur.Resource var at = template.GetAttributeTemplate(kv.Key); if (at != null) - if (at.Info.CanWrite) - at.Info.SetValue(res, DC.CastConvert(kv.Value, at.Info.PropertyType)); + if (at.PropertyInfo.CanWrite) + at.PropertyInfo.SetValue(res, DC.CastConvert(kv.Value, at.PropertyInfo.PropertyType)); } } @@ -354,17 +354,17 @@ namespace Esiur.Resource #endif */ - if (pt.Info.PropertyType == typeof(DistributedPropertyContext)) + if (pt.PropertyInfo.PropertyType == typeof(DistributedPropertyContext)) return false; - if (pt.Info.CanWrite) + if (pt.PropertyInfo.CanWrite) { try { loading = true; - pt.Info.SetValue(res, DC.CastConvert(value, pt.Info.PropertyType)); + pt.PropertyInfo.SetValue(res, DC.CastConvert(value, pt.PropertyInfo.PropertyType)); } catch (Exception ex) { @@ -453,7 +453,7 @@ namespace Esiur.Resource if (resource.TryGetTarget(out res)) { - var rt = pt.Info.GetValue(res, null); + var rt = pt.PropertyInfo.GetValue(res, null); props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index])); } } @@ -628,7 +628,7 @@ namespace Esiur.Resource var pt = template.GetPropertyTemplateByName(name); - if (pt != null && pt.Info != null) + if (pt != null && pt.PropertyInfo != null) { /* #if NETSTANDARD @@ -649,7 +649,7 @@ namespace Esiur.Resource IResource res; if (resource.TryGetTarget(out res)) - value = pt.Info.GetValue(res, null); + value = pt.PropertyInfo.GetValue(res, null); else { value = null; @@ -900,28 +900,30 @@ namespace Esiur.Resource //if (evt.EventHandlerType != typeof(ResourceEventHanlder)) // continue; - if (evt.Info == null) + if (evt.EventInfo == null) continue; - if (evt.Info.EventHandlerType == typeof(ResourceEventHanlder)) + var eventGenericType = evt.EventInfo.EventHandlerType.GetGenericTypeDefinition(); + + if (eventGenericType == typeof(ResourceEventHanlder<>)) { // var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true); // if (ca.Length == 0) // continue; - - ResourceEventHanlder proxyDelegate = (args) => EmitResourceEvent(evt.Name, args); - evt.Info.AddEventHandler(resource, proxyDelegate); + + ResourceEventHanlder proxyDelegate = (args) => EmitResourceEvent(evt.Name, args); + evt.EventInfo.AddEventHandler(resource, proxyDelegate); } - else if (evt.Info.EventHandlerType == typeof(CustomResourceEventHanlder)) + else if (eventGenericType == typeof(CustomResourceEventHanlder<>)) { //var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true); //if (ca.Length == 0) // continue; - CustomResourceEventHanlder proxyDelegate = (issuer, receivers, args) => EmitCustomResourceEvent(issuer, receivers, evt.Name, args); - evt.Info.AddEventHandler(resource, proxyDelegate); + CustomResourceEventHanlder proxyDelegate = (issuer, receivers, args) => EmitCustomResourceEvent(issuer, receivers, evt.Name, args); + evt.EventInfo.AddEventHandler(resource, proxyDelegate); } diff --git a/Esiur/Resource/ResourceEventHandler.cs b/Esiur/Resource/ResourceEventHandler.cs index fa80565..0bb5fe6 100644 --- a/Esiur/Resource/ResourceEventHandler.cs +++ b/Esiur/Resource/ResourceEventHandler.cs @@ -33,12 +33,14 @@ using System.Threading.Tasks; namespace Esiur.Resource { - public delegate void ResourceEventHanlder(object args); + public delegate R DCovariant(); + + public delegate void ResourceEventHanlder(T argument); // public delegate void CustomUsersEventHanlder(string[] usernames, params object[] args); //public delegate void CustomReceiversEventHanlder(DistributedConnection[] connections, params object[] args); //public delegate void CustomInquirerEventHanlder(object inquirer, params object[] args); - public delegate void CustomResourceEventHanlder(object issuer, Func receivers, object args);// object issuer, Session[] receivers, params object[] args); + public delegate void CustomResourceEventHanlder(object issuer, Func receivers, T argument);// object issuer, Session[] receivers, params object[] args); // public delegate void CustomReceiversEventHanlder(string[] usernames, DistributedConnection[] connections, params object[] args); diff --git a/Esiur/Resource/Template/ArgumentTemplate.cs b/Esiur/Resource/Template/ArgumentTemplate.cs new file mode 100644 index 0000000..effc86c --- /dev/null +++ b/Esiur/Resource/Template/ArgumentTemplate.cs @@ -0,0 +1,49 @@ +using Esiur.Data; +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; + +namespace Esiur.Resource.Template +{ + public class ArgumentTemplate + { + public string Name { get; set; } + + public TemplateDataType Type { get; set; } + + public ParameterInfo ParameterInfo { get; set; } + + public static (uint, ArgumentTemplate) Parse(byte[] data, uint offset) + { + var cs = (uint)data[offset++]; + var name = DC.GetString(data, offset, cs); + offset += cs; + var (size, type) = TemplateDataType.Parse(data, offset); + + return (cs + 1 + size, new ArgumentTemplate(name, type)); + } + + public ArgumentTemplate() + { + + } + + public ArgumentTemplate(string name, TemplateDataType type) + { + Name = name; + Type = type; + } + + public byte[] Compose() + { + var name = DC.ToBytes(Name); + + return new BinaryList() + .AddUInt8((byte)name.Length) + .AddUInt8Array(name) + .AddUInt8Array(Type.Compose()) + .ToArray(); + } + } +} diff --git a/Esiur/Resource/Template/AttributeTemplate.cs b/Esiur/Resource/Template/AttributeTemplate.cs index 7c26dde..1eaa829 100644 --- a/Esiur/Resource/Template/AttributeTemplate.cs +++ b/Esiur/Resource/Template/AttributeTemplate.cs @@ -10,7 +10,7 @@ namespace Esiur.Resource.Template { public class AttributeTemplate : MemberTemplate { - public PropertyInfo Info + public PropertyInfo PropertyInfo { get; set; diff --git a/Esiur/Resource/Template/EventTemplate.cs b/Esiur/Resource/Template/EventTemplate.cs index 544c8b7..5cee5db 100644 --- a/Esiur/Resource/Template/EventTemplate.cs +++ b/Esiur/Resource/Template/EventTemplate.cs @@ -18,7 +18,9 @@ namespace Esiur.Resource.Template public bool Listenable { get; set; } - public EventInfo Info { get; set; } + public EventInfo EventInfo { get; set; } + + public TemplateDataType ArgumentType { get; set; } public override byte[] Compose() { @@ -26,12 +28,12 @@ namespace Esiur.Resource.Template if (Expansion != null) { - var exp = DC.ToBytes(Expansion); return new BinaryList() .AddUInt8(Listenable ? (byte) 0x58 : (byte) 0x50) .AddUInt8((byte)name.Length) .AddUInt8Array(name) + .AddUInt8Array(ArgumentType.Compose()) .AddInt32(exp.Length) .AddUInt8Array(exp) .ToArray(); @@ -41,15 +43,17 @@ namespace Esiur.Resource.Template .AddUInt8(Listenable ? (byte) 0x48 : (byte) 0x40) .AddUInt8((byte)name.Length) .AddUInt8Array(name) + .AddUInt8Array(ArgumentType.Compose()) .ToArray(); } - public EventTemplate(ResourceTemplate template, byte index, string name, string expansion = null, bool listenable=false) + public EventTemplate(ResourceTemplate template, byte index, string name, TemplateDataType argumentType, string expansion = null, bool listenable=false) :base(template, MemberType.Property, index, name) { this.Expansion = expansion; this.Listenable = listenable; + this.ArgumentType = argumentType; } } } diff --git a/Esiur/Resource/Template/FunctionTemplate.cs b/Esiur/Resource/Template/FunctionTemplate.cs index 8ce3109..edc79ac 100644 --- a/Esiur/Resource/Template/FunctionTemplate.cs +++ b/Esiur/Resource/Template/FunctionTemplate.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -16,7 +17,17 @@ namespace Esiur.Resource.Template set; } - public bool IsVoid + //public bool IsVoid + //{ + // get; + // set; + //} + + public TemplateDataType ReturnType { get; set; } + + public ArgumentTemplate[] Arguments { get; set; } + + public MethodInfo MethodInfo { get; set; @@ -25,30 +36,40 @@ namespace Esiur.Resource.Template public override byte[] Compose() { + var name = base.Compose(); + + var bl = new BinaryList() + //.AddUInt8(Expansion != null ? (byte)0x10 : (byte)0) + .AddUInt8((byte)name.Length) + .AddUInt8Array(name) + .AddUInt8Array(ReturnType.Compose()) + .AddUInt8((byte)Arguments.Length); + + for (var i = 0; i < Arguments.Length; i++) + bl.AddUInt8Array(Arguments[i].Compose()); + if (Expansion != null) { var exp = DC.ToBytes(Expansion); - return new BinaryList().AddUInt8((byte)(0x10 | (IsVoid ? 0x8 : 0x0))) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .AddInt32(exp.Length) - .AddUInt8Array(exp) - .ToArray(); + bl.AddInt32(exp.Length) + .AddUInt8Array(exp); + bl.InsertUInt8(0, 0x10); } else - return new BinaryList().AddUInt8((byte)(IsVoid ? 0x8 : 0x0)) - .AddUInt8((byte)name.Length) - .AddUInt8Array(name) - .ToArray(); + bl.InsertUInt8(0, 0x0); + + return bl.ToArray(); } - public FunctionTemplate(ResourceTemplate template, byte index, string name,bool isVoid, string expansion = null) - :base(template, MemberType.Property, index, name) + public FunctionTemplate(ResourceTemplate template, byte index, string name, ArgumentTemplate[] arguments, TemplateDataType returnType, string expansion = null) + : base(template, MemberType.Property, index, name) { - this.IsVoid = isVoid; + //this.IsVoid = isVoid; + this.Arguments = arguments; + this.ReturnType = returnType; this.Expansion = expansion; } } diff --git a/Esiur/Resource/Template/PropertyTemplate.cs b/Esiur/Resource/Template/PropertyTemplate.cs index d827388..9744cd2 100644 --- a/Esiur/Resource/Template/PropertyTemplate.cs +++ b/Esiur/Resource/Template/PropertyTemplate.cs @@ -10,7 +10,7 @@ namespace Esiur.Resource.Template { public class PropertyTemplate : MemberTemplate { - public enum PropertyPermission:byte + public enum PropertyPermission : byte { Read = 1, Write, @@ -18,12 +18,15 @@ namespace Esiur.Resource.Template } - public PropertyInfo Info + public PropertyInfo PropertyInfo { get; set; } + public TemplateDataType ValueType { get; set; } + + /* public bool Serilize { @@ -32,12 +35,13 @@ namespace Esiur.Resource.Template */ //bool ReadOnly; //IIPTypes::DataType ReturnType; - public PropertyPermission Permission { + public PropertyPermission Permission + { get; set; } - + public bool Recordable { get; @@ -84,6 +88,7 @@ namespace Esiur.Resource.Template .AddUInt8((byte)(0x38 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) + .AddUInt8Array(ValueType.Compose()) .AddInt32(wexp.Length) .AddUInt8Array(wexp) .AddInt32(rexp.Length) @@ -97,6 +102,7 @@ namespace Esiur.Resource.Template .AddUInt8((byte)(0x30 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) + .AddUInt8Array(ValueType.Compose()) .AddInt32(wexp.Length) .AddUInt8Array(wexp) .ToArray(); @@ -108,25 +114,30 @@ namespace Esiur.Resource.Template .AddUInt8((byte)(0x28 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) + .AddUInt8Array(ValueType.Compose()) .AddInt32(rexp.Length) .AddUInt8Array(rexp) .ToArray(); } else + { return new BinaryList() .AddUInt8((byte)(0x20 | pv)) .AddUInt8((byte)name.Length) .AddUInt8Array(name) + .AddUInt8Array(ValueType.Compose()) .ToArray(); + } } - public PropertyTemplate(ResourceTemplate template, byte index, string name, string read = null, string write = null, bool recordable = false) - :base(template, MemberType.Property, index, name) + public PropertyTemplate(ResourceTemplate template, byte index, string name, TemplateDataType valueType, string read = null, string write = null, bool recordable = false) + : base(template, MemberType.Property, index, name) { this.Recordable = recordable; //this.Storage = storage; this.ReadExpansion = read; this.WriteExpansion = write; + this.ValueType = valueType; } } } diff --git a/Esiur/Resource/Template/ResourceTemplate.cs b/Esiur/Resource/Template/ResourceTemplate.cs index c2b043b..12e8c5b 100644 --- a/Esiur/Resource/Template/ResourceTemplate.cs +++ b/Esiur/Resource/Template/ResourceTemplate.cs @@ -14,6 +14,7 @@ namespace Esiur.Resource.Template { public class ResourceTemplate { + Guid classId; string className; List members = new List(); @@ -31,6 +32,8 @@ namespace Esiur.Resource.Template get { return content; } } + public Type RuntimeType { get; set; } + public MemberTemplate GetMemberTemplate(MemberInfo member) { if (member is MethodInfo) @@ -135,20 +138,137 @@ namespace Esiur.Resource.Template } + public static Guid GetTypeGuid(Type type) => GetTypeGuid(type.FullName); + + public static Guid GetTypeGuid(string typeName) + { + var tn = Encoding.UTF8.GetBytes(typeName); + var hash = SHA256.Create().ComputeHash(tn).Clip(0, 16); + + return new Guid(hash); + } + + static Type GetElementType(Type type) => type switch + { + { IsArray: true } => type.GetElementType(), + { IsEnum: true } => type.GetEnumUnderlyingType(), + (_) => type + }; + + + + public static ResourceTemplate[] GetRuntimeTypes(ResourceTemplate template) + { + + var list = new List(); + + list.Add(template); + + Action> getRuntimeTypes = null; + + getRuntimeTypes = (ResourceTemplate tmp, List bag) => + { + if (template.RuntimeType == null) + return; + + // functions + foreach (var f in tmp.functions) + { + var frtt = Warehouse.GetTemplate(GetElementType(f.MethodInfo.ReturnType)); + if (frtt != null) + { + if (!bag.Contains(frtt)) + { + list.Add(frtt); + getRuntimeTypes(frtt, bag); + } + } + + var args = f.MethodInfo.GetParameters(); + + for(var i = 0; i < args.Length - 1; i++) + { + var fpt = Warehouse.GetTemplate(GetElementType(args[i].ParameterType)); + if (fpt != null) + { + if (!bag.Contains(fpt)) + { + bag.Add(fpt); + getRuntimeTypes(fpt, bag); + } + } + } + + // skip DistributedConnection argument + if (args.Length > 0) + { + var last = args.Last(); + if (last.ParameterType != typeof(DistributedConnection)) + { + var fpt = Warehouse.GetTemplate(GetElementType(last.ParameterType)); + if (fpt != null) + { + if (!bag.Contains(fpt)) + { + bag.Add(fpt); + getRuntimeTypes(fpt, bag); + } + } + } + } + + } + + // properties + foreach (var p in tmp.properties) + { + var pt = Warehouse.GetTemplate(GetElementType(p.PropertyInfo.PropertyType)); + if (pt != null) + { + if (!bag.Contains(pt)) + { + bag.Add(pt); + getRuntimeTypes(pt, bag); + } + } + } + + // events + foreach (var e in tmp.events) + { + var et = Warehouse.GetTemplate(GetElementType(e.EventInfo.EventHandlerType.GenericTypeArguments[0])); + + if (et != null) + { + if (!bag.Contains(et)) + { + bag.Add(et); + getRuntimeTypes(et, bag); + } + } + } + }; + + getRuntimeTypes(template, list); + return list.ToArray(); + } + public ResourceTemplate(Type type) { + if (!Codec.ImplementsInterface(type, typeof(IResource))) + throw new Exception("Type is not a resource."); type = ResourceProxy.GetBaseType(type); - // set guid + RuntimeType = type; - var typeName = Encoding.UTF8.GetBytes(type.FullName); - var hash = SHA256.Create().ComputeHash(typeName).Clip(0, 16); - - classId = new Guid(hash); className = type.FullName; + //Console.WriteLine($"Creating {className}"); + // set guid + classId = GetTypeGuid(className); + #if NETSTANDARD PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance);// | BindingFlags.DeclaredOnly); EventInfo[] eventsInfo = type.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance);// | BindingFlags.DeclaredOnly); @@ -175,8 +295,9 @@ namespace Esiur.Resource.Template { var annotationAttr = pi.GetCustomAttribute(true); var storageAttr = pi.GetCustomAttribute(true); + + var pt = new PropertyTemplate(this, i++, pi.Name, TemplateDataType.FromType(pi.PropertyType)); - var pt = new PropertyTemplate(this, i++, pi.Name); if (storageAttr != null) pt.Recordable = storageAttr.Mode == StorageMode.Recordable; @@ -185,7 +306,7 @@ namespace Esiur.Resource.Template else pt.ReadExpansion = pi.PropertyType.Name; - pt.Info = pi; + pt.PropertyInfo = pi; //pt.Serilize = publicAttr.Serialize; properties.Add(pt); } @@ -195,7 +316,7 @@ namespace Esiur.Resource.Template if (attributeAttr != null) { var at = new AttributeTemplate(this, 0, pi.Name); - at.Info = pi; + at.PropertyInfo = pi; attributes.Add(at); } } @@ -211,8 +332,9 @@ namespace Esiur.Resource.Template var annotationAttr = ei.GetCustomAttribute(true); var listenableAttr = ei.GetCustomAttribute(true); - var et = new EventTemplate(this, i++, ei.Name); - et.Info = ei; + var argType = ei.EventHandlerType.GenericTypeArguments[0]; + var et = new EventTemplate(this, i++, ei.Name, TemplateDataType.FromType(argType)); + et.EventInfo = ei; if (annotationAttr != null) et.Expansion = annotationAttr.Annotation; @@ -232,12 +354,32 @@ namespace Esiur.Resource.Template { var annotationAttr = mi.GetCustomAttribute(true); - var ft = new FunctionTemplate(this, i++, mi.Name, mi.ReturnType == typeof(void)); + var returnType = TemplateDataType.FromType(mi.ReturnType); + + var args = mi.GetParameters(); + + if (args.Length > 0) + { + if (args.Last().ParameterType == typeof(DistributedConnection)) + args = args.Take(args.Count() - 1).ToArray(); + } + + var arguments = args.Select(x => new ArgumentTemplate() + { + Name = x.Name, + Type = TemplateDataType.FromType(x.ParameterType), + ParameterInfo = x + }) + .ToArray(); + + var ft = new FunctionTemplate(this, i++, mi.Name, arguments, returnType);// mi.ReturnType == typeof(void)); if (annotationAttr != null) ft.Expansion = annotationAttr.Annotation; else ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x => x.ParameterType != typeof(DistributedConnection)).Select(x => "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name; + + ft.MethodInfo = mi; functions.Add(ft); } } @@ -253,8 +395,9 @@ namespace Esiur.Resource.Template { var annotationAttr = pi.GetCustomAttribute(true); var storageAttr = pi.GetCustomAttribute(true); + var valueType = TemplateDataType.FromType(pi.PropertyType); - var pt = new PropertyTemplate(this, i++, pi.Name);//, rp.ReadExpansion, rp.WriteExpansion, rp.Storage); + var pt = new PropertyTemplate(this, i++, pi.Name, valueType);//, rp.ReadExpansion, rp.WriteExpansion, rp.Storage); if (storageAttr != null) pt.Recordable = storageAttr.Mode == StorageMode.Recordable; @@ -263,7 +406,7 @@ namespace Esiur.Resource.Template else pt.ReadExpansion = pi.PropertyType.Name; - pt.Info = pi; + pt.PropertyInfo = pi; //pt.Serilize = publicAttr.Serialize; properties.Add(pt); } @@ -273,7 +416,7 @@ namespace Esiur.Resource.Template if (attributeAttr != null) { var at = new AttributeTemplate(this, 0, pi.Name); - at.Info = pi; + at.PropertyInfo = pi; attributes.Add(at); } } @@ -289,8 +432,10 @@ namespace Esiur.Resource.Template var annotationAttr = ei.GetCustomAttribute(true); var listenableAttr = ei.GetCustomAttribute(true); - var et = new EventTemplate(this, i++, ei.Name); - et.Info = ei; + var argType = ei.EventHandlerType.GenericTypeArguments[0]; + + var et = new EventTemplate(this, i++, ei.Name, TemplateDataType.FromType(argType)); + et.EventInfo = ei; if (annotationAttr != null) et.Expansion = annotationAttr.Annotation; @@ -309,13 +454,32 @@ namespace Esiur.Resource.Template if (publicAttr != null) { var annotationAttr = mi.GetCustomAttribute(true); + var returnType = TemplateDataType.FromType(mi.ReturnType); - var ft = new FunctionTemplate(this, i++, mi.Name, mi.ReturnType == typeof(void)); + var args = mi.GetParameters(); + + if (args.Length > 0) + { + if (args.Last().ParameterType == typeof(DistributedConnection)) + args = args.Take(args.Count() - 1).ToArray(); + } + + var arguments = args.Select(x => new ArgumentTemplate() + { + Name = x.Name, + Type = TemplateDataType.FromType(x.ParameterType), + ParameterInfo = x + }) + .ToArray(); + + var ft = new FunctionTemplate(this, i++, mi.Name, arguments, returnType);// mi.ReturnType == typeof(void)); if (annotationAttr != null) ft.Expansion = annotationAttr.Annotation; else ft.Expansion = "(" + String.Join(",", mi.GetParameters().Where(x=>x.ParameterType != typeof(DistributedConnection)).Select(x=> "[" + x.ParameterType.Name + "] " + x.Name)) + ") -> " + mi.ReturnType.Name; + + ft.MethodInfo = mi; functions.Add(ft); } } @@ -391,11 +555,27 @@ namespace Esiur.Resource.Template if (type == 0) // function { string expansion = null; - var hasExpansion = ((data[offset] & 0x10) == 0x10); - var isVoid = ((data[offset++] & 0x08) == 0x08); + var hasExpansion = ((data[offset++] & 0x10) == 0x10); + var name = data.GetString(offset + 1, data[offset]); offset += (uint)data[offset] + 1; - + + // return type + var (rts, returnType) = TemplateDataType.Parse(data, offset); + offset += rts; + + // arguments count + var argsCount = data[offset++]; + List arguments = new(); + + for (var a = 0; a < argsCount; a++) + { + var (cs, argType) = ArgumentTemplate.Parse(data, offset); + arguments.Add(argType); + offset += cs; + } + + // arguments if (hasExpansion) // expansion ? { var cs = data.GetUInt32(offset); @@ -404,7 +584,7 @@ namespace Esiur.Resource.Template offset += cs; } - var ft = new FunctionTemplate(od, functionIndex++, name, isVoid, expansion); + var ft = new FunctionTemplate(od, functionIndex++, name, arguments.ToArray(), returnType, expansion); od.functions.Add(ft); } @@ -421,6 +601,10 @@ namespace Esiur.Resource.Template offset += (uint)data[offset] + 1; + var (dts, valueType) = TemplateDataType.Parse(data, offset); + + offset += dts; + if (hasReadExpansion) // expansion ? { var cs = data.GetUInt32(offset); @@ -437,7 +621,7 @@ namespace Esiur.Resource.Template offset += cs; } - var pt = new PropertyTemplate(od, propertyIndex++, name, readExpansion, writeExpansion, recordable); + var pt = new PropertyTemplate(od, propertyIndex++, name, valueType, readExpansion, writeExpansion, recordable); od.properties.Add(pt); } @@ -451,6 +635,10 @@ namespace Esiur.Resource.Template var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]); offset += (uint)data[offset] + 1; + var (dts, argType) = TemplateDataType.Parse(data, offset); + + offset += dts; + if (hasExpansion) // expansion ? { var cs = data.GetUInt32(offset); @@ -459,7 +647,7 @@ namespace Esiur.Resource.Template offset += cs; } - var et = new EventTemplate(od, eventIndex++, name, expansion, listenable); + var et = new EventTemplate(od, eventIndex++, name, argType, expansion, listenable); od.events.Add(et); diff --git a/Esiur/Resource/Template/TemplateDataType.cs b/Esiur/Resource/Template/TemplateDataType.cs new file mode 100644 index 0000000..fbe667e --- /dev/null +++ b/Esiur/Resource/Template/TemplateDataType.cs @@ -0,0 +1,118 @@ +using Esiur.Data; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Text; + +namespace Esiur.Resource.Template +{ + public struct TemplateDataType + { + public DataType Type { get; set; } + //public string TypeName { get; set; } + public ResourceTemplate TypeTemplate => TypeGuid == null ? null : Warehouse.GetTemplate((Guid)TypeGuid); + + public Guid? TypeGuid { get; set; } + //public TemplateDataType(DataType type, string typeName) + //{ + // Type = type; + // TypeName = typeName; + //} + + + + public static TemplateDataType FromType(Type type) + { + + var t = type switch + { + { IsArray: true } => type.GetElementType(), + { IsEnum: true } => type.GetEnumUnderlyingType(), + (_) => type + }; + + DataType dt = t switch + { + _ when t == typeof(bool) => DataType.Bool, + _ when t == typeof(char) => DataType.Char, + _ when t == typeof(byte) => DataType.UInt8, + _ when t == typeof(sbyte) => DataType.Int8, + _ when t == typeof(short) => DataType.Int16, + _ when t == typeof(ushort) => DataType.UInt16, + _ when t == typeof(int) => DataType.Int32, + _ when t == typeof(uint) => DataType.UInt32, + _ when t == typeof(long) => DataType.Int64, + _ when t == typeof(ulong) => DataType.UInt64, + _ when t == typeof(float) => DataType.Float32, + _ when t == typeof(double) => DataType.Float64, + _ when t == typeof(decimal) => DataType.Decimal, + _ when t == typeof(string) => DataType.String, + _ when t == typeof(DateTime) => DataType.DateTime, + _ when t == typeof(IResource) => DataType.Void, // Dynamic resource (unspecified type) + _ when typeof(Structure).IsAssignableFrom(t) || t == typeof(ExpandoObject) => DataType.Structure, + _ when Codec.ImplementsInterface(t, typeof(IResource)) => DataType.Resource, + _ => DataType.Void + }; + + + //string tn = dt switch + //{ + // DataType.Resource => t.FullName, + // DataType.Structure when t != typeof(Structure) => t.FullName, + // _ => null + //}; + + Guid? typeGuid = null; + + if (dt == DataType.Resource) + typeGuid = ResourceTemplate.GetTypeGuid(t); + + if (type.IsArray) + dt = (DataType)((byte)dt | 0x80); + + return new TemplateDataType() + { + Type = dt, + TypeGuid = typeGuid + }; + } + + public byte[] Compose() + { + if (Type == DataType.Resource || + Type == DataType.ResourceArray)//|| + //Type == DataType.DistributedResource || + //Type == DataType.DistributedResourceArray || + //Type == DataType.Structure || + //Type == DataType.StructureArray) + { + var guid = DC.ToBytes((Guid)TypeGuid); + return new BinaryList() + .AddUInt8((byte)Type) + .AddUInt8Array(guid).ToArray(); + } + else + return new byte[] { (byte)Type }; + } + + public override string ToString() => Type.ToString() + TypeTemplate != null ? "<" + TypeTemplate.ClassName + ">" : ""; + + + public static (uint, TemplateDataType) Parse(byte[] data, uint offset) + { + var type = (DataType)data[offset++]; + if (type == DataType.Resource || + type == DataType.ResourceArray)//|| + // type == DataType.DistributedResource || + // type == DataType.DistributedResourceArray)// || + // type == DataType.Structure || + // type == DataType.StructureArray) + { + var guid = DC.GetGuid(data, offset); + return (17, new TemplateDataType() { Type = type, TypeGuid = guid }); + } + else + return (1, new TemplateDataType() { Type = type }); + } + } +} diff --git a/Esiur/Resource/Warehouse.cs b/Esiur/Resource/Warehouse.cs index 006f370..d7ee258 100644 --- a/Esiur/Resource/Warehouse.cs +++ b/Esiur/Resource/Warehouse.cs @@ -113,6 +113,22 @@ namespace Esiur.Resource return new AsyncReply(null); } + static void LoadGenerated() + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var generatedType = assembly.GetType("Esiur.Generated"); + if (generatedType != null) + { + var types = (Type[])generatedType.GetProperty("Types").GetValue(null); + foreach (var t in types) + { + PutTemplate(new ResourceTemplate(t)); + } + } + } + } + /// /// Open the warehouse. /// This function issues the initialize trigger to all stores and resources. @@ -123,6 +139,10 @@ namespace Esiur.Resource if (warehouseIsOpen) return false; + // Load generated models + LoadGenerated(); + + warehouseIsOpen = true; var resSnap = resources.Select(x => @@ -380,7 +400,8 @@ namespace Esiur.Resource /// /// /// Resource instance. - public static async AsyncReply Get(string path, object attributes = null, IResource parent = null, IPermissionsManager manager = null) + public static async AsyncReply Get(string path, object attributes = null, IResource parent = null, IPermissionsManager manager = null) + where T: IResource { //var rt = new AsyncReply(); @@ -404,9 +425,9 @@ namespace Esiur.Resource //await Put(store, url[2], null, parent, null, 0, manager, attributes); if (url[3].Length > 0 && url[3] != "") - return await store.Get(url[3]); + return (T)await store.Get(url[3]); else - return store; + return (T)store; } catch (Exception ex) @@ -471,9 +492,9 @@ namespace Esiur.Resource var res = await Query(path); if (res.Length == 0) - return null; + return default(T); else - return res.First(); + return (T)res.First(); } @@ -503,7 +524,7 @@ namespace Esiur.Resource if (parent != null) throw new Exception("Parent can't be set when using path in instance name"); - parent = await Warehouse.Get(string.Join("/", path.Take(path.Length - 1))); + parent = await Warehouse.Get(string.Join("/", path.Take(path.Length - 1))); if (parent == null) throw new Exception("Can't find parent"); @@ -518,27 +539,28 @@ namespace Esiur.Resource if (store == null) { - // assign parent as a store - if (parent is IStore) - { - store = (IStore)parent; - List> list; - if (stores.TryGetValue(store, out list)) - lock (((ICollection)list).SyncRoot) - list.Add(resourceReference); - //stores[store].Add(resourceReference); - } // assign parent's store as a store - else if (parent != null) + if (parent != null) { - store = parent.Instance.Store; + // assign parent as a store + if (parent is IStore) + { + store = (IStore)parent; + List> list; + if (stores.TryGetValue(store, out list)) + lock (((ICollection)list).SyncRoot) + list.Add(resourceReference); + //stores[store].Add(resourceReference); + } + else + { + store = parent.Instance.Store; - List> list; - if (stores.TryGetValue(store, out list)) - lock (((ICollection)list).SyncRoot) - list.Add(resourceReference); - - //stores[store].Add(resourceReference); + List> list; + if (stores.TryGetValue(store, out list)) + lock (((ICollection)list).SyncRoot) + list.Add(resourceReference); + } } // assign self as a store (root store) else if (resource is IStore) @@ -722,9 +744,16 @@ namespace Esiur.Resource /// Resource template. public static ResourceTemplate GetTemplate(Type type) { + + if (!Codec.ImplementsInterface(type, typeof(IResource))) + return null; var baseType = ResourceProxy.GetBaseType(type); + if (baseType == typeof(IResource) ) + return null; + + // loaded ? foreach (var t in templates.Values) if (t.ClassName == baseType.FullName) @@ -741,10 +770,10 @@ namespace Esiur.Resource /// /// Class Id. /// Resource template. - public static AsyncReply GetTemplate(Guid classId) + public static ResourceTemplate GetTemplate(Guid classId) { if (templates.ContainsKey(classId)) - return new AsyncReply(templates[classId]); + return templates[classId]; return null; } diff --git a/Esiur/Security/Membership/IMembership.cs b/Esiur/Security/Membership/IMembership.cs index 5f07ee0..51fa0be 100644 --- a/Esiur/Security/Membership/IMembership.cs +++ b/Esiur/Security/Membership/IMembership.cs @@ -42,7 +42,7 @@ namespace Esiur.Security.Membership AsyncReply GetToken(ulong tokenIndex, string domain); AsyncReply Login(Session session); AsyncReply Logout(Session session); - + bool GuestsAllowed { get; } AsyncReply TokenExists(ulong tokenIndex, string domain); } } diff --git a/Esiur/Security/Permissions/ActionType.cs b/Esiur/Security/Permissions/ActionType.cs index 9ec856c..678fc0b 100644 --- a/Esiur/Security/Permissions/ActionType.cs +++ b/Esiur/Security/Permissions/ActionType.cs @@ -45,6 +45,7 @@ namespace Esiur.Security.Permissions AddChild, RemoveChild, Rename, - ReceiveEvent + ReceiveEvent, + ViewTemplate } }