manager

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrChannelLockUnavailable = errors.New("channel lock unavailable")

ErrChannelLockUnavailable is returned when a channel lock cannot be obtained.

Functions

This section is empty.

Types

type ChannelLock

type ChannelLock interface {
	// Key returns the key associated with this lock.
	Key() string

	// Release releases the lock.
	// It does not accept a context parameter because releasing a lock is a commitment
	// that must complete regardless of the caller's context state. Each implementation
	// is responsible for managing its own timeouts internally.
	Release() error
}

ChannelLock represents a single lock on a channel.

type ChannelLocker

type ChannelLocker interface {
	// Obtain tries to obtain a lock for the given key (channel ID) with the specified TTL (time-to-live).
	// It will retry obtaining the lock until the maxWait duration is reached.
	// If the lock is successfully obtained, it returns a ChannelLock instance.
	// If the lock cannot be obtained within the maxWait duration, it returns ErrChannelLockUnavailable.
	Obtain(ctx context.Context, key string, ttl time.Duration, maxWait time.Duration) (ChannelLock, error)
}

ChannelLocker is an interface for obtaining and releasing distributed locks on channels. It is used to ensure that only one process can perform operations on a channel at a time. This is particularly useful in a distributed environment where multiple instances of the manager may be running. Use the RedisChannelLocker implementation for a production-ready solution, using Redis as the backing store. Use the NoopChannelLocker for testing or when locking is not required (i.e in a single instance setup).

type FifoQueue

type FifoQueue interface {
	// Name returns the name of the queue.
	Name() string

	// Send sends a single message to the queue.
	//
	// slackChannelID is the Slack channel to which the message belongs.
	// A queue implementation should use this value to partition the queue (i.e. group ID in an AWS SQS Fifo queue),
	// but it is not required.
	//
	// dedupID is a unique identifier for the message.
	// A queue implementation should use this value to deduplicate messages, but it is not required.
	//
	// body is the json formatted message body.
	Send(ctx context.Context, slackChannelID, dedupID, body string) error

	// Receive receives messages from the queue, until the context is cancelled.
	// Messages are sent to the provided channel.
	// The channel must be closed by the implementation before returning, typically when the context is cancelled or a fatal error occurs.
	Receive(ctx context.Context, sinkCh chan<- *types.FifoQueueItem) error
}

FifoQueue is an interface for interacting with a fifo queue.

type HTTPWebhookHandler

type HTTPWebhookHandler struct {
	// contains filtered or unexported fields
}

HTTPWebhookHandler is an implementation of the WebhookHandler interface that sends webhooks via HTTP. This is the default webhook handler when no other webhook handlers are configured.

func NewHTTPWebhookHandler

func NewHTTPWebhookHandler(logger types.Logger) *HTTPWebhookHandler

NewHTTPWebhookHandler creates a new HTTPWebhookHandler.

func (*HTTPWebhookHandler) HandleWebhook

func (h *HTTPWebhookHandler) HandleWebhook(ctx context.Context, target string, data *types.WebhookCallback, logger types.Logger) error

HandleWebhook sends the webhook data to the target URL via an HTTP POST request. It expects a successful HTTP status code (2xx) in response.

func (*HTTPWebhookHandler) ShouldHandleWebhook

func (h *HTTPWebhookHandler) ShouldHandleWebhook(_ context.Context, target string) bool

ShouldHandleWebhook returns true if the target is an HTTP or HTTPS URL. If other webhooks should handle certain URLs (e.g. SQS queue URLs), they must be registred *before* this handler.

func (*HTTPWebhookHandler) WithRequestTimeout

func (h *HTTPWebhookHandler) WithRequestTimeout(timeout time.Duration) *HTTPWebhookHandler

WithRequestTimeout sets the timeout for HTTP requests made by the webhook handler. The default timeout is 3 seconds.

type LocalRateLimitGate added in v0.4.0

type LocalRateLimitGate struct {
	// contains filtered or unexported fields
}

LocalRateLimitGate is an in-process RateLimitGate implementation suitable for single-instance deployments. For multi-instance deployments use RedisRateLimitGate so that all instances respect the same rate-limit window.

func NewLocalRateLimitGate added in v0.4.0

func NewLocalRateLimitGate(logger types.Logger, maxDrainWait time.Duration) *LocalRateLimitGate

