public static async void Run([BlobTrigger("requests/{name}", Connection = "Blob:StorageConnection")] Stream requestBlob, string name, [OrchestrationClient] DurableOrchestrationClient orchestrationClient, TraceWriter log)
        {
            log.Info($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {requestBlob.Length} Bytes");
            string blobStorageBasePath = Environment.GetEnvironmentVariable("Blob:StorageBasePath", EnvironmentVariableTarget.Process);
            string requestor           = "";
            string subject             = "";

            // If the blob name containes a '+' sign, it identifies the first part of the blob name as the requestor and the remaining as the subject. Otherwise, the requestor is unknown and the subject is the full blobname.
            if (name.Contains("+"))
            {
                requestor = Uri.UnescapeDataString(name.Substring(0, name.LastIndexOf("+")));
                subject   = name.Substring(name.LastIndexOf("+") + 1);
            }
            else
            {
                requestor = "unknown";
                subject   = name;
            }

            ApprovalRequestMetadata requestMetadata = new ApprovalRequestMetadata()
            {
                ApprovalType = "FurryModel",
                ReferenceUrl = $"{blobStorageBasePath}requests/{name}",
                Subject      = subject,
                Requestor    = requestor
            };

            string instanceId = await orchestrationClient.StartNewAsync("OrchestrateRequestApproval", requestMetadata);

            log.Info($"Durable Function Ochestration started: {instanceId}");
        }
예제 #2
0
        public static async Task <string> Run(
            [ActivityTrigger] ApprovalRequestMetadata requestMetadata,
            TraceWriter log)
        {
            string approvalRequestUrl      = Environment.GetEnvironmentVariable("Slack:ApprovalUrl", EnvironmentVariableTarget.Process);
            string approvalMessageTemplate = Environment.GetEnvironmentVariable("Slack:ApprovalMessageTemplate", EnvironmentVariableTarget.Process);
            Uri    uri             = new Uri(requestMetadata.ReferenceUrl);
            string approvalMessage = string.Format(approvalMessageTemplate, requestMetadata.ReferenceUrl, requestMetadata.ApprovalType, requestMetadata.InstanceId, requestMetadata.ApplicantId, requestMetadata.ApplicationName);
            string resultContent;

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(approvalRequestUrl);
                var content = new StringContent(approvalMessage, UnicodeEncoding.UTF8, "application/json");
                var result  = await client.PostAsync(approvalRequestUrl, content);

                resultContent = await result.Content.ReadAsStringAsync();

                if (result.StatusCode != HttpStatusCode.OK)
                {
                    throw new HttpRequestException(resultContent);
                }
            }
            log.Info($"Message regarding {requestMetadata.ApplicationName} sent to Slack!");
            return(resultContent);
        }
