1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
package pagerduty
import (
"fmt"
"net/url"
"regexp"
"strconv"
"github.com/nicholas-fedor/shoutrrr/pkg/format"
"github.com/nicholas-fedor/shoutrrr/pkg/types"
)
const (
// Scheme is the identifying part of this service's configuration URL.
Scheme = "pagerduty"
// integrationKeyRegex validates that integration keys are 32-character hexadecimal strings.
integrationKeyRegex = `^[a-fA-F0-9]{32}$`
)
// Config holds the configuration for the PagerDuty service.
type Config struct {
IntegrationKey string `desc:"The PagerDuty API integration key" url:"path"`
Host string `desc:"The PagerDuty API host." url:"host" default:"events.pagerduty.com"`
Port uint16 `desc:"The PagerDuty API port." url:"port" default:"443"`
Severity string `desc:"The perceived severity of the status the event (critical, error, warning, or info); required" default:"error" key:"severity"`
Source string `desc:"The unique location of the affected system, preferably a hostname or FQDN; required" default:"default" key:"source"`
Action string `desc:"The type of event (trigger, acknowledge, or resolve)" default:"trigger" key:"action"`
DedupKey string `desc:"A unique key used for incident deduplication" key:"dedup_key"`
Details string `desc:"Additional details about the incident (JSON string that will be parsed into an object)" key:"details"`
Contexts string `desc:"Additional context links or images" key:"contexts"`
Client string `desc:"The name of the monitoring client that is triggering this event" key:"client"`
ClientURL string `desc:"The URL of the monitoring client that is triggering this event" key:"client_url"`
}
// Enums returns an empty map because the PagerDuty service doesn't use Enums.
func (config *Config) Enums() map[string]types.EnumFormatter {
return map[string]types.EnumFormatter{}
}
// GetURL returns a URL representation of the Config's current field values.
func (config *Config) GetURL() *url.URL {
resolver := format.NewPropKeyResolver(config)
return config.getURL(&resolver)
}
// getURL constructs a URL from the Config's fields using the provided resolver.
func (config *Config) getURL(resolver types.ConfigQueryResolver) *url.URL {
var host string
if config.Port > 0 {
host = fmt.Sprintf("%s:%d", config.Host, config.Port)
} else {
host = config.Host
}
return &url.URL{
Host: host,
Path: "/" + config.IntegrationKey,
Scheme: Scheme,
RawQuery: format.BuildQuery(resolver),
}
}
// SetURL updates the Config from a URL representation of its field values.
func (config *Config) SetURL(url *url.URL) error {
resolver := format.NewPropKeyResolver(config)
return config.setURL(&resolver, url)
}
// setURL updates the Config from a URL using the provided resolver.
func (config *Config) setURL(resolver types.ConfigQueryResolver, url *url.URL) error {
if len(url.Path) <= 1 {
return errMissingIntegrationKey
}
config.IntegrationKey = url.Path[1:]
// Validate integration key format
if matched, err := regexp.MatchString(integrationKeyRegex, config.IntegrationKey); err != nil {
return fmt.Errorf("failed to validate integration key: %w", err)
} else if !matched {
return errInvalidIntegrationKey
}
if url.Hostname() != "" {
config.Host = url.Hostname()
}
if url.Port() != "" {
port, err := strconv.ParseUint(url.Port(), 10, 16)
if err != nil {
return fmt.Errorf("failed to parse port: %w", err)
}
config.Port = uint16(port)
}
for key, vals := range url.Query() {
if err := resolver.Set(key, vals[0]); err != nil {
return fmt.Errorf("failed to set query parameter %q: %w", key, err)
}
}
return nil
}
|