NewLocalRateLimitGate creates a LocalRateLimitGate. Pass a positive maxDrainWait to override the default 30-second Socket Mode drain limit; zero or negative values use the default.

func (*LocalRateLimitGate) IsBlocked added in v0.4.0

func (g *LocalRateLimitGate) IsBlocked(_ context.Context) (bool, error)

IsBlocked reports whether the rate-limit window is currently active.

func (*LocalRateLimitGate) SetReadyCheck added in v0.4.0

func (g *LocalRateLimitGate) SetReadyCheck(fn func() bool)

SetReadyCheck registers the function used to determine when Socket Mode has no in-flight handlers. Called once from Manager.Run after the Slack client connects.

func (*LocalRateLimitGate) Signal added in v0.4.0

func (g *LocalRateLimitGate) Signal(_ context.Context, until time.Time) error

Signal records a rate-limit deadline. The blocked window is extended if until is later than any previously recorded deadline.

func (*LocalRateLimitGate) Wait added in v0.4.0

func (g *LocalRateLimitGate) Wait(ctx context.Context) error

Wait blocks until the rate-limit window has expired and Socket Mode is quiet. Returns immediately when the gate is not blocked.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager processes alerts from an alert queue and manages the lifecycle of Slack issues. It connects to Slack via Socket Mode, coordinates per-channel workers, and persists issue state through the injected database.

Create a Manager with New and start it with Manager.Run. Settings can be updated at runtime via Manager.UpdateSettings without restarting the service.

func New

func New(db types.DB, alertQueue FifoQueue, commandQueue FifoQueue, logger types.Logger, cfg *config.ManagerConfig) *Manager

New creates a Manager with the five required dependencies.

Optional dependencies can be configured via method chaining before calling Manager.Run:

func (*Manager) RegisterWebhookHandler

func (m *Manager) RegisterWebhookHandler(handler WebhookHandler) *Manager

RegisterWebhookHandler adds a WebhookHandler that is consulted whenever an issue has a webhook callback configured. Handlers are tried in registration order; the first one whose ShouldHandleWebhook returns true handles the call. Returns the Manager to allow method chaining.

func (*Manager) Run

func (m *Manager) Run(ctx context.Context) error

Run starts the Manager and blocks until ctx is cancelled or a fatal error occurs. It validates configuration, connects to Slack via Socket Mode, initialises the coordinator and per-channel managers, and starts the alert and command queue consumers. All goroutines are managed with an errgroup; any single fatal error triggers a shutdown of the entire group.

func (*Manager) UpdateSettings

func (m *Manager) UpdateSettings(settings *config.ManagerSettings) error

UpdateSettings hot-reloads manager settings without restarting. The new settings are validated before being applied; if validation fails the existing settings remain active and an error is returned. Passing nil replaces the current settings with zero-value defaults.

func (*Manager) WithCacheStore added in v0.5.0

func (m *Manager) WithCacheStore(cacheStore store.StoreInterface) *Manager

WithCacheStore sets the cache store. If not called, Manager.Run defaults to an in-process go-cache instance (unsuitable for multi-instance deployments unless SkipDatabaseCache is true in the config). Passing nil is a no-op.

func (*Manager) WithLocker added in v0.5.0

func (m *Manager) WithLocker(locker ChannelLocker) *Manager

WithLocker sets the ChannelLocker and recalculates the RateLimitGate. RedisChannelLocker produces a RedisRateLimitGate; all other lockers fall back to LocalRateLimitGate. Passing nil is a no-op.

func (*Manager) WithMetrics added in v0.5.0

func (m *Manager) WithMetrics(metrics types.Metrics) *Manager

WithMetrics sets the metrics implementation. If not called, a no-op implementation is used. Passing nil is a no-op.

func (*Manager) WithSettings added in v0.5.0

func (m *Manager) WithSettings(settings *config.ManagerSettings) *Manager

WithSettings sets the initial manager settings. If not called, zero-value settings are used. For runtime updates after Manager.Run is started, use Manager.UpdateSettings instead. Passing nil is a no-op.

type NoopChannelLock

type NoopChannelLock struct {
	// contains filtered or unexported fields
}

NoopChannelLock is a no-op implementation of the ChannelLock interface.

func (*NoopChannelLock) Key

