Openstatus
www.openstatus.dev
1package tinybird
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "fmt"
8 "net/http"
9 "net/url"
10 "os"
11
12 "github.com/rs/zerolog/log"
13)
14
15// Datasource names for Tinybird events
16const (
17 DatasourceHTTP = "ping_response__v8"
18 DatasourceTCP = "tcp_response__v0"
19 DatasourceDNS = "tcp_dns__v0"
20)
21
22func getBaseURL() string {
23 // Use local Tinybird container if available (Docker/self-hosted)
24 // https://www.tinybird.co/docs/api-reference
25 if tinybirdURL := os.Getenv("TINYBIRD_URL"); tinybirdURL != "" {
26 return tinybirdURL + "/v0/events"
27 }
28 return "https://api.tinybird.co/v0/events"
29}
30
31type Client interface {
32 SendEvent(ctx context.Context, event any, dataSourceName string) error
33}
34
35type client struct {
36 httpClient *http.Client
37 apiKey string
38 baseURL string
39}
40
41func NewClient(httpClient *http.Client, apiKey string) Client {
42 return client{
43 httpClient: httpClient,
44 apiKey: apiKey,
45 baseURL: getBaseURL(),
46 }
47}
48
49func (c client) SendEvent(ctx context.Context, event any, dataSourceName string) error {
50 requestURL, err := url.Parse(c.baseURL)
51 if err != nil {
52 log.Ctx(ctx).Error().Err(err).Msg("unable to parse url")
53 return fmt.Errorf("unable to parse url: %w", err)
54 }
55
56 q := requestURL.Query()
57 q.Add("name", dataSourceName)
58 requestURL.RawQuery = q.Encode()
59
60 var payload bytes.Buffer
61 if err := json.NewEncoder(&payload).Encode(event); err != nil {
62 log.Ctx(ctx).Error().Err(err).Msg("unable to encode payload")
63 return fmt.Errorf("unable to encode payload: %w", err)
64 }
65
66 req, err := http.NewRequestWithContext(ctx, http.MethodPost, requestURL.String(), bytes.NewReader(payload.Bytes()))
67 if err != nil {
68 log.Ctx(ctx).Error().Err(err).Msg("unable to create request")
69 return fmt.Errorf("unable to create request: %w", err)
70 }
71 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
72
73 resp, err := c.httpClient.Do(req)
74 if err != nil {
75 log.Ctx(ctx).Error().Err(err).Msg("unable to send request")
76 return fmt.Errorf("unable to send request: %w", err)
77 }
78 defer resp.Body.Close()
79
80 if resp.StatusCode != http.StatusAccepted {
81 log.Ctx(ctx).Error().Str("status", resp.Status).Msg("unexpected status code")
82 return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
83 }
84
85 return nil
86}