glucose_exporter/api/api.go
2024-03-20 07:58:17 +00:00

144 lines
3.2 KiB
Go

package api
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"go.xsfx.dev/glucose_exporter/internal/epoch"
)
const BaseURL = "https://api.libreview.io/llu"
// client is our default HTTP client to use.
//
//nolint:gochecknoglobals
var client = http.Client{
Timeout: 2 * time.Second,
}
func request(
ctx context.Context,
method string,
url string,
token string,
data []byte,
) (*http.Response, error) {
req, err := http.NewRequest(method, url, bytes.NewReader(data))
if err != nil {
return &http.Response{}, fmt.Errorf("creating request: %w", err)
}
req.Header.Add("cache-control", "no-cache")
req.Header.Add("connection", "Keep-Alive")
req.Header.Add("content-type", "application/json")
req.Header.Add("product", "llu.android")
req.Header.Add("version", "4.7")
if token != "" {
req.Header.Add("authorization", "Bearer "+token)
}
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
return &http.Response{}, fmt.Errorf("doing the request: %w", err)
}
return resp, nil
}
type Ticket struct {
Token string `json:"token"`
Expires epoch.Epoch `json:"expires"`
}
type LoginResponse struct {
Data struct {
AuthTicket Ticket `json:"authTicket"`
} `json:"data"`
}
func Login(ctx context.Context, baseURL, username, password string) (LoginResponse, error) {
data := struct {
Email string `json:"email"`
Password string `json:"password"`
}{username, password}
d, err := json.Marshal(data)
if err != nil {
return LoginResponse{}, fmt.Errorf("marshalling data: %w", err)
}
url, err := url.JoinPath(baseURL, "/auth/login")
if err != nil {
return LoginResponse{}, fmt.Errorf("joining url: %w", err)
}
resp, err := request(ctx, http.MethodPost, url, "", d)
if err != nil {
return LoginResponse{}, fmt.Errorf("doing request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return LoginResponse{}, fmt.Errorf("reading body: %w", err)
}
var authTicket LoginResponse
if err := json.Unmarshal(body, &authTicket); err != nil {
return LoginResponse{}, fmt.Errorf("unmarshal login response: %w", err)
}
return authTicket, nil
}
type ConnectionsResponse struct {
Data []struct {
PatientID string `json:"patientID"`
Sensor struct {
DeviceID string `json:"deviceId"`
Serialnumber string `json:"sn"`
} `json:"sensor"`
GlucoseMeasurement struct {
ValueInMgPerDl int `json:"ValueInMgPerDl"`
TrendArrow int `json:"TrendArrow"`
} `json:"glucoseMeasurement"`
} `json:"data"`
Ticket Ticket `json:"ticket"`
}
func Connections(ctx context.Context, baseURL, token string) (ConnectionsResponse, error) {
url, err := url.JoinPath(baseURL, "/connections")
if err != nil {
return ConnectionsResponse{}, fmt.Errorf("joining url: %w", err)
}
resp, err := request(ctx, http.MethodGet, url, token, []byte{})
if err != nil {
return ConnectionsResponse{}, fmt.Errorf("doing request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return ConnectionsResponse{}, fmt.Errorf("reading body: %w", err)
}
var connResp ConnectionsResponse
if err := json.Unmarshal(body, &connResp); err != nil {
return ConnectionsResponse{}, fmt.Errorf("unmarshal response: %w", err)
}
return connResp, nil
}