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)); }
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)); }
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()); }
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)); }
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)); }
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()); }
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()); }
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()); }
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()); }
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()); }
// 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(); })); }
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()); } }
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)); } }
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()); }
// 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); })); }
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)); }
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()); }
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)); }
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()); }
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()); }
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()); }