func (n *NoopChannelLock) Key() string

Key returns the key associated with the no-op channel lock.

func (*NoopChannelLock) Release

func (n *NoopChannelLock) Release() error

Release is a no-op method that does nothing.

type NoopChannelLocker

type NoopChannelLocker struct{}

NoopChannelLocker is a no-op implementation of the ChannelLocker interface. It does not perform any locking and is used for testing or when locking is not required. Locking is typically always required when running in a distributed environment, such as in Kubernetes.

func (*NoopChannelLocker) Obtain

Obtain returns a no-op channel lock that does nothing.

type NoopRateLimitGate added in v0.4.0

type NoopRateLimitGate struct{}

NoopRateLimitGate is a no-op RateLimitGate for tests and single-instance deployments that do not need distributed rate-limit coordination.

func (*NoopRateLimitGate) IsBlocked added in v0.4.0

func (n *NoopRateLimitGate) IsBlocked(_ context.Context) (bool, error)

IsBlocked always returns false; the gate is never blocking.

func (*NoopRateLimitGate) SetReadyCheck added in v0.4.0

func (n *NoopRateLimitGate) SetReadyCheck(_ func() bool)

SetReadyCheck is a no-op; the gate does not use a ready-check function.

func (*NoopRateLimitGate) Signal added in v0.4.0

func (n *NoopRateLimitGate) Signal(_ context.Context, _ time.Time) error

Signal is a no-op; the gate never records a rate-limit deadline.

func (*NoopRateLimitGate) Wait added in v0.4.0

Wait is a no-op; the gate never blocks callers.

type RateLimitGate added in v0.4.0

type RateLimitGate interface {
	// Signal records that a rate limit was hit and that callers should block
	// until `until` has passed. Implementations extend the window if `until`
	// is later than any previously recorded deadline.
	Signal(ctx context.Context, until time.Time) error

	// Wait blocks until the gate is open and Socket Mode is quiet. It returns
	// immediately when the gate is not blocked. Context cancellation is
	// propagated as an error.
	Wait(ctx context.Context) error

	// IsBlocked reports whether the gate is currently blocking callers.
	IsBlocked(ctx context.Context) (bool, error)

	// SetReadyCheck registers a function that returns true when Socket Mode is
	// quiet (no in-flight event handlers). Must be called once from [Manager.Run]
	// after the Slack client is connected.
	SetReadyCheck(fn func() bool)
}

RateLimitGate coordinates a global pause across all channel managers when a Slack 429 is detected. Channel managers call [RateLimitGate.Wait] before processing each work item; the gate blocks until the rate-limit window has expired and the Socket Mode handler is quiet (no in-flight event handlers).

type RedisChannelLock

type RedisChannelLock struct {
	// contains filtered or unexported fields
}

RedisChannelLock is an implementation of the ChannelLock interface that uses Redis locks. It represents a lock on a specific channel. It is returned by RedisChannelLocker when a lock is successfully obtained. The lock can be released using RedisChannelLock.Release, which will remove the lock from Redis.

func (*RedisChannelLock) Key

func (l *RedisChannelLock) Key() string

Key returns the full Redis key of the lock, including its namespace prefix and channel ID segment. Returns an empty string if RedisChannelLock.Release has been called.

func (*RedisChannelLock) Release

func (l *RedisChannelLock) Release() error

Release releases the lock held by the RedisChannelLock instance. It removes the lock from Redis, allowing other instances to obtain the lock. If the lock is already released, or not held, it returns nil. It uses a new timeout context to ensure the release operation completes within a reasonable time frame, even if the caller's context is cancelled.

type RedisChannelLocker

type RedisChannelLocker struct {
	// contains filtered or unexported fields
}

RedisChannelLocker is an implementation of the ChannelLocker interface that uses Redis for distributed locking. It allows multiple instances of the manager to coordinate access to channels, ensuring that only one instance can perform operations on a channel at a time. The locker uses a key prefix to avoid conflicts with other locks in Redis. It also supports retry backoff for obtaining locks, allowing for configurable retry strategies. The default retry backoff is 2 seconds, but it can be configured using RedisChannelLocker.WithRetryBackoff.

func NewRedisChannelLocker

func NewRedisChannelLocker(client *redis.Client) *RedisChannelLocker