예제 #3
0
        public static void EventGridTest(
            [EventGridTrigger] EventGridEvent eventGridEvent,
            [OrchestrationClient] DurableOrchestrationClient orchestrationClient,
            TraceWriter log)
        {
            string blobName = eventGridEvent.Subject.Replace(@"/blobServices/default/containers/requests/blobs/", "");

            log.Info($"Event Grid trigger function Processed blob\n Name: '{blobName}'");

            dynamic eventGridData = eventGridEvent.Data;
            string  blobUrl       = eventGridData?.url;
            string  contentLenghtProp;
            int     contentLenght = 0;

            if (eventGridData?.contentLength != null)
            {
                contentLenghtProp = (string)eventGridData?.contentLength;
                int.TryParse(contentLenghtProp, out contentLenght);
            }

            log.Info($"Content Lenght '{contentLenght.ToString()}'");

            if (contentLenght > 1000 && contentLenght < 2400000)
            {
                string blobStorageBasePath = Environment.GetEnvironmentVariable("Blob:StorageBasePath", EnvironmentVariableTarget.Process);
                string blobContainer       = Environment.GetEnvironmentVariable("Bindings:InputContainerName", EnvironmentVariableTarget.Process);
                string applicantId         = "";
                string applicationName     = "";

                // If the blob name containes a '+' sign, it identifies the first part of the blob name as the applicant and the remaining as the application name. Otherwise, the applicant is unknown and the application name is the full blobname.
                if (blobName.Contains("_-_"))
                {
                    applicantId     = Uri.UnescapeDataString(blobName.Substring(0, blobName.LastIndexOf("_-_")));
                    applicationName = blobName.Substring(blobName.LastIndexOf("_-_") + 3);
                }
                else
                {
                    applicantId     = "unknown";
                    applicationName = blobName;
                }

                ApprovalRequestMetadata requestMetadata = new ApprovalRequestMetadata()
                {
                    ApprovalType    = "FurryModel",
                    ReferenceUrl    = blobUrl,
                    ApplicationName = applicationName,
                    ApplicantId     = applicantId
                };

                var instanceId = orchestrationClient.StartNewAsync("OrchestrateRequestApproval", requestMetadata).Result;
                log.Info($"Durable Function Ochestration started applicant '{applicantId}' and application '{applicationName}'. InstanceId: {instanceId}");
            }
            else
            {
                log.Error($"Event Grid Event has been discarded. Blob content lenght: {contentLenght}");
            }
        }
 public static void Run([ActivityTrigger] ApprovalRequestMetadata requestMetadata, [SendGrid] out SendGridMessage message, TraceWriter log)
 {
     message = new SendGridMessage();
     message.AddTo(Environment.GetEnvironmentVariable("SendGrid:To"));
     message.AddContent("text/html", string.Format(Environment.GetEnvironmentVariable("SendGrid:ApprovalEmailTemplate"), requestMetadata.Subject, requestMetadata.Requestor, requestMetadata.ReferenceUrl, requestMetadata.InstanceId, Environment.GetEnvironmentVariable("Function:BasePath")));
     message.SetFrom(Environment.GetEnvironmentVariable("SendGrid:From"));
     message.SetSubject(String.Format(Environment.GetEnvironmentVariable("SendGrid:SubjectTemplate"), requestMetadata.Subject, requestMetadata.Requestor));
     log.Info($"Message '{message.Subject}' sent!");
 }
예제 #5
0
        public static async void Run(
            [BlobTrigger("%Bindings:InputContainerName%/{name}", Connection = "Blob:StorageConnection")] Stream requestBlob,
            string name,
            [OrchestrationClient] DurableOrchestrationClient orchestrationClient,
            TraceWriter log)
        {
            log.Info($"Blob trigger function Processed blob\n Name:{name} \n Size: {requestBlob.Length} Bytes");
            string blobStorageBasePath = Environment.GetEnvironmentVariable("Blob:StorageBasePath", EnvironmentVariableTarget.Process);
            string blobContainer       = Environment.GetEnvironmentVariable("Bindings:InputContainerName", EnvironmentVariableTarget.Process);
            string applicantId         = "";
            string applicationName     = "";

            // If the blob name containes a '+' sign, it identifies the first part of the blob name as the applicant and the remaining as the application name. Otherwise, the applicant is unknown and the application name is the full blobname.
            if (name.Contains("_-_"))
            {
                applicantId     = Uri.UnescapeDataString(name.Substring(0, name.LastIndexOf("_-_")));
                applicationName = name.Substring(name.LastIndexOf("_-_") + 3);
            }
            else
            {
                applicantId     = "unknown";
                applicationName = name;
            }

            ApprovalRequestMetadata requestMetadata = new ApprovalRequestMetadata()
            {
                ApprovalType    = "FurryModel",
                ReferenceUrl    = $"{blobStorageBasePath}{blobContainer}/{name}",
                ApplicationName = applicationName,
                ApplicantId     = applicantId
            };

            var instanceId = await orchestrationClient.StartNewAsync("OrchestrateRequestApproval", requestMetadata);

            log.Info($"Durable Function Ochestration started applicant '{applicantId}' and application '{applicationName}'. InstanceId: {instanceId}");
        }
