diff --git a/docs/configuration.md b/docs/configuration.md index 0dd8219..be5a988 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1292,6 +1292,9 @@ Examples: | ---- | ---- | -------- | ------- | | url | string | yes | | | headers | key (string) & value (string) | no | | +| method | string | no | GET | +| body-type | string | no | json | +| body | any | no | | | frameless | boolean | no | false | | allow-insecure | boolean | no | false | | template | string | yes | | @@ -1310,6 +1313,31 @@ headers: Accept: application/json ``` +##### `method` +The HTTP method to use when making the request. Possible values are `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS` and `HEAD`. + +##### `body-type` +The type of the body that will be sent with the request. Possible values are `json`, and `string`. + +##### `body` +The body that will be sent with the request. It can be a string or a map. Example: + +```yaml +body-type: json +body: + key1: value1 + key2: value2 + multiple-items: + - item1 + - item2 +``` + +```yaml +body-type: string +body: | + key1=value1&key2=value2 +``` + ##### `frameless` When set to `true`, removes the border and padding around the widget. diff --git a/internal/glance/widget-custom-api.go b/internal/glance/widget-custom-api.go index d3ab123..e20ff5a 100644 --- a/internal/glance/widget-custom-api.go +++ b/internal/glance/widget-custom-api.go @@ -3,6 +3,7 @@ package glance import ( "bytes" "context" + "encoding/json" "errors" "fmt" "html/template" @@ -21,10 +22,14 @@ var customAPIWidgetTemplate = mustParseTemplate("custom-api.html", "widget-base. // Needs to be exported for the YAML unmarshaler to work type CustomAPIRequest struct { - URL string `json:"url"` - AllowInsecure bool `json:"allow-insecure"` - Headers map[string]string `json:"headers"` - Parameters queryParametersField `json:"parameters"` + URL string `yaml:"url"` + AllowInsecure bool `yaml:"allow-insecure"` + Headers map[string]string `yaml:"headers"` + Parameters queryParametersField `yaml:"parameters"` + Method string `yaml:"method"` + BodyType string `yaml:"body-type"` + Body any `yaml:"body"` + bodyReader io.ReadSeeker `yaml:"-"` httpRequest *http.Request `yaml:"-"` } @@ -83,7 +88,41 @@ func (req *CustomAPIRequest) initialize() error { return errors.New("URL is required") } - httpReq, err := http.NewRequest(http.MethodGet, req.URL, nil) + if req.Body != nil { + if req.Method == "" { + req.Method = http.MethodPost + } + + if req.BodyType == "" { + req.BodyType = "json" + } + + if req.BodyType != "json" && req.BodyType != "string" { + return errors.New("invalid body type, must be either 'json' or 'string'") + } + + switch req.BodyType { + case "json": + encoded, err := json.Marshal(req.Body) + if err != nil { + return fmt.Errorf("marshaling body: %v", err) + } + + req.bodyReader = bytes.NewReader(encoded) + case "string": + bodyAsString, ok := req.Body.(string) + if !ok { + return errors.New("body must be a string when body-type is 'string'") + } + + req.bodyReader = strings.NewReader(bodyAsString) + } + + } else if req.Method == "" { + req.Method = http.MethodGet + } + + httpReq, err := http.NewRequest(strings.ToUpper(req.Method), req.URL, req.bodyReader) if err != nil { return err } @@ -92,6 +131,10 @@ func (req *CustomAPIRequest) initialize() error { httpReq.URL.RawQuery = req.Parameters.toQueryString() } + if req.BodyType == "json" { + httpReq.Header.Set("Content-Type", "application/json") + } + for key, value := range req.Headers { httpReq.Header.Add(key, value) } @@ -126,6 +169,10 @@ func (data *customAPITemplateData) Subrequest(key string) *customAPIResponseData } func fetchCustomAPIRequest(ctx context.Context, req *CustomAPIRequest) (*customAPIResponseData, error) { + if req.bodyReader != nil { + req.bodyReader.Seek(0, io.SeekStart) + } + client := ternary(req.AllowInsecure, defaultInsecureHTTPClient, defaultHTTPClient) resp, err := client.Do(req.httpRequest.WithContext(ctx)) if err != nil {