package service import ( "axolotl/models" "bytes" "encoding/json" "fmt" "net/http" "net/url" ) type apiClient struct { base string user string http *http.Client } func (c *apiClient) User() string { return c.user } func (c *apiClient) do(method, path string, body any) (*http.Response, error) { var buf bytes.Buffer if body != nil { if err := json.NewEncoder(&buf).Encode(body); err != nil { return nil, err } } req, err := http.NewRequest(method, c.base+path, &buf) if err != nil { return nil, err } if err := c.setAuth(req); err != nil { return nil, err } if body != nil { req.Header.Set("Content-Type", "application/json") } return c.http.Do(req) } // setAuth attaches either a Bearer token (when a session exists) or the // X-Ax-User header (no session / non-OIDC servers). func (c *apiClient) setAuth(req *http.Request) error { sess, err := LoadSession() if err != nil || sess == nil || sess.Token == "" { req.Header.Set("X-Ax-User", c.user) return nil } req.Header.Set("Authorization", "Bearer "+sess.Token) return nil } func apiDecode[T any](resp *http.Response) (T, error) { var v T defer resp.Body.Close() if resp.StatusCode >= 400 { var e struct{ Error string } json.NewDecoder(resp.Body).Decode(&e) return v, fmt.Errorf("%s", e.Error) } return v, json.NewDecoder(resp.Body).Decode(&v) } func (c *apiClient) GetByID(id string) (*models.Node, error) { resp, err := c.do("GET", "/nodes/"+id, nil) if err != nil { return nil, err } return apiDecode[*models.Node](resp) } func (c *apiClient) List(filter ListFilter) ([]*models.Node, error) { q := url.Values{} for _, r := range filter.Rels { if r.Target == "" { q.Add("rel", string(r.Type)) } else { q.Add("rel", string(r.Type)+":"+r.Target) } } path := "/nodes" if len(q) > 0 { path += "?" + q.Encode() } resp, err := c.do("GET", path, nil) if err != nil { return nil, err } return apiDecode[[]*models.Node](resp) } func (c *apiClient) Add(input AddInput) (*models.Node, error) { resp, err := c.do("POST", "/nodes", input) if err != nil { return nil, err } return apiDecode[*models.Node](resp) } func (c *apiClient) Update(id string, input UpdateInput) (*models.Node, error) { resp, err := c.do("PATCH", "/nodes/"+id, input) if err != nil { return nil, err } return apiDecode[*models.Node](resp) } func (c *apiClient) Delete(id string) error { resp, err := c.do("DELETE", "/nodes/"+id, nil) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { var e struct{ Error string } json.NewDecoder(resp.Body).Decode(&e) return fmt.Errorf("%s", e.Error) } return nil } func (c *apiClient) ListUsers() ([]*models.Node, error) { resp, err := c.do("GET", "/users", nil) if err != nil { return nil, err } return apiDecode[[]*models.Node](resp) } func (c *apiClient) AddUser(name string) (*models.Node, error) { resp, err := c.do("POST", "/users", map[string]string{"name": name}) if err != nil { return nil, err } return apiDecode[*models.Node](resp) }