public async Task Run( [TimerTrigger("0 */5 * * * *")] TimerInfo timerInfo, [DurableClient] IDurableOrchestrationClient starter) { if (starter is null) { throw new ArgumentNullException(nameof(starter)); } var allGlobalXSettings = await _mediator.Send(new GlobalXOrgSettingsQuery() { DocumentSyncEnabled = true }); var allExceptions = new List <Exception>(); foreach (var globalXSettings in allGlobalXSettings) { var lastDocumentSyncInstant = Instant.FromDateTimeUtc(globalXSettings.LastDocumentSyncUtc); try { var validator = new GlobalXOrgSettings.Validator(); validator.ValidateAndThrow(globalXSettings); } catch (ValidationException vex) { allExceptions.Add(vex); _logger.LogError(vex, $"Error encountered processing documents for org key'{globalXSettings?.ActionstepOrgKey}'. Settings are invalid."); } try { var documentsQuery = new DocumentsRequest() { UserId = globalXSettings.GlobalXAdminId, After = lastDocumentSyncInstant.WithOffset(Offset.Zero), Statuses = { DocumentStatus.Complete } }; var thisSyncTime = _clock.GetCurrentInstant(); await foreach (var documentWithoutVersions in _globalXService.GetDocuments(documentsQuery)) { if (!documentWithoutVersions.DocumentId.HasValue) { _logger.LogError("Error encountered processing document. Response was missing DocumentId for" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); continue; } Document documentWithVersions; try { documentWithVersions = await _globalXService.GetDocument(documentWithoutVersions.DocumentId.Value, globalXSettings.GlobalXAdminId); } catch (Exception ex) { _logger.LogError(ex, "Error encountered retrieving document version information for DocumentId '{DocumentId}'" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", documentWithoutVersions.DocumentId.Value, globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); continue; } foreach (var documentVersion in documentWithVersions.DocumentVersions) { try { var documentVersionId = documentVersion.DocumentVersionId.ToString(); // Only process "Complete" documents if (documentVersion.StatusDescription != DocumentStatus.Complete.ToString()) { _logger.LogInformation("Skipping document version '{DocumentVersionId}' and statys '{Status}', because it's status is not {RequiredDocumentStatus}.", documentVersionId, documentVersion.StatusDescription, DocumentStatus.Complete.ToString()); continue; } // Only process PDFs if (documentVersion.MimeType != _mimeTypePdf) { _logger.LogInformation("Skipping document version '{DocumentVersionId}' and Mime type '{DocumentMimeType}', as it does not have the mime type {RequiredMimeType}.", documentVersionId, documentVersion.MimeType, _mimeTypePdf); continue; } // Only process document versions updated since the last sync if (documentVersion.Timestamp.Value.ToInstant() < lastDocumentSyncInstant) { _logger.LogInformation("Skipping document version '{DocumentVersionId}' with Timestamp '{Timestamp}', because it's timestamp is before the last sync job time of 'LastDocumentSync' which means that this document version should already have been processed.", documentVersionId, documentVersion.Timestamp.Value, lastDocumentSyncInstant); continue; } var instanceId = GlobalXDocumentSyncOrchestrator.InstancePrefix + documentVersionId; var existingInstance = await starter.GetStatusAsync(instanceId); if (existingInstance is null) { _logger.LogDebug("About to start '{OrchestratorName}' for document version '{DocumentVersionId}'.", nameof(GlobalXDocumentSyncOrchestrator), documentVersionId, documentVersion.Timestamp.Value, lastDocumentSyncInstant); await starter.StartNewAsync( orchestratorFunctionName : nameof(GlobalXDocumentSyncOrchestrator), instanceId : instanceId, input : new CopyDocumentVersionToActionstepCommand() { GlobalXUserId = globalXSettings.GlobalXAdminId, ActionstepUserId = globalXSettings.ActionstepSyncUserId, ActionstepOrgKey = globalXSettings.ActionstepOrgKey, MinimumMatterIdToSync = globalXSettings.MinimumMatterIdToSync, // Use document without versions because we only care about the single version being // processed by this orchestrator. Any remaining versions are unnecessary. Document = documentWithoutVersions, DocumentVersion = documentVersion }); } else { _logger.LogInformation("Orchestration '{OrchestratorName}' for document version '{DocumentVersionId}' is already running, so does not need to be started", nameof(GlobalXDocumentSyncOrchestrator), documentVersionId); } } catch (Exception ex) { allExceptions.Add(ex); _logger.LogError(ex, "Error encountered processing document version '{DocumentVersionId}' for" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", documentVersion?.DocumentVersionId, globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); } } } await _mediator.Send(new SetLastDocumentSyncTimeCommand(globalXSettings.ActionstepOrgKey, thisSyncTime)); } catch (Exception ex) { allExceptions.Add(ex); _logger.LogError(ex, "Error encountered while retrieving document versions for" + " org '{ActionstepOrgKey}', GlobalX Admin ID: '{GlobalXAdminId}'", globalXSettings?.ActionstepOrgKey, globalXSettings?.GlobalXAdminId); } } if (allExceptions.Count > 0) { // If there were any failures, throwing ensures that the TimerJob shows up as failed. throw new AggregateException("One or more failures encountered while processing GlobalX documents.", allExceptions); } }
public async Task Run( [TimerTrigger("0 */5 * * * *")] TimerInfo timerInfo, [DurableClient] IDurableOrchestrationClient starter) { if (starter is null) { throw new ArgumentNullException(nameof(starter)); } var allGlobalXSettings = await _mediator.Send(new GlobalXOrgSettingsQuery() { TransactionSyncEnabled = true }); var allExceptions = new List <Exception>(); foreach (var globalXSettings in allGlobalXSettings) { var latestTransactionId = globalXSettings.LatestTransactionId; try { var validator = new GlobalXOrgSettings.Validator(); validator.ValidateAndThrow(globalXSettings); } catch (ValidationException vex) { allExceptions.Add(vex); _logger.LogError(vex, $"Error encountered processing transactions for org key'{globalXSettings?.ActionstepOrgKey}'. Settings are invalid."); } TransactionsResponse transactionsResponse = null; try { transactionsResponse = await _globalXService.GetTransactions(new TransactionsQuery() { UserId = globalXSettings.GlobalXAdminId, TransId = globalXSettings.LatestTransactionId + 1, UserType = UserType.AllChildren }); } catch (Exception ex) { allExceptions.Add(ex); _logger.LogError(ex, $"Error encountered while retrieving transactions for" + $" org '{globalXSettings?.ActionstepOrgKey}', GlobalX Admin ID: '{globalXSettings?.GlobalXAdminId}'"); } if (!(transactionsResponse is null)) { foreach (var transaction in transactionsResponse.Transactions) { try { var transactionSyncInstanceId = GlobalXTransactionSyncOrchestrator.InstancePrefix + transaction.TransactionId.ToString(CultureInfo.InvariantCulture); var existingInstance = await starter.GetStatusAsync(transactionSyncInstanceId); if (existingInstance is null) { await starter.StartNewAsync( orchestratorFunctionName : nameof(GlobalXTransactionSyncOrchestrator), instanceId : transactionSyncInstanceId, input : new CreateDisbursementsCommand() { ActionstepUserId = globalXSettings.ActionstepSyncUserId, Transaction = transaction, ActionstepOrgKey = globalXSettings.ActionstepOrgKey, MinimumMatterIdToSync = globalXSettings.MinimumMatterIdToSync, TaxCodeIdWithGST = globalXSettings.TaxCodeIdWithGST.Value, TaxCodeIdNoGST = globalXSettings.TaxCodeIdNoGST.Value }); } else { _logger.LogWarning($"Orchestration '{nameof(GlobalXTransactionSyncOrchestrator)}' with id '{transactionSyncInstanceId}' is already running, so does not need to be started"); } if (transaction.TransactionId > latestTransactionId) { latestTransactionId = transaction.TransactionId; } } catch (Exception ex) { allExceptions.Add(ex); _logger.LogError(ex, $"Error encountered processing transaction '{transaction?.TransactionId}' for" + $" org '{globalXSettings?.ActionstepOrgKey}', GlobalX Admin ID: '{globalXSettings?.GlobalXAdminId}'"); } } } if (latestTransactionId > globalXSettings.LatestTransactionId) { await _mediator.Send(new SetLatestGlobalXTransactionIdCommand(globalXSettings.ActionstepOrgKey, latestTransactionId)); } } if (allExceptions.Count > 0) { // If there were any failures, throwing ensures that the TimerJob shows up as failed. throw new AggregateException("One or more failures encountered while processing GlobalX transactions.", allExceptions); } }