Documentation
¶
Overview ¶
Package types provides shared interfaces and data structures for the Slack Manager system.
This package defines contracts for database access, logging, metrics, and core domain types like alerts and issues. It serves as the foundation for the Slack Manager ecosystem, enabling dependency injection and allowing different implementations to be plugged in.
Core Interfaces ¶
DB - Database abstraction for persisting alerts, issues, move mappings, and channel processing state. Implementations must handle storage as opaque JSON to allow flexibility.
Logger - Structured logging interface with Debug/Info/Error levels and field support. Supports method chaining with WithField and WithFields.
Metrics - Prometheus-style metrics interface supporting counters, gauges, and histograms. Allows registration of metrics with labels and observation of values.
Core Domain Types ¶
Alert - The central type representing an alert with comprehensive validation and cleaning. Contains severity, header, text, fields, webhooks, escalations, and routing information. Use constructor functions: NewPanicAlert(), NewErrorAlert(), NewWarningAlert(), NewResolvedAlert(), NewInfoAlert().
Issue - Interface for tracking issue state in channels. Issues group related alerts together using correlation IDs. The actual implementation is internal and stored as opaque JSON.
MoveMapping - Interface for tracking issues that have been moved between channels. Ensures new alerts with the same correlation ID go to the new channel.
ChannelProcessingState - Tracks per-channel processing timestamps and open issue counts to prevent concurrent processing and ensure regular intervals.
Webhook Support ¶
The package provides rich webhook support with interactive buttons, input forms, and access control:
- Webhook - Interactive button configuration with HTTP callbacks or custom handlers
- WebhookCallback - Data received when a webhook is triggered
- WebhookAccessLevel - Control who can click buttons (global_admins, channel_admins, channel_members)
- WebhookButtonStyle - Visual style (primary, danger)
- WebhookDisplayMode - When to show buttons (always, open_issue, resolved_issue)
Validation and Cleaning ¶
Alert provides extensive validation and cleaning methods:
- Clean() - Normalizes and truncates all fields to valid values
- Validate() - Returns error if any field is invalid
- Individual validation methods for specific fields (ValidateSlackChannelIDAndRouteKey, etc.)
The package defines comprehensive constants for maximum lengths and limits (e.g., MaxHeaderLength = 130).
Testing Utilities ¶
The dbtests subpackage provides a shared test suite that can be run against any DB implementation to ensure compliance with the interface contract.
No-op implementations (NoopLogger, NoopMetrics) are provided for testing purposes. InMemoryFifoQueue is provided for testing but should not be used in production.
Usage Example ¶
// Initialize a new error-level notification
alert := types\.NewErrorAlert()
alert.Header = "Database Connection Failed"
alert.Text = "Unable to connect to production database"
alert.SlackChannelID = "C12345678"
alert.IssueFollowUpEnabled = true
alert.AutoResolveSeconds = 300
// Add fields
alert.Fields = []*types\.Field{
{Title: "Host", Value: "db-prod-01"},
{Title: "Port", Value: "5432"},
}
// Add escalation
alert.Escalation = []*types\.Escalation{
{
Severity: types\.AlertPanic,
DelaySeconds: 300,
SlackMentions: []string{"<!here>"},
},
}
// Add webhook button
alert.Webhooks = []*types\.Webhook{
{
ID: "restart",
URL: "https://example.com/webhook/restart",
ButtonText: "Restart DB",
ButtonStyle: types\.WebhookButtonStyleDanger,
AccessLevel: types\.WebhookAccessLevelChannelAdmins,
},
}
// Clean and validate
alert.Clean()
if err := alert.Validate(); err != nil {
panic(err)
}
Index ¶
- Constants
- Variables
- func SeverityIsValid(s AlertSeverity) bool
- func SeverityPriority(s AlertSeverity) int
- func ValidSeverities() []string
- func ValidWebhookAccessLevels() []string
- func ValidWebhookButtonStyles() []string
- func ValidWebhookDisplayModes() []string
- func WebhookAccessLevelIsValid(s WebhookAccessLevel) bool
- func WebhookButtonStyleIsValid(s WebhookButtonStyle) bool
- func WebhookDisplayModeIsValid(s WebhookDisplayMode) bool
- type Alert
- func (a *Alert) Clean()
- func (a *Alert) UniqueID() string
- func (a *Alert) Validate() error
- func (a *Alert) ValidateAutoResolve() error
- func (a *Alert) ValidateCorrelationID() error
- func (a *Alert) ValidateEscalation() error
- func (a *Alert) ValidateFields() error
- func (a *Alert) ValidateHeaderAndText() error
- func (a *Alert) ValidateIcon() error
- func (a *Alert) ValidateIgnoreIfTextContains() error
- func (a *Alert) ValidateLink() error
- func (a *Alert) ValidateSeverity() error
- func (a *Alert) ValidateSlackChannelIDAndRouteKey() error
- func (a *Alert) ValidateWebhooks() error
- type AlertSeverity
- type ChannelProcessingState
- type DB
- type Escalation
- type Field
- type FifoQueueItem
- type InMemoryDB
- func (db *InMemoryDB) DeleteMoveMapping(_ context.Context, channelID, correlationID string) error
- func (db *InMemoryDB) DropAllData(_ context.Context) error
- func (db *InMemoryDB) FindActiveChannels(_ context.Context) ([]string, error)
- func (db *InMemoryDB) FindChannelProcessingState(_ context.Context, channelID string) (*ChannelProcessingState, error)
- func (db *InMemoryDB) FindIssueBySlackPostID(_ context.Context, channelID, postID string) (string, json.RawMessage, error)
- func (db *InMemoryDB) FindMoveMapping(_ context.Context, channelID, correlationID string) (json.RawMessage, error)
- func (db *InMemoryDB) FindOpenIssueByCorrelationID(_ context.Context, channelID, correlationID string) (string, json.RawMessage, error)
- func (db *InMemoryDB) Init(_ context.Context, _ bool) error
- func (db *InMemoryDB) LoadOpenIssuesInChannel(_ context.Context, channelID string) (map[string]json.RawMessage, error)
- func (db *InMemoryDB) MoveIssue(_ context.Context, issue Issue, sourceChannelID, targetChannelID string) error
- func (db *InMemoryDB) SaveAlert(_ context.Context, alert *Alert) error
- func (db *InMemoryDB) SaveChannelProcessingState(_ context.Context, state *ChannelProcessingState) error
- func (db *InMemoryDB) SaveIssue(_ context.Context, issue Issue) error
- func (db *InMemoryDB) SaveIssues(ctx context.Context, issues ...Issue) error
- func (db *InMemoryDB) SaveMoveMapping(_ context.Context, moveMapping MoveMapping) error
- type InMemoryFifoQueue
- type Issue
- type Logger
- type Metrics
- type MoveMapping
- type NoopLogger
- func (l *NoopLogger) Debug(msg string)
- func (l *NoopLogger) Debugf(format string, args ...any)
- func (l *NoopLogger) Error(msg string)
- func (l *NoopLogger) Errorf(format string, args ...any)
- func (l *NoopLogger) Info(msg string)
- func (l *NoopLogger) Infof(format string, args ...any)
- func (l *NoopLogger) WithField(key string, value any) Logger
- func (l *NoopLogger) WithFields(fields map[string]any) Logger
- type NoopMetrics
- func (m *NoopMetrics) Add(_ string, _ float64, _ ...string)
- func (m *NoopMetrics) Inc(_ string, _ ...string)
- func (m *NoopMetrics) Observe(_ string, _ float64, _ ...string)
- func (m *NoopMetrics) RegisterCounter(_, _ string, _ ...string)
- func (m *NoopMetrics) RegisterGauge(_, _ string, _ ...string)
- func (m *NoopMetrics) RegisterHistogram(_, _ string, _ []float64, _ ...string)
- func (m *NoopMetrics) Set(_ string, _ float64, _ ...string)
- type Webhook
- type WebhookAccessLevel
- type WebhookButtonStyle
- type WebhookCallback
- func (w *WebhookCallback) GetCheckboxInputSelectedValues(key string) []string
- func (w *WebhookCallback) GetInputValue(key string) string
- func (w *WebhookCallback) GetPayloadBool(key string, defaultValue bool) bool
- func (w *WebhookCallback) GetPayloadInt(key string, defaultValue int) int
- func (w *WebhookCallback) GetPayloadString(key string) string
- func (w *WebhookCallback) GetPayloadValue(key string) any
- type WebhookCheckboxInput
- type WebhookCheckboxOption
- type WebhookDisplayMode
- type WebhookPlainTextInput
Constants ¶
const ( // MaxTimestampAge is the maximum age of an alert timestamp. // If the timestamp is older than this, it will be replaced with the current time. MaxTimestampAge = 7 * 24 * time.Hour // MaxSlackChannelIDLength is the maximum length of a Slack channel ID or name. MaxSlackChannelIDLength = 80 // MaxRouteKeyLength is the maximum length of a routing key for channel routing. MaxRouteKeyLength = 1000 // MaxHeaderLength is the maximum length of the alert header (title). // Slack's header block limit is 150 characters; we reserve space for status emoji replacement. MaxHeaderLength = 130 // MaxFallbackTextLength is the maximum length of the fallback notification text. MaxFallbackTextLength = 150 // MaxTextLength is the maximum length of the alert text (body). MaxTextLength = 10000 // MaxAuthorLength is the maximum length of the author field. MaxAuthorLength = 100 // MaxHostLength is the maximum length of the host field. MaxHostLength = 100 MaxFooterLength = 300 // MaxUsernameLength is the maximum length of the bot username. MaxUsernameLength = 100 // MaxFieldTitleLength is the maximum length of a field title. MaxFieldTitleLength = 30 // MaxFieldValueLength is the maximum length of a field value. MaxFieldValueLength = 200 // MaxIconEmojiLength is the maximum length of the icon emoji (excluding colons). MaxIconEmojiLength = 50 // MaxMentionLength is the maximum length of a Slack mention (excluding angle brackets). MaxMentionLength = 20 // MaxCorrelationIDLength is the maximum length of the correlation ID. MaxCorrelationIDLength = 500 // MinAutoResolveSeconds is the minimum seconds before auto-resolving an issue. MinAutoResolveSeconds = 30 // MaxAutoResolveSeconds is the maximum seconds before auto-resolving an issue (approximately 2 years). MaxAutoResolveSeconds = 63113851 // MaxIgnoreIfTextContainsLength is the maximum length of each ignore pattern. MaxIgnoreIfTextContainsLength = 1000 // MaxIgnoreIfTextContainsCount is the maximum number of ignore patterns per alert. MaxIgnoreIfTextContainsCount = 20 // MaxFieldCount is the maximum number of fields per alert. MaxFieldCount = 20 // MaxWebhookCount is the maximum number of webhooks per alert. MaxWebhookCount = 5 // MaxWebhookIDLength is the maximum length of a webhook ID. MaxWebhookIDLength = 100 // MaxWebhookURLLength is the maximum length of a webhook URL. MaxWebhookURLLength = 1000 // MaxWebhookButtonTextLength is the maximum length of button text (Slack limit: 25 characters). MaxWebhookButtonTextLength = 25 // MaxWebhookConfirmationTextLength is the maximum length of confirmation dialog text. MaxWebhookConfirmationTextLength = 1000 // MaxWebhookPayloadCount is the maximum number of key-value pairs in webhook payload. MaxWebhookPayloadCount = 50 // MaxWebhookPlainTextInputCount is the maximum number of text inputs per webhook. MaxWebhookPlainTextInputCount = 10 // MaxWebhookCheckboxInputCount is the maximum number of checkbox groups per webhook. MaxWebhookCheckboxInputCount = 10 // MaxWebhookInputIDLength is the maximum length of an input field ID. MaxWebhookInputIDLength = 200 // MaxWebhookInputDescriptionLength is the maximum length of an input field description/placeholder. MaxWebhookInputDescriptionLength = 200 // MaxWebhookInputLabelLength is the maximum length of a checkbox group label. MaxWebhookInputLabelLength = 200 // MaxWebhookInputTextLength is the maximum length of text input content. MaxWebhookInputTextLength = 3000 // MaxWebhookCheckboxOptionCount is the maximum number of options per checkbox group. MaxWebhookCheckboxOptionCount = 5 // MaxWebhookCheckboxOptionTextLength is the maximum length of checkbox option text. MaxWebhookCheckboxOptionTextLength = 50 // MaxCheckboxOptionValueLength is the maximum length of a checkbox option value. MaxCheckboxOptionValueLength = 100 // MaxEscalationCount is the maximum number of escalation points per alert. MaxEscalationCount = 3 // MinEscalationDelaySeconds is the minimum delay before the first escalation triggers. MinEscalationDelaySeconds = 30 // MinEscalationDelayDiffSeconds is the minimum time between consecutive escalations. MinEscalationDelayDiffSeconds = 30 // MaxEscalationSlackMentionCount is the maximum number of Slack mentions per escalation. MaxEscalationSlackMentionCount = 10 )
Variables ¶
var ( // SlackChannelIDOrNameRegex matches valid Slack channel IDs and channel names. // Channel names are mapped to channel IDs by the API. SlackChannelIDOrNameRegex = regexp.MustCompile(fmt.Sprintf(`^[0-9a-zA-Z\-_]{1,%d}$`, MaxSlackChannelIDLength)) // IconRegex matches valid Slack icon emojis, on the format ':emoji:'. IconRegex = regexp.MustCompile(fmt.Sprintf(`^:[^:]{1,%d}:$`, MaxIconEmojiLength)) // SlackMentionRegex matches valid Slack mentions, such as <!here>, <!channel> and <@U12345678>. SlackMentionRegex = regexp.MustCompile(fmt.Sprintf(`^((<!here>)|(<!channel>)|(<@[^>\s]{1,%d}>))$`, MaxMentionLength)) )
Functions ¶
func SeverityIsValid ¶
func SeverityIsValid(s AlertSeverity) bool
func SeverityPriority ¶
func SeverityPriority(s AlertSeverity) int
func ValidSeverities ¶
func ValidSeverities() []string
func ValidWebhookAccessLevels ¶
func ValidWebhookAccessLevels() []string
ValidWebhookAccessLevels returns a slice of valid WebhookAccessLevel values.
func ValidWebhookButtonStyles ¶
func ValidWebhookButtonStyles() []string
ValidWebhookButtonStyles returns a slice of valid WebhookButtonStyle values.
func ValidWebhookDisplayModes ¶
func ValidWebhookDisplayModes() []string
ValidWebhookDisplayModes returns a slice of valid WebhookDisplayMode values.
func WebhookAccessLevelIsValid ¶
func WebhookAccessLevelIsValid(s WebhookAccessLevel) bool
WebhookAccessLevelIsValid returns true if the provided WebhookAccessLevel is valid.
func WebhookButtonStyleIsValid ¶
func WebhookButtonStyleIsValid(s WebhookButtonStyle) bool
WebhookButtonStyleIsValid returns true if the provided WebhookButtonStyle is valid.
func WebhookDisplayModeIsValid ¶
func WebhookDisplayModeIsValid(s WebhookDisplayMode) bool
WebhookDisplayModeIsValid returns true if the provided WebhookDisplayMode is valid.
Types ¶
type Alert ¶
type Alert struct {
// Timestamp is the time when the alert was created. If the timestamp is empty (or older than 7 days), it will be replaced with the current time.
Timestamp time.Time `json:"timestamp"`
// CorrelationID is an optional field used to group related alerts together in issues.
// If unset, the correlation ID is constructed by hashing [Header, Text, Author, Host, SlackChannelID].
// It is strongly recommended to set this to an explicit value, which makes sense in your context, rather than relying on the default hash value.
// With a custom correlation ID, you can update both header and text without creating a new issue.
CorrelationID string `json:"correlationId"`
// Type is the type of alert, such as 'compliance', 'security' or 'metrics'.
// It is primarily used for routing, when the alert RouteKey field is used (rather than SlackChannelID).
// This field is optional, and case-insensitive.
Type string `json:"type"`
// Header is the main header (title) of the alert.
// It is automatically truncated at MaxHeaderLength characters.
// Include :status: in the header (or text) to have it replaced with the appropriate emoji for the issue severity.
// This field is optional, but Header and Text cannot both be empty.
Header string `json:"header"`
// HeaderWhenResolved is the main header (title) of the issue when in the *resolved* state.
// It is automatically truncated at MaxHeaderLength characters.
// This field is optional. If unset, the Header field is used for all issue states.
HeaderWhenResolved string `json:"headerWhenResolved"`
// Text is the main text (body) of the alert.
// It is automatically truncated at MaxTextLength characters.
// Include :status: in the text (or header) to have it replaced with the appropriate emoji for the issue severity.
// This field is optional, but Header and Text cannot both be empty.
Text string `json:"text"`
// TextWhenResolved is the main text (body) of the alert when in the *resolved* state.
// It is automatically truncated at MaxTextLength characters.
// This field is optional. If unset, the Text field is used for all issue states.
TextWhenResolved string `json:"textWhenResolved"`
// FallbackText is the text displayed in Slack notifications.
// It should be a short, human-readable summary of the alert, without markdown or line breaks.
// It is automatically truncated at MaxFallbackTextLength characters.
// This field is optional. If unset, Slack decides what to display in notifications (which may not always be ideal).
FallbackText string `json:"fallbackText"`
// Author is the 'author' of the alert (if relevant), displayed as a context block in the Slack post.
// It is automatically truncated at MaxAuthorLength characters.
// This field is optional.
Author string `json:"author"`
// Host is the 'host' on which the alert originated (if any), displayed as a context block in the Slack post.
// It is automatically truncated at MaxHostLength characters.
// This field is optional.
Host string `json:"host"`
// It is automatically truncated at MaxFooterLength characters.
// This field is optional.
Footer string `json:"footer"`
// Link is an optional link (url) to more information about the alert, displayed as a context block in the Slack post.
// This field is optional, but if set, it must be a valid absolute URL, starting with http:// or https://
Link string `json:"link"`
// IssueFollowUpEnabled is a flag that determines if the issue should be automatically resolved after a certain time.
// If set to true, the issue will be resolved after AutoResolveSeconds seconds.
// Set to false for fire-and-forget alerts, where no follow-up is needed (i.e. no issue tracking).
IssueFollowUpEnabled bool `json:"issueFollowUpEnabled"`
// AutoResolveSeconds is the number of seconds after which the issue should be automatically resolved, if IssueFollowUpEnabled is true.
// The value must be between MinAutoResolveSeconds and MaxAutoResolveSeconds.
AutoResolveSeconds int `json:"autoResolveSeconds"`
// AutoResolveAsInconclusive is a flag that determines if the issue should be automatically resolved as 'inconclusive' instead of 'resolved'.
// This affects the which emoji is used in the Slack post.
// The default value is false, which means the issue is resolved with status 'resolved'.
AutoResolveAsInconclusive bool `json:"autoResolveAsInconclusive"`
// Severity is the severity of the alert, such as 'panic', 'error', 'warning', 'resolved' or 'info'.
// This value determines the emoji used in the Slack post (for the :status: placeholder in header or text).
// The value must be one of the predefined AlertSeverity constants.
// If unset, the severity is automatically set to 'error'.
Severity AlertSeverity `json:"severity"`
// SlackChannelID is the ID of the Slack channel where the alert should be posted.
// Slack channel names are also accepted, and are automatically converted to channel IDs by the API.
// The value must be an existing channel ID or name, and the Slack Manager integration must have been added to the channel.
// This field is optional.
// If both SlackChannelID and RouteKey are set, SlackChannelID takes precedence.
// If both SlackChannelID and RouteKey are empty, the API will still accept the alert IF a fallback mapping exists. Otherwise, it will return an error.
SlackChannelID string `json:"slackChannelId"`
// RouteKey is the case-insensitive route key of the alert, used for routing to the correct Slack channel by the API.
// The API will return an error if the route key does not match any configured route.
// This field is optional.
// If both SlackChannelID and RouteKey are set, SlackChannelID takes precedence.
// If both SlackChannelID and RouteKey are empty, the API will still accept the alert IF a fallback mapping exists. Otherwise, it will return an error.
RouteKey string `json:"routeKey"`
// Username is the username that the alert should be posted as in Slack.
// This field is optional. If omitted, the alert is posted as the default bot user.
Username string `json:"username"`
// IconEmoji is the emoji that the alert should be posted with in Slack, on the format ':emoji:'.
IconEmoji string `json:"iconEmoji"`
// Fields are rendered in a compact format that allows for 2 columns of side-by-side text.
Fields []*Field `json:"fields"`
// NotificationDelaySeconds is the number of seconds to wait before creating an actual Slack post.
// If the issue is resolved before the delay is over, no Slack post is created for the issue.
// This is useful for issues that may be resolved quickly, to avoid unnecessary notifications.
NotificationDelaySeconds int `json:"notificationDelaySeconds"`
// ArchivingDelaySeconds is the number of seconds to wait before archiving the issue, after it is resolved.
// A non-archived issue is re-opened if a new alert with the same CorrelationID is received, and the same Slack post is updated.
// An archived issue can never be re-opened, and any new alerts with the same CorrelationID will generate a new issue and new Slack post.
ArchivingDelaySeconds int `json:"archivingDelaySeconds"`
// Escalation defines a list of escalation points for this alert's issue.
// Each escalation can increase severity, add Slack mentions, or move the issue to a different channel after a specified delay.
// Escalations are sorted by DelaySeconds and triggered in order if the issue remains unresolved.
// Maximum of MaxEscalationCount escalations allowed.
Escalation []*Escalation `json:"escalation"`
// IgnoreIfTextContains is a list of substrings that, if found in the alert text, will cause the alert to be ignored.
// This is useful for filtering out known noise or false positives.
// Maximum of MaxIgnoreIfTextContainsCount items, each up to MaxIgnoreIfTextContainsLength characters.
IgnoreIfTextContains []string `json:"ignoreIfTextContains"`
// Webhooks defines interactive buttons that appear on the Slack post.
// Each webhook triggers an HTTP POST to the specified URL when clicked.
// Webhooks can include confirmation dialogs, input forms, and access level restrictions.
// Maximum of MaxWebhookCount webhooks allowed.
Webhooks []*Webhook `json:"webhooks"`
// Metadata is an arbitrary key-value map for storing custom data with the alert.
// This data is passed through to webhook payloads and can be used for tracking or correlation purposes.
// The Slack Manager does not interpret this data.
Metadata map[string]any `json:"metadata"`
// Deprecated: FailOnRateLimitError is no longer in use.
FailOnRateLimitError bool `json:"failOnRateLimitError"`
}
Alert represents a single alert that can be sent to the Slack Manager. Alerts with the same CorrelationID are grouped together in issues by the Slack Manager.
func NewAlert ¶
func NewAlert(severity AlertSeverity) *Alert
NewAlert returns an alert with the specified severity
func NewErrorAlert ¶
func NewErrorAlert() *Alert
NewErrorAlert returns an alert with the severity set to 'error'
func NewInfoAlert ¶
func NewInfoAlert() *Alert
NewInfoAlert returns an alert with the severity set to 'info'
func NewPanicAlert ¶
func NewPanicAlert() *Alert
NewPanicAlert returns an alert with the severity set to 'panic'
func NewResolvedAlert ¶
func NewResolvedAlert() *Alert
NewResolvedAlert returns an alert with the severity set to 'resolved'
func NewWarningAlert ¶
func NewWarningAlert() *Alert
NewWarningAlert returns an alert with the severity set to 'warning'
func (*Alert) Clean ¶
func (a *Alert) Clean()
Clean normalizes and sanitizes all alert fields. It trims whitespace, normalizes case where appropriate, truncates fields that exceed maximum lengths, and applies default values for empty or invalid fields (e.g., sets Severity to 'error' if empty). This method should be called before validation to ensure consistent data.
func (*Alert) UniqueID ¶
UniqueID returns a unique and deterministic ID for this alert, for database/storage purposes. The ID is based on certain fields of the alert, and is base64 encoded to ensure it is safe for use in URLs and as a database key.
func (*Alert) Validate ¶
Validate returns an error if one or more of the required fields are empty or invalid
func (*Alert) ValidateAutoResolve ¶
ValidateAutoResolve validates that AutoResolveSeconds is within the allowed range when IssueFollowUpEnabled is true.
func (*Alert) ValidateCorrelationID ¶
ValidateCorrelationID validates that CorrelationID, if set, does not exceed MaxCorrelationIDLength.
func (*Alert) ValidateEscalation ¶
ValidateEscalation validates all escalation points in the alert. It checks that the escalation count is within limits, delays are properly spaced, severities are valid for escalation, and Slack mentions and channels are valid.
func (*Alert) ValidateFields ¶
ValidateFields validates that the number of fields does not exceed MaxFieldCount.
func (*Alert) ValidateHeaderAndText ¶
ValidateHeaderAndText validates that at least one of Header or Text is non-empty. An alert must have either a header or text content to be meaningful.
func (*Alert) ValidateIcon ¶
ValidateIcon validates that IconEmoji, if set, matches the expected Slack emoji format ':emoji:'.
func (*Alert) ValidateIgnoreIfTextContains ¶
ValidateIgnoreIfTextContains validates that the IgnoreIfTextContains slice does not exceed the maximum count and that each item does not exceed the maximum length.
func (*Alert) ValidateLink ¶
ValidateLink validates that Link, if set, is a valid absolute URL with a scheme.
func (*Alert) ValidateSeverity ¶
ValidateSeverity validates that Severity is one of the allowed AlertSeverity values.
func (*Alert) ValidateSlackChannelIDAndRouteKey ¶
ValidateSlackChannelIDAndRouteKey validates that SlackChannelID and RouteKey are valid, if set. Both values are allowed to be empty (in which case a fallback mapping must exist in the API).
func (*Alert) ValidateWebhooks ¶
ValidateWebhooks validates all webhooks in the alert. It checks that the webhook count is within limits, all required fields are present, URLs are valid, IDs are unique, and all nested inputs are properly configured.
type AlertSeverity ¶
type AlertSeverity string
AlertSeverity represents the severity for a given alert
const ( // AlertPanic is used for panic situations (panic icon in Slack). AlertPanic AlertSeverity = "panic" // AlertError is used for error (critical) situations (red error icon in Slack). // Alerts with severity 'critical' are automatically converted to 'error'. AlertError AlertSeverity = "error" // AlertWarning is used for warning situations (yellow warning icon in Slack). AlertWarning AlertSeverity = "warning" // AlertResolved is used when a previous panic/error/warning situation has been resolved (and IssueFollowUpEnabled is true). // The previous icon is replaced with a green OK icon. // Not to be confused with an info alert! AlertResolved AlertSeverity = "resolved" // AlertInfo is used for pure info situations (blue info icon in Slack). // Typically used for fire-and-forget status messages, where IssueFollowUpEnabled is false. // Not to be confused with a resolved alert! AlertInfo AlertSeverity = "info" )
type ChannelProcessingState ¶
type ChannelProcessingState struct {
ChannelID string `json:"channelId"`
Created time.Time `json:"created"`
LastChannelActivity time.Time `json:"lastChannelActivity"`
LastProcessed time.Time `json:"lastProcessed"`
OpenIssues int `json:"openIssues"`
}
ChannelProcessingState represents the state of processing for a specific Slack channel, used internally by the Slack Manager. It tracks when the processing started and when it was last processed. This is used to prevent multiple instances of the Slack Manager from processing the same channel at the same time, and to ensure that processing is done at regular intervals.
func NewChannelProcessingState ¶
func NewChannelProcessingState(channelID string) *ChannelProcessingState
type DB ¶
type DB interface {
// Init initializes the database, for example by creating necessary tables or collections.
// Set skipSchemaValidation to true to skip schema validation.
Init(ctx context.Context, skipSchemaValidation bool) error
// SaveAlert saves an alert to the database (for auditing purposes).
// The same alert may be saved multiple times, in case of errors and retries.
//
// A database implementation can choose to skip saving the alerts, since they are never read by the manager.
SaveAlert(ctx context.Context, alert *Alert) error
// SaveIssue creates or updates a single issue in the database.
SaveIssue(ctx context.Context, issue Issue) error
// SaveIssues creates or updates multiple issues in the database.
SaveIssues(ctx context.Context, issues ...Issue) error
// MoveIssue moves an issue from one channel to another.
// This channel ID in the issue must match the targetChannelID.
// The sourceChannelID is used to find the existing issue in the database.
MoveIssue(ctx context.Context, issue Issue, sourceChannelID, targetChannelID string) error
// FindOpenIssueByCorrelationID finds a single open issue in the database, based on the provided channel ID and correlation ID.
//
// The database implementation should return an error if the query matches multiple issues, and [nil, nil] if no issue is found.
FindOpenIssueByCorrelationID(ctx context.Context, channelID, correlationID string) (string, json.RawMessage, error)
// FindIssueBySlackPostID finds a single issue in the database, based on the provided channel ID and Slack post ID.
//
// The database implementation should return an error if the query matches multiple issues, and [nil, nil] if no issue is found.
FindIssueBySlackPostID(ctx context.Context, channelID, postID string) (string, json.RawMessage, error)
// FindActiveChannels returns a list of all active channels in the database.
// An active channel is one that has at least one open issue.
// The returned list may be empty if no active channels are found.
FindActiveChannels(ctx context.Context) ([]string, error)
// LoadOpenIssuesInChannel loads all open (non-archived) issues from the database, for the specified channel ID.
// The returned list may be empty if no open issues are found in the channel.
LoadOpenIssuesInChannel(ctx context.Context, channelID string) (map[string]json.RawMessage, error)
// SaveMoveMapping creates or updates a single move mapping in the database.
SaveMoveMapping(ctx context.Context, moveMapping MoveMapping) error
// FindMoveMapping finds a single move mapping in the database, for the specified channel ID and correlation ID.
//
// The database implementation should return an error if the query matches multiple mappings, and [nil, nil] if no mapping is found.
FindMoveMapping(ctx context.Context, channelID, correlationID string) (json.RawMessage, error)
// DeleteMoveMapping deletes a single move mapping from the database, for the specified channel ID and correlation ID.
DeleteMoveMapping(ctx context.Context, channelID, correlationID string) error
// SaveChannelProcessingState creates or updates a single channel processing state in the database.
SaveChannelProcessingState(ctx context.Context, state *ChannelProcessingState) error
// FindChannelProcessingState finds a single channel processing state in the database, for the specified channel ID.
//
// The database implementation should return an error if the query matches multiple states, and [nil, nil] if no state is found.
FindChannelProcessingState(ctx context.Context, channelID string) (*ChannelProcessingState, error)
// DropAllData drops *all* data from the database.
// This is useful for testing purposes, to reset the database state.
// It should be used with caution, as it will remove all alerts, issues, move mappings, and processing states.
DropAllData(ctx context.Context) error
}
DB is an interface for interacting with the database. It must be implemented by any database driver used by the Slack Manager.
type Escalation ¶
type Escalation struct {
// Severity is the new severity of the issue, when the escalation is triggered.
Severity AlertSeverity `json:"severity"`
// DelaySeconds is the number of seconds since the issue was created (first alert received),
// before the escalation is triggered.
DelaySeconds int `json:"delaySeconds"`
// SlackMentions is a list of Slack mentions that should be added to the Slack post when the escalation is triggered.
SlackMentions []string `json:"slackMentions"`
// MoveToChannel is the ID or name of the Slack channel where the alert should be moved when the escalation is triggered.
MoveToChannel string `json:"moveToChannel"`
}
Escalation represents an escalation point for an issue.
type Field ¶
type Field struct {
// Title is the title of the field. It is automatically truncated at MaxFieldTitleLength characters.
Title string `json:"title"`
// Value is the value of the field. It is automatically truncated at MaxFieldValueLength characters.
Value string `json:"value"`
}
Field is an alert field.
type FifoQueueItem ¶
type FifoQueueItem struct {
// MessageID is the unique identifier of the message (as defined by the queue implementation).
MessageID string
// SlackChannelID is the ID of the Slack channel to which the message is related.
SlackChannelID string
// ReceiveTimestamp is the time when the message was received from the queue.
ReceiveTimestamp time.Time
// Body is the body of the message.
Body string
// Ack acknowledges the successful processing of the message, effectively removing it from the queue.
// This function cannot be nil.
//
// Ack does not accept a context parameter because acknowledgment is a commitment that must complete
// regardless of the caller's context state. Each queue implementation is responsible for managing
// its own timeouts and retry logic internally.
Ack func()
// Nack negatively acknowledges the processing of the message, thus making it available for reprocessing.
// This function cannot be nil.
//
// Nack does not accept a context parameter because negative acknowledgment is a commitment that must
// complete regardless of the caller's context state. Each queue implementation is responsible for
// managing its own timeouts and retry logic internally.
Nack func()
}
FifoQueueItem represents an item received from a FIFO queue.
type InMemoryDB ¶ added in v0.3.1
type InMemoryDB struct {
// contains filtered or unexported fields
}
InMemoryDB is an in-memory implementation of the DB interface. For TEST purposes only! Do not use in production!
func NewInMemoryDB ¶ added in v0.3.1
func NewInMemoryDB() *InMemoryDB
NewInMemoryDB creates a new InMemoryDB instance. For TEST purposes only! Do not use in production!
func (*InMemoryDB) DeleteMoveMapping ¶ added in v0.3.1
func (db *InMemoryDB) DeleteMoveMapping(_ context.Context, channelID, correlationID string) error
DeleteMoveMapping deletes a move mapping. No error is returned if the mapping does not exist.
func (*InMemoryDB) DropAllData ¶ added in v0.3.1
func (db *InMemoryDB) DropAllData(_ context.Context) error
DropAllData clears all data from the in-memory store.
func (*InMemoryDB) FindActiveChannels ¶ added in v0.3.1
func (db *InMemoryDB) FindActiveChannels(_ context.Context) ([]string, error)
FindActiveChannels returns a list of all channels that have at least one open issue.
func (*InMemoryDB) FindChannelProcessingState ¶ added in v0.3.1
func (db *InMemoryDB) FindChannelProcessingState(_ context.Context, channelID string) (*ChannelProcessingState, error)
FindChannelProcessingState finds a channel processing state by channel ID. Returns nil without an error if no state is found.
func (*InMemoryDB) FindIssueBySlackPostID ¶ added in v0.3.1
func (db *InMemoryDB) FindIssueBySlackPostID(_ context.Context, channelID, postID string) (string, json.RawMessage, error)
FindIssueBySlackPostID finds a single issue by channel ID and Slack post ID. Returns an error if channelID or postID are empty.
func (*InMemoryDB) FindMoveMapping ¶ added in v0.3.1
func (db *InMemoryDB) FindMoveMapping(_ context.Context, channelID, correlationID string) (json.RawMessage, error)
FindMoveMapping finds a move mapping by channel ID and correlation ID. Returns an error if channelID or correlationID are empty.
func (*InMemoryDB) FindOpenIssueByCorrelationID ¶ added in v0.3.1
func (db *InMemoryDB) FindOpenIssueByCorrelationID(_ context.Context, channelID, correlationID string) (string, json.RawMessage, error)
FindOpenIssueByCorrelationID finds a single open issue by channel ID and correlation ID. Returns an error if channelID or correlationID are empty, or if multiple open issues match.
func (*InMemoryDB) Init ¶ added in v0.3.1
func (db *InMemoryDB) Init(_ context.Context, _ bool) error
Init is a no-op for the in-memory implementation.
func (*InMemoryDB) LoadOpenIssuesInChannel ¶ added in v0.3.1
func (db *InMemoryDB) LoadOpenIssuesInChannel(_ context.Context, channelID string) (map[string]json.RawMessage, error)
LoadOpenIssuesInChannel loads all open issues for the specified channel.
func (*InMemoryDB) MoveIssue ¶ added in v0.3.1
func (db *InMemoryDB) MoveIssue(_ context.Context, issue Issue, sourceChannelID, targetChannelID string) error
MoveIssue moves an issue from one channel to another. Returns an error if sourceChannelID and targetChannelID are the same. If the issue does not exist in the store, this is a no-op.
func (*InMemoryDB) SaveAlert ¶ added in v0.3.1
func (db *InMemoryDB) SaveAlert(_ context.Context, alert *Alert) error
SaveAlert saves an alert to the in-memory store.
func (*InMemoryDB) SaveChannelProcessingState ¶ added in v0.3.1
func (db *InMemoryDB) SaveChannelProcessingState(_ context.Context, state *ChannelProcessingState) error
SaveChannelProcessingState creates or updates a channel processing state.
func (*InMemoryDB) SaveIssue ¶ added in v0.3.1
func (db *InMemoryDB) SaveIssue(_ context.Context, issue Issue) error
SaveIssue creates or updates a single issue.
func (*InMemoryDB) SaveIssues ¶ added in v0.3.1
func (db *InMemoryDB) SaveIssues(ctx context.Context, issues ...Issue) error
SaveIssues creates or updates multiple issues.
func (*InMemoryDB) SaveMoveMapping ¶ added in v0.3.1
func (db *InMemoryDB) SaveMoveMapping(_ context.Context, moveMapping MoveMapping) error
SaveMoveMapping creates or updates a move mapping.
type InMemoryFifoQueue ¶
type InMemoryFifoQueue struct {
// contains filtered or unexported fields
}
InMemoryFifoQueue is an in-memory FIFO queue implementation For TEST purposes only! Do not use in production!
func NewInMemoryFifoQueue ¶
func NewInMemoryFifoQueue(name string, bufferSize int, writeTimeout time.Duration) *InMemoryFifoQueue
NewInMemoryFifoQueue creates a new InMemoryFifoQueue instance. name is the name of the queue (for logging purposes only). bufferSize is the maximum number of items that can be stored in the queue. writeTimeout is the maximum time to wait for writing an item to the queue.
For TEST purposes only! Do not use in production!
func (*InMemoryFifoQueue) Name ¶
func (q *InMemoryFifoQueue) Name() string
Name returns the name of the queue.
func (*InMemoryFifoQueue) Receive ¶
func (q *InMemoryFifoQueue) Receive(ctx context.Context, sinkCh chan<- *FifoQueueItem) error
Receive receives messages from the queue, to the specified sink channel. An error is returned if the context is canceled. The sink channel is closed when the function returns.
type Issue ¶
type Issue interface {
json.Marshaler
// ChannelID returns the Slack channel ID that this issue belongs to.
ChannelID() string
// UniqueID returns a unique and deterministic ID for this issue, for database/storage purposes.
// The ID is based on certain issue fields, and is base64 encoded to ensure it is safe for use in URLs and as a database key.
UniqueID() string
// GetCorrelationID returns the correlation ID for this issue.
// The correlation ID is used to group related alerts together, and may or may not be client defined.
// It is not guaranteed to be unique across all issues, even in the same channel, and *cannot* be used as a unique issue identifier.
// It is not URL safe, and should thus be encoded before being used in URLs or as part of a database key.
GetCorrelationID() string
// IsOpen returns true if this issue is currently open (i.e. not archived).
// A resolved issue is still considered open until it is archived.
IsOpen() bool
// CurrentPostID returns the current Slack post ID associated with this issue.
// The value may change over time as the issue is updated.
// If the issue has no current post, it returns an empty string.
CurrentPostID() string
}
Issue represents an issue in a Slack channel. It is used to track alerts, their resolution status, and their association with Slack posts.
The actual implementation of this interface is internal to the Slack Manager, and may change without notice. The database implementation should only store a JSON representation of the issue, and should not depend on any specific fields or structures of the issue.
type Metrics ¶
type Metrics interface {
// RegisterCounter registers a counter metric with the given name, help text, and optional labels.
RegisterCounter(name, help string, labels ...string)
// RegisterGauge registers a gauge metric with the given name, help text, and optional labels.
RegisterGauge(name, help string, labels ...string)
// RegisterHistogram registers a histogram metric with the given name, help text, buckets, and optional labels.
RegisterHistogram(name, help string, buckets []float64, labels ...string)
// Add adds the given value to the specified counter metric, with optional label values.
Add(name string, value float64, labelValues ...string)
// Inc increments the specified counter metric by 1, with optional label values.
Inc(name string, labelValues ...string)
// Set sets the specified gauge metric to the given value, with optional label values.
Set(name string, value float64, labelValues ...string)
// Observe records an observation for the specified histogram metric, with optional label values.
Observe(name string, value float64, labelValues ...string)
}
type MoveMapping ¶
type MoveMapping interface {
json.Marshaler
// ChannelID returns the Slack channel ID that this move mapping belongs to (i.e. the channel where the move was initiated).
ChannelID() string
// UniqueID returns a unique and deterministic ID for this move mapping, for database/storage purposes.
// The ID is based on the original channel and the correlation ID, and is base64 encoded to ensure it is safe for use in URLs and as a database key.
UniqueID() string
// GetCorrelationID returns the correlation ID that this move mapping is associated with, for the given channel.
// It is not URL safe, and should thus be encoded before being used in URLs or as part of a database key.
GetCorrelationID() string
}
MoveMapping represents an issue that has been moved from one channel to another, based on the correlation ID. It is used to ensure that new alerts with the same correlation ID are not processed in the original channel, but instead are processed in the new channel.
The actual implementation of this interface is internal to the Slack Manager, and may change without notice. The database implementation should only store a JSON representation of the move mapping, and should not depend on any specific fields or structure of the move mapping.
type NoopLogger ¶
type NoopLogger struct{}
func (*NoopLogger) Debug ¶
func (l *NoopLogger) Debug(msg string)
func (*NoopLogger) Debugf ¶
func (l *NoopLogger) Debugf(format string, args ...any)
func (*NoopLogger) Error ¶
func (l *NoopLogger) Error(msg string)
func (*NoopLogger) Errorf ¶
func (l *NoopLogger) Errorf(format string, args ...any)
func (*NoopLogger) Info ¶
func (l *NoopLogger) Info(msg string)
func (*NoopLogger) Infof ¶
func (l *NoopLogger) Infof(format string, args ...any)
func (*NoopLogger) WithFields ¶
func (l *NoopLogger) WithFields(fields map[string]any) Logger
type NoopMetrics ¶
type NoopMetrics struct{}
NoopMetrics is a no-op implementation of the Metrics interface. It does not record any metrics. Use if no metrics are needed.
func (*NoopMetrics) Inc ¶
func (m *NoopMetrics) Inc(_ string, _ ...string)
func (*NoopMetrics) RegisterCounter ¶
func (m *NoopMetrics) RegisterCounter(_, _ string, _ ...string)
func (*NoopMetrics) RegisterGauge ¶
func (m *NoopMetrics) RegisterGauge(_, _ string, _ ...string)
func (*NoopMetrics) RegisterHistogram ¶
func (m *NoopMetrics) RegisterHistogram(_, _ string, _ []float64, _ ...string)
type Webhook ¶
type Webhook struct {
// ID is the unique identifier for this webhook within the alert.
// It must be unique among all webhooks in the same alert.
// Maximum length: MaxWebhookIDLength characters.
ID string `json:"id"`
// URL specifies the target for the webhook when the button is clicked.
// For HTTP webhooks, this must be a valid absolute URL starting with http:// or https://.
// For custom webhook handlers registered in the Slack Manager app, this can be an arbitrary
// ASCII string identifier that the handler recognizes.
// The field name "URL" is retained for backwards compatibility.
// Maximum length: MaxWebhookURLLength characters.
URL string `json:"url"`
// ConfirmationText is the text displayed in a confirmation dialog before triggering the webhook.
// If empty, no confirmation dialog is shown and the webhook is triggered immediately.
// Maximum length: MaxWebhookConfirmationTextLength characters.
ConfirmationText string `json:"confirmationText"`
// ButtonText is the label displayed on the button in Slack.
// This field is required.
// Maximum length: MaxWebhookButtonTextLength characters.
ButtonText string `json:"buttonText"`
// ButtonStyle determines the visual appearance of the button in Slack.
// Valid values are defined by WebhookButtonStyle constants.
// If empty, the default Slack button style is used.
ButtonStyle WebhookButtonStyle `json:"buttonStyle"`
// AccessLevel controls who can click this webhook button.
// Valid values are defined by WebhookAccessLevel constants.
// If empty, anyone in the channel can trigger the webhook.
AccessLevel WebhookAccessLevel `json:"accessLevel"`
// DisplayMode controls when the webhook button is visible.
// Valid values are defined by WebhookDisplayMode constants.
// If empty, the button is always visible.
DisplayMode WebhookDisplayMode `json:"displayMode"`
// Payload is a map of key-value pairs sent in the HTTP POST body when the webhook is triggered.
// Alert metadata and input values are merged into this payload.
// Maximum of MaxWebhookPayloadCount items.
Payload map[string]any `json:"payload"`
// PlainTextInput defines text input fields shown in the webhook's modal dialog.
// User-entered values are included in the webhook payload.
// Maximum of MaxWebhookPlainTextInputCount inputs.
PlainTextInput []*WebhookPlainTextInput `json:"plainTextInput"`
// CheckboxInput defines checkbox groups shown in the webhook's modal dialog.
// Selected values are included in the webhook payload.
// Maximum of MaxWebhookCheckboxInputCount inputs.
CheckboxInput []*WebhookCheckboxInput `json:"checkboxInput"`
}
Webhook represents an interactive button that appears on the Slack post. When clicked, it triggers an HTTP POST request to the specified URL (for http/https URLs), or invokes a custom webhook handler registered in the Slack Manager app.
type WebhookAccessLevel ¶
type WebhookAccessLevel string
WebhookAccessLevel is the access level required to invoke a webhook.
const ( // WebhookAccessLevelGlobalAdmins indicates that a webhook is available only to Slack Manager global admins. WebhookAccessLevelGlobalAdmins WebhookAccessLevel = "global_admins" // WebhookAccessLevelChannelAdmins indicates that a webhook is available to the Slack Manager channel admins (and global admins). WebhookAccessLevelChannelAdmins WebhookAccessLevel = "channel_admins" // WebhookAccessLevelChannelMembers indicates that a webhook is available to all members in a channel. WebhookAccessLevelChannelMembers WebhookAccessLevel = "channel_members" )
type WebhookButtonStyle ¶
type WebhookButtonStyle string
WebhookButtonStyle represents a webhook button style.
const ( // WebhookButtonStylePrimary represents Slack button style 'primary'. WebhookButtonStylePrimary WebhookButtonStyle = "primary" // WebhookButtonStyleDanger represents Slack button style 'danger'. WebhookButtonStyleDanger WebhookButtonStyle = "danger" )
type WebhookCallback ¶
type WebhookCallback struct {
ID string `json:"id"`
UserID string `json:"userId"`
UserRealName string `json:"userRealName"`
ChannelID string `json:"channelId"`
MessageID string `json:"messageId"`
Timestamp time.Time `json:"timestamp"`
Input map[string]string `json:"input"`
CheckboxInput map[string][]string `json:"checkboxInput"`
Payload map[string]any `json:"payload"`
}
func (*WebhookCallback) GetCheckboxInputSelectedValues ¶
func (w *WebhookCallback) GetCheckboxInputSelectedValues(key string) []string
func (*WebhookCallback) GetInputValue ¶
func (w *WebhookCallback) GetInputValue(key string) string
func (*WebhookCallback) GetPayloadBool ¶
func (w *WebhookCallback) GetPayloadBool(key string, defaultValue bool) bool
func (*WebhookCallback) GetPayloadInt ¶
func (w *WebhookCallback) GetPayloadInt(key string, defaultValue int) int
func (*WebhookCallback) GetPayloadString ¶
func (w *WebhookCallback) GetPayloadString(key string) string
func (*WebhookCallback) GetPayloadValue ¶
func (w *WebhookCallback) GetPayloadValue(key string) any
type WebhookCheckboxInput ¶
type WebhookCheckboxInput struct {
// ID is the unique identifier for this checkbox group.
// It must be unique among all inputs (both text and checkbox) in the same webhook.
// The ID is used as the key in the webhook payload.
// Maximum length: MaxWebhookInputIDLength characters.
ID string `json:"id"`
// Label is the text displayed above the checkbox group.
// Maximum length: MaxWebhookInputLabelLength characters.
Label string `json:"label"`
// Options is the list of checkbox options available in this group.
// Maximum of MaxWebhookCheckboxOptionCount options.
Options []*WebhookCheckboxOption `json:"options"`
}
WebhookCheckboxInput represents a group of checkboxes in a webhook's modal dialog. Selected option values are included in the webhook payload as an array with the field ID as the key.
type WebhookCheckboxOption ¶
type WebhookCheckboxOption struct {
// Value is the value included in the webhook payload when this option is selected.
// Must be unique among all options in the same checkbox group.
// Maximum length: MaxCheckboxOptionValueLength characters.
Value string `json:"value"`
// Text is the label displayed next to the checkbox.
// Maximum length: MaxWebhookCheckboxOptionTextLength characters.
Text string `json:"text"`
// Selected determines whether this checkbox is pre-selected when the modal opens.
Selected bool `json:"selected"`
}
WebhookCheckboxOption represents a single checkbox option within a WebhookCheckboxInput.
type WebhookDisplayMode ¶
type WebhookDisplayMode string
WebhookDisplayMode represents a display mode for a webhook button.
const ( // WebhookDisplayModeAlways means that the webhook button is always displayed, regardless of issue state. WebhookDisplayModeAlways WebhookDisplayMode = "always" // WebhookDisplayModeOpenIssue means that the webhook button is displayed for open issues only (error, warning etc). WebhookDisplayModeOpenIssue WebhookDisplayMode = "open_issue" // WebhookDisplayModeResolvedIssue means that the webhook button is displayed for resolved issues only. WebhookDisplayModeResolvedIssue WebhookDisplayMode = "resolved_issue" )
type WebhookPlainTextInput ¶
type WebhookPlainTextInput struct {
// ID is the unique identifier for this input field.
// It must be unique among all inputs (both text and checkbox) in the same webhook.
// The ID is used as the key in the webhook payload.
// Maximum length: MaxWebhookInputIDLength characters.
ID string `json:"id"`
// Description is the placeholder text shown in the input field before the user types.
// Maximum length: MaxWebhookInputDescriptionLength characters.
Description string `json:"description"`
// MinLength is the minimum number of characters required for the input.
// Must be >= 0 and <= MaxLength.
MinLength int `json:"minLength"`
// MaxLength is the maximum number of characters allowed for the input.
// Must be >= MinLength and <= MaxWebhookInputTextLength.
MaxLength int `json:"maxLength"`
// Multiline determines whether the input field allows multiple lines of text.
// If true, a larger textarea is shown instead of a single-line input.
Multiline bool `json:"multiline"`
// InitialValue is the default text pre-filled in the input field.
// Must satisfy the MinLength and MaxLength constraints.
InitialValue string `json:"initialValue"`
}
WebhookPlainTextInput represents a text input field in a webhook's modal dialog. The user's input is included in the webhook payload with the field ID as the key.