| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | package promlib | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/datasource" | 
					
						
							|  |  |  | 	sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/log" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/promlib/client" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/promlib/instrumentation" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/promlib/querydata" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/promlib/resource" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Service struct { | 
					
						
							|  |  |  | 	im     instancemgmt.InstanceManager | 
					
						
							|  |  |  | 	logger log.Logger | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type instance struct { | 
					
						
							| 
									
										
										
										
											2024-12-24 04:29:44 +08:00
										 |  |  | 	queryData *querydata.QueryData | 
					
						
							|  |  |  | 	resource  *resource.Resource | 
					
						
							| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 22:09:55 +08:00
										 |  |  | type ExtendOptions func(ctx context.Context, settings backend.DataSourceInstanceSettings, clientOpts *sdkhttpclient.Options, log log.Logger) error | 
					
						
							| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func NewService(httpClientProvider *sdkhttpclient.Provider, plog log.Logger, extendOptions ExtendOptions) *Service { | 
					
						
							|  |  |  | 	if httpClientProvider == nil { | 
					
						
							|  |  |  | 		httpClientProvider = sdkhttpclient.NewProvider() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &Service{ | 
					
						
							|  |  |  | 		im:     datasource.NewInstanceManager(newInstanceSettings(httpClientProvider, plog, extendOptions)), | 
					
						
							|  |  |  | 		logger: plog, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-30 23:58:18 +08:00
										 |  |  | // Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
 | 
					
						
							|  |  |  | // created. As soon as datasource settings change detected by SDK old datasource instance will
 | 
					
						
							|  |  |  | // be disposed and a new one will be created using NewSampleDatasource factory function.
 | 
					
						
							|  |  |  | func (s *Service) Dispose() { | 
					
						
							|  |  |  | 	// Clean up datasource instance resources.
 | 
					
						
							|  |  |  | 	s.logger.Debug("Disposing the instance...") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | func newInstanceSettings(httpClientProvider *sdkhttpclient.Provider, log log.Logger, extendOptions ExtendOptions) datasource.InstanceFactoryFunc { | 
					
						
							|  |  |  | 	return func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { | 
					
						
							|  |  |  | 		// Creates a http roundTripper.
 | 
					
						
							|  |  |  | 		opts, err := client.CreateTransportOptions(ctx, settings, log) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("error creating transport options: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-15 20:37:29 +08:00
										 |  |  | 		if extendOptions != nil { | 
					
						
							| 
									
										
										
										
											2024-07-17 22:09:55 +08:00
										 |  |  | 			err = extendOptions(ctx, settings, opts, log) | 
					
						
							| 
									
										
										
										
											2024-03-15 20:37:29 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, fmt.Errorf("error extending transport options: %v", err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		httpClient, err := httpClientProvider.New(*opts) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("error creating http client: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// New version using custom client and better response parsing
 | 
					
						
							|  |  |  | 		qd, err := querydata.New(httpClient, settings, log) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Resource call management using new custom client same as querydata
 | 
					
						
							|  |  |  | 		r, err := resource.New(httpClient, settings, log) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return instance{ | 
					
						
							| 
									
										
										
										
											2024-12-24 04:29:44 +08:00
										 |  |  | 			queryData: qd, | 
					
						
							|  |  |  | 			resource:  r, | 
					
						
							| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | 		}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | 
					
						
							|  |  |  | 	if len(req.Queries) == 0 { | 
					
						
							|  |  |  | 		err := fmt.Errorf("query contains no queries") | 
					
						
							|  |  |  | 		instrumentation.UpdateQueryDataMetrics(err, nil) | 
					
						
							|  |  |  | 		return &backend.QueryDataResponse{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i, err := s.getInstance(ctx, req.PluginContext) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		instrumentation.UpdateQueryDataMetrics(err, nil) | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	qd, err := i.queryData.Execute(ctx, req) | 
					
						
							|  |  |  | 	instrumentation.UpdateQueryDataMetrics(err, qd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return qd, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { | 
					
						
							|  |  |  | 	i, err := s.getInstance(ctx, req.PluginContext) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 19:17:59 +08:00
										 |  |  | 	switch { | 
					
						
							|  |  |  | 	case strings.EqualFold(req.Path, "suggestions"): | 
					
						
							|  |  |  | 		resp, err := i.resource.GetSuggestions(ctx, req) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return sender.Send(resp) | 
					
						
							| 
									
										
										
										
											2024-03-12 00:22:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp, err := i.resource.Execute(ctx, req) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return sender.Send(resp) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *Service) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*instance, error) { | 
					
						
							|  |  |  | 	i, err := s.im.Get(ctx, pluginCtx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	in := i.(instance) | 
					
						
							|  |  |  | 	return &in, nil | 
					
						
							|  |  |  | } |