/// <summary> /// Creates a shift mapping entity to be stored in the table. /// </summary> /// <param name="shift">The shift received from Shifts.</param> /// <param name="uniqueId">The unique ID for the shift.</param> /// <param name="kronoUserId">The user id of the user in Kronos.</param> /// <returns>Returns a <see cref="TeamsShiftMappingEntity"/>.</returns> public TeamsShiftMappingEntity CreateNewShiftMappingEntity( Microsoft.Teams.Shifts.Integration.API.Models.IntegrationAPI.Shift shift, string uniqueId, string kronoUserId) { var createNewShiftMappingEntityProps = new Dictionary <string, string>() { { "GraphShiftId", shift?.Id }, { "KronosUniqueId", uniqueId }, { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; var startDateTime = DateTime.SpecifyKind(shift.SharedShift.StartDateTime, DateTimeKind.Utc); var endDateTime = DateTime.SpecifyKind(shift.SharedShift.EndDateTime, DateTimeKind.Utc); TeamsShiftMappingEntity shiftMappingEntity = new TeamsShiftMappingEntity { AadUserId = shift.UserId, KronosUniqueId = uniqueId, KronosPersonNumber = kronoUserId, ShiftStartDate = startDateTime, ShiftEndDate = endDateTime, }; this.telemetryClient.TrackTrace("Creating new shift mapping entity.", createNewShiftMappingEntityProps); return(shiftMappingEntity); }
/// <summary> /// Method that will create a new Shift Mapping entity to store in /// Azure table storage. /// </summary> /// <param name="responseModel">The Shift object response that is received from MS Graph.</param> /// <param name="uniqueId">The Kronos Unique ID that is generated.</param> /// <param name="user">The user for which the new shift is being created.</param> /// <returns>An object of the type <see cref="TeamsShiftMappingEntity"/>.</returns> private TeamsShiftMappingEntity CreateNewShiftMappingEntity( Models.Response.Shifts.Shift responseModel, string uniqueId, UserDetailsModel user) { var createNewShiftMappingEntityProps = new Dictionary<string, string>() { { "GraphShiftId", responseModel.Id }, { "GraphShiftEtag", responseModel.ETag }, { "KronosUniqueId", uniqueId }, { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; TeamsShiftMappingEntity shiftMappingEntity = new TeamsShiftMappingEntity { ETag = responseModel.ETag, AadUserId = responseModel.UserId, KronosUniqueId = uniqueId, KronosPersonNumber = user.KronosPersonNumber, ShiftStartDate = this.utility.UTCToKronosTimeZone(responseModel.SharedShift.StartDateTime), }; this.telemetryClient.TrackTrace(MethodBase.GetCurrentMethod().Name, createNewShiftMappingEntityProps); return shiftMappingEntity; }
/// <summary> /// Method that will create a new Shift Mapping entity to store in /// Azure table storage. /// </summary> /// <param name="responseModel">The Shift object response that is received from MS Graph.</param> /// <param name="uniqueId">The Kronos Unique ID that is generated.</param> /// <param name="user">The user for which the new shift is being created.</param> /// <returns>An object of the type <see cref="TeamsShiftMappingEntity"/>.</returns> private TeamsShiftMappingEntity CreateNewShiftMappingEntity( Models.Response.Shifts.Shift responseModel, string uniqueId, UserDetailsModel user) { var createNewShiftMappingEntityProps = new Dictionary <string, string>() { { "GraphShiftId", responseModel.Id }, { "GraphShiftEtag", responseModel.ETag }, { "KronosUniqueId", uniqueId }, { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; var startDateTime = DateTime.SpecifyKind(responseModel.SharedShift.StartDateTime.DateTime, DateTimeKind.Utc); var endDateTime = DateTime.SpecifyKind(responseModel.SharedShift.EndDateTime.DateTime, DateTimeKind.Utc); TeamsShiftMappingEntity shiftMappingEntity = new TeamsShiftMappingEntity { ETag = responseModel.ETag, AadUserId = responseModel.UserId, KronosUniqueId = uniqueId, KronosPersonNumber = user.KronosPersonNumber, ShiftStartDate = startDateTime, ShiftEndDate = endDateTime, }; this.telemetryClient.TrackTrace("Creating new shift mapping entity.", createNewShiftMappingEntityProps); return(shiftMappingEntity); }
/// <summary> /// Method to delete the orphan data from shifts mapping entity. /// </summary> /// <param name="entity">The mapping entity to be deleted.</param> /// <returns>A unit of execution to say whether or not the delete happened successfully.</returns> public Task DeleteOrphanDataFromShiftMappingAsync(TeamsShiftMappingEntity entity) { if (entity is null) { throw new ArgumentNullException(nameof(entity)); } return(this.DeleteEntityAsync(entity)); }
/// <summary> /// Having the ability to create a new TeamsShiftMappingEntity. /// </summary> /// <param name="aadUserId">AAD user Id associated with the Shift.</param> /// <param name="kronosUniqueId">Kronos Unique Id fro that Shift.</param> /// <param name="kronosPersonNumber">Kronos Person number for the user.</param> /// <returns>Mapping Entity associated with Team and Shift.</returns> public static TeamsShiftMappingEntity CreateShiftMappingEntity( string aadUserId, string kronosUniqueId, string kronosPersonNumber) { TeamsShiftMappingEntity teamsShiftMappingEntity = new TeamsShiftMappingEntity { AadUserId = aadUserId, KronosUniqueId = kronosUniqueId, KronosPersonNumber = kronosPersonNumber, }; return(teamsShiftMappingEntity); }
/// <summary> /// Having the ability to create a new TeamsShiftMappingEntity. /// </summary> /// <param name="shift">The Shift model.</param> /// <param name="userMappingEntity">Details of user from User Mapping Entity table.</param> /// <param name="kronosUniqueId">Kronos Unique Id corresponds to the shift.</param> /// <returns>Mapping Entity associated with Team and Shift.</returns> public static TeamsShiftMappingEntity CreateShiftMappingEntity( Models.IntegrationAPI.Shift shift, AllUserMappingEntity userMappingEntity, string kronosUniqueId) { TeamsShiftMappingEntity teamsShiftMappingEntity = new TeamsShiftMappingEntity { AadUserId = shift?.UserId, KronosUniqueId = kronosUniqueId, KronosPersonNumber = userMappingEntity?.RowKey, }; return(teamsShiftMappingEntity); }
/// <summary> /// Having the ability to create a new TeamsShiftMappingEntity. /// </summary> /// <param name="shift">The Shift model.</param> /// <param name="userMappingEntity">Details of user from User Mapping Entity table.</param> /// <param name="kronosUniqueId">Kronos Unique Id corresponds to the shift.</param> /// <returns>Mapping Entity associated with Team and Shift.</returns> public TeamsShiftMappingEntity CreateShiftMappingEntity( Models.IntegrationAPI.Shift shift, AllUserMappingEntity userMappingEntity, string kronosUniqueId) { TeamsShiftMappingEntity teamsShiftMappingEntity = new TeamsShiftMappingEntity { AadUserId = shift?.UserId, KronosUniqueId = kronosUniqueId, KronosPersonNumber = userMappingEntity?.RowKey, ShiftStartDate = this.UTCToKronosTimeZone(shift.SharedShift.StartDateTime), }; return(teamsShiftMappingEntity); }
private async Task <TableResult> StoreOrUpdateEntityAsync(TeamsShiftMappingEntity entity) { if (entity is null) { throw new ArgumentNullException(nameof(entity)); } await this.EnsureInitializedAsync().ConfigureAwait(false); var storeOrUpdateEntityProps = new Dictionary <string, string>() { { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; this.telemetryClient.TrackTrace(MethodBase.GetCurrentMethod().Name, storeOrUpdateEntityProps); TableOperation addOrUpdateOperation = TableOperation.InsertOrReplace(entity); return(await this.shiftEntityMappingCloudTable.ExecuteAsync(addOrUpdateOperation).ConfigureAwait(false)); }
/// <summary> /// Saves or updates the shift mapping entity. /// </summary> /// <param name="entity">The shift mapping data.</param> /// <param name="shiftId">The ShiftID to save or update.</param> /// <param name="monthPartitionKey">Month Partition Value.</param> /// <returns>A unit of execution.</returns> public Task SaveOrUpdateShiftMappingEntityAsync( TeamsShiftMappingEntity entity, string shiftId, string monthPartitionKey) { if (entity is null) { throw new ArgumentNullException(nameof(entity)); } var saveOrUpdateShiftMappingProps = new Dictionary <string, string>() { { "IncomingShiftsRequestId", shiftId }, { "KronosUniqueId", entity?.KronosUniqueId }, { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; this.telemetryClient.TrackTrace(MethodBase.GetCurrentMethod().Name, saveOrUpdateShiftMappingProps); entity.PartitionKey = monthPartitionKey; entity.RowKey = shiftId; return(this.StoreOrUpdateEntityAsync(entity)); }
/// <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> /// 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); }