NewRedisChannelLocker creates a new RedisChannelLocker instance. It takes a Redis client as an argument, which is used to communicate with the Redis server. The client should be configured with the appropriate Redis server address and authentication details. The RedisChannelLocker can be configured with a custom retry backoff and key prefix using RedisChannelLocker.WithRetryBackoff and RedisChannelLocker.WithKeyPrefix. If no custom values are provided, it defaults to a retry backoff of 2 seconds and the default key prefix ("slack-manager:"). Lock keys have the form <keyPrefix>channel-lock:<channelID>.

func (*RedisChannelLocker) Obtain

func (r *RedisChannelLocker) Obtain(ctx context.Context, key string, ttl time.Duration, maxWait time.Duration) (ChannelLock, error)

Obtain tries to obtain a lock for the given key (channel ID) with a specified TTL (time to live). It uses a retry strategy based on the configured retry backoff and max wait duration. If the lock is successfully obtained, it returns a RedisChannelLock instance. If the lock cannot be obtained within the max wait duration, it returns ErrChannelLockUnavailable. The key is prefixed with the configured key prefix to avoid conflicts with other locks in Redis.

func (*RedisChannelLocker) WithKeyPrefix

func (r *RedisChannelLocker) WithKeyPrefix(prefix string) *RedisChannelLocker

WithKeyPrefix sets the Redis key namespace shared by all keys produced by this locker and its matching gate. The default prefix is "slack-manager:" (config.DefaultKeyPrefix). This prefix is used to avoid conflicts with other keys in Redis. An empty prefix means that keys will have no namespace.

func (*RedisChannelLocker) WithMaxDrainWait added in v0.5.0

func (r *RedisChannelLocker) WithMaxDrainWait(d time.Duration) *RedisChannelLocker

WithMaxDrainWait sets the maximum time to wait for Socket Mode to go quiet after a rate-limit window has expired before resuming with fail-open behaviour. If the duration is zero or negative, the default of 30 seconds is used.

func (*RedisChannelLocker) WithRetryBackoff

func (r *RedisChannelLocker) WithRetryBackoff(backoff time.Duration) *RedisChannelLocker

WithRetryBackoff sets the retry backoff duration for obtaining locks. If the backoff is less than or equal to zero, it defaults to 2 seconds.

type RedisFifoQueue

type RedisFifoQueue struct {
	*RedisFifoQueueProducer // Embedded for Send() and Name()
	// contains filtered or unexported fields
}

RedisFifoQueue implements the FifoQueue interface using Redis Streams. It uses one stream per Slack channel to ensure message ordering within a channel, mimicking the behavior of SQS FIFO message groups.

Multiple instances can consume from the same queue using Redis consumer groups, providing at-least-once delivery semantics. Messages for different channels can be processed in parallel across instances.

Strict per-channel ordering is enforced using distributed locks. When a consumer reads from a stream, it acquires a lock for that channel. Other consumers cannot read from that stream until the lock is released (after ack/nack). This ensures that messages for a single channel are processed strictly in order, even across multiple instances.

For write-only usage (e.g., API server), consider using RedisFifoQueueProducer instead.

func NewRedisFifoQueue

func NewRedisFifoQueue(client redis.UniversalClient, locker ChannelLocker, name string, logger types.Logger, opts ...RedisFifoQueueOption) *RedisFifoQueue

NewRedisFifoQueue creates a new RedisFifoQueue instance with full send and receive capabilities. The client should be a configured Redis client (can be a single node, sentinel, or cluster client). The locker is used to ensure strict per-channel ordering across multiple instances. The name is used as part of the Redis key prefix and for identification.

For write-only usage (e.g., API server), consider using NewRedisFifoQueueProducer instead.

func (*RedisFifoQueue) Init

func (q *RedisFifoQueue) Init() (*RedisFifoQueue, error)

Init initializes the RedisFifoQueue. It validates options and generates a unique consumer name for this instance.

func (*RedisFifoQueue) Receive

func (q *RedisFifoQueue) Receive(ctx context.Context, sinkCh chan<- *types.FifoQueueItem) error

Receive receives messages from the queue until the context is cancelled. Messages are sent to the provided channel, which is closed when Receive returns. The receiver uses the shared knownStreams map which is updated by Send() for immediate discovery of new streams created by in-process senders.

func (*RedisFifoQueue) Send

