/// <summary> /// Returns the document stream for the given file name in specified library. /// </summary> /// <param name="filename">The document to find and stream.</param> /// <param name="libraryName">The folder name of the document location.</param> /// <returns> A stream containing the contents of the document. </returns> public async Task <byte[]> GetDocument(string filename, string libraryName) { _logger.LogInformation($"[{nameof(GetDocument)}] - Attempting to connect to SharePoint location."); string fileRelativeUrl = $"{_spConfig.RelativeSiteURL}/{libraryName}/{filename}"; try { var file = _clientContext.Web.GetFileByServerRelativeUrl(fileRelativeUrl); _clientContext.Load(file); if (file is null) { _logger.LogError($"[{nameof(GetDocument)}] - File not found: {fileRelativeUrl}"); return(HandleFileNotFoundExceptionWithTestPdf(new DocumentNotFoundException($"[{nameof(GetDocument)}] - File not found: {fileRelativeUrl}"))); } ClientResult <Stream> stream = file.OpenBinaryStream(); await _clientContext.ExecuteQueryAsync(); if (stream.Value is null) { _logger.LogError($"[{nameof(GetDocument)}] - File not stream from location: {fileRelativeUrl}"); return(HandleFileNotFoundExceptionWithTestPdf(new DocumentNotFoundException($"[{nameof(GetDocument)}] - File not found: {fileRelativeUrl}"))); } _logger.LogInformation($"[{nameof(GetDocument)}] - File stream location: {fileRelativeUrl} completed."); return(stream.Value.ToByteArray()); } catch (Exception ex) { _logger.LogError(ex, $"[{nameof(GetDocument)}] - The contract pdf file is not accessible. File: {fileRelativeUrl}"); return(HandleFileNotFoundExceptionWithTestPdf(new DocumentNotAccessibleException("The contract pdf file is not accessible.", ex))); } }
/// <inheritdoc/> public async Task <string> AcquireSPTokenAsync() { Uri site = new Uri($"{_spConfig.ApiBaseAddress}{_spConfig.RelativeSiteURL}"); var cleanAccessRequestContent = "grant_type=client_credentials" + $"&resource={_spConfig.Resource}/{site.DnsSafeHost}@{_spConfig.TenantId}" + $"&client_id={_spConfig.ClientId}@{_spConfig.TenantId}"; var body = cleanAccessRequestContent + $"&client_secret={HttpUtility.UrlEncode(_spConfig.ClientSecret)}"; using (var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded")) { var sharepointAadEndpoint = $"{_spConfig.Authority}{_spConfig.TenantId}/tokens/OAuth/2"; _logger.LogInformation($"[{nameof(AcquireSPTokenAsync)}] - Attempting to get token from the SharePoint account access control [{sharepointAadEndpoint}] with access request [{cleanAccessRequestContent}]. For site: {site.AbsoluteUri}"); var result = await _httpClient.PostAsync(sharepointAadEndpoint, stringContent); var resultContent = await result.Content.ReadAsStringAsync(); if (!result.IsSuccessStatusCode) { _logger.LogError($"[{nameof(AcquireSPTokenAsync)}] - Failed to get access token with status code [{result.StatusCode}] and reponse message [{resultContent}]"); throw new SPTokenAcquisitionFailureException($"Access token cannot be acquired for the SharePoint AAD Auth. Failed with error: [{resultContent}]"); } var tokenResult = JsonSerializer.Deserialize <JsonElement>(resultContent); string token = string.Empty; try { token = tokenResult.GetProperty("access_token").GetString(); } catch (KeyNotFoundException ex) { _logger.LogError(ex, $"[{nameof(AcquireSPTokenAsync)}] - Access token cannot be acquired for the SharePoint AAD Auth. Site: {site.AbsoluteUri}"); throw new SPTokenAcquisitionFailureException("Access token cannot be acquired for the SharePoint AAD Auth.", ex); } if (string.IsNullOrEmpty(token)) { throw new SPTokenAcquisitionFailureException($"Access token cannot be acquired from token result [{resultContent}] for the SharePoint AAD Auth."); } _logger.LogInformation($"[{nameof(AcquireSPTokenAsync)}] - Successfully acquired the SharePoint token from the SharePoint account access control. Site: {site.AbsoluteUri}"); return(token); } }
/// <inheritdoc/> public async Task <SessionWorkflowState> ProcessSessionMessageAsync(IMessageSession session, Message message) { var state = await _stateManager.GetWorkflowStateAsync(session); if (message.UserProperties.TryGetValue("ResetWorkflowState", out var shouldReset) && Convert.ToBoolean(shouldReset)) { _logger.LogInformation($"[{nameof(ContractEventSessionManager)}.{nameof(ProcessSessionMessageAsync)}] - Reset workflow state message [{message.MessageId}] received, resetting session [{session.SessionId}]."); return(await _stateManager.ResetWorkflowStateAsync(session)); } if (state.IsFaulted && state.FailedMessageId != message.MessageId) { _logger.LogWarning($"[{nameof(ContractEventSessionManager)}.{nameof(ProcessSessionMessageAsync)}] - Moving this message [{message.MessageId}] to DLQ, beacause previous message [{state.FailedMessageId}] failed, hence dropping all messages in session [{session.SessionId}]."); state.PostponedMessages.Add(message.MessageId); await session.DeadLetterAsync(message.SystemProperties.LockToken, $"Previous message failed, hence dropping all messages in session", $"Previous message {state.FailedMessageId} failed, hence dropping all messages in session {session.SessionId}"); await _stateManager.SetWorkflowStateAsync(session, state); } else { if (state.IsFaulted && state.FailedMessageId == message.MessageId) { state = await _stateManager.ResetWorkflowStateAsync(session); } try { var contractEvent = JsonConvert.DeserializeObject <ContractEvent>(Encoding.UTF8.GetString(message.Body)); _processLog.Initialise(message, contractEvent); await _contractService.ProcessMessage(contractEvent); } catch (NotImplementedException notImplemented) { await SaveFailedState(session, message, state, notImplemented, "Message contains not implemented input"); await session.DeadLetterAsync(message.SystemProperties.LockToken, $"Message contains not imeplemented input", $"Invalid message {state.FailedMessageId} in session {session.SessionId} reason - {notImplemented.Message}."); } catch (ContractEventExpectationFailedException badMessage) { await SaveFailedState(session, message, state, badMessage, "Message contains invalid input"); await session.DeadLetterAsync(message.SystemProperties.LockToken, $"Message contains invalid input", $"Invalid message {state.FailedMessageId} in session {session.SessionId} reason - {badMessage.Message}."); } catch (Exception ex) { if (message.SystemProperties.DeliveryCount >= _functionSettings.MaximumDeliveryCount) { await SaveFailedState(session, message, state, ex, "Message delivery count exceeded"); } throw; } } return(state); }
/// <summary> /// Converts the byte array PDF to be in the PDF\2A_2B format. /// </summary> /// <param name="pdf">The original PDF.</param> /// <returns>The formatted PDF.</returns> public byte[] ConvertToPdfA(byte[] pdf) { _logger.LogInformation("Starting conversion of PDF to PDF_A_2B."); var sw = Stopwatch.StartNew(); using (var inputStream = new MemoryStream(pdf)) { using (var doc = new Document(inputStream)) { doc.ConvertToPdfA(); using (var outputStream = new MemoryStream()) { EnsureLandscapePagesAreMarkedCorrectly(doc); doc.Save(outputStream); _logger.LogInformation($"PDF conversion took {sw.ElapsedMilliseconds}ms to complete."); return(outputStream.ToArray()); } } } }
/// <summary> /// It will create a new contract. /// </summary> /// <param name="contractEvent">New contract Data.</param> /// <returns>Returns true when a new contract created else false.</returns> public async Task <bool> CreateAsync(ContractEvent contractEvent) { _logger.LogInformation($"[{nameof(CreateAsync)}] - Processing message for contract creation. ContractNumber: {contractEvent.ContractNumber}, ContractVersion: {contractEvent.ContractVersion}"); var createRequest = _contractEventMapper.GetCreateRequest(contractEvent); var fileName = _contractEventMapper.GetFileNameForContractDocument(contractEvent.UKPRN, contractEvent.ContractNumber, contractEvent.ContractVersion); var folderName = _contractEventMapper.GetFolderNameForContractDocument(contractEvent.FundingType.GetEnumShortName(), contractEvent.ContractPeriodValue, _spConfig.PublicationFolderSuffix); var urlSafeFolderName = _contractEventMapper.GetUrlSafeFolderNameForContractDocument(folderName); var pdfDoc = await _sharePointClientService.GetDocument(fileName, urlSafeFolderName); var pdfADoc = _documentManagementService.ConvertToPdfA(pdfDoc); createRequest.ContractContent = _contractEventMapper.GetContractContent(pdfADoc, fileName); await _contractsDataService.CreateContractAsync(createRequest); return(true); }
/// <inheritdoc/> public async Task ProcessMessage(ContractEvent contractEvent) { _logger.LogInformation($"[{nameof(ProcessMessage)}] Processing message for contract event : {contractEvent.BookmarkId}"); var eventType = contractEvent.GetContractEventType(); switch (eventType) { case ContractEventType.Create: var existingContract = await _contractsDataService.TryGetContractAsync(contractEvent.ContractNumber, contractEvent.ContractVersion); if (existingContract is null) { await _contractCreationService.CreateAsync(contractEvent); } else { _logger.LogWarning($"[{nameof(ContractEventProcessor)}] - Ignoring contract event with id [{contractEvent.BookmarkId}] because a contract with contract number [{contractEvent.ContractNumber}], version [{contractEvent.ContractVersion}] and Id [{existingContract.Id}] already exists."); } break; case ContractEventType.Approve: var approveContract = await _contractsDataService.TryGetContractAsync(contractEvent.ContractNumber, contractEvent.ContractVersion); if (approveContract is null) { _logger.LogWarning($"[{nameof(ContractEventProcessor)}] - Ignoring contract event with id [{contractEvent.BookmarkId}] because unable to find a contract with contract number [{contractEvent.ContractNumber}], version [{contractEvent.ContractVersion}]."); } else { await _contractApprovalService.ApproveAsync(contractEvent, approveContract); } break; case ContractEventType.Withdraw: await _contractWithdrawService.WithdrawAsync(contractEvent); break; default: throw new NotImplementedException($"[{nameof(ContractService)}] - [{nameof(ProcessMessage)}] does not have an implementation for event type [{eventType}]."); } }
/// <inheritdoc/> public async Task <bool> ApproveAsync(ContractEvent contractEvent, Contract existingContract) { var approvalRequest = new ApprovalRequest() { ContractNumber = existingContract.ContractNumber, ContractVersion = existingContract.ContractVersion, Id = existingContract.Id, FileName = contractEvent.ContractEventXml }; var eventType = contractEvent.GetContractEventType(); if (eventType != Enums.ContractEventType.Approve) { throw new InvalidOperationException($"[{nameof(ContractApprovalService)}] - [{nameof(ApproveAsync)}] called for event type [{eventType}]."); } switch (existingContract.Status) { case Data.Api.Client.Enumerations.ContractStatus.PublishedToProvider: await _contractsDataService.ManualApproveAsync(approvalRequest); break; case Data.Api.Client.Enumerations.ContractStatus.ApprovedWaitingConfirmation: await _contractsDataService.ConfirmApprovalAsync(approvalRequest); break; default: _logger.LogInformation($"[{nameof(ContractApprovalService)}] - [{nameof(ApproveAsync)}] - No further action taken on [{existingContract.ContractNumber}], version [{existingContract.ContractVersion}], Id [{existingContract.Id}], event parent status [{contractEvent.ParentStatus}], event status [{contractEvent.Status}], event amendment type [{contractEvent.AmendmentType}] and contract status [{existingContract.Status}]."); break; } return(true); }