public static async Task Run(
            //[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            [TimerTrigger("0 */10 * * * *", RunOnStartup = false)] TimerInfo timer, // once every 10 minutes
            [CosmosDB("annoydb",
                      "reminders",
                      ConnectionStringSetting = "CosmosDBConnection",
                      SqlQuery = @"SELECT * FROM c WHERE GetCurrentDateTime() >= c.NextReminder AND c.LastReminder < c.NextReminder")]
            IEnumerable <ReminderDocument> dueReminders,
            [CosmosDB("annoydb",
                      "reminders",
                      ConnectionStringSetting = "CosmosDBConnection")]
            IAsyncCollector <ReminderDocument> documents,
            ILogger log)
        {
            foreach (var reminder in dueReminders)
            {
                try
                {
                    // round down to the current hour
                    var now = DateTime.UtcNow.Date.AddHours(DateTime.UtcNow.Hour);
                    reminder.LastReminder = now;

                    CalculateNextReminder(reminder, now);

                    var installationClient = await GitHubHelper.GetInstallationClient(reminder.InstallationId);

                    var newIssue = new NewIssue(reminder.Reminder.Title)
                    {
                        Body = reminder.Reminder.Message,
                    };
                    foreach (var assignee in reminder.Reminder.Assignee?.Split(';',
                                                                               StringSplitOptions.RemoveEmptyEntries) ?? Enumerable.Empty <string>())
                    {
                        newIssue.Assignees.Add(assignee);
                    }

                    foreach (var label in reminder.Reminder.Labels?.Split(';', StringSplitOptions.RemoveEmptyEntries) ??
                             Enumerable.Empty <string>())
                    {
                        newIssue.Labels.Add(label);
                    }

                    log.LogDebug($"Scheduling next due date for reminder {reminder.Id} for {reminder.NextReminder}");

                    var issue = await installationClient.Issue.Create(reminder.RepositoryId, newIssue);

                    log.LogInformation($"Created reminder issue based on reminder {reminder.Id}");

                    await documents.AddAsync(reminder);
                }
                catch (ApiValidationException validationException)
                {
                    log.LogCritical(validationException, $"ApiValidation on reminder '{reminder.Id}' exception: {validationException.Message}:{validationException.HttpResponse}");
                }
                catch (Exception e)
                {
                    log.LogCritical(e, $"Failed to create reminder for {reminder.Id}");
                }
            }
        }
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            [CosmosDB("annoydb", "reminders", ConnectionStringSetting = "CosmosDBConnection")] IAsyncCollector <ReminderDocument> documents,
            [CosmosDB("annoydb", "reminders", ConnectionStringSetting = "CosmosDBConnection")] IDocumentClient documentClient,
            ILogger log)
        {
            if (!req.Headers.TryGetValue("X-GitHub-Event", out var callbackEvent) || callbackEvent != "push")
            {
                // this typically seem to be installation related events.
                // Or check_run (action:requested/rerequested) / check_suite events.
                log.LogWarning($"Non-push callback. 'X-GitHub-Event': '{callbackEvent}'");
                return(new OkResult());
            }

            var requestObject = await ParseRequest(req, log);

            var installationClient = await GitHubHelper.GetInstallationClient(requestObject.Installation.Id);

            if (requestObject.HeadCommit == null)
            {
                // no commits on push (e.g. branch delete)
                return(new OkResult());
            }

            log.LogInformation($"Handling changes made to branch '{requestObject.Ref}' by head-commit '{requestObject.HeadCommit}'.");

            if (requestObject.Ref.EndsWith($"/{requestObject.Repository.DefaultBranch}"))
            {
                var commitsToConsider = requestObject.Commits;
                if (commitsToConsider.LastOrDefault()?.Message?.StartsWith("Merge ") ?? false)
                {
                    // if the last commit is a merge commit, ignore other commits as the merge commits contains all the relevant changes
                    // TODO: This behavior will be incorrect if a non-merge-commit contains this commit message. To be absolutely sure, we'd have to retrieve the full commit object and inspect the parent information. This information is not available on the callback object
                    commitsToConsider = new[] { commitsToConsider.Last() };
                }
                var newReminders = await FindNewReminders(commitsToConsider, requestObject, installationClient);

                foreach ((string fileName, Reminder reminder) in newReminders)
                {
                    await documents.AddAsync(new ReminderDocument
                    {
                        Id             = BuildDocumentId(requestObject, fileName),
                        InstallationId = requestObject.Installation.Id,
                        RepositoryId   = requestObject.Repository.Id,
                        Reminder       = reminder,
                        NextReminder   = new DateTime(reminder.Date.Ticks, DateTimeKind.Utc),
                        Path           = fileName
                    });
                    await CreateCommitComment(installationClient, requestObject,
                                              $"Created reminder '{reminder.Title}' for {reminder.Date:D}");
                }

                await DeleteRemovedReminders(documentClient, log, requestObject, installationClient);
            }
            else
            {
                IList <(string, Reminder)> newReminders;
                try
                {
                    // inspect all commits on branches as we just want to see whether they are valid
                    newReminders = await FindNewReminders(requestObject.Commits, requestObject, installationClient);
                }
                catch (Exception e)
                {
                    await TryCreateCheckRun(installationClient, requestObject.Repository.Id,
                                            new NewCheckRun("annoy-o-bot", requestObject.HeadCommit.Id)
                    {
                        Status     = CheckStatus.Completed,
                        Conclusion = CheckConclusion.Failure,
                        Output     = new NewCheckRunOutput(
                            "Invalid reminder definition",
                            "The provided reminder seems to be invalid or incorrect." + e.Message)
                    }, log);

                    throw;
                }

                if (newReminders.Any())
                {
                    await TryCreateCheckRun(installationClient, requestObject.Repository.Id,
                                            new NewCheckRun("annoy-o-bot", requestObject.HeadCommit.Id)
                    {
                        Status     = CheckStatus.Completed,
                        Conclusion = CheckConclusion.Success
                    }, log);
                }
            }

            return(new OkResult());
        }