DataSource Plugin Development - DataSourceHttpSettings component not setting url with defaultUrl

Hi!

I am currently developing a dataSource Plugin but I am struggling with the setting of the instanceSettings.url in the ConfigEditor.tsx. From what I understood from the examples and the documentation and another community post, the defaultUrl prop on the DataSourceHttpSettings component should automatically set the instanceSettings.url. This is not the case for me. The url I specified in the defaultUrl prop is displayed as the placeholder but is not set in the instanceSettings. So I still have to manually type the url, when configuring the datasource. Is this expected behaviour?

My code currently looks like this:
ConfigEditor.tsx:

interface Props extends DataSourcePluginOptionsEditorProps<MyDataSourceOptions> {}

export function ConfigEditor(props: Props) {
  const { onOptionsChange, options } = props;

  const onAPIKeyChange = (event: ChangeEvent<HTMLInputElement>) => {
    const jsonData = {
      ...options.jsonData,
      apiKey: event.currentTarget.value,
    };
    onOptionsChange({ ...options, jsonData });
  };

  const { jsonData } = options;

  return (
    <>
      <DataSourceHttpSettings
        defaultUrl="https://api.test.com/v1"
        dataSourceConfig={options}
        onChange={onOptionsChange}
        showAccessOptions={true}
      />

      <FieldSet label="API Key">
        <InlineField required label="API Key" labelWidth={26}>
          <Input
            required
            onChange={onAPIKeyChange}
            value={jsonData.apiKey || ''}
            placeholder="API Key"
            width={40}
          />
        </InlineField>
      </FieldSet>
    </>
  );
}

datasource.ts:

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

  constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
    super(instanceSettings);

    this.apiKey = instanceSettings.jsonData.apiKey;
    this.baseUrl = instanceSettings.url!;
  }

  async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    const { range } = options;
    const from = range!.from.valueOf();
    const to = range!.to.valueOf();

    // Return a constant for each query.
    const data = options.targets.map((target) => {
      return createDataFrame({
        refId: target.refId,
        fields: [
          { name: 'Time', values: [from, to], type: FieldType.time },
          { name: 'Value', values: [target.constant, target.constant], type: FieldType.number },
        ],
      });
    });

    return { data };
  }

  async request(url: string, method: string, payload?: any) {
    const options = {
      url: this.baseUrl + url,
      headers: { 'X-Api-Key': this.apiKey },
      method: method,
    };
    const response = getBackendSrv().fetch(options);

    return lastValueFrom(response);
  }

  async testDatasource() {
    const response = await this.request('/licenses, 'GET');

    if (response.status === 200) {
      return {
        status: 'success',
        message: 'Datasource was setup successfully.',
      };
    } else {
      return {
        status: 'error',
        message: response.statusText
          ? response.statusText
          : 'An unexpected error occurred. Datasource not setup correctly.',
      };
    }
  }
}

Hi @myriamgantner yes you are correct this is the expected behaviour. but this is something you can handle yourself inside the code.

in your datasource constructor you can verify if the url is empty and set the default value.

So imagine you create a defaultUrlValue variable and you can use it as both the defaultUrl prop in the DataSourceHttpSettings component and use it as default on your datasource:

// a constants file is an idea of where to put it for common usage
import { defaultUrlValue } from './constants'; 
constructor(...) {
  //...
  this.baseUrl = instanceSettings.url || defaultUrlValue
  //...
}

another way is to always set it if empty when the configuration page runs. e.g.:

ConfigEditor.tsx:

export function ConfigEditor(props: Props) {
const { onOptionsChange, options } = props;

useEffect( () => {
   if (options.url === "") {
       onOptionsChange({ ...options, url: defaultUrlValue, jsonData });
   }
}, [options.url])

There are many ways of tackling this same problem. But generally speaking is up to your plugin to do it

1 Like

Thank you for your answer @academo. I guess I was confused by the naming of the defaultUrl prop, as its behaviour is more like a placeholder than a defaultValue.