internal static List <EfParaquickMessage> FindNextMessageSet(DbContext db, EfParaquickSession efSession) { //Find the next messageset sequence for this session (we go by ResponseDate) - can be null long?messageSetSequence = efSession.ParaquickMessages.Where(m => m.ResponseDate == null).OrderBy(m => m.MessageSetSequence).ThenBy(m => m.MessageSequence).FirstOrDefault()?.MessageSetSequence; //get the messages for the set - can be empty list var efMessages = efSession.ParaquickMessages.Where(m => m.MessageSetSequence == messageSetSequence).OrderBy(m => m.MessageSetSequence).ThenBy(m => m.MessageSequence).ToList(); //OLD //Find the next messageset sequence for this session (we go by ResponseDate) - can be null //var xmessageSetSequence= (from msg in db.ParaquickMessages where msg.SessionId == efSession.Id && msg.ResponseDate==null orderby msg.MessageSetSequence, msg.MessageSequence select msg.MessageSetSequence).First(); //get the messages for the set - can be empty list //var efMessages=(from msg in db.ParaquickMessages where msg.MessageSetSequence==messageSetSequence orderby msg.MessageSetSequence, msg.MessageSequence select msg).ToList(); //verify that the set has not been partially processed for some reason foreach (var efMessage in efMessages) { if (efMessage.ResponseDate != null && efMessage.ResponseXml != null && efMessage.StatusCode != null) { return(null); } } return(efMessages); }
protected override string OnGetLastError(string ticket) { string message; if (ticket == ServiceUtils.ZeroTicket) { //note: we should never see the zero tickets, but we report it here message = "Can't process zero ticket"; Error(message); } else { using (var db = ServiceUtils.CreateDbContext()) { EfParaquickSession efSession = ServiceUtils.FindSession(db, ticket); if (efSession != null) { //return response-level and ticket-level errors //(no need to log them again) message = OnGetLastError(db, efSession); } else { //we report bad tickets here message = $"can't find ticket ({ticket})"; Error(message); } } } return(message); }
internal static void Close(DbContext db, EfParaquickSession efSession) { //change status to success or error based on session errors and response status codes if (efSession.ParaquickMessages.Where(m => m.StatusCode == null).Count() > 0) { efSession.StatusId = (long)SessionStatuses.Incomplete; } else if (efSession.ParaquickSessionErrors.Count > 0) { efSession.StatusId = (long)SessionStatuses.Error; } else if (efSession.ParaquickMessages.Where(m => m.StatusCode != "0").Count() > 0) { efSession.StatusId = (long)SessionStatuses.Error; } else { efSession.StatusId = (long)SessionStatuses.Success; } efSession.EndDate = DateTime.Now; TruncateSession(efSession); db.SaveChanges(); }
internal static int CalculatePercentComplete(DbContext db, EfParaquickSession efSession) { int complete = efSession.ParaquickMessages.Where(m => m.ResponseDate != null).ToList().Count; int total = efSession.ParaquickMessages.Count; int pctComplete; if (complete == total) { pctComplete = 100; } else { var fPc = ((float)complete) / total; pctComplete = (int)(fPc * 100); //excessive but certain if (pctComplete <= 0) { pctComplete = 1; } //excessive but certain if (pctComplete >= 100) { pctComplete = 99; } } return(pctComplete); }
protected virtual bool OnRetryConnection(DbContext db, EfParaquickSession efSession, HResult hResult, out string companyFilePath) { //default behavior is to not retry //but implmentations may want to instruct the webconnector to try again, optionally with another file companyFilePath = null; return(false); }
public void Save() { using (var db = ServiceUtils.CreateDbContext()) { EfParaquickSession efSession = new EfParaquickSession(); efSession.CompanyId = CompanyId; efSession.Ticket = Ticket; efSession.CreateDate = CreateDate; efSession.StatusId = (long)SessionStatuses.New; ServiceUtils.TruncateSession(efSession); db.ParaquickSessions.Add(efSession); //add requests foreach (var msgSet in _messageSets.OrderBy(ms => ms.Sequence)) { foreach (var msg in msgSet.Messages.OrderBy(r => r.Sequence)) { if (msg.RqMsg != null) { string rqTypeName = msg.RqMsg.GetType().Name; var efMessageType = (from mt in db.ParaquickMessageTypes where mt.RequestTypeName == rqTypeName select mt).FirstOrDefault(); if (efMessageType != null) { EfParaquickMessage efMessage = new EfParaquickMessage(); efMessage.Session = efSession; efMessage.MessageSetSequence = msgSet.Sequence; efMessage.MessageSequence = msg.Sequence; efMessage.ApplicationEntityId = msg.ApplicationEntityId; efMessage.RequestId = msg.RequestId; efMessage.RequestDate = msg.RequestDate; efMessage.MessageType = efMessageType; efMessage.RequestXml = msg.RqMsg.Serialize(); ServiceUtils.TruncateMessage(efMessage); db.ParaquickMessages.Add(efMessage); } else { throw new InvalidOperationException($"RqMsg type not found ({rqTypeName})"); } } else { throw new InvalidOperationException("RqMsg must have a value"); } } } db.SaveChanges(); //refresh stuff Id = efSession.Id; LoadMessageSets(db, efSession); } }
internal static void Open(DbContext db, EfParaquickSession efSession) { efSession.StatusId = (long)SessionStatuses.Open; efSession.StartDate = DateTime.Now; TruncateSession(efSession); db.SaveChanges(); }
internal static void Reset(DbContext db, EfParaquickSession efSession) { efSession.StatusId = (long)SessionStatuses.New; efSession.StartDate = null; TruncateSession(efSession); db.SaveChanges(); }
internal static void Session(DbContext db, EfParaquickSession efSession, string hcpXml, string qbCountry, int?qbMajorVersion, int?qbMinorVersion) { efSession.Company.HcpXml = hcpXml; efSession.Company.Country = qbCountry; efSession.Company.Major = qbMajorVersion; efSession.Company.Minor = qbMinorVersion; TruncateSession(efSession); db.SaveChanges(); }
protected virtual string OnGetCloseMessage(DbContext db, EfParaquickSession efSession) { //TODO StopOnErrors? if we didn't stop on errors, do we notify user that errors occured? string message = $"Session closed."; if (efSession.StatusId == (int)SessionStatuses.Error) { message += " Errors occurred."; } return(message); }
internal static void SessionError(DbContext db, EfParaquickSession efSession, string message) { EfParaquickSessionError sessionError = new EfParaquickSessionError(); sessionError.Session = efSession; sessionError.Date = DateTime.Now; sessionError.Message = message; TruncateSessionError(sessionError); db.ParaquickSessionErrors.Add(sessionError); db.SaveChanges(); }
protected override string OnAuthenticate(string username, string password, out AuthCodes authCode, out AuthOptions authOptions) { using (var db = ServiceUtils.CreateDbContext()) { //Find the company based on the unique user name EfParaquickCompany efCompany = (from c in db.ParaquickCompanies where c.UserName == username && c.Password == password select c).FirstOrDefault(); if (efCompany != null) { authOptions = new AuthOptions(); EfParaquickSession efSession = ServiceUtils.FindNextSession(db, efCompany.Id); //work to do? if (efSession != null) { //allow implementors to override authCode = OnNewSession(db, efSession, authOptions); if (authCode == AuthCodes.VALID) { //open and return ticket ServiceUtils.Open(db, efSession); return(efSession.Ticket); } else { //implementors can override the code and the options //but not the session record or the ticket return(ServiceUtils.ZeroTicket); } } else { //nothing to do authCode = AuthCodes.NONE; } } else { //bad credentials Error($"Incorrect username or password for user ({username})"); authCode = AuthCodes.NVU; } authOptions = null; return(ServiceUtils.ZeroTicket); } }
protected void LoadMessageSets(DbContext db, EfParaquickSession efSession) { _messageSets.Clear(); var efMessages = (from m in db.ParaquickMessages where m.SessionId == efSession.Id orderby m.MessageSetSequence, m.MessageSequence select m).ToList(); MessageSet messageSet = null; foreach (var efMessage in efMessages) { if (efMessage.MessageSetSequence != messageSet?.Sequence) { messageSet = new MessageSet(efMessage); _messageSets.Add(messageSet); } messageSet.AddMessage(efMessage); } }
public static string FormatErrors(EfParaquickSession efSession) { //format response-level and ticket-level errors string message = "Errors occurred."; foreach (var se in efSession.ParaquickSessionErrors) { message += $" [Session ({se.SessionId}) : {se.Message}]"; } foreach (var msg in efSession.ParaquickMessages) { if (msg.StatusCode != "0") { message += $" [Request ({msg.Id}) : {msg.StatusCode} - {msg.StatusMessage}]"; } } return(message); }
protected override string OnConnectionError(string ticket, HResult hResult) { if (ticket != ServiceUtils.ZeroTicket) { using (var db = ServiceUtils.CreateDbContext()) { EfParaquickSession efSession = ServiceUtils.FindSession(db, ticket); if (efSession != null) { //log this error SessionError(db, efSession, hResult.Format()); //ask the implementation if we should tell the WC to retry string companyFilePath; if (OnRetryConnection(db, efSession, hResult, out companyFilePath)) { return(companyFilePath); } else { //if not, we reset the session to "New" and let the user fix it and try again ServiceUtils.Reset(db, efSession); } } else { //note: getLastError will not be called so we log this here Error("Can't find ticket ({ticket}) during connection error"); } } } else { //note: getLastError will not be called so we log this here Error("Can't process zeroticket during connection error"); } return("done"); }
protected override string OnCloseConnection(string ticket) { //note: we should never see the zero ticket here either // but it's already been logged at this point if (ticket != ServiceUtils.ZeroTicket) { using (var db = ServiceUtils.CreateDbContext()) { EfParaquickSession efSession = ServiceUtils.FindSession(db, ticket); //bad tickets have already been logged if (efSession != null) { //close session ServiceUtils.Close(db, efSession); //allow implementation to send custom message back to WC return(OnGetCloseMessage(db, efSession)); } } } return($"Session closed for invalid ticket {ticket}"); }
protected virtual void OnSessionError(DbContext db, EfParaquickSession efSession, string errorMessage) { }
protected virtual AuthCodes OnNewSession(DbContext db, EfParaquickSession efSession, AuthOptions authOptions) { //the default behavior is to require the company file to be open ("") with no other options specified //but implmentations may want to do something different return(AuthCodes.VALID); }
protected void ResponseError(DbContext db, EfParaquickSession efSession, EfParaquickMessage efMessage, string errorMessage) { //error message is already in messages table OnResponseError(db, efSession, efMessage, errorMessage); }
protected virtual void OnResponseError(DbContext db, EfParaquickSession efSession, EfParaquickMessage efMessage, string errorMessage) { }
public static void TruncateSession(EfParaquickSession efSession) { DataAnnotations.ObjectTruncator.Truncate(efSession); DataAnnotations.ObjectTruncator.Truncate(efSession.Company); }
protected void RequestError(DbContext db, EfParaquickSession efSession, EfParaquickMessage efMessage, string errorMessage) { ServiceUtils.RequestError(db, efMessage, errorMessage); OnRequestError(db, efSession, efMessage, errorMessage); }
protected virtual string OnGetLastError(DbContext db, EfParaquickSession efSession) { return(ServiceUtils.FormatErrors(efSession)); }
protected override string OnCreateRequestMessage(string ticket, string hcpXml, string companyFilePath, string qbCountry, int qbMajorVersion, int qbMinorVersion) { //zerotickets should never be seen here, but if so, they will recur in getLastError and be logged there if (ticket != ServiceUtils.ZeroTicket) { using (var db = ServiceUtils.CreateDbContext()) { EfParaquickSession efSession = ServiceUtils.FindSession(db, ticket); //bad tickets will recur in getLastError and be logged there if (efSession != null) { //confirm file path if (IsCompanyFileValid(db, efSession, companyFilePath)) { //save the hcpXml & other information in the company record if (!string.IsNullOrEmpty(hcpXml)) { ServiceUtils.Session(db, efSession, hcpXml, qbCountry, qbMajorVersion, qbMinorVersion); } //find next message for this ticket List <EfParaquickMessage> efMessages = ServiceUtils.FindNextMessageSet(db, efSession); if (efMessages != null) { //TODO do we set OnError to stopOnErrors in the request? RqMsgSet rqMsgSet = new RqMsgSet(); foreach (var efMessage in efMessages) { //deserialize request message IRqMsg rqMsg = (IRqMsg)Msg.Deserialize(efMessage.MessageType.RequestTypeName, efMessage.RequestXml); //allow implementation to see/modify message string errorMessage = OnRequest(db, efMessage, rqMsg); if (errorMessage == null) { //add it to message set rqMsgSet.Add(rqMsg); } else { //"close" this request, don't send to WC RequestError(db, efSession, efMessage, errorMessage); } } //TODO is "nothing to do" at this point an error? //send it string xml = rqMsgSet.Serialize(); return(xml); } else { //something wrong with the message set in the database, log it SessionError(db, efSession, $"An error occurred when building the message set for session ({efSession.Id})"); //this is pretty bad, let's just close the session ServiceUtils.Close(db, efSession); } } else { //log error SessionError(db, efSession, $"Incorrect company file ({companyFilePath}) for company ({efSession.CompanyId})"); } } } } //error condition return(""); }
protected void SessionError(DbContext db, EfParaquickSession efSession, string errorMessage) { ServiceUtils.SessionError(db, efSession, errorMessage); OnSessionError(db, efSession, errorMessage); }
protected override int OnResponseMessage(string ticket, string responseXml, HResult hResult) { //zerotickets should never be seen here, but if so, they will recur in getLastError and be logged there if (ticket != ServiceUtils.ZeroTicket) { using (var db = ServiceUtils.CreateDbContext()) { EfParaquickSession efSession = ServiceUtils.FindSession(db, ticket); //bad tickets will recur in getLastError and be logged there if (efSession != null) { //COM error? if (hResult == null) { //deserialize response and process success/error RsMsgSet rsMsgSet = new RsMsgSet(); QBXML qbxml = rsMsgSet.Deserialize(responseXml); //TODO update paraquick entities based on response type foreach (var rsMsg in rsMsgSet) { var efMessage = efSession.ParaquickMessages.Where(m => m.RequestId == rsMsg.requestID).FirstOrDefault(); if (efMessage != null) { ServiceUtils.Response(db, efMessage, rsMsg); //allow implementor to do something with response OnResponse(db, efMessage, rsMsg); if (rsMsg.statusCode != "0") { //TODO stop on errors? ResponseError(db, efSession, efMessage, rsMsg.statusMessage); } } else { //TODO stop on errors? SessionError(db, efSession, $"Can't find request ({rsMsg.requestID})"); } } //TODO StopOnErrors? do we stop on errors here (return -1) or keep going? //report "%" - completed messages/total messages for session int pctComplete = ServiceUtils.CalculatePercentComplete(db, efSession); return(pctComplete); } else { SessionError(db, efSession, $"COM Error {hResult.Format()}"); } } } } //error condition return(-1); }
protected virtual bool IsCompanyFileValid(DbContext db, EfParaquickSession efSession, string companyFilePath) { //the default behavior is to always use the file that is currently open, //but implmentations may want to do something different return(true); }