• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Aldaviva / FreshPager / 15600885521

12 Jun 2025 03:27AM UTC coverage: 80.711% (+26.8%) from 53.886%
15600885521

push

github

Aldaviva
Target runtime comes from matrix, not env anymore

34 of 96 branches covered (35.42%)

159 of 197 relevant lines covered (80.71%)

12.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.52
/FreshPager/FreshpingResource.cs
1
using FreshPager.Data;
2
using Microsoft.AspNetCore.Mvc;
3
using Microsoft.Extensions.Options;
4
using Pager.Duty;
5
using Pager.Duty.Exceptions;
6
using Pager.Duty.Requests;
7
using Pager.Duty.Responses;
8
using System.Collections.Concurrent;
9
using ThrottleDebounce;
10
using Unfucked;
11

12
namespace FreshPager;
13

14
public class FreshpingResource: WebResource {
15

16
    private static readonly Retrier.Options RETRY_OPTIONS = new() {
2✔
17
        MaxAttempts    = 20,
2✔
18
        Delay          = Retrier.Delays.Exponential(TimeSpan.FromSeconds(5), max: TimeSpan.FromMinutes(5)),
2✔
19
        IsRetryAllowed = exception => exception is not (OutOfMemoryException or PagerDutyException { RetryAllowedAfterDelay: false })
×
20
    };
2✔
21

22
    private readonly  ILogger<FreshpingResource>          logger;
23
    private readonly  IOptions<Configuration>             configuration;
24
    internal readonly ConcurrentDictionary<Check, string> dedupKeys;
25

26
    public FreshpingResource(ILogger<FreshpingResource> logger, IOptions<Configuration> configuration) {
10✔
27
        this.logger        = logger;
10✔
28
        this.configuration = configuration;
10✔
29
        int checkCount = configuration.Value.pagerDutyIntegrationKeysByFreshpingCheckId.Count;
10✔
30
        dedupKeys = new ConcurrentDictionary<Check, string>(Math.Min(checkCount * 2, Environment.ProcessorCount), checkCount);
10✔
31
    }
10✔
32

33
    public void map(WebApplication webapp) {
34
        webapp.MapPost("/freshping", async Task<IResult> ([FromBody] FreshpingWebhookPayload payload, PagerDutyFactory pagerDutyFactory) => {
10✔
35
            Check check = payload.check;
16✔
36
            logger.LogTrace("Received webhook payload from Freshping: {payload}", payload);
16✔
37

10✔
38
            if (configuration.Value.pagerDutyIntegrationKeysByFreshpingCheckId.TryGetValue(check.id, out string? pagerDutyIntegrationKey)) {
16✔
39
                using IPagerDuty pagerDuty = pagerDutyFactory(pagerDutyIntegrationKey);
12✔
40

10✔
41
                if (payload.isCheckUp) {
12✔
42
                    return await onCheckUp(check, pagerDuty);
6✔
43
                } else {
10✔
44
                    return await onCheckDown(check, pagerDuty, payload);
6✔
45
                }
10✔
46
            } else {
10✔
47
                logger.LogWarning("No PagerDuty integration key configured for Freshping check {check}, not sending an alert to PagerDuty", check.name);
4✔
48
                return Results.NoContent();
4✔
49
            }
10✔
50
        });
26✔
51
        logger.LogDebug("Listening for Freshping webhooks");
10✔
52
    }
10✔
53

54
    private async Task<IResult> onCheckUp(Check check, IPagerDuty pagerDuty) {
55
        logger.LogInformation("Freshping reports that {check} is up", check.name);
6✔
56
        if (dedupKeys.TryRemove(check, out string? dedupKey)) {
6!
57
            ResolveAlert resolution = new(dedupKey);
6✔
58
            await Retrier.Attempt(async _ => await pagerDuty.Send(resolution), RETRY_OPTIONS);
12✔
59
            logger.LogInformation("Resolved PagerDuty alert for {check} being down using deduplication key {key}", check.name, dedupKey);
6✔
60
        } else {
61
            logger.LogWarning("No known PagerDuty alerts for check {check}, not resolving anything", check.name);
×
62
        }
63
        return Results.NoContent();
6✔
64
    }
6✔
65

66
    private async Task<IResult> onCheckDown(Check check, IPagerDuty pagerDuty, FreshpingWebhookPayload requestBody) {
67
        logger.LogInformation("Freshping reports that {check} is down", check.name);
6✔
68
        dedupKeys.TryGetValue(check, out string? oldDedupKey);
6✔
69
        Uri reportUrl = new UrlBuilder("https", $"{requestBody.organizationSubdomain}.freshping.io").Path("reports").QueryParam("check_id", requestBody.checkId);
6✔
70

71
        try {
72
            TriggerAlert triggerAlert = new(Severity.Error, requestBody.eventTitle) {
6!
73
                DedupKey  = oldDedupKey,
6✔
74
                Timestamp = requestBody.requestDateTime,
6✔
75
                Links = {
6✔
76
                    new Link(reportUrl, "Freshping Report"),
6✔
77
                    new Link(requestBody.checkedUrl, "Checked Service URL")
6✔
78
                },
6✔
79
                CustomDetails = new Dictionary<string, object> {
6✔
80
                    ["Check Name"]              = requestBody.checkName,
6✔
81
                    ["Request Location"]        = requestBody.requestLocation,
6✔
82
                    ["Response Duration (sec)"] = requestBody.responseTime.TotalSeconds,
6✔
83
                    ["Response State"]          = requestBody.responseState,
6✔
84
                    ["Response Status Code"]    = requestBody.responseStatusCode?.ToString() ?? "(none)",
6✔
85
                    ["Response Summary"]        = requestBody.responseSummary
6✔
86
                },
6✔
87

6✔
88
                // The following fields only appear on the webapp alert details page, and nowhere in the mobile app
6✔
89
                Class     = requestBody.responseSummary,
6✔
90
                Component = requestBody.checkedUrl.ToString(),
6✔
91
                Source    = requestBody.requestLocation
6✔
92
            };
6✔
93

94
            string newDedupKey = await Retrier.Attempt(async _ => {
6✔
95
                AlertResponse alertResponse = await pagerDuty.Send(triggerAlert);
6✔
96
                dedupKeys[check] = alertResponse.DedupKey;
6✔
97
                return alertResponse.DedupKey;
6✔
98
            }, RETRY_OPTIONS);
12✔
99

100
            logger.LogInformation("Triggered alert in PagerDuty for {check} being down, got deduplication key {key}", check.name, newDedupKey);
6✔
101
        } catch (Exception e) when (e is not OutOfMemoryException) {
6✔
102
            logger.LogError(e, "Failed to trigger alert in PagerDuty after {attempts}, giving up", RETRY_OPTIONS.MaxAttempts);
×
103
            return Results.Problem(statusCode: StatusCodes.Status503ServiceUnavailable, detail: "Failed to trigger PagerDuty alert");
×
104
        }
105
        return Results.Created();
6✔
106
    }
6✔
107

108
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc