ReactMigration: Migrate DataSource HTTP Settings to React (#19452)

* Basic components for HTTP settings migration WIP

* Add secureJsonFields to DataSourceSettings

* Introduce datasource-http-settings-next directive for backward compatibility

* fix lint

* renames

* rename fix

* TagsInput component

* move tags from app to grafana/ui

* implement tagsinput on datasourcesettings

* capitalize

* new file for react directive for testing

* some layout touch ups

* FormField story

* Minor touch ups

* add url validation

* using prevent default to prevent updating datasource when adding tag

* using Stylefactory and fix tslint issue on MouseEvent

* only show tlsauthsettings if tls or ca cert

* fix url input length

* fix for showAccessOptions

* Implemented CertTextArea, removed commented code

* removed commented / not used code

* Rename and add more elements to Certification component

* fixing newSecureJsonData

* spelling

* Fix issue with checkboxes being undefined

* Removed old partials and minor fix

* removed unused props from story
This commit is contained in:
Dominik Prokop
2019-10-18 12:09:53 +02:00
committed by Torkel Ödegaard
parent cb0e80e7b9
commit c9b11bfc7a
24 changed files with 760 additions and 197 deletions

View File

@@ -7,7 +7,13 @@ import { TagFilter } from './components/TagFilter/TagFilter';
import { SideMenu } from './components/sidemenu/SideMenu';
import { MetricSelect } from './components/Select/MetricSelect';
import AppNotificationList from './components/AppNotifications/AppNotificationList';
import { ColorPicker, SeriesColorPickerPopoverWithTheme, SecretFormField, DataLinksEditor } from '@grafana/ui';
import {
ColorPicker,
SeriesColorPickerPopoverWithTheme,
SecretFormField,
DataLinksEditor,
DataSourceHttpSettings,
} from '@grafana/ui';
import { FunctionEditor } from 'app/plugins/datasource/graphite/FunctionEditor';
import { SearchField } from './components/search/SearchField';
import { GraphContextMenu } from 'app/plugins/panel/graph/GraphContextMenu';
@@ -111,4 +117,10 @@ export function registerAngularDirectives() {
'onChange',
['datasource', { watchDepth: 'reference' }],
]);
react2AngularDirective('datasourceHttpSettingsNext', DataSourceHttpSettings, [
'defaultUrl',
'showAccessOptions',
'dataSourceConfig',
['onChange', { watchDepth: 'reference', wrapApply: true }],
]);
}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import tags from 'app/core/utils/tags';
import { getTagColorsFromName } from '@grafana/ui';
export interface Props {
label: string;
@@ -15,7 +15,7 @@ export class TagBadge extends React.Component<Props, any> {
render() {
const { label, removeIcon, count } = this.props;
const { color, borderColor } = tags.getTagColorsFromName(label);
const { color, borderColor } = getTagColorsFromName(label);
const tagStyle = {
backgroundColor: color,
borderColor: borderColor,

View File

@@ -1,11 +1,11 @@
import angular from 'angular';
import { getTagColorsFromName } from '@grafana/ui';
import $ from 'jquery';
import coreModule from '../core_module';
import tags from 'app/core/utils/tags';
import 'vendor/tagsinput/bootstrap-tagsinput.js';
function setColor(name: string, element: JQuery) {
const { color, borderColor } = tags.getTagColorsFromName(name);
const { color, borderColor } = getTagColorsFromName(name);
element.css('background-color', color);
element.css('border-color', borderColor);
}

View File

@@ -1,86 +0,0 @@
const TAG_COLORS = [
'#E24D42',
'#1F78C1',
'#BA43A9',
'#705DA0',
'#466803',
'#508642',
'#447EBC',
'#C15C17',
'#890F02',
'#757575',
'#0A437C',
'#6D1F62',
'#584477',
'#629E51',
'#2F4F4F',
'#BF1B00',
'#806EB7',
'#8a2eb8',
'#699e00',
'#000000',
'#3F6833',
'#2F575E',
'#99440A',
'#E0752D',
'#0E4AB4',
'#58140C',
'#052B51',
'#511749',
'#3F2B5B',
];
const TAG_BORDER_COLORS = [
'#FF7368',
'#459EE7',
'#E069CF',
'#9683C6',
'#6C8E29',
'#76AC68',
'#6AA4E2',
'#E7823D',
'#AF3528',
'#9B9B9B',
'#3069A2',
'#934588',
'#7E6A9D',
'#88C477',
'#557575',
'#E54126',
'#A694DD',
'#B054DE',
'#8FC426',
'#262626',
'#658E59',
'#557D84',
'#BF6A30',
'#FF9B53',
'#3470DA',
'#7E3A32',
'#2B5177',
'#773D6F',
'#655181',
];
/**
* Returns tag badge background and border colors based on hashed tag name.
* @param name tag name
*/
export function getTagColorsFromName(name: string): { color: string; borderColor: string } {
const hash = djb2(name.toLowerCase());
const color = TAG_COLORS[Math.abs(hash % TAG_COLORS.length)];
const borderColor = TAG_BORDER_COLORS[Math.abs(hash % TAG_BORDER_COLORS.length)];
return { color, borderColor };
}
function djb2(str: string) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) + hash + str.charCodeAt(i); /* hash * 33 + c */
}
return hash;
}
export default {
getTagColorsFromName,
};

View File

@@ -1,6 +1,6 @@
import { getTagColorsFromName } from '@grafana/ui';
import { BackendSrv } from 'app/core/services/backend_srv';
import { NavModelSrv } from 'app/core/core';
import tags from 'app/core/utils/tags';
export default class AdminListUsersCtrl {
users: any;
@@ -63,7 +63,7 @@ function getAuthLabelStyle(label: string) {
return {};
}
const { color, borderColor } = tags.getTagColorsFromName(label);
const { color, borderColor } = getTagColorsFromName(label);
return {
'background-color': color,
'border-color': borderColor,

View File

@@ -1,114 +0,0 @@
<div class="gf-form-group">
<h3 class="page-heading">HTTP</h3>
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-10">URL</span>
<input class="gf-form-input gf-form-input--has-help-icon" type="text"
ng-model='current.url' placeholder="{{suggestUrl}}"
bs-typeahead="getSuggestUrls" min-length="0"
ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
<info-popover mode="right-absolute">
<p>Specify a complete HTTP URL (for example http://your_server:8080)</p>
<span ng-show="current.access === 'direct'">
Your access method is <em>Browser</em>, this means the URL
needs to be accessible from the browser.
</span>
<span ng-show="current.access === 'proxy'">
Your access method is <em>Server</em>, this means the URL
needs to be accessible from the grafana backend/server.
</span>
</info-popover>
</div>
</div>
<div class="gf-form-inline" ng-if="showAccessOption">
<div class="gf-form max-width-30">
<span class="gf-form-label width-10">Access</span>
<div class="gf-form-select-wrapper max-width-24">
<select class="gf-form-input" ng-model="current.access" ng-options="f.key as f.value for f in [{key: 'proxy', value: 'Server (Default)'}, { key: 'direct', value: 'Browser'}]"></select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword pointer" ng-click="toggleAccessHelp()">
Help&nbsp;
<i class="fa fa-caret-down" ng-show="showAccessHelp"></i>
<i class="fa fa-caret-right" ng-hide="showAccessHelp">&nbsp;</i>
</label>
</div>
</div>
<div class="grafana-info-box m-t-2" ng-show="showAccessHelp">
<p>
Access mode controls how requests to the data source will be handled.
<strong><i>Server</i></strong> should be the preferred way if nothing else stated.
</p>
<div class="alert-title">Server access mode (Default):</div>
<p>
All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to the data source
and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements.
The URL needs to be accessible from the grafana backend/server if you select this access mode.
</p>
<div class="alert-title">Browser access mode:</div>
<p>
All requests will be made from the browser directly to the data source and may be subject to
Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this
access mode.
</div>
<div class="gf-form-inline" ng-if="current.access=='proxy'">
<div class="gf-form">
<span class="gf-form-label width-10">Whitelisted Cookies</span>
<bootstrap-tagsinput ng-model="current.jsonData.keepCookies" width-class="width-20 gf-form-input--has-help-icon" tagclass="label label-tag" placeholder="Add Name">
</bootstrap-tagsinput>
<info-popover mode="right-absolute">
Grafana Proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source.
</info-popover>
</div>
</div>
</div>
<h3 class="page-heading">Auth</h3>
<div class="gf-form-group">
<div class="gf-form-inline">
<gf-form-checkbox class="gf-form" label="Basic Auth" checked="current.basicAuth" label-class="width-13" switch-class="max-width-6"></gf-form-checkbox>
<gf-form-checkbox class="gf-form" label="With Credentials" tooltip="Whether credentials such as cookies or auth
headers should be sent with cross-site requests." checked="current.withCredentials" label-class="width-13"
switch-class="max-width-6"></gf-form-checkbox>
</div>
<div class="gf-form-inline">
<gf-form-checkbox class="gf-form" ng-if="current.access=='proxy'" label="TLS Client Auth" label-class="width-13"
checked="current.jsonData.tlsAuth" switch-class="max-width-6"></gf-form-checkbox>
<gf-form-checkbox class="gf-form" ng-if="current.access=='proxy'" label="With CA Cert" tooltip="Needed for
verifing self-signed TLS Certs" checked="current.jsonData.tlsAuthWithCACert" label-class="width-13"
switch-class="max-width-6"></gf-form-checkbox>
</div>
<div class="gf-form-inline">
<gf-form-checkbox class="gf-form" ng-if="current.access=='proxy'" label="Skip TLS Verify" label-class="width-13"
checked="current.jsonData.tlsSkipVerify" switch-class="max-width-6"></gf-form-checkbox>
</div>
<div class="gf-form-inline">
<gf-form-checkbox class="gf-form" ng-if="current.access=='proxy'" label="Forward OAuth Identity" label-class="width-13" tooltip="Forward the user's upstream OAuth identity to the datasource (Their access token gets passed along)." checked="current.jsonData.oauthPassThru" switch-class="max-width-6"></gf-form-checkbox>
</div>
</div>
<div class="gf-form-group" ng-if="current.basicAuth">
<h6>Basic Auth Details</h6>
<div class="gf-form" ng-if="current.basicAuth">
<span class="gf-form-label width-10">User</span>
<input class="gf-form-input max-width-21" type="text" ng-model='current.basicAuthUser' placeholder="user" required></input>
</div>
<div class="gf-form">
<secret-form-field
isConfigured="current.basicAuthPassword || current.secureJsonFields.basicAuthPassword"
value="current.secureJsonData.basicAuthPassword || ''"
on-reset="onBasicAuthPasswordReset"
on-change="onBasicAuthPasswordChange"
inputWidth="18"
labelWidth="10"
/>
</div>
</div>
<datasource-tls-auth-settings current="current" ng-if="(current.jsonData.tlsAuth || current.jsonData.tlsAuthWithCACert) && current.access=='proxy'">
</datasource-tls-auth-settings>

View File

@@ -0,0 +1 @@
<datasource-http-settings-next on-change="onChange" dataSourceConfig="current" showAccessOptions="showAccessOption" defaultUrl="suggestUrl" />

View File

@@ -1,62 +0,0 @@
<div class="gf-form-group">
<div class="gf-form">
<h6>TLS Auth Details</h6>
<info-popover mode="header">TLS Certs are encrypted and stored in the Grafana database.</info-popover>
</div>
<div ng-if="current.jsonData.tlsAuthWithCACert">
<div class="gf-form-inline">
<div class="gf-form gf-form--v-stretch"><label class="gf-form-label width-7">CA Cert</label></div>
<div class="gf-form gf-form--grow" ng-if="!current.secureJsonFields.tlsCACert">
<textarea
rows="7"
class="gf-form-input gf-form-textarea"
ng-model="current.secureJsonData.tlsCACert"
placeholder="Begins with -----BEGIN CERTIFICATE-----"
></textarea>
</div>
<div class="gf-form" ng-if="current.secureJsonFields.tlsCACert">
<input type="text" class="gf-form-input max-width-12" disabled="disabled" value="configured" />
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="current.secureJsonFields.tlsCACert = false">reset</a>
</div>
</div>
</div>
<div ng-if="current.jsonData.tlsAuth">
<div class="gf-form-inline">
<div class="gf-form gf-form--v-stretch"><label class="gf-form-label width-7">Client Cert</label></div>
<div class="gf-form gf-form--grow" ng-if="!current.secureJsonFields.tlsClientCert">
<textarea
rows="7"
class="gf-form-input gf-form-textarea"
ng-model="current.secureJsonData.tlsClientCert"
placeholder="Begins with -----BEGIN CERTIFICATE-----"
required
></textarea>
</div>
<div class="gf-form" ng-if="current.secureJsonFields.tlsClientCert">
<input type="text" class="gf-form-input max-width-12" disabled="disabled" value="configured" />
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="current.secureJsonFields.tlsClientCert = false"
>reset</a
>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form gf-form--v-stretch"><label class="gf-form-label width-7">Client Key</label></div>
<div class="gf-form gf-form--grow" ng-if="!current.secureJsonFields.tlsClientKey">
<textarea
rows="7"
class="gf-form-input gf-form-textarea"
ng-model="current.secureJsonData.tlsClientKey"
placeholder="Begins with -----BEGIN RSA PRIVATE KEY-----"
required
></textarea>
</div>
<div class="gf-form" ng-if="current.secureJsonFields.tlsClientKey">
<input type="text" class="gf-form-input max-width-12" disabled="disabled" value="configured" />
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="current.secureJsonFields.tlsClientKey = false">reset</a>
</div>
</div>
</div>
</div>

View File

@@ -1,5 +1,4 @@
import { coreModule } from 'app/core/core';
import { createChangeHandler, createResetHandler, PasswordFieldEnum } from '../utils/passwordHandlers';
coreModule.directive('datasourceHttpSettings', () => {
return {
@@ -8,22 +7,14 @@ coreModule.directive('datasourceHttpSettings', () => {
suggestUrl: '@',
noDirectAccess: '@',
},
templateUrl: 'public/app/features/datasources/partials/http_settings.html',
templateUrl: 'public/app/features/datasources/partials/http_settings_next.html',
link: {
pre: ($scope: any, elem, attrs) => {
pre: ($scope: any) => {
// do not show access option if direct access is disabled
$scope.showAccessOption = $scope.noDirectAccess !== 'true';
$scope.showAccessHelp = false;
$scope.toggleAccessHelp = () => {
$scope.showAccessHelp = !$scope.showAccessHelp;
$scope.onChange = (datasourceSetting: any) => {
$scope.current = datasourceSetting;
};
$scope.getSuggestUrls = () => {
return [$scope.suggestUrl];
};
$scope.onBasicAuthPasswordReset = createResetHandler($scope, PasswordFieldEnum.BasicAuthPassword);
$scope.onBasicAuthPasswordChange = createChangeHandler($scope, PasswordFieldEnum.BasicAuthPassword);
},
},
};