func (q *RedisFifoQueue) Send(ctx context.Context, slackChannelID, dedupID, body string) error

Send sends a message to the queue with additional support for in-process receivers. This overrides the embedded producer's Send to add: - Consumer group creation for new streams (enables immediate consumption) - Local stream tracking for fast discovery by in-process receiver - Notification to wake up in-process receiver immediately

type RedisFifoQueueOption

type RedisFifoQueueOption func(*RedisFifoQueueOptions)

RedisFifoQueueOption is a function that configures RedisFifoQueueOptions.

func WithClaimMinIdleTime

func WithClaimMinIdleTime(duration time.Duration) RedisFifoQueueOption

WithClaimMinIdleTime sets the minimum idle time before a message can be claimed.

func WithConsumerGroup

func WithConsumerGroup(group string) RedisFifoQueueOption

WithConsumerGroup sets the consumer group name.

func WithKeyPrefix

func WithKeyPrefix(prefix string) RedisFifoQueueOption

WithKeyPrefix sets the Redis key prefix for the queue.

func WithLockTTL

func WithLockTTL(ttl time.Duration) RedisFifoQueueOption

WithLockTTL sets the time-to-live for the per-stream lock.

func WithMaxStreamLength

func WithMaxStreamLength(length int64) RedisFifoQueueOption

WithMaxStreamLength sets the approximate maximum length of each stream.

func WithPollInterval

func WithPollInterval(interval time.Duration) RedisFifoQueueOption

WithPollInterval sets how long to wait between polling cycles when no messages are available.

func WithStreamInactivityTimeout

func WithStreamInactivityTimeout(timeout time.Duration) RedisFifoQueueOption

WithStreamInactivityTimeout sets how long a stream can be inactive before cleanup. Set to 0 to disable automatic cleanup.

func WithStreamRefreshInterval

func WithStreamRefreshInterval(interval time.Duration) RedisFifoQueueOption

WithStreamRefreshInterval sets how often to check for new streams.

type RedisFifoQueueOptions

type RedisFifoQueueOptions struct {
	// contains filtered or unexported fields
}

RedisFifoQueueOptions holds configuration for RedisFifoQueue.

type RedisFifoQueueProducer

type RedisFifoQueueProducer struct {
	// contains filtered or unexported fields
}

RedisFifoQueueProducer is a lightweight write-only queue producer. Use this when the service only needs to send messages (e.g., API server in a separate service). For full queue functionality including Receive(), use RedisFifoQueue instead.

This producer has minimal overhead: no consumer groups, no distributed locks, no in-process notification mechanism. It simply writes messages to Redis streams.

func NewRedisFifoQueueProducer

func NewRedisFifoQueueProducer(client redis.UniversalClient, name string, logger types.Logger, opts ...RedisFifoQueueOption) *RedisFifoQueueProducer

NewRedisFifoQueueProducer creates a new lightweight write-only queue producer. Use this when the service only needs to send messages and doesn't need to receive. The client should be a configured Redis client (can be a single node, sentinel, or cluster client). The name is used as part of the Redis key prefix and for identification.

func (*RedisFifoQueueProducer) Init

Init initializes the RedisFifoQueueProducer. It validates options and marks the producer as ready for use.

func (*RedisFifoQueueProducer) Name

func (p *RedisFifoQueueProducer) Name() string

Name returns the name of the queue.

func (*RedisFifoQueueProducer) Send

func (p *RedisFifoQueueProducer) Send(ctx context.Context, slackChannelID, _, body string) error

Send sends a message to the queue. The slackChannelID is used to route the message to a specific stream, ensuring ordering per channel. Note: The dedupID parameter is accepted for interface compatibility but is not used for deduplication. Redis Streams does not natively support message deduplication like SQS FIFO queues. Deduplication should be handled at the application level if needed.

type RedisRateLimitGate added in v0.4.0

type RedisRateLimitGate struct {
	// contains filtered or unexported fields
}

RedisRateLimitGate is a distributed RateLimitGate backed by Redis. It is suitable for multi-instance deployments where every instance must respect the same rate-limit window.

func NewRedisRateLimitGate added in v0.4.0

func NewRedisRateLimitGate(client redis.UniversalClient, logger types.Logger, keyPrefix string, maxDrainWait time.Duration) *RedisRateLimitGate

