Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

datasource-http-backend: Example migration with existing APIs #407

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion examples/datasource-http-backend/pkg/kinds/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (
type DataQuery struct {
v0alpha1.CommonQueryProperties

// Multiplier is the number to multiply the input by
// Multiply is the number to multiply the input by
Multiply int `json:"multiply,omitempty"`

// Deprecated: Use Multiply instead
Multiplier int `json:"multiplier,omitempty"`
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
"type": "integer"
},
"multiplier": {
"description": "Multiplier is the number to multiply the input by",
"description": "Deprecated: Use Multiply instead",
"type": "integer"
},
"multiply": {
"description": "Multiply is the number to multiply the input by",
"type": "integer"
},
"queryType": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
"type": "integer"
},
"multiplier": {
"description": "Multiplier is the number to multiply the input by",
"description": "Deprecated: Use Multiply instead",
"type": "integer"
},
"multiply": {
"description": "Multiply is the number to multiply the input by",
"type": "integer"
},
"queryType": {
Expand Down
12 changes: 8 additions & 4 deletions examples/datasource-http-backend/pkg/kinds/query.types.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
"kind": "QueryTypeDefinitionList",
"apiVersion": "query.grafana.app/v0alpha1",
"metadata": {
"resourceVersion": "1728987065588"
"resourceVersion": "1729780517379"
},
"items": [
{
"metadata": {
"name": "default",
"resourceVersion": "1728987397884",
"creationTimestamp": "2024-10-15T10:11:05Z"
"resourceVersion": "1729780517379",
"creationTimestamp": "2024-10-24T14:35:17Z"
},
"spec": {
"schema": {
Expand Down Expand Up @@ -47,7 +47,11 @@
"type": "integer"
},
"multiplier": {
"description": "Multiplier is the number to multiply the input by",
"description": "Deprecated: Use Multiply instead",
"type": "integer"
},
"multiply": {
"description": "Multiply is the number to multiply the input by",
"type": "integer"
},
"queryType": {
Expand Down
58 changes: 58 additions & 0 deletions examples/datasource-http-backend/pkg/plugin/convert_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package plugin

import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/grafana/datasource-http-backend/pkg/kinds"
"github.com/grafana/grafana-plugin-sdk-go/backend"
)

// convertQuery parses a given DataQuery and migrates it if necessary.
func convertQuery(orig backend.DataQuery) (*kinds.DataQuery, error) {
input := &kinds.DataQuery{}
err := json.Unmarshal(orig.JSON, input)
if err != nil {
return nil, fmt.Errorf("unmarshal: %w", err)
}
if input.Multiplier != 0 && input.Multiply == 0 {
input.Multiply = input.Multiplier
input.Multiplier = 0
}
return input, nil
}

// handleMigrateQuery handles the migration of a query.
func handleMigrateQuery(rw http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
rw.WriteHeader(http.StatusNotFound)
return
}

defer req.Body.Close()
queryJSON, err := io.ReadAll(req.Body)
if err != nil {
http.Error(rw, fmt.Sprintf("read body: %s", err), http.StatusBadRequest)
return
}
query := &backend.DataQuery{}
err = json.Unmarshal(queryJSON, query)
if err != nil {
http.Error(rw, fmt.Sprintf("unmarshal: %s", err), http.StatusBadRequest)
return
}
query.JSON = queryJSON
input, err := convertQuery(*query)
if err != nil {
http.Error(rw, fmt.Sprintf("migrate query: %s", err), http.StatusBadRequest)
return
}
err = json.NewEncoder(rw).Encode(input)
if err != nil {
http.Error(rw, fmt.Sprintf("encode response: %s", err), http.StatusInternalServerError)
return
}
rw.WriteHeader(http.StatusOK)
}
26 changes: 18 additions & 8 deletions examples/datasource-http-backend/pkg/plugin/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
"strconv"
"time"

"github.com/grafana/datasource-http-backend/pkg/kinds"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"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-plugin-sdk-go/backend/resource/httpadapter"
"github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
"github.com/grafana/grafana-plugin-sdk-go/data"
"go.opentelemetry.io/otel/attribute"
Expand Down Expand Up @@ -60,10 +60,16 @@ func NewDatasource(ctx context.Context, settings backend.DataSourceInstanceSetti
if err != nil {
return nil, fmt.Errorf("httpclient new: %w", err)
}
return &Datasource{
ds := &Datasource{
settings: settings,
httpClient: cl,
}, nil
}

mux := http.NewServeMux()
mux.HandleFunc("/migrate-query", handleMigrateQuery)
ds.resourceHandler = httpadapter.New(mux)

return ds, nil
}

// DatasourceOpts contains the default ManageOpts for the datasource.
Expand All @@ -82,7 +88,8 @@ var DatasourceOpts = datasource.ManageOpts{
type Datasource struct {
settings backend.DataSourceInstanceSettings

httpClient *http.Client
httpClient *http.Client
resourceHandler backend.CallResourceHandler
}

// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
Expand Down Expand Up @@ -148,6 +155,10 @@ func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataReques
return response, nil
}

func (d *Datasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
return d.resourceHandler.CallResource(ctx, req, sender)
}

func (d *Datasource) query(ctx context.Context, pCtx backend.PluginContext, query backend.DataQuery) (backend.DataResponse, error) {
// Create spans for this function.
// tracing.DefaultTracer() returns the tracer initialized when calling Manage().
Expand All @@ -174,13 +185,12 @@ func (d *Datasource) query(ctx context.Context, pCtx backend.PluginContext, quer
return backend.DataResponse{}, fmt.Errorf("new request with context: %w", err)
}
if len(query.JSON) > 0 {
input := &kinds.DataQuery{}
err = json.Unmarshal(query.JSON, input)
input, err := convertQuery(query)
if err != nil {
return backend.DataResponse{}, fmt.Errorf("unmarshal: %w", err)
return backend.DataResponse{}, err
}
q := req.URL.Query()
q.Add("multiplier", strconv.Itoa(input.Multiplier))
q.Add("multiplier", strconv.Itoa(input.Multiply))
req.URL.RawQuery = q.Encode()
}
httpResp, err := d.httpClient.Do(req)
Expand Down
45 changes: 28 additions & 17 deletions examples/datasource-http-backend/src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import React, { PureComponent } from 'react';
import React, { useState, useEffect } from 'react';
import { QueryEditorProps } from '@grafana/data';
import { DataSource } from '../datasource';
import { MyDataSourceOptions, MyQuery } from '../types';
import { HorizontalGroup, Input, Label } from '@grafana/ui';

type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;

export class QueryEditor extends PureComponent<Props> {
render() {
return (
<HorizontalGroup>
<Label htmlFor="multiplier">Multiplier</Label>
<Input
type="number"
id="multiplier"
name="multiplier"
value={this.props.query.multiplier}
onChange={(e) => this.props.onChange({ ...this.props.query, multiplier: e.currentTarget.valueAsNumber })}
/>
</HorizontalGroup>
);
}
}
export const QueryEditor = (props: Props) => {
const [query, setQuery] = useState<MyQuery>(props.query);
useEffect(() => {
props.datasource.migrateQuery(props.query).then((query) => {
setQuery(query);
});
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const onChangeMultiplier = (multiply: number) => {
const newQuery = { ...query, multiply, pluginVersion: props.datasource.meta.info.version };
setQuery(newQuery);
props.onChange(newQuery);
};

return (
<HorizontalGroup>
<Label htmlFor="multiplier">Multiplier</Label>
<Input
type="number"
id="multiplier"
name="multiplier"
value={query.multiply}
onChange={(e) => onChangeMultiplier(e.currentTarget.valueAsNumber)}
/>
</HorizontalGroup>
);
};
9 changes: 8 additions & 1 deletion examples/datasource-http-backend/src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export class DataSource extends DataSourceWithBackend<MyQuery, MyDataSourceOptio
}

getDefaultQuery(_: CoreApp): Partial<MyQuery> {
return { multiplier: 1 };
return { multiply: 1 };
}

async migrateQuery(query: Partial<MyQuery>): Promise<MyQuery> {
if ('multiply' in query) {
return query as MyQuery;
}
return await this.postResource<MyQuery>(`/migrate-query`, query);
}
}
2 changes: 1 addition & 1 deletion examples/datasource-http-backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { DataSourceJsonData } from '@grafana/data';
import type { DataQuery } from '@grafana/schema';

export interface MyQuery extends DataQuery {
multiplier: number;
multiply: number;
}

/**
Expand Down
Loading