在(二)中实现query方法 的末尾,我们遇到了跨域问题。如果你没有后端的修改权,这个问题在纯前端的情况下相对比较棘手。好在grafana为我们提供了代理路由,我们只需要让对数据库的请求经过grafana后端中转就可以解决跨域问题了。此外,这也防止了请求直接由浏览器发出而导致的不安全问题。

此外,本节中也将介绍如何自定义Grafana插件的配置项,例如在添加数据源时自定义数据库URL

你可能需要一点关于React的基础。即便没接触过React也不用担心。React自己的api函数非常少,基本上就是Js加类似Html的<jsx>,非常好理解。

为数据源自定义配置项

修改ConfigEditor组件

如果你之前接触过React,那么你应该一看就都懂了。。

模板中具有两个示例,Path和ApiKey,对应了jsonData普通配置项和安全性较高的secureJsonData配置项。

想要新增我们自己的配置字段,创建一个文本框,然后绑定对应的配置修改方法即可。

下面将以创建一个数据库地址的配置字段为例:

因为是TypeScript,所以我们先来修改接口类型。模板中的配置对象接口类型叫MyDataSourceOptions。

export interface MyDataSourceOptions extends DataSourceJsonData {
  path?: string;
  URL: string; //我们新加的
}

在组件内部添加一个方法

(这方法看着感觉挺啰嗦的。。你可以自己优化一下)

  onURLChange = (event: ChangeEvent<HTMLInputElement>) => {
    const { onOptionsChange, options } = this.props;
    const jsonData = {
      ...options.jsonData,
      URL: event.target.value,
    };
    onOptionsChange({ ...options, jsonData });
  };

然后在render方法的返回值内,找个地方插入我们的FormField组件。如果是私密数据的话可以选择SecretFormField组件。

因为都是普通字段,所以我把它和模板里自带的Path放到了一起

......

<div className="gf-form">
          <FormField
            label="URL"
            labelWidth={6}
            inputWidth={20}
            onChange={this.onURLChange}
            value={jsonData.URL || ''}
            placeholder="http://greptime.example.com"
          />
          {/* 下面这个是自带的 */}
          <FormField
            label="Path"
            labelWidth={6}
            inputWidth={20}
            onChange={this.onPathChange}
            value={jsonData.path || ''}
            placeholder="json field returned to frontend"
          />
</div>

......

Build一下,然后让Grafana重新加载插件,看看效果

可以看到我们新增的配置项已经显示出来了!

使用配置文件中的数据

在query中使用

在(二)中,我们对数据库地址进行了硬编码。现在我们可以将其修改为从数据源配置中获取了!

回到我们的datasource.ts ,你可以在constructor中看到,其中传入了一个参数instanceSettings,其中便包括了我们插件的各项设置

通过instanceSettings.jsonData我们可以把上面添加的URL拿出来。

export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
  URL: string;

  constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
    super(instanceSettings);
    this.URL = instanceSettings.jsonData.URL;
  }

…………

然后修改我们的doRequest方法。如果你是在类内部定义的,可以直接使用this.URL。

因为我是在文件外定义的,这里我使用了一个闭包,在构造函数中使用setUrl将url传递过来。

let BaseUrl = '';

export const setUrl = (url: string) => {
  BaseUrl = url;
};

export async function doRequst(sql: String): Promise<GreptimeDBResponse> {
  const SQL_URL = `${BaseUrl}/v1/sql`;

  const response: GreptimeDBResponse = await getBackendSrv().post(`${SQL_URL}?sql=${sql}`);
  return response;
}

打包测试一下结果

可以看到确实向我们在设置中填写的地址发出了请求。

如此,你便可以自由的编写你的数据库配置项了。

为数据源设置代理

我们首先需要在plugin.json中进行填写route相关属性。我们最少需要其中的url

  • url - For data source plugins. Route URL is where the request is proxied to.

在我们的plugin.json内添加如下内容。通过{{ .JsonData.foobar}}可以读取数据源配置文件中的配置项。

"routes": [
    {
      "url": "{{ .JsonData.URL }}"
    }
  ],

然后回到我们的datasource.ts,更改URL的来源

export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
  URL: string;

  constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
    super(instanceSettings);
    this.URL = instanceSettings.url!;

  }

更新插件进行测试

可以看到这次我们的请求的目标链接变成了grafana的proxy而不是之前填写的数据库地址了,并且查询仍然有效!

Grafana非常人性化的帮我们生成了一个代理服务 instanceSettings.url => JsonData.URL ,这样就解决了跨域和请求直接暴露的问题。


I am a noob