using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices.ComTypes; using InABox.Core; namespace InABox.Clients { public class Credentials : ISerializeBinary { [Obsolete] public string? UserID { get; set; } [Obsolete] public string? Password { get; set; } public string Platform { get; set; } public string Version { get; set; } public Guid Session { get; set; } public void SerializeBinary(CoreBinaryWriter writer) { writer.Write(UserID ?? ""); writer.Write(Password ?? ""); writer.Write(Platform); writer.Write(Version); writer.Write(Session); } public void DeserializeBinary(CoreBinaryReader reader) { UserID = reader.ReadString(); Password = reader.ReadString(); Platform = reader.ReadString(); Version = reader.ReadString(); Session = reader.ReadGuid(); } } public abstract class Request { public Request() { Credentials = new Credentials(); } public static Action? BeforeRequest { get; set; } public Credentials Credentials { get; set; } public abstract RequestMethod GetMethod(); public virtual void SerializeBinary(CoreBinaryWriter writer) { Credentials.SerializeBinary(writer); } public virtual void DeserializeBinary(CoreBinaryReader reader) { Credentials.DeserializeBinary(reader); } } public enum StatusCode { OK, Incomplete, Error, Unauthenticated, BadServer } public enum RequestMethod { Info, Ping, Check2FA, Validate, MultiQuery, MultiDelete, Delete, Save, MultiSave, Query, Notify } public abstract class Response { public Response() { Status = StatusCode.Incomplete; Messages = new List(); } public StatusCode Status { get; set; } public List Messages { get; } public virtual void DeserializeBinary(CoreBinaryReader reader) { Status = (StatusCode)Enum.ToObject(typeof(StatusCode), reader.ReadInt32()); Messages.Clear(); var nMessages = reader.ReadInt32(); for (int i = 0; i < nMessages; ++i) { Messages.Add(reader.ReadString()); } } public virtual void SerializeBinary(CoreBinaryWriter writer) { writer.Write((int)Status); writer.Write(Messages.Count); foreach(var message in Messages) { writer.Write(message); } } } public abstract class BaseRequest : Request where TEntity : Entity, new() { } public abstract class BaseResponse : Response where TEntity : Entity, new() { } /*public class ListRequest : BaseRequest where TEntity : Entity, new() { public Filter? Filter { get; set; } public Columns? Columns { get; set; } public SortOrder? Sort { get; set; } public override RequestMethod GetMethod() => RequestMethod.Query; } public class ListResponse : BaseResponse where TEntity : Entity, new() { public IEnumerable Data { get; set; } }*/ public class QueryRequest : BaseRequest where TEntity : Entity, new() { public Filter? Filter { get; set; } public Columns? Columns { get; set; } public SortOrder? Sort { get; set; } public override RequestMethod GetMethod() => RequestMethod.Query; } public class QueryResponse : BaseResponse, ISerializeBinary where TEntity : Entity, new() { public CoreTable? Items { get; set; } public override void DeserializeBinary(CoreBinaryReader reader) { base.DeserializeBinary(reader); if (reader.ReadBoolean()) { Items = new CoreTable(); Items.DeserializeBinary(reader); } else { Items = null; } } public override void SerializeBinary(CoreBinaryWriter writer) { base.SerializeBinary(writer); if(Items is null) { writer.Write(false); } else { writer.Write(true); Items.SerializeBinary(writer); } } } /*public class LoadRequest : BaseRequest where TEntity : Entity, new() { public Filter? Filter { get; set; } public SortOrder? Sort { get; set; } } public class LoadResponse : BaseResponse where TEntity : Entity, new() { public TEntity[] Items { get; set; } }*/ public class MultiSaveRequest : BaseRequest, ISerializeBinary where TEntity : Entity, new() { public TEntity[] Items { get; set; } public string AuditNote { get; set; } [Obsolete("We don't like this; it should always be true.")] // Added 11/04/23 to address incompatibility, remove as soon as possible; the relevant code to update is in RestService; update assuming that ReturnOnlyChanged is always true. public bool ReturnOnlyChanged { get; set; } = false; public override RequestMethod GetMethod() => RequestMethod.MultiSave; public override void SerializeBinary(CoreBinaryWriter writer) { base.SerializeBinary(writer); writer.WriteObjects(Items); writer.Write(AuditNote); writer.Write(ReturnOnlyChanged); } public override void DeserializeBinary(CoreBinaryReader reader) { base.DeserializeBinary(reader); Items = reader.ReadObjects().ToArray(); AuditNote = reader.ReadString(); ReturnOnlyChanged = reader.ReadBoolean(); } } public class MultiSaveResponse : BaseResponse where TEntity : Entity, new() { //public Guid[] IDs { get; set; } public TEntity[] Items { get; set; } public List> ChangedValues { get; set; } = new List>(); } public class SaveRequest : BaseRequest, ISerializeBinary where TEntity : Entity, new() { public TEntity Item { get; set; } public string AuditNote { get; set; } public bool ReturnOnlyChanged { get; set; } = false; public override RequestMethod GetMethod() => RequestMethod.Save; public override void SerializeBinary(CoreBinaryWriter writer) { base.SerializeBinary(writer); writer.WriteObject(Item); writer.Write(AuditNote); writer.Write(ReturnOnlyChanged); } public override void DeserializeBinary(CoreBinaryReader reader) { base.DeserializeBinary(reader); Item = reader.ReadObject(); AuditNote = reader.ReadString(); ReturnOnlyChanged = reader.ReadBoolean(); } } public class SaveResponse : BaseResponse where TEntity : Entity, new() { //public Guid ID { get; set; } public TEntity Item { get; set; } public Dictionary ChangedValues { get; set; } = new Dictionary(); } public class DeleteRequest : BaseRequest where TEntity : Entity, new() { public TEntity Item { get; set; } public string AuditNote { get; set; } public override RequestMethod GetMethod() => RequestMethod.Delete; } public class DeleteResponse : BaseResponse where TEntity : Entity, new() { } public class MultiDeleteRequest : BaseRequest where TEntity : Entity, new() { public TEntity[] Items { get; set; } public string AuditNote { get; set; } public override RequestMethod GetMethod() => RequestMethod.MultiDelete; } public class MultiDeleteResponse : BaseResponse where TEntity : Entity, new() { } public class MultiQueryRequest : Request { public Dictionary TableTypes { get; set; } public Dictionary Filters { get; set; } public Dictionary Columns { get; set; } public Dictionary Sorts { get; set; } public override RequestMethod GetMethod() => RequestMethod.MultiQuery; } public class MultiQueryResponse : Response, ISerializeBinary { public MultiQueryResponse() { Tables = new Dictionary(); } public Dictionary Tables { get; set; } public override void DeserializeBinary(CoreBinaryReader reader) { base.DeserializeBinary(reader); Tables.Clear(); var nTables = reader.ReadInt32(); for(int i = 0; i < nTables; ++i) { var name = reader.ReadString(); var table = new CoreTable(); table.DeserializeBinary(reader); Tables[name] = table; } } public override void SerializeBinary(CoreBinaryWriter writer) { base.SerializeBinary(writer); writer.Write(Tables.Count); foreach(var (name, table) in Tables) { writer.Write(name); table.SerializeBinary(writer); } } } public enum ValidationResult { VALID, INVALID, REQUIRE_2FA, PASSWORD_EXPIRED } public class ValidateRequest : Request { public string? UserID { get; set; } public string? Password { get; set; } public string? PIN { get; set; } public bool UsePIN { get; set; } public override RequestMethod GetMethod() => RequestMethod.Validate; } public class ValidateResponse : Response { public ValidationResult ValidationResult { get; set; } public Guid UserGuid { get; set; } public string UserID { get; set; } public Guid SecurityID { get; set; } public Guid Session { get; set; } public string? Recipient2FA { get; set; } public DateTime PasswordExpiration { get; set; } } public class Check2FARequest : Request { public string Code { get; set; } public override RequestMethod GetMethod() => RequestMethod.Check2FA; } public class Check2FAResponse : Response { public bool Valid { get; set; } } public class PingRequest : Request { public override RequestMethod GetMethod() => RequestMethod.Ping; } public class PingResponse : Response { } public class InfoRequest : Request { public override RequestMethod GetMethod() => RequestMethod.Info; } public class DatabaseInfo { public string? ColorScheme { get; set; } public byte[]? Logo { get; set; } public string Version { get; set; } public bool IsHTTPS { get; set; } } public class InfoResponse : Response { public DatabaseInfo Info { get; set; } public InfoResponse() : base() { Info = new DatabaseInfo(); } } public static class Extensions { public static T Status(this T response, StatusCode status) where T : Response { response.Status = status; return response; } } public class RequestException : Exception { public StatusCode Status { get; set; } public RequestMethod Method { get; set; } public RequestException(string message, StatusCode status, Request request): base(message) { Status = status; Method = request.GetMethod(); } } }