NewRedisRateLimitGate creates a RedisRateLimitGate. keyPrefix is prepended to the Redis key (defaults to config.DefaultKeyPrefix when empty). maxDrainWait is the maximum time to wait for Socket Mode to go quiet after the rate-limit window has expired; zero or negative values use the default of 30 seconds.

func (*RedisRateLimitGate) IsBlocked added in v0.4.0

func (g *RedisRateLimitGate) IsBlocked(ctx context.Context) (bool, error)

IsBlocked reads the rate-limit key from Redis and reports whether the stored deadline is still in the future. Returns false on Redis errors (fail-open).

func (*RedisRateLimitGate) SetReadyCheck added in v0.4.0

func (g *RedisRateLimitGate) SetReadyCheck(fn func() bool)

SetReadyCheck registers the function used to determine when Socket Mode has no in-flight handlers. Called once from Manager.Run after the Slack client connects.

func (*RedisRateLimitGate) Signal added in v0.4.0

func (g *RedisRateLimitGate) Signal(ctx context.Context, until time.Time) error

Signal stores the rate-limit deadline in Redis using a compare-and-swap Lua script, ensuring that all instances in a multi-instance deployment share the same window. The key's TTL is set to expire when the window closes, so RedisRateLimitGate.IsBlocked is self-cleaning.

func (*RedisRateLimitGate) Wait added in v0.4.0

func (g *RedisRateLimitGate) Wait(ctx context.Context) error

Wait polls Redis every 500 ms until the rate-limit key disappears, then waits for Socket Mode to drain. Redis errors are treated as non-blocking (fail-open) to avoid stalling channel managers when Redis is temporarily unavailable. Uses RedisRateLimitGate.IsBlocked for polling.

type SlackClient

type SlackClient interface {
	// GetChannelName returns the human-readable name of the given Slack channel, or an
	// empty string if the name cannot be resolved.
	GetChannelName(ctx context.Context, channelID string) string

	// IsAlertChannel reports whether channelID is a valid target for alerts.
	// Returns the channel name and any lookup error alongside the boolean result.
	IsAlertChannel(ctx context.Context, channelID string) (bool, string, error)

	// Update rebuilds and posts the consolidated issue summary message for all open
	// issues in the given channel.
	Update(ctx context.Context, channelID string, allChannelIssues []*models.Issue) error

	// UpdateSingleIssueWithThrottling updates the Slack message for one issue, applying
	// per-channel throttling when many issues are active to avoid flooding the API.
	UpdateSingleIssueWithThrottling(ctx context.Context, issue *models.Issue, reason string, issuesInChannel int) error

	// UpdateSingleIssue updates the Slack message for one issue without throttling.
	// Use [SlackClient.UpdateSingleIssueWithThrottling] when processing incoming alerts.
	UpdateSingleIssue(ctx context.Context, issue *models.Issue, reason string) error

	// Delete removes an issue's Slack message. When updateIfMessageHasReplies is true
	// and the post already has thread replies, the message is replaced with a tombstone
	// instead of being deleted outright. To delete an arbitrary post use [SlackClient.DeletePost].
	Delete(ctx context.Context, issue *models.Issue, reason string, updateIfMessageHasReplies bool) error

	// DeletePost deletes an arbitrary Slack message identified by channelID and timestamp.
	DeletePost(ctx context.Context, channelID, ts string) error
}

SlackClient is the internal Slack API interface used by the coordinator and channel managers to query channel state and post or update Slack messages. It is implemented by the internal slack.Client and is exposed here to allow injection of test doubles.

type WebhookHandler

type WebhookHandler interface {
	// ShouldHandleWebhook returns true if the handler should handle the specified webhook target.
	ShouldHandleWebhook(ctx context.Context, target string) bool

	// HandleWebhook handles the webhook target, with the specified callback data.
	// If successful, it should DEBUG log the send action and return nil.
	// If unsuccessful, it should return the error (without logging it).
	HandleWebhook(ctx context.Context, target string, data *types.WebhookCallback, logger types.Logger) error
}

WebhookHandler is an interface for handling alert webhooks (i.e. issue callbacks). The webhook may or may not be http based. More than one handler can be registered with a Manager. The first handler that returns true from ShouldHandleWebhook will be used to handle a given webhook.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL