فهرست منبع

created component for http settings

Peter Holmberg 7 سال پیش
والد
کامیت
e642fce4a5

+ 7 - 0
public/app/core/components/InfoPopover/InfoPopover.tsx

@@ -0,0 +1,7 @@
+import React, { SFC } from 'react';
+interface Props {}
+const InfoPopover: SFC<Props> = props => {
+  return <div />;
+};
+
+export default InfoPopover;

+ 223 - 0
public/app/features/datasources/DataSourceHttpSettings.tsx

@@ -0,0 +1,223 @@
+import React, { PureComponent } from 'react';
+
+interface Props {
+  access: any;
+  basicAuth: any;
+  showAccessOption: any;
+  tlsAuth: any;
+  tlsAuthWithCACert: any;
+  tlsCACert: any;
+  tlsClientCert: any;
+  tlsClientKey: any;
+  url: any;
+}
+
+interface State {
+  basicAuthUser: string;
+  basicAuthPassword: string;
+  showAccessHelp: boolean;
+}
+
+export default class DataSourceHttpSettings extends PureComponent<Props, State> {
+  state = {
+    basicAuthUser: '',
+    basicAuthPassword: '',
+    showAccessHelp: false,
+  };
+
+  onToggleAccessHelp = () => {};
+
+  render() {
+    const {
+      access,
+      basicAuth,
+      showAccessOption,
+      tlsAuth,
+      tlsAuthWithCACert,
+      tlsCACert,
+      tlsClientCert,
+      tlsClientKey,
+      url,
+    } = this.props;
+
+    const { showAccessHelp, basicAuthUser, basicAuthPassword } = this.state;
+
+    // const accessOptions = [{key: 'proxy', value: 'Server (Default)'}, { key: 'direct', value: 'Browser'}];
+
+    return (
+      <div className="gf-form-group">
+        <h3 className="page-heading">HTTP</h3>
+        <div className="gf-form-group">
+          <div className="gf-form-inline">
+            <div className="gf-form max-width-30">
+              <span className="gf-form-label width-10">URL</span>
+              <input className="gf-form-input" type="text" value={url} placeholder="https://localhost:9090" />
+            </div>
+          </div>
+          {showAccessOption && (
+            <div className="gf-form-inline">
+              <div className="gf-form max-width-30">
+                <span className="gf-form-label width-10">Access</span>
+                <div className="gf-form-select-wrapper max-width-24" />
+              </div>
+              <div className="gf-form">
+                <label className="gf-form-label query-keyword pointer" onClick={this.onToggleAccessHelp}>
+                  Help&nbsp;
+                  {showAccessHelp && <i className="fa fa-caret-down" />}
+                  {!showAccessHelp && <i className="fa fa-caret-right">&nbsp;</i>}
+                </label>
+              </div>
+            </div>
+          )}
+
+          {showAccessHelp && (
+            <div className="grafana-info-box m-t-2">
+              <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 className="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 className="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.
+              </p>
+            </div>
+          )}
+          {access === 'proxy' && (
+            <div className="gf-form-inline">
+              <div className="gf-form">
+                <span className="gf-form-label width-10">Whitelisted Cookies</span>
+              </div>
+            </div>
+          )}
+
+          <h3 className="page-heading">Auth</h3>
+          <div className="gf-form-group">
+            <div className="gf-form-inline" />
+            <div className="gf-form-inline" />
+            <div className="gf-form-inline" />
+          </div>
+
+          {basicAuth && (
+            <div className="gf-form-group">
+              <h6>Basic Auth Details</h6>
+              <div className="gf-form">
+                <span className="gf-form-label width-10">User</span>
+                <input className="gf-form-input max-width-21" type="text" value={basicAuthUser} placeholder="User" />
+              </div>
+              <div className="gf-form">
+                <span className="gf-form-label width-10">Password</span>
+                <input
+                  className="gf-form-input max-width-21"
+                  type="password"
+                  value={basicAuthPassword}
+                  placeholder="Password"
+                />
+              </div>
+            </div>
+          )}
+
+          {(tlsAuth || tlsAuthWithCACert) &&
+            access === 'proxy' && (
+              <div className="gf-form-group">
+                <div className="gf-form">
+                  <h6>TLS Auth Details</h6>
+                </div>
+                {tlsAuthWithCACert && (
+                  <div>
+                    <div className="gf-form-inline">
+                      <div className="gf-form gf-form--v-stretch">
+                        <label className="gf-form-label width-7">CA Cert</label>
+                      </div>
+                      {!tlsCACert && (
+                        <div className="gf-form gf-form--grow">
+                          <textarea
+                            rows={7}
+                            className="gf-form-input gf-form-textarea"
+                            value={tlsCACert}
+                            placeholder="Begins with -----BEGIN CERTIFICATE-----"
+                          />
+                        </div>
+                      )}
+                      {tlsCACert && (
+                        <div className="gf-form">
+                          <input type="text" className="gf-form-input max-width-12" value="configured" />
+                          <a className="btn btn-secondary gf-form-btn" href="#" onClick={() => {}}>
+                            reset
+                          </a>
+                        </div>
+                      )}
+                    </div>
+                  </div>
+                )}
+
+                {tlsAuth && (
+                  <div>
+                    <div className="gf-form-inline">
+                      <div className="gf-form gf-form--v-stretch">
+                        <label className="gf-form-label width-7">Client Cert</label>
+                      </div>
+                      {!tlsClientCert && (
+                        <div className="gf-form gf-form--grow">
+                          <textarea
+                            rows={7}
+                            className="gf-form-input gf-form-textarea"
+                            value={tlsClientCert}
+                            placeholder="Begins with -----BEGIN CERTIFICATE-----"
+                            required
+                          />
+                        </div>
+                      )}
+                      {tlsClientCert && (
+                        <div className="gf-form">
+                          <input type="text" className="gf-form-input max-width-12" value="configured" />
+                          <a className="btn btn-secondary gf-form-btn" href="#" onClick={() => {}}>
+                            reset
+                          </a>
+                        </div>
+                      )}
+                    </div>
+
+                    <div className="gf-form-inline">
+                      <div className="gf-form gf-form--v-stretch">
+                        <label className="gf-form-label width-7">Client Key</label>
+                      </div>
+                      {tlsClientKey && (
+                        <div className="gf-form gf-form--grow">
+                          <textarea
+                            rows={7}
+                            className="gf-form-input gf-form-textarea"
+                            value={tlsClientKey}
+                            placeholder="Begins with -----BEGIN RSA PRIVATE KEY-----"
+                          />
+                        </div>
+                      )}
+                      {tlsClientKey && (
+                        <div className="gf-form">
+                          <input type="text" className="gf-form-input max-width-12" value="configured" />
+                          <a className="btn btn-secondary gf-form-btn" href="#" onClick={() => {}}>
+                            reset
+                          </a>
+                        </div>
+                      )}
+                    </div>
+                  </div>
+                )}
+              </div>
+            )}
+        </div>
+      </div>
+    );
+  }
+}