예제 #6
0
        public static async Task <bool> Run(
            [OrchestrationTrigger] DurableOrchestrationContext context,
            ILogger log)
        {
            if (!context.IsReplaying)
            {
                log.LogInformation($"Starting the orchestration {context.InstanceId}");
            }

            context.SetCustomStatus("The application has been received.");

            var    isApproved      = false;
            string meansOfApproval = Environment.GetEnvironmentVariable("Workflow:MeansOfApproval");
            ApprovalRequestMetadata approvalRequestMetadata = context.GetInput <ApprovalRequestMetadata>();

            approvalRequestMetadata.InstanceId = context.InstanceId;

            await context.CallActivityAsync("PersistWorkflowCorrelation",
                                            new WorkflowCorrelation {
                EntityId           = $"{approvalRequestMetadata.ApplicantId}",
                WorkflowInstanceId = context.InstanceId
            });

            context.SetCustomStatus("We are reviewing your application. Bear with us.");

            // Check whether the approval request is to be sent via Email or Slack based on App Settings
            if (meansOfApproval.Equals("email", StringComparison.OrdinalIgnoreCase))
            {
                if (!context.IsReplaying)
                {
                    log.LogInformation("Sending Approval Request Via Email");
                }

                await context.CallActivityAsync("SendApprovalRequestViaEmail", approvalRequestMetadata);
            }
            else
            {
                if (!context.IsReplaying)
                {
                    log.LogInformation("Sending Approval Request Via Slack");
                }

                await context.CallActivityAsync("SendApprovalRequestViaSlack", approvalRequestMetadata);
            }

            // Wait for Response as an external event or a time out.
            // The approver has a limit to approve otherwise the request will be rejected.
            using (var timeoutCts = new CancellationTokenSource())
            {
                int timeout;
                if (!int.TryParse(Environment.GetEnvironmentVariable("Workflow:Timeout"), out timeout))
                {
                    timeout = 5;
                }
                DateTime expiration  = context.CurrentUtcDateTime.AddMinutes(timeout);
                Task     timeoutTask = context.CreateTimer(expiration, timeoutCts.Token);

                // This event can come from a click on the Email sent via SendGrid or a selection on the message sent via Slack.
                Task <bool> approvalResponse = context.WaitForExternalEvent <bool>("ReceiveApprovalResponse");

                Task winner = await Task.WhenAny(approvalResponse, timeoutTask);

                ApprovalResponseMetadata approvalResponseMetadata = new ApprovalResponseMetadata()
                {
                    ReferenceUrl = approvalRequestMetadata.ReferenceUrl
                };

                if (winner == approvalResponse)
                {
                    if (!context.IsReplaying)
                    {
                        log.LogInformation("An approval response was received");
                    }

                    if (approvalResponse.Result)
                    {
                        approvalResponseMetadata.Status = "approved";
                        context.SetCustomStatus("Approved. Congrats, You are super special!");
                    }
                    else
                    {
                        approvalResponseMetadata.Status = "rejected";
                        context.SetCustomStatus("Rejected. Sorry! Only the best can join us!");
                    }
                }
                else
                {
                    if (!context.IsReplaying)
                    {
                        log.LogInformation("The waiting time has exceeded!");
                    }

                    approvalResponseMetadata.Status = "rejected";
                    context.SetCustomStatus("Rejected. Sorry! We are extremely busy. Try again in your next life!");
                }

                if (!timeoutTask.IsCompleted)
                {
                    // All pending timers must be completed or cancelled before the function exits.
                    timeoutCts.Cancel();
                }

                // Once the approval process has been finished, the Blob is to be moved to the corresponding container.

                if (!context.IsReplaying)
                {
                    log.LogInformation("Moving the blob to the corresponding container");
                }

                await context.CallActivityAsync <string>("MoveBlob", approvalResponseMetadata);

                if (!context.IsReplaying)
                {
                    log.LogInformation("Orchestration has finished");
                }

                return(isApproved);
            }
        }
        public static async Task <bool> Run([OrchestrationTrigger] DurableOrchestrationContext context)
        {
            var    isApproved      = false;
            string meansOfApproval = Environment.GetEnvironmentVariable("Workflow:MeansOfApproval");
            ApprovalRequestMetadata approvalRequestMetadata = context.GetInput <ApprovalRequestMetadata>();

            approvalRequestMetadata.InstanceId = context.InstanceId;

            // Check whether the approval request is to be sent via Email or Slack based on App Settings
            if (meansOfApproval.Equals("email", StringComparison.OrdinalIgnoreCase))
            {
                await context.CallActivityAsync("SendApprovalRequestViaEmail", approvalRequestMetadata);
            }
            else
            {
                await context.CallActivityAsync("SendApprovalRequestViaSlack", approvalRequestMetadata);
            }

            // Wait for Response as an external event or a time out.
            // The approver has a limit to approve otherwise the request will be rejected.
            using (var timeoutCts = new CancellationTokenSource())
            {
                int timeout;
                if (!int.TryParse(Environment.GetEnvironmentVariable("Workflow:Timeout"), out timeout))
                {
                    timeout = 5;
                }
                DateTime expiration  = context.CurrentUtcDateTime.AddMinutes(timeout);
                Task     timeoutTask = context.CreateTimer(expiration, timeoutCts.Token);

                // This event can come from a click on the Email sent via SendGrid or a selection on the message sent via Slack.
                Task <bool> approvalResponse = context.WaitForExternalEvent <bool>("ReceiveApprovalResponse");
                Task        winner           = await Task.WhenAny(approvalResponse, timeoutTask);

                ApprovalResponseMetadata approvalResponseMetadata = new ApprovalResponseMetadata()
                {
                    ReferenceUrl = approvalRequestMetadata.ReferenceUrl
                };

                if (winner == approvalResponse)
                {
                    if (approvalResponse.Result)
                    {
                        approvalResponseMetadata.DestinationContainer = "approved";
                    }
                    else
                    {
                        approvalResponseMetadata.DestinationContainer = "rejected";
                    }
                }
                else
                {
                    approvalResponseMetadata.DestinationContainer = "rejected";
                }

                if (!timeoutTask.IsCompleted)
                {
                    // All pending timers must be completed or cancelled before the function exits.
                    timeoutCts.Cancel();
                }

                // Once the approval process has been finished, the Blob is to be moved to the corresponding container.
                await context.CallActivityAsync <string>("MoveBlob", approvalResponseMetadata);

                return(isApproved);
            }
        }
        public static async Task <bool> RunOrchestrator(
            [OrchestrationTrigger] DurableOrchestrationContext context)
        {
            var isApproved = false;
            ApprovalRequestMetadata approvalRequestMetadata = context.GetInput <ApprovalRequestMetadata>();

            approvalRequestMetadata.InstanceId = context.InstanceId;

            await context.CallActivityAsync("SendApprovalRequestViaEmail", approvalRequestMetadata);

            // Wait for Response as an external event or a time out.
            // The approver has a limit to approve otherwise the request will be rejected.
            using (var timeoutCts = new CancellationTokenSource())
            {
                int timeout;
                if (!int.TryParse(Environment.GetEnvironmentVariable("Workflow:Timeout"), out timeout))
                {
                    timeout = 1;
                }

                // create timeout task using Durable Function's orechestration context (writes message/event to Storage)
                // "Durable timers cannot last longer than 7 days due to limitations in Azure Storage.
                // We are working on a feature request to extend timers beyond 7 days."

                DateTime expiration  = context.CurrentUtcDateTime.AddMinutes(timeout);
                Task     timeoutTask = context.CreateTimer(expiration, timeoutCts.Token);

                // This event can come from a click on the Email sent via SendGrid.
                Task <bool> approvalResponse = context.WaitForExternalEvent <bool>("ReceiveApprovalResponse");

                // and off to the races!
                Task winner = await Task.WhenAny(approvalResponse, timeoutTask);

                ApprovalResponseMessage responseMessage = new ApprovalResponseMessage()
                {
                    Id           = context.InstanceId,
                    ReferenceUrl = approvalRequestMetadata.ReferenceUrl
                };

                if (winner == approvalResponse)
                {
                    if (approvalResponse.Result)
                    {
                        responseMessage.Result = "approved";
                    }
                    else
                    {
                        responseMessage.Result = "rejected";
                    }
                }
                else
                {
                    responseMessage.Result = "rejected";
                }

                if (!timeoutTask.IsCompleted)
                {
                    // All pending timers must be completed or cancelled before the function exits.
                    timeoutCts.Cancel();
                }

                // Once the approval process has been finished, the result is published to Storage Queue.
                await context.CallActivityAsync <string>("PublishApprovalResult", responseMessage);

                return(isApproved);
            }
        }