Files

83 lines
2.3 KiB
Go

package glpi
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
)
// SearchRow is one CI hit from a search: the well-known fields 2 (id)
// and 1 (name) plus the raw row keyed by field-id string, exactly as
// GLPI returns it under forcedisplay.
type SearchRow struct {
ID int
Name string
Fields map[string]any
}
// SearchCI searches any itemtype's CIs (GET /search/<itemtype>) with a
// contains criterion on field 1 (name) and forcedisplay 1,2.
func (c *Client) SearchCI(ctx context.Context, itemtype, term string) ([]SearchRow, error) {
if itemtype == "" {
return nil, fmt.Errorf("%w: itemtype required", ErrValidation)
}
q := url.Values{}
q.Add("forcedisplay[]", "1")
q.Add("forcedisplay[]", "2")
if term != "" {
crit, err := json.Marshal([]map[string]any{{
"field": 1, "searchtype": "contains", "value": term,
}})
if err != nil {
return nil, fmt.Errorf("%w: cannot encode criteria", ErrMalformedResponse)
}
q.Set("criteria", string(crit))
}
var body struct {
Total int `json:"totalcount"`
Data []map[string]any `json:"data"`
}
if err := c.call(ctx, http.MethodGet, "/search/"+esc(itemtype), q, nil, &body); err != nil {
return nil, err
}
rows := make([]SearchRow, 0, len(body.Data))
for _, d := range body.Data {
rows = append(rows, SearchRow{
Name: asString(d["1"]),
ID: asInt(d["2"]),
Fields: d,
})
}
return rows, nil
}
// GetItem fetches one CI of any itemtype raw (GET /<itemtype>/<id>),
// used by `ci show`. The full server object is returned untouched.
func (c *Client) GetItem(ctx context.Context, itemtype string, id int) (map[string]any, error) {
var obj map[string]any
if err := c.call(ctx, http.MethodGet, "/"+esc(itemtype)+"/"+itoa(id), nil, nil, &obj); err != nil {
return nil, err
}
return obj, nil
}
// esc path-escapes an itemtype.
func esc(s string) string { return url.PathEscape(s) }
// eqfold is strings.EqualFold kept local for the status table.
func eqfold(a, b string) bool { return strings.EqualFold(a, b) }
// trimFloat renders a JSON number without a trailing ".0".
func trimFloat(f float64) string {
return strconv.FormatFloat(f, 'f', -1, 64)
}
// parseLeadingInt parses an int from a possibly messy search cell.
func parseLeadingInt(s string) (int, error) {
return strconv.Atoi(strings.TrimSpace(s))
}