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"
                };
            }));
        }
Пример #2
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));
        }
Пример #3
0
        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));
        }
Пример #4
0
        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"
            });
        }
Пример #5
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));
        }
Пример #6
0
        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);
                    }
                }
            }
        }
Пример #8
0
        // 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);
        }
Пример #9
0
 // 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)));
     }
 }
Пример #10
0
        // 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);
        }