83 lines
2.3 KiB
Go
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))
|
|
}
|