sentry

package
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MaxHistoryEntries        = 20
	MaxAnswerLengthInContext = 500
)

Variables

This section is empty.

Functions

func BuildQuery

func BuildQuery(params map[string]string) string

BuildQuery formats a Sentry endpoint query string from a values bag, stripping empty values so we don't send `?query=`-style noise.

func CreateSentryCommands

func CreateSentryCommands() *cobra.Command

CreateSentryCommands builds the `clanker sentry` command tree. The ask subcommand is added separately by cmd/sentry.go (so cmd/ keeps its dependency on internal/ai out of this package).

func DecodeJSON

func DecodeJSON(body []byte, v any) error

DecodeJSON unmarshals body into v, returning a clearer error than the raw json package message when the response is e.g. an HTML login redirect.

func ParseNextCursor

func ParseNextCursor(resp *http.Response) string

func ResolveAuthToken

func ResolveAuthToken() string

ResolveAuthToken returns the configured Sentry User Auth Token, checking config first then environment, mirroring how cloudflare/client.go resolves its credentials.

func ResolveDefaultProject

func ResolveDefaultProject() string

func ResolveHost

func ResolveHost() string

ResolveHost returns the Sentry host, defaulting to sentry.io. Self-hosted users set this to their on-prem URL host; EU single-tenant users to `<org>.sentry.io`.

func ResolveOrgSlug

func ResolveOrgSlug() string

Types

type APIError

type APIError struct {
	Status int
	Body   string
	Detail string
}

APIError carries an HTTP-status-aware error from the upstream API.

func (*APIError) Error

func (e *APIError) Error() string

type AccountStatus

type AccountStatus struct {
	Timestamp        time.Time `json:"timestamp"`
	OrganizationSlug string    `json:"organization_slug"`
	ProjectCount     int       `json:"project_count"`
	UnresolvedCount  int       `json:"unresolved_count"`
	ErrorCount24h    int       `json:"error_count_24h"`
}

AccountStatus is the at-a-glance snapshot the ask command stashes in conversation history so follow-up questions can be answered with orientation context (project count, recent error volume) without re-fetching everything.

func GatherAccountStatus

func GatherAccountStatus(ctx context.Context, c *Client, orgSlug string) (*AccountStatus, error)

GatherAccountStatus collects an at-a-glance snapshot for the conversation history. The three Sentry calls run concurrently — `ask` cold-start latency is dominated by these network round-trips and they're independent. Errors are non-fatal: we want a partial snapshot rather than blocking the ask command on a single flaky endpoint.

type Client

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

Client is a thin REST wrapper around the Sentry management API.

Auth is Bearer-token via the User Auth Token (Settings → Account → API). We hit https://{host}/api/0/ directly with net/http — there is no official Go management SDK; getsentry/sentry-go is for error reporting only.

func NewClient

func NewClient(authToken, orgSlug, host string, debug bool) (*Client, error)

NewClient returns a Client. orgSlug is optional — many endpoints scope by org but a handful (list orgs) don't. Empty host falls back to sentry.io.

func (*Client) AssignIssue

func (c *Client) AssignIssue(ctx context.Context, orgSlug, issueID, assignee string) error

AssignIssue assigns a single issue to a username.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the canonical base for Sentry REST calls. Always trailing `/api/0` with no slash; callers prepend `/...` paths.

func (*Client) CreateIssueAlertRule

func (c *Client) CreateIssueAlertRule(ctx context.Context, orgSlug, projectSlug string, rule any) (*IssueAlertRule, error)

CreateIssueAlertRule posts a new issue alert rule. The body is passed through as-is — the upstream schema for conditions/filters/actions is large enough that we don't model it strictly, leaving callers free to construct the payload from documentation.

func (*Client) Debug

func (c *Client) Debug() bool

func (*Client) DeleteIssueAlertRule

func (c *Client) DeleteIssueAlertRule(ctx context.Context, orgSlug, projectSlug, ruleID string) error

DeleteIssueAlertRule removes an issue alert rule.

func (*Client) Do

func (c *Client) Do(ctx context.Context, method, path string, body any) (*http.Response, []byte, error)

Do executes a Sentry API call with exponential backoff on 429s, honoring Retry-After and X-Sentry-Rate-Limit-Reset. path must begin with `/`. If body is non-nil it is JSON-marshalled.

func (*Client) GetEvent

func (c *Client) GetEvent(ctx context.Context, orgSlug, projectSlug, eventID string) (*Event, error)

GetEvent fetches a single event by eventID within a project. eventID is the 32-char hex string (not the numeric primary key).

func (*Client) GetIssue

func (c *Client) GetIssue(ctx context.Context, issueID string) (*Issue, error)

GetIssue fetches a single issue by ID.

func (*Client) GetIssueEvents

func (c *Client) GetIssueEvents(ctx context.Context, issueID string, limit int) ([]Event, error)

GetIssueEvents returns events for a given issue, newest first. Limit caps the number returned (Sentry's per-page max is 100).

func (*Client) GetMonitor

func (c *Client) GetMonitor(ctx context.Context, orgSlug, monitorSlug string) (*Monitor, error)

GetMonitor fetches a single monitor by slug.

func (*Client) GetMonitorCheckins

func (c *Client) GetMonitorCheckins(ctx context.Context, orgSlug, monitorSlug string, limit int) ([]MonitorCheckin, error)

GetMonitorCheckins returns recent check-ins for a monitor.

func (*Client) GetOrganization

func (c *Client) GetOrganization(ctx context.Context, slug string) (*Organization, error)

GetOrganization fetches a single org by slug.

func (*Client) GetProject

func (c *Client) GetProject(ctx context.Context, orgSlug, projectSlug string) (*Project, error)

GetProject fetches a single project.

func (*Client) GetProjectStats

func (c *Client) GetProjectStats(ctx context.Context, orgSlug, projectSlug, stat, resolution string) ([]ProjectStatsPoint, error)

GetProjectStats returns event-volume buckets for a project. `stat` is typically "received" or "rejected"; `resolution` is "10s" | "1h" | "1d".

func (*Client) GetRelease

func (c *Client) GetRelease(ctx context.Context, orgSlug, projectSlug, version string) (*Release, error)

GetRelease fetches a single release by version.

func (*Client) GetReleaseHealth

func (c *Client) GetReleaseHealth(ctx context.Context, orgSlug, projectSlug, version string) (*SessionsResponse, error)

GetReleaseHealth returns crash-free session/user metrics for a release. Uses /organizations/{org}/sessions/ with the v2 sessions endpoint.

func (*Client) Host

func (c *Client) Host() string

func (*Client) IgnoreIssues

func (c *Client) IgnoreIssues(ctx context.Context, orgSlug string, ids []string) error

IgnoreIssues marks issues as ignored.

func (*Client) ListIssueAlertRules

func (c *Client) ListIssueAlertRules(ctx context.Context, orgSlug, projectSlug string) ([]IssueAlertRule, error)

ListIssueAlertRules returns project-scoped issue alert rules (the legacy /rules/ endpoint). For metric alerts, use ListMetricAlertRules.

func (*Client) ListIssues

func (c *Client) ListIssues(ctx context.Context, orgSlug string, opts IssueListOptions) ([]Issue, string, error)

ListIssues fetches a page of issues. Pagination is exposed via the returned NextCursor — callers that want all pages can iterate.

func (*Client) ListMembers

func (c *Client) ListMembers(ctx context.Context, orgSlug string) ([]Member, error)

ListMembers returns members in an org.

func (*Client) ListMetricAlertRules

func (c *Client) ListMetricAlertRules(ctx context.Context, orgSlug string) ([]MetricAlertRule, error)

ListMetricAlertRules returns org-scoped metric alert rules.

func (*Client) ListMonitors

func (c *Client) ListMonitors(ctx context.Context, orgSlug string) ([]Monitor, error)

ListMonitors returns Sentry Crons monitors for an org.

func (*Client) ListOrganizations

func (c *Client) ListOrganizations(ctx context.Context) ([]Organization, error)

ListOrganizations returns the auth-token holder's accessible orgs. Paginates internally and accumulates all pages.

func (*Client) ListProjectEvents

func (c *Client) ListProjectEvents(ctx context.Context, orgSlug, projectSlug string, limit int) ([]Event, error)

ListProjectEvents returns recent events for a project (newest first). limit caps the page size (max 100 per Sentry's API).

func (*Client) ListProjects

func (c *Client) ListProjects(ctx context.Context, orgSlug string) ([]Project, error)

ListProjects returns all projects in the client's org. orgSlug overrides the client's default when non-empty.

func (*Client) ListReleases

func (c *Client) ListReleases(ctx context.Context, orgSlug, projectSlug string) ([]Release, error)

ListReleases returns recent releases for a project (newest first).

func (*Client) ListTeams

func (c *Client) ListTeams(ctx context.Context, orgSlug string) ([]Team, error)

ListTeams returns teams in an org.

func (*Client) MuteMonitor

func (c *Client) MuteMonitor(ctx context.Context, orgSlug, monitorSlug string) error

MuteMonitor marks a monitor as muted (no alerts on missed check-ins).

func (*Client) OrgSlug

func (c *Client) OrgSlug() string

func (*Client) ResolveIssues

func (c *Client) ResolveIssues(ctx context.Context, orgSlug string, ids []string) error

ResolveIssues is a convenience wrapper.

func (*Client) SetHTTPClient

func (c *Client) SetHTTPClient(hc *http.Client)

SetHTTPClient lets tests swap in an httptest.Server-backed client.

func (*Client) UnmuteMonitor

func (c *Client) UnmuteMonitor(ctx context.Context, orgSlug, monitorSlug string) error

UnmuteMonitor restores alerting on a previously muted monitor.

func (*Client) UpdateIssueAlertRule

func (c *Client) UpdateIssueAlertRule(ctx context.Context, orgSlug, projectSlug, ruleID string, rule any) (*IssueAlertRule, error)

UpdateIssueAlertRule replaces an existing issue alert rule.

func (*Client) UpdateIssues

func (c *Client) UpdateIssues(ctx context.Context, orgSlug string, ids []string, update IssueUpdate) error

UpdateIssues bulk-mutates issues. IDs are passed as repeated `id=` query params; the body carries the new status/assignment.

type ConversationEntry

type ConversationEntry struct {
	Timestamp time.Time `json:"timestamp"`
	Question  string    `json:"question"`
	Answer    string    `json:"answer"`
	OrgSlug   string    `json:"org_slug"`
}

ConversationEntry is a single Q&A turn against the Sentry ask agent.

type ConversationHistory

type ConversationHistory struct {
	Entries    []ConversationEntry `json:"entries"`
	OrgSlug    string              `json:"org_slug"`
	LastStatus *AccountStatus      `json:"last_status,omitempty"`
	// contains filtered or unexported fields
}

ConversationHistory persists Sentry ask sessions per-org under ~/.clanker/sentry-{orgSlug}.json — same pattern as the Cloudflare history.

func NewConversationHistory

func NewConversationHistory(orgSlug string) *ConversationHistory

func (*ConversationHistory) AddEntry

func (h *ConversationHistory) AddEntry(question, answer, orgSlug string)

func (*ConversationHistory) GetAccountStatusContext

func (h *ConversationHistory) GetAccountStatusContext() string

func (*ConversationHistory) GetRecentContext

func (h *ConversationHistory) GetRecentContext(maxEntries int) string

GetRecentContext renders the last maxEntries turns as a single string suitable for prepending to an LLM prompt. Answers are truncated so a single long response can't crowd out the user's actual question.

func (*ConversationHistory) Load

func (h *ConversationHistory) Load() error

func (*ConversationHistory) Save

func (h *ConversationHistory) Save() error

func (*ConversationHistory) UpdateAccountStatus

func (h *ConversationHistory) UpdateAccountStatus(status *AccountStatus)

UpdateAccountStatus stashes the latest snapshot so follow-up questions can reference orientation context (project count, unresolved count) without re-fetching.

type Event

type Event struct {
	ID           string                     `json:"id"`
	EventID      string                     `json:"eventID"`
	GroupID      string                     `json:"groupID"`
	ProjectID    string                     `json:"projectID"`
	Title        string                     `json:"title"`
	Message      string                     `json:"message"`
	Platform     string                     `json:"platform"`
	Type         string                     `json:"type"`
	DateCreated  time.Time                  `json:"dateCreated"`
	DateReceived time.Time                  `json:"dateReceived"`
	Tags         []Tag                      `json:"tags"`
	User         json.RawMessage            `json:"user"`
	Entries      []EventEntry               `json:"entries"`
	Contexts     map[string]json.RawMessage `json:"contexts"`
}

type EventEntry

type EventEntry struct {
	Type string          `json:"type"`
	Data json.RawMessage `json:"data"`
}

Event entries are a polymorphic array — each entry has a `type` field (exception, breadcrumbs, request, message, ...) and a `data` payload whose shape depends on the type. Callers that need to introspect should dispatch on Type and unmarshal Data with the appropriate concrete struct.

type Issue

type Issue struct {
	ID            string          `json:"id"`
	ShortID       string          `json:"shortId"`
	Title         string          `json:"title"`
	Culprit       string          `json:"culprit"`
	Permalink     string          `json:"permalink"`
	Logger        string          `json:"logger"`
	Level         string          `json:"level"`
	Status        string          `json:"status"`
	StatusDetails json.RawMessage `json:"statusDetails"`
	IsPublic      bool            `json:"isPublic"`
	Platform      string          `json:"platform"`
	Project       *Project        `json:"project,omitempty"`
	Type          string          `json:"type"`
	Metadata      map[string]any  `json:"metadata"`
	NumComments   int             `json:"numComments"`
	AssignedTo    json.RawMessage `json:"assignedTo"`
	IsBookmarked  bool            `json:"isBookmarked"`
	IsSubscribed  bool            `json:"isSubscribed"`
	HasSeen       bool            `json:"hasSeen"`
	FirstSeen     time.Time       `json:"firstSeen"`
	LastSeen      time.Time       `json:"lastSeen"`
	Count         string          `json:"count"`
	UserCount     int             `json:"userCount"`
}

type IssueAlertRule

type IssueAlertRule struct {
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	Environment string            `json:"environment"`
	Frequency   int               `json:"frequency"`
	ActionMatch string            `json:"actionMatch"`
	FilterMatch string            `json:"filterMatch"`
	Conditions  []json.RawMessage `json:"conditions"`
	Filters     []json.RawMessage `json:"filters"`
	Actions     []json.RawMessage `json:"actions"`
	DateCreated time.Time         `json:"dateCreated"`
	CreatedBy   json.RawMessage   `json:"createdBy"`
}

IssueAlertRule is a Sentry issue alert rule (the legacy /rules/ endpoint). MetricAlertRule (the newer /alert-rules/ endpoint) has a different shape.

type IssueListOptions

type IssueListOptions struct {
	Query       string
	Environment string
	StatsPeriod string // e.g. "24h", "14d"
	Project     string // project ID (numeric string) — multiple allowed via repeated params; we keep single for simplicity
	Sort        string // "new" | "priority" | "freq" | "user"
	Limit       int
	Cursor      string
}

IssueListOptions controls /organizations/{org}/issues/. Query is Sentry's search-syntax string (e.g. `is:unresolved level:error environment:prod`) and is passed verbatim — we do no client-side parsing.

type IssueStatus

type IssueStatus string

IssueStatus is the set of accepted values for IssueUpdate.Status. Defining them as constants prevents typos (`"resolve"` would silently no-op against Sentry) and lets callers refer to them by name.

const (
	IssueStatusResolved   IssueStatus = "resolved"
	IssueStatusIgnored    IssueStatus = "ignored"
	IssueStatusUnresolved IssueStatus = "unresolved"
)

type IssueUpdate

type IssueUpdate struct {
	Status     IssueStatus `json:"status,omitempty"`
	AssignedTo string      `json:"assignedTo,omitempty"`
}

IssueUpdate is the payload Sentry expects on PUT /organizations/{org}/issues/. AssignedTo is a username string (or "" to clear).

type Member

type Member struct {
	ID    string          `json:"id"`
	Email string          `json:"email"`
	Name  string          `json:"name"`
	Role  string          `json:"role"`
	User  json.RawMessage `json:"user"`
}

type MetricAlertRule

type MetricAlertRule struct {
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	Environment string            `json:"environment"`
	DataSet     string            `json:"dataset"`
	Query       string            `json:"query"`
	Aggregate   string            `json:"aggregate"`
	TimeWindow  float64           `json:"timeWindow"`
	Threshold   float64           `json:"threshold"`
	Triggers    []json.RawMessage `json:"triggers"`
	Projects    []string          `json:"projects"`
	DateCreated time.Time         `json:"dateCreated"`
}

type Monitor

type Monitor struct {
	Slug    string          `json:"slug"`
	Name    string          `json:"name"`
	Status  string          `json:"status"`
	Type    string          `json:"type"`
	IsMuted bool            `json:"isMuted"`
	Config  json.RawMessage `json:"config"`
	Project struct {
		Slug string `json:"slug"`
		Name string `json:"name"`
	} `json:"project"`
	DateCreated time.Time `json:"dateCreated"`
}

type MonitorCheckin

type MonitorCheckin struct {
	ID          string          `json:"id"`
	Status      string          `json:"status"`
	Duration    *float64        `json:"duration"`
	DateCreated time.Time       `json:"dateCreated"`
	Attachment  json.RawMessage `json:"attachment"`
}

type Organization

type Organization struct {
	ID          string    `json:"id"`
	Slug        string    `json:"slug"`
	Name        string    `json:"name"`
	DateCreated time.Time `json:"dateCreated"`
}

type Project

type Project struct {
	ID           string        `json:"id"`
	Slug         string        `json:"slug"`
	Name         string        `json:"name"`
	Platform     string        `json:"platform"`
	DateCreated  time.Time     `json:"dateCreated"`
	IsBookmarked bool          `json:"isBookmarked"`
	Organization *Organization `json:"organization,omitempty"`
}

type ProjectStatsPoint

type ProjectStatsPoint struct {
	Timestamp int64
	Count     int64
}

ProjectStatsPoint is one bucket of `/projects/{org}/{project}/stats/`. The endpoint returns tuples of [unix_seconds, count].

func (ProjectStatsPoint) MarshalJSON

func (p ProjectStatsPoint) MarshalJSON() ([]byte, error)

MarshalJSON keeps round-trip parity with the upstream tuple format so tests that re-encode a fixture and diff against the input don't drift.

func (*ProjectStatsPoint) UnmarshalJSON

func (p *ProjectStatsPoint) UnmarshalJSON(data []byte) error

UnmarshalJSON handles the tuple shape `[<unix>, <count>]`.

type Release

type Release struct {
	Version      string     `json:"version"`
	ShortVersion string     `json:"shortVersion"`
	Ref          string     `json:"ref"`
	URL          string     `json:"url"`
	DateCreated  time.Time  `json:"dateCreated"`
	DateReleased *time.Time `json:"dateReleased"`
	NewGroups    int        `json:"newGroups"`
	Projects     []struct {
		Slug string `json:"slug"`
		Name string `json:"name"`
	} `json:"projects"`
}

type SessionGroup

type SessionGroup struct {
	By     map[string]string    `json:"by"`
	Totals map[string]float64   `json:"totals"`
	Series map[string][]float64 `json:"series"`
}

SessionGroup carries release-health rollups returned by /organizations/{org}/sessions/. The shape is a tuple-array under `groups`; caller selects fields via the `field=` query (e.g. `sum(session)`).

type SessionsResponse

type SessionsResponse struct {
	Start     time.Time      `json:"start"`
	End       time.Time      `json:"end"`
	Intervals []time.Time    `json:"intervals"`
	Groups    []SessionGroup `json:"groups"`
}

type Tag

type Tag struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

type Team

type Team struct {
	ID          string    `json:"id"`
	Slug        string    `json:"slug"`
	Name        string    `json:"name"`
	IsMember    bool      `json:"isMember"`
	MemberCount int       `json:"memberCount"`
	DateCreated time.Time `json:"dateCreated"`
}

Jump to

Keyboard shortcuts

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