コード例 #1
0
        public static async Task <IActionResult> GetIdSuggestionsFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "a/p/i/id-suggestions(prefix='{prefix}')")] HttpRequest req,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            string prefix,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var response = await durableClient.ListInstancesAsync(new OrchestrationStatusQueryCondition()
            {
                InstanceIdPrefix = prefix,
                PageSize         = 50,
                ShowInput        = false
            },
                                                                  CancellationToken.None);

            var orchestrationIds = response.DurableOrchestrationState.Select((s) => s.InstanceId);

            return(orchestrationIds.ToJsonContentResult(Globals.FixUndefinedsInJson));
        }
コード例 #2
0
        public static async Task <IActionResult> DfmGetIdSuggestionsFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.ApiRoutePrefix + "/id-suggestions(prefix='{prefix}')")] HttpRequest req,
            [DurableClient(TaskHub = Globals.TaskHubRouteParamName, ExternalClient = true)] IDurableClient durableClient,
            string prefix,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, durableClient.TaskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var response = await durableClient.ListInstancesAsync(new OrchestrationStatusQueryCondition()
            {
                InstanceIdPrefix = prefix,
                PageSize         = 50,
                ShowInput        = false
            },
                                                                  CancellationToken.None);

            var orchestrationIds = response.DurableOrchestrationState.Select((s) => s.InstanceId);

            return(orchestrationIds.ToJsonContentResult(Globals.FixUndefinedsInJson));
        }
コード例 #3
0
        public static async Task <IActionResult> DfmPurgeHistoryFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = Globals.ApiRoutePrefix + "/purge-history")] HttpRequest req,
            [DurableClient(TaskHub = Globals.TaskHubRouteParamName)] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, durableClient.TaskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            // Checking that we're not in ReadOnly mode
            if (DfmEndpoint.Settings.Mode == DfmMode.ReadOnly)
            {
                log.LogError("Endpoint is in ReadOnly mode");
                return(new StatusCodeResult(403));
            }

            // Important to deserialize time fields as strings, because otherwise time zone will appear to be local
            var request = JsonConvert.DeserializeObject <PurgeHistoryRequest>(await req.ReadAsStringAsync());

            var result = request.EntityType == EntityTypeEnum.DurableEntity ?
                         await durableClient.PurgeDurableEntitiesHistory(DateTime.Parse(request.TimeFrom),
                                                                         DateTime.Parse(request.TimeTill)) :
                         await durableClient.PurgeOrchestrationsHistory(DateTime.Parse(request.TimeFrom),
                                                                        DateTime.Parse(request.TimeTill), request.Statuses);

            return(result.ToJsonContentResult());
        }
コード例 #4
0
        public static async Task <IActionResult> DfmGetOrchestrationFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.ApiRoutePrefix + "/orchestrations('{instanceId}')")] HttpRequest req,
            string instanceId,
            [DurableClient(TaskHub = Globals.TaskHubRouteParamName)] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, durableClient.TaskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var status = await GetInstanceStatus(instanceId, durableClient, log);

            if (status == null)
            {
                return(new NotFoundObjectResult($"Instance {instanceId} doesn't exist"));
            }

            return(status.ToJsonContentResult(Globals.FixUndefinedsInJson));
        }
コード例 #5
0
        public static async Task <IActionResult> GetOrchestrationFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "a/p/i/orchestrations('{instanceId}')")] HttpRequest req,
            string instanceId,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var status = await GetInstanceStatus(instanceId, durableClient, log);

            if (status == null)
            {
                return(new NotFoundObjectResult($"Instance {instanceId} doesn't exist"));
            }

            return(status.ToJsonContentResult(Globals.FixUndefinedsInJson));
        }
コード例 #6
0
        public static async Task <IActionResult> AboutFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "a/p/i/about")] HttpRequest req,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log
            )
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            string accountName = string.Empty;
            var    match       = AccountNameRegex.Match(Environment.GetEnvironmentVariable(EnvVariableNames.AzureWebJobsStorage));

            if (match.Success)
            {
                accountName = match.Groups[1].Value;
            }

            return(new
            {
                accountName,
                hubName = durableClient.TaskHubName,
                version = Assembly.GetExecutingAssembly().GetName().Version.ToString()
            }
                   .ToJsonContentResult());
        }
コード例 #7
0
        public static async Task <IActionResult> DfmGetTaskHubNamesFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "a/p/i/task-hub-names")] HttpRequest req,
            ILogger log
            )
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, null);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var hubNames = await Auth.GetAllowedTaskHubNamesAsync();

            if (hubNames == null)
            {
                return(new ObjectResult("Failed to load the list of Task Hubs")
                {
                    StatusCode = 500
                });
            }
            return(hubNames.ToJsonContentResult());
        }
コード例 #8
0
        public static async Task <IActionResult> PurgeHistoryFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "a/p/i/purge-history")] HttpRequest req,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            // Important to deserialize time fields as strings, because otherwise time zone will appear to be local
            var request = JsonConvert.DeserializeObject <PurgeHistoryRequest>(await req.ReadAsStringAsync());

            var result = request.EntityType == EntityTypeEnum.DurableEntity ?
                         await durableClient.PurgeDurableEntitiesHistory(DateTime.Parse(request.TimeFrom),
                                                                         DateTime.Parse(request.TimeTill)) :
                         await durableClient.PurgeOrchestrationsHistory(DateTime.Parse(request.TimeFrom),
                                                                        DateTime.Parse(request.TimeTill), request.Statuses);

            return(result.ToJsonContentResult());
        }
コード例 #9
0
        public static async Task <IActionResult> DfmCleanEntityStorageFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = Globals.ApiRoutePrefix + "/clean-entity-storage")] HttpRequest req,
            [DurableClient(TaskHub = Globals.TaskHubRouteParamName)] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, durableClient.TaskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            // Checking that we're not in ReadOnly mode
            if (DfmEndpoint.Settings.Mode == DfmMode.ReadOnly)
            {
                log.LogError("Endpoint is in ReadOnly mode");
                return(new StatusCodeResult(403));
            }

            var request = JsonConvert.DeserializeObject <CleanEntityStorageRequest>(await req.ReadAsStringAsync());

            var result = await durableClient.CleanEntityStorageAsync(request.removeEmptyEntities, request.releaseOrphanedLocks, CancellationToken.None);

            return(result.ToJsonContentResult());
        }
コード例 #10
0
        public static async Task <IActionResult> DfmAboutFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.ApiRoutePrefix + "/about")] HttpRequest req,
            string taskHubName,
            ILogger log
            )
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, taskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            string accountName = string.Empty;
            var    match       = AccountNameRegex.Match(Environment.GetEnvironmentVariable(EnvVariableNames.AzureWebJobsStorage));

            if (match.Success)
            {
                accountName = match.Groups[1].Value;
            }

            return(new
            {
                accountName,
                hubName = taskHubName,
                version = Assembly.GetExecutingAssembly().GetName().Version.ToString()
            }
                   .ToJsonContentResult());
        }
コード例 #11
0
        // Applies authN/authZ rules and handles incoming HTTP request. Also does error handling.
        public static async Task <IActionResult> HandleAuthAndErrors(this HttpRequest req, string connName, string hubName, ILogger log, Func <Task <IActionResult> > todo)
        {
            return(await HandleErrors(req, log, async() => {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, req.Cookies, CombineConnNameAndHubName(connName, hubName));

                return await todo();
            }));
        }
コード例 #12
0
        public static async Task <IActionResult> DfmManageConnectionFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "put", Route = Globals.ApiRoutePrefix + "/manage-connection")] HttpRequest req,
            string taskHubName,
            ExecutionContext executionContext,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, taskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            string localSettingsFileName = Path.Combine(executionContext.FunctionAppDirectory, "local.settings.json");

            if (req.Method == "GET")
            {
                bool isRunningOnAzure = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(EnvVariableNames.WEBSITE_SITE_NAME));
                // Don't allow editing, when running in Azure or as a container
                bool isReadOnly = isRunningOnAzure || !File.Exists(localSettingsFileName);

                string connectionString = Environment.GetEnvironmentVariable(EnvVariableNames.AzureWebJobsStorage);
                // No need for your accountKey to ever leave the server side
                connectionString = AccountKeyRegex.Replace(connectionString, "AccountKey=*****");

                return(new { connectionString, hubName = taskHubName, isReadOnly }.ToJsonContentResult());
            }
            else
            {
                // Checking that we're not in ReadOnly mode
                if (DfmEndpoint.Settings.Mode == DfmMode.ReadOnly)
                {
                    log.LogError("Endpoint is in ReadOnly mode");
                    return(new StatusCodeResult(403));
                }

                dynamic bodyObject = JObject.Parse(await req.ReadAsStringAsync());

                string connectionString = bodyObject.connectionString;

                // local.settings.json file does should already exist
                dynamic localSettings = JObject.Parse(await File.ReadAllTextAsync(localSettingsFileName));

                localSettings.Merge(JObject.Parse("{Values: {}}"));
                if (!string.IsNullOrEmpty(connectionString))
                {
                    localSettings.Values.AzureWebJobsStorage = connectionString;
                }

                await File.WriteAllTextAsync(localSettingsFileName, localSettings.ToString());

                return(new OkResult());
            }
        }
コード例 #13
0
        public static async Task <IActionResult> GetOrchestrationTabMarkupFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "a/p/i/orchestrations('{instanceId}')/custom-tab-markup('{templateName}')")] HttpRequest req,
            string instanceId,
            string templateName,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var status = await GetInstanceStatus(instanceId, durableClient, log);

            if (status == null)
            {
                return(new NotFoundObjectResult($"Instance {instanceId} doesn't exist"));
            }

            // The underlying Task never throws, so it's OK.
            var templatesMap = DetailedOrchestrationStatus.TabTemplatesTask.Result;

            string templateCode = templatesMap.GetTemplate(status.GetEntityTypeName(), templateName);

            if (templateCode == null)
            {
                return(new NotFoundObjectResult("The specified template doesn't exist"));
            }

            try
            {
                var fluidTemplate = FluidTemplate.Parse(templateCode);
                var fluidContext  = new TemplateContext(status);

                return(new ContentResult()
                {
                    Content = fluidTemplate.Render(fluidContext),
                    ContentType = "text/html; charset=UTF-8"
                });
            }
            catch (Exception ex)
            {
                return(new BadRequestObjectResult(ex.Message));
            }
        }
コード例 #14
0
        public static async Task <IActionResult> DfmDeleteTaskHubFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = Globals.ApiRoutePrefix + "/delete-task-hub")] HttpRequest req,
            string taskHubName,
            ILogger log
            )
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, taskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            // Checking that we're not in ReadOnly mode
            if (DfmEndpoint.Settings.Mode == DfmMode.ReadOnly)
            {
                log.LogError("Endpoint is in ReadOnly mode");
                return(new StatusCodeResult(403));
            }

            string connectionString = Environment.GetEnvironmentVariable(EnvVariableNames.AzureWebJobsStorage);

            var orcService = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
            {
                StorageConnectionString = connectionString,
                TaskHubName             = taskHubName,
            });

            // .DeleteAsync() tends to throw "The requested operation cannot be performed on this container because of a concurrent operation"
            // (though still seems to do its job). So just wrapping with try-catch
            try
            {
                await orcService.DeleteAsync();
            }
            catch (Exception ex)
            {
                log.LogError(ex, "AzureStorageOrchestrationService.DeleteAsync() failed");
            }

            return(new OkResult());
        }
コード例 #15
0
        // Applies authN/authZ rules and handles incoming HTTP request. Also creates IDurableClient (when needed) and does error handling.
        protected async Task <IActionResult> HandleAuthAndErrors(IDurableClient defaultDurableClient, HttpRequest req, string connName, string hubName, ILogger log, Func <IDurableClient, Task <IActionResult> > todo)
        {
            return(await Globals.HandleErrors(req, log, async() => {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, req.Cookies, Globals.CombineConnNameAndHubName(connName, hubName));

                // For default storage connections using default durableClient, injected normally, as a parameter.
                // Only using IDurableClientFactory for custom connections, just in case.
                var durableClient = Globals.IsDefaultConnectionStringName(connName) ?
                                    defaultDurableClient :
                                    this._durableClientFactory.CreateClient(new DurableClientOptions
                {
                    TaskHub = hubName,
                    ConnectionName = Globals.GetFullConnectionStringEnvVariableName(connName)
                });

                return await todo(durableClient);
            }));
        }
コード例 #16
0
        public static async Task <IActionResult> DfmGetOrchestrationsFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.ApiRoutePrefix + "/orchestrations")] HttpRequest req,
            [DurableClient(TaskHub = Globals.TaskHubRouteParamName)] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, durableClient.TaskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            DateTime?timeFrom, timeTill;
            string   filterString = ExtractTimeRange(req.Query["$filter"], out timeFrom, out timeTill);

            string[] statuses;
            filterString = ExtractRuntimeStatuses(filterString, out statuses);
            var filterClause = new FilterClause(filterString);

            string hiddenColumnsString = req.Query["hidden-columns"];
            var    hiddenColumns       = string.IsNullOrEmpty(hiddenColumnsString) ? new HashSet <string>() : new HashSet <string>(hiddenColumnsString.Split('|'));

            // Filtered column should always be returned
            if (!string.IsNullOrEmpty(filterClause.FieldName))
            {
                hiddenColumns.Remove(filterClause.FieldName);
            }

            var orchestrations = durableClient
                                 .ListAllInstances(timeFrom, timeTill, !hiddenColumns.Contains("input"), statuses)
                                 .ExpandStatusIfNeeded(durableClient, filterClause, hiddenColumns)
                                 .ApplyRuntimeStatusesFilter(statuses)
                                 .ApplyFilter(filterClause)
                                 .ApplyOrderBy(req.Query)
                                 .ApplySkip(req.Query)
                                 .ApplyTop(req.Query);

            return(orchestrations.ToJsonContentResult(Globals.FixUndefinedsInJson));
        }
コード例 #17
0
        public static async Task <IActionResult> DeleteTaskHubFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "a/p/i/delete-task-hub")] HttpRequest req,
            ILogger log
            )
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            string hubName          = Environment.GetEnvironmentVariable(EnvVariableNames.DFM_HUB_NAME);
            string connectionString = Environment.GetEnvironmentVariable(EnvVariableNames.AzureWebJobsStorage);

            var orcService = new AzureStorageOrchestrationService(new AzureStorageOrchestrationServiceSettings
            {
                StorageConnectionString = connectionString,
                TaskHubName             = hubName,
            });

            // .DeleteAsync() tends to throw "The requested operation cannot be performed on this container because of a concurrent operation"
            // (though still seems to do its job). So just wrapping with try-catch
            try
            {
                await orcService.DeleteAsync();
            }
            catch (Exception ex)
            {
                log.LogError(ex, "AzureStorageOrchestrationService.DeleteAsync() failed");
            }

            return(new OkResult());
        }
コード例 #18
0
        public static async Task <IActionResult> GetOrchestrationsFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "a/p/i/orchestrations")] HttpRequest req,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            DateTime?timeFrom, timeTill;
            string   filterString = ExtractTimeRange(req.Query["$filter"], out timeFrom, out timeTill);

            string entityType;

            filterString = ExtractEntityType(filterString, out entityType);
            var filterClause = new FilterClause(filterString);

            string           hiddenColumnsString = req.Query["hidden-columns"];
            HashSet <string> hiddenColumns       = string.IsNullOrEmpty(hiddenColumnsString) ? null : new HashSet <string>(hiddenColumnsString.Split('|'));

            var orchestrations = durableClient.ListAllInstances(timeFrom, timeTill, (hiddenColumns == null || !hiddenColumns.Contains("input")))
                                 .ExpandStatusIfNeeded(durableClient, filterClause, hiddenColumns)
                                 .ApplyEntityTypeFilter(entityType)
                                 .ApplyFilter(filterClause)
                                 .ApplyOrderBy(req.Query)
                                 .ApplySkip(req.Query)
                                 .ApplyTop(req.Query);

            return(orchestrations.ToJsonContentResult(Globals.FixUndefinedsInJson));
        }
コード例 #19
0
        public static async Task <IActionResult> CleanEntityStorageFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "a/p/i/clean-entity-storage")] HttpRequest req,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            var request = JsonConvert.DeserializeObject <CleanEntityStorageRequest>(await req.ReadAsStringAsync());

            var result = await durableClient.CleanEntityStorageAsync(request.removeEmptyEntities, request.releaseOrphanedLocks, CancellationToken.None);

            return(result.ToJsonContentResult());
        }
