feat: v0 — glpi-go client, mglpi CLI, mglpi-mcp, fake-GLPI test suite [#767]
https://projects.knownelement.com/issues/767#note-4191
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user