+ 48 - 10
public/app/features/datasources/DataSourceSettings.tsx

@@ -1,6 +1,7 @@
 import React, { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import { DataSource, Plugin } from 'app/types';
+import DataSourceHttpSettings from './DataSourceHttpSettings';
 
 export interface Props {
   dataSource: DataSource;
@@ -8,6 +9,7 @@ export interface Props {
 }
 interface State {
   name: string;
+  showNamePopover: boolean;
 }
 
 enum DataSourceStates {
@@ -16,13 +18,10 @@ enum DataSourceStates {
 }
 
 export class DataSourceSettings extends PureComponent<Props, State> {
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      name: props.dataSource.name,
-    };
-  }
+  state = {
+    name: this.props.dataSource.name,
+    showNamePopover: false,
+  };
 
   onNameChange = event => {
     this.setState({
@@ -39,6 +38,12 @@ export class DataSourceSettings extends PureComponent<Props, State> {
     console.log(event);
   };
 
+  onTogglePopover = () => {
+    this.setState(prevState => ({
+      showNamePopover: !prevState.showNamePopover,
+    }));
+  };
+
   isReadyOnly() {
     return this.props.dataSource.readOnly === true;
   }
@@ -70,11 +75,22 @@ export class DataSourceSettings extends PureComponent<Props, State> {
   }
 
   render() {
-    const { name } = this.state;
+    const { name, showNamePopover } = this.state;
+
+    const props = {
+      access: {},
+      basicAuth: {},
+      showAccessOption: {},
+      tlsAuth: {},
+      tlsAuthWithCACert: {},
+      tlsCACert: {},
+      tlsClientCert: {},
+      tlsClientKey: {},
+      url: {},
+    };
 
     return (
       <div>
-        <h3 className="page-sub-heading">Settings</h3>
         <form onSubmit={this.onSubmit}>
           <div className="gf-form-group">
             <div className="gf-form-inline">
@@ -84,10 +100,31 @@ export class DataSourceSettings extends PureComponent<Props, State> {
                   className="gf-form-input max-width-23"
                   type="text"
                   value={name}
-                  placeholder="name"
+                  placeholder="Name"
                   onChange={this.onNameChange}
                   required
                 />
+                <div onClick={this.onTogglePopover}>
+                  <i className="fa fa-info-circle" />
+                </div>
+                {showNamePopover && (
+                  <div
+                    style={{
+                      position: 'absolute',
+                      left: '450px',
+                      top: '-20px',
+                      padding: '10px',
+                      backgroundColor: 'black',
+                      zIndex: 2,
+                      width: '300px',
+                      border: '1px solid gray',
+                      borderRadius: '3px',
+                    }}
+                  >
+                    The name is used when you select the data source in panels. The <em>Default</em> data source is
+                    preselected in new panels.
+                  </div>
+                )}
               </div>
             </div>
           </div>
@@ -110,6 +147,7 @@ export class DataSourceSettings extends PureComponent<Props, State> {
             </a>
           </div>
         </form>
+        <DataSourceHttpSettings {...props} />
       </div>
     );
   }

+ 74 - 0
public/app/features/datasources/EditDataSourcePage.test.tsx

@@ -0,0 +1,74 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { EditDataSourcePage, Props } from './EditDataSourcePage';
+import { DataSource, NavModel } from '../../types';
+
+const setup = (propOverrides?: object) => {
+  const props: Props = {
+    navModel: {} as NavModel,
+    dataSource: {} as DataSource,
+    dataSourceId: 1,
+    pageName: '',
+    loadDataSource: jest.fn(),
+  };
+
+  Object.assign(props, propOverrides);
+
+  const wrapper = shallow(<EditDataSourcePage {...props} />);
+  const instance = wrapper.instance() as EditDataSourcePage;
+
+  return {
+    wrapper,
+    instance,
+  };
+};
+
+describe('Render', () => {
+  it('should render component', () => {
+    const { wrapper } = setup();
+
+    expect(wrapper).toMatchSnapshot();
+  });
+
+  it('should render permissions page', () => {
+    const { wrapper } = setup({
+      pageName: 'permissions',
+    });
+
+    expect(wrapper).toMatchSnapshot();
+  });
+});
+
+describe('Functions', () => {
+  describe('is page valid', () => {
+    it('should be a valid page', () => {
+      const { instance } = setup();
+
+      expect(instance.isValidPage('permissions')).toBeTruthy();
+    });
+
+    it('should not be a valid page', () => {
+      const { instance } = setup();
+
+      expect(instance.isValidPage('asdf')).toBeFalsy();
+    });
+  });
+
+  describe('get current page', () => {
+    it('should return permissions', () => {
+      const { instance } = setup({
+        pageName: 'permissions',
+      });
+
+      expect(instance.getCurrentPage()).toEqual('permissions');
+    });
+
+    it('should return settings if bogus route', () => {
+      const { instance } = setup({
+        pageName: 'asdf',
+      });
+
+      expect(instance.getCurrentPage()).toEqual('settings');
+    });
+  });
+});

+ 7 - 3
public/app/features/datasources/EditDataSourcePage.tsx

@@ -3,6 +3,7 @@ import { hot } from 'react-hot-loader';
 import { connect } from 'react-redux';
 import PageHeader from '../../core/components/PageHeader/PageHeader';
 import DataSourcePermissions from './DataSourcePermissions';
+import DataSourceSettings from './DataSourceSettings';
 import { DataSource, NavModel } from 'app/types';
 import { loadDataSource } from './state/actions';
 import { getNavModel } from '../../core/selectors/navModel';
@@ -24,6 +25,8 @@ enum PageTypes {
   Dashboards = 'dashboards',
 }
 
+const fallBackPage = PageTypes.Settings;
+
 export class EditDataSourcePage extends PureComponent<Props> {
   componentDidMount() {
     this.fetchDataSource();
@@ -39,12 +42,13 @@ export class EditDataSourcePage extends PureComponent<Props> {
 
   getCurrentPage() {
     const currentPage = this.props.pageName;
-
-    return this.isValidPage(currentPage) ? currentPage : PageTypes.Permissions;
+    return this.isValidPage(currentPage) ? currentPage : fallBackPage;
   }
 
   renderPage() {
     switch (this.getCurrentPage()) {
+      case PageTypes.Settings:
+        return <DataSourceSettings />;
       case PageTypes.Permissions:
         return <DataSourcePermissions />;
     }
@@ -65,7 +69,7 @@ export class EditDataSourcePage extends PureComponent<Props> {
 }
 
 function mapStateToProps(state) {
-  const pageName = getRouteParamsPage(state.location) || PageTypes.Permissions;
+  const pageName = getRouteParamsPage(state.location) || fallBackPage;
   const dataSourceId = getRouteParamsId(state.location);
   const dataSourceLoadingNav = getDataSourceLoadingNav(pageName);
 

+ 27 - 0
public/app/features/datasources/__snapshots__/EditDataSourcePage.test.tsx.snap

@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Render should render component 1`] = `
+<div>
+  <PageHeader
+    model={Object {}}
+  />
+  <div
+    className="page-container page-body"
+  >
+    <Connect(DataSourceSettings) />
+  </div>
+</div>
+`;
+
+exports[`Render should render permissions page 1`] = `
+<div>
+  <PageHeader
+    model={Object {}}
+  />
+  <div
+    className="page-container page-body"
+  >
+    <Connect(DataSourcePermissions) />
+  </div>
+</div>
+`;

+ 0 - 5
public/app/routes/routes.ts

@@ -73,11 +73,6 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
         component: () => DataSourcesListPage,
       },
     })
-    .when('/datasources/edit/:id', {
-      templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
-      controller: 'DataSourceEditCtrl',
-      controllerAs: 'ctrl',
-    })
     .when('/datasources/edit/:id/dashboards', {
       templateUrl: 'public/app/features/plugins/partials/ds_dashboards.html',
       controller: 'DataSourceDashboardsCtrl',