Bläddra i källkod

Merge pull request #378 from ralphocdol/custom-api-array-parameters

feat: add parameters and array parameters support
Svilen Markov 4 månader sedan
förälder
incheckning
652f9ceb5c

+ 16 - 0
docs/configuration.md

@@ -1294,6 +1294,7 @@ Examples:
 | headers | key (string) & value (string) | no | |
 | headers | key (string) & value (string) | no | |
 | frameless | boolean | no | false |
 | frameless | boolean | no | false |
 | template | string | yes | |
 | template | string | yes | |
+| parameters | key & value | no | |
 
 
 ##### `url`
 ##### `url`
 The URL to fetch the data from. It must be accessible from the server that Glance is running on.
 The URL to fetch the data from. It must be accessible from the server that Glance is running on.
@@ -1313,6 +1314,21 @@ When set to `true`, removes the border and padding around the widget.
 ##### `template`
 ##### `template`
 The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
 The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
 
 
+##### `parameters`
+A list of keys and values that will be sent to the custom-api as query paramters.
+
+> [!NOTE]
+>
+> Setting this property will override any query parameters that are already in the URL.
+
+```yaml
+parameters:
+  param1: value1
+  param2:
+    - item1
+    - item2
+```
+
 ### Extension
 ### Extension
 Display a widget provided by an external source (3rd party). If you want to learn more about developing extensions, checkout the [extensions documentation](extensions.md) (WIP).
 Display a widget provided by an external source (3rd party). If you want to learn more about developing extensions, checkout the [extensions documentation](extensions.md) (WIP).
 
 

+ 52 - 0
internal/glance/config-fields.go

@@ -219,3 +219,55 @@ func (p *proxyOptionsField) UnmarshalYAML(node *yaml.Node) error {
 
 
 	return nil
 	return nil
 }
 }
+
+type queryParametersField map[string][]string
+
+func (q *queryParametersField) UnmarshalYAML(node *yaml.Node) error {
+	var decoded map[string]any
+
+	if err := node.Decode(&decoded); err != nil {
+		return err
+	}
+
+	*q = make(queryParametersField)
+
+	for key, value := range decoded {
+		switch v := value.(type) {
+		case string:
+			(*q)[key] = []string{v}
+		case int, int8, int16, int32, int64, float32, float64:
+			(*q)[key] = []string{fmt.Sprintf("%v", v)}
+		case []string:
+			(*q)[key] = append((*q)[key], v...)
+		case []any:
+			for _, item := range v {
+				switch item := item.(type) {
+				case string:
+					(*q)[key] = append((*q)[key], item)
+				case int, int8, int16, int32, int64, float32, float64:
+					(*q)[key] = append((*q)[key], fmt.Sprintf("%v", item))
+				case bool:
+					(*q)[key] = append((*q)[key], fmt.Sprintf("%t", item))
+				default:
+					return fmt.Errorf("invalid query parameter value type: %T", item)
+				}
+			}
+		default:
+			return fmt.Errorf("invalid query parameter value type: %T", value)
+		}
+	}
+
+	return nil
+}
+
+func (q *queryParametersField) toQueryString() string {
+	query := url.Values{}
+
+	for key, values := range *q {
+		for _, value := range values {
+			query.Add(key, value)
+		}
+	}
+
+	return query.Encode()
+}

+ 10 - 7
internal/glance/widget-custom-api.go

