/// <summary> /// Generates the response for each outbound request. /// </summary> /// <param name="itemId">Id for response.</param> /// <param name="statusCode">HttpStatusCode for the request been processed.</param> /// <param name="eTag">Etag based on response.</param> /// <param name="error">Forward error to Shifts if any.</param> /// <returns>ShiftsIntegResponse.</returns> private static ShiftsIntegResponse GenerateResponse(string itemId, HttpStatusCode statusCode, string eTag, ResponseError error) { // The outbound acknowledgement does not honor the null Etag, 502 Bad Gateway is thrown if so. // Checking for the null eTag value, from the attributes in the payload. string responseEtag; if (string.IsNullOrEmpty(eTag)) { responseEtag = GenerateNewGuid(); } else { responseEtag = eTag; } var integrationResponse = new ShiftsIntegResponse() { Id = itemId, Status = (int)statusCode, Body = new Body { Error = error, ETag = responseEtag, }, }; return(integrationResponse); }
/// <summary> /// Generate response to prevent actions. /// </summary> /// <param name="jsonModel">The request payload.</param> /// <param name="errorMessage">Error message to send while preventing action.</param> /// <returns>List of ShiftsIntegResponse.</returns> public static List <ShiftsIntegResponse> CreateMultipleBadResponses(RequestModel jsonModel, string errorMessage) { List <ShiftsIntegResponse> shiftsIntegResponses = new List <ShiftsIntegResponse>(); var integrationResponse = new ShiftsIntegResponse(); foreach (var item in jsonModel.Requests) { integrationResponse = CreateBadResponse(item.Id, error: errorMessage); shiftsIntegResponses.Add(integrationResponse); } return(shiftsIntegResponses); }
/// <summary> /// Generate response to prevent actions. /// </summary> /// <param name="jsonModel">The request payload.</param> /// <param name="errorMessage">Error message to send while preventing action.</param> /// <returns>List of ShiftsIntegResponse.</returns> private static List <ShiftsIntegResponse> GenerateResponseToPreventAction(RequestModel jsonModel, string errorMessage) { List <ShiftsIntegResponse> shiftsIntegResponses = new List <ShiftsIntegResponse>(); var integrationResponse = new ShiftsIntegResponse(); foreach (var item in jsonModel.Requests) { ResponseError responseError = new ResponseError(); responseError.Code = HttpStatusCode.BadRequest.ToString(); responseError.Message = errorMessage; integrationResponse = GenerateResponse(item.Id, HttpStatusCode.BadRequest, null, responseError); shiftsIntegResponses.Add(integrationResponse); } return(shiftsIntegResponses); }
/// <summary> /// Returns a response if a given object is null. /// </summary> /// <typeparam name="T">The type of the object.</typeparam> /// <param name="potentialNullObject">The object.</param> /// <param name="id">The id for the response.</param> /// <param name="error">The error message for the response.</param> /// <param name="response">The output of the response.</param> /// <returns><see cref="bool"/> to denote whether it passed or failed.</returns> public static bool ErrorIfNull <T>( this T potentialNullObject, string id, string error, out ShiftsIntegResponse response) { response = null; if (potentialNullObject == null) { response = CreateBadResponse(id, error: error); return(true); } return(false); }
public async Task <ShiftsIntegResponse> SubmitOpenShiftRequestToKronosAsync( Models.IntegrationAPI.OpenShiftRequestIS request, string teamsId) { ShiftsIntegResponse openShiftSubmitResponse; this.telemetryClient.TrackTrace($"{Resource.SubmitOpenShiftRequestToKronosAsync} starts at: {DateTime.Now.ToString("O", CultureInfo.InvariantCulture)}"); if (request is null) { throw new ArgumentNullException(nameof(request)); } GraphOpenShift graphOpenShift; var telemetryProps = new Dictionary <string, string>() { { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, { "CallingMethod", "UpdateTeam" }, { "OpenShiftId", request.OpenShiftId }, { "OpenShiftRequestId", request.Id }, { "RequesterId", request.SenderUserId }, }; // Prereq. Steps // Step 1 - Obtain the necessary prerequisites required. // Step 1a - Obtain the ConfigurationInfo entity. // Step 1b - Obtain the Team-Department Mapping. // Step 1c - Obtain the Graph Token and other prerequisite information. // Step 1d - Obtain the user from the user to user mapping table. // Step 1e - Login to Kronos. // Step 1c. var allRequiredConfigurations = await this.utility.GetAllConfigurationsAsync().ConfigureAwait(false); var kronosTimeZoneId = this.appSettings.KronosTimeZone; var kronosTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(kronosTimeZoneId); if (allRequiredConfigurations != null && (bool)allRequiredConfigurations?.IsAllSetUpExists) { // Step 1d. var userMappingRecord = await this.GetMappedUserDetailsAsync( allRequiredConfigurations.WFIId, request?.SenderUserId, teamsId).ConfigureAwait(false); // Submit open shift request to Kronos if user and it's corresponding team is mapped correctly. if (string.IsNullOrEmpty(userMappingRecord.Error)) { var queryingOrgJobPath = userMappingRecord.OrgJobPath; var teamDepartmentMapping = await this.teamDepartmentMappingProvider.GetTeamMappingForOrgJobPathAsync( allRequiredConfigurations.WFIId, queryingOrgJobPath).ConfigureAwait(false); telemetryProps.Add("TenantId", allRequiredConfigurations.TenantId); telemetryProps.Add("WorkforceIntegrationId", allRequiredConfigurations.WFIId); // Step 2 - Getting the Open Shift - the start date/time and end date/time are needed. var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI"); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", allRequiredConfigurations.ShiftsAccessToken); using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "teams/" + teamDepartmentMapping.TeamId + "/schedule/openShifts/" + request.OpenShiftId)) { var response = await httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); graphOpenShift = JsonConvert.DeserializeObject <GraphOpenShift>(responseContent); // Logging the required Open Shift ID from the Graph call. this.telemetryClient.TrackTrace($"OpenShiftRequestController - OpenShift Graph API call succeeded with getting the Open Shift: {graphOpenShift?.Id}"); var shiftStartDate = graphOpenShift.SharedOpenShift.StartDateTime.AddDays( -Convert.ToInt16(this.appSettings.CorrectedDateSpanForOutboundCalls, CultureInfo.InvariantCulture)) .ToString(this.appSettings.KronosQueryDateSpanFormat, CultureInfo.InvariantCulture); var shiftEndDate = graphOpenShift.SharedOpenShift.EndDateTime.AddDays( Convert.ToInt16(this.appSettings.CorrectedDateSpanForOutboundCalls, CultureInfo.InvariantCulture)) .ToString(this.appSettings.KronosQueryDateSpanFormat, CultureInfo.InvariantCulture); var openShiftReqQueryDateSpan = $"{shiftStartDate}-{shiftEndDate}"; // Builds out the Open Shift Segments prior to actual object being built. // Take into account the activities of the open shift that has been retrieved // from the Graph API call for open shift details. var openShiftSegments = this.BuildKronosOpenShiftSegments( graphOpenShift.SharedOpenShift.Activities, queryingOrgJobPath, kronosTimeZoneInfo, graphOpenShift.Id); // Step 3 - Create the necessary OpenShiftObj // Having the open shift segments which have been retrieved. var inputDraftOpenShiftRequest = new OpenShiftObj() { StartDayNumber = Constants.StartDayNumberString, EndDayNumber = Constants.EndDayNumberString, SegmentTypeName = Resource.DraftOpenShiftRequestSegmentTypeName, StartTime = TimeZoneInfo.ConvertTime(graphOpenShift.SharedOpenShift.StartDateTime, kronosTimeZoneInfo).ToString("h:mm tt", CultureInfo.InvariantCulture), EndTime = TimeZoneInfo.ConvertTime(graphOpenShift.SharedOpenShift.EndDateTime, kronosTimeZoneInfo).ToString("h:mm tt", CultureInfo.InvariantCulture), ShiftDate = TimeZoneInfo.ConvertTime(graphOpenShift.SharedOpenShift.StartDateTime, kronosTimeZoneInfo).ToString(Constants.DateFormat, CultureInfo.InvariantCulture).Replace('-', '/'), OrgJobPath = Utility.OrgJobPathKronosConversion(userMappingRecord?.OrgJobPath), QueryDateSpan = openShiftReqQueryDateSpan, PersonNumber = userMappingRecord?.KronosPersonNumber, OpenShiftSegments = openShiftSegments, }; // Step 4 - Submit over to Kronos WFC, Open Shift Request goes into DRAFT state. var postDraftOpenShiftRequestResult = await this.openShiftActivity.PostDraftOpenShiftRequestAsync( allRequiredConfigurations.TenantId, allRequiredConfigurations.KronosSession, inputDraftOpenShiftRequest, new Uri(allRequiredConfigurations.WfmEndPoint)).ConfigureAwait(false); if (postDraftOpenShiftRequestResult?.Status == ApiConstants.Success) { this.telemetryClient.TrackTrace($"{Resource.SubmitOpenShiftRequestToKronosAsync} - Operation to submit the DRAFT request has succeeded with: {postDraftOpenShiftRequestResult?.Status}"); // Step 5 - Update the submitted Open Shift Request from DRAFT to SUBMITTED // so that it renders inside of the Kronos WFC Request Manager for final approval/decline. var postUpdateOpenShiftRequestStatusResult = await this.openShiftActivity.PostOpenShiftRequestStatusUpdateAsync( userMappingRecord.KronosPersonNumber, postDraftOpenShiftRequestResult.EmployeeRequestMgmt?.RequestItems?.EmployeeGlobalOpenShiftRequestItem?.Id, openShiftReqQueryDateSpan, Resource.KronosOpenShiftStatusUpdateToSubmittedMessage, new Uri(allRequiredConfigurations.WfmEndPoint), allRequiredConfigurations.KronosSession).ConfigureAwait(false); if (postUpdateOpenShiftRequestStatusResult?.Status == ApiConstants.Success) { this.telemetryClient.TrackTrace($"{Resource.SubmitOpenShiftRequestToKronosAsync} - Operation to update the DRAFT request to SUBMITTED has succeeded with: {postUpdateOpenShiftRequestStatusResult?.Status}"); var openShiftEntityWithKronosUniqueId = await this.openShiftMappingEntityProvider.GetOpenShiftMappingEntitiesAsync( request.OpenShiftId).ConfigureAwait(false); // Step 6 - Insert the submitted open shift request into Azure table storage. // Ensuring to pass the monthwise partition key from the Open Shift as the partition key for the Open Shift // Request mapping entity. var openShiftRequestMappingEntity = CreateOpenShiftRequestMapping( request?.OpenShiftId, request?.Id, request?.SenderUserId, userMappingRecord?.KronosPersonNumber, postDraftOpenShiftRequestResult.EmployeeRequestMgmt?.RequestItems?.EmployeeGlobalOpenShiftRequestItem?.Id, ApiConstants.Submitted, openShiftEntityWithKronosUniqueId.FirstOrDefault().RowKey, openShiftEntityWithKronosUniqueId.FirstOrDefault().PartitionKey, ApiConstants.Pending); telemetryProps.Add( "KronosRequestId", postDraftOpenShiftRequestResult.EmployeeRequestMgmt?.RequestItems?.EmployeeGlobalOpenShiftRequestItem?.Id); telemetryProps.Add( "KronosRequestStatus", postUpdateOpenShiftRequestStatusResult.EmployeeRequestMgmt?.RequestItems?.EmployeeGlobalOpenShiftRequestItem?.StatusName); telemetryProps.Add("KronosOrgJobPath", Utility.OrgJobPathKronosConversion(userMappingRecord?.OrgJobPath)); await this.openShiftRequestMappingEntityProvider.SaveOrUpdateOpenShiftRequestMappingEntityAsync(openShiftRequestMappingEntity).ConfigureAwait(false); this.telemetryClient.TrackTrace(Resource.SubmitOpenShiftRequestToKronosAsync, telemetryProps); openShiftSubmitResponse = new ShiftsIntegResponse() { Id = request.Id, Status = StatusCodes.Status200OK, Body = new Body { Error = null, ETag = GenerateNewGuid(), }, }; } else { this.telemetryClient.TrackTrace(Resource.SubmitOpenShiftRequestToKronosAsync, telemetryProps); openShiftSubmitResponse = new ShiftsIntegResponse() { Id = request.Id, Status = StatusCodes.Status500InternalServerError, Body = new Body { Error = new ResponseError { Code = Resource.KronosWFCOpenShiftRequestErrorCode, Message = postUpdateOpenShiftRequestStatusResult?.Status, }, }, }; } } else { this.telemetryClient.TrackTrace($"{Resource.SubmitOpenShiftRequestToKronosAsync} - There was an error from Kronos WFC when updating the DRAFT request to SUBMITTED status: {postDraftOpenShiftRequestResult?.Status}"); openShiftSubmitResponse = new ShiftsIntegResponse() { Id = request.Id, Status = StatusCodes.Status500InternalServerError, Body = new Body { Error = new ResponseError { Code = Resource.KronosWFCOpenShiftRequestErrorCode, Message = postDraftOpenShiftRequestResult?.Status, }, }, }; } } else { this.telemetryClient.TrackTrace($"{Resource.SubmitOpenShiftRequestToKronosAsync} - There is an error when getting Open Shift: {request?.OpenShiftId} from Graph APIs: {response.StatusCode.ToString()}"); openShiftSubmitResponse = new ShiftsIntegResponse { Id = request.Id, Status = StatusCodes.Status404NotFound, Body = new Body { Error = new ResponseError() { Code = Resource.OpenShiftNotFoundCode, Message = string.Format(CultureInfo.InvariantCulture, Resource.OpenShiftNotFoundMessage, request.OpenShiftId), }, }, }; } } } // Either user or it's team is not mapped correctly. else { openShiftSubmitResponse = new ShiftsIntegResponse { Id = request.Id, Status = StatusCodes.Status500InternalServerError, Body = new Body { Error = new ResponseError { Code = userMappingRecord.Error, Message = userMappingRecord.Error, }, }, }; } } else { this.telemetryClient.TrackTrace(Resource.SubmitOpenShiftRequestToKronosAsync + "-" + Resource.SetUpNotDoneMessage); openShiftSubmitResponse = new ShiftsIntegResponse { Id = request.Id, Status = StatusCodes.Status500InternalServerError, Body = new Body { Error = new ResponseError { Code = Resource.SetUpNotDoneCode, Message = Resource.SetUpNotDoneMessage, }, }, }; } this.telemetryClient.TrackTrace($"{Resource.SubmitOpenShiftRequestToKronosAsync} ends at: {DateTime.Now.ToString("O", CultureInfo.InvariantCulture)}"); return(openShiftSubmitResponse); }
/// <summary> /// This method further processes the Swap Shift request approval. /// </summary> /// <param name="jsonModel">The decryped JSON payload from Shifts/MS Graph.</param> /// <param name="aadGroupId">The team ID for which the Swap Shift request has been approved.</param> /// <returns>A unit of execution that contains the type of <see cref="ShiftsIntegResponse"/>.</returns> private async Task <List <ShiftsIntegResponse> > ProcessSwapShiftRequestApprovalAsync(RequestModel jsonModel, string aadGroupId) { List <ShiftsIntegResponse> swapShiftsIntegResponses = new List <ShiftsIntegResponse>(); ShiftsIntegResponse integrationResponse = null; var swapShiftApprovalRes = from requests in jsonModel.Requests group requests by requests.Url; var swapRequests = jsonModel.Requests.Where(c => c.Url.Contains("/swapRequests/", StringComparison.InvariantCulture)); // Filter all the system declined requests. var autoDeclinedRequests = swapRequests.Where(c => c.Body != null && c.Body["state"].Value <string>() == ApiConstants.Declined && c.Body["assignedTo"].Value <string>() == ApiConstants.System).ToList(); // Filter approved swap shift request. var approvedSwapShiftRequest = swapRequests.Where(c => c.Body != null && c.Body["state"].Value <string>() == ApiConstants.ShiftsApproved && c.Body["assignedTo"].Value <string>() == ApiConstants.ShiftsManager).FirstOrDefault(); var swapShiftRequest = JsonConvert.DeserializeObject <SwapRequest>(approvedSwapShiftRequest.Body.ToString()); var postedShifts = jsonModel.Requests.Where(x => x.Url.Contains("/shifts/", StringComparison.InvariantCulture) && x.Method == "POST").ToList(); var deletedShifts = jsonModel.Requests.Where(x => x.Url.Contains("/shifts/", StringComparison.InvariantCulture) && x.Method == "DELETE").ToList(); if (swapShiftRequest != null) { var newShiftFirst = JsonConvert.DeserializeObject <Shift>(postedShifts.First().Body.ToString()); var newShiftSecond = JsonConvert.DeserializeObject <Shift>(postedShifts.Last().Body.ToString()); // Step 1 - Create the Kronos Unique ID. var kronosUniqueIdFirst = this.utility.CreateUniqueId(newShiftFirst); var kronosUniqueIdSecond = this.utility.CreateUniqueId(newShiftSecond); try { var userMappingRecord = await this.userMappingProvider.GetUserMappingEntityAsyncNew( newShiftFirst?.UserId, aadGroupId).ConfigureAwait(false); // When getting the month partition key, make sure to take into account the Kronos Time Zone as well var provider = CultureInfo.InvariantCulture; var actualStartDateTimeStr = this.utility.CalculateStartDateTime( newShiftFirst.SharedShift.StartDateTime.Date).ToString("M/dd/yyyy", provider); var actualEndDateTimeStr = this.utility.CalculateEndDateTime( newShiftFirst.SharedShift.EndDateTime.Date).ToString("M/dd/yyyy", provider); // Create the month partition key based on the finalShift object. var monthPartitions = Common.Utility.GetMonthPartition(actualStartDateTimeStr, actualEndDateTimeStr); var monthPartition = monthPartitions?.FirstOrDefault(); // Create the shift mapping entity based on the finalShift object also. var shiftEntity = this.utility.CreateShiftMappingEntity(newShiftFirst, userMappingRecord, kronosUniqueIdFirst); await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync( shiftEntity, newShiftFirst.Id, monthPartition).ConfigureAwait(false); var userMappingRecordSec = await this.userMappingProvider.GetUserMappingEntityAsyncNew( newShiftSecond?.UserId, aadGroupId).ConfigureAwait(false); integrationResponse = GenerateResponse(newShiftFirst.Id, HttpStatusCode.OK, null, null); swapShiftsIntegResponses.Add(integrationResponse); // When getting the month partition key, make sure to take into account the Kronos Time Zone as well var actualStartDateTimeStrSec = this.utility.CalculateStartDateTime( newShiftSecond.SharedShift.StartDateTime).ToString("M/dd/yyyy", provider); var actualEndDateTimeStrSec = this.utility.CalculateEndDateTime( newShiftSecond.SharedShift.EndDateTime).ToString("M/dd/yyyy", provider); // Create the month partition key based on the finalShift object. var monthPartitionsSec = Common.Utility.GetMonthPartition(actualStartDateTimeStrSec, actualEndDateTimeStrSec); var monthPartitionSec = monthPartitionsSec?.FirstOrDefault(); // Create the shift mapping entity based on the finalShift object also. var shiftEntitySec = this.utility.CreateShiftMappingEntity(newShiftSecond, userMappingRecordSec, kronosUniqueIdSecond); await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync( shiftEntitySec, newShiftSecond.Id, monthPartitionSec).ConfigureAwait(false); integrationResponse = GenerateResponse(newShiftSecond.Id, HttpStatusCode.OK, null, null); swapShiftsIntegResponses.Add(integrationResponse); foreach (var delShifts in deletedShifts) { integrationResponse = GenerateResponse(delShifts.Id, HttpStatusCode.OK, null, null); swapShiftsIntegResponses.Add(integrationResponse); } integrationResponse = GenerateResponse(approvedSwapShiftRequest.Id, HttpStatusCode.OK, swapShiftRequest.ETag, null); swapShiftsIntegResponses.Add(integrationResponse); foreach (var declinedRequest in autoDeclinedRequests) { this.telemetryClient.TrackTrace($"SystemDeclinedOpenShiftRequestId: {declinedRequest.Id}"); var declinedSwapShiftRequest = JsonConvert.DeserializeObject <SwapRequest>(declinedRequest.Body.ToString()); // Get the requests from storage. var entityToUpdate = await this.swapShiftMappingEntityProvider.GetKronosReqAsync( declinedRequest.Id).ConfigureAwait(false); entityToUpdate.KronosStatus = declinedSwapShiftRequest.State; entityToUpdate.ShiftsStatus = declinedSwapShiftRequest.State; // Commit the change to the database. await this.swapShiftMappingEntityProvider.AddOrUpdateSwapShiftMappingAsync(entityToUpdate).ConfigureAwait(false); this.telemetryClient.TrackTrace($"OpenShiftRequestId: {declinedSwapShiftRequest.Id}, assigned to: {declinedSwapShiftRequest.AssignedTo}, state: {declinedSwapShiftRequest.State}"); // Adding response for system declined open shift request. integrationResponse = GenerateResponse(declinedSwapShiftRequest.Id, HttpStatusCode.OK, declinedSwapShiftRequest.ETag, null); swapShiftsIntegResponses.Add(integrationResponse); } } catch (Exception ex) { var exceptionProps = new Dictionary <string, string>() { { "NewFirstShiftId", newShiftFirst.Id }, { "NewSecondShiftId", newShiftSecond.Id }, }; this.telemetryClient.TrackException(ex, exceptionProps); throw; } } return(swapShiftsIntegResponses); }
/// <summary> /// This method processes the open shift request approval, and proceeds to update the Azure table storage accordingly with the Shifts status /// of the open shift request, and also ensures that the ShiftMappingEntity table is properly in sync. /// </summary> /// <param name="jsonModel">The decrypted JSON payload.</param> /// <param name="updateProps">A dictionary of string, string that will be logged to ApplicationInsights.</param> /// <returns>A unit of execution.</returns> private async Task <List <ShiftsIntegResponse> > ProcessOpenShiftRequestApprovalAsync(RequestModel jsonModel, Dictionary <string, string> updateProps) { List <ShiftsIntegResponse> responseModelList = new List <ShiftsIntegResponse>(); ShiftsIntegResponse integrationResponse = null; var openShiftRequests = jsonModel?.Requests?.Where(x => x.Url.Contains("/openshiftrequests/", StringComparison.InvariantCulture)); var finalOpenShiftObj = jsonModel?.Requests?.FirstOrDefault(x => x.Url.Contains("/openshifts/", StringComparison.InvariantCulture)); var finalShiftObj = jsonModel?.Requests?.FirstOrDefault(x => x.Url.Contains("/shifts/", StringComparison.InvariantCulture)); // Filter all the system declined requests. var autoDeclinedRequests = openShiftRequests.Where(c => c.Body != null && c.Body["state"].Value <string>() == ApiConstants.Declined && c.Body["assignedTo"].Value <string>() == ApiConstants.System).ToList(); // Filter approved open shift request. var approvedOpenShiftRequest = openShiftRequests.Where(c => c.Body != null && c.Body["state"].Value <string>() == ApiConstants.ShiftsApproved && c.Body["assignedTo"].Value <string>() == ApiConstants.ShiftsManager).FirstOrDefault(); var finalShift = JsonConvert.DeserializeObject <Shift>(finalShiftObj.Body.ToString()); var finalOpenShiftRequest = JsonConvert.DeserializeObject <OpenShiftRequestIS>(approvedOpenShiftRequest.Body.ToString()); var finalOpenShift = JsonConvert.DeserializeObject <OpenShiftIS>(finalOpenShiftObj.Body.ToString()); updateProps.Add("NewShiftId", finalShift.Id); updateProps.Add("GraphOpenShiftRequestId", finalOpenShiftRequest.Id); updateProps.Add("GraphOpenShiftId", finalOpenShift.Id); // Step 1 - Create the Kronos Unique ID. var kronosUniqueId = this.utility.CreateUniqueId(finalShift); this.telemetryClient.TrackTrace("KronosHash-OpenShiftRequestApproval-TeamsController: " + kronosUniqueId); try { this.telemetryClient.TrackTrace("Updating entities-OpenShiftRequestApproval started: " + DateTime.Now.ToString(CultureInfo.InvariantCulture)); // Step 1 - Get the temp shift record first by table scan against RowKey. var tempShiftRowKey = $"SHFT_PENDING_{finalOpenShiftRequest.Id}"; var tempShiftEntity = await this.shiftMappingEntityProvider.GetShiftMappingEntityByRowKeyAsync(tempShiftRowKey).ConfigureAwait(false); // We need to check if the tempShift is not null because in the Open Shift Request controller, the tempShift was created // as part of the Graph API call to approve the Open Shift Request. if (tempShiftEntity != null) { // Step 2 - Form the new shift record. var shiftToInsert = new TeamsShiftMappingEntity() { RowKey = finalShift.Id, KronosPersonNumber = tempShiftEntity.KronosPersonNumber, KronosUniqueId = tempShiftEntity.KronosUniqueId, PartitionKey = tempShiftEntity.PartitionKey, AadUserId = tempShiftEntity.AadUserId, ShiftStartDate = this.utility.UTCToKronosTimeZone(finalShift.SharedShift.StartDateTime), }; // Step 3 - Save the new shift record. await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync(shiftToInsert, shiftToInsert.RowKey, shiftToInsert.PartitionKey).ConfigureAwait(false); // Step 4 - Delete the temp shift record. await this.shiftMappingEntityProvider.DeleteOrphanDataFromShiftMappingAsync(tempShiftEntity).ConfigureAwait(false); // Adding response for create new shift. integrationResponse = GenerateResponse(finalShift.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } else { // We are logging to ApplicationInsights that the tempShift entity could not be found. this.telemetryClient.TrackTrace(string.Format(CultureInfo.InvariantCulture, Resource.EntityNotFoundWithRowKey, tempShiftRowKey)); } // Logging to ApplicationInsights the OpenShiftRequestId. this.telemetryClient.TrackTrace("OpenShiftRequestId = " + finalOpenShiftRequest.Id); // Find the open shift request for which we update the ShiftsStatus to Approved. var openShiftRequestEntityToUpdate = await this.openShiftRequestMappingEntityProvider.GetOpenShiftRequestMappingEntityByOpenShiftIdAsync( finalOpenShift.Id, finalOpenShiftRequest.Id).ConfigureAwait(false); openShiftRequestEntityToUpdate.ShiftsStatus = finalOpenShiftRequest.State; // Update the open shift request to Approved in the ShiftStatus column. await this.openShiftRequestMappingEntityProvider.SaveOrUpdateOpenShiftRequestMappingEntityAsync(openShiftRequestEntityToUpdate).ConfigureAwait(false); // Delete the open shift entity accordingly from the OpenShiftEntityMapping table in Azure Table storage as the open shift request has been approved. await this.openShiftMappingEntityProvider.DeleteOrphanDataFromOpenShiftMappingByOpenShiftIdAsync(finalOpenShift.Id).ConfigureAwait(false); // Adding response for delete open shift. integrationResponse = GenerateResponse(finalOpenShift.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); // Adding response for approved open shift request. integrationResponse = GenerateResponse(finalOpenShiftRequest.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); foreach (var declinedRequest in autoDeclinedRequests) { this.telemetryClient.TrackTrace($"SystemDeclinedOpenShiftRequestId: {declinedRequest.Id}"); var declinedOpenShiftRequest = JsonConvert.DeserializeObject <OpenShiftRequestIS>(declinedRequest.Body.ToString()); // Update the status in Azure table storage. var entityToUpdate = await this.openShiftRequestMappingEntityProvider.GetOpenShiftRequestMappingEntityByOpenShiftRequestIdAsync( declinedRequest.Id).ConfigureAwait(false); entityToUpdate.KronosStatus = declinedOpenShiftRequest.State; entityToUpdate.ShiftsStatus = declinedOpenShiftRequest.State; // Commit the change to the database. await this.openShiftRequestMappingEntityProvider.SaveOrUpdateOpenShiftRequestMappingEntityAsync(entityToUpdate).ConfigureAwait(false); this.telemetryClient.TrackTrace($"OpenShiftRequestId: {declinedOpenShiftRequest.Id}, assigned to: {declinedOpenShiftRequest.AssignedTo}, state: {declinedOpenShiftRequest.State}"); // Adding response for system declined open shift request. integrationResponse = GenerateResponse(declinedOpenShiftRequest.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } this.telemetryClient.TrackTrace("Updating entities-OpenShiftRequestApproval complete: " + DateTime.Now.ToString(CultureInfo.InvariantCulture)); } catch (Exception ex) { if (ex.InnerException != null) { this.telemetryClient.TrackTrace($"Shift mapping has failed for {finalOpenShiftRequest.Id}: " + ex.InnerException.ToString()); } this.telemetryClient.TrackTrace($"Shift mapping has resulted in some type of error with the following: {ex.StackTrace.ToString(CultureInfo.InvariantCulture)}"); throw; } return(responseModelList); }
/// <summary> /// Process swap shift requests outbound calls. /// </summary> /// <param name="jsonModel">Incoming payload for the request been made in Shifts.</param> /// <param name="aadGroupId">AAD Group id.</param> /// <returns>Returns list of ShiftIntegResponse for request.</returns> private async Task <List <ShiftsIntegResponse> > ProcessSwapShiftRequest(RequestModel jsonModel, string aadGroupId, bool isRequestFromCorrectIntegration) { List <ShiftsIntegResponse> responseModelList = new List <ShiftsIntegResponse>(); var requestBody = jsonModel.Requests.First(x => x.Url.Contains("/swapRequests/", StringComparison.InvariantCulture)).Body; ShiftsIntegResponse integrationResponseSwap = null; try { // If the requestBody is not null - represents either a new Swap Shift request being created by FLW1, // FLW2 accepts or declines FLW1's request, or FLM approves or declines the Swap Shift request. // If the requestBody is null - represents FLW1's cancellation of the Swap Shift Request. if (requestBody != null) { var requestState = requestBody?["state"].Value <string>(); var requestAssignedTo = requestBody?["assignedTo"].Value <string>(); var swapRequest = JsonConvert.DeserializeObject <SwapRequest>(requestBody.ToString()); // FLW1 has requested for swap shift, submit the request in Kronos. if (requestState == ApiConstants.ShiftsPending && requestAssignedTo == ApiConstants.ShiftsRecipient) { integrationResponseSwap = await this.swapShiftController.SubmitSwapShiftRequestToKronosAsync(swapRequest, aadGroupId).ConfigureAwait(false); responseModelList.Add(integrationResponseSwap); } // FLW2 has approved the swap shift, updates the status in Kronos to submitted and request goes to manager for approval. else if (requestState == ApiConstants.ShiftsPending && requestAssignedTo == ApiConstants.ShiftsManager) { integrationResponseSwap = await this.swapShiftController.ApproveOrDeclineSwapShiftRequestToKronosAsync(swapRequest, aadGroupId).ConfigureAwait(false); responseModelList.Add(integrationResponseSwap); } // FLW2 has declined the swap shift, updates the status in Kronos to refused. else if (requestState == ApiConstants.Declined && requestAssignedTo == ApiConstants.ShiftsRecipient) { integrationResponseSwap = await this.swapShiftController.ApproveOrDeclineSwapShiftRequestToKronosAsync(swapRequest, aadGroupId).ConfigureAwait(false); responseModelList.Add(integrationResponseSwap); } // Manager has declined the request in Kronos, which declines the request in Shifts also. else if (requestState == ApiConstants.ShiftsDeclined && requestAssignedTo == ApiConstants.ShiftsManager) { // The request is coming from intended workforce integration. if (isRequestFromCorrectIntegration) { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for SwapShiftRequest decline outbound call."); integrationResponseSwap = GenerateResponse(swapRequest.Id, HttpStatusCode.OK, swapRequest.ETag, null); responseModelList.Add(integrationResponseSwap); } // Request is coming from either Shifts UI or from incorrect workforce integration. else { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for SwapShiftRequest decline outbound call."); responseModelList = GenerateResponseToPreventAction(jsonModel, Resource.InvalidApproval); } } // Manager has approved the request in Kronos. else if (requestState == ApiConstants.ShiftsApproved && requestAssignedTo == ApiConstants.ShiftsManager) { // The request is coming from intended workforce integration. if (isRequestFromCorrectIntegration) { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for SwapShiftRequest approval outbound call."); responseModelList = await this.ProcessSwapShiftRequestApprovalAsync(jsonModel, aadGroupId).ConfigureAwait(false); } // Request is coming from either Shifts UI or from incorrect workforce integration. else { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for SwapShiftRequest approval outbound call."); responseModelList = GenerateResponseToPreventAction(jsonModel, Resource.InvalidApproval); } } // There is a System decline with the Swap Shift Request else if (requestState == ApiConstants.Declined && requestAssignedTo == ApiConstants.System) { var systemDeclineSwapReqId = jsonModel.Requests.First(x => x.Url.Contains("/swapRequests/", StringComparison.InvariantCulture)).Id; ResponseError responseError = new ResponseError { Message = Resource.SystemDeclined, }; integrationResponseSwap = GenerateResponse(systemDeclineSwapReqId, HttpStatusCode.OK, null, responseError); responseModelList.Add(integrationResponseSwap); } } else if (jsonModel.Requests.Any(c => c.Method == "DELETE")) { // Code below handles the delete swap shift request. var deleteSwapRequestId = jsonModel.Requests.First(x => x.Url.Contains("/swapRequests/", StringComparison.InvariantCulture)).Id; // Logging to telemetry the incoming cancelled request by FLW1. this.telemetryClient.TrackTrace($"The Swap Shift Request: {deleteSwapRequestId} has been declined by FLW1."); var entityToCancel = await this.swapShiftMappingEntityProvider.GetKronosReqAsync(deleteSwapRequestId).ConfigureAwait(false); // Updating the ShiftsStatus to Cancelled. entityToCancel.ShiftsStatus = ApiConstants.SwapShiftCancelled; // Updating the entity accordingly await this.swapShiftMappingEntityProvider.AddOrUpdateSwapShiftMappingAsync(entityToCancel).ConfigureAwait(false); integrationResponseSwap = GenerateResponse(deleteSwapRequestId, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponseSwap); } } catch (Exception) { this.telemetryClient.TrackTrace("Teams Controller swapRequests responseModelList Exception" + JsonConvert.SerializeObject(responseModelList)); throw; } this.telemetryClient.TrackTrace("Teams Controller swapRequests responseModelList" + JsonConvert.SerializeObject(responseModelList)); return(responseModelList); }
/// <summary> /// Process open shift requests outbound calls. /// </summary> /// <param name="jsonModel">Incoming payload for the request been made in Shifts.</param> /// <param name="updateProps">telemetry properties.</param> /// <returns>Returns list of ShiftIntegResponse for request.</returns> private async Task <List <ShiftsIntegResponse> > ProcessOpenShiftRequest(RequestModel jsonModel, Dictionary <string, string> updateProps, string teamsId, bool isRequestFromCorrectIntegration) { List <ShiftsIntegResponse> responseModelList = new List <ShiftsIntegResponse>(); var requestBody = jsonModel.Requests.First(x => x.Url.Contains("/openshiftrequests/", StringComparison.InvariantCulture)).Body; if (requestBody != null) { var requestState = requestBody?["state"].Value <string>(); switch (requestState) { // The Open shift request is submitted in Shifts and is pending with manager for approval. case ApiConstants.ShiftsPending: { var openShiftRequest = JsonConvert.DeserializeObject <OpenShiftRequestIS>(requestBody.ToString()); responseModelList = await this.ProcessOutboundOpenShiftRequestAsync(openShiftRequest, updateProps, teamsId).ConfigureAwait(false); } break; // The Open shift request is approved by manager. case ApiConstants.ShiftsApproved: { // The request is coming from intended workforce integration. if (isRequestFromCorrectIntegration) { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for OpenShiftRequest approval outbound call."); responseModelList = await this.ProcessOpenShiftRequestApprovalAsync(jsonModel, updateProps).ConfigureAwait(false); } // Request is coming from either Shifts UI or from incorrect workforce integration. else { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for OpenShiftRequest approval outbound call."); responseModelList = GenerateResponseToPreventAction(jsonModel, Resource.InvalidApproval); } } break; // The code below would be when there is a decline. case ApiConstants.ShiftsDeclined: { // The request is coming from intended workforce integration. if (isRequestFromCorrectIntegration) { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for OpenShiftRequest decline outbound call."); var integrationResponse = new ShiftsIntegResponse(); foreach (var item in jsonModel.Requests) { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } } // Request is coming from either Shifts UI or from incorrect workforce integration. else { this.telemetryClient.TrackTrace($"Request coming from correct workforce integration is {isRequestFromCorrectIntegration} for OpenShiftRequest decline outbound call."); responseModelList = GenerateResponseToPreventAction(jsonModel, Resource.InvalidApproval); } } break; } } return(responseModelList); }
/// <summary> /// This method further processes the Swap Shift request approval. /// </summary> /// <param name="jsonModel">The decryped JSON payload from Shifts/MS Graph.</param> /// <param name="aadGroupId">The team ID for which the Swap Shift request has been approved.</param> /// <returns>A unit of execution that contains the type of <see cref="ShiftsIntegResponse"/>.</returns> private async Task <List <ShiftsIntegResponse> > ProcessSwapShiftRequestApprovalAsync(RequestModel jsonModel, string aadGroupId) { List <ShiftsIntegResponse> swapShiftsIntegResponses = new List <ShiftsIntegResponse>(); ShiftsIntegResponse integrationResponse = null; var swapShiftApprovalRes = from requests in jsonModel.Requests group requests by requests.Url; var swapShiftRequestObj = swapShiftApprovalRes?.FirstOrDefault(x => x.Key.Contains("/swapRequests/", StringComparison.InvariantCulture))?.First(); var swapShiftRequest = JsonConvert.DeserializeObject <SwapRequest>(swapShiftRequestObj.Body.ToString()); var postedShifts = jsonModel.Requests.Where(x => x.Url.Contains("/shifts/", StringComparison.InvariantCulture) && x.Method == "POST").ToList(); if (swapShiftRequest != null) { var newShiftFirst = JsonConvert.DeserializeObject <Shift>(postedShifts.First().Body.ToString()); var newShiftSecond = JsonConvert.DeserializeObject <Shift>(postedShifts.Last().Body.ToString()); // Step 1 - Create the Kronos Unique ID. var kronosUniqueIdFirst = this.utility.CreateUniqueId(newShiftFirst); var kronosUniqueIdSecond = this.utility.CreateUniqueId(newShiftSecond); try { var userMappingRecord = await this.userMappingProvider.GetUserMappingEntityAsyncNew( newShiftFirst?.UserId, aadGroupId).ConfigureAwait(false); // When getting the month partition key, make sure to take into account the Kronos Time Zone as well var provider = CultureInfo.InvariantCulture; var actualStartDateTimeStr = this.utility.CalculateStartDateTime( newShiftFirst.SharedShift.StartDateTime.Date).ToString("M/dd/yyyy", provider); var actualEndDateTimeStr = this.utility.CalculateEndDateTime( newShiftFirst.SharedShift.EndDateTime.Date).ToString("M/dd/yyyy", provider); // Create the month partition key based on the finalShift object. var monthPartitions = Common.Utility.GetMonthPartition(actualStartDateTimeStr, actualEndDateTimeStr); var monthPartition = monthPartitions?.FirstOrDefault(); // Create the shift mapping entity based on the finalShift object also. var shiftEntity = Common.Utility.CreateShiftMappingEntity(newShiftFirst, userMappingRecord, kronosUniqueIdFirst); await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync( shiftEntity, newShiftFirst.Id, monthPartition).ConfigureAwait(false); var userMappingRecordSec = await this.userMappingProvider.GetUserMappingEntityAsyncNew( newShiftSecond?.UserId, aadGroupId).ConfigureAwait(false); // When getting the month partition key, make sure to take into account the Kronos Time Zone as well var actualStartDateTimeStrSec = this.utility.CalculateStartDateTime( newShiftSecond.SharedShift.StartDateTime).ToString("M/dd/yyyy", provider); var actualEndDateTimeStrSec = this.utility.CalculateEndDateTime( newShiftSecond.SharedShift.EndDateTime).ToString("M/dd/yyyy", provider); // Create the month partition key based on the finalShift object. var monthPartitionsSec = Common.Utility.GetMonthPartition(actualStartDateTimeStrSec, actualEndDateTimeStrSec); var monthPartitionSec = monthPartitionsSec?.FirstOrDefault(); // Create the shift mapping entity based on the finalShift object also. var shiftEntitySec = Common.Utility.CreateShiftMappingEntity(newShiftSecond, userMappingRecordSec, kronosUniqueIdSecond); await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync( shiftEntitySec, newShiftSecond.Id, monthPartitionSec).ConfigureAwait(false); foreach (var item in jsonModel.Requests) { if (item.Url.Contains("/swapRequests/", StringComparison.InvariantCulture)) { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, swapShiftRequest.ETag, null); } else { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, null, null); } swapShiftsIntegResponses.Add(integrationResponse); } } catch (Exception ex) { var exceptionProps = new Dictionary <string, string>() { { "NewFirstShiftId", newShiftFirst.Id }, { "NewSecondShiftId", newShiftSecond.Id }, }; this.telemetryClient.TrackException(ex, exceptionProps); throw; } } return(swapShiftsIntegResponses); }
/// <summary> /// This method processes the open shift request approval, and proceeds to update the Azure table storage accordingly with the Shifts status /// of the open shift request, and also ensures that the ShiftMappingEntity table is properly in sync. /// </summary> /// <param name="jsonModel">The decrypted JSON payload.</param> /// <param name="updateProps">A dictionary of string, string that will be logged to ApplicationInsights.</param> /// <returns>A unit of execution.</returns> private async Task <List <ShiftsIntegResponse> > ProcessOpenShiftRequestApprovalAsync(RequestModel jsonModel, Dictionary <string, string> updateProps) { List <ShiftsIntegResponse> responseModelList = new List <ShiftsIntegResponse>(); ShiftsIntegResponse integrationResponse = null; var finalOpenShiftReqObj = jsonModel?.Requests?.FirstOrDefault(x => x.Url.Contains("/openshiftrequests/", StringComparison.InvariantCulture)); var finalOpenShiftObj = jsonModel?.Requests?.FirstOrDefault(x => x.Url.Contains("/openshifts/", StringComparison.InvariantCulture)); var finalShiftObj = jsonModel?.Requests?.FirstOrDefault(x => x.Url.Contains("/shifts/", StringComparison.InvariantCulture)); var finalShift = JsonConvert.DeserializeObject <Shift>(finalShiftObj.Body.ToString()); var finalOpenShiftRequest = JsonConvert.DeserializeObject <OpenShiftRequestIS>(finalOpenShiftReqObj.Body.ToString()); var finalOpenShift = JsonConvert.DeserializeObject <OpenShiftIS>(finalOpenShiftObj.Body.ToString()); updateProps.Add("NewShiftId", finalShift.Id); updateProps.Add("GraphOpenShiftRequestId", finalOpenShiftRequest.Id); updateProps.Add("GraphOpenShiftId", finalOpenShift.Id); // Step 1 - Create the Kronos Unique ID. var kronosUniqueId = this.utility.CreateUniqueId(finalShift); this.telemetryClient.TrackTrace("KronosHash-OpenShiftRequestApproval-TeamsController: " + kronosUniqueId); try { this.telemetryClient.TrackTrace("Updating entities-OpenShiftRequestApproval started: " + DateTime.Now.ToString(CultureInfo.InvariantCulture)); // Step 1 - Get the temp shift record first by table scan against RowKey. var tempShiftRowKey = $"SHFT_PENDING_{finalOpenShiftRequest.Id}"; var tempShiftEntity = await this.shiftMappingEntityProvider.GetShiftMappingEntityByRowKeyAsync(tempShiftRowKey).ConfigureAwait(false); // We need to check if the tempShift is not null because in the Open Shift Request controller, the tempShift was created // as part of the Graph API call to approve the Open Shift Request. if (tempShiftEntity != null) { // Step 2 - Form the new shift record. var shiftToInsert = new TeamsShiftMappingEntity() { RowKey = finalShift.Id, KronosPersonNumber = tempShiftEntity.KronosPersonNumber, KronosUniqueId = tempShiftEntity.KronosUniqueId, PartitionKey = tempShiftEntity.PartitionKey, AadUserId = tempShiftEntity.AadUserId, }; // Step 3 - Save the new shift record. await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync(shiftToInsert, shiftToInsert.RowKey, shiftToInsert.PartitionKey).ConfigureAwait(false); // Step 4 - Delete the temp shift record. await this.shiftMappingEntityProvider.DeleteOrphanDataFromShiftMappingAsync(tempShiftEntity).ConfigureAwait(false); } else { // We are logging to ApplicationInsights that the tempShift entity could not be found. this.telemetryClient.TrackTrace(string.Format(CultureInfo.InvariantCulture, Resource.EntityNotFoundWithRowKey, tempShiftRowKey)); } // Logging to ApplicationInsights the OpenShiftRequestId. this.telemetryClient.TrackTrace("OpenShiftRequestId = " + finalOpenShiftRequest.Id); // Find the open shift request for which we update the ShiftsStatus to Approved. var openShiftRequestEntityToUpdate = await this.openShiftRequestMappingEntityProvider.GetOpenShiftRequestMappingEntityByOpenShiftIdAsync( finalOpenShift.Id, finalOpenShiftRequest.Id).ConfigureAwait(false); openShiftRequestEntityToUpdate.ShiftsStatus = finalOpenShiftRequest.State; // Update the open shift request to Approved in the ShiftStatus column. await this.openShiftRequestMappingEntityProvider.SaveOrUpdateOpenShiftRequestMappingEntityAsync(openShiftRequestEntityToUpdate).ConfigureAwait(false); // Delete the open shift entity accordingly from the OpenShiftEntityMapping table in Azure Table storage as the open shift request has been approved. await this.openShiftMappingEntityProvider.DeleteOrphanDataFromOpenShiftMappingByOpenShiftIdAsync(finalOpenShift.Id).ConfigureAwait(false); // Sending the acknowledgement for each subrequest from the request payload received from Shifts. foreach (var item in jsonModel.Requests) { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } this.telemetryClient.TrackTrace("Updating entities-OpenShiftRequestApproval complete: " + DateTime.Now.ToString(CultureInfo.InvariantCulture)); } catch (Exception ex) { // Logging when the inner exception is not null - including the open shift request ID. if (ex.InnerException != null) { this.telemetryClient.TrackTrace($"Shift mapping has failed for {finalOpenShiftRequest.Id}: " + ex.InnerException.ToString()); this.telemetryClient.TrackException(ex.InnerException); } // Logging the exception regardless, and making sure to add the open shift request ID as well. this.telemetryClient.TrackTrace($"Shift mapping has resulted in some type of error with the following: {ex.StackTrace.ToString(CultureInfo.InvariantCulture)}, happening with open shift request ID: {finalOpenShiftRequest.Id}"); this.telemetryClient.TrackException(ex); throw; } return(responseModelList); }
/// <summary> /// Process open shift requests outbound calls. /// </summary> /// <param name="jsonModel">Incoming payload for the request been made in Shifts.</param> /// <param name="updateProps">telemetry properties.</param> /// <returns>Returns list of ShiftIntegResponse for request.</returns> private async Task <List <ShiftsIntegResponse> > ProcessOpenShiftRequest(RequestModel jsonModel, Dictionary <string, string> updateProps) { List <ShiftsIntegResponse> responseModelList = new List <ShiftsIntegResponse>(); var requestBody = jsonModel.Requests.First(x => x.Url.Contains("/openshiftrequests/", StringComparison.InvariantCulture)).Body; var requestState = requestBody != null ? requestBody["state"].Value <string>() : null; if (requestBody != null) { switch (requestState) { // The Open shift request is submitted in Shifts and is pending with manager for approval. case ApiConstants.ShiftsPending: { responseModelList = await this.ProcessOutboundOpenShiftRequestAsync(jsonModel, updateProps).ConfigureAwait(false); } break; // The Open shift request is approved by manager. case ApiConstants.ShiftsApproved: { responseModelList = await this.ProcessOpenShiftRequestApprovalAsync(jsonModel, updateProps).ConfigureAwait(false); } break; // The code below would be when there is a decline. There is no need for further // processing, the decline was made on Kronos side. case ApiConstants.ShiftsDeclined: { var integrationResponse = new ShiftsIntegResponse(); foreach (var item in jsonModel.Requests) { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } } break; // The code below handles the system declined request. default: { var integrationResponse = new ShiftsIntegResponse(); foreach (var item in jsonModel.Requests) { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } } break; } } else { // Code below handles the delete open shift request. var integrationResponse = new ShiftsIntegResponse(); foreach (var item in jsonModel.Requests) { integrationResponse = GenerateResponse(item.Id, HttpStatusCode.OK, null, null); responseModelList.Add(integrationResponse); } } return(responseModelList); }