コード例 #20
0
        public static async Task <IActionResult> DfmPostOrchestrationFunction(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = Globals.ApiRoutePrefix + "/orchestrations('{instanceId}')/{action?}")] HttpRequest req,
            string instanceId,
            string action,
            [DurableClient(TaskHub = Globals.TaskHubRouteParamName)] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers, durableClient.TaskHubName);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            // Checking that we're not in ReadOnly mode
            if (DfmEndpoint.Settings.Mode == DfmMode.ReadOnly)
            {
                log.LogError("Endpoint is in ReadOnly mode");
                return(new StatusCodeResult(403));
            }

            string bodyString = await req.ReadAsStringAsync();

            switch (action)
            {
            case "purge":
                await durableClient.PurgeInstanceHistoryAsync(instanceId);

                break;

            case "rewind":
                await durableClient.RewindAsync(instanceId, bodyString);

                break;

            case "terminate":
                await durableClient.TerminateAsync(instanceId, bodyString);

                break;

            case "raise-event":

                dynamic bodyObject = JObject.Parse(bodyString);
                string  eventName  = bodyObject.name;
                JObject eventData  = bodyObject.data;

                var match = ExpandedOrchestrationStatus.EntityIdRegex.Match(instanceId);
                // if this looks like an Entity
                if (match.Success)
                {
                    // then sending signal
                    var entityId = new EntityId(match.Groups[1].Value, match.Groups[2].Value);

                    await durableClient.SignalEntityAsync(entityId, eventName, eventData);
                }
                else
                {
                    // otherwise raising event
                    await durableClient.RaiseEventAsync(instanceId, eventName, eventData);
                }

                break;

            case "set-custom-status":

                // Updating the table directly, as there is no other known way
                var table = TableClient.GetTableClient().GetTableReference($"{durableClient.TaskHubName}Instances");

                var orcEntity = (await table.ExecuteAsync(TableOperation.Retrieve(instanceId, string.Empty))).Result as DynamicTableEntity;

                if (string.IsNullOrEmpty(bodyString))
                {
                    orcEntity.Properties.Remove("CustomStatus");
                }
                else
                {
                    // Ensuring that it is at least a valid JSON
                    string customStatus = JObject.Parse(bodyString).ToString();
                    orcEntity.Properties["CustomStatus"] = new EntityProperty(customStatus);
                }

                await table.ExecuteAsync(TableOperation.Replace(orcEntity));

                break;

            case "restart":
                bool restartWithNewInstanceId = ((dynamic)JObject.Parse(bodyString)).restartWithNewInstanceId;

                await durableClient.RestartAsync(instanceId, restartWithNewInstanceId);

                break;

            default:
                return(new NotFoundResult());
            }

            return(new OkResult());
        }
コード例 #21
0
        public static async Task <IActionResult> PostOrchestrationFunction(
            // Using /a/p/i route prefix, to let Functions Host distinguish api methods from statics
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "a/p/i/orchestrations('{instanceId}')/{action?}")] HttpRequest req,
            string instanceId,
            string action,
            [DurableClient(TaskHub = "%DFM_HUB_NAME%")] IDurableClient durableClient,
            ILogger log)
        {
            // Checking that the call is authenticated properly
            try
            {
                await Auth.ValidateIdentityAsync(req.HttpContext.User, req.Headers);
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to authenticate request");
                return(new UnauthorizedResult());
            }

            string bodyString = await req.ReadAsStringAsync();

            switch (action)
            {
            case "purge":
                await durableClient.PurgeInstanceHistoryAsync(instanceId);

                break;

            case "rewind":
                await durableClient.RewindAsync(instanceId, bodyString);

                break;

            case "terminate":
                await durableClient.TerminateAsync(instanceId, bodyString);

                break;

            case "raise-event":
                dynamic bodyObject = JObject.Parse(bodyString);
                string  eventName  = bodyObject.name;
                JObject eventData  = bodyObject.data;

                await durableClient.RaiseEventAsync(instanceId, eventName, eventData);

                break;

            case "set-custom-status":

                string connectionString = Environment.GetEnvironmentVariable(EnvVariableNames.AzureWebJobsStorage);
                string hubName          = Environment.GetEnvironmentVariable(EnvVariableNames.DFM_HUB_NAME);

                // Updating the table directly, as there is no other known way
                var tableClient = CloudStorageAccount.Parse(connectionString).CreateCloudTableClient();
                var table       = tableClient.GetTableReference($"{hubName}Instances");

                var orcEntity = (await table.ExecuteAsync(TableOperation.Retrieve(instanceId, string.Empty))).Result as DynamicTableEntity;

                if (string.IsNullOrEmpty(bodyString))
                {
                    orcEntity.Properties.Remove("CustomStatus");
                }
                else
                {
                    // Ensuring that it is at least a valid JSON
                    string customStatus = JObject.Parse(bodyString).ToString();
                    orcEntity.Properties["CustomStatus"] = new EntityProperty(customStatus);
                }

                await table.ExecuteAsync(TableOperation.Replace(orcEntity));

                break;

            case "restart":
                bool restartWithNewInstanceId = ((dynamic)JObject.Parse(bodyString)).restartWithNewInstanceId;

                await durableClient.RestartAsync(instanceId, restartWithNewInstanceId);

                break;

            default:
                return(new NotFoundResult());
            }

            return(new OkResult());
        }