public Task <IActionResult> DfmGetOrchestrationHistoryFunction( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.ApiRoutePrefix + "/orchestrations('{instanceId}')/history")] HttpRequest req, [DurableClient(TaskHub = Globals.HubNameRouteParamName)] IDurableClient defaultDurableClient, string connName, string hubName, string instanceId, ILogger log) { return(this.HandleAuthAndErrors(defaultDurableClient, req, connName, hubName, log, async(durableClient) => { var filterClause = new FilterClause(req.Query["$filter"]); HistoryEvent[] history; int?totalCount = null; try { var connEnvVariableName = Globals.GetFullConnectionStringEnvVariableName(connName); history = DfmEndpoint.ExtensionPoints.GetInstanceHistoryRoutine(durableClient, connEnvVariableName, hubName, instanceId) // This code duplication is intentional. We need to keep the whole iteration process inside try-block, because of potential exceptions during it. .ApplyTimeFrom(filterClause.TimeFrom) .ApplyFilter(filterClause) .ApplySkip(req.Query) .ApplyTop(req.Query) .ToArray(); } catch (Exception ex) { log.LogWarning(ex, "Failed to get execution history from storage, falling back to DurableClient"); // Falling back to DurableClient var status = await GetInstanceStatusWithHistory(connName, instanceId, durableClient, log); if (status == null) { return new NotFoundObjectResult($"Instance {instanceId} doesn't exist"); } var historyJArray = status.History == null ? new JArray() : status.History; totalCount = historyJArray.Count; history = historyJArray .Select(OrchestrationHistory.ToHistoryEvent) .ApplyTimeFrom(filterClause.TimeFrom) .ApplyFilter(filterClause) .ApplySkip(req.Query) .ApplyTop(req.Query) .ToArray(); } return new ContentResult() { Content = JsonConvert.SerializeObject(new { totalCount, history }, HistorySerializerSettings), ContentType = "application/json" }; })); }
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> Run( // 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 { Globals.ValidateIdentity(req.HttpContext.User, req.Headers); } catch (UnauthorizedAccessException ex) { return(new OkObjectResult(ex.Message) { StatusCode = 401 }); } 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); var orchestrations = (await( (timeFrom.HasValue && timeTill.HasValue) ? durableClient.GetStatusAsync(timeFrom.Value, timeTill, new OrchestrationRuntimeStatus[0]) : durableClient.GetStatusAsync() )) .ExpandStatusIfNeeded(durableClient, filterClause) .ApplyEntityTypeFilter(entityType) .ApplyFilter(filterClause) .ApplyOrderBy(req.Query) .ApplySkip(req.Query) .ApplyTop(req.Query); return(orchestrations.ToJsonContentResult(Globals.FixUndefinedsInJson)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, [OrchestrationClient] DurableOrchestrationClient orchestrationClient, ILogger log) { // Checking that the call is authenticated properly try { Globals.ValidateIdentity(req.HttpContext.User, req.Headers); } catch (UnauthorizedAccessException ex) { return(new OkObjectResult(ex.Message) { StatusCode = 401 }); } DateTime?timeFrom, timeTill; string filterString = ExtractTimeRange(req.Query["$filter"], out timeFrom, out timeTill); var filterClause = new FilterClause(filterString); var orchestrations = (await( (timeFrom.HasValue && timeTill.HasValue) ? orchestrationClient.GetStatusAsync(timeFrom.Value, timeTill, new OrchestrationRuntimeStatus[0]) : orchestrationClient.GetStatusAsync() )) .ExpandStatusIfNeeded(orchestrationClient, filterClause) .ApplyFilter(filterClause) .ApplyOrderBy(req.Query) .ApplySkip(req.Query) .ApplyTop(req.Query); string json = JsonConvert.SerializeObject(orchestrations, Globals.SerializerSettings) .FixUndefinedsInJson(); return(new ContentResult() { Content = json, ContentType = "application/json" }); }
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)); }
private static IEnumerable <ExpandedOrchestrationStatus> ApplyFilter(this IEnumerable <ExpandedOrchestrationStatus> orchestrations, FilterClause filter) { if (string.IsNullOrEmpty(filter.FieldName)) { foreach (var orchestration in orchestrations) { yield return(orchestration); } } else { if (filter.Predicate == null) { // if filter expression is invalid, returning nothing yield break; } var propInfo = typeof(ExpandedOrchestrationStatus) .GetProperties() .FirstOrDefault(p => p.Name.Equals(filter.FieldName, StringComparison.InvariantCultureIgnoreCase)); if (propInfo == null) { // if field name is invalid, returning nothing yield break; } foreach (var orchestration in orchestrations) { if (filter.Predicate(orchestration.GetPropertyValueAsString(propInfo))) { yield return(orchestration); } } } }
// Applies a filter to a collection of items internal static IEnumerable <T> ApplyFilter <T>(this IEnumerable <T> items, FilterClause filter) { if (string.IsNullOrEmpty(filter.FieldName)) { // if field to be filtered is not specified, returning everything foreach (var orchestration in items) { yield return(orchestration); } } else { if (filter.Predicate == null) { // if filter expression is invalid, returning nothing yield break; } var propInfo = typeof(T).GetProperties() .FirstOrDefault(p => p.Name.Equals(filter.FieldName, StringComparison.InvariantCultureIgnoreCase)); if (propInfo == null) { // if field name is invalid, returning nothing yield break; } foreach (var item in items) { if (filter.Predicate(item.GetPropertyValueAsString(propInfo))) { yield return(item); } } } }
// Adds 'lastEvent' field to each entity private static IEnumerable <ExpandedOrchestrationStatus> ExpandStatus(this IEnumerable <DurableOrchestrationStatus> orchestrations, IDurableClient client, FilterClause filterClause, HashSet <string> hiddenColumns) { // Deliberately explicitly enumerating orchestrations here, to trigger all GetStatusAsync tasks in parallel. // If just using yield return, they would be started and finished sequentially, one by one. var list = new List <ExpandedOrchestrationStatus>(); foreach (var orchestration in orchestrations) { list.Add(new ExpandedOrchestrationStatus(orchestration, client.GetStatusAsync(orchestration.InstanceId, true, false, false), hiddenColumns)); } return(list); }
// Adds 'lastEvent' field to each entity, but only if being filtered by that field private static IEnumerable <ExpandedOrchestrationStatus> ExpandStatusIfNeeded(this IEnumerable <DurableOrchestrationStatus> orchestrations, IDurableClient client, FilterClause filterClause, HashSet <string> hiddenColumns) { // Only expanding if being filtered by lastEvent if (filterClause.FieldName == "lastEvent") { return(orchestrations.ExpandStatus(client, filterClause, hiddenColumns)); } else { return(orchestrations.Select(o => new ExpandedOrchestrationStatus(o, null, hiddenColumns))); } }
// Adds 'lastEvent' field to each entity, but only if being filtered by that field private static IEnumerable <ExpandedOrchestrationStatus> ExpandStatusIfNeeded(this IEnumerable <DurableOrchestrationStatus> orchestrations, IDurableClient client, FilterClause filterClause) { // Only expanding if being filtered by lastEvent bool needToExpand = filterClause.FieldName == "lastEvent"; // Deliberately explicitly enumerating orchestrations here, to trigger all GetStatusAsync tasks in parallel. // If just using yield return, they would be started and finished sequentially, one by one. var list = new List <ExpandedOrchestrationStatus>(); foreach (var orchestration in orchestrations) { list.Add(new ExpandedOrchestrationStatus(orchestration, needToExpand ? client.GetStatusAsync(orchestration.InstanceId, true, false, false) : null)); } return(list); }