1package influxdb
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/grafana/grafana-plugin-sdk-go/backend"
8)
9
10var renders map[string]QueryDefinition
11
12type DefinitionParameters struct {
13	Name string
14	Type string
15}
16
17type QueryDefinition struct {
18	Renderer func(query *Query, queryContext *backend.QueryDataRequest, part *QueryPart, innerExpr string) string
19	Params   []DefinitionParameters
20}
21
22func init() {
23	renders = make(map[string]QueryDefinition)
24
25	renders["field"] = QueryDefinition{Renderer: fieldRenderer}
26
27	renders["spread"] = QueryDefinition{Renderer: functionRenderer}
28	renders["count"] = QueryDefinition{Renderer: functionRenderer}
29	renders["distinct"] = QueryDefinition{Renderer: functionRenderer}
30	renders["integral"] = QueryDefinition{Renderer: functionRenderer}
31	renders["mean"] = QueryDefinition{Renderer: functionRenderer}
32	renders["median"] = QueryDefinition{Renderer: functionRenderer}
33	renders["sum"] = QueryDefinition{Renderer: functionRenderer}
34	renders["mode"] = QueryDefinition{Renderer: functionRenderer}
35	renders["cumulative_sum"] = QueryDefinition{Renderer: functionRenderer}
36	renders["non_negative_difference"] = QueryDefinition{Renderer: functionRenderer}
37
38	renders["holt_winters"] = QueryDefinition{
39		Renderer: functionRenderer,
40		Params:   []DefinitionParameters{{Name: "number", Type: "number"}, {Name: "season", Type: "number"}},
41	}
42	renders["holt_winters_with_fit"] = QueryDefinition{
43		Renderer: functionRenderer,
44		Params:   []DefinitionParameters{{Name: "number", Type: "number"}, {Name: "season", Type: "number"}},
45	}
46
47	renders["derivative"] = QueryDefinition{
48		Renderer: functionRenderer,
49		Params:   []DefinitionParameters{{Name: "duration", Type: "interval"}},
50	}
51
52	renders["non_negative_derivative"] = QueryDefinition{
53		Renderer: functionRenderer,
54		Params:   []DefinitionParameters{{Name: "duration", Type: "interval"}},
55	}
56	renders["difference"] = QueryDefinition{Renderer: functionRenderer}
57	renders["moving_average"] = QueryDefinition{
58		Renderer: functionRenderer,
59		Params:   []DefinitionParameters{{Name: "window", Type: "number"}},
60	}
61	renders["stddev"] = QueryDefinition{Renderer: functionRenderer}
62	renders["time"] = QueryDefinition{
63		Renderer: functionRenderer,
64		Params:   []DefinitionParameters{{Name: "interval", Type: "time"}, {Name: "offset", Type: "time"}},
65	}
66	renders["fill"] = QueryDefinition{
67		Renderer: functionRenderer,
68		Params:   []DefinitionParameters{{Name: "fill", Type: "string"}},
69	}
70	renders["elapsed"] = QueryDefinition{
71		Renderer: functionRenderer,
72		Params:   []DefinitionParameters{{Name: "duration", Type: "interval"}},
73	}
74	renders["bottom"] = QueryDefinition{
75		Renderer: functionRenderer,
76		Params:   []DefinitionParameters{{Name: "count", Type: "int"}},
77	}
78
79	renders["first"] = QueryDefinition{Renderer: functionRenderer}
80	renders["last"] = QueryDefinition{Renderer: functionRenderer}
81	renders["max"] = QueryDefinition{Renderer: functionRenderer}
82	renders["min"] = QueryDefinition{Renderer: functionRenderer}
83	renders["percentile"] = QueryDefinition{
84		Renderer: functionRenderer,
85		Params:   []DefinitionParameters{{Name: "nth", Type: "int"}},
86	}
87	renders["top"] = QueryDefinition{
88		Renderer: functionRenderer,
89		Params:   []DefinitionParameters{{Name: "count", Type: "int"}},
90	}
91	renders["tag"] = QueryDefinition{
92		Renderer: fieldRenderer,
93		Params:   []DefinitionParameters{{Name: "tag", Type: "string"}},
94	}
95
96	renders["math"] = QueryDefinition{Renderer: suffixRenderer}
97	renders["alias"] = QueryDefinition{Renderer: aliasRenderer}
98}
99
100func fieldRenderer(query *Query, queryContext *backend.QueryDataRequest, part *QueryPart, innerExpr string) string {
101	if part.Params[0] == "*" {
102		return "*"
103	}
104	return fmt.Sprintf(`"%s"`, part.Params[0])
105}
106
107func functionRenderer(query *Query, queryContext *backend.QueryDataRequest, part *QueryPart, innerExpr string) string {
108	for i, param := range part.Params {
109		if part.Type == "time" && param == "auto" {
110			part.Params[i] = "$__interval"
111		}
112	}
113
114	if innerExpr != "" {
115		part.Params = append([]string{innerExpr}, part.Params...)
116	}
117
118	params := strings.Join(part.Params, ", ")
119
120	return fmt.Sprintf("%s(%s)", part.Type, params)
121}
122
123func suffixRenderer(query *Query, queryContext *backend.QueryDataRequest, part *QueryPart, innerExpr string) string {
124	return fmt.Sprintf("%s %s", innerExpr, part.Params[0])
125}
126
127func aliasRenderer(query *Query, queryContext *backend.QueryDataRequest, part *QueryPart, innerExpr string) string {
128	return fmt.Sprintf(`%s AS "%s"`, innerExpr, part.Params[0])
129}
130
131func NewQueryPart(typ string, params []string) (*QueryPart, error) {
132	def, exist := renders[typ]
133	if !exist {
134		return nil, fmt.Errorf("missing query definition for %q", typ)
135	}
136
137	return &QueryPart{
138		Type:   typ,
139		Params: params,
140		Def:    def,
141	}, nil
142}
143
144type QueryPart struct {
145	Def    QueryDefinition
146	Type   string
147	Params []string
148}
149
150func (qp *QueryPart) Render(query *Query, queryContext *backend.QueryDataRequest, expr string) string {
151	return qp.Def.Renderer(query, queryContext, qp, expr)
152}
153