private async Task StartWorkflowAsync(SigningRequest req, string[] signerEmails)
        {
            var model = new WorkflowStartModel
            {
                RequestId    = req.Id,
                DocumentName = req.DocumentName,
                Message      = req.Message,
                SignerEmails = signerEmails,
                Subject      = req.Subject
            };
            string json         = JsonSerializer.Serialize(model);
            var    startRequest = new HttpRequestMessage(HttpMethod.Post, _workflowStartUrl)
            {
                Content = new StringContent(json, Encoding.UTF8, "application/json")
            };
            HttpResponseMessage startResponse = await _httpClient.SendAsync(startRequest);

            if (startResponse.StatusCode != System.Net.HttpStatusCode.Accepted)
            {
                throw new Exception("Unexpected start response: " + startResponse.StatusCode);
            }

            // Update info about the started workflow in DB
            // Allows us to query status, terminate it etc.
            DurableFunctionsCheckStatusResponse checkStatusResponse = JsonSerializer.Deserialize <DurableFunctionsCheckStatusResponse>(await startResponse.Content.ReadAsStringAsync());

            req.Workflow.Id              = checkStatusResponse.Id;
            req.Workflow.StatusQueryUrl  = checkStatusResponse.StatusQueryGetUri;
            req.Workflow.SendEventUrl    = checkStatusResponse.SendEventPostUri;
            req.Workflow.TerminateUrl    = checkStatusResponse.TerminatePostUri;
            req.Workflow.PurgeHistoryUrl = checkStatusResponse.PurgeHistoryDeleteUri;
            await _db.SaveChangesAsync();
        }
Example #2
0
        public async Task <HttpResponseMessage> StartSigningWorkflow(
            [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestMessage req,
            [DurableClient] IDurableOrchestrationClient starter,
            ILogger log)
        {
            WorkflowStartModel request = await req.Content.ReadAsAsync <WorkflowStartModel>();

            string instanceId = await starter.StartNewAsync(nameof(MainOrchestrator), request);

            log.LogInformation("Started orchestration with ID '{InstanceId}'.", instanceId);
            return(starter.CreateCheckStatusResponse(req, instanceId));
        }
Example #3
0
        public async Task <SignerResult[]> MainOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            WorkflowStartModel request = context.GetInput <WorkflowStartModel>();

            // Database update, has to be done in an activity
            await context.CallActivityAsync(nameof(MarkWorkflowStarted), request.RequestId);

            // there is also a variant that supports retries

            // Prepare email send tasks
            string[] signerEmails   = request.SignerEmails;
            var      emailSendTasks = new List <Task>(signerEmails.Length);

            for (int i = 0; i < signerEmails.Length; i++)
            {
                Task sendTask = context.CallActivityAsync(nameof(SendPleaseSignEmail), new EmailSendParameters
                {
                    To           = signerEmails[i],
                    Subject      = request.Subject,
                    Message      = request.Message,
                    RequestId    = request.RequestId,
                    DocumentName = request.DocumentName
                });
                emailSendTasks.Add(sendTask);
            }

            // Fan out to send emails, activities triggered in parallel
            await Task.WhenAll(emailSendTasks);

            // Prepare parallel signing tasks
            var signingTasks = new List <Task <SignerResult> >(signerEmails.Length);

            for (int i = 0; i < signerEmails.Length; i++)
            {
                Task <SignerResult> signingTask = context.CallSubOrchestratorAsync <SignerResult>(nameof(WaitForSignature.WaitForSign), new WaitForSignParameters
                {
                    SignerEmail = signerEmails[i],
                    RequestId   = request.RequestId
                });
                signingTasks.Add(signingTask);
            }

            // Wait for result from each signer, another fan out
            SignerResult[] results = await Task.WhenAll(signingTasks);

            // Create signed document if everyone signed
            if (results.All(r => r.Result == SigningDecision.Signed))
            {
                await context.CallActivityAsync(nameof(CreateSignedDocument), new CreateSignedDocumentParameters
                {
                    RequestId = request.RequestId,
                    Results   = results
                });
            }

            // Send completion email to all signers
            var completionEmailSendTasks = new List <Task>(signerEmails.Length);

            for (int i = 0; i < signerEmails.Length; i++)
            {
                Task sendTask = context.CallActivityAsync(nameof(SendCompletionEmail), new SendCompletionEmailParameters
                {
                    RequestId    = request.RequestId,
                    To           = signerEmails[i],
                    DocumentName = request.DocumentName
                });
                completionEmailSendTasks.Add(sendTask);
            }

            // Fan out to send completion emails
            await Task.WhenAll(completionEmailSendTasks);

            // Finally, mark the workflow completed in the DB
            await context.CallActivityAsync(nameof(MarkWorkflowCompleted), request.RequestId);

            return(results);
        }