private void RegisterSubscriptionClientMessageHandler() { _subscriptionClient.RegisterMessageHandler( async(message, token) => { var responseId = message.CorrelationId; var eventType = (EventType)Enum.Parse(typeof(EventType), message.UserProperties[ServiceBusConstants.UserPropertyEventType] as string); //var eventName = (EventName)Enum.Parse(typeof(EventName), message.UserProperties[ServiceBusConstants.UserPropertyEventName] as string); var eventName = (EventName)Enum.Parse(typeof(EventName), message.Label as string); var eventIndexInFlowMap = Int32.Parse(message.UserProperties[ServiceBusConstants.UserPropertyEventIndexInFlowMap].ToString()); var messageData = Encoding.UTF8.GetString(message.Body); var payLoad = JsonConvert.DeserializeObject <BasePayLoad>(messageData); SedaEvent sedaEvent = new SedaEvent( responseId, eventType, payLoad, eventName, eventIndexInFlowMap ); await this._messageProcessor.ProcessMessageAsync(sedaEvent); // Complete the message so that it is not received again. await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken); }, new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = this._azureServiceBusSettings.MaxConcurrentCalls, AutoComplete = false }); }
private void RegisterSubscriptionClientMessageHandler() { _subscriptionClient.RegisterMessageHandler( async(message, token) => { SedaEvent sedaEvent = new SedaEvent(); sedaEvent.ResponseId = message.CorrelationId; sedaEvent.To = (SedaService)Enum.Parse(typeof(SedaService), message.To); sedaEvent.From = (SedaService)Enum.Parse(typeof(SedaService), message.UserProperties[UserPropertyFrom] as string); sedaEvent.CmsOperation = (CmsOperation)Enum.Parse(typeof(CmsOperation), message.UserProperties[UserPropertyCmsOperation] as string); sedaEvent.EventType = (EventType)Enum.Parse(typeof(EventType), message.UserProperties[UserPropertyEventType] as string); sedaEvent.Priority = (Priority)Enum.Parse(typeof(Priority), message.UserProperties[UserPropertyPriority] as string); sedaEvent.EventName = (EventName)Enum.Parse(typeof(EventName), message.UserProperties[UserPropertyEventName] as string); sedaEvent.EventIndexInFlowMap = Int32.Parse(message.UserProperties[UserPropertyEventIndexInFlowMap].ToString()); var messageData = Encoding.UTF8.GetString(message.Body); sedaEvent.PayLoad = JsonConvert.DeserializeObject <BasePayLoad>(messageData); await ProcessEvent(sedaEvent); // Complete the message so that it is not received again. await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken); }, new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = this._azureServiceBusSettings.MaxConcurrentCalls, AutoComplete = false }); }
public async Task ProcessMessageAsync(SedaEvent sedaEvent) { Type eventHandlerType = null; var responseSedaEvent = sedaEvent; if (_eventHandlerRegistry.HasHandlersForEvent(sedaEvent.EventName)) { try { eventHandlerType = _eventHandlerRegistry.GetHandlerForEvent(sedaEvent.EventName); var eventHandlerInstance = this._serviceProvider.GetService(eventHandlerType); var concreteType = typeof(IEventHandler <>).MakeGenericType(eventHandlerType); dynamic eventHandlerResult = await(Task <ExpandoObject>) concreteType.GetMethod("Handle").Invoke(eventHandlerInstance, new object[] { sedaEvent }); EventTypeResponseSuccessPayLoad responsePayLoad = new EventTypeResponseSuccessPayLoad(); responsePayLoad.Request = sedaEvent.PayLoad.Request; responsePayLoad.Response = eventHandlerResult; responseSedaEvent.PayLoad = responsePayLoad; responseSedaEvent.EventType = EventType.ResponseSuccess; await this._messagePublisher.PublishAsync(responseSedaEvent); } catch (Exception ex) { // The event handlers would retry (Retry policies to be configured - TODO) the operations on the message // Will throw an exception when they cannot process the message // So we are not depending on message to be re-processed. var exceptionDetail = new ExceptionDetail() { ExceptionMessage = ex.Message, StackTrace = ex.StackTrace, InnerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : null, InnerExceptionStackTrace = ex.InnerException != null ? ex.InnerException.StackTrace : null }; var errorResponse = new ErrorResponse() { Message = "Error occured in execution of: " + eventHandlerType.ToString(), ExceptionDetail = exceptionDetail }; var errorResponsePayLoad = new EventTypeResponseFailurePayLoad() { Failure = errorResponse }; responseSedaEvent.PayLoad = errorResponsePayLoad; responseSedaEvent.EventType = EventType.ResponseFailure; await this._messagePublisher.PublishAsync(responseSedaEvent); } } else { // TODO log warning } }
public async Task <IActionResult> ValidateRepository([FromBody] ValidateRepositoryRequest request) { var result = await this._responseGenerator.CreateResponseAsync(CmsOperation.GetProjects); BasePayLoad payLoad = new BasePayLoad(); payLoad.Request = request; SedaEvent sedaEvent = new SedaEvent(result._id, SedaService.Orchestrator, SedaService.CMS, EventType.Request, CmsOperation.ValidateRepository, Priority.One, payLoad); await this._eventBusMessagePublisher.PublishAsync(sedaEvent); BaseApiResponse apiResponse = new BaseApiResponse(); apiResponse.ResponseId = result._id; return(Ok(apiResponse)); }
public async Task PublishAsync(SedaEvent sedaEvent) { var message = new Message() { CorrelationId = sedaEvent.ResponseId, Label = sedaEvent.EventName.ToString() }; message.UserProperties.Add(ServiceBusConstants.UserPropertyEventType, sedaEvent.EventType.ToString()); message.UserProperties.Add(ServiceBusConstants.UserPropertyEventIndexInFlowMap, sedaEvent.EventIndexInFlowMap); var jsonString = JsonConvert.SerializeObject(sedaEvent.PayLoad); message.Body = Encoding.UTF8.GetBytes(jsonString); await this._topicClient.SendAsync(message); }
public async Task PublishAsync(SedaEvent sedaEvent) { var message = new Message() { CorrelationId = sedaEvent.ResponseId, To = sedaEvent.To.ToString() }; message.UserProperties.Add(UserPropertyFrom, sedaEvent.From.ToString()); message.UserProperties.Add(UserPropertyCmsOperation, sedaEvent.CmsOperation.ToString()); message.UserProperties.Add(UserPropertyEventType, sedaEvent.EventType.ToString()); message.UserProperties.Add(UserPropertyPriority, sedaEvent.Priority.ToString()); message.UserProperties.Add(UserPropertyEventIndexInFlowMap, sedaEvent.EventIndexInFlowMap); message.UserProperties.Add(UserPropertyEventName, sedaEvent.EventName.ToString()); var jsonString = JsonConvert.SerializeObject(sedaEvent.PayLoad); message.Body = Encoding.UTF8.GetBytes(jsonString); await this._topicClient.SendAsync(message); }
public async Task ProcessMessageAsync(SedaEvent sedaEvent) { var responseObject = await this._responseRepository.GetDocumentAsync(sedaEvent.ResponseId); var flowMap = responseObject.FlowMap; switch (sedaEvent.EventType) { case EventType.Request: // Need to raise the first set of events from the Map var nextEventSet = flowMap.GetNextSetofEvents(-1); foreach (var eventToRaise in nextEventSet) { var eventMessage = new SedaEvent( sedaEvent.ResponseId, EventType.Request, sedaEvent.PayLoad, eventToRaise.EventName, eventToRaise.Index ); // Set the Event Status to Wait await this._responseRepository.UpdateSpecificElementByFilterAsync( x => x._id == sedaEvent.ResponseId && x.FlowMap.Events.Any(y => y.Index == eventToRaise.Index), x => x.FlowMap.Events[-1].Status, EventStatus.Wait); await this._messagePublisher.PublishAsync(eventMessage); } break; case EventType.ResponseSuccess: // Update the current event status in DB await this._responseRepository.UpdateSpecificElementByFilterAsync( x => x._id == sedaEvent.ResponseId && x.FlowMap.Events.Any(y => y.Index == sedaEvent.EventIndexInFlowMap), x => x.FlowMap.Events[-1].Status, EventStatus.Success); // Get the latest response status - Is the response already marked as complete, // this can happen if a failure occurred with one of the other parallel events within the group. // If so, dont need to do anything, just return if (responseObject.Status == EntityStatus.Error) { break; } // Check if the entire operation is complete (current one could be the last pending event) // if so, update the response status to OK and add the response to response content // send FCM notification that operation is complete with success // return // ?? ToDo: multiple success events in parallel (See if only one thread can update the response status through MongoDB findAndModify() // and only that thread raises the FCM notification var latestResponseObj = await this._responseRepository.GetDocumentAsync(sedaEvent.ResponseId); if (latestResponseObj.FlowMap.IsOperationComplete) { latestResponseObj.Content = currentCmsMessage.CMSMessageBody; latestResponseObj.Status = EntityStatus.Ok; await this._responseRepository.ReplaceElementAsync(sedaEvent.ResponseId, latestResponseObj); await SendOperationCompleteNotificationAsync(latestResponseObj); break; } // If the whole operation is not complete, check if all events from the current Event Group are complete // if so, raise the next set of events. // TODO: (?? multiple success events in parallel within the same group. // Need to have a flag to mark the group as complete, whichever thread gets that update will raise next set of events) if (latestResponseObj.FlowMap.GetCompleteStatusForEventGroupByEventIndex(sedaEvent.EventIndexInFlowMap)) { nextEventSet = flowMap.GetNextSetofEvents(sedaEvent.EventIndexInFlowMap); foreach (var eventToRaise in nextEventSet) { var eventMessage = new SedaEvent( sedaEvent.ResponseId, EventType.Request, currentCmsMessage.CMSMessageBody, eventToRaise.EventName, eventToRaise.Index ); // Set the Event Status to Wait await this._responseRepository.UpdateSpecificElementByFilterAsync( x => x._id == sedaEvent.ResponseId && x.FlowMap.Events.Any(y => y.Index == eventToRaise.Index), x => x.FlowMap.Events[-1].Status, EventStatus.Wait); await this._messagePublisher.PublishAsync(eventMessage); } } break; case EventType.ResponseFailure: // update the DB status for the event await this._responseRepository.UpdateSpecificElementByFilterAsync( x => x._id == sedaEvent.ResponseId && x.FlowMap.Events.Any(y => y.Index == sedaEvent.EventIndexInFlowMap), x => x.FlowMap.Events[-1].Status, EventStatus.Failure); // assumption: Abort the operation on the first failure. That is: // On first failure, update the operation status to Error // and send the FCM notification for complete with failure // TODO: If multiple parallel events are executing in a taskgroup, // the one or more events could fail and others could be successful // should not be sending multiple failure complete notifications in that case. // Get the response status, if it is not Error, then this is the first error // Update the response status to Error, if that is successful // raise the FCM notification for complete with failure var failResponseObj = await this._responseRepository.GetDocumentAsync(sedaEvent.ResponseId); if (failResponseObj.Status != EntityStatus.Error) { failResponseObj.Status = EntityStatus.Error; await this._responseRepository.ReplaceElementAsync(sedaEvent.ResponseId, failResponseObj); await SendOperationCompleteNotificationAsync(failResponseObj); break; } break; } }