master
go 149 lines 3.92 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package dyncfg
4
5 import (
6 "encoding/json"
7 "fmt"
8 "strings"
9
10 "gopkg.in/yaml.v2"
11
12 "github.com/netdata/netdata/go/plugins/plugin/framework/functions"
13 )
14
15 // Function wraps functions.Function with dyncfg-specific accessor and helper methods.
16 type Function struct {
17 fn functions.Function
18 }
19
20 // NewFunction creates a new dyncfg Function wrapper.
21 func NewFunction(fn functions.Function) Function {
22 return Function{fn: fn}
23 }
24
25 // Fn returns the underlying functions.Function.
26 // Use this when passing to APIs that expect functions.Function.
27 func (f Function) Fn() functions.Function {
28 return f.fn
29 }
30
31 // UID returns the function's unique identifier.
32 func (f Function) UID() string {
33 return f.fn.UID
34 }
35
36 // Source returns the function's source field.
37 func (f Function) Source() string {
38 return f.fn.Source
39 }
40
41 // Payload returns the function's payload.
42 func (f Function) Payload() []byte {
43 return f.fn.Payload
44 }
45
46 // ContentType returns the function's content type.
47 func (f Function) ContentType() string {
48 return f.fn.ContentType
49 }
50
51 // Command returns the dyncfg command from Args[1].
52 // Returns empty Command if args has fewer than 2 elements.
53 func (f Function) Command() Command {
54 if len(f.fn.Args) < 2 {
55 return ""
56 }
57 return Command(strings.ToLower(f.fn.Args[1]))
58 }
59
60 // ID returns the config ID from Args[0].
61 // Returns empty string if args is empty.
62 func (f Function) ID() string {
63 if len(f.fn.Args) < 1 {
64 return ""
65 }
66 return f.fn.Args[0]
67 }
68
69 // JobName returns the job name from Args[2] (used in add command).
70 // Returns empty string if args has fewer than 3 elements.
71 // Sanitizes the name by replacing spaces and colons with underscores.
72 func (f Function) JobName() string {
73 if len(f.fn.Args) < 3 {
74 return ""
75 }
76 name := strings.ReplaceAll(f.fn.Args[2], " ", "_")
77 name = strings.ReplaceAll(name, ":", "_")
78 return name
79 }
80
81 // User returns the user value from the Source field.
82 func (f Function) User() string {
83 return f.SourceValue("user")
84 }
85
86 // SourceValue extracts a value from the Source field.
87 // Source format is "key1=value1,key2=value2,...".
88 func (f Function) SourceValue(key string) string {
89 prefix := key + "="
90 for part := range strings.SplitSeq(f.fn.Source, ",") {
91 if v, ok := strings.CutPrefix(part, prefix); ok {
92 return strings.TrimSpace(v)
93 }
94 }
95 return ""
96 }
97
98 // HasPayload returns true if the function has a non-empty payload.
99 func (f Function) HasPayload() bool {
100 return len(f.fn.Payload) > 0
101 }
102
103 // ValidateArgs checks if the function has at least the required number of arguments.
104 // Returns an error with a descriptive message if validation fails.
105 func (f Function) ValidateArgs(required int) error {
106 if len(f.fn.Args) < required {
107 return fmt.Errorf("missing required arguments: need %d, got %d", required, len(f.fn.Args))
108 }
109 return nil
110 }
111
112 // ValidateHasPayload checks if the function has a payload.
113 // Returns an error if the payload is empty.
114 func (f Function) ValidateHasPayload() error {
115 if !f.HasPayload() {
116 return fmt.Errorf("missing configuration payload")
117 }
118 return nil
119 }
120
121 // UnmarshalPayload unmarshals the payload into dst based on ContentType.
122 // Uses JSON for "application/json", YAML otherwise.
123 func (f Function) UnmarshalPayload(dst any) error {
124 if f.fn.ContentType == "application/json" {
125 return f.unmarshalJSON(dst)
126 }
127 return f.unmarshalYAML(dst)
128 }
129
130 // unmarshalJSON unmarshals the payload as JSON into dst.
131 func (f Function) unmarshalJSON(dst any) error {
132 if err := json.Unmarshal(f.fn.Payload, dst); err != nil {
133 return fmt.Errorf("failed to unmarshal JSON payload: %w", err)
134 }
135 return nil
136 }
137
138 // unmarshalYAML unmarshals the payload as YAML into dst.
139 func (f Function) unmarshalYAML(dst any) error {
140 if err := yaml.Unmarshal(f.fn.Payload, dst); err != nil {
141 return fmt.Errorf("failed to unmarshal YAML payload: %w", err)
142 }
143 return nil
144 }
145
146 // IsContentTypeJSON returns true if the content type is JSON.
147 func (f Function) IsContentTypeJSON() bool {
148 return f.fn.ContentType == "application/json"
149 }