@@ -19,13 +19,14 @@ var customAPIWidgetTemplate = mustParseTemplate("custom-api.html", "widget-base.
 
 
 type customAPIWidget struct {
 type customAPIWidget struct {
 	widgetBase       `yaml:",inline"`
 	widgetBase       `yaml:",inline"`
-	URL              string             `yaml:"url"`
-	Template         string             `yaml:"template"`
-	Frameless        bool               `yaml:"frameless"`
-	Headers          map[string]string  `yaml:"headers"`
-	APIRequest       *http.Request      `yaml:"-"`
-	compiledTemplate *template.Template `yaml:"-"`
-	CompiledHTML     template.HTML      `yaml:"-"`
+	URL              string               `yaml:"url"`
+	Template         string               `yaml:"template"`
+	Frameless        bool                 `yaml:"frameless"`
+	Headers          map[string]string    `yaml:"headers"`
+	Parameters       queryParametersField `yaml:"parameters"`
+	APIRequest       *http.Request        `yaml:"-"`
+	compiledTemplate *template.Template   `yaml:"-"`
+	CompiledHTML     template.HTML        `yaml:"-"`
 }
 }
 
 
 func (widget *customAPIWidget) initialize() error {
 func (widget *customAPIWidget) initialize() error {
@@ -51,6 +52,8 @@ func (widget *customAPIWidget) initialize() error {
 		return err
 		return err
 	}
 	}
 
 
+	req.URL.RawQuery = widget.Parameters.toQueryString()
+
 	for key, value := range widget.Headers {
 	for key, value := range widget.Headers {
 		req.Header.Add(key, value)
 		req.Header.Add(key, value)
 	}
 	}

+ 11 - 18
internal/glance/widget-extension.go

@@ -19,12 +19,12 @@ const extensionWidgetDefaultTitle = "Extension"
 
 
 type extensionWidget struct {
 type extensionWidget struct {
 	widgetBase          `yaml:",inline"`
 	widgetBase          `yaml:",inline"`
-	URL                 string            `yaml:"url"`
-	FallbackContentType string            `yaml:"fallback-content-type"`
-	Parameters          map[string]string `yaml:"parameters"`
-	AllowHtml           bool              `yaml:"allow-potentially-dangerous-html"`
-	Extension           extension         `yaml:"-"`
-	cachedHTML          template.HTML     `yaml:"-"`
+	URL                 string               `yaml:"url"`
+	FallbackContentType string               `yaml:"fallback-content-type"`
+	Parameters          queryParametersField `yaml:"parameters"`
+	AllowHtml           bool                 `yaml:"allow-potentially-dangerous-html"`
+	Extension           extension            `yaml:"-"`
+	cachedHTML          template.HTML        `yaml:"-"`
 }
 }
 
 
 func (widget *extensionWidget) initialize() error {
 func (widget *extensionWidget) initialize() error {
@@ -82,10 +82,10 @@ const (
 )
 )
 
 
 type extensionRequestOptions struct {
 type extensionRequestOptions struct {
-	URL                 string            `yaml:"url"`
-	FallbackContentType string            `yaml:"fallback-content-type"`
-	Parameters          map[string]string `yaml:"parameters"`
-	AllowHtml           bool              `yaml:"allow-potentially-dangerous-html"`
+	URL                 string               `yaml:"url"`
+	FallbackContentType string               `yaml:"fallback-content-type"`
+	Parameters          queryParametersField `yaml:"parameters"`
+	AllowHtml           bool                 `yaml:"allow-potentially-dangerous-html"`
 }
 }
 
 
 type extension struct {
 type extension struct {
@@ -109,14 +109,7 @@ func convertExtensionContent(options extensionRequestOptions, content []byte, co
 
 
 func fetchExtension(options extensionRequestOptions) (extension, error) {
 func fetchExtension(options extensionRequestOptions) (extension, error) {
 	request, _ := http.NewRequest("GET", options.URL, nil)
 	request, _ := http.NewRequest("GET", options.URL, nil)
-
-	query := url.Values{}
-
-	for key, value := range options.Parameters {
-		query.Set(key, value)
-	}
-
-	request.URL.RawQuery = query.Encode()
+	request.URL.RawQuery = options.Parameters.toQueryString()
 
 
 	response, err := http.DefaultClient.Do(request)
 	response, err := http.DefaultClient.Do(request)
 	if err != nil {
 	if err != nil {