From 813e3ffc15442a8199b04671d08b132efe0a55cf Mon Sep 17 00:00:00 2001
From: Ryan McKinley
Date: Fri, 10 May 2019 22:41:32 -0700
Subject: [PATCH 001/166] GraphPanel: show results for all SeriesData (#16966)
* Graph panel should support SeriesData
* Graph panel should support SeriesData
* same path for all series
* merge master
* support docs
* add test for processor
* Graph: removed old unused data processing logic
* Graph: minor refactoring data processing
* fix histogram
* set Count as title
---
public/app/plugins/panel/graph/axes_editor.ts | 11 +-
.../app/plugins/panel/graph/data_processor.ts | 189 +++++---------
public/app/plugins/panel/graph/module.ts | 9 +-
.../__snapshots__/data_processor.test.ts.snap | 233 ++++++++++++++++++
.../panel/graph/specs/data_processor.test.ts | 86 ++++---
5 files changed, 346 insertions(+), 182 deletions(-)
create mode 100644 public/app/plugins/panel/graph/specs/__snapshots__/data_processor.test.ts.snap
diff --git a/public/app/plugins/panel/graph/axes_editor.ts b/public/app/plugins/panel/graph/axes_editor.ts
index 04ef62f16fbb..2cb7aa4eed0a 100644
--- a/public/app/plugins/panel/graph/axes_editor.ts
+++ b/public/app/plugins/panel/graph/axes_editor.ts
@@ -10,7 +10,7 @@ export class AxesEditorCtrl {
xNameSegment: any;
/** @ngInject */
- constructor(private $scope, private $q) {
+ constructor(private $scope) {
this.panelCtrl = $scope.ctrl;
this.panel = this.panelCtrl.panel;
this.$scope.ctrl = this;
@@ -65,15 +65,6 @@ export class AxesEditorCtrl {
xAxisValueChanged() {
this.panelCtrl.onDataReceived(this.panelCtrl.dataList);
}
-
- getDataFieldNames(onlyNumbers) {
- const props = this.panelCtrl.processor.getDataFieldNames(this.panelCtrl.dataList, onlyNumbers);
- const items = props.map(prop => {
- return { text: prop, value: prop };
- });
-
- return this.$q.when(items);
- }
}
/** @ngInject */
diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts
index 4b57934824cc..37fe9f3e2195 100644
--- a/public/app/plugins/panel/graph/data_processor.ts
+++ b/public/app/plugins/panel/graph/data_processor.ts
@@ -1,11 +1,10 @@
import _ from 'lodash';
-import { colors, getColorFromHexRgbOrName } from '@grafana/ui';
+import { TimeRange, colors, getColorFromHexRgbOrName, FieldCache, FieldType, Field, SeriesData } from '@grafana/ui';
import TimeSeries from 'app/core/time_series2';
import config from 'app/core/config';
-import { LegacyResponseData, TimeRange } from '@grafana/ui';
type Options = {
- dataList: LegacyResponseData[];
+ dataList: SeriesData[];
range?: TimeRange;
};
@@ -13,68 +12,81 @@ export class DataProcessor {
constructor(private panel) {}
getSeriesList(options: Options): TimeSeries[] {
- if (!options.dataList || options.dataList.length === 0) {
- return [];
- }
+ const list: TimeSeries[] = [];
+ const { dataList, range } = options;
- // auto detect xaxis mode
- let firstItem;
- if (options.dataList && options.dataList.length > 0) {
- firstItem = options.dataList[0];
- const autoDetectMode = this.getAutoDetectXAxisMode(firstItem);
- if (this.panel.xaxis.mode !== autoDetectMode) {
- this.panel.xaxis.mode = autoDetectMode;
- this.setPanelDefaultsForNewXAxisMode();
- }
+ if (!dataList || !dataList.length) {
+ return list;
}
- switch (this.panel.xaxis.mode) {
- case 'series':
- case 'time': {
- return options.dataList.map((item, index) => {
- return this.timeSeriesHandler(item, index, options);
- });
+ for (const series of dataList) {
+ const { fields } = series;
+ const cache = new FieldCache(fields);
+ const time = cache.getFirstFieldOfType(FieldType.time);
+
+ if (!time) {
+ continue;
}
- case 'histogram': {
- let histogramDataList;
- if (this.panel.stack) {
- histogramDataList = options.dataList;
- } else {
- histogramDataList = [
- {
- target: 'count',
- datapoints: _.concat([], _.flatten(_.map(options.dataList, 'datapoints'))),
- },
- ];
+
+ const seriesName = series.name ? series.name : series.refId;
+
+ for (let i = 0; i < fields.length; i++) {
+ if (fields[i].type !== FieldType.number) {
+ continue;
}
- return histogramDataList.map((item, index) => {
- return this.timeSeriesHandler(item, index, options);
- });
- }
- case 'field': {
- return this.customHandler(firstItem);
+
+ const field = fields[i];
+ let name = field.title;
+
+ if (!field.title) {
+ name = field.name;
+ }
+
+ if (seriesName && dataList.length > 0 && name !== seriesName) {
+ name = seriesName + ' ' + name;
+ }
+
+ const datapoints = [];
+ for (const row of series.rows) {
+ datapoints.push([row[i], row[time.index]]);
+ }
+
+ list.push(this.toTimeSeries(field, name, datapoints, list.length, range));
}
}
- return [];
+ // Merge all the rows if we want to show a histogram
+ if (this.panel.xaxis.mode === 'histogram' && !this.panel.stack && list.length > 1) {
+ const first = list[0];
+ first.alias = first.aliasEscaped = 'Count';
+ for (let i = 1; i < list.length; i++) {
+ first.datapoints = first.datapoints.concat(list[i].datapoints);
+ }
+ return [first];
+ }
+ return list;
}
- getAutoDetectXAxisMode(firstItem) {
- switch (firstItem.type) {
- case 'docs':
- return 'field';
- case 'table':
- return 'field';
- default: {
- if (this.panel.xaxis.mode === 'series') {
- return 'series';
- }
- if (this.panel.xaxis.mode === 'histogram') {
- return 'histogram';
- }
- return 'time';
+ private toTimeSeries(field: Field, alias: string, datapoints: any[][], index: number, range?: TimeRange) {
+ const colorIndex = index % colors.length;
+ const color = this.panel.aliasColors[alias] || colors[colorIndex];
+
+ const series = new TimeSeries({
+ datapoints: datapoints || [],
+ alias: alias,
+ color: getColorFromHexRgbOrName(color, config.theme.type),
+ unit: field.unit,
+ });
+
+ if (datapoints && datapoints.length > 0 && range) {
+ const last = datapoints[datapoints.length - 1][1];
+ const from = range.from;
+
+ if (last - from.valueOf() < -10000) {
+ series.isOutsideRange = true;
}
}
+ return series;
}
setPanelDefaultsForNewXAxisMode() {
@@ -110,43 +122,6 @@ export class DataProcessor {
}
}
- timeSeriesHandler(seriesData: LegacyResponseData, index: number, options: Options) {
- const datapoints = seriesData.datapoints || [];
- const alias = seriesData.target;
-
- const colorIndex = index % colors.length;
-
- const color = this.panel.aliasColors[alias] || colors[colorIndex];
-
- const series = new TimeSeries({
- datapoints: datapoints,
- alias: alias,
- color: getColorFromHexRgbOrName(color, config.theme.type),
- unit: seriesData.unit,
- });
-
- if (datapoints && datapoints.length > 0) {
- const last = datapoints[datapoints.length - 1][1];
- const from = options.range.from;
-
- if (last - from.valueOf() < -10000) {
- series.isOutsideRange = true;
- }
- }
-
- return series;
- }
-
- customHandler(dataItem) {
- const nameField = this.panel.xaxis.name;
- if (!nameField) {
- throw {
- message: 'No field name specified to use for x-axis, check your axes settings',
- };
- }
- return [];
- }
-
validateXAxisSeriesValue() {
switch (this.panel.xaxis.mode) {
case 'series': {
@@ -165,40 +140,6 @@ export class DataProcessor {
}
}
- getDataFieldNames(dataList, onlyNumbers) {
- if (dataList.length === 0) {
- return [];
- }
-
- const fields = [];
- const firstItem = dataList[0];
- const fieldParts = [];
-
- function getPropertiesRecursive(obj) {
- _.forEach(obj, (value, key) => {
- if (_.isObject(value)) {
- fieldParts.push(key);
- getPropertiesRecursive(value);
- } else {
- if (!onlyNumbers || _.isNumber(value)) {
- const field = fieldParts.concat(key).join('.');
- fields.push(field);
- }
- }
- });
- fieldParts.pop();
- }
-
- if (firstItem.type === 'docs') {
- if (firstItem.datapoints.length === 0) {
- return [];
- }
- getPropertiesRecursive(firstItem.datapoints[0]);
- }
-
- return fields;
- }
-
getXAxisValueOptions(options) {
switch (this.panel.xaxis.mode) {
case 'series': {
diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts
index 2412224eca6b..f34ed05039d7 100644
--- a/public/app/plugins/panel/graph/module.ts
+++ b/public/app/plugins/panel/graph/module.ts
@@ -11,7 +11,8 @@ import { DataProcessor } from './data_processor';
import { axesEditorComponent } from './axes_editor';
import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
-import { getColorFromHexRgbOrName, LegacyResponseData } from '@grafana/ui';
+import { getColorFromHexRgbOrName, LegacyResponseData, SeriesData } from '@grafana/ui';
+import { getProcessedSeriesData } from 'app/features/dashboard/state/PanelQueryState';
class GraphCtrl extends MetricsPanelCtrl {
static template = template;
@@ -19,7 +20,7 @@ class GraphCtrl extends MetricsPanelCtrl {
renderError: boolean;
hiddenSeries: any = {};
seriesList: TimeSeries[] = [];
- dataList: LegacyResponseData[] = [];
+ dataList: SeriesData[] = [];
annotations: any = [];
alertState: any;
@@ -188,9 +189,9 @@ class GraphCtrl extends MetricsPanelCtrl {
}
onDataReceived(dataList: LegacyResponseData[]) {
- this.dataList = dataList;
+ this.dataList = getProcessedSeriesData(dataList);
this.seriesList = this.processor.getSeriesList({
- dataList: dataList,
+ dataList: this.dataList,
range: this.range,
});
diff --git a/public/app/plugins/panel/graph/specs/__snapshots__/data_processor.test.ts.snap b/public/app/plugins/panel/graph/specs/__snapshots__/data_processor.test.ts.snap
new file mode 100644
index 000000000000..99c3366fff3c
--- /dev/null
+++ b/public/app/plugins/panel/graph/specs/__snapshots__/data_processor.test.ts.snap
@@ -0,0 +1,233 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return a new series for each field 1`] = `
+Array [
+ TimeSeries {
+ "alias": "Value",
+ "aliasEscaped": "Value",
+ "bars": Object {
+ "fillColor": "#7EB26D",
+ },
+ "color": "#7EB26D",
+ "datapoints": Array [
+ Array [
+ 1,
+ 1001,
+ ],
+ Array [
+ 2,
+ 1002,
+ ],
+ Array [
+ 3,
+ 1003,
+ ],
+ ],
+ "hasMsResolution": false,
+ "id": "Value",
+ "label": "Value",
+ "legend": true,
+ "stats": Object {},
+ "unit": "watt",
+ "valueFormater": [Function],
+ },
+ TimeSeries {
+ "alias": "table_data v1",
+ "aliasEscaped": "table_data v1",
+ "bars": Object {
+ "fillColor": "#EAB839",
+ },
+ "color": "#EAB839",
+ "datapoints": Array [
+ Array [
+ 0.1,
+ 1001,
+ ],
+ Array [
+ 0.2,
+ 1002,
+ ],
+ Array [
+ 0.3,
+ 1003,
+ ],
+ ],
+ "hasMsResolution": false,
+ "id": "table_data v1",
+ "label": "table_data v1",
+ "legend": true,
+ "stats": Object {},
+ "unit": "ohm",
+ "valueFormater": [Function],
+ },
+ TimeSeries {
+ "alias": "table_data v2",
+ "aliasEscaped": "table_data v2",
+ "bars": Object {
+ "fillColor": "#6ED0E0",
+ },
+ "color": "#6ED0E0",
+ "datapoints": Array [
+ Array [
+ 1.1,
+ 1001,
+ ],
+ Array [
+ 2.2,
+ 1002,
+ ],
+ Array [
+ 3.3,
+ 1003,
+ ],
+ ],
+ "hasMsResolution": false,
+ "id": "table_data v2",
+ "label": "table_data v2",
+ "legend": true,
+ "stats": Object {},
+ "unit": undefined,
+ "valueFormater": [Function],
+ },
+ TimeSeries {
+ "alias": "series v1",
+ "aliasEscaped": "series v1",
+ "bars": Object {
+ "fillColor": "#EF843C",
+ },
+ "color": "#EF843C",
+ "datapoints": Array [
+ Array [
+ 0.1,
+ 1001,
+ ],
+ Array [
+ 0.2,
+ 1002,
+ ],
+ Array [
+ 0.3,
+ 1003,
+ ],
+ ],
+ "hasMsResolution": false,
+ "id": "series v1",
+ "label": "series v1",
+ "legend": true,
+ "stats": Object {},
+ "unit": undefined,
+ "valueFormater": [Function],
+ },
+ TimeSeries {
+ "alias": "series v2",
+ "aliasEscaped": "series v2",
+ "bars": Object {
+ "fillColor": "#E24D42",
+ },
+ "color": "#E24D42",
+ "datapoints": Array [
+ Array [
+ 1.1,
+ 1001,
+ ],
+ Array [
+ 2.2,
+ 1002,
+ ],
+ Array [
+ 3.3,
+ 1003,
+ ],
+ ],
+ "hasMsResolution": false,
+ "id": "series v2",
+ "label": "series v2",
+ "legend": true,
+ "stats": Object {},
+ "unit": undefined,
+ "valueFormater": [Function],
+ },
+]
+`;
+
+exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return single histogram 1`] = `
+Array [
+ TimeSeries {
+ "alias": "Count",
+ "aliasEscaped": "Count",
+ "bars": Object {
+ "fillColor": "#7EB26D",
+ },
+ "color": "#7EB26D",
+ "datapoints": Array [
+ Array [
+ 1,
+ 1001,
+ ],
+ Array [
+ 2,
+ 1002,
+ ],
+ Array [
+ 3,
+ 1003,
+ ],
+ Array [
+ 0.1,
+ 1001,
+ ],
+ Array [
+ 0.2,
+ 1002,
+ ],
+ Array [
+ 0.3,
+ 1003,
+ ],
+ Array [
+ 1.1,
+ 1001,
+ ],
+ Array [
+ 2.2,
+ 1002,
+ ],
+ Array [
+ 3.3,
+ 1003,
+ ],
+ Array [
+ 0.1,
+ 1001,
+ ],
+ Array [
+ 0.2,
+ 1002,
+ ],
+ Array [
+ 0.3,
+ 1003,
+ ],
+ Array [
+ 1.1,
+ 1001,
+ ],
+ Array [
+ 2.2,
+ 1002,
+ ],
+ Array [
+ 3.3,
+ 1003,
+ ],
+ ],
+ "hasMsResolution": false,
+ "id": "Value",
+ "label": "Value",
+ "legend": true,
+ "stats": Object {},
+ "unit": "watt",
+ "valueFormater": [Function],
+ },
+]
+`;
diff --git a/public/app/plugins/panel/graph/specs/data_processor.test.ts b/public/app/plugins/panel/graph/specs/data_processor.test.ts
index b919bb0f3b18..d8b791bd3863 100644
--- a/public/app/plugins/panel/graph/specs/data_processor.test.ts
+++ b/public/app/plugins/panel/graph/specs/data_processor.test.ts
@@ -1,62 +1,60 @@
import { DataProcessor } from '../data_processor';
+import { getProcessedSeriesData } from 'app/features/dashboard/state/PanelQueryState';
describe('Graph DataProcessor', () => {
const panel: any = {
- xaxis: {},
+ xaxis: { mode: 'series' },
+ aliasColors: {},
};
const processor = new DataProcessor(panel);
- describe('Given default xaxis options and query that returns docs', () => {
- beforeEach(() => {
- panel.xaxis.mode = 'time';
- panel.xaxis.name = 'hostname';
- panel.xaxis.values = [];
-
- processor.getSeriesList({
- dataList: [
- {
- type: 'docs',
- datapoints: [{ hostname: 'server1', avg: 10 }],
- },
+ describe('getTimeSeries from LegacyResponseData', () => {
+ // Try each type of data
+ const dataList = getProcessedSeriesData([
+ {
+ alias: 'First (time_series)',
+ datapoints: [[1, 1001], [2, 1002], [3, 1003]],
+ unit: 'watt',
+ },
+ {
+ name: 'table_data',
+ columns: [
+ { text: 'time' },
+ { text: 'v1', unit: 'ohm' },
+ { text: 'v2' }, // no unit
+ { text: 'string' }, // skipped
],
- });
- });
-
- it('Should automatically set xaxis mode to field', () => {
- expect(panel.xaxis.mode).toBe('field');
- });
- });
-
- describe('getDataFieldNames(', () => {
- const dataList = [
+ rows: [
+ [1001, 0.1, 1.1, 'a'], // a
+ [1002, 0.2, 2.2, 'b'], // b
+ [1003, 0.3, 3.3, 'c'], // c
+ ],
+ },
{
- type: 'docs',
- datapoints: [
- {
- hostname: 'server1',
- valueField: 11,
- nested: {
- prop1: 'server2',
- value2: 23,
- },
- },
+ name: 'series',
+ fields: [
+ { name: 'v1' }, // first
+ { name: 'v2' }, // second
+ { name: 'string' }, // skip
+ { name: 'time' }, // Time is last column
],
+ rows: [[0.1, 1.1, 'a', 1001], [0.2, 2.2, 'b', 1002], [0.3, 3.3, 'c', 1003]],
},
- ];
+ ]);
- it('Should return all field names', () => {
- const fields = processor.getDataFieldNames(dataList, false);
- expect(fields).toContain('hostname');
- expect(fields).toContain('valueField');
- expect(fields).toContain('nested.prop1');
- expect(fields).toContain('nested.value2');
+ it('Should return a new series for each field', () => {
+ panel.xaxis.mode = 'series';
+ const series = processor.getSeriesList({ dataList });
+ expect(series.length).toEqual(5);
+ expect(series).toMatchSnapshot();
});
- it('Should return all number fields', () => {
- const fields = processor.getDataFieldNames(dataList, true);
- expect(fields).toContain('valueField');
- expect(fields).toContain('nested.value2');
+ it('Should return single histogram', () => {
+ panel.xaxis.mode = 'histogram';
+ const series = processor.getSeriesList({ dataList });
+ expect(series.length).toEqual(1);
+ expect(series).toMatchSnapshot();
});
});
});
From f12d47ef52f9d423c41542a490925dc25960e93a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Sun, 12 May 2019 14:15:23 +0200
Subject: [PATCH 002/166] Chore: Typescript no-implicit any fixes progress
(#17018)
* Chore: Typescript no-implicit any fixes progress
* Fixed tests
* Updated snapshot
---
.../src/components/Select/Select.tsx | 16 ++---
packages/grafana-ui/src/types/navModel.ts | 2 +
public/app/core/angular_wrappers.ts | 2 -
.../AppNotifications/AppNotificationItem.tsx | 4 +-
.../AppNotifications/AppNotificationList.tsx | 8 +--
.../components/OrgActionBar/OrgActionBar.tsx | 4 +-
.../components/PageHeader/PageHeader.test.tsx | 4 +-
.../PermissionList/AddPermission.tsx | 6 +-
.../PermissionList/PermissionListItem.tsx | 12 ++--
.../core/components/PluginHelp/PluginHelp.tsx | 3 +-
.../core/components/Select/MetricSelect.tsx | 6 +-
.../components/Select/TeamPicker.test.tsx | 1 +
.../app/core/components/Select/TeamPicker.tsx | 6 +-
.../components/Select/UserPicker.test.tsx | 1 +
.../app/core/components/Select/UserPicker.tsx | 6 +-
.../SharedPreferences/SharedPreferences.tsx | 4 +-
.../components/code_editor/code_editor.ts | 8 +--
.../components/colorpicker/spectrum_picker.ts | 4 +-
public/app/core/components/navbar/navbar.html | 12 ----
public/app/core/components/navbar/navbar.ts | 54 ---------------
.../core/components/query_part/query_part.ts | 12 ++--
.../query_part/query_part_editor.ts | 22 +++---
public/app/core/components/scroll/scroll.ts | 3 +-
.../core/components/search/SearchField.tsx | 1 +
.../core/components/search/SearchResult.tsx | 67 -------------------
public/app/core/components/search/search.ts | 25 ++++---
.../core/components/search/search_results.ts | 14 ++--
.../sidemenu/BottomNavLinks.test.tsx | 8 ++-
.../components/sidemenu/BottomNavLinks.tsx | 7 +-
.../components/sidemenu/BottomSection.tsx | 5 +-
.../components/sidemenu/SideMenuDropDown.tsx | 3 +-
.../BottomNavLinks.test.tsx.snap | 4 +-
public/app/core/core.ts | 2 -
.../__snapshots__/TeamMemberRow.test.tsx.snap | 6 +-
public/app/types/acl.ts | 2 +
35 files changed, 111 insertions(+), 233 deletions(-)
delete mode 100644 public/app/core/components/navbar/navbar.html
delete mode 100644 public/app/core/components/navbar/navbar.ts
delete mode 100644 public/app/core/components/search/SearchResult.tsx
diff --git a/packages/grafana-ui/src/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx
index f36f78a50fea..da2b0229c627 100644
--- a/packages/grafana-ui/src/components/Select/Select.tsx
+++ b/packages/grafana-ui/src/components/Select/Select.tsx
@@ -55,11 +55,11 @@ export interface CommonProps {
onCloseMenu?: () => void;
}
-export interface SelectProps {
+export interface SelectProps extends CommonProps {
options: Array>;
}
-interface AsyncProps {
+interface AsyncProps extends CommonProps {
defaultOptions: boolean;
loadOptions: (query: string) => Promise>>;
loadingMessage?: () => string;
@@ -95,9 +95,8 @@ export const MenuList = (props: any) => {
);
};
-export class Select extends PureComponent & SelectProps> {
- static defaultProps = {
- width: null,
+export class Select extends PureComponent> {
+ static defaultProps: Partial> = {
className: '',
isDisabled: false,
isSearchable: true,
@@ -108,7 +107,7 @@ export class Select extends PureComponent & SelectProps> {
isLoading: false,
backspaceRemovesValue: true,
maxMenuHeight: 300,
- menuIsOpen: false,
+ isOpen: false,
components: {
Option: SelectOption,
SingleValue,
@@ -201,9 +200,8 @@ export class Select extends PureComponent & SelectProps> {
}
}
-export class AsyncSelect extends PureComponent & AsyncProps> {
- static defaultProps = {
- width: null,
+export class AsyncSelect extends PureComponent> {
+ static defaultProps: Partial> = {
className: '',
components: {},
loadingMessage: () => 'Loading...',
diff --git a/packages/grafana-ui/src/types/navModel.ts b/packages/grafana-ui/src/types/navModel.ts
index 2f1018eed9ce..7d23f222e149 100644
--- a/packages/grafana-ui/src/types/navModel.ts
+++ b/packages/grafana-ui/src/types/navModel.ts
@@ -7,11 +7,13 @@ export interface NavModelItem {
id?: string;
active?: boolean;
hideFromTabs?: boolean;
+ hideFromMenu?: boolean;
divider?: boolean;
children?: NavModelItem[];
breadcrumbs?: NavModelBreadcrumb[];
target?: string;
parentItem?: NavModelItem;
+ showOrgSwitcher?: boolean;
}
export interface NavModel {
diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts
index 6da0ca1a44df..30163be0edf5 100644
--- a/public/app/core/angular_wrappers.ts
+++ b/public/app/core/angular_wrappers.ts
@@ -4,7 +4,6 @@ import { AnnotationQueryEditor as StackdriverAnnotationQueryEditor } from 'app/p
import { PasswordStrength } from './components/PasswordStrength';
import PageHeader from './components/PageHeader/PageHeader';
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
-import { SearchResult } from './components/search/SearchResult';
import { TagFilter } from './components/TagFilter/TagFilter';
import { SideMenu } from './components/sidemenu/SideMenu';
import { MetricSelect } from './components/Select/MetricSelect';
@@ -20,7 +19,6 @@ export function registerAngularDirectives() {
react2AngularDirective('appNotificationsList', AppNotificationList, []);
react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);
react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
- react2AngularDirective('searchResult', SearchResult, []);
react2AngularDirective('searchField', SearchField, [
'query',
'autoFocus',
diff --git a/public/app/core/components/AppNotifications/AppNotificationItem.tsx b/public/app/core/components/AppNotifications/AppNotificationItem.tsx
index d1fc506d54cd..f1617a0a6351 100644
--- a/public/app/core/components/AppNotifications/AppNotificationItem.tsx
+++ b/public/app/core/components/AppNotifications/AppNotificationItem.tsx
@@ -4,11 +4,11 @@ import { AlertBox } from '../AlertBox/AlertBox';
interface Props {
appNotification: AppNotification;
- onClearNotification: (id) => void;
+ onClearNotification: (id: number) => void;
}
export default class AppNotificationItem extends Component {
- shouldComponentUpdate(nextProps) {
+ shouldComponentUpdate(nextProps: Props) {
return this.props.appNotification.id !== nextProps.appNotification.id;
}
diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx
index c91f8372384f..991ac37eda6c 100644
--- a/public/app/core/components/AppNotifications/AppNotificationList.tsx
+++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx
@@ -20,12 +20,12 @@ export class AppNotificationList extends PureComponent {
componentDidMount() {
const { notifyApp } = this.props;
- appEvents.on('alert-warning', options => notifyApp(createWarningNotification(options[0], options[1])));
- appEvents.on('alert-success', options => notifyApp(createSuccessNotification(options[0], options[1])));
- appEvents.on('alert-error', options => notifyApp(createErrorNotification(options[0], options[1])));
+ appEvents.on('alert-warning', (options: string[]) => notifyApp(createWarningNotification(options[0], options[1])));
+ appEvents.on('alert-success', (options: string[]) => notifyApp(createSuccessNotification(options[0], options[1])));
+ appEvents.on('alert-error', (options: string[]) => notifyApp(createErrorNotification(options[0], options[1])));
}
- onClearAppNotification = id => {
+ onClearAppNotification = (id: number) => {
this.props.clearAppNotification(id);
};
diff --git a/public/app/core/components/OrgActionBar/OrgActionBar.tsx b/public/app/core/components/OrgActionBar/OrgActionBar.tsx
index f83d5c957f74..016c47776686 100644
--- a/public/app/core/components/OrgActionBar/OrgActionBar.tsx
+++ b/public/app/core/components/OrgActionBar/OrgActionBar.tsx
@@ -14,10 +14,10 @@ export interface Props {
export default class OrgActionBar extends PureComponent {
render() {
const { searchQuery, layoutMode, onSetLayoutMode, linkButton, setSearchQuery, target } = this.props;
- const linkProps = { href: linkButton.href, target: undefined };
+ const linkProps = { href: linkButton.href };
if (target) {
- linkProps.target = target;
+ (linkProps as any).target = target;
}
return (
diff --git a/public/app/core/components/PageHeader/PageHeader.test.tsx b/public/app/core/components/PageHeader/PageHeader.test.tsx
index a9ba8d008a3a..1999831a6d55 100644
--- a/public/app/core/components/PageHeader/PageHeader.test.tsx
+++ b/public/app/core/components/PageHeader/PageHeader.test.tsx
@@ -1,9 +1,9 @@
import React from 'react';
import PageHeader from './PageHeader';
-import { shallow } from 'enzyme';
+import { shallow, ShallowWrapper } from 'enzyme';
describe('PageHeader', () => {
- let wrapper;
+ let wrapper: ShallowWrapper;
describe('when the nav tree has a node with a title', () => {
beforeAll(() => {
diff --git a/public/app/core/components/PermissionList/AddPermission.tsx b/public/app/core/components/PermissionList/AddPermission.tsx
index d55ec3511ea0..b668229939ff 100644
--- a/public/app/core/components/PermissionList/AddPermission.tsx
+++ b/public/app/core/components/PermissionList/AddPermission.tsx
@@ -22,7 +22,7 @@ class AddPermissions extends Component {
showPermissionLevels: true,
};
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = this.getCleanState();
}
@@ -36,7 +36,7 @@ class AddPermissions extends Component {
};
}
- onTypeChanged = evt => {
+ onTypeChanged = (evt: any) => {
const type = evt.target.value as AclTarget;
switch (type) {
@@ -65,7 +65,7 @@ class AddPermissions extends Component {
this.setState({ permission: permission.value });
};
- onSubmit = async evt => {
+ onSubmit = async (evt: React.SyntheticEvent) => {
evt.preventDefault();
await this.props.onAddPermission(this.state);
this.setState(this.getCleanState());
diff --git a/public/app/core/components/PermissionList/PermissionListItem.tsx b/public/app/core/components/PermissionList/PermissionListItem.tsx
index b949209d6a38..f590f5fd8ae3 100644
--- a/public/app/core/components/PermissionList/PermissionListItem.tsx
+++ b/public/app/core/components/PermissionList/PermissionListItem.tsx
@@ -1,13 +1,13 @@
import React, { PureComponent } from 'react';
-import { Select } from '@grafana/ui';
+import { Select, SelectOptionItem } from '@grafana/ui';
import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
import { FolderInfo } from 'app/types';
-const setClassNameHelper = inherited => {
+const setClassNameHelper = (inherited: boolean) => {
return inherited ? 'gf-form-disabled' : '';
};
-function ItemAvatar({ item }) {
+function ItemAvatar({ item }: { item: DashboardAcl }) {
if (item.userAvatarUrl) {
return ;
}
@@ -21,7 +21,7 @@ function ItemAvatar({ item }) {
return ;
}
-function ItemDescription({ item }) {
+function ItemDescription({ item }: { item: DashboardAcl }) {
if (item.userId) {
return (User);
}
@@ -39,8 +39,8 @@ interface Props {
}
export default class PermissionsListItem extends PureComponent {
- onPermissionChanged = option => {
- this.props.onPermissionChanged(this.props.item, option.value as PermissionLevel);
+ onPermissionChanged = (option: SelectOptionItem) => {
+ this.props.onPermissionChanged(this.props.item, option.value);
};
onRemoveItem = () => {
diff --git a/public/app/core/components/PluginHelp/PluginHelp.tsx b/public/app/core/components/PluginHelp/PluginHelp.tsx
index c37498afc459..677fb254314e 100644
--- a/public/app/core/components/PluginHelp/PluginHelp.tsx
+++ b/public/app/core/components/PluginHelp/PluginHelp.tsx
@@ -1,4 +1,5 @@
import React, { PureComponent } from 'react';
+// @ts-ignore
import Remarkable from 'remarkable';
import { getBackendSrv } from '../../services/backend_srv';
@@ -37,7 +38,7 @@ export class PluginHelp extends PureComponent {
getBackendSrv()
.get(`/api/plugins/${plugin.id}/markdown/${type}`)
- .then(response => {
+ .then((response: string) => {
const markdown = new Remarkable();
const helpHtml = markdown.render(response);
diff --git a/public/app/core/components/Select/MetricSelect.tsx b/public/app/core/components/Select/MetricSelect.tsx
index e4b6cfb8abd9..aa0ea5d40c45 100644
--- a/public/app/core/components/Select/MetricSelect.tsx
+++ b/public/app/core/components/Select/MetricSelect.tsx
@@ -19,13 +19,13 @@ interface State {
}
export class MetricSelect extends React.Component {
- static defaultProps = {
+ static defaultProps: Partial = {
variables: [],
options: [],
isSearchable: true,
};
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = { options: [] };
}
@@ -45,7 +45,7 @@ export class MetricSelect extends React.Component {
return nextProps.value !== this.props.value || !_.isEqual(nextOptions, this.state.options);
}
- buildOptions({ variables = [], options }) {
+ buildOptions({ variables = [], options }: Props) {
return variables.length > 0 ? [this.getVariablesGroup(), ...options] : options;
}
diff --git a/public/app/core/components/Select/TeamPicker.test.tsx b/public/app/core/components/Select/TeamPicker.test.tsx
index 3db9f7bb4eb4..8ee09461c7fa 100644
--- a/public/app/core/components/Select/TeamPicker.test.tsx
+++ b/public/app/core/components/Select/TeamPicker.test.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+// @ts-ignore
import renderer from 'react-test-renderer';
import { TeamPicker } from './TeamPicker';
diff --git a/public/app/core/components/Select/TeamPicker.tsx b/public/app/core/components/Select/TeamPicker.tsx
index 8d9e1d48d810..16df350ad67f 100644
--- a/public/app/core/components/Select/TeamPicker.tsx
+++ b/public/app/core/components/Select/TeamPicker.tsx
@@ -23,7 +23,7 @@ export interface State {
export class TeamPicker extends Component {
debouncedSearch: any;
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = { isLoading: false };
this.search = this.search.bind(this);
@@ -42,8 +42,8 @@ export class TeamPicker extends Component {
query = '';
}
- return backendSrv.get(`/api/teams/search?perpage=10&page=1&query=${query}`).then(result => {
- const teams = result.teams.map(team => {
+ return backendSrv.get(`/api/teams/search?perpage=10&page=1&query=${query}`).then((result: any) => {
+ const teams = result.teams.map((team: any) => {
return {
id: team.id,
value: team.id,
diff --git a/public/app/core/components/Select/UserPicker.test.tsx b/public/app/core/components/Select/UserPicker.test.tsx
index 054ca643700e..b1dddb48cff0 100644
--- a/public/app/core/components/Select/UserPicker.test.tsx
+++ b/public/app/core/components/Select/UserPicker.test.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+// @ts-ignore
import renderer from 'react-test-renderer';
import { UserPicker } from './UserPicker';
diff --git a/public/app/core/components/Select/UserPicker.tsx b/public/app/core/components/Select/UserPicker.tsx
index ff4ae32f0683..ba054a20620c 100644
--- a/public/app/core/components/Select/UserPicker.tsx
+++ b/public/app/core/components/Select/UserPicker.tsx
@@ -24,7 +24,7 @@ export interface State {
export class UserPicker extends Component {
debouncedSearch: any;
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = { isLoading: false };
this.search = this.search.bind(this);
@@ -45,8 +45,8 @@ export class UserPicker extends Component {
return backendSrv
.get(`/api/org/users?query=${query}&limit=10`)
- .then(result => {
- return result.map(user => ({
+ .then((result: any) => {
+ return result.map((user: any) => ({
id: user.userId,
value: user.userId,
label: user.login === user.email ? user.login : `${user.login} - ${user.email}`,
diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx
index a39eefcec4b3..3b804ba47051 100644
--- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx
+++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx
@@ -27,7 +27,7 @@ const timezones = [
export class SharedPreferences extends PureComponent {
backendSrv: BackendSrv = getBackendSrv();
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = {
@@ -72,7 +72,7 @@ export class SharedPreferences extends PureComponent {
});
}
- onSubmitForm = async event => {
+ onSubmitForm = async (event: React.SyntheticEvent) => {
event.preventDefault();
const { homeDashboardId, theme, timezone } = this.state;
diff --git a/public/app/core/components/code_editor/code_editor.ts b/public/app/core/components/code_editor/code_editor.ts
index ca9023e86570..542454988bd6 100644
--- a/public/app/core/components/code_editor/code_editor.ts
+++ b/public/app/core/components/code_editor/code_editor.ts
@@ -55,7 +55,7 @@ const DEFAULT_SNIPPETS = true;
const editorTemplate = ``;
-function link(scope, elem, attrs) {
+function link(scope: any, elem: any, attrs: any) {
// Options
const langMode = attrs.mode || DEFAULT_MODE;
const maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
@@ -116,7 +116,7 @@ function link(scope, elem, attrs) {
});
// Sync with outer scope - update editor content if model has been changed from outside of directive.
- scope.$watch('content', (newValue, oldValue) => {
+ scope.$watch('content', (newValue: any, oldValue: any) => {
const editorValue = codeEditor.getValue();
if (newValue !== editorValue && newValue !== oldValue) {
scope.$$postDigest(() => {
@@ -142,7 +142,7 @@ function link(scope, elem, attrs) {
},
});
- function setLangMode(lang) {
+ function setLangMode(lang: string) {
ace.acequire('ace/ext/language_tools');
codeEditor.setOptions({
enableBasicAutocompletion: true,
@@ -170,7 +170,7 @@ function link(scope, elem, attrs) {
codeEditor.setTheme(theme);
}
- function setEditorContent(value) {
+ function setEditorContent(value: string) {
codeEditor.setValue(value);
codeEditor.clearSelection();
}
diff --git a/public/app/core/components/colorpicker/spectrum_picker.ts b/public/app/core/components/colorpicker/spectrum_picker.ts
index 4576648df835..d9831aa96440 100644
--- a/public/app/core/components/colorpicker/spectrum_picker.ts
+++ b/public/app/core/components/colorpicker/spectrum_picker.ts
@@ -13,9 +13,9 @@ export function spectrumPicker() {
scope: true,
replace: true,
template: '',
- link: (scope, element, attrs, ngModel) => {
+ link: (scope: any, element: any, attrs: any, ngModel: any) => {
scope.ngModel = ngModel;
- scope.onColorChange = color => {
+ scope.onColorChange = (color: string) => {
ngModel.$setViewValue(color);
};
},
diff --git a/public/app/core/components/navbar/navbar.html b/public/app/core/components/navbar/navbar.html
deleted file mode 100644
index 6d611692efc9..000000000000
--- a/public/app/core/components/navbar/navbar.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
diff --git a/public/app/core/components/navbar/navbar.ts b/public/app/core/components/navbar/navbar.ts
deleted file mode 100644
index f4de87451728..000000000000
--- a/public/app/core/components/navbar/navbar.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import coreModule from '../../core_module';
-import appEvents from 'app/core/app_events';
-import { NavModel } from '@grafana/ui';
-
-export class NavbarCtrl {
- model: NavModel;
-
- /** @ngInject */
- constructor() {}
-
- showSearch() {
- appEvents.emit('show-dash-search');
- }
-
- navItemClicked(navItem, evt) {
- if (navItem.clickHandler) {
- navItem.clickHandler();
- evt.preventDefault();
- }
- }
-}
-
-export function navbarDirective() {
- return {
- restrict: 'E',
- templateUrl: 'public/app/core/components/navbar/navbar.html',
- controller: NavbarCtrl,
- bindToController: true,
- controllerAs: 'ctrl',
- scope: {
- model: '=',
- },
- link: (scope, elem) => {},
- };
-}
-
-export function pageH1() {
- return {
- restrict: 'E',
- template: `
-
- `,
- scope: {
- model: '=',
- },
- };
-}
-
-coreModule.directive('pageH1', pageH1);
-coreModule.directive('navbar', navbarDirective);
diff --git a/public/app/core/components/query_part/query_part.ts b/public/app/core/components/query_part/query_part.ts
index 10ac878f4b56..b2466977c229 100644
--- a/public/app/core/components/query_part/query_part.ts
+++ b/public/app/core/components/query_part/query_part.ts
@@ -40,7 +40,7 @@ export class QueryPart {
return this.def.renderer(this, innerExpr);
}
- hasMultipleParamsInString(strValue, index) {
+ hasMultipleParamsInString(strValue: string, index: number) {
if (strValue.indexOf(',') === -1) {
return false;
}
@@ -48,7 +48,7 @@ export class QueryPart {
return this.def.params[index + 1] && this.def.params[index + 1].optional;
}
- updateParam(strValue, index) {
+ updateParam(strValue: string, index: number) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (this.hasMultipleParamsInString(strValue, index)) {
@@ -81,7 +81,7 @@ export class QueryPart {
}
}
-export function functionRenderer(part, innerExpr) {
+export function functionRenderer(part: any, innerExpr: string) {
const str = part.def.type + '(';
const parameters = _.map(part.params, (value, index) => {
const paramType = part.def.params[index];
@@ -105,14 +105,14 @@ export function functionRenderer(part, innerExpr) {
return str + parameters.join(', ') + ')';
}
-export function suffixRenderer(part, innerExpr) {
+export function suffixRenderer(part: QueryPartDef, innerExpr: string) {
return innerExpr + ' ' + part.params[0];
}
-export function identityRenderer(part, innerExpr) {
+export function identityRenderer(part: QueryPartDef, innerExpr: string) {
return part.params[0];
}
-export function quotedIdentityRenderer(part, innerExpr) {
+export function quotedIdentityRenderer(part: QueryPartDef, innerExpr: string) {
return '"' + part.params[0] + '"';
}
diff --git a/public/app/core/components/query_part/query_part_editor.ts b/public/app/core/components/query_part/query_part_editor.ts
index 4e6b82e151be..4e7221749aa1 100644
--- a/public/app/core/components/query_part/query_part_editor.ts
+++ b/public/app/core/components/query_part/query_part_editor.ts
@@ -14,7 +14,7 @@ const template = `
`;
/** @ngInject */
-export function queryPartEditorDirective($compile, templateSrv) {
+export function queryPartEditorDirective(templateSrv: any) {
const paramTemplate = '';
return {
@@ -25,7 +25,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
handleEvent: '&',
debounce: '@',
},
- link: function postLink($scope, elem) {
+ link: function postLink($scope: any, elem: any) {
const part = $scope.part;
const partDef = part.def;
const $paramsContainer = elem.find('.query-part-parameters');
@@ -33,7 +33,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
$scope.partActions = [];
- function clickFuncParam(this: any, paramIndex) {
+ function clickFuncParam(this: any, paramIndex: number) {
/*jshint validthis:true */
const $link = $(this);
const $input = $link.next();
@@ -53,7 +53,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
}
}
- function inputBlur(this: any, paramIndex) {
+ function inputBlur(this: any, paramIndex: number) {
/*jshint validthis:true */
const $input = $(this);
const $link = $input.prev();
@@ -72,7 +72,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
$link.show();
}
- function inputKeyPress(this: any, paramIndex, e) {
+ function inputKeyPress(this: any, paramIndex: number, e: any) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
@@ -84,12 +84,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
this.style.width = (3 + this.value.length) * 8 + 'px';
}
- function addTypeahead($input, param, paramIndex) {
+ function addTypeahead($input: JQuery, param: any, paramIndex: number) {
if (!param.options && !param.dynamicLookup) {
return;
}
- const typeaheadSource = (query, callback) => {
+ const typeaheadSource = (query: string, callback: any) => {
if (param.options) {
let options = param.options;
if (param.type === 'int') {
@@ -101,7 +101,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
}
$scope.$apply(() => {
- $scope.handleEvent({ $event: { name: 'get-param-options' } }).then(result => {
+ $scope.handleEvent({ $event: { name: 'get-param-options' } }).then((result: any) => {
const dynamicOptions = _.map(result, op => {
return _.escape(op.value);
});
@@ -116,7 +116,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
source: typeaheadSource,
minLength: 0,
items: 1000,
- updater: value => {
+ updater: (value: string) => {
value = _.unescape(value);
setTimeout(() => {
inputBlur.call($input[0], paramIndex);
@@ -138,12 +138,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
}
$scope.showActionsMenu = () => {
- $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then(res => {
+ $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then((res: any) => {
$scope.partActions = res;
});
};
- $scope.triggerPartAction = action => {
+ $scope.triggerPartAction = (action: string) => {
$scope.handleEvent({ $event: { name: 'action', action: action } });
};
diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts
index 49931ecaac45..fe7e729bb190 100644
--- a/public/app/core/components/scroll/scroll.ts
+++ b/public/app/core/components/scroll/scroll.ts
@@ -1,4 +1,5 @@
import $ from 'jquery';
+// @ts-ignore
import baron from 'baron';
import coreModule from 'app/core/core_module';
@@ -14,7 +15,7 @@ const scrollerClass = 'baron__scroller';
export function geminiScrollbar() {
return {
restrict: 'A',
- link: (scope, elem, attrs) => {
+ link: (scope: any, elem: any, attrs: any) => {
let scrollRoot = elem.parent();
const scroller = elem;
diff --git a/public/app/core/components/search/SearchField.tsx b/public/app/core/components/search/SearchField.tsx
index 5b6dd03165f5..a72782e21c75 100644
--- a/public/app/core/components/search/SearchField.tsx
+++ b/public/app/core/components/search/SearchField.tsx
@@ -1,4 +1,5 @@
import React, { useContext } from 'react';
+// @ts-ignore
import tinycolor from 'tinycolor2';
import { SearchQuery } from './search';
import { css, cx } from 'emotion';
diff --git a/public/app/core/components/search/SearchResult.tsx b/public/app/core/components/search/SearchResult.tsx
deleted file mode 100644
index 13333c168f91..000000000000
--- a/public/app/core/components/search/SearchResult.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React from 'react';
-import classNames from 'classnames';
-
-export class SearchResult extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- search: '',
- };
- }
-
- render() {
- return this.state.search.sections.map(section => {
- return ;
- });
- }
-}
-
-export interface SectionProps {
- section: any;
-}
-
-export class SearchResultSection extends React.Component {
- constructor(props) {
- super(props);
- }
-
- renderItem(item) {
- return (
-
-
-
-
-
- {item.title}
-
-
- );
- }
-
- toggleSection = () => {
- this.props.section.toggle();
- };
-
- render() {
- const collapseClassNames = classNames({
- fa: true,
- 'fa-plus': !this.props.section.expanded,
- 'fa-minus': this.props.section.expanded,
- 'search-section__header__toggle': true,
- });
-
- return (
-
-
-
- {this.props.section.title}
-
-
- {this.props.section.expanded && (
-
{this.props.section.items.map(this.renderItem)}
- )}
-
- );
- }
-}
diff --git a/public/app/core/components/search/search.ts b/public/app/core/components/search/search.ts
index 9cf8e9cd8c12..8728cbb1f348 100644
--- a/public/app/core/components/search/search.ts
+++ b/public/app/core/components/search/search.ts
@@ -6,6 +6,7 @@ import { contextSrv } from 'app/core/services/context_srv';
import appEvents from 'app/core/app_events';
import { parse, SearchParserOptions, SearchParserResult } from 'search-query-parser';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
+
export interface SearchQuery {
query: string;
parsedQuery: SearchParserResult;
@@ -32,6 +33,11 @@ class SearchQueryParser {
}
}
+interface SelectedIndicies {
+ dashboardIndex?: number;
+ folderIndex?: number;
+}
+
export class SearchCtrl {
isOpen: boolean;
query: SearchQuery;
@@ -49,7 +55,7 @@ export class SearchCtrl {
queryParser: SearchQueryParser;
/** @ngInject */
- constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv) {
+ constructor($scope: any, private $location: any, private $timeout: any, private searchSrv: SearchSrv) {
appEvents.on('show-dash-search', this.openSearch.bind(this), $scope);
appEvents.on('hide-dash-search', this.closeSearch.bind(this), $scope);
appEvents.on('search-query', debounce(this.search.bind(this), 500), $scope);
@@ -88,7 +94,7 @@ export class SearchCtrl {
appEvents.emit('search-query');
}
- openSearch(evt, payload) {
+ openSearch(evt: any, payload: any) {
if (this.isOpen) {
this.closeSearch();
return;
@@ -166,7 +172,7 @@ export class SearchCtrl {
}, 100);
}
- moveSelection(direction) {
+ moveSelection(direction: number) {
if (this.results.length === 0) {
return;
}
@@ -252,14 +258,14 @@ export class SearchCtrl {
return query.query === '' && query.starred === false && query.tags.length === 0;
}
- filterByTag(tag) {
+ filterByTag(tag: string) {
if (_.indexOf(this.query.tags, tag) === -1) {
this.query.tags.push(tag);
this.search();
}
}
- removeTag(tag, evt) {
+ removeTag(tag: string, evt: any) {
this.query.tags = _.without(this.query.tags, tag);
this.search();
this.giveSearchFocus = true;
@@ -298,14 +304,11 @@ export class SearchCtrl {
this.moveSelection(0);
}
- private getFlattenedResultForNavigation(): Array<{
- folderIndex: number;
- dashboardIndex: number;
- }> {
+ private getFlattenedResultForNavigation(): SelectedIndicies[] {
let folderIndex = 0;
- return _.flatMap(this.results, s => {
- let result = [];
+ return _.flatMap(this.results, (s: any) => {
+ let result: SelectedIndicies[] = [];
result.push({
folderIndex: folderIndex,
diff --git a/public/app/core/components/search/search_results.ts b/public/app/core/components/search/search_results.ts
index 35ee1365e222..20cd49d3443e 100644
--- a/public/app/core/components/search/search_results.ts
+++ b/public/app/core/components/search/search_results.ts
@@ -10,15 +10,15 @@ export class SearchResultsCtrl {
editable: boolean;
/** @ngInject */
- constructor(private $location) {}
+ constructor(private $location: any) {}
- toggleFolderExpand(section) {
+ toggleFolderExpand(section: any) {
if (section.toggle) {
if (!section.expanded && this.onFolderExpanding) {
this.onFolderExpanding();
}
- section.toggle(section).then(f => {
+ section.toggle(section).then((f: any) => {
if (this.editable && f.expanded) {
if (f.items) {
_.each(f.items, i => {
@@ -34,7 +34,7 @@ export class SearchResultsCtrl {
}
}
- navigateToFolder(section, evt) {
+ navigateToFolder(section: any, evt: any) {
this.$location.path(section.url);
if (evt) {
@@ -43,7 +43,7 @@ export class SearchResultsCtrl {
}
}
- toggleSelection(item, evt) {
+ toggleSelection(item: any, evt: any) {
item.checked = !item.checked;
if (item.items) {
@@ -62,14 +62,14 @@ export class SearchResultsCtrl {
}
}
- onItemClick(item) {
+ onItemClick(item: any) {
//Check if one string can be found in the other
if (this.$location.path().indexOf(item.url) > -1 || item.url.indexOf(this.$location.path()) > -1) {
appEvents.emit('hide-dash-search');
}
}
- selectTag(tag, evt) {
+ selectTag(tag: any, evt: any) {
if (this.onTagSelected) {
this.onTagSelected({ $tag: tag });
}
diff --git a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx
index 5ba4503da139..d59973fe02d6 100644
--- a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx
+++ b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx
@@ -10,7 +10,9 @@ jest.mock('../../app_events', () => ({
const setup = (propOverrides?: object) => {
const props = Object.assign(
{
- link: {},
+ link: {
+ text: 'Hello',
+ },
user: {
id: 1,
isGrafanaAdmin: false,
@@ -87,9 +89,9 @@ describe('Functions', () => {
const wrapper = setup();
const mockEvent = { preventDefault: jest.fn() };
it('should emit show modal event if url matches shortcut', () => {
- const child = { url: '/shortcuts' };
+ const child = { url: '/shortcuts', text: 'hello' };
const instance = wrapper.instance() as BottomNavLinks;
- instance.itemClicked(mockEvent, child);
+ instance.itemClicked(mockEvent as any, child);
expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { templateHtml: '' });
});
diff --git a/public/app/core/components/sidemenu/BottomNavLinks.tsx b/public/app/core/components/sidemenu/BottomNavLinks.tsx
index e1665c9ebc5a..fee58bad15fc 100644
--- a/public/app/core/components/sidemenu/BottomNavLinks.tsx
+++ b/public/app/core/components/sidemenu/BottomNavLinks.tsx
@@ -1,14 +1,15 @@
import React, { PureComponent } from 'react';
import appEvents from '../../app_events';
import { User } from '../../services/context_srv';
+import { NavModelItem } from '@grafana/ui';
export interface Props {
- link: any;
+ link: NavModelItem;
user: User;
}
class BottomNavLinks extends PureComponent {
- itemClicked = (event, child) => {
+ itemClicked = (event: React.SyntheticEvent, child: NavModelItem) => {
if (child.url === '/shortcuts') {
event.preventDefault();
appEvents.emit('show-modal', {
@@ -57,7 +58,7 @@ class BottomNavLinks extends PureComponent {
link.children.map((child, index) => {
if (!child.hideFromMenu) {
return (
-
+
this.itemClicked(event, child)}>
{child.icon && }
{child.text}
diff --git a/public/app/core/components/sidemenu/BottomSection.tsx b/public/app/core/components/sidemenu/BottomSection.tsx
index 85754a910fa0..f0668eaf4dd7 100644
--- a/public/app/core/components/sidemenu/BottomSection.tsx
+++ b/public/app/core/components/sidemenu/BottomSection.tsx
@@ -4,10 +4,11 @@ import SignIn from './SignIn';
import BottomNavLinks from './BottomNavLinks';
import { contextSrv } from 'app/core/services/context_srv';
import config from '../../config';
+import { NavModelItem } from '@grafana/ui';
export default function BottomSection() {
- const navTree: any = _.cloneDeep(config.bootData.navTree);
- const bottomNav: any = _.filter(navTree, item => item.hideFromMenu);
+ const navTree: NavModelItem[] = _.cloneDeep(config.bootData.navTree);
+ const bottomNav: NavModelItem[] = _.filter(navTree, item => item.hideFromMenu);
const isSignedIn = contextSrv.isSignedIn;
const user = contextSrv.user;
diff --git a/public/app/core/components/sidemenu/SideMenuDropDown.tsx b/public/app/core/components/sidemenu/SideMenuDropDown.tsx
index db2172039c6f..b3959d55d4a7 100644
--- a/public/app/core/components/sidemenu/SideMenuDropDown.tsx
+++ b/public/app/core/components/sidemenu/SideMenuDropDown.tsx
@@ -1,8 +1,9 @@
import React, { FC } from 'react';
import DropDownChild from './DropDownChild';
+import { NavModelItem } from '@grafana/ui';
interface Props {
- link: any;
+ link: NavModelItem;
}
const SideMenuDropDown: FC = props => {
diff --git a/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap b/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap
index ae8c9c753aa2..8b15bb0ae361 100644
--- a/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap
+++ b/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap
@@ -67,7 +67,9 @@ exports[`Render should render component 1`] = `
>
+ >
+ Hello
+
diff --git a/public/app/core/core.ts b/public/app/core/core.ts
index c7f03781710c..ba4230cfe451 100644
--- a/public/app/core/core.ts
+++ b/public/app/core/core.ts
@@ -20,7 +20,6 @@ import { colors } from '@grafana/ui/';
import { searchDirective } from './components/search/search';
import { infoPopover } from './components/info_popover';
-import { navbarDirective } from './components/navbar/navbar';
import { arrayJoin } from './directives/array_join';
import { liveSrv } from './live/live_srv';
import { Emitter } from './utils/emitter';
@@ -56,7 +55,6 @@ export {
registerAngularDirectives,
arrayJoin,
coreModule,
- navbarDirective,
searchDirective,
liveSrv,
layoutSelector,
diff --git a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
index 6072b1046fcc..46e465f3c312 100644
--- a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
+++ b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
@@ -97,9 +97,9 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
isDisabled={false}
isLoading={false}
isMulti={false}
+ isOpen={false}
isSearchable={false}
maxMenuHeight={300}
- menuIsOpen={false}
onChange={[Function]}
openMenuOnFocus={false}
options={
@@ -123,7 +123,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
"value": 0,
}
}
- width={null}
/>
@@ -183,9 +182,9 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
isDisabled={false}
isLoading={false}
isMulti={false}
+ isOpen={false}
isSearchable={false}
maxMenuHeight={300}
- menuIsOpen={false}
onChange={[Function]}
openMenuOnFocus={false}
options={
@@ -209,7 +208,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
"value": 0,
}
}
- width={null}
/>
diff --git a/public/app/types/acl.ts b/public/app/types/acl.ts
index 55e9bff620bc..977791f41596 100644
--- a/public/app/types/acl.ts
+++ b/public/app/types/acl.ts
@@ -39,6 +39,8 @@ export interface DashboardAcl {
name?: string;
inherited?: boolean;
sortRank?: number;
+ userAvatarUrl?: string;
+ teamAvatarUrl?: string;
}
export interface DashboardPermissionInfo {
From aa400d54e1320423363604b96d97f87d7b4d9a85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Sun, 12 May 2019 20:03:44 +0200
Subject: [PATCH 003/166] Select: Fixed isOpen issue
---
packages/grafana-ui/src/components/Select/Select.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/grafana-ui/src/components/Select/Select.tsx b/packages/grafana-ui/src/components/Select/Select.tsx
index da2b0229c627..d2c784ca8839 100644
--- a/packages/grafana-ui/src/components/Select/Select.tsx
+++ b/packages/grafana-ui/src/components/Select/Select.tsx
@@ -107,7 +107,6 @@ export class Select extends PureComponent> {
isLoading: false,
backspaceRemovesValue: true,
maxMenuHeight: 300,
- isOpen: false,
components: {
Option: SelectOption,
SingleValue,
From ce38de4ad7c8a0b89d3b2a4e63168b4783a8af23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Sun, 12 May 2019 20:18:58 +0200
Subject: [PATCH 004/166] Chore: Updated snapshot
---
.../features/teams/__snapshots__/TeamMemberRow.test.tsx.snap | 2 --
1 file changed, 2 deletions(-)
diff --git a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
index 46e465f3c312..96ff1b4cbc99 100644
--- a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
+++ b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap
@@ -97,7 +97,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
isDisabled={false}
isLoading={false}
isMulti={false}
- isOpen={false}
isSearchable={false}
maxMenuHeight={300}
onChange={[Function]}
@@ -182,7 +181,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
isDisabled={false}
isLoading={false}
isMulti={false}
- isOpen={false}
isSearchable={false}
maxMenuHeight={300}
onChange={[Function]}
From 0efb8821f9fa1e5c213280f1c94f87befead290c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?=
Date: Mon, 13 May 2019 07:47:07 +0200
Subject: [PATCH 005/166] Chore: Lowered implicit anys limit to 5386
Progress: #14714
---
scripts/ci-frontend-metrics.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/ci-frontend-metrics.sh b/scripts/ci-frontend-metrics.sh
index 85464aff3e0a..e1bcc3a0031a 100755
--- a/scripts/ci-frontend-metrics.sh
+++ b/scripts/ci-frontend-metrics.sh
@@ -2,7 +2,7 @@
echo -e "Collecting code stats (typescript errors & more)"
-ERROR_COUNT_LIMIT=5564
+ERROR_COUNT_LIMIT=5386
DIRECTIVES_LIMIT=172
CONTROLLERS_LIMIT=139
From 2fff8f77dcdc90ab9a4890eeed95d8f3dced370b Mon Sep 17 00:00:00 2001
From: zhulongcheng
Date: Mon, 13 May 2019 14:45:54 +0800
Subject: [PATCH 006/166] move log package to /infra (#17023)
ref #14679
Signed-off-by: zhulongcheng
---
pkg/api/app_routes.go | 2 +-
pkg/api/avatar/avatar.go | 2 +-
pkg/api/dashboard.go | 2 +-
pkg/api/frontendsettings.go | 2 +-
pkg/api/http_server.go | 2 +-
pkg/api/live/conn.go | 2 +-
pkg/api/live/hub.go | 2 +-
pkg/api/live/stream_manager.go | 2 +-
pkg/api/login.go | 2 +-
pkg/api/login_oauth.go | 2 +-
pkg/api/playlist.go | 2 +-
pkg/api/playlist_play.go | 2 +-
pkg/api/pluginproxy/ds_proxy.go | 2 +-
pkg/api/pluginproxy/ds_proxy_test.go | 2 +-
pkg/api/pluginproxy/pluginproxy.go | 2 +-
pkg/cmd/grafana-server/main.go | 2 +-
pkg/cmd/grafana-server/server.go | 2 +-
pkg/components/imguploader/azureblobuploader.go | 2 +-
pkg/components/imguploader/gcsuploader.go | 2 +-
pkg/components/imguploader/imguploader.go | 2 +-
pkg/components/imguploader/s3uploader.go | 2 +-
pkg/components/securejsondata/securejsondata.go | 2 +-
pkg/{ => infra}/log/file.go | 0
pkg/{ => infra}/log/file_test.go | 0
pkg/{ => infra}/log/handlers.go | 0
pkg/{ => infra}/log/interface.go | 0
pkg/{ => infra}/log/log.go | 0
pkg/{ => infra}/log/log_writer.go | 0
pkg/{ => infra}/log/log_writer_test.go | 0
pkg/{ => infra}/log/syslog.go | 0
pkg/{ => infra}/log/syslog_windows.go | 0
pkg/infra/metrics/service.go | 2 +-
pkg/infra/remotecache/database_storage.go | 2 +-
pkg/infra/remotecache/database_storage_test.go | 2 +-
pkg/infra/remotecache/remotecache.go | 2 +-
pkg/infra/serverlock/serverlock.go | 2 +-
pkg/infra/serverlock/serverlock_test.go | 2 +-
pkg/infra/tracing/tracing.go | 2 +-
pkg/infra/usagestats/service.go | 2 +-
pkg/login/social/common.go | 2 +-
pkg/login/social/social.go | 2 +-
pkg/middleware/middleware.go | 2 +-
pkg/middleware/recovery.go | 2 +-
pkg/models/context.go | 2 +-
pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go | 2 +-
.../datasource/wrapper/datasource_plugin_wrapper_test.go | 2 +-
pkg/plugins/datasource_plugin.go | 2 +-
pkg/plugins/hclog-wrapper.go | 2 +-
pkg/plugins/plugins.go | 2 +-
pkg/plugins/update_checker.go | 2 +-
pkg/services/alerting/engine.go | 2 +-
pkg/services/alerting/eval_context.go | 2 +-
pkg/services/alerting/eval_handler.go | 2 +-
pkg/services/alerting/extractor.go | 2 +-
pkg/services/alerting/notifier.go | 2 +-
pkg/services/alerting/notifiers/alertmanager.go | 2 +-
pkg/services/alerting/notifiers/alertmanager_test.go | 2 +-
pkg/services/alerting/notifiers/base.go | 2 +-
pkg/services/alerting/notifiers/dingding.go | 2 +-
pkg/services/alerting/notifiers/discord.go | 2 +-
pkg/services/alerting/notifiers/email.go | 2 +-
pkg/services/alerting/notifiers/googlechat.go | 2 +-
pkg/services/alerting/notifiers/hipchat.go | 2 +-
pkg/services/alerting/notifiers/kafka.go | 2 +-
pkg/services/alerting/notifiers/line.go | 2 +-
pkg/services/alerting/notifiers/opsgenie.go | 2 +-
pkg/services/alerting/notifiers/pagerduty.go | 2 +-
pkg/services/alerting/notifiers/pushover.go | 2 +-
pkg/services/alerting/notifiers/sensu.go | 2 +-
pkg/services/alerting/notifiers/slack.go | 2 +-
pkg/services/alerting/notifiers/teams.go | 2 +-
pkg/services/alerting/notifiers/telegram.go | 2 +-
pkg/services/alerting/notifiers/threema.go | 2 +-
pkg/services/alerting/notifiers/victorops.go | 2 +-
pkg/services/alerting/notifiers/webhook.go | 2 +-
pkg/services/alerting/reader.go | 2 +-
pkg/services/alerting/result_handler.go | 2 +-
pkg/services/alerting/scheduler.go | 2 +-
pkg/services/alerting/test_notification.go | 2 +-
pkg/services/auth/auth_token.go | 2 +-
pkg/services/auth/auth_token_test.go | 2 +-
pkg/services/cleanup/cleanup.go | 2 +-
pkg/services/dashboards/dashboard_service.go | 2 +-
pkg/services/guardian/guardian.go | 2 +-
pkg/services/ldap/ldap.go | 2 +-
pkg/services/ldap/ldap_login_test.go | 2 +-
pkg/services/ldap/ldap_test.go | 2 +-
pkg/services/ldap/settings.go | 2 +-
pkg/services/login/login.go | 2 +-
pkg/services/notifications/notifications.go | 2 +-
pkg/services/provisioning/dashboards/config_reader.go | 2 +-
pkg/services/provisioning/dashboards/config_reader_test.go | 2 +-
pkg/services/provisioning/dashboards/dashboard.go | 2 +-
pkg/services/provisioning/dashboards/file_reader.go | 2 +-
pkg/services/provisioning/dashboards/file_reader_linux_test.go | 2 +-
pkg/services/provisioning/dashboards/file_reader_test.go | 2 +-
pkg/services/provisioning/datasources/config_reader.go | 2 +-
pkg/services/provisioning/datasources/config_reader_test.go | 2 +-
pkg/services/provisioning/datasources/datasources.go | 2 +-
pkg/services/provisioning/datasources/types.go | 2 +-
pkg/services/provisioning/notifiers/alert_notifications.go | 2 +-
pkg/services/provisioning/notifiers/config_reader.go | 2 +-
pkg/services/provisioning/notifiers/config_reader_test.go | 2 +-
pkg/services/provisioning/provisioning.go | 2 +-
pkg/services/rendering/phantomjs.go | 2 +-
pkg/services/rendering/rendering.go | 2 +-
pkg/services/sqlstore/logger.go | 2 +-
pkg/services/sqlstore/migrator/migrator.go | 2 +-
pkg/services/sqlstore/sqlstore.go | 2 +-
pkg/services/sqlstore/transactions.go | 2 +-
pkg/setting/setting.go | 2 +-
pkg/setting/setting_session_test.go | 2 +-
pkg/tsdb/azuremonitor/azuremonitor.go | 2 +-
pkg/tsdb/cloudwatch/cloudwatch.go | 2 +-
pkg/tsdb/elasticsearch/client/client.go | 2 +-
pkg/tsdb/elasticsearch/elasticsearch.go | 2 +-
pkg/tsdb/graphite/graphite.go | 2 +-
pkg/tsdb/influxdb/influxdb.go | 2 +-
pkg/tsdb/mssql/mssql.go | 2 +-
pkg/tsdb/mysql/mysql.go | 2 +-
pkg/tsdb/opentsdb/opentsdb.go | 2 +-
pkg/tsdb/postgres/postgres.go | 2 +-
pkg/tsdb/prometheus/prometheus.go | 2 +-
pkg/tsdb/sql_engine.go | 2 +-
pkg/tsdb/stackdriver/stackdriver.go | 2 +-
pkg/tsdb/testdata/scenarios.go | 2 +-
pkg/tsdb/testdata/testdata.go | 2 +-
127 files changed, 118 insertions(+), 118 deletions(-)
rename pkg/{ => infra}/log/file.go (100%)
rename pkg/{ => infra}/log/file_test.go (100%)
rename pkg/{ => infra}/log/handlers.go (100%)
rename pkg/{ => infra}/log/interface.go (100%)
rename pkg/{ => infra}/log/log.go (100%)
rename pkg/{ => infra}/log/log_writer.go (100%)
rename pkg/{ => infra}/log/log_writer_test.go (100%)
rename pkg/{ => infra}/log/syslog.go (100%)
rename pkg/{ => infra}/log/syslog_windows.go (100%)
diff --git a/pkg/api/app_routes.go b/pkg/api/app_routes.go
index e85a19dbce81..8e462e176fcb 100644
--- a/pkg/api/app_routes.go
+++ b/pkg/api/app_routes.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/api/pluginproxy"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
diff --git a/pkg/api/avatar/avatar.go b/pkg/api/avatar/avatar.go
index 6cf164285bfb..e01982c8fdab 100644
--- a/pkg/api/avatar/avatar.go
+++ b/pkg/api/avatar/avatar.go
@@ -22,7 +22,7 @@ import (
"sync"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/macaron.v1"
diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go
index 212b56842809..ed153c02efe3 100644
--- a/pkg/api/dashboard.go
+++ b/pkg/api/dashboard.go
@@ -14,8 +14,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/dashdiffs"
"github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/guardian"
diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go
index e4fb0ed34c01..e881fc1539de 100644
--- a/pkg/api/frontendsettings.go
+++ b/pkg/api/frontendsettings.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go
index 4f368d8bb0a3..7ec4fbaa3b3c 100644
--- a/pkg/api/http_server.go
+++ b/pkg/api/http_server.go
@@ -16,8 +16,8 @@ import (
httpstatic "github.com/grafana/grafana/pkg/api/static"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
- "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
diff --git a/pkg/api/live/conn.go b/pkg/api/live/conn.go
index 0fae7f75b73c..f6450fa1d63d 100644
--- a/pkg/api/live/conn.go
+++ b/pkg/api/live/conn.go
@@ -6,7 +6,7 @@ import (
"github.com/gorilla/websocket"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
)
const (
diff --git a/pkg/api/live/hub.go b/pkg/api/live/hub.go
index 9708bc515d1c..10eaca4f79d6 100644
--- a/pkg/api/live/hub.go
+++ b/pkg/api/live/hub.go
@@ -5,7 +5,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
)
type hub struct {
diff --git a/pkg/api/live/stream_manager.go b/pkg/api/live/stream_manager.go
index 00d98286882e..2369bb1f9b51 100644
--- a/pkg/api/live/stream_manager.go
+++ b/pkg/api/live/stream_manager.go
@@ -6,7 +6,7 @@ import (
"sync"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/api/login.go b/pkg/api/login.go
index ebf4cc8db07c..e7fe6db93477 100644
--- a/pkg/api/login.go
+++ b/pkg/api/login.go
@@ -7,8 +7,8 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go
index a3599bc7a24e..8c5ca6fc7d1d 100644
--- a/pkg/api/login_oauth.go
+++ b/pkg/api/login_oauth.go
@@ -16,8 +16,8 @@ import (
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/login/social"
m "github.com/grafana/grafana/pkg/models"
diff --git a/pkg/api/playlist.go b/pkg/api/playlist.go
index 0963df7d4c47..bb429847dc01 100644
--- a/pkg/api/playlist.go
+++ b/pkg/api/playlist.go
@@ -2,7 +2,7 @@ package api
import (
"github.com/grafana/grafana/pkg/bus"
- _ "github.com/grafana/grafana/pkg/log"
+ _ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/api/playlist_play.go b/pkg/api/playlist_play.go
index 21c91a1288da..53eab8908d78 100644
--- a/pkg/api/playlist_play.go
+++ b/pkg/api/playlist_play.go
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
- _ "github.com/grafana/grafana/pkg/log"
+ _ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
)
diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go
index fe79fa565950..98fd30cc1c9b 100644
--- a/pkg/api/pluginproxy/ds_proxy.go
+++ b/pkg/api/pluginproxy/ds_proxy.go
@@ -17,7 +17,7 @@ import (
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/login/social"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
diff --git a/pkg/api/pluginproxy/ds_proxy_test.go b/pkg/api/pluginproxy/ds_proxy_test.go
index c3eccb4a57c9..13c3be3214b8 100644
--- a/pkg/api/pluginproxy/ds_proxy_test.go
+++ b/pkg/api/pluginproxy/ds_proxy_test.go
@@ -17,7 +17,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/login/social"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
diff --git a/pkg/api/pluginproxy/pluginproxy.go b/pkg/api/pluginproxy/pluginproxy.go
index 4ee4e5b8db3a..4c37cd00db86 100644
--- a/pkg/api/pluginproxy/pluginproxy.go
+++ b/pkg/api/pluginproxy/pluginproxy.go
@@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/util"
diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go
index 825d48e00764..d836f9a7d703 100644
--- a/pkg/cmd/grafana-server/main.go
+++ b/pkg/cmd/grafana-server/main.go
@@ -14,8 +14,8 @@ import (
"time"
"github.com/grafana/grafana/pkg/extensions"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
_ "github.com/grafana/grafana/pkg/services/alerting/conditions"
_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go
index 2ac326ed35d6..f1823260e16d 100644
--- a/pkg/cmd/grafana-server/server.go
+++ b/pkg/cmd/grafana-server/server.go
@@ -18,12 +18,12 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
_ "github.com/grafana/grafana/pkg/extensions"
+ "github.com/grafana/grafana/pkg/infra/log"
_ "github.com/grafana/grafana/pkg/infra/metrics"
_ "github.com/grafana/grafana/pkg/infra/remotecache"
_ "github.com/grafana/grafana/pkg/infra/serverlock"
_ "github.com/grafana/grafana/pkg/infra/tracing"
_ "github.com/grafana/grafana/pkg/infra/usagestats"
- "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/middleware"
diff --git a/pkg/components/imguploader/azureblobuploader.go b/pkg/components/imguploader/azureblobuploader.go
index b37763931c89..bfcb901dd0c2 100644
--- a/pkg/components/imguploader/azureblobuploader.go
+++ b/pkg/components/imguploader/azureblobuploader.go
@@ -20,7 +20,7 @@ import (
"strings"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util"
)
diff --git a/pkg/components/imguploader/gcsuploader.go b/pkg/components/imguploader/gcsuploader.go
index 4513019ad98a..8932e96e59eb 100644
--- a/pkg/components/imguploader/gcsuploader.go
+++ b/pkg/components/imguploader/gcsuploader.go
@@ -8,7 +8,7 @@ import (
"os"
"path"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util"
"golang.org/x/oauth2/google"
)
diff --git a/pkg/components/imguploader/imguploader.go b/pkg/components/imguploader/imguploader.go
index 422a03d3501a..9aeb4eefb42f 100644
--- a/pkg/components/imguploader/imguploader.go
+++ b/pkg/components/imguploader/imguploader.go
@@ -5,7 +5,7 @@ import (
"fmt"
"regexp"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/components/imguploader/s3uploader.go b/pkg/components/imguploader/s3uploader.go
index 9c8af21e39e9..d690f629f577 100644
--- a/pkg/components/imguploader/s3uploader.go
+++ b/pkg/components/imguploader/s3uploader.go
@@ -15,7 +15,7 @@ import (
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util"
)
diff --git a/pkg/components/securejsondata/securejsondata.go b/pkg/components/securejsondata/securejsondata.go
index 4e7f63601739..dae9584995ad 100644
--- a/pkg/components/securejsondata/securejsondata.go
+++ b/pkg/components/securejsondata/securejsondata.go
@@ -1,7 +1,7 @@
package securejsondata
import (
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
diff --git a/pkg/log/file.go b/pkg/infra/log/file.go
similarity index 100%
rename from pkg/log/file.go
rename to pkg/infra/log/file.go
diff --git a/pkg/log/file_test.go b/pkg/infra/log/file_test.go
similarity index 100%
rename from pkg/log/file_test.go
rename to pkg/infra/log/file_test.go
diff --git a/pkg/log/handlers.go b/pkg/infra/log/handlers.go
similarity index 100%
rename from pkg/log/handlers.go
rename to pkg/infra/log/handlers.go
diff --git a/pkg/log/interface.go b/pkg/infra/log/interface.go
similarity index 100%
rename from pkg/log/interface.go
rename to pkg/infra/log/interface.go
diff --git a/pkg/log/log.go b/pkg/infra/log/log.go
similarity index 100%
rename from pkg/log/log.go
rename to pkg/infra/log/log.go
diff --git a/pkg/log/log_writer.go b/pkg/infra/log/log_writer.go
similarity index 100%
rename from pkg/log/log_writer.go
rename to pkg/infra/log/log_writer.go
diff --git a/pkg/log/log_writer_test.go b/pkg/infra/log/log_writer_test.go
similarity index 100%
rename from pkg/log/log_writer_test.go
rename to pkg/infra/log/log_writer_test.go
diff --git a/pkg/log/syslog.go b/pkg/infra/log/syslog.go
similarity index 100%
rename from pkg/log/syslog.go
rename to pkg/infra/log/syslog.go
diff --git a/pkg/log/syslog_windows.go b/pkg/infra/log/syslog_windows.go
similarity index 100%
rename from pkg/log/syslog_windows.go
rename to pkg/infra/log/syslog_windows.go
diff --git a/pkg/infra/metrics/service.go b/pkg/infra/metrics/service.go
index ac1d934e375f..6f981085ffb6 100644
--- a/pkg/infra/metrics/service.go
+++ b/pkg/infra/metrics/service.go
@@ -3,8 +3,8 @@ package metrics
import (
"context"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics/graphitebridge"
- "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/infra/remotecache/database_storage.go b/pkg/infra/remotecache/database_storage.go
index 16c42054df09..3e15bd9a2cd9 100644
--- a/pkg/infra/remotecache/database_storage.go
+++ b/pkg/infra/remotecache/database_storage.go
@@ -4,7 +4,7 @@ import (
"context"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
diff --git a/pkg/infra/remotecache/database_storage_test.go b/pkg/infra/remotecache/database_storage_test.go
index 6e3c8fd4844e..ea7192f93383 100644
--- a/pkg/infra/remotecache/database_storage_test.go
+++ b/pkg/infra/remotecache/database_storage_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/stretchr/testify/assert"
)
diff --git a/pkg/infra/remotecache/remotecache.go b/pkg/infra/remotecache/remotecache.go
index c5049f8b27fb..0c04277192f5 100644
--- a/pkg/infra/remotecache/remotecache.go
+++ b/pkg/infra/remotecache/remotecache.go
@@ -7,7 +7,7 @@ import (
"errors"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/infra/serverlock/serverlock.go b/pkg/infra/serverlock/serverlock.go
index 824fa0484b28..497575681570 100644
--- a/pkg/infra/serverlock/serverlock.go
+++ b/pkg/infra/serverlock/serverlock.go
@@ -4,7 +4,7 @@ import (
"context"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
diff --git a/pkg/infra/serverlock/serverlock_test.go b/pkg/infra/serverlock/serverlock_test.go
index ccd1c252090d..d72f2a357d95 100644
--- a/pkg/infra/serverlock/serverlock_test.go
+++ b/pkg/infra/serverlock/serverlock_test.go
@@ -4,7 +4,7 @@ import (
"context"
"testing"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/sqlstore"
. "github.com/smartystreets/goconvey/convey"
)
diff --git a/pkg/infra/tracing/tracing.go b/pkg/infra/tracing/tracing.go
index fd7258b7a0ab..2e1b75e81d13 100644
--- a/pkg/infra/tracing/tracing.go
+++ b/pkg/infra/tracing/tracing.go
@@ -5,7 +5,7 @@ import (
"io"
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/infra/usagestats/service.go b/pkg/infra/usagestats/service.go
index ccebf6a17f48..7cda2cbef4b6 100644
--- a/pkg/infra/usagestats/service.go
+++ b/pkg/infra/usagestats/service.go
@@ -8,7 +8,7 @@ import (
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/sqlstore"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/login/social/common.go b/pkg/login/social/common.go
index cf2fa3711492..b813e07ab795 100644
--- a/pkg/login/social/common.go
+++ b/pkg/login/social/common.go
@@ -6,7 +6,7 @@ import (
"net/http"
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
)
type HttpGetResponse struct {
diff --git a/pkg/login/social/social.go b/pkg/login/social/social.go
index e73e9229a2af..46192ba861e0 100644
--- a/pkg/login/social/social.go
+++ b/pkg/login/social/social.go
@@ -8,7 +8,7 @@ import (
"golang.org/x/oauth2"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go
index f4f60c015593..ec7194a7fc66 100644
--- a/pkg/middleware/middleware.go
+++ b/pkg/middleware/middleware.go
@@ -9,8 +9,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
- "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
diff --git a/pkg/middleware/recovery.go b/pkg/middleware/recovery.go
index b8ca85fb45b2..33ceff8bc7ef 100644
--- a/pkg/middleware/recovery.go
+++ b/pkg/middleware/recovery.go
@@ -23,7 +23,7 @@ import (
"gopkg.in/macaron.v1"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/models/context.go b/pkg/models/context.go
index 947e442c2006..f4b8d2c87d5d 100644
--- a/pkg/models/context.go
+++ b/pkg/models/context.go
@@ -3,7 +3,7 @@ package models
import (
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
"gopkg.in/macaron.v1"
diff --git a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go
index 0af727e14df0..cc88f90f2730 100644
--- a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go
+++ b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper.go
@@ -8,7 +8,7 @@ import (
"github.com/grafana/grafana-plugin-model/go/datasource"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
)
diff --git a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go
index e312913fc562..2086ea4b9911 100644
--- a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go
+++ b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana-plugin-model/go/datasource"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/tsdb"
)
diff --git a/pkg/plugins/datasource_plugin.go b/pkg/plugins/datasource_plugin.go
index dd4ae6972aaa..57dacbe43b51 100644
--- a/pkg/plugins/datasource_plugin.go
+++ b/pkg/plugins/datasource_plugin.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/grafana/grafana-plugin-model/go/datasource"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins/datasource/wrapper"
"github.com/grafana/grafana/pkg/tsdb"
diff --git a/pkg/plugins/hclog-wrapper.go b/pkg/plugins/hclog-wrapper.go
index ac50d15f3d88..7bd031565c16 100644
--- a/pkg/plugins/hclog-wrapper.go
+++ b/pkg/plugins/hclog-wrapper.go
@@ -5,7 +5,7 @@ import (
"io/ioutil"
"log"
- glog "github.com/grafana/grafana/pkg/log"
+ glog "github.com/grafana/grafana/pkg/infra/log"
hclog "github.com/hashicorp/go-hclog"
)
diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go
index 0a031e3a5041..4c75e6b3d581 100644
--- a/pkg/plugins/plugins.go
+++ b/pkg/plugins/plugins.go
@@ -13,7 +13,7 @@ import (
"strings"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go
index e61f4cf1df74..1d4cad1399ec 100644
--- a/pkg/plugins/update_checker.go
+++ b/pkg/plugins/update_checker.go
@@ -7,7 +7,7 @@ import (
"strings"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/hashicorp/go-version"
)
diff --git a/pkg/services/alerting/engine.go b/pkg/services/alerting/engine.go
index db8079583878..c67b59839d79 100644
--- a/pkg/services/alerting/engine.go
+++ b/pkg/services/alerting/engine.go
@@ -10,7 +10,7 @@ import (
tlog "github.com/opentracing/opentracing-go/log"
"github.com/benbjohnson/clock"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/eval_context.go b/pkg/services/alerting/eval_context.go
index 02b9955662f0..6358c22a97f1 100644
--- a/pkg/services/alerting/eval_context.go
+++ b/pkg/services/alerting/eval_context.go
@@ -6,7 +6,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/services/alerting/eval_handler.go b/pkg/services/alerting/eval_handler.go
index 097f4bcb2b6b..22d172568f55 100644
--- a/pkg/services/alerting/eval_handler.go
+++ b/pkg/services/alerting/eval_handler.go
@@ -5,8 +5,8 @@ import (
"strings"
"time"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
)
type DefaultEvalHandler struct {
diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go
index 5b911c5a9adb..c3fcc01ad55e 100644
--- a/pkg/services/alerting/extractor.go
+++ b/pkg/services/alerting/extractor.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go
index c28ac49b8945..8c2ac839e439 100644
--- a/pkg/services/alerting/notifier.go
+++ b/pkg/services/alerting/notifier.go
@@ -6,8 +6,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/imguploader"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/notifiers/alertmanager.go b/pkg/services/alerting/notifiers/alertmanager.go
index 2caa4d5ab587..9febe42505be 100644
--- a/pkg/services/alerting/notifiers/alertmanager.go
+++ b/pkg/services/alerting/notifiers/alertmanager.go
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/alertmanager_test.go b/pkg/services/alerting/notifiers/alertmanager_test.go
index 7510742ed172..9197926035ea 100644
--- a/pkg/services/alerting/notifiers/alertmanager_test.go
+++ b/pkg/services/alerting/notifiers/alertmanager_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
diff --git a/pkg/services/alerting/notifiers/base.go b/pkg/services/alerting/notifiers/base.go
index 800505b63801..6bd375b53bcb 100644
--- a/pkg/services/alerting/notifiers/base.go
+++ b/pkg/services/alerting/notifiers/base.go
@@ -4,7 +4,7 @@ import (
"context"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go
index 3e3496622b7e..0aa2ba078f37 100644
--- a/pkg/services/alerting/notifiers/dingding.go
+++ b/pkg/services/alerting/notifiers/dingding.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/discord.go b/pkg/services/alerting/notifiers/discord.go
index c7178211f0ea..6ed422b1cbb0 100644
--- a/pkg/services/alerting/notifiers/discord.go
+++ b/pkg/services/alerting/notifiers/discord.go
@@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/notifiers/email.go b/pkg/services/alerting/notifiers/email.go
index 6c7bf5edb297..b3f465a2b80c 100644
--- a/pkg/services/alerting/notifiers/email.go
+++ b/pkg/services/alerting/notifiers/email.go
@@ -5,7 +5,7 @@ import (
"strings"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/notifiers/googlechat.go b/pkg/services/alerting/notifiers/googlechat.go
index 1aba15a79282..41f3503640cb 100644
--- a/pkg/services/alerting/notifiers/googlechat.go
+++ b/pkg/services/alerting/notifiers/googlechat.go
@@ -6,7 +6,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/notifiers/hipchat.go b/pkg/services/alerting/notifiers/hipchat.go
index 388cec795976..6b94c41065fb 100644
--- a/pkg/services/alerting/notifiers/hipchat.go
+++ b/pkg/services/alerting/notifiers/hipchat.go
@@ -8,7 +8,7 @@ import (
"fmt"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/kafka.go b/pkg/services/alerting/notifiers/kafka.go
index a8a424c87a7c..d7da05499b75 100644
--- a/pkg/services/alerting/notifiers/kafka.go
+++ b/pkg/services/alerting/notifiers/kafka.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/line.go b/pkg/services/alerting/notifiers/line.go
index 9e3888b8f954..edbec373becd 100644
--- a/pkg/services/alerting/notifiers/line.go
+++ b/pkg/services/alerting/notifiers/line.go
@@ -5,7 +5,7 @@ import (
"net/url"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/opsgenie.go b/pkg/services/alerting/notifiers/opsgenie.go
index 629968b51023..84242ea97691 100644
--- a/pkg/services/alerting/notifiers/opsgenie.go
+++ b/pkg/services/alerting/notifiers/opsgenie.go
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/pagerduty.go b/pkg/services/alerting/notifiers/pagerduty.go
index 9f6ce3c2dc89..ab2a36fd86b2 100644
--- a/pkg/services/alerting/notifiers/pagerduty.go
+++ b/pkg/services/alerting/notifiers/pagerduty.go
@@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/pushover.go b/pkg/services/alerting/notifiers/pushover.go
index 8e1903f68ac0..0d23bbf6fb3c 100644
--- a/pkg/services/alerting/notifiers/pushover.go
+++ b/pkg/services/alerting/notifiers/pushover.go
@@ -9,7 +9,7 @@ import (
"strconv"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/sensu.go b/pkg/services/alerting/notifiers/sensu.go
index 21d5d3d9d9e5..b018c53208e8 100644
--- a/pkg/services/alerting/notifiers/sensu.go
+++ b/pkg/services/alerting/notifiers/sensu.go
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go
index ca5f47a322f5..7754b7de71aa 100644
--- a/pkg/services/alerting/notifiers/slack.go
+++ b/pkg/services/alerting/notifiers/slack.go
@@ -10,7 +10,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/notifiers/teams.go b/pkg/services/alerting/notifiers/teams.go
index e19357d7b7e2..e33a93f8a0cf 100644
--- a/pkg/services/alerting/notifiers/teams.go
+++ b/pkg/services/alerting/notifiers/teams.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/telegram.go b/pkg/services/alerting/notifiers/telegram.go
index ab43f3bce356..a5876b77b228 100644
--- a/pkg/services/alerting/notifiers/telegram.go
+++ b/pkg/services/alerting/notifiers/telegram.go
@@ -8,7 +8,7 @@ import (
"os"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/threema.go b/pkg/services/alerting/notifiers/threema.go
index 28a62fade177..2360073d2281 100644
--- a/pkg/services/alerting/notifiers/threema.go
+++ b/pkg/services/alerting/notifiers/threema.go
@@ -6,7 +6,7 @@ import (
"strings"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/notifiers/victorops.go b/pkg/services/alerting/notifiers/victorops.go
index f33b3f4019e0..74988c21ed8f 100644
--- a/pkg/services/alerting/notifiers/victorops.go
+++ b/pkg/services/alerting/notifiers/victorops.go
@@ -5,7 +5,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/alerting/notifiers/webhook.go b/pkg/services/alerting/notifiers/webhook.go
index 4045e496af99..7c582a41aeb8 100644
--- a/pkg/services/alerting/notifiers/webhook.go
+++ b/pkg/services/alerting/notifiers/webhook.go
@@ -3,7 +3,7 @@ package notifiers
import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
diff --git a/pkg/services/alerting/reader.go b/pkg/services/alerting/reader.go
index d9f20a21c8e6..3f033a746f27 100644
--- a/pkg/services/alerting/reader.go
+++ b/pkg/services/alerting/reader.go
@@ -5,8 +5,8 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go
index c8e9e2dc25c7..6f2669ff41a9 100644
--- a/pkg/services/alerting/result_handler.go
+++ b/pkg/services/alerting/result_handler.go
@@ -5,8 +5,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/rendering"
diff --git a/pkg/services/alerting/scheduler.go b/pkg/services/alerting/scheduler.go
index b7555ae8d898..9a0769d25f24 100644
--- a/pkg/services/alerting/scheduler.go
+++ b/pkg/services/alerting/scheduler.go
@@ -4,7 +4,7 @@ import (
"math"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/alerting/test_notification.go b/pkg/services/alerting/test_notification.go
index 22f6a2118b7d..fbb1633d4a5b 100644
--- a/pkg/services/alerting/test_notification.go
+++ b/pkg/services/alerting/test_notification.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go
index dc9936f2f3ff..527d054f6ee9 100644
--- a/pkg/services/auth/auth_token.go
+++ b/pkg/services/auth/auth_token.go
@@ -8,7 +8,7 @@ import (
"github.com/grafana/grafana/pkg/infra/serverlock"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/sqlstore"
diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go
index b1398834bdc9..802b4602cbfc 100644
--- a/pkg/services/auth/auth_token_test.go
+++ b/pkg/services/auth/auth_token_test.go
@@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/setting"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
. "github.com/smartystreets/goconvey/convey"
diff --git a/pkg/services/cleanup/cleanup.go b/pkg/services/cleanup/cleanup.go
index 63d829bccec1..6e07a76f8002 100644
--- a/pkg/services/cleanup/cleanup.go
+++ b/pkg/services/cleanup/cleanup.go
@@ -8,8 +8,8 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/serverlock"
- "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/dashboards/dashboard_service.go b/pkg/services/dashboards/dashboard_service.go
index 884d993f43c6..bec718700a78 100644
--- a/pkg/services/dashboards/dashboard_service.go
+++ b/pkg/services/dashboards/dashboard_service.go
@@ -5,7 +5,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
diff --git a/pkg/services/guardian/guardian.go b/pkg/services/guardian/guardian.go
index 366bc90fc377..4d242722d6b1 100644
--- a/pkg/services/guardian/guardian.go
+++ b/pkg/services/guardian/guardian.go
@@ -4,7 +4,7 @@ import (
"errors"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/services/ldap/ldap.go b/pkg/services/ldap/ldap.go
index 5ff74bd7a3e9..d6b5b69d7525 100644
--- a/pkg/services/ldap/ldap.go
+++ b/pkg/services/ldap/ldap.go
@@ -12,7 +12,7 @@ import (
LDAP "gopkg.in/ldap.v3"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
models "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/services/ldap/ldap_login_test.go b/pkg/services/ldap/ldap_login_test.go
index 5afed5462463..b8dd502667ea 100644
--- a/pkg/services/ldap/ldap_login_test.go
+++ b/pkg/services/ldap/ldap_login_test.go
@@ -6,7 +6,7 @@ import (
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ldap.v3"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
)
func TestLdapLogin(t *testing.T) {
diff --git a/pkg/services/ldap/ldap_test.go b/pkg/services/ldap/ldap_test.go
index c5232a54e2bd..4da041ae1642 100644
--- a/pkg/services/ldap/ldap_test.go
+++ b/pkg/services/ldap/ldap_test.go
@@ -8,7 +8,7 @@ import (
"gopkg.in/ldap.v3"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/ldap/settings.go b/pkg/services/ldap/settings.go
index 633920d0a194..de2da2402bfe 100644
--- a/pkg/services/ldap/settings.go
+++ b/pkg/services/ldap/settings.go
@@ -8,7 +8,7 @@ import (
"github.com/grafana/grafana/pkg/util/errutil"
"golang.org/x/xerrors"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
diff --git a/pkg/services/login/login.go b/pkg/services/login/login.go
index 9b2a258dea02..791b34e0b30a 100644
--- a/pkg/services/login/login.go
+++ b/pkg/services/login/login.go
@@ -2,7 +2,7 @@ package login
import (
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/quota"
diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go
index 769fdd06fd04..3168fdcd5e95 100644
--- a/pkg/services/notifications/notifications.go
+++ b/pkg/services/notifications/notifications.go
@@ -11,7 +11,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/services/provisioning/dashboards/config_reader.go b/pkg/services/provisioning/dashboards/config_reader.go
index 50cedc079e33..d004221a3525 100644
--- a/pkg/services/provisioning/dashboards/config_reader.go
+++ b/pkg/services/provisioning/dashboards/config_reader.go
@@ -7,7 +7,7 @@ import (
"path/filepath"
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
yaml "gopkg.in/yaml.v2"
)
diff --git a/pkg/services/provisioning/dashboards/config_reader_test.go b/pkg/services/provisioning/dashboards/config_reader_test.go
index 18d8022d62d0..03aa19af11e8 100644
--- a/pkg/services/provisioning/dashboards/config_reader_test.go
+++ b/pkg/services/provisioning/dashboards/config_reader_test.go
@@ -4,7 +4,7 @@ import (
"os"
"testing"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
. "github.com/smartystreets/goconvey/convey"
)
diff --git a/pkg/services/provisioning/dashboards/dashboard.go b/pkg/services/provisioning/dashboards/dashboard.go
index fd3a824420c6..9cf2e3225f4d 100644
--- a/pkg/services/provisioning/dashboards/dashboard.go
+++ b/pkg/services/provisioning/dashboards/dashboard.go
@@ -3,7 +3,7 @@ package dashboards
import (
"context"
"fmt"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/pkg/errors"
)
diff --git a/pkg/services/provisioning/dashboards/file_reader.go b/pkg/services/provisioning/dashboards/file_reader.go
index 96da9c8f6dfb..61f6be457ee5 100644
--- a/pkg/services/provisioning/dashboards/file_reader.go
+++ b/pkg/services/provisioning/dashboards/file_reader.go
@@ -16,7 +16,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/provisioning/dashboards/file_reader_linux_test.go b/pkg/services/provisioning/dashboards/file_reader_linux_test.go
index d62a59a4f4c7..4adeeafe49b4 100644
--- a/pkg/services/provisioning/dashboards/file_reader_linux_test.go
+++ b/pkg/services/provisioning/dashboards/file_reader_linux_test.go
@@ -6,7 +6,7 @@ import (
"path/filepath"
"testing"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
)
var (
diff --git a/pkg/services/provisioning/dashboards/file_reader_test.go b/pkg/services/provisioning/dashboards/file_reader_test.go
index efc6052cbe79..668608d79eca 100644
--- a/pkg/services/provisioning/dashboards/file_reader_test.go
+++ b/pkg/services/provisioning/dashboards/file_reader_test.go
@@ -13,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
. "github.com/smartystreets/goconvey/convey"
)
diff --git a/pkg/services/provisioning/datasources/config_reader.go b/pkg/services/provisioning/datasources/config_reader.go
index 334ce879e5ad..60794e3b4b03 100644
--- a/pkg/services/provisioning/datasources/config_reader.go
+++ b/pkg/services/provisioning/datasources/config_reader.go
@@ -6,7 +6,7 @@ import (
"path/filepath"
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"gopkg.in/yaml.v2"
)
diff --git a/pkg/services/provisioning/datasources/config_reader_test.go b/pkg/services/provisioning/datasources/config_reader_test.go
index 6aba82622141..6bf60b30fc1b 100644
--- a/pkg/services/provisioning/datasources/config_reader_test.go
+++ b/pkg/services/provisioning/datasources/config_reader_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
diff --git a/pkg/services/provisioning/datasources/datasources.go b/pkg/services/provisioning/datasources/datasources.go
index de6c876baada..f25d717b915f 100644
--- a/pkg/services/provisioning/datasources/datasources.go
+++ b/pkg/services/provisioning/datasources/datasources.go
@@ -5,7 +5,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/provisioning/datasources/types.go b/pkg/services/provisioning/datasources/types.go
index bbd072052dd7..d1a76246c9db 100644
--- a/pkg/services/provisioning/datasources/types.go
+++ b/pkg/services/provisioning/datasources/types.go
@@ -2,7 +2,7 @@ package datasources
import (
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/provisioning/values"
)
diff --git a/pkg/services/provisioning/notifiers/alert_notifications.go b/pkg/services/provisioning/notifiers/alert_notifications.go
index 3659a73832ee..865e215e38bf 100644
--- a/pkg/services/provisioning/notifiers/alert_notifications.go
+++ b/pkg/services/provisioning/notifiers/alert_notifications.go
@@ -4,7 +4,7 @@ import (
"errors"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
)
diff --git a/pkg/services/provisioning/notifiers/config_reader.go b/pkg/services/provisioning/notifiers/config_reader.go
index 896d5105cedb..5dbb3d87b786 100644
--- a/pkg/services/provisioning/notifiers/config_reader.go
+++ b/pkg/services/provisioning/notifiers/config_reader.go
@@ -7,7 +7,7 @@ import (
"path/filepath"
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"gopkg.in/yaml.v2"
diff --git a/pkg/services/provisioning/notifiers/config_reader_test.go b/pkg/services/provisioning/notifiers/config_reader_test.go
index d705bd0d93d9..e2ffb5aa75f8 100644
--- a/pkg/services/provisioning/notifiers/config_reader_test.go
+++ b/pkg/services/provisioning/notifiers/config_reader_test.go
@@ -4,7 +4,7 @@ import (
"os"
"testing"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/alerting/notifiers"
diff --git a/pkg/services/provisioning/provisioning.go b/pkg/services/provisioning/provisioning.go
index 29f2d1391643..7a88aa9e3391 100644
--- a/pkg/services/provisioning/provisioning.go
+++ b/pkg/services/provisioning/provisioning.go
@@ -5,7 +5,7 @@ import (
"path"
"sync"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/pkg/errors"
"github.com/grafana/grafana/pkg/registry"
diff --git a/pkg/services/rendering/phantomjs.go b/pkg/services/rendering/phantomjs.go
index 35389fe7918e..a67d43c3c239 100644
--- a/pkg/services/rendering/phantomjs.go
+++ b/pkg/services/rendering/phantomjs.go
@@ -10,7 +10,7 @@ import (
"strings"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
)
diff --git a/pkg/services/rendering/rendering.go b/pkg/services/rendering/rendering.go
index 06db73771b26..259b5db2d7af 100644
--- a/pkg/services/rendering/rendering.go
+++ b/pkg/services/rendering/rendering.go
@@ -10,7 +10,7 @@ import (
plugin "github.com/hashicorp/go-plugin"
pluginModel "github.com/grafana/grafana-plugin-model/go/renderer"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
diff --git a/pkg/services/sqlstore/logger.go b/pkg/services/sqlstore/logger.go
index 9b0b068c9184..498c2b58c17d 100644
--- a/pkg/services/sqlstore/logger.go
+++ b/pkg/services/sqlstore/logger.go
@@ -3,7 +3,7 @@ package sqlstore
import (
"fmt"
- glog "github.com/grafana/grafana/pkg/log"
+ glog "github.com/grafana/grafana/pkg/infra/log"
"github.com/go-xorm/core"
)
diff --git a/pkg/services/sqlstore/migrator/migrator.go b/pkg/services/sqlstore/migrator/migrator.go
index ce0b2663def4..68820c7feb6a 100644
--- a/pkg/services/sqlstore/migrator/migrator.go
+++ b/pkg/services/sqlstore/migrator/migrator.go
@@ -5,7 +5,7 @@ import (
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)
diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go
index 116c246cbe34..97b9080ffcfd 100644
--- a/pkg/services/sqlstore/sqlstore.go
+++ b/pkg/services/sqlstore/sqlstore.go
@@ -14,7 +14,7 @@ import (
"github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/annotations"
diff --git a/pkg/services/sqlstore/transactions.go b/pkg/services/sqlstore/transactions.go
index edf29fffb8f3..ed8b6a2309fd 100644
--- a/pkg/services/sqlstore/transactions.go
+++ b/pkg/services/sqlstore/transactions.go
@@ -5,7 +5,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
sqlite3 "github.com/mattn/go-sqlite3"
)
diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go
index 788ea7677c99..192f300021b8 100644
--- a/pkg/setting/setting.go
+++ b/pkg/setting/setting.go
@@ -20,7 +20,7 @@ import (
"github.com/go-macaron/session"
ini "gopkg.in/ini.v1"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util"
)
diff --git a/pkg/setting/setting_session_test.go b/pkg/setting/setting_session_test.go
index 7bf31123473e..0a73b47a91d8 100644
--- a/pkg/setting/setting_session_test.go
+++ b/pkg/setting/setting_session_test.go
@@ -4,7 +4,7 @@ import (
"path/filepath"
"testing"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
. "github.com/smartystreets/goconvey/convey"
)
diff --git a/pkg/tsdb/azuremonitor/azuremonitor.go b/pkg/tsdb/azuremonitor/azuremonitor.go
index 31a42d21a12f..39014bf38dae 100644
--- a/pkg/tsdb/azuremonitor/azuremonitor.go
+++ b/pkg/tsdb/azuremonitor/azuremonitor.go
@@ -5,7 +5,7 @@ import (
"fmt"
"net/http"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
)
diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go
index 56e9bd30e98b..34d57f4d71fc 100644
--- a/pkg/tsdb/cloudwatch/cloudwatch.go
+++ b/pkg/tsdb/cloudwatch/cloudwatch.go
@@ -10,7 +10,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
"golang.org/x/sync/errgroup"
diff --git a/pkg/tsdb/elasticsearch/client/client.go b/pkg/tsdb/elasticsearch/client/client.go
index 131bcdc97844..48f9cce0a5a2 100644
--- a/pkg/tsdb/elasticsearch/client/client.go
+++ b/pkg/tsdb/elasticsearch/client/client.go
@@ -13,7 +13,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/models"
diff --git a/pkg/tsdb/elasticsearch/elasticsearch.go b/pkg/tsdb/elasticsearch/elasticsearch.go
index 857b847f0f93..01c9194877c9 100644
--- a/pkg/tsdb/elasticsearch/elasticsearch.go
+++ b/pkg/tsdb/elasticsearch/elasticsearch.go
@@ -4,7 +4,7 @@ import (
"context"
"fmt"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go
index 9a5de205a23a..b74112c3001e 100644
--- a/pkg/tsdb/graphite/graphite.go
+++ b/pkg/tsdb/graphite/graphite.go
@@ -13,7 +13,7 @@ import (
"golang.org/x/net/context/ctxhttp"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb"
diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go
index 6bff961f981f..3b8f365211a5 100644
--- a/pkg/tsdb/influxdb/influxdb.go
+++ b/pkg/tsdb/influxdb/influxdb.go
@@ -10,7 +10,7 @@ import (
"path"
"strings"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb"
diff --git a/pkg/tsdb/mssql/mssql.go b/pkg/tsdb/mssql/mssql.go
index 6503632aea3a..81a7e159a868 100644
--- a/pkg/tsdb/mssql/mssql.go
+++ b/pkg/tsdb/mssql/mssql.go
@@ -8,7 +8,7 @@ import (
_ "github.com/denisenkom/go-mssqldb"
"github.com/go-xorm/core"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/util"
diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go
index 95a9e02598f0..1f569f215db9 100644
--- a/pkg/tsdb/mysql/mysql.go
+++ b/pkg/tsdb/mysql/mysql.go
@@ -10,7 +10,7 @@ import (
"github.com/go-sql-driver/mysql"
"github.com/go-xorm/core"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
)
diff --git a/pkg/tsdb/opentsdb/opentsdb.go b/pkg/tsdb/opentsdb/opentsdb.go
index d0f61e052335..372f0ef67849 100644
--- a/pkg/tsdb/opentsdb/opentsdb.go
+++ b/pkg/tsdb/opentsdb/opentsdb.go
@@ -15,7 +15,7 @@ import (
"net/url"
"github.com/grafana/grafana/pkg/components/null"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb"
diff --git a/pkg/tsdb/postgres/postgres.go b/pkg/tsdb/postgres/postgres.go
index 7840a47fb187..dc3a006123b3 100644
--- a/pkg/tsdb/postgres/postgres.go
+++ b/pkg/tsdb/postgres/postgres.go
@@ -7,7 +7,7 @@ import (
"strconv"
"github.com/go-xorm/core"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
)
diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go
index bb343aea26e7..04fef37c2cf2 100644
--- a/pkg/tsdb/prometheus/prometheus.go
+++ b/pkg/tsdb/prometheus/prometheus.go
@@ -12,7 +12,7 @@ import (
"net/http"
"github.com/grafana/grafana/pkg/components/null"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
api "github.com/prometheus/client_golang/api"
diff --git a/pkg/tsdb/sql_engine.go b/pkg/tsdb/sql_engine.go
index ab7230e9b029..b4ceead85678 100644
--- a/pkg/tsdb/sql_engine.go
+++ b/pkg/tsdb/sql_engine.go
@@ -12,7 +12,7 @@ import (
"sync"
"time"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/components/null"
diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go
index 76b346553ad2..e811e70c775d 100644
--- a/pkg/tsdb/stackdriver/stackdriver.go
+++ b/pkg/tsdb/stackdriver/stackdriver.go
@@ -21,7 +21,7 @@ import (
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
diff --git a/pkg/tsdb/testdata/scenarios.go b/pkg/tsdb/testdata/scenarios.go
index 964e6f586f33..0a521894f430 100644
--- a/pkg/tsdb/testdata/scenarios.go
+++ b/pkg/tsdb/testdata/scenarios.go
@@ -9,7 +9,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/null"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/tsdb"
)
diff --git a/pkg/tsdb/testdata/testdata.go b/pkg/tsdb/testdata/testdata.go
index c2c2ea3f696c..11b25c73542f 100644
--- a/pkg/tsdb/testdata/testdata.go
+++ b/pkg/tsdb/testdata/testdata.go
@@ -3,7 +3,7 @@ package testdata
import (
"context"
- "github.com/grafana/grafana/pkg/log"
+ "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
)
From e0b760e08e1d83805c1286f4ea2b4b7bf263a375 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 13 May 2019 09:38:19 +0200
Subject: [PATCH 007/166] Chore: No implict any fixes (#17020)
---
package.json | 3 +-
packages/grafana-ui/src/types/datasource.ts | 6 ++
.../app/core/components/PasswordStrength.tsx | 2 +-
.../core/components/TagFilter/TagBadge.tsx | 2 +-
.../core/components/TagFilter/TagFilter.tsx | 24 +++---
.../core/components/TagFilter/TagOption.tsx | 2 +
.../core/components/TagFilter/TagValue.tsx | 5 +-
.../ToggleButtonGroup/ToggleButtonGroup.tsx | 4 +-
.../app/core/components/dashboard_selector.ts | 3 +-
public/app/core/components/info_popover.ts | 5 +-
public/app/core/components/org_switcher.ts | 7 +-
.../app/core/components/sql_part/sql_part.ts | 2 +-
.../components/sql_part/sql_part_editor.ts | 26 +++---
public/app/core/components/switch.ts | 2 +-
public/app/core/jquery_extended.ts | 2 +-
public/app/core/logs_model.ts | 10 +--
public/app/core/nav_model_srv.ts | 2 +-
public/app/core/partials.ts | 2 +-
public/app/core/profiler.ts | 4 +-
public/app/core/services/backend_srv.ts | 2 +-
public/app/core/services/segment_srv.ts | 84 +++++++++++--------
public/app/core/table_model.ts | 23 +++--
public/app/core/time_series2.ts | 20 ++---
.../app/core/utils/connectWithReduxStore.tsx | 2 +-
public/app/features/admin/AdminEditOrgCtrl.ts | 19 +++--
.../app/features/admin/AdminEditUserCtrl.ts | 24 +++---
.../app/features/admin/AdminListOrgsCtrl.ts | 11 ++-
.../app/features/admin/AdminListUsersCtrl.ts | 15 ++--
.../app/features/admin/ServerStats.test.tsx | 1 +
public/app/features/admin/StyleGuideCtrl.ts | 6 +-
public/app/features/admin/index.ts | 12 +--
.../features/alerting/AlertRuleItem.test.tsx | 2 +-
.../app/features/alerting/AlertRuleItem.tsx | 1 +
public/app/features/alerting/AlertTabCtrl.ts | 65 +++++++-------
.../alerting/NotificationsEditCtrl.ts | 31 ++++---
.../alerting/NotificationsListCtrl.ts | 13 +--
public/app/features/alerting/StateHistory.tsx | 6 +-
.../alerting/state/ThresholdMapper.ts | 6 +-
.../app/features/alerting/state/alertDef.ts | 8 +-
.../features/alerting/state/reducers.test.ts | 4 +-
.../app/features/alerting/state/reducers.ts | 18 ++--
.../features/alerting/state/selectors.test.ts | 6 +-
.../app/features/alerting/state/selectors.ts | 6 +-
.../dashboard/dashgrid/PanelChrome.tsx | 2 +-
public/app/features/explore/Explore.tsx | 2 +-
public/app/features/explore/QueryRow.tsx | 21 ++---
public/app/features/explore/Wrapper.tsx | 1 +
public/app/features/org/state/actions.ts | 9 +-
public/app/features/panel/panel_ctrl.ts | 2 +-
.../datasource/prometheus/datasource.ts | 2 +-
public/app/types/alerting.ts | 4 +-
scripts/ci-frontend-metrics.sh | 2 +-
yarn.lock | 19 ++++-
53 files changed, 321 insertions(+), 241 deletions(-)
diff --git a/package.json b/package.json
index 69309e2e341b..85c953f907a0 100644
--- a/package.json
+++ b/package.json
@@ -187,6 +187,7 @@
"dependencies": {
"@babel/polyfill": "7.2.5",
"@torkelo/react-select": "2.4.1",
+ "@types/react-redux": "^7.0.8",
"@types/reselect": "2.2.0",
"angular": "1.6.6",
"angular-bindonce": "0.3.1",
@@ -201,8 +202,8 @@
"d3": "4.13.0",
"d3-scale-chromatic": "1.3.3",
"eventemitter3": "2.0.3",
- "file-saver": "1.3.8",
"fast-text-encoding": "^1.0.0",
+ "file-saver": "1.3.8",
"immutable": "3.8.2",
"jquery": "3.4.0",
"lodash": "4.17.11",
diff --git a/packages/grafana-ui/src/types/datasource.ts b/packages/grafana-ui/src/types/datasource.ts
index 2622efd3ed50..3be0bae09dd8 100644
--- a/packages/grafana-ui/src/types/datasource.ts
+++ b/packages/grafana-ui/src/types/datasource.ts
@@ -78,6 +78,7 @@ export interface DataSourcePluginMeta extends PluginMeta {
logs?: boolean;
explore?: boolean;
annotations?: boolean;
+ alerting?: boolean;
mixed?: boolean;
hasQueryHelp?: boolean;
category?: string;
@@ -181,6 +182,11 @@ export abstract class DataSourceApi<
* static information about the datasource
*/
meta?: DataSourcePluginMeta;
+
+ /**
+ * Used by alerting to check if query contains template variables
+ */
+ targetContainsTemplate?(query: TQuery): boolean;
}
export abstract class ExploreDataSourceApi<
diff --git a/public/app/core/components/PasswordStrength.tsx b/public/app/core/components/PasswordStrength.tsx
index 1d676a00a375..6c6bb697f653 100644
--- a/public/app/core/components/PasswordStrength.tsx
+++ b/public/app/core/components/PasswordStrength.tsx
@@ -5,7 +5,7 @@ export interface Props {
}
export class PasswordStrength extends React.Component {
- constructor(props) {
+ constructor(props: Props) {
super(props);
}
diff --git a/public/app/core/components/TagFilter/TagBadge.tsx b/public/app/core/components/TagFilter/TagBadge.tsx
index b00b95b70cba..d7a0c8977958 100644
--- a/public/app/core/components/TagFilter/TagBadge.tsx
+++ b/public/app/core/components/TagFilter/TagBadge.tsx
@@ -9,7 +9,7 @@ export interface Props {
}
export class TagBadge extends React.Component {
- constructor(props) {
+ constructor(props: Props) {
super(props);
}
diff --git a/public/app/core/components/TagFilter/TagFilter.tsx b/public/app/core/components/TagFilter/TagFilter.tsx
index 967792a9f7b2..89d3e8469b6b 100644
--- a/public/app/core/components/TagFilter/TagFilter.tsx
+++ b/public/app/core/components/TagFilter/TagFilter.tsx
@@ -1,10 +1,14 @@
+// Libraries
import React from 'react';
-import { NoOptionsMessage, IndicatorsContainer, resetSelectStyles } from '@grafana/ui';
+// @ts-ignore
+import { components } from '@torkelo/react-select';
+// @ts-ignore
import AsyncSelect from '@torkelo/react-select/lib/Async';
+// Components
import { TagOption } from './TagOption';
import { TagBadge } from './TagBadge';
-import { components } from '@torkelo/react-select';
+import { NoOptionsMessage, IndicatorsContainer, resetSelectStyles } from '@grafana/ui';
import { escapeStringForRegex } from '../FilterInput/FilterInput';
export interface Props {
@@ -16,12 +20,12 @@ export interface Props {
export class TagFilter extends React.Component {
inlineTags: boolean;
- constructor(props) {
+ constructor(props: Props) {
super(props);
}
- onLoadOptions = query => {
- return this.props.tagOptions().then(options => {
+ onLoadOptions = (query: string) => {
+ return this.props.tagOptions().then((options: any[]) => {
return options.map(option => ({
value: option.term,
label: option.term,
@@ -47,11 +51,11 @@ export class TagFilter extends React.Component {
placeholder: 'Tags',
loadingMessage: () => 'Loading...',
noOptionsMessage: () => 'No tags found',
- getOptionValue: i => i.value,
- getOptionLabel: i => i.label,
+ getOptionValue: (i: any) => i.value,
+ getOptionLabel: (i: any) => i.label,
value: tags,
styles: resetSelectStyles(),
- filterOption: (option, searchQuery) => {
+ filterOption: (option: any, searchQuery: string) => {
const regex = RegExp(escapeStringForRegex(searchQuery), 'i');
return regex.test(option.value);
},
@@ -59,10 +63,10 @@ export class TagFilter extends React.Component {
Option: TagOption,
IndicatorsContainer,
NoOptionsMessage,
- MultiValueLabel: () => {
+ MultiValueLabel: (): any => {
return null; // We want the whole tag to be clickable so we use MultiValueRemove instead
},
- MultiValueRemove: props => {
+ MultiValueRemove: (props: any) => {
const { data } = props;
return (
diff --git a/public/app/core/components/TagFilter/TagOption.tsx b/public/app/core/components/TagFilter/TagOption.tsx
index 9db15aaca0b3..4da6202bf3e8 100644
--- a/public/app/core/components/TagFilter/TagOption.tsx
+++ b/public/app/core/components/TagFilter/TagOption.tsx
@@ -1,4 +1,6 @@
+// Libraries
import React from 'react';
+// @ts-ignore
import { components } from '@torkelo/react-select';
import { OptionProps } from 'react-select/lib/components/Option';
import { TagBadge } from './TagBadge';
diff --git a/public/app/core/components/TagFilter/TagValue.tsx b/public/app/core/components/TagFilter/TagValue.tsx
index 43e41c7fdc29..0406ead16a62 100644
--- a/public/app/core/components/TagFilter/TagValue.tsx
+++ b/public/app/core/components/TagFilter/TagValue.tsx
@@ -9,18 +9,17 @@ export interface Props {
}
export class TagValue extends React.Component {
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.onClick = this.onClick.bind(this);
}
- onClick(event) {
+ onClick(event: React.SyntheticEvent) {
this.props.onRemove(this.props.value, event);
}
render() {
const { value } = this.props;
-
return ;
}
}
diff --git a/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx b/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx
index e550afc20c3f..ce1f80c8dcae 100644
--- a/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx
+++ b/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx
@@ -21,7 +21,7 @@ export default class ToggleButtonGroup extends PureComponent void;
+ onChange?: (value: any) => void;
selected?: boolean;
value: any;
className?: string;
@@ -37,7 +37,7 @@ export const ToggleButton: FC = ({
tooltip,
onChange,
}) => {
- const onClick = event => {
+ const onClick = (event: React.SyntheticEvent) => {
event.stopPropagation();
if (onChange) {
onChange(value);
diff --git a/public/app/core/components/dashboard_selector.ts b/public/app/core/components/dashboard_selector.ts
index e1809f3d42cd..f312c77f6a85 100644
--- a/public/app/core/components/dashboard_selector.ts
+++ b/public/app/core/components/dashboard_selector.ts
@@ -1,4 +1,5 @@
import coreModule from 'app/core/core_module';
+import { BackendSrv } from '../services/backend_srv';
const template = `
@@ -9,7 +10,7 @@ export class DashboardSelectorCtrl {
options: any;
/** @ngInject */
- constructor(private backendSrv) {}
+ constructor(private backendSrv: BackendSrv) {}
$onInit() {
this.options = [{ value: 0, text: 'Default' }];
diff --git a/public/app/core/components/info_popover.ts b/public/app/core/components/info_popover.ts
index 2ada91b09f1b..9c5694188d6e 100644
--- a/public/app/core/components/info_popover.ts
+++ b/public/app/core/components/info_popover.ts
@@ -1,5 +1,6 @@
import _ from 'lodash';
import coreModule from 'app/core/core_module';
+// @ts-ignore
import Drop from 'tether-drop';
export function infoPopover() {
@@ -7,7 +8,7 @@ export function infoPopover() {
restrict: 'E',
template: '',
transclude: true,
- link: (scope, elem, attrs, ctrl, transclude) => {
+ link: (scope: any, elem: any, attrs: any, transclude: any) => {
const offset = attrs.offset || '0 -10px';
const position = attrs.position || 'right middle';
let classes = 'drop-help drop-hide-out-of-bounds';
@@ -23,7 +24,7 @@ export function infoPopover() {
elem.addClass('gf-form-help-icon--' + attrs.mode);
}
- transclude((clone, newScope) => {
+ transclude((clone: any, newScope: any) => {
const content = document.createElement('div');
content.className = 'markdown-html';
diff --git a/public/app/core/components/org_switcher.ts b/public/app/core/components/org_switcher.ts
index dc8f00f0fdb4..0e25482e190a 100644
--- a/public/app/core/components/org_switcher.ts
+++ b/public/app/core/components/org_switcher.ts
@@ -1,6 +1,7 @@
import coreModule from 'app/core/core_module';
import { contextSrv } from 'app/core/services/context_srv';
import config from 'app/core/config';
+import { BackendSrv } from '../services/backend_srv';
const template = `
@@ -47,18 +48,18 @@ export class OrgSwitchCtrl {
currentOrgId: any;
/** @ngInject */
- constructor(private backendSrv) {
+ constructor(private backendSrv: BackendSrv) {
this.currentOrgId = contextSrv.user.orgId;
this.getUserOrgs();
}
getUserOrgs() {
- this.backendSrv.get('/api/user/orgs').then(orgs => {
+ this.backendSrv.get('/api/user/orgs').then((orgs: any) => {
this.orgs = orgs;
});
}
- setUsingOrg(org) {
+ setUsingOrg(org: any) {
return this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
this.setWindowLocation(config.appSubUrl + (config.appSubUrl.endsWith('/') ? '' : '/') + '?orgId=' + org.orgId);
});
diff --git a/public/app/core/components/sql_part/sql_part.ts b/public/app/core/components/sql_part/sql_part.ts
index a9b76b1ed2dc..543d9eaf761b 100644
--- a/public/app/core/components/sql_part/sql_part.ts
+++ b/public/app/core/components/sql_part/sql_part.ts
@@ -61,7 +61,7 @@ export class SqlPart {
this.params = part.params;
}
- updateParam(strValue, index) {
+ updateParam(strValue: string, index: number) {
// handle optional parameters
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
diff --git a/public/app/core/components/sql_part/sql_part_editor.ts b/public/app/core/components/sql_part/sql_part_editor.ts
index b095f5fefed7..7799537b0fc2 100644
--- a/public/app/core/components/sql_part/sql_part_editor.ts
+++ b/public/app/core/components/sql_part/sql_part_editor.ts
@@ -14,7 +14,7 @@ const template = `
`;
/** @ngInject */
-export function sqlPartEditorDirective($compile, templateSrv) {
+export function sqlPartEditorDirective(templateSrv: any) {
const paramTemplate = '
';
return {
@@ -25,16 +25,16 @@ export function sqlPartEditorDirective($compile, templateSrv) {
handleEvent: '&',
debounce: '@',
},
- link: function postLink($scope, elem) {
+ link: function postLink($scope: any, elem: any) {
const part = $scope.part;
const partDef = part.def;
const $paramsContainer = elem.find('.query-part-parameters');
const debounceLookup = $scope.debounce;
- let cancelBlur = null;
+ let cancelBlur: any = null;
$scope.partActions = [];
- function clickFuncParam(this: any, paramIndex) {
+ function clickFuncParam(this: any, paramIndex: number) {
/*jshint validthis:true */
const $link = $(this);
const $input = $link.next();
@@ -54,13 +54,13 @@ export function sqlPartEditorDirective($compile, templateSrv) {
}
}
- function inputBlur($input, paramIndex) {
+ function inputBlur($input: JQuery, paramIndex: number) {
cancelBlur = setTimeout(() => {
switchToLink($input, paramIndex);
}, 200);
}
- function switchToLink($input, paramIndex) {
+ function switchToLink($input: JQuery, paramIndex: number) {
/*jshint validthis:true */
const $link = $input.prev();
const newValue = $input.val();
@@ -78,7 +78,7 @@ export function sqlPartEditorDirective($compile, templateSrv) {
$link.show();
}
- function inputKeyPress(this: any, paramIndex, e) {
+ function inputKeyPress(this: any, paramIndex: number, e: any) {
/*jshint validthis:true */
if (e.which === 13) {
switchToLink($(this), paramIndex);
@@ -90,12 +90,12 @@ export function sqlPartEditorDirective($compile, templateSrv) {
this.style.width = (3 + this.value.length) * 8 + 'px';
}
- function addTypeahead($input, param, paramIndex) {
+ function addTypeahead($input: JQuery, param: any, paramIndex: number) {
if (!param.options && !param.dynamicLookup) {
return;
}
- const typeaheadSource = (query, callback) => {
+ const typeaheadSource = (query: string, callback: any) => {
if (param.options) {
let options = param.options;
if (param.type === 'int') {
@@ -107,7 +107,7 @@ export function sqlPartEditorDirective($compile, templateSrv) {
}
$scope.$apply(() => {
- $scope.handleEvent({ $event: { name: 'get-param-options', param: param } }).then(result => {
+ $scope.handleEvent({ $event: { name: 'get-param-options', param: param } }).then((result: any) => {
const dynamicOptions = _.map(result, op => {
return _.escape(op.value);
});
@@ -128,7 +128,7 @@ export function sqlPartEditorDirective($compile, templateSrv) {
source: typeaheadSource,
minLength: 0,
items: 1000,
- updater: value => {
+ updater: (value: string) => {
value = _.unescape(value);
if (value === part.params[paramIndex]) {
clearTimeout(cancelBlur);
@@ -152,12 +152,12 @@ export function sqlPartEditorDirective($compile, templateSrv) {
}
$scope.showActionsMenu = () => {
- $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then(res => {
+ $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then((res: any) => {
$scope.partActions = res;
});
};
- $scope.triggerPartAction = action => {
+ $scope.triggerPartAction = (action: string) => {
$scope.handleEvent({ $event: { name: 'action', action: action } });
};
diff --git a/public/app/core/components/switch.ts b/public/app/core/components/switch.ts
index 4a1d3ff804fe..d6a66a1a362f 100644
--- a/public/app/core/components/switch.ts
+++ b/public/app/core/components/switch.ts
@@ -38,7 +38,7 @@ export class SwitchCtrl {
label: string;
/** @ngInject */
- constructor($scope, private $timeout) {
+ constructor($scope: any, private $timeout: any) {
this.show = true;
this.id = $scope.$id;
}
diff --git a/public/app/core/jquery_extended.ts b/public/app/core/jquery_extended.ts
index fa9b1aeb8234..11c67be9656c 100644
--- a/public/app/core/jquery_extended.ts
+++ b/public/app/core/jquery_extended.ts
@@ -9,7 +9,7 @@ $.fn.place_tt = (() => {
offset: 5,
};
- return function(this: any, x, y, opts) {
+ return function(this: any, x: number, y: number, opts: any) {
opts = $.extend(true, {}, defaults, opts);
return this.each(() => {
diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts
index 2ba60ce428e9..eb292c033f8a 100644
--- a/public/app/core/logs_model.ts
+++ b/public/app/core/logs_model.ts
@@ -123,7 +123,7 @@ export const LogsParsers: { [name: string]: LogsParser } = {
JSON: {
buildMatcher: label => new RegExp(`(?:{|,)\\s*"${label}"\\s*:\\s*"?([\\d\\.]+|[^"]*)"?`),
getFields: line => {
- const fields = [];
+ const fields: string[] = [];
try {
const parsed = JSON.parse(line);
_.map(parsed, (value, key) => {
@@ -149,7 +149,7 @@ export const LogsParsers: { [name: string]: LogsParser } = {
logfmt: {
buildMatcher: label => new RegExp(`(?:^|\\s)${label}=("[^"]*"|\\S+)`),
getFields: line => {
- const fields = [];
+ const fields: string[] = [];
line.replace(new RegExp(LOGFMT_REGEXP, 'g'), substring => {
fields.push(substring.trim());
return '';
@@ -273,9 +273,9 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Time
// intervalMs = intervalMs * 10;
// Graph time series by log level
- const seriesByLevel = {};
+ const seriesByLevel: any = {};
const bucketSize = intervalMs * 10;
- const seriesList = [];
+ const seriesList: any[] = [];
for (const row of rows) {
let series = seriesByLevel[row.logLevel];
@@ -312,7 +312,7 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Time
}
return seriesList.map(series => {
- series.datapoints.sort((a, b) => {
+ series.datapoints.sort((a: number[], b: number[]) => {
return a[1] - b[1];
});
diff --git a/public/app/core/nav_model_srv.ts b/public/app/core/nav_model_srv.ts
index bf31c2169c5d..598db4a53f2b 100644
--- a/public/app/core/nav_model_srv.ts
+++ b/public/app/core/nav_model_srv.ts
@@ -15,7 +15,7 @@ export class NavModelSrv {
return _.find(this.navItems, { id: 'cfg' });
}
- getNav(...args) {
+ getNav(...args: string[]) {
let children = this.navItems;
const nav = {
breadcrumbs: [],
diff --git a/public/app/core/partials.ts b/public/app/core/partials.ts
index 864a3dcfa8a3..32203a18f42e 100644
--- a/public/app/core/partials.ts
+++ b/public/app/core/partials.ts
@@ -1,4 +1,4 @@
let templates = (require as any).context('../', true, /\.html$/);
-templates.keys().forEach(key => {
+templates.keys().forEach((key: string) => {
templates(key);
});
diff --git a/public/app/core/profiler.ts b/public/app/core/profiler.ts
index 2284401803c6..02f58b92d221 100644
--- a/public/app/core/profiler.ts
+++ b/public/app/core/profiler.ts
@@ -4,7 +4,7 @@ export class Profiler {
$rootScope: any;
window: any;
- init(config, $rootScope) {
+ init(config: any, $rootScope: any) {
this.$rootScope = $rootScope;
this.window = window;
@@ -13,7 +13,7 @@ export class Profiler {
}
}
- renderingCompleted(panelId) {
+ renderingCompleted() {
// add render counter to root scope
// used by phantomjs render.js to know when panel has rendered
this.panelsRendered = (this.panelsRendered || 0) + 1;
diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts
index 96e7a2cc258e..e14b5f57b288 100644
--- a/public/app/core/services/backend_srv.ts
+++ b/public/app/core/services/backend_srv.ts
@@ -29,7 +29,7 @@ export class BackendSrv {
return this.request({ method: 'DELETE', url });
}
- post(url: string, data: any) {
+ post(url: string, data?: any) {
return this.request({ method: 'POST', url, data });
}
diff --git a/public/app/core/services/segment_srv.ts b/public/app/core/services/segment_srv.ts
index e7653ec5cf73..d093a5570d6a 100644
--- a/public/app/core/services/segment_srv.ts
+++ b/public/app/core/services/segment_srv.ts
@@ -2,39 +2,51 @@ import _ from 'lodash';
import coreModule from '../core_module';
/** @ngInject */
-export function uiSegmentSrv(this: any, $sce, templateSrv) {
+export function uiSegmentSrv(this: any, $sce: any, templateSrv: any) {
const self = this;
- function MetricSegment(this: any, options) {
- if (options === '*' || options.value === '*') {
- this.value = '*';
- this.html = $sce.trustAsHtml('
');
- this.type = options.type;
- this.expandable = true;
- return;
- }
+ class MetricSegment {
+ value: string;
+ html: any;
+ type: any;
+ expandable: boolean;
+ text: string;
+ cssClass: string;
+ fake: boolean;
+ custom: boolean;
+ selectMode: any;
+
+ constructor(options: any) {
+ if (options === '*' || options.value === '*') {
+ this.value = '*';
+ this.html = $sce.trustAsHtml('');
+ this.type = options.type;
+ this.expandable = true;
+ return;
+ }
- if (_.isString(options)) {
- this.value = options;
- this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
- return;
- }
+ if (_.isString(options)) {
+ this.value = options;
+ this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
+ return;
+ }
- // temp hack to work around legacy inconsistency in segment model
- this.text = options.value;
-
- this.cssClass = options.cssClass;
- this.custom = options.custom;
- this.type = options.type;
- this.fake = options.fake;
- this.value = options.value;
- this.selectMode = options.selectMode;
- this.type = options.type;
- this.expandable = options.expandable;
- this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
+ // temp hack to work around legacy inconsistency in segment model
+ this.text = options.value;
+
+ this.cssClass = options.cssClass;
+ this.custom = options.custom;
+ this.type = options.type;
+ this.fake = options.fake;
+ this.value = options.value;
+ this.selectMode = options.selectMode;
+ this.type = options.type;
+ this.expandable = options.expandable;
+ this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
+ }
}
- this.getSegmentForValue = function(value, fallbackText) {
+ this.getSegmentForValue = function(value: string, fallbackText: string) {
if (value) {
return this.newSegment(value);
} else {
@@ -46,38 +58,38 @@ export function uiSegmentSrv(this: any, $sce, templateSrv) {
return new MetricSegment({ value: 'select measurement', fake: true });
};
- this.newFake = (text, type, cssClass) => {
+ this.newFake = (text: string, type: string, cssClass: string) => {
return new MetricSegment({ value: text, fake: true, type: type, cssClass: cssClass });
};
- this.newSegment = options => {
+ this.newSegment = (options: any) => {
return new MetricSegment(options);
};
- this.newKey = key => {
+ this.newKey = (key: string) => {
return new MetricSegment({ value: key, type: 'key', cssClass: 'query-segment-key' });
};
- this.newKeyValue = value => {
+ this.newKeyValue = (value: string) => {
return new MetricSegment({ value: value, type: 'value', cssClass: 'query-segment-value' });
};
- this.newCondition = condition => {
+ this.newCondition = (condition: string) => {
return new MetricSegment({ value: condition, type: 'condition', cssClass: 'query-keyword' });
};
- this.newOperator = op => {
+ this.newOperator = (op: string) => {
return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' });
};
- this.newOperators = ops => {
+ this.newOperators = (ops: string[]) => {
return _.map(ops, op => {
return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' });
});
};
- this.transformToSegments = (addTemplateVars, variableTypeFilter) => {
- return results => {
+ this.transformToSegments = (addTemplateVars: boolean, variableTypeFilter: string) => {
+ return (results: any[]) => {
const segments = _.map(results, segment => {
return self.newSegment({ value: segment.text, expandable: segment.expandable });
});
diff --git a/public/app/core/table_model.ts b/public/app/core/table_model.ts
index ffd1e54dcbc6..d193e7fcbe74 100644
--- a/public/app/core/table_model.ts
+++ b/public/app/core/table_model.ts
@@ -26,15 +26,19 @@ export default class TableModel implements TableData {
if (table) {
if (table.columns) {
- table.columns.forEach(col => this.addColumn(col));
+ for (const col of table.columns) {
+ this.addColumn(col);
+ }
}
if (table.rows) {
- table.rows.forEach(row => this.addRow(row));
+ for (const row of table.rows) {
+ this.addRow(row);
+ }
}
}
}
- sort(options) {
+ sort(options: { col: number; desc: boolean }) {
if (options.col === null || this.columns.length <= options.col) {
return;
}
@@ -54,21 +58,21 @@ export default class TableModel implements TableData {
this.columns[options.col].desc = options.desc;
}
- addColumn(col) {
+ addColumn(col: Column) {
if (!this.columnMap[col.text]) {
this.columns.push(col);
this.columnMap[col.text] = col;
}
}
- addRow(row) {
+ addRow(row: any[]) {
this.rows.push(row);
}
}
// Returns true if both rows have matching non-empty fields as well as matching
// indexes where one field is empty and the other is not
-function areRowsMatching(columns, row, otherRow) {
+function areRowsMatching(columns: Column[], row: any[], otherRow: any[]) {
let foundFieldToMatch = false;
for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
if (row[columnIndex] !== undefined && otherRow[columnIndex] !== undefined) {
@@ -96,7 +100,7 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
}
// Track column indexes of union: name -> index
- const columnNames = {};
+ const columnNames: { [key: string]: any } = {};
// Union of all non-value columns
const columnsUnion = tables.slice().reduce((acc, series) => {
@@ -119,7 +123,7 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
const flattenedRows = tables.reduce((acc, series, seriesIndex) => {
const mapper = columnIndexMapper[seriesIndex];
series.rows.forEach(row => {
- const alteredRow = [];
+ const alteredRow: any[] = [];
// Shifting entries according to index mapper
mapper.forEach((to, from) => {
alteredRow[to] = row[from];
@@ -130,7 +134,8 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
}, []);
// Merge rows that have same values for columns
- const mergedRows = {};
+ const mergedRows: { [key: string]: any } = {};
+
const compactedRows = flattenedRows.reduce((acc, row, rowIndex) => {
if (!mergedRows[rowIndex]) {
// Look from current row onwards
diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts
index 10c91c148d79..05815ab7ab38 100644
--- a/public/app/core/time_series2.ts
+++ b/public/app/core/time_series2.ts
@@ -1,8 +1,8 @@
import { getFlotTickDecimals } from 'app/core/utils/ticks';
import _ from 'lodash';
-import { getValueFormat, stringToJsRegex } from '@grafana/ui';
+import { getValueFormat, stringToJsRegex, ValueFormatter, DecimalCount } from '@grafana/ui';
-function matchSeriesOverride(aliasOrRegex, seriesAlias) {
+function matchSeriesOverride(aliasOrRegex: string, seriesAlias: string) {
if (!aliasOrRegex) {
return false;
}
@@ -15,7 +15,7 @@ function matchSeriesOverride(aliasOrRegex, seriesAlias) {
return aliasOrRegex === seriesAlias;
}
-function translateFillOption(fill) {
+function translateFillOption(fill: number) {
return fill === 0 ? 0.001 : fill / 10;
}
@@ -25,7 +25,7 @@ function translateFillOption(fill) {
* @param panel
* @param height
*/
-export function updateLegendValues(data: TimeSeries[], panel, height) {
+export function updateLegendValues(data: TimeSeries[], panel: any, height: number) {
for (let i = 0; i < data.length; i++) {
const series = data[i];
const yaxes = panel.yaxes;
@@ -97,7 +97,7 @@ export default class TimeSeries {
flotpairs: any;
unit: any;
- constructor(opts) {
+ constructor(opts: any) {
this.datapoints = opts.datapoints;
this.label = opts.alias;
this.id = opts.alias;
@@ -112,7 +112,7 @@ export default class TimeSeries {
this.hasMsResolution = this.isMsResolutionNeeded();
}
- applySeriesOverrides(overrides) {
+ applySeriesOverrides(overrides: any[]) {
this.lines = {};
this.dashes = {
dashLength: [],
@@ -192,7 +192,7 @@ export default class TimeSeries {
}
}
- getFlotPairs(fillStyle) {
+ getFlotPairs(fillStyle: string) {
const result = [];
this.stats.total = 0;
@@ -314,13 +314,13 @@ export default class TimeSeries {
return result;
}
- updateLegendValues(formater, decimals, scaledDecimals) {
+ updateLegendValues(formater: ValueFormatter, decimals: DecimalCount, scaledDecimals: DecimalCount) {
this.valueFormater = formater;
this.decimals = decimals;
this.scaledDecimals = scaledDecimals;
}
- formatValue(value) {
+ formatValue(value: number) {
if (!_.isFinite(value)) {
value = null; // Prevent NaN formatting
}
@@ -339,7 +339,7 @@ export default class TimeSeries {
return false;
}
- hideFromLegend(options) {
+ hideFromLegend(options: any) {
if (options.hideEmpty && this.allIsNull) {
return true;
}
diff --git a/public/app/core/utils/connectWithReduxStore.tsx b/public/app/core/utils/connectWithReduxStore.tsx
index be91958f8cd1..19493681c0d7 100644
--- a/public/app/core/utils/connectWithReduxStore.tsx
+++ b/public/app/core/utils/connectWithReduxStore.tsx
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import { store } from '../../store/store';
export function connectWithStore(WrappedComponent, ...args) {
- const ConnectedWrappedComponent = connect(...args)(WrappedComponent);
+ const ConnectedWrappedComponent = (connect as any)(...args)(WrappedComponent);
return props => {
return ;
diff --git a/public/app/features/admin/AdminEditOrgCtrl.ts b/public/app/features/admin/AdminEditOrgCtrl.ts
index 4ce0e2366f63..a10067b3dbab 100644
--- a/public/app/features/admin/AdminEditOrgCtrl.ts
+++ b/public/app/features/admin/AdminEditOrgCtrl.ts
@@ -1,8 +1,11 @@
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { NavModelSrv } from 'app/core/core';
+
export default class AdminEditOrgCtrl {
/** @ngInject */
- constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
+ constructor($scope: any, $routeParams: any, backendSrv: BackendSrv, $location: any, navModelSrv: NavModelSrv) {
$scope.init = () => {
- $scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0);
+ $scope.navModel = navModelSrv.getNav('admin', 'global-orgs');
if ($routeParams.id) {
$scope.getOrg($routeParams.id);
@@ -10,14 +13,14 @@ export default class AdminEditOrgCtrl {
}
};
- $scope.getOrg = id => {
- backendSrv.get('/api/orgs/' + id).then(org => {
+ $scope.getOrg = (id: number) => {
+ backendSrv.get('/api/orgs/' + id).then((org: any) => {
$scope.org = org;
});
};
- $scope.getOrgUsers = id => {
- backendSrv.get('/api/orgs/' + id + '/users').then(orgUsers => {
+ $scope.getOrgUsers = (id: number) => {
+ backendSrv.get('/api/orgs/' + id + '/users').then((orgUsers: any) => {
$scope.orgUsers = orgUsers;
});
};
@@ -32,11 +35,11 @@ export default class AdminEditOrgCtrl {
});
};
- $scope.updateOrgUser = orgUser => {
+ $scope.updateOrgUser = (orgUser: any) => {
backendSrv.patch('/api/orgs/' + orgUser.orgId + '/users/' + orgUser.userId, orgUser);
};
- $scope.removeOrgUser = orgUser => {
+ $scope.removeOrgUser = (orgUser: any) => {
backendSrv.delete('/api/orgs/' + orgUser.orgId + '/users/' + orgUser.userId).then(() => {
$scope.getOrgUsers($scope.org.id);
});
diff --git a/public/app/features/admin/AdminEditUserCtrl.ts b/public/app/features/admin/AdminEditUserCtrl.ts
index 4b7dc9d21e59..f64f5005dfa2 100644
--- a/public/app/features/admin/AdminEditUserCtrl.ts
+++ b/public/app/features/admin/AdminEditUserCtrl.ts
@@ -1,12 +1,15 @@
import _ from 'lodash';
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { NavModelSrv } from 'app/core/core';
+import { User } from 'app/core/services/context_srv';
export default class AdminEditUserCtrl {
/** @ngInject */
- constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
+ constructor($scope: any, $routeParams: any, backendSrv: BackendSrv, $location: any, navModelSrv: NavModelSrv) {
$scope.user = {};
$scope.newOrg = { name: '', role: 'Editor' };
$scope.permissions = {};
- $scope.navModel = navModelSrv.getNav('admin', 'global-users', 0);
+ $scope.navModel = navModelSrv.getNav('admin', 'global-users');
$scope.init = () => {
if ($routeParams.id) {
@@ -15,8 +18,8 @@ export default class AdminEditUserCtrl {
}
};
- $scope.getUser = id => {
- backendSrv.get('/api/users/' + id).then(user => {
+ $scope.getUser = (id: number) => {
+ backendSrv.get('/api/users/' + id).then((user: User) => {
$scope.user = user;
$scope.user_id = id;
$scope.permissions.isGrafanaAdmin = user.isGrafanaAdmin;
@@ -52,8 +55,8 @@ export default class AdminEditUserCtrl {
});
};
- $scope.getUserOrgs = id => {
- backendSrv.get('/api/users/' + id + '/orgs').then(orgs => {
+ $scope.getUserOrgs = (id: number) => {
+ backendSrv.get('/api/users/' + id + '/orgs').then((orgs: any) => {
$scope.orgs = orgs;
});
};
@@ -68,11 +71,11 @@ export default class AdminEditUserCtrl {
});
};
- $scope.updateOrgUser = orgUser => {
+ $scope.updateOrgUser = (orgUser: { orgId: string }) => {
backendSrv.patch('/api/orgs/' + orgUser.orgId + '/users/' + $scope.user_id, orgUser).then(() => {});
};
- $scope.removeOrgUser = orgUser => {
+ $scope.removeOrgUser = (orgUser: { orgId: string }) => {
backendSrv.delete('/api/orgs/' + orgUser.orgId + '/users/' + $scope.user_id).then(() => {
$scope.getUser($scope.user_id);
$scope.getUserOrgs($scope.user_id);
@@ -81,13 +84,13 @@ export default class AdminEditUserCtrl {
$scope.orgsSearchCache = [];
- $scope.searchOrgs = (queryStr, callback) => {
+ $scope.searchOrgs = (queryStr: any, callback: any) => {
if ($scope.orgsSearchCache.length > 0) {
callback(_.map($scope.orgsSearchCache, 'name'));
return;
}
- backendSrv.get('/api/orgs', { query: '' }).then(result => {
+ backendSrv.get('/api/orgs', { query: '' }).then((result: any) => {
$scope.orgsSearchCache = result;
callback(_.map(result, 'name'));
});
@@ -101,6 +104,7 @@ export default class AdminEditUserCtrl {
const orgInfo: any = _.find($scope.orgsSearchCache, {
name: $scope.newOrg.name,
});
+
if (!orgInfo) {
return;
}
diff --git a/public/app/features/admin/AdminListOrgsCtrl.ts b/public/app/features/admin/AdminListOrgsCtrl.ts
index d6ee4b33dbbe..439811edbf28 100644
--- a/public/app/features/admin/AdminListOrgsCtrl.ts
+++ b/public/app/features/admin/AdminListOrgsCtrl.ts
@@ -1,18 +1,21 @@
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { NavModelSrv } from 'app/core/core';
+
export default class AdminListOrgsCtrl {
/** @ngInject */
- constructor($scope, backendSrv, navModelSrv) {
+ constructor($scope: any, backendSrv: BackendSrv, navModelSrv: NavModelSrv) {
$scope.init = () => {
- $scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0);
+ $scope.navModel = navModelSrv.getNav('admin', 'global-orgs');
$scope.getOrgs();
};
$scope.getOrgs = () => {
- backendSrv.get('/api/orgs').then(orgs => {
+ backendSrv.get('/api/orgs').then((orgs: any) => {
$scope.orgs = orgs;
});
};
- $scope.deleteOrg = org => {
+ $scope.deleteOrg = (org: any) => {
$scope.appEvent('confirm-modal', {
title: 'Delete',
text: 'Do you want to delete organization ' + org.name + '?',
diff --git a/public/app/features/admin/AdminListUsersCtrl.ts b/public/app/features/admin/AdminListUsersCtrl.ts
index 5b5321d13cea..33a43adf0598 100644
--- a/public/app/features/admin/AdminListUsersCtrl.ts
+++ b/public/app/features/admin/AdminListUsersCtrl.ts
@@ -1,6 +1,9 @@
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { NavModelSrv } from 'app/core/core';
+
export default class AdminListUsersCtrl {
users: any;
- pages = [];
+ pages: any[] = [];
perPage = 50;
page = 1;
totalPages: number;
@@ -9,8 +12,8 @@ export default class AdminListUsersCtrl {
navModel: any;
/** @ngInject */
- constructor(private $scope, private backendSrv, navModelSrv) {
- this.navModel = navModelSrv.getNav('admin', 'global-users', 0);
+ constructor(private $scope: any, private backendSrv: BackendSrv, navModelSrv: NavModelSrv) {
+ this.navModel = navModelSrv.getNav('admin', 'global-users');
this.query = '';
this.getUsers();
}
@@ -18,7 +21,7 @@ export default class AdminListUsersCtrl {
getUsers() {
this.backendSrv
.get(`/api/users/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`)
- .then(result => {
+ .then((result: any) => {
this.users = result.users;
this.page = result.page;
this.perPage = result.perPage;
@@ -32,12 +35,12 @@ export default class AdminListUsersCtrl {
});
}
- navigateToPage(page) {
+ navigateToPage(page: any) {
this.page = page.page;
this.getUsers();
}
- deleteUser(user) {
+ deleteUser(user: any) {
this.$scope.appEvent('confirm-modal', {
title: 'Delete',
text: 'Do you want to delete ' + user.login + '?',
diff --git a/public/app/features/admin/ServerStats.test.tsx b/public/app/features/admin/ServerStats.test.tsx
index cbcc580f6120..c33a5f4fbb21 100644
--- a/public/app/features/admin/ServerStats.test.tsx
+++ b/public/app/features/admin/ServerStats.test.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+// @ts-ignore
import renderer from 'react-test-renderer';
import { ServerStats } from './ServerStats';
import { createNavModel } from 'test/mocks/common';
diff --git a/public/app/features/admin/StyleGuideCtrl.ts b/public/app/features/admin/StyleGuideCtrl.ts
index 6548aa091989..44ef3c487760 100644
--- a/public/app/features/admin/StyleGuideCtrl.ts
+++ b/public/app/features/admin/StyleGuideCtrl.ts
@@ -1,4 +1,6 @@
import config from 'app/core/config';
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { NavModelSrv } from 'app/core/core';
export default class StyleGuideCtrl {
theme: string;
@@ -8,8 +10,8 @@ export default class StyleGuideCtrl {
navModel: any;
/** @ngInject */
- constructor(private $routeParams, private backendSrv, navModelSrv) {
- this.navModel = navModelSrv.getNav('admin', 'styleguide', 0);
+ constructor(private $routeParams: any, private backendSrv: BackendSrv, navModelSrv: NavModelSrv) {
+ this.navModel = navModelSrv.getNav('admin', 'styleguide');
this.theme = config.bootData.user.lightTheme ? 'light' : 'dark';
}
diff --git a/public/app/features/admin/index.ts b/public/app/features/admin/index.ts
index fecc04bc4106..17c3204cda20 100644
--- a/public/app/features/admin/index.ts
+++ b/public/app/features/admin/index.ts
@@ -5,15 +5,17 @@ import AdminEditOrgCtrl from './AdminEditOrgCtrl';
import StyleGuideCtrl from './StyleGuideCtrl';
import coreModule from 'app/core/core_module';
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { NavModelSrv } from 'app/core/core';
class AdminSettingsCtrl {
navModel: any;
/** @ngInject */
- constructor($scope, backendSrv, navModelSrv) {
- this.navModel = navModelSrv.getNav('admin', 'server-settings', 0);
+ constructor($scope: any, backendSrv: BackendSrv, navModelSrv: NavModelSrv) {
+ this.navModel = navModelSrv.getNav('admin', 'server-settings');
- backendSrv.get('/api/admin/settings').then(settings => {
+ backendSrv.get('/api/admin/settings').then((settings: any) => {
$scope.settings = settings;
});
}
@@ -23,8 +25,8 @@ class AdminHomeCtrl {
navModel: any;
/** @ngInject */
- constructor(navModelSrv) {
- this.navModel = navModelSrv.getNav('admin', 0);
+ constructor(navModelSrv: NavModelSrv) {
+ this.navModel = navModelSrv.getNav('admin');
}
}
diff --git a/public/app/features/alerting/AlertRuleItem.test.tsx b/public/app/features/alerting/AlertRuleItem.test.tsx
index bd37e127c398..680cd69dc31a 100644
--- a/public/app/features/alerting/AlertRuleItem.test.tsx
+++ b/public/app/features/alerting/AlertRuleItem.test.tsx
@@ -3,7 +3,7 @@ import { shallow } from 'enzyme';
import AlertRuleItem, { Props } from './AlertRuleItem';
jest.mock('react-redux', () => ({
- connect: () => params => params,
+ connect: () => (params: any) => params,
}));
const setup = (propOverrides?: object) => {
diff --git a/public/app/features/alerting/AlertRuleItem.tsx b/public/app/features/alerting/AlertRuleItem.tsx
index ad9e7337db78..e630b921237e 100644
--- a/public/app/features/alerting/AlertRuleItem.tsx
+++ b/public/app/features/alerting/AlertRuleItem.tsx
@@ -1,4 +1,5 @@
import React, { PureComponent } from 'react';
+// @ts-ignore
import Highlighter from 'react-highlight-words';
import classNames from 'classnames';
import { AlertRule } from '../../types';
diff --git a/public/app/features/alerting/AlertTabCtrl.ts b/public/app/features/alerting/AlertTabCtrl.ts
index 7820d938433e..0b9e5d639b75 100644
--- a/public/app/features/alerting/AlertTabCtrl.ts
+++ b/public/app/features/alerting/AlertTabCtrl.ts
@@ -5,9 +5,14 @@ import { QueryPart } from 'app/core/components/query_part/query_part';
import alertDef from './state/alertDef';
import config from 'app/core/config';
import appEvents from 'app/core/app_events';
+import { BackendSrv } from 'app/core/services/backend_srv';
+import { DashboardSrv } from '../dashboard/services/DashboardSrv';
+import DatasourceSrv from '../plugins/datasource_srv';
+import { DataQuery } from '@grafana/ui/src/types/datasource';
+import { PanelModel } from 'app/features/dashboard/state';
export class AlertTabCtrl {
- panel: any;
+ panel: PanelModel;
panelCtrl: any;
subTabIndex: number;
conditionTypes: any;
@@ -17,21 +22,21 @@ export class AlertTabCtrl {
evalOperators: any;
noDataModes: any;
executionErrorModes: any;
- addNotificationSegment;
- notifications;
- alertNotifications;
+ addNotificationSegment: any;
+ notifications: any;
+ alertNotifications: any;
error: string;
appSubUrl: string;
alertHistory: any;
/** @ngInject */
constructor(
- private $scope,
- private backendSrv,
- private dashboardSrv,
- private uiSegmentSrv,
- private $q,
- private datasourceSrv
+ private $scope: any,
+ private backendSrv: BackendSrv,
+ private dashboardSrv: DashboardSrv,
+ private uiSegmentSrv: any,
+ private $q: any,
+ private datasourceSrv: DatasourceSrv
) {
this.panelCtrl = $scope.ctrl;
this.panel = this.panelCtrl.panel;
@@ -65,7 +70,7 @@ export class AlertTabCtrl {
this.alertNotifications = [];
this.alertHistory = [];
- return this.backendSrv.get('/api/alert-notifications').then(res => {
+ return this.backendSrv.get('/api/alert-notifications').then((res: any) => {
this.notifications = res;
this.initModel();
@@ -76,7 +81,7 @@ export class AlertTabCtrl {
getAlertHistory() {
this.backendSrv
.get(`/api/annotations?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}&limit=50&type=alert`)
- .then(res => {
+ .then((res: any) => {
this.alertHistory = _.map(res, ah => {
ah.time = this.dashboardSrv.getCurrent().formatDate(ah.time, 'MMM D, YYYY HH:mm:ss');
ah.stateModel = alertDef.getStateDisplayModel(ah.newState);
@@ -86,7 +91,7 @@ export class AlertTabCtrl {
});
}
- getNotificationIcon(type): string {
+ getNotificationIcon(type: string): string {
switch (type) {
case 'email':
return 'fa fa-envelope';
@@ -114,20 +119,12 @@ export class AlertTabCtrl {
getNotifications() {
return this.$q.when(
- this.notifications.map(item => {
+ this.notifications.map((item: any) => {
return this.uiSegmentSrv.newSegment(item.name);
})
);
}
- changeTabIndex(newTabIndex) {
- this.subTabIndex = newTabIndex;
-
- if (this.subTabIndex === 2) {
- this.getAlertHistory();
- }
- }
-
notificationAdded() {
const model: any = _.find(this.notifications, {
name: this.addNotificationSegment.value,
@@ -154,7 +151,7 @@ export class AlertTabCtrl {
this.addNotificationSegment.fake = true;
}
- removeNotification(an) {
+ removeNotification(an: any) {
// remove notifiers refeered to by id and uid to support notifiers added
// before and after we added support for uid
_.remove(this.alert.notifications, (n: any) => n.uid === an.uid || n.id === an.id);
@@ -220,7 +217,7 @@ export class AlertTabCtrl {
this.panelCtrl.render();
}
- graphThresholdChanged(evt) {
+ graphThresholdChanged(evt: any) {
for (const condition of this.alert.conditions) {
if (condition.type === 'query') {
condition.evaluator.params[evt.handleIndex] = evt.threshold.value;
@@ -234,8 +231,8 @@ export class AlertTabCtrl {
return {
type: 'query',
query: { params: ['A', '5m', 'now'] },
- reducer: { type: 'avg', params: [] },
- evaluator: { type: 'gt', params: [null] },
+ reducer: { type: 'avg', params: [] as any[] },
+ evaluator: { type: 'gt', params: [null] as any[] },
operator: { type: 'and' },
};
}
@@ -246,7 +243,7 @@ export class AlertTabCtrl {
}
let firstTarget;
- let foundTarget = null;
+ let foundTarget: DataQuery = null;
for (const condition of this.alert.conditions) {
if (condition.type !== 'query') {
@@ -285,7 +282,7 @@ export class AlertTabCtrl {
}
}
- buildConditionModel(source) {
+ buildConditionModel(source: any) {
const cm: any = { source: source, type: source.type };
cm.queryPart = new QueryPart(source.query, alertDef.alertQueryDef);
@@ -296,7 +293,7 @@ export class AlertTabCtrl {
return cm;
}
- handleQueryPartEvent(conditionModel, evt) {
+ handleQueryPartEvent(conditionModel: any, evt: any) {
switch (evt.name) {
case 'action-remove-part': {
break;
@@ -317,7 +314,7 @@ export class AlertTabCtrl {
}
}
- handleReducerPartEvent(conditionModel, evt) {
+ handleReducerPartEvent(conditionModel: any, evt: any) {
switch (evt.name) {
case 'action': {
conditionModel.source.reducer.type = evt.action.value;
@@ -336,7 +333,7 @@ export class AlertTabCtrl {
}
}
- addCondition(type) {
+ addCondition(type: string) {
const condition = this.buildDefaultCondition();
// add to persited model
this.alert.conditions.push(condition);
@@ -344,7 +341,7 @@ export class AlertTabCtrl {
this.conditionModels.push(this.buildConditionModel(condition));
}
- removeCondition(index) {
+ removeCondition(index: number) {
this.alert.conditions.splice(index, 1);
this.conditionModels.splice(index, 1);
}
@@ -378,7 +375,7 @@ export class AlertTabCtrl {
this.panelCtrl.render();
}
- evaluatorTypeChanged(evaluator) {
+ evaluatorTypeChanged(evaluator: any) {
// ensure params array is correct length
switch (evaluator.type) {
case 'lt':
@@ -411,7 +408,7 @@ export class AlertTabCtrl {
dashboardId: this.panelCtrl.dashboard.id,
panelId: this.panel.id,
})
- .then(res => {
+ .then(() => {
this.alertHistory = [];
this.panelCtrl.refresh();
});
diff --git a/public/app/features/alerting/NotificationsEditCtrl.ts b/public/app/features/alerting/NotificationsEditCtrl.ts
index 2607121bb0e7..eb6d48405cd4 100644
--- a/public/app/features/alerting/NotificationsEditCtrl.ts
+++ b/public/app/features/alerting/NotificationsEditCtrl.ts
@@ -1,5 +1,6 @@
import _ from 'lodash';
-import { appEvents, coreModule } from 'app/core/core';
+import { appEvents, coreModule, NavModelSrv } from 'app/core/core';
+import { BackendSrv } from 'app/core/services/backend_srv';
export class AlertNotificationEditCtrl {
theForm: any;
@@ -24,8 +25,14 @@ export class AlertNotificationEditCtrl {
getFrequencySuggestion: any;
/** @ngInject */
- constructor(private $routeParams, private backendSrv, private $location, private $templateCache, navModelSrv) {
- this.navModel = navModelSrv.getNav('alerting', 'channels', 0);
+ constructor(
+ private $routeParams: any,
+ private backendSrv: BackendSrv,
+ private $location: any,
+ private $templateCache: any,
+ navModelSrv: NavModelSrv
+ ) {
+ this.navModel = navModelSrv.getNav('alerting', 'channels');
this.isNew = !this.$routeParams.id;
this.getFrequencySuggestion = () => {
@@ -34,7 +41,7 @@ export class AlertNotificationEditCtrl {
this.backendSrv
.get(`/api/alert-notifiers`)
- .then(notifiers => {
+ .then((notifiers: any) => {
this.notifiers = notifiers;
// add option templates
@@ -48,14 +55,14 @@ export class AlertNotificationEditCtrl {
return _.defaults(this.model, this.defaults);
}
- return this.backendSrv.get(`/api/alert-notifications/${this.$routeParams.id}`).then(result => {
+ return this.backendSrv.get(`/api/alert-notifications/${this.$routeParams.id}`).then((result: any) => {
this.navModel.breadcrumbs.push({ text: result.name });
this.navModel.node = { text: result.name };
result.settings = _.defaults(result.settings, this.defaults.settings);
return result;
});
})
- .then(model => {
+ .then((model: any) => {
this.model = model;
this.notifierTemplateId = this.getNotifierTemplateId(this.model.type);
});
@@ -69,11 +76,11 @@ export class AlertNotificationEditCtrl {
if (this.model.id) {
this.backendSrv
.put(`/api/alert-notifications/${this.model.id}`, this.model)
- .then(res => {
+ .then((res: any) => {
this.model = res;
appEvents.emit('alert-success', ['Notification updated', '']);
})
- .catch(err => {
+ .catch((err: any) => {
if (err.data && err.data.error) {
appEvents.emit('alert-error', [err.data.error]);
}
@@ -81,11 +88,11 @@ export class AlertNotificationEditCtrl {
} else {
this.backendSrv
.post(`/api/alert-notifications`, this.model)
- .then(res => {
+ .then((res: any) => {
appEvents.emit('alert-success', ['Notification created', '']);
this.$location.path('alerting/notifications');
})
- .catch(err => {
+ .catch((err: any) => {
if (err.data && err.data.error) {
appEvents.emit('alert-error', [err.data.error]);
}
@@ -93,7 +100,7 @@ export class AlertNotificationEditCtrl {
}
}
- getNotifierTemplateId(type) {
+ getNotifierTemplateId(type: string) {
return `notifier-options-${type}`;
}
@@ -114,7 +121,7 @@ export class AlertNotificationEditCtrl {
settings: this.model.settings,
};
- this.backendSrv.post(`/api/alert-notifications/test`, payload).then(res => {
+ this.backendSrv.post(`/api/alert-notifications/test`, payload).then((res: any) => {
appEvents.emit('alert-success', ['Test notification sent', '']);
});
}
diff --git a/public/app/features/alerting/NotificationsListCtrl.ts b/public/app/features/alerting/NotificationsListCtrl.ts
index 1113ebd7ceb3..4db2978b08dd 100644
--- a/public/app/features/alerting/NotificationsListCtrl.ts
+++ b/public/app/features/alerting/NotificationsListCtrl.ts
@@ -1,24 +1,25 @@
-import { coreModule } from 'app/core/core';
+import { coreModule, NavModelSrv } from 'app/core/core';
+import { BackendSrv } from 'app/core/services/backend_srv';
export class AlertNotificationsListCtrl {
notifications: any;
navModel: any;
/** @ngInject */
- constructor(private backendSrv, navModelSrv) {
+ constructor(private backendSrv: BackendSrv, navModelSrv: NavModelSrv) {
this.loadNotifications();
- this.navModel = navModelSrv.getNav('alerting', 'channels', 0);
+ this.navModel = navModelSrv.getNav('alerting', 'channels');
}
loadNotifications() {
- this.backendSrv.get(`/api/alert-notifications`).then(result => {
+ this.backendSrv.get(`/api/alert-notifications`).then((result: any) => {
this.notifications = result;
});
}
- deleteNotification(id) {
+ deleteNotification(id: number) {
this.backendSrv.delete(`/api/alert-notifications/${id}`).then(() => {
- this.notifications = this.notifications.filter(notification => {
+ this.notifications = this.notifications.filter((notification: any) => {
return notification.id !== id;
});
});
diff --git a/public/app/features/alerting/StateHistory.tsx b/public/app/features/alerting/StateHistory.tsx
index 3d092e7435a2..c0c804c8bd1e 100644
--- a/public/app/features/alerting/StateHistory.tsx
+++ b/public/app/features/alerting/StateHistory.tsx
@@ -15,7 +15,7 @@ interface State {
}
class StateHistory extends PureComponent {
- state = {
+ state: State = {
stateHistoryItems: [],
};
@@ -24,8 +24,8 @@ class StateHistory extends PureComponent {
getBackendSrv()
.get(`/api/annotations?dashboardId=${dashboard.id}&panelId=${panelId}&limit=50&type=alert`)
- .then(res => {
- const items = res.map(item => {
+ .then((res: any[]) => {
+ const items = res.map((item: any) => {
return {
stateModel: alertDef.getStateDisplayModel(item.newState),
time: dashboard.formatDate(item.time, 'MMM D, YYYY HH:mm:ss'),
diff --git a/public/app/features/alerting/state/ThresholdMapper.ts b/public/app/features/alerting/state/ThresholdMapper.ts
index 995add42ef01..0ab5777c5e8d 100644
--- a/public/app/features/alerting/state/ThresholdMapper.ts
+++ b/public/app/features/alerting/state/ThresholdMapper.ts
@@ -1,5 +1,7 @@
+import { PanelModel } from 'app/features/dashboard/state';
+
export class ThresholdMapper {
- static alertToGraphThresholds(panel) {
+ static alertToGraphThresholds(panel: PanelModel) {
for (let i = 0; i < panel.alert.conditions.length; i++) {
const condition = panel.alert.conditions[i];
if (condition.type !== 'query') {
@@ -7,7 +9,7 @@ export class ThresholdMapper {
}
const evaluator = condition.evaluator;
- const thresholds = (panel.thresholds = []);
+ const thresholds: any[] = (panel.thresholds = []);
switch (evaluator.type) {
case 'gt': {
diff --git a/public/app/features/alerting/state/alertDef.ts b/public/app/features/alerting/state/alertDef.ts
index 8e50697d33c5..882e83b21be0 100644
--- a/public/app/features/alerting/state/alertDef.ts
+++ b/public/app/features/alerting/state/alertDef.ts
@@ -57,12 +57,12 @@ const noDataModes = [
const executionErrorModes = [{ text: 'Alerting', value: 'alerting' }, { text: 'Keep Last State', value: 'keep_state' }];
-function createReducerPart(model) {
+function createReducerPart(model: any) {
const def = new QueryPartDef({ type: model.type, defaultParams: [] });
return new QueryPart(model, def);
}
-function getStateDisplayModel(state) {
+function getStateDisplayModel(state: string) {
switch (state) {
case 'ok': {
return {
@@ -111,7 +111,7 @@ function getStateDisplayModel(state) {
throw { message: 'Unknown alert state' };
}
-function joinEvalMatches(matches, separator: string) {
+function joinEvalMatches(matches: any, separator: string) {
return _.reduce(
matches,
(res, ev) => {
@@ -130,7 +130,7 @@ function joinEvalMatches(matches, separator: string) {
).join(separator);
}
-function getAlertAnnotationInfo(ah) {
+function getAlertAnnotationInfo(ah: any) {
// backward compatibility, can be removed in grafana 5.x
// old way stored evalMatches in data property directly,
// new way stores it in evalMatches property on new data object
diff --git a/public/app/features/alerting/state/reducers.test.ts b/public/app/features/alerting/state/reducers.test.ts
index a176acc66d2a..d66ec1884f33 100644
--- a/public/app/features/alerting/state/reducers.test.ts
+++ b/public/app/features/alerting/state/reducers.test.ts
@@ -85,7 +85,7 @@ describe('Alert rules', () => {
};
const result = alertRulesReducer(initialState, action);
-
- expect(result.items).toEqual(payload);
+ expect(result.items.length).toEqual(payload.length);
+ expect(result.items[0].stateClass).toEqual('alert-state-critical');
});
});
diff --git a/public/app/features/alerting/state/reducers.ts b/public/app/features/alerting/state/reducers.ts
index 240076009386..966819231029 100644
--- a/public/app/features/alerting/state/reducers.ts
+++ b/public/app/features/alerting/state/reducers.ts
@@ -5,14 +5,18 @@ import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
export const initialState: AlertRulesState = { items: [], searchQuery: '', isLoading: false };
-function convertToAlertRule(rule, state): AlertRule {
+function convertToAlertRule(dto: AlertRuleDTO, state: string): AlertRule {
const stateModel = alertDef.getStateDisplayModel(state);
- rule.stateText = stateModel.text;
- rule.stateIcon = stateModel.iconClass;
- rule.stateClass = stateModel.stateClass;
- rule.stateAge = dateTime(rule.newStateDate)
- .fromNow()
- .replace(' ago', '');
+
+ const rule: AlertRule = {
+ ...dto,
+ stateText: stateModel.text,
+ stateIcon: stateModel.iconClass,
+ stateClass: stateModel.stateClass,
+ stateAge: dateTime(dto.newStateDate)
+ .fromNow()
+ .replace(' ago', ''),
+ };
if (rule.state !== 'paused') {
if (rule.executionError) {
diff --git a/public/app/features/alerting/state/selectors.test.ts b/public/app/features/alerting/state/selectors.test.ts
index e853b146c033..8ee3f01a167d 100644
--- a/public/app/features/alerting/state/selectors.test.ts
+++ b/public/app/features/alerting/state/selectors.test.ts
@@ -3,7 +3,7 @@ import { getSearchQuery, getAlertRuleItems } from './selectors';
describe('Get search query', () => {
it('should get search query', () => {
const state = { searchQuery: 'dashboard' };
- const result = getSearchQuery(state);
+ const result = getSearchQuery(state as any);
expect(result).toEqual(state.searchQuery);
});
@@ -29,7 +29,7 @@ describe('Get alert rule items', () => {
searchQuery: '',
};
- const result = getAlertRuleItems(state);
+ const result = getAlertRuleItems(state as any);
expect(result.length).toEqual(1);
});
@@ -88,7 +88,7 @@ describe('Get alert rule items', () => {
searchQuery: 'dashboard',
};
- const result = getAlertRuleItems(state);
+ const result = getAlertRuleItems(state as any);
expect(result.length).toEqual(3);
});
});
diff --git a/public/app/features/alerting/state/selectors.ts b/public/app/features/alerting/state/selectors.ts
index 7c72520d7739..0b03076b5733 100644
--- a/public/app/features/alerting/state/selectors.ts
+++ b/public/app/features/alerting/state/selectors.ts
@@ -1,6 +1,8 @@
-export const getSearchQuery = state => state.searchQuery;
+import { AlertRulesState } from 'app/types';
-export const getAlertRuleItems = state => {
+export const getSearchQuery = (state: AlertRulesState) => state.searchQuery;
+
+export const getAlertRuleItems = (state: AlertRulesState) => {
const regex = new RegExp(state.searchQuery, 'i');
return state.items.filter(item => {
diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx
index 38291d787c36..bbebbbf2f65c 100644
--- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx
+++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx
@@ -234,7 +234,7 @@ export class PanelChrome extends PureComponent {
// image rendering (phantomjs/headless chrome) to know when to capture image
const loading = data.state;
if (loading === LoadingState.Done) {
- profiler.renderingCompleted(panel.id);
+ profiler.renderingCompleted();
}
// do not render component until we have first data
diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx
index 643e1d0d0f90..895ae0eeec87 100644
--- a/public/app/features/explore/Explore.tsx
+++ b/public/app/features/explore/Explore.tsx
@@ -365,4 +365,4 @@ export default hot(module)(
mapStateToProps,
mapDispatchToProps
)(Explore)
-);
+) as React.ComponentType<{ exploreId: ExploreId }>;
diff --git a/public/app/features/explore/QueryRow.tsx b/public/app/features/explore/QueryRow.tsx
index 969d39db7b4b..06034ecdbfff 100644
--- a/public/app/features/explore/QueryRow.tsx
+++ b/public/app/features/explore/QueryRow.tsx
@@ -28,19 +28,22 @@ import { Emitter } from 'app/core/utils/emitter';
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
import QueryStatus from './QueryStatus';
-interface QueryRowProps {
+interface PropsFromParent {
+ exploreId: ExploreId;
+ index: number;
+ exploreEvents: Emitter;
+}
+
+interface QueryRowProps extends PropsFromParent {
addQueryRow: typeof addQueryRow;
changeQuery: typeof changeQuery;
className?: string;
- exploreId: ExploreId;
datasourceInstance: ExploreDataSourceApi;
datasourceStatus: DataSourceStatus;
highlightLogsExpressionAction: typeof highlightLogsExpressionAction;
history: HistoryItem[];
- index: number;
query: DataQuery;
modifyQueries: typeof modifyQueries;
- exploreEvents: Emitter;
range: TimeRange;
removeQueryRowAction: typeof removeQueryRowAction;
runQueries: typeof runQueries;
@@ -219,9 +222,7 @@ const mapDispatchToProps = {
runQueries,
};
-export default hot(module)(
- connect(
- mapStateToProps,
- mapDispatchToProps
- )(QueryRow)
-);
+export default hot(module)(connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(QueryRow) as React.ComponentType);
diff --git a/public/app/features/explore/Wrapper.tsx b/public/app/features/explore/Wrapper.tsx
index e8b894bc53d8..b73917e09ca3 100644
--- a/public/app/features/explore/Wrapper.tsx
+++ b/public/app/features/explore/Wrapper.tsx
@@ -36,6 +36,7 @@ export class Wrapper extends Component {
)}
+ x
);
diff --git a/public/app/features/org/state/actions.ts b/public/app/features/org/state/actions.ts
index 52793698a45d..fc8742d12226 100644
--- a/public/app/features/org/state/actions.ts
+++ b/public/app/features/org/state/actions.ts
@@ -1,9 +1,6 @@
-import { ThunkAction } from 'redux-thunk';
-import { Organization, StoreState } from 'app/types';
+import { Organization, ThunkResult } from 'app/types';
import { getBackendSrv } from 'app/core/services/backend_srv';
-type ThunkResult = ThunkAction;
-
export enum ActionTypes {
LoadOrganization = 'LOAD_ORGANIZATION',
SetOrganizationName = 'SET_ORGANIZATION_NAME',
@@ -31,7 +28,7 @@ export const setOrganizationName = (orgName: string) => ({
export type Action = LoadOrganizationAction | SetOrganizationNameAction;
-export function loadOrganization(): ThunkResult {
+export function loadOrganization(): ThunkResult {
return async dispatch => {
const organizationResponse = await getBackendSrv().get('/api/org');
dispatch(organizationLoaded(organizationResponse));
@@ -40,7 +37,7 @@ export function loadOrganization(): ThunkResult {
};
}
-export function updateOrganization() {
+export function updateOrganization(): ThunkResult {
return async (dispatch, getStore) => {
const organization = getStore().organization.organization;
diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts
index e81312aa0de2..cf110cdc705b 100644
--- a/public/app/features/panel/panel_ctrl.ts
+++ b/public/app/features/panel/panel_ctrl.ts
@@ -60,7 +60,7 @@ export class PanelCtrl {
}
renderingCompleted() {
- profiler.renderingCompleted(this.panel.id);
+ profiler.renderingCompleted();
}
refresh() {
diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts
index c80c30fc6827..1bd2ffb5d060 100644
--- a/public/app/plugins/datasource/prometheus/datasource.ts
+++ b/public/app/plugins/datasource/prometheus/datasource.ts
@@ -137,7 +137,7 @@ export class PrometheusDatasource extends DataSourceApi
return escapedValues.join('|');
}
- targetContainsTemplate(target) {
+ targetContainsTemplate(target: PromQuery) {
return this.templateSrv.variableExists(target.expr);
}
diff --git a/public/app/types/alerting.ts b/public/app/types/alerting.ts
index c987c2d38ede..26516a052ab4 100644
--- a/public/app/types/alerting.ts
+++ b/public/app/types/alerting.ts
@@ -8,7 +8,7 @@ export interface AlertRuleDTO {
state: string;
newStateDate: string;
evalDate: string;
- evalData?: object;
+ evalData?: { noData?: boolean; evalMatches?: any };
executionError: string;
url: string;
}
@@ -26,7 +26,7 @@ export interface AlertRule {
url: string;
info?: string;
executionError?: string;
- evalData?: { noData: boolean };
+ evalData?: { noData?: boolean; evalMatches?: any };
}
export interface AlertRulesState {
diff --git a/scripts/ci-frontend-metrics.sh b/scripts/ci-frontend-metrics.sh
index e1bcc3a0031a..c39cf7cf7dac 100755
--- a/scripts/ci-frontend-metrics.sh
+++ b/scripts/ci-frontend-metrics.sh
@@ -2,7 +2,7 @@
echo -e "Collecting code stats (typescript errors & more)"
-ERROR_COUNT_LIMIT=5386
+ERROR_COUNT_LIMIT=5150
DIRECTIVES_LIMIT=172
CONTROLLERS_LIMIT=139
diff --git a/yarn.lock b/yarn.lock
index 624f08d556b8..d78a984dd926 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2219,6 +2219,14 @@
"@types/minimatch" "*"
"@types/node" "*"
+"@types/hoist-non-react-statics@^3.3.0":
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
+ integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
+ dependencies:
+ "@types/react" "*"
+ hoist-non-react-statics "^3.3.0"
+
"@types/inquirer@0.0.43":
version "0.0.43"
resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-0.0.43.tgz#1eb0bbb4648e6cc568bd396c1e989f620ad01273"
@@ -2384,6 +2392,15 @@
dependencies:
"@types/react" "*"
+"@types/react-redux@^7.0.8":
+ version "7.0.8"
+ resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.0.8.tgz#c928863058e334d41031c6bedd0f113bc514e234"
+ integrity sha512-vIBC15E84ehN6RzdGwRVa41whp9e4CkfPm+WfD0r6y6vqBf4tQPKZeKEBfLLM8k79uSwQC7rh3rH/MFaN1IESQ==
+ dependencies:
+ "@types/hoist-non-react-statics" "^3.3.0"
+ "@types/react" "*"
+ redux "^4.0.0"
+
"@types/react-select@2.0.15":
version "2.0.15"
resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-2.0.15.tgz#51d607667f59a12e980abcc5bbf9636307293e44"
@@ -14671,7 +14688,7 @@ redux-thunk@2.3.0:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
-redux@4.0.1:
+redux@4.0.1, redux@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"
integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==
From bf5b60f74a72433f98acecc526ec12a0e1df718e Mon Sep 17 00:00:00 2001
From: David
Date: Mon, 13 May 2019 09:55:20 +0200
Subject: [PATCH 008/166] fix(explore): Prevent double querying for Prometheus
and Loki (#17004)
* fix(explore): Prevent double querying for Prometheus and Loki
- queries were triggered twice because two Enter handlers existed
- removed runner plugin from Loki and Prom query fields (runner plugin
is still being used in azure)
Part of #16995
* Set loki's mtrics capability to false
Loki is not a metrics store. Explore was using the `metrics` field in
the plugin definition to issue a second query run. But Loki shows only
one result viewer.
Fixes #16995
---
public/app/features/explore/QueryField.tsx | 6 ++++--
.../datasource/loki/components/LokiQueryFieldForm.tsx | 5 -----
public/app/plugins/datasource/loki/plugin.json | 2 +-
.../datasource/prometheus/components/PromQueryField.tsx | 2 --
4 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/public/app/features/explore/QueryField.tsx b/public/app/features/explore/QueryField.tsx
index e5d7ff436313..d2a5d04e0d44 100644
--- a/public/app/features/explore/QueryField.tsx
+++ b/public/app/features/explore/QueryField.tsx
@@ -329,11 +329,13 @@ export class QueryField extends React.PureComponent {
diff --git a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx
index 76e94904c1f3..210d2acb4ccc 100644
--- a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx
+++ b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx
@@ -12,7 +12,6 @@ import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explor
// dom also includes Element polyfills
import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
-import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
// Types
import { LokiQuery } from '../types';
@@ -77,7 +76,6 @@ export interface LokiQueryFieldFormProps extends ExploreQueryFieldProps {
plugins: any[];
- pluginsSearch: any[];
modifiedSearch: string;
modifiedQuery: string;
@@ -86,14 +84,11 @@ export class LokiQueryFieldForm extends React.PureComponent node.type === 'code_block',
getSyntax: (node: any) => 'promql',
}),
];
-
- this.pluginsSearch = [RunnerPlugin({ handler: props.onRunQuery })];
}
loadOptions = (selectedOptions: CascaderOption[]) => {
diff --git a/public/app/plugins/datasource/loki/plugin.json b/public/app/plugins/datasource/loki/plugin.json
index 66e0f1027e9e..cd14a7fe48ad 100644
--- a/public/app/plugins/datasource/loki/plugin.json
+++ b/public/app/plugins/datasource/loki/plugin.json
@@ -4,7 +4,7 @@
"id": "loki",
"category": "logging",
- "metrics": true,
+ "metrics": false,
"alerting": false,
"annotations": false,
"logs": true,
diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
index a63e2ef52b97..14d03df6d388 100644
--- a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
+++ b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
@@ -12,7 +12,6 @@ import { TypeaheadOutput, HistoryItem } from 'app/types/explore';
// dom also includes Element polyfills
import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
-import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
import { PromQuery } from '../types';
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
@@ -126,7 +125,6 @@ class PromQueryField extends React.PureComponent node.type === 'code_block',
getSyntax: (node: any) => 'promql',
From 927e1cbd27888b5e4e913862bf820909dcd3fb6a Mon Sep 17 00:00:00 2001
From: David
Date: Mon, 13 May 2019 09:58:26 +0200
Subject: [PATCH 009/166] (feat/explore): Support for new LogQL filtering
syntax (#16674)
* (feat/explore): Support for new LogQL filtering syntax
Loki is adding syntax to support chained filtering.
This PR adapts Grafana to support this.
- Send only `query` parameter in loki request
- Automatically wrap search text in simple syntax, e.g., `{} foo` is
sent as `{} |~ "foo"`.
* Adapted to regexp parameter staying on in Loki
* Dont wrap single regexp in new filter syntax
* Fix datasource test
* Fallback regexp parameter for legacy queries
* Fix search highlighting
* Make highlighting work for filter chains
* Fix datasource test
---
packages/grafana-ui/src/types/data.ts | 2 +-
packages/grafana-ui/src/types/datasource.ts | 2 +-
public/app/core/logs_model.ts | 4 +-
public/app/core/utils/text.ts | 2 +-
public/app/features/explore/LogRow.tsx | 2 +-
public/app/features/explore/QueryRow.tsx | 2 +-
.../datasource/loki/datasource.test.ts | 4 +-
.../app/plugins/datasource/loki/datasource.ts | 17 +++--
.../datasource/loki/query_utils.test.ts | 53 +++++++++++----
.../plugins/datasource/loki/query_utils.ts | 64 ++++++++++++++++---
public/app/plugins/datasource/loki/types.ts | 5 ++
11 files changed, 122 insertions(+), 35 deletions(-)
diff --git a/packages/grafana-ui/src/types/data.ts b/packages/grafana-ui/src/types/data.ts
index 678efc40f007..e629e76043a7 100644
--- a/packages/grafana-ui/src/types/data.ts
+++ b/packages/grafana-ui/src/types/data.ts
@@ -21,7 +21,7 @@ export interface QueryResultMeta {
requestId?: string;
// Used in Explore for highlighting
- search?: string;
+ searchWords?: string[];
// Used in Explore to show limit applied to search result
limit?: number;
diff --git a/packages/grafana-ui/src/types/datasource.ts b/packages/grafana-ui/src/types/datasource.ts
index 3be0bae09dd8..2b40064d7d9d 100644
--- a/packages/grafana-ui/src/types/datasource.ts
+++ b/packages/grafana-ui/src/types/datasource.ts
@@ -194,7 +194,7 @@ export abstract class ExploreDataSourceApi<
TOptions extends DataSourceJsonData = DataSourceJsonData
> extends DataSourceApi {
modifyQuery?(query: TQuery, action: QueryFixAction): TQuery;
- getHighlighterExpression?(query: TQuery): string;
+ getHighlighterExpression?(query: TQuery): string[];
languageProvider?: any;
}
diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts
index eb292c033f8a..fd7e5c638a94 100644
--- a/public/app/core/logs_model.ts
+++ b/public/app/core/logs_model.ts
@@ -446,7 +446,7 @@ export function processLogSeriesRow(
const timeLocal = time.format('YYYY-MM-DD HH:mm:ss');
const logLevel = getLogLevel(message);
const hasAnsi = hasAnsiCodes(message);
- const search = series.meta && series.meta.search ? series.meta.search : '';
+ const searchWords = series.meta && series.meta.searchWords ? series.meta.searchWords : [];
return {
logLevel,
@@ -455,10 +455,10 @@ export function processLogSeriesRow(
timeLocal,
uniqueLabels,
hasAnsi,
+ searchWords,
entry: hasAnsi ? ansicolor.strip(message) : message,
raw: message,
labels: series.labels,
- searchWords: search ? [search] : [],
timestamp: ts,
};
}
diff --git a/public/app/core/utils/text.ts b/public/app/core/utils/text.ts
index faf801b45be8..6db844e736ef 100644
--- a/public/app/core/utils/text.ts
+++ b/public/app/core/utils/text.ts
@@ -6,7 +6,7 @@ import xss from 'xss';
* See https://github.com/bvaughn/react-highlight-words#props
*/
export function findHighlightChunksInText({ searchWords, textToHighlight }) {
- return findMatchesInText(textToHighlight, searchWords.join(' '));
+ return searchWords.reduce((acc, term) => [...acc, ...findMatchesInText(textToHighlight, term)], []);
}
const cleanNeedle = (needle: string): string => {
diff --git a/public/app/features/explore/LogRow.tsx b/public/app/features/explore/LogRow.tsx
index 19930a8a3393..8e3de04749b8 100644
--- a/public/app/features/explore/LogRow.tsx
+++ b/public/app/features/explore/LogRow.tsx
@@ -133,7 +133,7 @@ export class LogRow extends PureComponent {
const { entry, hasAnsi, raw } = row;
const previewHighlights = highlighterExpressions && !_.isEqual(highlighterExpressions, row.searchWords);
const highlights = previewHighlights ? highlighterExpressions : row.searchWords;
- const needsHighlighter = highlights && highlights.length > 0 && highlights[0].length > 0;
+ const needsHighlighter = highlights && highlights.length > 0 && highlights[0] && highlights[0].length > 0;
const highlightClassName = classnames('logs-row__match-highlight', {
'logs-row__match-highlight--preview': previewHighlights,
});
diff --git a/public/app/features/explore/QueryRow.tsx b/public/app/features/explore/QueryRow.tsx
index 06034ecdbfff..fc4a5e025e94 100644
--- a/public/app/features/explore/QueryRow.tsx
+++ b/public/app/features/explore/QueryRow.tsx
@@ -98,7 +98,7 @@ export class QueryRow extends PureComponent {
const { datasourceInstance } = this.props;
if (datasourceInstance.getHighlighterExpression) {
const { exploreId } = this.props;
- const expressions = [datasourceInstance.getHighlighterExpression(value)];
+ const expressions = datasourceInstance.getHighlighterExpression(value);
this.props.highlightLogsExpressionAction({ exploreId, expressions });
}
}, 500);
diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts
index 8b970f05f63e..8200cf0f4a14 100644
--- a/public/app/plugins/datasource/loki/datasource.test.ts
+++ b/public/app/plugins/datasource/loki/datasource.test.ts
@@ -61,7 +61,7 @@ describe('LokiDatasource', () => {
backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp));
const options = getQueryOptions({
- targets: [{ expr: 'foo', refId: 'B' }],
+ targets: [{ expr: '{} foo', refId: 'B' }],
});
const res = await ds.query(options);
@@ -69,7 +69,7 @@ describe('LokiDatasource', () => {
const seriesData = res.data[0] as SeriesData;
expect(seriesData.rows[0][1]).toBe('hello');
expect(seriesData.meta.limit).toBe(20);
- expect(seriesData.meta.search).toBe('(?i)foo');
+ expect(seriesData.meta.searchWords).toEqual(['(?i)foo']);
done();
});
});
diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts
index 3e7e5f3b3968..04f846584911 100644
--- a/public/app/plugins/datasource/loki/datasource.ts
+++ b/public/app/plugins/datasource/loki/datasource.ts
@@ -6,7 +6,7 @@ import * as dateMath from '@grafana/ui/src/utils/datemath';
import { addLabelToSelector } from 'app/plugins/datasource/prometheus/add_label_to_query';
import LanguageProvider from './language_provider';
import { logStreamToSeriesData } from './result_transformer';
-import { formatQuery, parseQuery } from './query_utils';
+import { formatQuery, parseQuery, getHighlighterExpressionsFromQuery } from './query_utils';
// Types
import {
@@ -69,12 +69,14 @@ export class LokiDatasource extends DataSourceApi {
prepareQueryTarget(target: LokiQuery, options: DataQueryRequest) {
const interpolated = this.templateSrv.replace(target.expr);
+ const { query, regexp } = parseQuery(interpolated);
const start = this.getTime(options.range.from, false);
const end = this.getTime(options.range.to, true);
const refId = target.refId;
return {
...DEFAULT_QUERY_PARAMS,
- ...parseQuery(interpolated),
+ query,
+ regexp,
start,
end,
limit: this.maxLines,
@@ -126,14 +128,15 @@ export class LokiDatasource extends DataSourceApi {
for (let i = 0; i < results.length; i++) {
const result = results[i];
-
if (result.data) {
const refId = queryTargets[i].refId;
for (const stream of result.data.streams || []) {
const seriesData = logStreamToSeriesData(stream);
seriesData.refId = refId;
seriesData.meta = {
- search: queryTargets[i].regexp,
+ searchWords: getHighlighterExpressionsFromQuery(
+ formatQuery(queryTargets[i].query, queryTargets[i].regexp)
+ ),
limit: this.maxLines,
};
series.push(seriesData);
@@ -160,7 +163,7 @@ export class LokiDatasource extends DataSourceApi {
modifyQuery(query: LokiQuery, action: any): LokiQuery {
const parsed = parseQuery(query.expr || '');
- let selector = parsed.query;
+ let { query: selector } = parsed;
switch (action.type) {
case 'ADD_FILTER': {
selector = addLabelToSelector(selector, action.key, action.value);
@@ -173,8 +176,8 @@ export class LokiDatasource extends DataSourceApi {
return { ...query, expr: expression };
}
- getHighlighterExpression(query: LokiQuery): string {
- return parseQuery(query.expr).regexp;
+ getHighlighterExpression(query: LokiQuery): string[] {
+ return getHighlighterExpressionsFromQuery(query.expr);
}
getTime(date, roundUp) {
diff --git a/public/app/plugins/datasource/loki/query_utils.test.ts b/public/app/plugins/datasource/loki/query_utils.test.ts
index 3685a3dd358e..964f8678a795 100644
--- a/public/app/plugins/datasource/loki/query_utils.test.ts
+++ b/public/app/plugins/datasource/loki/query_utils.test.ts
@@ -1,56 +1,87 @@
-import { parseQuery } from './query_utils';
+import { parseQuery, getHighlighterExpressionsFromQuery } from './query_utils';
+import { LokiExpression } from './types';
describe('parseQuery', () => {
it('returns empty for empty string', () => {
expect(parseQuery('')).toEqual({
query: '',
regexp: '',
- });
+ } as LokiExpression);
});
it('returns regexp for strings without query', () => {
expect(parseQuery('test')).toEqual({
- query: '',
- regexp: '(?i)test',
- });
+ query: 'test',
+ regexp: '',
+ } as LokiExpression);
});
it('returns query for strings without regexp', () => {
expect(parseQuery('{foo="bar"}')).toEqual({
query: '{foo="bar"}',
regexp: '',
- });
+ } as LokiExpression);
});
it('returns query for strings with query and search string', () => {
expect(parseQuery('x {foo="bar"}')).toEqual({
query: '{foo="bar"}',
regexp: '(?i)x',
- });
+ } as LokiExpression);
});
it('returns query for strings with query and regexp', () => {
expect(parseQuery('{foo="bar"} x|y')).toEqual({
query: '{foo="bar"}',
regexp: '(?i)x|y',
- });
+ } as LokiExpression);
});
it('returns query for selector with two labels', () => {
expect(parseQuery('{foo="bar", baz="42"}')).toEqual({
query: '{foo="bar", baz="42"}',
regexp: '',
- });
+ } as LokiExpression);
});
it('returns query and regexp with quantifiers', () => {
expect(parseQuery('{foo="bar"} \\.java:[0-9]{1,5}')).toEqual({
query: '{foo="bar"}',
regexp: '(?i)\\.java:[0-9]{1,5}',
- });
+ } as LokiExpression);
expect(parseQuery('\\.java:[0-9]{1,5} {foo="bar"}')).toEqual({
query: '{foo="bar"}',
regexp: '(?i)\\.java:[0-9]{1,5}',
- });
+ } as LokiExpression);
+ });
+
+ it('returns query with filter operands as is', () => {
+ expect(parseQuery('{foo="bar"} |= "x|y"')).toEqual({
+ query: '{foo="bar"} |= "x|y"',
+ regexp: '',
+ } as LokiExpression);
+ expect(parseQuery('{foo="bar"} |~ "42"')).toEqual({
+ query: '{foo="bar"} |~ "42"',
+ regexp: '',
+ } as LokiExpression);
+ });
+});
+
+describe('getHighlighterExpressionsFromQuery', () => {
+ it('returns no expressions for empty query', () => {
+ expect(getHighlighterExpressionsFromQuery('')).toEqual([]);
+ });
+ it('returns a single expressions for legacy query', () => {
+ expect(getHighlighterExpressionsFromQuery('{} x')).toEqual(['(?i)x']);
+ expect(getHighlighterExpressionsFromQuery('{foo="bar"} x')).toEqual(['(?i)x']);
+ });
+ it('returns an expression for query with filter', () => {
+ expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x"')).toEqual(['x']);
+ });
+ it('returns expressions for query with filter chain', () => {
+ expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" |~ "y"')).toEqual(['x', 'y']);
+ });
+ it('returns drops expressions for query with negative filter chain', () => {
+ expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" != "y"')).toEqual(['x']);
});
});
diff --git a/public/app/plugins/datasource/loki/query_utils.ts b/public/app/plugins/datasource/loki/query_utils.ts
index 7d2c6234f050..54fcca8809a3 100644
--- a/public/app/plugins/datasource/loki/query_utils.ts
+++ b/public/app/plugins/datasource/loki/query_utils.ts
@@ -1,22 +1,70 @@
+import { LokiExpression } from './types';
+
const selectorRegexp = /(?:^|\s){[^{]*}/g;
const caseInsensitive = '(?i)'; // Golang mode modifier for Loki, doesn't work in JavaScript
-export function parseQuery(input: string) {
+export function parseQuery(input: string): LokiExpression {
input = input || '';
const match = input.match(selectorRegexp);
- let query = '';
- let regexp = input;
+ let query = input;
+ let regexp = '';
if (match) {
- query = match[0].trim();
regexp = input.replace(selectorRegexp, '').trim();
+ // Keep old-style regexp, otherwise take whole query
+ if (regexp && regexp.search(/\|=|\|~|!=|!~/) === -1) {
+ query = match[0].trim();
+ if (!regexp.startsWith(caseInsensitive)) {
+ regexp = `${caseInsensitive}${regexp}`;
+ }
+ } else {
+ regexp = '';
+ }
}
- if (regexp) {
- regexp = caseInsensitive + regexp;
- }
- return { query, regexp };
+ return { regexp, query };
}
export function formatQuery(selector: string, search: string): string {
return `${selector || ''} ${search || ''}`.trim();
}
+
+/**
+ * Returns search terms from a LogQL query.
+ * E.g., `{} |= foo |=bar != baz` returns `['foo', 'bar']`.
+ */
+export function getHighlighterExpressionsFromQuery(input: string): string[] {
+ const parsed = parseQuery(input);
+ // Legacy syntax
+ if (parsed.regexp) {
+ return [parsed.regexp];
+ }
+ let expression = input;
+ const results = [];
+ // Consume filter expression from left to right
+ while (expression) {
+ const filterStart = expression.search(/\|=|\|~|!=|!~/);
+ // Nothing more to search
+ if (filterStart === -1) {
+ break;
+ }
+ // Drop terms for negative filters
+ const skip = expression.substr(filterStart).search(/!=|!~/) === 0;
+ expression = expression.substr(filterStart + 2);
+ if (skip) {
+ continue;
+ }
+ // Check if there is more chained
+ const filterEnd = expression.search(/\|=|\|~|!=|!~/);
+ let filterTerm;
+ if (filterEnd === -1) {
+ filterTerm = expression.trim();
+ } else {
+ filterTerm = expression.substr(0, filterEnd);
+ expression = expression.substr(filterEnd);
+ }
+
+ // Unwrap the filter term by removing quotes
+ results.push(filterTerm.replace(/^\s*"/g, '').replace(/"\s*$/g, ''));
+ }
+ return results;
+}
diff --git a/public/app/plugins/datasource/loki/types.ts b/public/app/plugins/datasource/loki/types.ts
index c4cfa3fcdc43..4c973f8a79ed 100644
--- a/public/app/plugins/datasource/loki/types.ts
+++ b/public/app/plugins/datasource/loki/types.ts
@@ -22,3 +22,8 @@ export interface LokiLogsStreamEntry {
// Legacy, was renamed to ts
timestamp?: string;
}
+
+export interface LokiExpression {
+ regexp: string;
+ query: string;
+}
From db95414d02a4c2bc54f8759abc351c9af53a7b5c Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Mon, 13 May 2019 11:51:16 +0300
Subject: [PATCH 010/166] Feature: provide multildap server configuration
(#16914)
* Feature: provide multildap server configuration
* Add multildap server configuration for devenv
* Change some of the notes for openldap devenv configuration
* Change the maintainer label for *main* dockerfile and of the devenv one
Re-checked the multildap logic - everything seems to be working fine
---
Dockerfile | 2 +
.../admins-ldap-server/Dockerfile | 30 ++++++
.../admins-ldap-server/modules/memberof.ldif | 33 +++++++
.../prepopulate/1_units.ldif | 9 ++
.../prepopulate/2_users.ldif | 20 ++++
.../prepopulate/3_groups.ldif | 6 ++
.../multiple-openldap/docker-compose.yaml | 19 ++++
.../blocks/multiple-openldap/entrypoint.sh | 98 +++++++++++++++++++
.../multiple-openldap/ldap-server/Dockerfile | 30 ++++++
.../ldap-server/modules/memberof.ldif | 33 +++++++
.../ldap-server/prepopulate/1_units.ldif | 9 ++
.../ldap-server/prepopulate/2_users.ldif | 59 +++++++++++
.../ldap-server/prepopulate/3_groups.ldif | 23 +++++
.../blocks/multiple-openldap/ldap_dev.toml | 59 +++++++++++
.../docker/blocks/multiple-openldap/notes.md | 38 +++++++
.../blocks/multiple-openldap/prepopulate.sh | 14 +++
devenv/docker/blocks/openldap/Dockerfile | 2 +-
devenv/docker/blocks/openldap/notes.md | 5 +-
18 files changed, 484 insertions(+), 5 deletions(-)
create mode 100644 devenv/docker/blocks/multiple-openldap/admins-ldap-server/Dockerfile
create mode 100644 devenv/docker/blocks/multiple-openldap/admins-ldap-server/modules/memberof.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/1_units.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/2_users.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/3_groups.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/docker-compose.yaml
create mode 100755 devenv/docker/blocks/multiple-openldap/entrypoint.sh
create mode 100644 devenv/docker/blocks/multiple-openldap/ldap-server/Dockerfile
create mode 100644 devenv/docker/blocks/multiple-openldap/ldap-server/modules/memberof.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/1_units.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/2_users.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/3_groups.ldif
create mode 100644 devenv/docker/blocks/multiple-openldap/ldap_dev.toml
create mode 100644 devenv/docker/blocks/multiple-openldap/notes.md
create mode 100755 devenv/docker/blocks/multiple-openldap/prepopulate.sh
diff --git a/Dockerfile b/Dockerfile
index 537aaca840cb..fd305d8af3ee 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -35,6 +35,8 @@ RUN ./node_modules/.bin/grunt build
# Final container
FROM debian:stretch-slim
+LABEL maintainer="Grafana team "
+
ARG GF_UID="472"
ARG GF_GID="472"
diff --git a/devenv/docker/blocks/multiple-openldap/admins-ldap-server/Dockerfile b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/Dockerfile
new file mode 100644
index 000000000000..979d01c7dad4
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/Dockerfile
@@ -0,0 +1,30 @@
+# Fork of https://github.com/dinkel/docker-openldap
+
+FROM debian:jessie
+
+LABEL maintainer="Grafana team "
+
+ENV OPENLDAP_VERSION 2.4.40
+
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
+ slapd=${OPENLDAP_VERSION}* \
+ ldap-utils && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN mv /etc/ldap /etc/ldap.dist
+
+EXPOSE 389
+
+VOLUME ["/etc/ldap", "/var/lib/ldap"]
+
+COPY modules/ /etc/ldap.dist/modules
+COPY prepopulate/ /etc/ldap.dist/prepopulate
+
+COPY ../entrypoint.sh /entrypoint.sh
+COPY ../prepopulate.sh /prepopulate.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
+
+CMD ["slapd", "-d", "32768", "-u", "openldap", "-g", "openldap"]
diff --git a/devenv/docker/blocks/multiple-openldap/admins-ldap-server/modules/memberof.ldif b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/modules/memberof.ldif
new file mode 100644
index 000000000000..fd9cce957c33
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/modules/memberof.ldif
@@ -0,0 +1,33 @@
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: memberof.la
+
+dn: olcOverlay={0}memberof,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcMemberOf
+objectClass: olcOverlayConfig
+objectClass: top
+olcOverlay: memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: refint.la
+
+dn: olcOverlay={1}refint,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcOverlayConfig
+objectClass: olcRefintConfig
+objectClass: top
+olcOverlay: {1}refint
+olcRefintAttribute: memberof member manager owner
diff --git a/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/1_units.ldif b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/1_units.ldif
new file mode 100644
index 000000000000..22e063036889
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/1_units.ldif
@@ -0,0 +1,9 @@
+dn: ou=groups,dc=grafana,dc=org
+ou: Groups
+objectclass: top
+objectclass: organizationalUnit
+
+dn: ou=users,dc=grafana,dc=org
+ou: Users
+objectclass: top
+objectclass: organizationalUnit
diff --git a/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/2_users.ldif b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/2_users.ldif
new file mode 100644
index 000000000000..1ee592dc7a06
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/2_users.ldif
@@ -0,0 +1,20 @@
+# ldap-admin
+dn: cn=ldap-admin,ou=users,dc=grafana,dc=org
+mail: ldap-admin@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-admin
+cn: ldap-admin
+
+dn: cn=ldap-torkel,ou=users,dc=grafana,dc=org
+mail: ldap-torkel@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-torkel
+cn: ldap-torkel
diff --git a/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/3_groups.ldif b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/3_groups.ldif
new file mode 100644
index 000000000000..f7285f8a9c32
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/admins-ldap-server/prepopulate/3_groups.ldif
@@ -0,0 +1,6 @@
+dn: cn=admins,ou=groups,dc=grafana,dc=org
+cn: admins
+objectClass: groupOfNames
+objectClass: top
+member: cn=ldap-admin,ou=users,dc=grafana,dc=org
+member: cn=ldap-torkel,ou=users,dc=grafana,dc=org
diff --git a/devenv/docker/blocks/multiple-openldap/docker-compose.yaml b/devenv/docker/blocks/multiple-openldap/docker-compose.yaml
new file mode 100644
index 000000000000..74f5d29a90ff
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/docker-compose.yaml
@@ -0,0 +1,19 @@
+ admins-openldap:
+ build: docker/blocks/multiple-openldap/admins-ldap-server
+ environment:
+ SLAPD_PASSWORD: grafana
+ SLAPD_DOMAIN: grafana.org
+ SLAPD_ADDITIONAL_MODULES: memberof
+ ports:
+ - "389:389"
+
+ openldap:
+ build: docker/blocks/multiple-openldap/ldap-server
+ environment:
+ SLAPD_PASSWORD: grafana
+ SLAPD_DOMAIN: grafana.org
+ SLAPD_ADDITIONAL_MODULES: memberof
+ ports:
+ - "388:389"
+
+
diff --git a/devenv/docker/blocks/multiple-openldap/entrypoint.sh b/devenv/docker/blocks/multiple-openldap/entrypoint.sh
new file mode 100755
index 000000000000..d202ed14b31f
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/entrypoint.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+
+# When not limiting the open file descritors limit, the memory consumption of
+# slapd is absurdly high. See https://github.com/docker/docker/issues/8231
+ulimit -n 8192
+
+
+set -e
+
+chown -R openldap:openldap /var/lib/ldap/
+
+if [[ ! -d /etc/ldap/slapd.d ]]; then
+
+ if [[ -z "$SLAPD_PASSWORD" ]]; then
+ echo -n >&2 "Error: Container not configured and SLAPD_PASSWORD not set. "
+ echo >&2 "Did you forget to add -e SLAPD_PASSWORD=... ?"
+ exit 1
+ fi
+
+ if [[ -z "$SLAPD_DOMAIN" ]]; then
+ echo -n >&2 "Error: Container not configured and SLAPD_DOMAIN not set. "
+ echo >&2 "Did you forget to add -e SLAPD_DOMAIN=... ?"
+ exit 1
+ fi
+
+ SLAPD_ORGANIZATION="${SLAPD_ORGANIZATION:-${SLAPD_DOMAIN}}"
+
+ cp -a /etc/ldap.dist/* /etc/ldap
+
+ cat <<-EOF | debconf-set-selections
+ slapd slapd/no_configuration boolean false
+ slapd slapd/password1 password $SLAPD_PASSWORD
+ slapd slapd/password2 password $SLAPD_PASSWORD
+ slapd shared/organization string $SLAPD_ORGANIZATION
+ slapd slapd/domain string $SLAPD_DOMAIN
+ slapd slapd/backend select HDB
+ slapd slapd/allow_ldap_v2 boolean false
+ slapd slapd/purge_database boolean false
+ slapd slapd/move_old_database boolean true
+EOF
+
+ dpkg-reconfigure -f noninteractive slapd >/dev/null 2>&1
+
+ dc_string=""
+
+ IFS="."; declare -a dc_parts=($SLAPD_DOMAIN)
+
+ for dc_part in "${dc_parts[@]}"; do
+ dc_string="$dc_string,dc=$dc_part"
+ done
+
+ base_string="BASE ${dc_string:1}"
+
+ sed -i "s/^#BASE.*/${base_string}/g" /etc/ldap/ldap.conf
+
+ if [[ -n "$SLAPD_CONFIG_PASSWORD" ]]; then
+ password_hash=`slappasswd -s "${SLAPD_CONFIG_PASSWORD}"`
+
+ sed_safe_password_hash=${password_hash//\//\\\/}
+
+ slapcat -n0 -F /etc/ldap/slapd.d -l /tmp/config.ldif
+ sed -i "s/\(olcRootDN: cn=admin,cn=config\)/\1\nolcRootPW: ${sed_safe_password_hash}/g" /tmp/config.ldif
+ rm -rf /etc/ldap/slapd.d/*
+ slapadd -n0 -F /etc/ldap/slapd.d -l /tmp/config.ldif >/dev/null 2>&1
+ fi
+
+ if [[ -n "$SLAPD_ADDITIONAL_SCHEMAS" ]]; then
+ IFS=","; declare -a schemas=($SLAPD_ADDITIONAL_SCHEMAS); unset IFS
+
+ for schema in "${schemas[@]}"; do
+ slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/schema/${schema}.ldif" >/dev/null 2>&1
+ done
+ fi
+
+ if [[ -n "$SLAPD_ADDITIONAL_MODULES" ]]; then
+ IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES); unset IFS
+
+ for module in "${modules[@]}"; do
+ echo "Adding module ${module}"
+ slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/modules/${module}.ldif" >/dev/null 2>&1
+ done
+ fi
+
+ # This needs to run in background
+ # Will prepopulate entries after ldap daemon has started
+ ./prepopulate.sh &
+
+ chown -R openldap:openldap /etc/ldap/slapd.d/ /var/lib/ldap/ /var/run/slapd/
+else
+ slapd_configs_in_env=`env | grep 'SLAPD_'`
+
+ if [ -n "${slapd_configs_in_env:+x}" ]; then
+ echo "Info: Container already configured, therefore ignoring SLAPD_xxx environment variables"
+ fi
+fi
+
+exec "$@"
+
diff --git a/devenv/docker/blocks/multiple-openldap/ldap-server/Dockerfile b/devenv/docker/blocks/multiple-openldap/ldap-server/Dockerfile
new file mode 100644
index 000000000000..979d01c7dad4
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/ldap-server/Dockerfile
@@ -0,0 +1,30 @@
+# Fork of https://github.com/dinkel/docker-openldap
+
+FROM debian:jessie
+
+LABEL maintainer="Grafana team "
+
+ENV OPENLDAP_VERSION 2.4.40
+
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
+ slapd=${OPENLDAP_VERSION}* \
+ ldap-utils && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN mv /etc/ldap /etc/ldap.dist
+
+EXPOSE 389
+
+VOLUME ["/etc/ldap", "/var/lib/ldap"]
+
+COPY modules/ /etc/ldap.dist/modules
+COPY prepopulate/ /etc/ldap.dist/prepopulate
+
+COPY ../entrypoint.sh /entrypoint.sh
+COPY ../prepopulate.sh /prepopulate.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
+
+CMD ["slapd", "-d", "32768", "-u", "openldap", "-g", "openldap"]
diff --git a/devenv/docker/blocks/multiple-openldap/ldap-server/modules/memberof.ldif b/devenv/docker/blocks/multiple-openldap/ldap-server/modules/memberof.ldif
new file mode 100644
index 000000000000..fd9cce957c33
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/ldap-server/modules/memberof.ldif
@@ -0,0 +1,33 @@
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: memberof.la
+
+dn: olcOverlay={0}memberof,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcMemberOf
+objectClass: olcOverlayConfig
+objectClass: top
+olcOverlay: memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: refint.la
+
+dn: olcOverlay={1}refint,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcOverlayConfig
+objectClass: olcRefintConfig
+objectClass: top
+olcOverlay: {1}refint
+olcRefintAttribute: memberof member manager owner
diff --git a/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/1_units.ldif b/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/1_units.ldif
new file mode 100644
index 000000000000..22e063036889
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/1_units.ldif
@@ -0,0 +1,9 @@
+dn: ou=groups,dc=grafana,dc=org
+ou: Groups
+objectclass: top
+objectclass: organizationalUnit
+
+dn: ou=users,dc=grafana,dc=org
+ou: Users
+objectclass: top
+objectclass: organizationalUnit
diff --git a/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/2_users.ldif b/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/2_users.ldif
new file mode 100644
index 000000000000..8e1dfbf603ad
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/2_users.ldif
@@ -0,0 +1,59 @@
+dn: cn=ldap-editor,ou=users,dc=grafana,dc=org
+mail: ldap-editor@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-editor
+cn: ldap-editor
+
+dn: cn=ldap-viewer,ou=users,dc=grafana,dc=org
+mail: ldap-viewer@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-viewer
+cn: ldap-viewer
+
+dn: cn=ldap-carl,ou=users,dc=grafana,dc=org
+mail: ldap-carl@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-carl
+cn: ldap-carl
+
+dn: cn=ldap-daniel,ou=users,dc=grafana,dc=org
+mail: ldap-daniel@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-daniel
+cn: ldap-daniel
+
+dn: cn=ldap-leo,ou=users,dc=grafana,dc=org
+mail: ldap-leo@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-leo
+cn: ldap-leo
+
+dn: cn=ldap-tobias,ou=users,dc=grafana,dc=org
+mail: ldap-tobias@grafana.com
+userPassword: grafana
+objectClass: person
+objectClass: top
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+sn: ldap-tobias
+cn: ldap-tobias
diff --git a/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/3_groups.ldif b/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/3_groups.ldif
new file mode 100644
index 000000000000..8d55eaaa7079
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/ldap-server/prepopulate/3_groups.ldif
@@ -0,0 +1,23 @@
+dn: cn=admins,ou=groups,dc=grafana,dc=org
+cn: admins
+objectClass: groupOfNames
+objectClass: top
+
+dn: cn=editors,ou=groups,dc=grafana,dc=org
+cn: editors
+objectClass: groupOfNames
+member: cn=ldap-editor,ou=users,dc=grafana,dc=org
+
+dn: cn=backend,ou=groups,dc=grafana,dc=org
+cn: backend
+objectClass: groupOfNames
+member: cn=ldap-carl,ou=users,dc=grafana,dc=org
+member: cn=ldap-leo,ou=users,dc=grafana,dc=org
+member: cn=ldap-torkel,ou=users,dc=grafana,dc=org
+
+dn: cn=frontend,ou=groups,dc=grafana,dc=org
+cn: frontend
+objectClass: groupOfNames
+member: cn=ldap-torkel,ou=users,dc=grafana,dc=org
+member: cn=ldap-daniel,ou=users,dc=grafana,dc=org
+member: cn=ldap-leo,ou=users,dc=grafana,dc=org
diff --git a/devenv/docker/blocks/multiple-openldap/ldap_dev.toml b/devenv/docker/blocks/multiple-openldap/ldap_dev.toml
new file mode 100644
index 000000000000..c4c2516694f4
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/ldap_dev.toml
@@ -0,0 +1,59 @@
+# To troubleshoot and get more log info enable ldap debug logging in grafana.ini
+# [log]
+# filters = ldap:debug
+
+# For the verbose comments options see "openldap" env block
+
+# --- First LDAP Server (only admins) ---
+
+[[servers]]
+host = "127.0.0.1"
+port = 389
+use_ssl = false
+start_tls = false
+ssl_skip_verify = false
+bind_dn = "cn=admin,dc=grafana,dc=org"
+bind_password = 'grafana'
+search_filter = "(cn=%s)"
+search_base_dns = ["ou=users,dc=grafana,dc=org"]
+
+[servers.attributes]
+name = "givenName"
+surname = "sn"
+username = "cn"
+member_of = "memberOf"
+email = "email"
+
+[[servers.group_mappings]]
+group_dn = "cn=admins,ou=groups,dc=grafana,dc=org"
+org_role = "Admin"
+grafana_admin = true
+
+# --- Second LDAP Server (rest of the users) ---
+
+[[servers]]
+host = "127.0.0.1"
+port = 388
+use_ssl = false
+start_tls = false
+ssl_skip_verify = false
+
+bind_dn = "cn=admin,dc=grafana,dc=org"
+bind_password = 'grafana'
+search_filter = "(cn=%s)"
+search_base_dns = ["ou=users,dc=grafana,dc=org"]
+
+[servers.attributes]
+name = "givenName"
+surname = "sn"
+username = "cn"
+member_of = "memberOf"
+email = "email"
+
+[[servers.group_mappings]]
+group_dn = "cn=editors,ou=groups,dc=grafana,dc=org"
+org_role = "Editor"
+
+[[servers.group_mappings]]
+group_dn = "*"
+org_role = "Viewer"
diff --git a/devenv/docker/blocks/multiple-openldap/notes.md b/devenv/docker/blocks/multiple-openldap/notes.md
new file mode 100644
index 000000000000..1fcbfa013db0
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/notes.md
@@ -0,0 +1,38 @@
+# Notes on Multiple OpenLdap Docker Block
+
+This is very similar to openldap docker block, but it creates multiple ldap servers instead of one.
+
+Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database.
+
+"admins-ldap-server" block contains admin group and admin users. The "ldap-server" block has all the rest of the users. See below for the full list of users.
+
+This blocks are here to help with testing multiple LDAP servers, for any other LDAP related development and testing "openldap" block should be used.
+
+## Enabling LDAP in Grafana
+
+Copy the ldap_dev.toml file in this folder into your `conf` folder (it is gitignored already). To enable it in the .ini file to get Grafana to use this block:
+
+```ini
+[auth.ldap]
+enabled = true
+config_file = conf/ldap_dev.toml
+; allow_sign_up = true
+```
+
+## Groups & Users
+
+admins
+ ldap-admin
+ ldap-torkel
+backend
+ ldap-carl
+ ldap-torkel
+ ldap-leo
+frontend
+ ldap-torkel
+ ldap-tobias
+ ldap-daniel
+editors
+ ldap-editor
+no groups
+ ldap-viewer
diff --git a/devenv/docker/blocks/multiple-openldap/prepopulate.sh b/devenv/docker/blocks/multiple-openldap/prepopulate.sh
new file mode 100755
index 000000000000..aa11f8aba4f7
--- /dev/null
+++ b/devenv/docker/blocks/multiple-openldap/prepopulate.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+echo "Pre-populating ldap entries, first waiting for ldap to start"
+
+sleep 3
+
+adminUserDn="cn=admin,dc=grafana,dc=org"
+adminPassword="grafana"
+
+for file in `ls /etc/ldap/prepopulate/*.ldif`; do
+ ldapadd -x -D $adminUserDn -w $adminPassword -f "$file"
+done
+
+
diff --git a/devenv/docker/blocks/openldap/Dockerfile b/devenv/docker/blocks/openldap/Dockerfile
index 76172e133a45..b0d23b9e0c91 100644
--- a/devenv/docker/blocks/openldap/Dockerfile
+++ b/devenv/docker/blocks/openldap/Dockerfile
@@ -2,7 +2,7 @@
FROM debian:jessie
-LABEL maintainer="Christian Luginbühl "
+LABEL maintainer="Grafana team "
ENV OPENLDAP_VERSION 2.4.40
diff --git a/devenv/docker/blocks/openldap/notes.md b/devenv/docker/blocks/openldap/notes.md
index fb4130859706..a74c1427901f 100644
--- a/devenv/docker/blocks/openldap/notes.md
+++ b/devenv/docker/blocks/openldap/notes.md
@@ -2,8 +2,6 @@
Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database.
-The ldif files add eight users, `ldap-admin`, `ldap-editor`, `ldap-viewer`, `ldap-carl`, `ldap-daniel`, `ldap-leo`, `ldap-tobias` and `ldap-torkel`. Two groups, `admins` and `users`, are added that correspond with the group mappings in the default conf/ldap.toml. `ldap-admin` is a member of `admins` and `ldap-editor` is a member of `users`.
-
Note that users that are added here need to specify a `memberOf` attribute manually as well as the `member` attribute for the group. The `memberOf` module usually does this automatically (if you add a group in Apache Directory Studio for example) but this does not work in the entrypoint script as it uses the `slapadd` command to add entries before the server has started and before the `memberOf` module is loaded.
After adding ldif files to `prepopulate`:
@@ -23,12 +21,11 @@ config_file = conf/ldap_dev.toml
; allow_sign_up = true
```
-Test groups & users
+## Groups & Users
admins
ldap-admin
ldap-torkel
- ldap-daniel
backend
ldap-carl
ldap-torkel
From 597c380ead0c5a3d75845e4210eab3cfe337432b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 13 May 2019 13:18:14 +0200
Subject: [PATCH 011/166] Gauge: tweaks to background color and height usage
(#17019)
---
packages/grafana-ui/src/components/Gauge/Gauge.tsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx
index b45712ac9b0b..026b9769a708 100644
--- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx
+++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx
@@ -69,14 +69,14 @@ export class Gauge extends PureComponent {
const backgroundColor = selectThemeVariant(
{
- dark: theme.colors.dark3,
- light: '#e6e6e6',
+ dark: theme.colors.dark8,
+ light: theme.colors.gray6,
},
theme.type
);
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
- const gaugeWidth = Math.min(dimension / 6, 40) / gaugeWidthReduceRatio;
+ const gaugeWidth = Math.min(dimension / 5, 40) / gaugeWidthReduceRatio;
const thresholdMarkersWidth = gaugeWidth / 5;
const fontSize = Math.min(dimension / 5.5, 100) * (value.text !== null ? this.getFontScale(value.text.length) : 1);
const thresholdLabelFontSize = fontSize / 2.5;
@@ -181,7 +181,7 @@ function calculateGaugeAutoProps(width: number, height: number, title: string |
const titleFontSize = Math.min((width * 0.15) / 1.5, 20); // 20% of height * line-height, max 40px
const titleHeight = titleFontSize * 1.5;
const availableHeight = showLabel ? height - titleHeight : height;
- const gaugeHeight = Math.min(availableHeight * 0.7, width);
+ const gaugeHeight = Math.min(availableHeight, width);
return {
showLabel,
From 29ad72f0484bcac0225cbeaa89dcd7c298812d72 Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Mon, 13 May 2019 14:56:36 +0300
Subject: [PATCH 012/166] Chore: reintroduce gosec (#17021)
It seems gosec memory issue was recently fixed.
We should be good to go again
Ref securego/gosec#307
---
pkg/services/notifications/codes.go | 2 +-
scripts/backend-lint.sh | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/pkg/services/notifications/codes.go b/pkg/services/notifications/codes.go
index 6382b609036b..b2bc9439549f 100644
--- a/pkg/services/notifications/codes.go
+++ b/pkg/services/notifications/codes.go
@@ -1,7 +1,7 @@
package notifications
import (
- "crypto/sha1"
+ "crypto/sha1" // #nosec
"encoding/hex"
"fmt"
"time"
diff --git a/scripts/backend-lint.sh b/scripts/backend-lint.sh
index 064b8626fb2c..5c337d2d2ea3 100755
--- a/scripts/backend-lint.sh
+++ b/scripts/backend-lint.sh
@@ -15,7 +15,7 @@ go get -u github.com/alecthomas/gometalinter
go get -u github.com/jgautheron/goconst/cmd/goconst
go get -u honnef.co/go/tools/cmd/staticcheck
go get -u github.com/mgechev/revive
-#go get -u github.com/securego/gosec/cmd/gosec/...
+go get -u github.com/securego/gosec/cmd/gosec/...
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
# use gometalinter when lints are not available in golangci or
@@ -39,7 +39,7 @@ exit_if_fail go vet ./pkg/...
exit_if_fail revive -formatter stylish -config ./scripts/revive.toml
# TODO recheck the rules and leave only necessary exclusions
-# exit_if_fail gosec -quiet \
-# -exclude=G104,G107,G201,G202,G204,G301,G304,G401,G402,G501 \
-# -conf=./scripts/gosec.json \
-# ./pkg/...
+exit_if_fail gosec -quiet \
+ -exclude=G104,G107,G201,G202,G204,G301,G304,G401,G402,G501 \
+ -conf=./scripts/gosec.json \
+ ./pkg/...
From 719c1c277fc403437e49d72307364d5ab77bec38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 13 May 2019 14:00:29 +0200
Subject: [PATCH 013/166] Devenv: Updated gauge test dashboard
---
.../panel-gauge/gauge-multi-series.json | 271 ++++++++----------
1 file changed, 120 insertions(+), 151 deletions(-)
diff --git a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json
index 2f89d3ea9f31..3e1428a98657 100644
--- a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json
+++ b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json
@@ -21,79 +21,62 @@
"datasource": "gdev-testdata",
"gridPos": {
"h": 8,
- "w": 24,
+ "w": 20,
"x": 0,
"y": 0
},
"id": 6,
"links": [],
- "options-gauge": {
- "decimals": 0,
- "maxValue": 100,
- "minValue": 0,
- "options": {
- "decimals": 0,
- "maxValue": 100,
- "minValue": 0,
- "prefix": "",
- "showThresholdLabels": false,
- "showThresholdMarkers": true,
- "stat": "avg",
- "suffix": "",
- "thresholds": [],
- "unit": "none",
- "valueMappings": []
+ "options": {
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "decimals": 0,
+ "max": 100,
+ "min": 0,
+ "unit": "none"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "#7EB26D",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "#EAB839",
+ "index": 1,
+ "value": 50
+ },
+ {
+ "color": "#6ED0E0",
+ "index": 2,
+ "value": 75
+ },
+ {
+ "color": "#EF843C",
+ "index": 3,
+ "value": 87.5
+ },
+ {
+ "color": "#E24D42",
+ "index": 4,
+ "value": 93.75
+ },
+ {
+ "color": "#1F78C1",
+ "index": 5,
+ "value": 96.875
+ }
+ ],
+ "values": false
},
- "prefix": "",
+ "orientation": "auto",
"showThresholdLabels": false,
- "showThresholdMarkers": true,
- "stat": "avg",
- "suffix": "",
- "thresholds": [
- {
- "color": "#1F78C1",
- "index": 5,
- "value": 96.875
- },
- {
- "color": "#E24D42",
- "index": 4,
- "value": 93.75
- },
- {
- "color": "#EF843C",
- "index": 3,
- "value": 87.5
- },
- {
- "color": "#6ED0E0",
- "index": 2,
- "value": 75
- },
- {
- "color": "#EAB839",
- "index": 1,
- "value": 50
- },
- {
- "color": "#7EB26D",
- "index": 0,
- "value": null
- }
- ],
- "unit": "none",
- "valueMappings": [
- {
- "from": "50",
- "id": 1,
- "operator": "",
- "text": "Hello :) ",
- "to": "90",
- "type": 2,
- "value": ""
- }
- ]
+ "showThresholdMarkers": true
},
+ "pluginVersion": "6.3.0-pre",
"targets": [
{
"refId": "A",
@@ -124,50 +107,48 @@
{
"datasource": "gdev-testdata",
"gridPos": {
- "h": 8,
- "w": 24,
- "x": 0,
- "y": 8
+ "h": 28,
+ "w": 4,
+ "x": 20,
+ "y": 0
},
- "id": 2,
+ "id": 4,
"links": [],
- "options-gauge": {
- "decimals": 0,
- "maxValue": 100,
- "minValue": 0,
- "options": {
- "decimals": 0,
- "maxValue": 100,
- "minValue": 0,
- "prefix": "",
- "showThresholdLabels": false,
- "showThresholdMarkers": true,
- "stat": "avg",
- "suffix": "",
- "thresholds": [],
- "unit": "none",
- "valueMappings": []
+ "options": {
+ "fieldOptions": {
+ "calcs": ["max"],
+ "defaults": {
+ "decimals": 0,
+ "max": "200",
+ "min": 0,
+ "unit": "none"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "#7EB26D",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "#EAB839",
+ "index": 1,
+ "value": 50
+ },
+ {
+ "color": "#6ED0E0",
+ "index": 2,
+ "value": 75
+ }
+ ],
+ "values": false
},
- "prefix": "",
+ "orientation": "auto",
"showThresholdLabels": false,
- "showThresholdMarkers": true,
- "stat": "avg",
- "suffix": "",
- "thresholds": [
- {
- "color": "#EAB839",
- "index": 1,
- "value": 50
- },
- {
- "color": "#7EB26D",
- "index": 0,
- "value": null
- }
- ],
- "unit": "none",
- "valueMappings": []
+ "showThresholdMarkers": true
},
+ "pluginVersion": "6.3.0-pre",
"targets": [
{
"refId": "A",
@@ -192,61 +173,49 @@
],
"timeFrom": null,
"timeShift": null,
- "title": "Repeat horizontal",
+ "title": "Vertical",
"type": "gauge"
},
{
"datasource": "gdev-testdata",
"gridPos": {
- "h": 14,
- "w": 5,
+ "h": 20,
+ "w": 20,
"x": 0,
- "y": 16
+ "y": 8
},
- "id": 4,
+ "id": 2,
"links": [],
- "options-gauge": {
- "decimals": 0,
- "maxValue": "200",
- "minValue": 0,
- "options": {
- "decimals": 0,
- "maxValue": 100,
- "minValue": 0,
- "prefix": "",
- "showThresholdLabels": false,
- "showThresholdMarkers": true,
- "stat": "avg",
- "suffix": "",
- "thresholds": [],
- "unit": "none",
- "valueMappings": []
+ "options": {
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "decimals": 0,
+ "max": 100,
+ "min": 0,
+ "unit": "none"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "#7EB26D",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "#EAB839",
+ "index": 1,
+ "value": 50
+ }
+ ],
+ "values": false
},
- "prefix": "",
+ "orientation": "auto",
"showThresholdLabels": false,
- "showThresholdMarkers": true,
- "stat": "max",
- "suffix": "",
- "thresholds": [
- {
- "color": "#6ED0E0",
- "index": 2,
- "value": 75
- },
- {
- "color": "#EAB839",
- "index": 1,
- "value": 50
- },
- {
- "color": "#7EB26D",
- "index": 0,
- "value": null
- }
- ],
- "unit": "none",
- "valueMappings": []
+ "showThresholdMarkers": true
},
+ "pluginVersion": "6.3.0-pre",
"targets": [
{
"refId": "A",
@@ -271,11 +240,11 @@
],
"timeFrom": null,
"timeShift": null,
- "title": "Vertical",
+ "title": "Repeat horizontal",
"type": "gauge"
}
],
- "schemaVersion": 17,
+ "schemaVersion": 18,
"style": "dark",
"tags": ["panel-tests", "gdev", "gauge"],
"templating": {
@@ -292,5 +261,5 @@
"timezone": "",
"title": "Panel Tests - Gauge Multi Series",
"uid": "szkuR1umk",
- "version": 7
+ "version": 2
}
From 3ce2f3b58db29340e3ec29fecab15bed0ba6f6a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 13 May 2019 15:49:54 +0200
Subject: [PATCH 014/166] Dashboard: show refresh button in kiosk mode (#17032)
Fixes #16945
---
public/sass/components/_view_states.scss | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/public/sass/components/_view_states.scss b/public/sass/components/_view_states.scss
index e1ca3d44f835..fbc50cfca3c8 100644
--- a/public/sass/components/_view_states.scss
+++ b/public/sass/components/_view_states.scss
@@ -16,8 +16,7 @@
}
}
- .navbar-button--zoom,
- .navbar-button--refresh {
+ .navbar-button--zoom {
display: none;
}
}
From 01a7c1f0985daf7cc2b86ee27c5059e07439c54f Mon Sep 17 00:00:00 2001
From: Marcus Efraimsson
Date: Mon, 13 May 2019 16:33:27 +0200
Subject: [PATCH 015/166] chore: remove x character in explore
---
public/app/features/explore/Wrapper.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/public/app/features/explore/Wrapper.tsx b/public/app/features/explore/Wrapper.tsx
index b73917e09ca3..e8b894bc53d8 100644
--- a/public/app/features/explore/Wrapper.tsx
+++ b/public/app/features/explore/Wrapper.tsx
@@ -36,7 +36,6 @@ export class Wrapper extends Component {
)}
- x
);
From bbb1427f46a811911bd9553214adf61bc118687e Mon Sep 17 00:00:00 2001
From: Ryan McKinley
Date: Mon, 13 May 2019 22:26:55 -0700
Subject: [PATCH 016/166] SeriesData: remove color from Field (#17044)
---
packages/grafana-ui/src/types/data.ts | 1 -
packages/grafana-ui/src/utils/displayValue.test.ts | 2 +-
packages/grafana-ui/src/utils/displayValue.ts | 4 ++--
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/packages/grafana-ui/src/types/data.ts b/packages/grafana-ui/src/types/data.ts
index e629e76043a7..d1a1a7857776 100644
--- a/packages/grafana-ui/src/types/data.ts
+++ b/packages/grafana-ui/src/types/data.ts
@@ -47,7 +47,6 @@ export interface Field {
unit?: string;
dateFormat?: string; // Source data format
decimals?: number | null; // Significant digits (for display)
- color?: string;
min?: number | null;
max?: number | null;
}
diff --git a/packages/grafana-ui/src/utils/displayValue.test.ts b/packages/grafana-ui/src/utils/displayValue.test.ts
index 63c78e979826..139286acc017 100644
--- a/packages/grafana-ui/src/utils/displayValue.test.ts
+++ b/packages/grafana-ui/src/utils/displayValue.test.ts
@@ -18,7 +18,7 @@ describe('Process simple display values', () => {
getDisplayProcessor(),
// Add a simple option that is not used (uses a different base class)
- getDisplayProcessor({ field: { color: '#FFF' } }),
+ getDisplayProcessor({ field: { min: 0, max: 100 } }),
// Add a simple option that is not used (uses a different base class)
getDisplayProcessor({ field: { unit: 'locale' } }),
diff --git a/packages/grafana-ui/src/utils/displayValue.ts b/packages/grafana-ui/src/utils/displayValue.ts
index 59a04fefc2b9..6565985f4451 100644
--- a/packages/grafana-ui/src/utils/displayValue.ts
+++ b/packages/grafana-ui/src/utils/displayValue.ts
@@ -42,7 +42,7 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
return (value: any) => {
const { mappings, thresholds, theme } = options;
- let color = field.color;
+ let color;
let text = _.toString(value);
let numeric = toNumber(value);
@@ -76,7 +76,7 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
const { decimals, scaledDecimals } = getDecimalsForValue(value, field.decimals);
text = formatFunc(numeric, decimals, scaledDecimals, options.isUtc);
}
- if (thresholds && thresholds.length > 0) {
+ if (thresholds && thresholds.length) {
color = getColorFromThreshold(numeric, thresholds, theme);
}
}
From 1001cd7ac37a71f7935b6bfaa7a399a8ae7e4ff2 Mon Sep 17 00:00:00 2001
From: Johannes Schill
Date: Tue, 14 May 2019 07:36:19 +0200
Subject: [PATCH 017/166] Dashboard: Fixes scrolling issues for Edge browser
(#17033)
* Fix: Only set scrollTop on CustomScroll element when it's needed and move arrow functions out of the props
* Fix: Update snapshots
* Minor refactoring to reuse same functions when rendering custom scrollbar
Fixes #16796
---
.../CustomScrollbar/CustomScrollbar.tsx | 75 ++++++++++++-------
.../CustomScrollbar.test.tsx.snap | 4 +-
.../__snapshots__/ServerStats.test.tsx.snap | 4 +-
.../dashboard/containers/DashboardPage.tsx | 12 +--
.../__snapshots__/DashboardPage.test.tsx.snap | 4 +-
5 files changed, 59 insertions(+), 40 deletions(-)
diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx
index f2f6b236e14b..3546148f57c7 100644
--- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx
+++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx
@@ -42,9 +42,10 @@ export class CustomScrollbar extends Component {
updateScroll() {
const ref = this.ref.current;
+ const { scrollTop } = this.props;
- if (ref && !isNil(this.props.scrollTop)) {
- ref.scrollTop(this.props.scrollTop);
+ if (ref && !isNil(scrollTop)) {
+ ref.scrollTop(scrollTop);
}
}
@@ -70,6 +71,44 @@ export class CustomScrollbar extends Component {
this.updateScroll();
}
+ renderTrack = (track: 'track-vertical' | 'track-horizontal', hideTrack: boolean | undefined, passedProps: any) => {
+ return (
+
+ );
+ };
+
+ renderThumb = (thumb: 'thumb-horizontal' | 'thumb-vertical', passedProps: any) => {
+ return ;
+ };
+
+ renderTrackHorizontal = (passedProps: any) => {
+ return this.renderTrack('track-horizontal', this.props.hideHorizontalTrack, passedProps);
+ };
+
+ renderTrackVertical = (passedProps: any) => {
+ return this.renderTrack('track-vertical', this.props.hideVerticalTrack, passedProps);
+ };
+
+ renderThumbHorizontal = (passedProps: any) => {
+ return this.renderThumb('thumb-horizontal', passedProps);
+ };
+
+ renderThumbVertical = (passedProps: any) => {
+ return this.renderThumb('thumb-vertical', passedProps);
+ };
+
+ renderView = (passedProps: any) => {
+ return ;
+ };
+
render() {
const {
className,
@@ -80,8 +119,6 @@ export class CustomScrollbar extends Component {
autoHide,
autoHideTimeout,
hideTracksWhenNotNeeded,
- hideHorizontalTrack,
- hideVerticalTrack,
} = this.props;
return (
@@ -97,31 +134,11 @@ export class CustomScrollbar extends Component {
// Before these where set to inhert but that caused problems with cut of legends in firefox
autoHeightMax={autoHeightMax}
autoHeightMin={autoHeightMin}
- renderTrackHorizontal={props => (
-
- )}
- renderTrackVertical={props => (
-
- )}
- renderThumbHorizontal={props => }
- renderThumbVertical={props => }
- renderView={props => }
+ renderTrackHorizontal={this.renderTrackHorizontal}
+ renderTrackVertical={this.renderTrackVertical}
+ renderThumbHorizontal={this.renderThumbHorizontal}
+ renderThumbVertical={this.renderThumbVertical}
+ renderView={this.renderView}
>
{children}
diff --git a/packages/grafana-ui/src/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap b/packages/grafana-ui/src/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap
index b348f7dd8bd0..2d941bb1767b 100644
--- a/packages/grafana-ui/src/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap
+++ b/packages/grafana-ui/src/components/CustomScrollbar/__snapshots__/CustomScrollbar.test.tsx.snap
@@ -37,7 +37,7 @@ exports[`CustomScrollbar renders correctly 1`] = `
{
showLoadingState: false,
fullscreenPanel: null,
scrollTop: 0,
+ updateScrollTop: null,
rememberScrollTop: 0,
};
@@ -168,7 +170,7 @@ export class DashboardPage extends PureComponent
{
isEditing: false,
isFullscreen: false,
fullscreenPanel: null,
- scrollTop: this.state.rememberScrollTop,
+ updateScrollTop: this.state.rememberScrollTop,
},
this.triggerPanelsRendering.bind(this)
);
@@ -204,7 +206,7 @@ export class DashboardPage extends PureComponent {
setScrollTop = (e: MouseEvent): void => {
const target = e.target as HTMLElement;
- this.setState({ scrollTop: target.scrollTop });
+ this.setState({ scrollTop: target.scrollTop, updateScrollTop: null });
};
onAddPanel = () => {
@@ -251,7 +253,7 @@ export class DashboardPage extends PureComponent {
render() {
const { dashboard, editview, $injector, isInitSlow, initError } = this.props;
- const { isSettingsOpening, isEditing, isFullscreen, scrollTop } = this.state;
+ const { isSettingsOpening, isEditing, isFullscreen, scrollTop, updateScrollTop } = this.state;
if (!dashboard) {
if (isInitSlow) {
@@ -285,9 +287,9 @@ export class DashboardPage extends PureComponent {
/>
diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
index 20fc00cafc01..d6be7658841b 100644
--- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
+++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap
@@ -111,7 +111,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
autoHideTimeout={200}
className="custom-scrollbar--page"
hideTracksWhenNotNeeded={false}
- scrollTop={0}
+ scrollTop={null}
setScrollTop={[Function]}
updateAfterMountMs={500}
>
@@ -349,7 +349,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
autoHideTimeout={200}
className="custom-scrollbar--page"
hideTracksWhenNotNeeded={false}
- scrollTop={0}
+ scrollTop={null}
setScrollTop={[Function]}
updateAfterMountMs={500}
>
From a87a763d83fb72a825fee1e5c575dd95d82be394 Mon Sep 17 00:00:00 2001
From: Ryan McKinley
Date: Mon, 13 May 2019 22:55:49 -0700
Subject: [PATCH 018/166] DataSourcePlugin: support custom tabs (#16859)
* use ConfigEditor
* add tabs
* add tabs
* set the nav in state
* remove actions
* reorder imports
* catch plugin loading errors
* better text
* keep props
* fix typo
* update snapshot
* rename tab to page
* add missing pages
---
packages/grafana-ui/src/types/plugin.ts | 18 +-
.../settings/DataSourceSettingsPage.test.tsx | 3 +-
.../settings/DataSourceSettingsPage.tsx | 211 ++++++++++++------
.../datasources/settings/PluginSettings.tsx | 2 +-
.../DataSourceSettingsPage.test.tsx.snap | 143 ------------
.../app/features/datasources/state/actions.ts | 5 +-
.../features/datasources/state/navModel.ts | 61 +++--
public/app/features/plugins/PluginPage.tsx | 92 ++++----
.../{ExampleTab1.tsx => ExamplePage1.tsx} | 6 +-
.../{ExampleTab2.tsx => ExamplePage2.tsx} | 6 +-
public/app/plugins/app/example-app/module.ts | 20 +-
.../datasource/testdata/TestInfoTab.tsx | 28 +++
.../plugins/datasource/testdata/module.tsx | 9 +-
public/app/routes/routes.ts | 1 +
14 files changed, 300 insertions(+), 305 deletions(-)
rename public/app/plugins/app/example-app/config/{ExampleTab1.tsx => ExamplePage1.tsx} (65%)
rename public/app/plugins/app/example-app/config/{ExampleTab2.tsx => ExamplePage2.tsx} (65%)
create mode 100644 public/app/plugins/datasource/testdata/TestInfoTab.tsx
diff --git a/packages/grafana-ui/src/types/plugin.ts b/packages/grafana-ui/src/types/plugin.ts
index c083fd95be38..72e9d2453fd5 100644
--- a/packages/grafana-ui/src/types/plugin.ts
+++ b/packages/grafana-ui/src/types/plugin.ts
@@ -91,17 +91,17 @@ export interface PluginMetaInfo {
version: string;
}
-export interface PluginConfigTabProps {
- meta: T;
+export interface PluginConfigPageProps {
+ plugin: T;
query: { [s: string]: any }; // The URL query parameters
}
-export interface PluginConfigTab {
+export interface PluginConfigPage {
title: string; // Display
icon?: string;
id: string; // Unique, in URL
- body: ComponentClass>;
+ body: ComponentClass>;
}
export class GrafanaPlugin {
@@ -112,14 +112,14 @@ export class GrafanaPlugin {
angularConfigCtrl?: any;
// Show configuration tabs on the plugin page
- configTabs?: Array>;
+ configPages?: Array>;
// Tabs on the plugin page
- addConfigTab(tab: PluginConfigTab) {
- if (!this.configTabs) {
- this.configTabs = [];
+ addConfigPage(tab: PluginConfigPage) {
+ if (!this.configPages) {
+ this.configPages = [];
}
- this.configTabs.push(tab);
+ this.configPages.push(tab);
return this;
}
}
diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx
index 575311c2d174..6f9939e882b9 100644
--- a/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx
+++ b/public/app/features/datasources/settings/DataSourceSettingsPage.test.tsx
@@ -19,7 +19,7 @@ const setup = (propOverrides?: object) => {
setDataSourceName,
updateDataSource: jest.fn(),
setIsDefault,
- plugin: pluginMock,
+ query: {},
...propOverrides,
};
@@ -45,7 +45,6 @@ describe('Render', () => {
it('should render beta info text', () => {
const wrapper = setup({
dataSourceMeta: { ...getMockPlugin(), state: 'beta' },
- plugin: pluginMock,
});
expect(wrapper).toMatchSnapshot();
diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx
index e02684d57fcc..5c31b946149c 100644
--- a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx
+++ b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx
@@ -2,6 +2,7 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
+import isString from 'lodash/isString';
// Components
import Page from 'app/core/components/Page/Page';
@@ -21,7 +22,7 @@ import { getNavModel } from 'app/core/selectors/navModel';
import { getRouteParamsId } from 'app/core/selectors/location';
// Types
-import { StoreState } from 'app/types/';
+import { StoreState, UrlQueryMap } from 'app/types/';
import { NavModel, DataSourceSettings, DataSourcePluginMeta } from '@grafana/ui';
import { getDataSourceLoadingNav } from '../state/navModel';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
@@ -38,14 +39,17 @@ export interface Props {
updateDataSource: typeof updateDataSource;
setIsDefault: typeof setIsDefault;
plugin?: GenericDataSourcePlugin;
+ query: UrlQueryMap;
+ page?: string;
}
interface State {
dataSource: DataSourceSettings;
- plugin: GenericDataSourcePlugin;
+ plugin?: GenericDataSourcePlugin;
isTesting?: boolean;
testingMessage?: string;
testingStatus?: string;
+ loadError?: any;
}
export class DataSourceSettingsPage extends PureComponent {
@@ -73,9 +77,17 @@ export class DataSourceSettingsPage extends PureComponent {
async componentDidMount() {
const { loadDataSource, pageId } = this.props;
- await loadDataSource(pageId);
- if (!this.state.plugin) {
- await this.loadPlugin();
+ if (isNaN(pageId)) {
+ this.setState({ loadError: 'Invalid ID' });
+ return;
+ }
+ try {
+ await loadDataSource(pageId);
+ if (!this.state.plugin) {
+ await this.loadPlugin();
+ }
+ } catch (err) {
+ this.setState({ loadError: err });
}
}
@@ -174,70 +186,133 @@ export class DataSourceSettingsPage extends PureComponent {
return this.state.dataSource.id > 0;
}
- render() {
- const { dataSourceMeta, navModel, setDataSourceName, setIsDefault } = this.props;
- const { testingMessage, testingStatus, plugin, dataSource } = this.state;
+ renderLoadError(loadError: any) {
+ let showDelete = false;
+ let msg = loadError.toString();
+ if (loadError.data) {
+ if (loadError.data.message) {
+ msg = loadError.data.message;
+ }
+ } else if (isString(loadError)) {
+ showDelete = true;
+ }
+
+ const node = {
+ text: msg,
+ subTitle: 'Data Source Error',
+ icon: 'fa fa-fw fa-warning',
+ };
+ const nav = {
+ node: node,
+ main: node,
+ };
return (
-
-
- {this.hasDataSource && (
-
-
+ return (
+
+ );
+ }
+
+ render() {
+ const { navModel, page } = this.props;
+ const { loadError } = this.state;
+
+ if (loadError) {
+ return this.renderLoadError(loadError);
+ }
+
+ return (
+
+
+ {this.hasDataSource && {page ? this.renderConfigPageBody(page) : this.renderSettings()}
}
);
@@ -247,11 +322,19 @@ export class DataSourceSettingsPage extends PureComponent
{
function mapStateToProps(state: StoreState) {
const pageId = getRouteParamsId(state.location);
const dataSource = getDataSource(state.dataSources, pageId);
+ const page = state.location.query.page as string;
+
return {
- navModel: getNavModel(state.navIndex, `datasource-settings-${pageId}`, getDataSourceLoadingNav('settings')),
+ navModel: getNavModel(
+ state.navIndex,
+ page ? `datasource-page-${page}` : `datasource-settings-${pageId}`,
+ getDataSourceLoadingNav('settings')
+ ),
dataSource: getDataSource(state.dataSources, pageId),
dataSourceMeta: getDataSourceMeta(state.dataSources, dataSource.type),
pageId: pageId,
+ query: state.location.query,
+ page,
};
}
diff --git a/public/app/features/datasources/settings/PluginSettings.tsx b/public/app/features/datasources/settings/PluginSettings.tsx
index 94c054d56ee9..a7462cbb45c7 100644
--- a/public/app/features/datasources/settings/PluginSettings.tsx
+++ b/public/app/features/datasources/settings/PluginSettings.tsx
@@ -54,7 +54,7 @@ export class PluginSettings extends PureComponent {
}
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
const { plugin } = this.props;
if (!plugin.components.ConfigEditor && this.props.dataSource !== prevProps.dataSource) {
this.scopeProps.ctrl.current = _.cloneDeep(this.props.dataSource);
diff --git a/public/app/features/datasources/settings/__snapshots__/DataSourceSettingsPage.test.tsx.snap b/public/app/features/datasources/settings/__snapshots__/DataSourceSettingsPage.test.tsx.snap
index 2ca6a2ee28f0..063ebf8418de 100644
--- a/public/app/features/datasources/settings/__snapshots__/DataSourceSettingsPage.test.tsx.snap
+++ b/public/app/features/datasources/settings/__snapshots__/DataSourceSettingsPage.test.tsx.snap
@@ -153,78 +153,6 @@ exports[`Render should render beta info text 1`] = `
onDefaultChange={[Function]}
onNameChange={[Function]}
/>
-
@@ -257,77 +185,6 @@ exports[`Render should render component 1`] = `
onDefaultChange={[Function]}
onNameChange={[Function]}
/>
-
diff --git a/public/app/features/datasources/state/actions.ts b/public/app/features/datasources/state/actions.ts
index a74943aa9ecf..a09289500693 100644
--- a/public/app/features/datasources/state/actions.ts
+++ b/public/app/features/datasources/state/actions.ts
@@ -10,6 +10,7 @@ import { StoreState, LocationUpdate } from 'app/types';
import { actionCreatorFactory } from 'app/core/redux';
import { ActionOf, noPayloadActionCreatorFactory } from 'app/core/redux/actionCreatorFactory';
import { getPluginSettings } from 'app/features/plugins/PluginSettingsCache';
+import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader';
export const dataSourceLoaded = actionCreatorFactory('LOAD_DATA_SOURCE').create();
@@ -52,9 +53,11 @@ export function loadDataSource(id: number): ThunkResult {
return async dispatch => {
const dataSource = await getBackendSrv().get(`/api/datasources/${id}`);
const pluginInfo = (await getPluginSettings(dataSource.type)) as DataSourcePluginMeta;
+ const plugin = await importDataSourcePlugin(pluginInfo);
+
dispatch(dataSourceLoaded(dataSource));
dispatch(dataSourceMetaLoaded(pluginInfo));
- dispatch(updateNavIndex(buildNavModel(dataSource, pluginInfo)));
+ dispatch(updateNavIndex(buildNavModel(dataSource, plugin)));
};
}
diff --git a/public/app/features/datasources/state/navModel.ts b/public/app/features/datasources/state/navModel.ts
index 6fdc6f5762f8..6b5f679451de 100644
--- a/public/app/features/datasources/state/navModel.ts
+++ b/public/app/features/datasources/state/navModel.ts
@@ -1,7 +1,10 @@
-import { PluginMeta, DataSourceSettings, PluginType, NavModel, NavModelItem, PluginInclude } from '@grafana/ui';
+import { DataSourceSettings, PluginType, NavModel, NavModelItem, PluginInclude } from '@grafana/ui';
import config from 'app/core/config';
+import { GenericDataSourcePlugin } from '../settings/PluginSettings';
+
+export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDataSourcePlugin): NavModelItem {
+ const pluginMeta = plugin.meta;
-export function buildNavModel(dataSource: DataSourceSettings, pluginMeta: PluginMeta): NavModelItem {
const navModel = {
img: pluginMeta.info.logos.large,
id: 'datasource-' + dataSource.id,
@@ -20,6 +23,18 @@ export function buildNavModel(dataSource: DataSourceSettings, pluginMeta: Plugin
],
};
+ if (plugin.configPages) {
+ for (const page of plugin.configPages) {
+ navModel.children.push({
+ active: false,
+ text: page.title,
+ icon: page.icon,
+ url: `datasources/edit/${dataSource.id}/?page=${page.id}`,
+ id: `datasource-page-${page.id}`,
+ });
+ }
+ }
+
if (pluginMeta.includes && hasDashboards(pluginMeta.includes)) {
navModel.children.push({
active: false,
@@ -65,28 +80,30 @@ export function getDataSourceLoadingNav(pageName: string): NavModel {
user: '',
},
{
- id: '1',
- type: PluginType.datasource,
- name: '',
- info: {
- author: {
- name: '',
- url: '',
- },
- description: '',
- links: [{ name: '', url: '' }],
- logos: {
- large: '',
- small: '',
+ meta: {
+ id: '1',
+ type: PluginType.datasource,
+ name: '',
+ info: {
+ author: {
+ name: '',
+ url: '',
+ },
+ description: '',
+ links: [{ name: '', url: '' }],
+ logos: {
+ large: '',
+ small: '',
+ },
+ screenshots: [],
+ updated: '',
+ version: '',
},
- screenshots: [],
- updated: '',
- version: '',
+ includes: [],
+ module: '',
+ baseUrl: '',
},
- includes: [],
- module: '',
- baseUrl: '',
- }
+ } as GenericDataSourcePlugin
);
let node: NavModelItem;
diff --git a/public/app/features/plugins/PluginPage.tsx b/public/app/features/plugins/PluginPage.tsx
index 176c03c6b6ca..9da5b097f2a7 100644
--- a/public/app/features/plugins/PluginPage.tsx
+++ b/public/app/features/plugins/PluginPage.tsx
@@ -74,12 +74,12 @@ interface State {
loading: boolean;
plugin?: GrafanaPlugin;
nav: NavModel;
- defaultTab: string; // The first configured one or readme
+ defaultPage: string; // The first configured one or readme
}
-const TAB_ID_README = 'readme';
-const TAB_ID_DASHBOARDS = 'dashboards';
-const TAB_ID_CONFIG_CTRL = 'config';
+const PAGE_ID_README = 'readme';
+const PAGE_ID_DASHBOARDS = 'dashboards';
+const PAGE_ID_CONFIG_CTRL = 'config';
class PluginPage extends PureComponent {
constructor(props: Props) {
@@ -87,7 +87,7 @@ class PluginPage extends PureComponent {
this.state = {
loading: true,
nav: getLoadingNav(),
- defaultTab: TAB_ID_README,
+ defaultPage: PAGE_ID_README,
};
}
@@ -103,14 +103,14 @@ class PluginPage extends PureComponent {
}
const { meta } = plugin;
- let defaultTab: string;
- const tabs: NavModelItem[] = [];
+ let defaultPage: string;
+ const pages: NavModelItem[] = [];
if (true) {
- tabs.push({
+ pages.push({
text: 'Readme',
icon: 'fa fa-fw fa-file-text-o',
- url: path + '?tab=' + TAB_ID_README,
- id: TAB_ID_README,
+ url: path + '?page=' + PAGE_ID_README,
+ id: PAGE_ID_README,
});
}
@@ -118,42 +118,42 @@ class PluginPage extends PureComponent {
if (meta.type === PluginType.app) {
// Legacy App Config
if (plugin.angularConfigCtrl) {
- tabs.push({
+ pages.push({
text: 'Config',
icon: 'gicon gicon-cog',
- url: path + '?tab=' + TAB_ID_CONFIG_CTRL,
- id: TAB_ID_CONFIG_CTRL,
+ url: path + '?page=' + PAGE_ID_CONFIG_CTRL,
+ id: PAGE_ID_CONFIG_CTRL,
});
- defaultTab = TAB_ID_CONFIG_CTRL;
+ defaultPage = PAGE_ID_CONFIG_CTRL;
}
- if (plugin.configTabs) {
- for (const tab of plugin.configTabs) {
- tabs.push({
- text: tab.title,
- icon: tab.icon,
- url: path + '?tab=' + tab.id,
- id: tab.id,
+ if (plugin.configPages) {
+ for (const page of plugin.configPages) {
+ pages.push({
+ text: page.title,
+ icon: page.icon,
+ url: path + '?page=' + page.id,
+ id: page.id,
});
- if (!defaultTab) {
- defaultTab = tab.id;
+ if (!defaultPage) {
+ defaultPage = page.id;
}
}
}
- // Check for the dashboard tabs
+ // Check for the dashboard pages
if (find(meta.includes, { type: 'dashboard' })) {
- tabs.push({
+ pages.push({
text: 'Dashboards',
icon: 'gicon gicon-dashboard',
- url: path + '?tab=' + TAB_ID_DASHBOARDS,
- id: TAB_ID_DASHBOARDS,
+ url: path + '?page=' + PAGE_ID_DASHBOARDS,
+ id: PAGE_ID_DASHBOARDS,
});
}
}
- if (!defaultTab) {
- defaultTab = tabs[0].id; // the first tab
+ if (!defaultPage) {
+ defaultPage = pages[0].id; // the first tab
}
const node = {
@@ -162,13 +162,13 @@ class PluginPage extends PureComponent {
subTitle: meta.info.author.name,
breadcrumbs: [{ title: 'Plugins', url: '/plugins' }],
url: path,
- children: this.setActiveTab(query.tab as string, tabs, defaultTab),
+ children: this.setActivePage(query.page as string, pages, defaultPage),
};
this.setState({
loading: false,
plugin,
- defaultTab,
+ defaultPage,
nav: {
node: node,
main: node,
@@ -176,15 +176,15 @@ class PluginPage extends PureComponent {
});
}
- setActiveTab(tabId: string, tabs: NavModelItem[], defaultTabId: string): NavModelItem[] {
+ setActivePage(pageId: string, pages: NavModelItem[], defaultPageId: string): NavModelItem[] {
let found = false;
- const selected = tabId || defaultTabId;
- const changed = tabs.map(tab => {
- const active = !found && selected === tab.id;
+ const selected = pageId || defaultPageId;
+ const changed = pages.map(p => {
+ const active = !found && selected === p.id;
if (active) {
found = true;
}
- return { ...tab, active };
+ return { ...p, active };
});
if (!found) {
changed[0].active = true;
@@ -193,13 +193,13 @@ class PluginPage extends PureComponent {
}
componentDidUpdate(prevProps: Props) {
- const prevTab = prevProps.query.tab as string;
- const tab = this.props.query.tab as string;
- if (prevTab !== tab) {
- const { nav, defaultTab } = this.state;
+ const prevPage = prevProps.query.page as string;
+ const page = this.props.query.page as string;
+ if (prevPage !== page) {
+ const { nav, defaultPage } = this.state;
const node = {
...nav.node,
- children: this.setActiveTab(tab, nav.node.children, defaultTab),
+ children: this.setActivePage(page, nav.node.children, defaultPage),
};
this.setState({
nav: {
@@ -221,21 +221,21 @@ class PluginPage extends PureComponent {
const active = nav.main.children.find(tab => tab.active);
if (active) {
// Find the current config tab
- if (plugin.configTabs) {
- for (const tab of plugin.configTabs) {
+ if (plugin.configPages) {
+ for (const tab of plugin.configPages) {
if (tab.id === active.id) {
- return ;
+ return ;
}
}
}
// Apps have some special behavior
if (plugin.meta.type === PluginType.app) {
- if (active.id === TAB_ID_DASHBOARDS) {
+ if (active.id === PAGE_ID_DASHBOARDS) {
return ;
}
- if (active.id === TAB_ID_CONFIG_CTRL && plugin.angularConfigCtrl) {
+ if (active.id === PAGE_ID_CONFIG_CTRL && plugin.angularConfigCtrl) {
return ;
}
}
diff --git a/public/app/plugins/app/example-app/config/ExampleTab1.tsx b/public/app/plugins/app/example-app/config/ExamplePage1.tsx
similarity index 65%
rename from public/app/plugins/app/example-app/config/ExampleTab1.tsx
rename to public/app/plugins/app/example-app/config/ExamplePage1.tsx
index cf79a880f435..c25eee35e535 100644
--- a/public/app/plugins/app/example-app/config/ExampleTab1.tsx
+++ b/public/app/plugins/app/example-app/config/ExamplePage1.tsx
@@ -2,11 +2,11 @@
import React, { PureComponent } from 'react';
// Types
-import { PluginConfigTabProps, AppPluginMeta } from '@grafana/ui';
+import { PluginConfigPageProps, AppPlugin } from '@grafana/ui';
-interface Props extends PluginConfigTabProps {}
+interface Props extends PluginConfigPageProps {}
-export class ExampleTab1 extends PureComponent {
+export class ExamplePage1 extends PureComponent {
constructor(props: Props) {
super(props);
}
diff --git a/public/app/plugins/app/example-app/config/ExampleTab2.tsx b/public/app/plugins/app/example-app/config/ExamplePage2.tsx
similarity index 65%
rename from public/app/plugins/app/example-app/config/ExampleTab2.tsx
rename to public/app/plugins/app/example-app/config/ExamplePage2.tsx
index bf2ae181405a..596bb88e9ba0 100644
--- a/public/app/plugins/app/example-app/config/ExampleTab2.tsx
+++ b/public/app/plugins/app/example-app/config/ExamplePage2.tsx
@@ -2,11 +2,11 @@
import React, { PureComponent } from 'react';
// Types
-import { PluginConfigTabProps, AppPluginMeta } from '@grafana/ui';
+import { PluginConfigPageProps, AppPlugin } from '@grafana/ui';
-interface Props extends PluginConfigTabProps {}
+interface Props extends PluginConfigPageProps {}
-export class ExampleTab2 extends PureComponent {
+export class ExamplePage2 extends PureComponent {
constructor(props: Props) {
super(props);
}
diff --git a/public/app/plugins/app/example-app/module.ts b/public/app/plugins/app/example-app/module.ts
index 0b4a2ae646a9..f82f7faec08b 100644
--- a/public/app/plugins/app/example-app/module.ts
+++ b/public/app/plugins/app/example-app/module.ts
@@ -2,8 +2,8 @@
import { ExampleConfigCtrl } from './legacy/config';
import { AngularExamplePageCtrl } from './legacy/angular_example_page';
import { AppPlugin } from '@grafana/ui';
-import { ExampleTab1 } from './config/ExampleTab1';
-import { ExampleTab2 } from './config/ExampleTab2';
+import { ExamplePage1 } from './config/ExamplePage1';
+import { ExamplePage2 } from './config/ExamplePage2';
import { ExampleRootPage } from './ExampleRootPage';
// Legacy exports just for testing
@@ -14,15 +14,15 @@ export {
export const plugin = new AppPlugin()
.setRootPage(ExampleRootPage)
- .addConfigTab({
- title: 'Tab 1',
+ .addConfigPage({
+ title: 'Page 1',
icon: 'fa fa-info',
- body: ExampleTab1,
- id: 'tab1',
+ body: ExamplePage1,
+ id: 'page1',
})
- .addConfigTab({
- title: 'Tab 2',
+ .addConfigPage({
+ title: 'Page 2',
icon: 'fa fa-user',
- body: ExampleTab2,
- id: 'tab2',
+ body: ExamplePage2,
+ id: 'page2',
});
diff --git a/public/app/plugins/datasource/testdata/TestInfoTab.tsx b/public/app/plugins/datasource/testdata/TestInfoTab.tsx
new file mode 100644
index 000000000000..686d8b94e4ab
--- /dev/null
+++ b/public/app/plugins/datasource/testdata/TestInfoTab.tsx
@@ -0,0 +1,28 @@
+// Libraries
+import React, { PureComponent } from 'react';
+
+// Types
+import { PluginConfigPageProps, DataSourcePlugin } from '@grafana/ui';
+import { TestDataDatasource } from './datasource';
+
+interface Props extends PluginConfigPageProps> {}
+
+export class TestInfoTab extends PureComponent {
+ constructor(props: Props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+ See github for more information about setting up a reproducable test environment.
+
+
+
+ Github
+
+
+
+ );
+ }
+}
diff --git a/public/app/plugins/datasource/testdata/module.tsx b/public/app/plugins/datasource/testdata/module.tsx
index 738149451b97..800f61db7370 100644
--- a/public/app/plugins/datasource/testdata/module.tsx
+++ b/public/app/plugins/datasource/testdata/module.tsx
@@ -1,6 +1,7 @@
import { DataSourcePlugin } from '@grafana/ui';
import { TestDataDatasource } from './datasource';
import { TestDataQueryCtrl } from './query_ctrl';
+import { TestInfoTab } from './TestInfoTab';
import { ConfigEditor } from './ConfigEditor';
class TestDataAnnotationsQueryCtrl {
@@ -12,4 +13,10 @@ class TestDataAnnotationsQueryCtrl {
export const plugin = new DataSourcePlugin(TestDataDatasource)
.setConfigEditor(ConfigEditor)
.setQueryCtrl(TestDataQueryCtrl)
- .setAnnotationQueryCtrl(TestDataAnnotationsQueryCtrl);
+ .setAnnotationQueryCtrl(TestDataAnnotationsQueryCtrl)
+ .addConfigPage({
+ title: 'Setup',
+ icon: 'fa fa-list-alt',
+ body: TestInfoTab,
+ id: 'setup',
+ });
diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts
index 9a7c9b5d2d50..a863797dea04 100644
--- a/public/app/routes/routes.ts
+++ b/public/app/routes/routes.ts
@@ -110,6 +110,7 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
})
.when('/datasources/edit/:id/', {
template: '',
+ reloadOnSearch: false, // for tabs
resolve: {
component: () => DataSourceSettingsPage,
},
From 13f137a17d91eda5e15b6006f257dfa9c5171c19 Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Tue, 14 May 2019 08:15:05 +0200
Subject: [PATCH 019/166] tech: avoid alias for importing models in alerting
(#17041)
ref #14679
---
pkg/services/alerting/commands.go | 8 ++--
pkg/services/alerting/conditions/query.go | 6 +--
.../alerting/conditions/query_test.go | 8 ++--
pkg/services/alerting/eval_context.go | 44 +++++++++----------
pkg/services/alerting/extractor.go | 36 +++++++--------
pkg/services/alerting/extractor_test.go | 36 +++++++--------
pkg/services/alerting/notifier.go | 19 ++++----
.../alerting/notifiers/alertmanager.go | 16 +++----
.../alerting/notifiers/alertmanager_test.go | 29 ++++++------
pkg/services/alerting/notifiers/dingding.go | 6 +--
.../alerting/notifiers/dingding_test.go | 6 +--
pkg/services/alerting/notifiers/discord.go | 8 ++--
.../alerting/notifiers/discord_test.go | 6 +--
pkg/services/alerting/notifiers/email.go | 9 ++--
pkg/services/alerting/notifiers/email_test.go | 8 ++--
pkg/services/alerting/notifiers/googlechat.go | 6 +--
.../alerting/notifiers/googlechat_test.go | 6 +--
.../alerting/notifiers/hipchat_test.go | 8 ++--
pkg/services/alerting/notifiers/kafka.go | 6 +--
pkg/services/alerting/notifiers/kafka_test.go | 6 +--
pkg/services/alerting/notifiers/line.go | 8 ++--
pkg/services/alerting/notifiers/line_test.go | 6 +--
pkg/services/alerting/notifiers/opsgenie.go | 12 ++---
.../alerting/notifiers/opsgenie_test.go | 6 +--
pkg/services/alerting/notifiers/pagerduty.go | 10 ++---
.../alerting/notifiers/pagerduty_test.go | 8 ++--
pkg/services/alerting/notifiers/pushover.go | 8 ++--
.../alerting/notifiers/pushover_test.go | 13 +++---
pkg/services/alerting/notifiers/sensu.go | 6 +--
pkg/services/alerting/notifiers/sensu_test.go | 6 +--
pkg/services/alerting/notifiers/slack.go | 10 ++---
pkg/services/alerting/notifiers/slack_test.go | 8 ++--
pkg/services/alerting/notifiers/teams.go | 8 ++--
pkg/services/alerting/notifiers/teams_test.go | 8 ++--
pkg/services/alerting/notifiers/telegram.go | 16 +++----
.../alerting/notifiers/telegram_test.go | 14 +++---
pkg/services/alerting/notifiers/threema.go | 12 ++---
.../alerting/notifiers/threema_test.go | 12 ++---
.../alerting/notifiers/victorops_test.go | 6 +--
pkg/services/alerting/notifiers/webhook.go | 6 +--
.../alerting/notifiers/webhook_test.go | 6 +--
pkg/services/alerting/reader.go | 5 +--
pkg/services/alerting/result_handler.go | 9 ++--
pkg/services/alerting/rule.go | 14 +++---
pkg/services/alerting/rule_test.go | 12 ++---
pkg/services/alerting/test_notification.go | 8 ++--
pkg/services/alerting/test_rule.go | 6 +--
47 files changed, 261 insertions(+), 259 deletions(-)
diff --git a/pkg/services/alerting/commands.go b/pkg/services/alerting/commands.go
index dd2ff5658d60..887334ec7290 100644
--- a/pkg/services/alerting/commands.go
+++ b/pkg/services/alerting/commands.go
@@ -2,7 +2,7 @@ package alerting
import (
"github.com/grafana/grafana/pkg/bus"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
)
func init() {
@@ -10,14 +10,14 @@ func init() {
bus.AddHandler("alerting", validateDashboardAlerts)
}
-func validateDashboardAlerts(cmd *m.ValidateDashboardAlertsCommand) error {
+func validateDashboardAlerts(cmd *models.ValidateDashboardAlertsCommand) error {
extractor := NewDashAlertExtractor(cmd.Dashboard, cmd.OrgId, cmd.User)
return extractor.ValidateAlerts()
}
-func updateDashboardAlerts(cmd *m.UpdateDashboardAlertsCommand) error {
- saveAlerts := m.SaveAlertsCommand{
+func updateDashboardAlerts(cmd *models.UpdateDashboardAlertsCommand) error {
+ saveAlerts := models.SaveAlertsCommand{
OrgId: cmd.OrgId,
UserId: cmd.User.UserId,
DashboardId: cmd.Dashboard.Id,
diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go
index 7d1a276c42e6..37dbd9b3f7a6 100644
--- a/pkg/services/alerting/conditions/query.go
+++ b/pkg/services/alerting/conditions/query.go
@@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/tsdb"
)
@@ -100,7 +100,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.Conditio
}
func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) {
- getDsInfo := &m.GetDataSourceByIdQuery{
+ getDsInfo := &models.GetDataSourceByIdQuery{
Id: c.Query.DatasourceId,
OrgId: context.Rule.OrgId,
}
@@ -139,7 +139,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
return result, nil
}
-func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource, timeRange *tsdb.TimeRange) *tsdb.TsdbQuery {
+func (c *QueryCondition) getRequestForAlertRule(datasource *models.DataSource, timeRange *tsdb.TimeRange) *tsdb.TsdbQuery {
req := &tsdb.TsdbQuery{
TimeRange: timeRange,
Queries: []*tsdb.Query{
diff --git a/pkg/services/alerting/conditions/query_test.go b/pkg/services/alerting/conditions/query_test.go
index 0ea6470bc2d8..2e1ecf5f39c5 100644
--- a/pkg/services/alerting/conditions/query_test.go
+++ b/pkg/services/alerting/conditions/query_test.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/tsdb"
. "github.com/smartystreets/goconvey/convey"
@@ -168,7 +168,7 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
ctx.condition = condition
- condition.HandleRequest = func(context context.Context, dsInfo *m.DataSource, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
+ condition.HandleRequest = func(context context.Context, dsInfo *models.DataSource, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
return &tsdb.Response{
Results: map[string]*tsdb.QueryResult{
"A": {Series: ctx.series},
@@ -182,8 +182,8 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
func queryConditionScenario(desc string, fn queryConditionScenarioFunc) {
Convey(desc, func() {
- bus.AddHandler("test", func(query *m.GetDataSourceByIdQuery) error {
- query.Result = &m.DataSource{Id: 1, Type: "graphite"}
+ bus.AddHandler("test", func(query *models.GetDataSourceByIdQuery) error {
+ query.Result = &models.DataSource{Id: 1, Type: "graphite"}
return nil
})
diff --git a/pkg/services/alerting/eval_context.go b/pkg/services/alerting/eval_context.go
index 6358c22a97f1..7d9a9014086b 100644
--- a/pkg/services/alerting/eval_context.go
+++ b/pkg/services/alerting/eval_context.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
@@ -23,12 +23,12 @@ type EvalContext struct {
Rule *Rule
log log.Logger
- dashboardRef *m.DashboardRef
+ dashboardRef *models.DashboardRef
ImagePublicUrl string
ImageOnDiskPath string
NoDataFound bool
- PrevAlertState m.AlertStateType
+ PrevAlertState models.AlertStateType
Ctx context.Context
}
@@ -53,22 +53,22 @@ type StateDescription struct {
func (c *EvalContext) GetStateModel() *StateDescription {
switch c.Rule.State {
- case m.AlertStateOK:
+ case models.AlertStateOK:
return &StateDescription{
Color: "#36a64f",
Text: "OK",
}
- case m.AlertStateNoData:
+ case models.AlertStateNoData:
return &StateDescription{
Color: "#888888",
Text: "No Data",
}
- case m.AlertStateAlerting:
+ case models.AlertStateAlerting:
return &StateDescription{
Color: "#D63232",
Text: "Alerting",
}
- case m.AlertStateUnknown:
+ case models.AlertStateUnknown:
return &StateDescription{
Color: "#888888",
Text: "Unknown",
@@ -90,12 +90,12 @@ func (c *EvalContext) GetNotificationTitle() string {
return "[" + c.GetStateModel().Text + "] " + c.Rule.Name
}
-func (c *EvalContext) GetDashboardUID() (*m.DashboardRef, error) {
+func (c *EvalContext) GetDashboardUID() (*models.DashboardRef, error) {
if c.dashboardRef != nil {
return c.dashboardRef, nil
}
- uidQuery := &m.GetDashboardRefByIdQuery{Id: c.Rule.DashboardId}
+ uidQuery := &models.GetDashboardRefByIdQuery{Id: c.Rule.DashboardId}
if err := bus.Dispatch(uidQuery); err != nil {
return nil, err
}
@@ -115,29 +115,29 @@ func (c *EvalContext) GetRuleUrl() (string, error) {
if err != nil {
return "", err
}
- return fmt.Sprintf(urlFormat, m.GetFullDashboardUrl(ref.Uid, ref.Slug), c.Rule.PanelId, c.Rule.OrgId), nil
+ return fmt.Sprintf(urlFormat, models.GetFullDashboardUrl(ref.Uid, ref.Slug), c.Rule.PanelId, c.Rule.OrgId), nil
}
// GetNewState returns the new state from the alert rule evaluation
-func (c *EvalContext) GetNewState() m.AlertStateType {
+func (c *EvalContext) GetNewState() models.AlertStateType {
ns := getNewStateInternal(c)
- if ns != m.AlertStateAlerting || c.Rule.For == 0 {
+ if ns != models.AlertStateAlerting || c.Rule.For == 0 {
return ns
}
since := time.Since(c.Rule.LastStateChange)
- if c.PrevAlertState == m.AlertStatePending && since > c.Rule.For {
- return m.AlertStateAlerting
+ if c.PrevAlertState == models.AlertStatePending && since > c.Rule.For {
+ return models.AlertStateAlerting
}
- if c.PrevAlertState == m.AlertStateAlerting {
- return m.AlertStateAlerting
+ if c.PrevAlertState == models.AlertStateAlerting {
+ return models.AlertStateAlerting
}
- return m.AlertStatePending
+ return models.AlertStatePending
}
-func getNewStateInternal(c *EvalContext) m.AlertStateType {
+func getNewStateInternal(c *EvalContext) models.AlertStateType {
if c.Error != nil {
c.log.Error("Alert Rule Result Error",
"ruleId", c.Rule.Id,
@@ -145,14 +145,14 @@ func getNewStateInternal(c *EvalContext) m.AlertStateType {
"error", c.Error,
"changing state to", c.Rule.ExecutionErrorState.ToAlertState())
- if c.Rule.ExecutionErrorState == m.ExecutionErrorKeepState {
+ if c.Rule.ExecutionErrorState == models.ExecutionErrorKeepState {
return c.PrevAlertState
}
return c.Rule.ExecutionErrorState.ToAlertState()
}
if c.Firing {
- return m.AlertStateAlerting
+ return models.AlertStateAlerting
}
if c.NoDataFound {
@@ -161,11 +161,11 @@ func getNewStateInternal(c *EvalContext) m.AlertStateType {
"name", c.Rule.Name,
"changing state to", c.Rule.NoDataState.ToAlertState())
- if c.Rule.NoDataState == m.NoDataKeepState {
+ if c.Rule.NoDataState == models.NoDataKeepState {
return c.PrevAlertState
}
return c.Rule.NoDataState.ToAlertState()
}
- return m.AlertStateOK
+ return models.AlertStateOK
}
diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go
index c3fcc01ad55e..6bf5e786c198 100644
--- a/pkg/services/alerting/extractor.go
+++ b/pkg/services/alerting/extractor.go
@@ -8,19 +8,19 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
)
// DashAlertExtractor extracts alerts from the dashboard json
type DashAlertExtractor struct {
- User *m.SignedInUser
- Dash *m.Dashboard
+ User *models.SignedInUser
+ Dash *models.Dashboard
OrgID int64
log log.Logger
}
// NewDashAlertExtractor returns a new DashAlertExtractor
-func NewDashAlertExtractor(dash *m.Dashboard, orgID int64, user *m.SignedInUser) *DashAlertExtractor {
+func NewDashAlertExtractor(dash *models.Dashboard, orgID int64, user *models.SignedInUser) *DashAlertExtractor {
return &DashAlertExtractor{
User: user,
Dash: dash,
@@ -29,9 +29,9 @@ func NewDashAlertExtractor(dash *m.Dashboard, orgID int64, user *m.SignedInUser)
}
}
-func (e *DashAlertExtractor) lookupDatasourceID(dsName string) (*m.DataSource, error) {
+func (e *DashAlertExtractor) lookupDatasourceID(dsName string) (*models.DataSource, error) {
if dsName == "" {
- query := &m.GetDataSourcesQuery{OrgId: e.OrgID}
+ query := &models.GetDataSourcesQuery{OrgId: e.OrgID}
if err := bus.Dispatch(query); err != nil {
return nil, err
}
@@ -42,7 +42,7 @@ func (e *DashAlertExtractor) lookupDatasourceID(dsName string) (*m.DataSource, e
}
}
} else {
- query := &m.GetDataSourceByNameQuery{Name: dsName, OrgId: e.OrgID}
+ query := &models.GetDataSourceByNameQuery{Name: dsName, OrgId: e.OrgID}
if err := bus.Dispatch(query); err != nil {
return nil, err
}
@@ -73,8 +73,8 @@ func copyJSON(in *simplejson.Json) (*simplejson.Json, error) {
return simplejson.NewJson(rawJSON)
}
-func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json, validateAlertFunc func(*m.Alert) bool) ([]*m.Alert, error) {
- alerts := make([]*m.Alert, 0)
+func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json, validateAlertFunc func(*models.Alert) bool) ([]*models.Alert, error) {
+ alerts := make([]*models.Alert, 0)
for _, panelObj := range jsonWithPanels.Get("panels").MustArray() {
panel := simplejson.NewFromAny(panelObj)
@@ -124,7 +124,7 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
}
}
- alert := &m.Alert{
+ alert := &models.Alert{
DashboardId: e.Dash.Id,
OrgId: e.OrgID,
PanelId: panelID,
@@ -161,9 +161,9 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
return nil, ValidationError{Reason: fmt.Sprintf("Data source used by alert rule not found, alertName=%v, datasource=%s", alert.Name, dsName)}
}
- dsFilterQuery := m.DatasourcesPermissionFilterQuery{
+ dsFilterQuery := models.DatasourcesPermissionFilterQuery{
User: e.User,
- Datasources: []*m.DataSource{datasource},
+ Datasources: []*models.DataSource{datasource},
}
if err := bus.Dispatch(&dsFilterQuery); err != nil {
@@ -172,7 +172,7 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
}
} else {
if len(dsFilterQuery.Result) == 0 {
- return nil, m.ErrDataSourceAccessDenied
+ return nil, models.ErrDataSourceAccessDenied
}
}
@@ -203,22 +203,22 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
return alerts, nil
}
-func validateAlertRule(alert *m.Alert) bool {
+func validateAlertRule(alert *models.Alert) bool {
return alert.ValidToSave()
}
// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data
-func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
+func (e *DashAlertExtractor) GetAlerts() ([]*models.Alert, error) {
return e.extractAlerts(validateAlertRule)
}
-func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *m.Alert) bool) ([]*m.Alert, error) {
+func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert) bool) ([]*models.Alert, error) {
dashboardJSON, err := copyJSON(e.Dash.Data)
if err != nil {
return nil, err
}
- alerts := make([]*m.Alert, 0)
+ alerts := make([]*models.Alert, 0)
// We extract alerts from rows to be backwards compatible
// with the old dashboard json model.
@@ -249,6 +249,6 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *m.Alert) boo
// ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id
// in the first validation pass
func (e *DashAlertExtractor) ValidateAlerts() error {
- _, err := e.extractAlerts(func(alert *m.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 })
+ _, err := e.extractAlerts(func(alert *models.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 })
return err
}
diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go
index 9c689fec921b..716ff746cd84 100644
--- a/pkg/services/alerting/extractor_test.go
+++ b/pkg/services/alerting/extractor_test.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
. "github.com/smartystreets/goconvey/convey"
)
@@ -21,17 +21,17 @@ func TestAlertRuleExtraction(t *testing.T) {
})
// mock data
- defaultDs := &m.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true}
- graphite2Ds := &m.DataSource{Id: 15, OrgId: 1, Name: "graphite2"}
- influxDBDs := &m.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"}
- prom := &m.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"}
+ defaultDs := &models.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true}
+ graphite2Ds := &models.DataSource{Id: 15, OrgId: 1, Name: "graphite2"}
+ influxDBDs := &models.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"}
+ prom := &models.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"}
- bus.AddHandler("test", func(query *m.GetDataSourcesQuery) error {
- query.Result = []*m.DataSource{defaultDs, graphite2Ds}
+ bus.AddHandler("test", func(query *models.GetDataSourcesQuery) error {
+ query.Result = []*models.DataSource{defaultDs, graphite2Ds}
return nil
})
- bus.AddHandler("test", func(query *m.GetDataSourceByNameQuery) error {
+ bus.AddHandler("test", func(query *models.GetDataSourceByNameQuery) error {
if query.Name == defaultDs.Name {
query.Result = defaultDs
}
@@ -55,7 +55,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(json)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
getTarget := func(j *simplejson.Json) string {
rowObj := j.Get("rows").MustArray()[0]
@@ -84,7 +84,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(json)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
extractor := NewDashAlertExtractor(dash, 1, nil)
alerts, err := extractor.GetAlerts()
@@ -152,7 +152,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(panelWithoutId)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
extractor := NewDashAlertExtractor(dash, 1, nil)
_, err = extractor.GetAlerts()
@@ -168,7 +168,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(panelWithIdZero)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
extractor := NewDashAlertExtractor(dash, 1, nil)
_, err = extractor.GetAlerts()
@@ -184,7 +184,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(json)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
extractor := NewDashAlertExtractor(dash, 1, nil)
alerts, err := extractor.GetAlerts()
@@ -200,10 +200,10 @@ func TestAlertRuleExtraction(t *testing.T) {
Convey("Alert notifications are in DB", func() {
sqlstore.InitTestDB(t)
- firstNotification := m.CreateAlertNotificationCommand{Uid: "notifier1", OrgId: 1, Name: "1"}
+ firstNotification := models.CreateAlertNotificationCommand{Uid: "notifier1", OrgId: 1, Name: "1"}
err = sqlstore.CreateAlertNotificationCommand(&firstNotification)
So(err, ShouldBeNil)
- secondNotification := m.CreateAlertNotificationCommand{Uid: "notifier2", OrgId: 1, Name: "2"}
+ secondNotification := models.CreateAlertNotificationCommand{Uid: "notifier2", OrgId: 1, Name: "2"}
err = sqlstore.CreateAlertNotificationCommand(&secondNotification)
So(err, ShouldBeNil)
@@ -213,7 +213,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(json)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
extractor := NewDashAlertExtractor(dash, 1, nil)
alerts, err := extractor.GetAlerts()
@@ -243,7 +243,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJson, err := simplejson.NewJson(json)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJson)
+ dash := models.NewDashboardFromJson(dashJson)
extractor := NewDashAlertExtractor(dash, 1, nil)
alerts, err := extractor.GetAlerts()
@@ -263,7 +263,7 @@ func TestAlertRuleExtraction(t *testing.T) {
dashJSON, err := simplejson.NewJson(json)
So(err, ShouldBeNil)
- dash := m.NewDashboardFromJson(dashJSON)
+ dash := models.NewDashboardFromJson(dashJSON)
extractor := NewDashAlertExtractor(dash, 1, nil)
err = extractor.ValidateAlerts()
diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go
index 8c2ac839e439..a2824da4a67c 100644
--- a/pkg/services/alerting/notifier.go
+++ b/pkg/services/alerting/notifier.go
@@ -8,10 +8,9 @@ import (
"github.com/grafana/grafana/pkg/components/imguploader"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting"
-
- m "github.com/grafana/grafana/pkg/models"
)
type NotifierPlugin struct {
@@ -73,7 +72,7 @@ func (n *notificationService) sendAndMarkAsComplete(evalContext *EvalContext, no
return nil
}
- cmd := &m.SetAlertNotificationStateToCompleteCommand{
+ cmd := &models.SetAlertNotificationStateToCompleteCommand{
Id: notifierState.state.Id,
Version: notifierState.state.Version,
}
@@ -83,14 +82,14 @@ func (n *notificationService) sendAndMarkAsComplete(evalContext *EvalContext, no
func (n *notificationService) sendNotification(evalContext *EvalContext, notifierState *notifierState) error {
if !evalContext.IsTestRun {
- setPendingCmd := &m.SetAlertNotificationStateToPendingCommand{
+ setPendingCmd := &models.SetAlertNotificationStateToPendingCommand{
Id: notifierState.state.Id,
Version: notifierState.state.Version,
AlertRuleStateUpdatedVersion: evalContext.Rule.StateChanges,
}
err := bus.DispatchCtx(evalContext.Ctx, setPendingCmd)
- if err == m.ErrAlertNotificationStateVersionConflict {
+ if err == models.ErrAlertNotificationStateVersionConflict {
return nil
}
@@ -128,7 +127,7 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
Height: 500,
Timeout: setting.AlertingEvaluationTimeout,
OrgId: context.Rule.OrgId,
- OrgRole: m.ROLE_ADMIN,
+ OrgRole: models.ROLE_ADMIN,
ConcurrentLimit: setting.AlertingRenderLimit,
}
@@ -158,7 +157,7 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
}
func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids []string, evalContext *EvalContext) (notifierStateSlice, error) {
- query := &m.GetAlertNotificationsWithUidToSendQuery{OrgId: orgId, Uids: notificationUids}
+ query := &models.GetAlertNotificationsWithUidToSendQuery{OrgId: orgId, Uids: notificationUids}
if err := bus.Dispatch(query); err != nil {
return nil, err
@@ -172,7 +171,7 @@ func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids [
continue
}
- query := &m.GetOrCreateNotificationStateQuery{
+ query := &models.GetOrCreateNotificationStateQuery{
NotifierId: notification.Id,
AlertId: evalContext.Rule.Id,
OrgId: evalContext.Rule.OrgId,
@@ -196,7 +195,7 @@ func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids [
}
// InitNotifier instantiate a new notifier based on the model
-func InitNotifier(model *m.AlertNotification) (Notifier, error) {
+func InitNotifier(model *models.AlertNotification) (Notifier, error) {
notifierPlugin, found := notifierFactories[model.Type]
if !found {
return nil, errors.New("Unsupported notification type")
@@ -205,7 +204,7 @@ func InitNotifier(model *m.AlertNotification) (Notifier, error) {
return notifierPlugin.Factory(model)
}
-type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)
+type NotifierFactory func(notification *models.AlertNotification) (Notifier, error)
var notifierFactories = make(map[string]*NotifierPlugin)
diff --git a/pkg/services/alerting/notifiers/alertmanager.go b/pkg/services/alerting/notifiers/alertmanager.go
index 9febe42505be..b90f9e65792c 100644
--- a/pkg/services/alerting/notifiers/alertmanager.go
+++ b/pkg/services/alerting/notifiers/alertmanager.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -27,7 +27,7 @@ func init() {
})
}
-func NewAlertmanagerNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewAlertmanagerNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -46,24 +46,24 @@ type AlertmanagerNotifier struct {
log log.Logger
}
-func (this *AlertmanagerNotifier) ShouldNotify(ctx context.Context, evalContext *alerting.EvalContext, notificationState *m.AlertNotificationState) bool {
+func (this *AlertmanagerNotifier) ShouldNotify(ctx context.Context, evalContext *alerting.EvalContext, notificationState *models.AlertNotificationState) bool {
this.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)
// Do not notify when we become OK for the first time.
- if (evalContext.PrevAlertState == m.AlertStatePending) && (evalContext.Rule.State == m.AlertStateOK) {
+ if (evalContext.PrevAlertState == models.AlertStatePending) && (evalContext.Rule.State == models.AlertStateOK) {
return false
}
// Notify on Alerting -> OK to resolve before alertmanager timeout.
- if (evalContext.PrevAlertState == m.AlertStateAlerting) && (evalContext.Rule.State == m.AlertStateOK) {
+ if (evalContext.PrevAlertState == models.AlertStateAlerting) && (evalContext.Rule.State == models.AlertStateOK) {
return true
}
- return evalContext.Rule.State == m.AlertStateAlerting
+ return evalContext.Rule.State == models.AlertStateAlerting
}
func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleUrl string) *simplejson.Json {
alertJSON := simplejson.New()
alertJSON.Set("startsAt", evalContext.StartTime.UTC().Format(time.RFC3339))
- if evalContext.Rule.State == m.AlertStateOK {
+ if evalContext.Rule.State == models.AlertStateOK {
alertJSON.Set("endsAt", time.Now().UTC().Format(time.RFC3339))
}
alertJSON.Set("generatorURL", ruleUrl)
@@ -128,7 +128,7 @@ func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) erro
bodyJSON := simplejson.NewFromAny(alerts)
body, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.Url + "/api/v1/alerts",
HttpMethod: "POST",
Body: string(body),
diff --git a/pkg/services/alerting/notifiers/alertmanager_test.go b/pkg/services/alerting/notifiers/alertmanager_test.go
index 9197926035ea..acf039fbf68e 100644
--- a/pkg/services/alerting/notifiers/alertmanager_test.go
+++ b/pkg/services/alerting/notifiers/alertmanager_test.go
@@ -6,36 +6,37 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
+
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
)
func TestWhenAlertManagerShouldNotify(t *testing.T) {
tcs := []struct {
- prevState m.AlertStateType
- newState m.AlertStateType
+ prevState models.AlertStateType
+ newState models.AlertStateType
expect bool
}{
{
- prevState: m.AlertStatePending,
- newState: m.AlertStateOK,
+ prevState: models.AlertStatePending,
+ newState: models.AlertStateOK,
expect: false,
},
{
- prevState: m.AlertStateAlerting,
- newState: m.AlertStateOK,
+ prevState: models.AlertStateAlerting,
+ newState: models.AlertStateOK,
expect: true,
},
{
- prevState: m.AlertStateOK,
- newState: m.AlertStatePending,
+ prevState: models.AlertStateOK,
+ newState: models.AlertStatePending,
expect: false,
},
{
- prevState: m.AlertStateUnknown,
- newState: m.AlertStatePending,
+ prevState: models.AlertStateUnknown,
+ newState: models.AlertStatePending,
expect: false,
},
}
@@ -48,7 +49,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) {
evalContext.Rule.State = tc.newState
- res := am.ShouldNotify(context.TODO(), evalContext, &m.AlertNotificationState{})
+ res := am.ShouldNotify(context.TODO(), evalContext, &models.AlertNotificationState{})
if res != tc.expect {
t.Errorf("got %v expected %v", res, tc.expect)
}
@@ -63,7 +64,7 @@ func TestAlertmanagerNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "alertmanager",
Type: "alertmanager",
Settings: settingsJSON,
@@ -77,7 +78,7 @@ func TestAlertmanagerNotifier(t *testing.T) {
json := `{ "url": "http://127.0.0.1:9093/" }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "alertmanager",
Type: "alertmanager",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go
index 0aa2ba078f37..45ce24c9aaa1 100644
--- a/pkg/services/alerting/notifiers/dingding.go
+++ b/pkg/services/alerting/notifiers/dingding.go
@@ -8,7 +8,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -36,7 +36,7 @@ func init() {
}
-func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewDingDingNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -129,7 +129,7 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
return err
}
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.Url,
Body: string(body),
}
diff --git a/pkg/services/alerting/notifiers/dingding_test.go b/pkg/services/alerting/notifiers/dingding_test.go
index f89bf6382ce9..7d645d7efb62 100644
--- a/pkg/services/alerting/notifiers/dingding_test.go
+++ b/pkg/services/alerting/notifiers/dingding_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -14,7 +14,7 @@ func TestDingDingNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "dingding_testing",
Type: "dingding",
Settings: settingsJSON,
@@ -28,7 +28,7 @@ func TestDingDingNotifier(t *testing.T) {
json := `{ "url": "https://www.google.com" }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "dingding_testing",
Type: "dingding",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/discord.go b/pkg/services/alerting/notifiers/discord.go
index 6ed422b1cbb0..9933ad5e5871 100644
--- a/pkg/services/alerting/notifiers/discord.go
+++ b/pkg/services/alerting/notifiers/discord.go
@@ -11,7 +11,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
)
@@ -32,7 +32,7 @@ func init() {
})
}
-func NewDiscordNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewDiscordNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find webhook url property in settings"}
@@ -111,7 +111,7 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error {
json, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.WebhookURL,
HttpMethod: "POST",
ContentType: "application/json",
@@ -135,7 +135,7 @@ func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error {
return nil
}
-func (this *DiscordNotifier) embedImage(cmd *m.SendWebhookSync, imagePath string, existingJSONBody []byte) error {
+func (this *DiscordNotifier) embedImage(cmd *models.SendWebhookSync, imagePath string, existingJSONBody []byte) error {
f, err := os.Open(imagePath)
defer f.Close()
if err != nil {
diff --git a/pkg/services/alerting/notifiers/discord_test.go b/pkg/services/alerting/notifiers/discord_test.go
index fe925aab362e..dfc6bbe9aee0 100644
--- a/pkg/services/alerting/notifiers/discord_test.go
+++ b/pkg/services/alerting/notifiers/discord_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestDiscordNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "discord_testing",
Type: "discord",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestDiscordNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "discord_testing",
Type: "discord",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/email.go b/pkg/services/alerting/notifiers/email.go
index b3f465a2b80c..61b7b11d893c 100644
--- a/pkg/services/alerting/notifiers/email.go
+++ b/pkg/services/alerting/notifiers/email.go
@@ -6,7 +6,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
+
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
)
@@ -35,7 +36,7 @@ type EmailNotifier struct {
log log.Logger
}
-func NewEmailNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewEmailNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
addressesString := model.Settings.Get("addresses").MustString()
if addressesString == "" {
@@ -72,8 +73,8 @@ func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
error = evalContext.Error.Error()
}
- cmd := &m.SendEmailCommandSync{
- SendEmailCommand: m.SendEmailCommand{
+ cmd := &models.SendEmailCommandSync{
+ SendEmailCommand: models.SendEmailCommand{
Subject: evalContext.GetNotificationTitle(),
Data: map[string]interface{}{
"Title": evalContext.GetNotificationTitle(),
diff --git a/pkg/services/alerting/notifiers/email_test.go b/pkg/services/alerting/notifiers/email_test.go
index 9750cbc2833c..b0c57f2f254d 100644
--- a/pkg/services/alerting/notifiers/email_test.go
+++ b/pkg/services/alerting/notifiers/email_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestEmailNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "email",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestEmailNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "email",
Settings: settingsJSON,
@@ -57,7 +57,7 @@ func TestEmailNotifier(t *testing.T) {
settingsJSON, err := simplejson.NewJson([]byte(json))
So(err, ShouldBeNil)
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "email",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/googlechat.go b/pkg/services/alerting/notifiers/googlechat.go
index 41f3503640cb..c00089e0dc57 100644
--- a/pkg/services/alerting/notifiers/googlechat.go
+++ b/pkg/services/alerting/notifiers/googlechat.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
)
@@ -29,7 +29,7 @@ func init() {
})
}
-func NewGoogleChatNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewGoogleChatNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -199,7 +199,7 @@ func (this *GoogleChatNotifier) Notify(evalContext *alerting.EvalContext) error
}
body, _ := json.Marshal(res1D)
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.Url,
HttpMethod: "POST",
HttpHeader: headers,
diff --git a/pkg/services/alerting/notifiers/googlechat_test.go b/pkg/services/alerting/notifiers/googlechat_test.go
index 1fdce878926f..5368eb63c96b 100644
--- a/pkg/services/alerting/notifiers/googlechat_test.go
+++ b/pkg/services/alerting/notifiers/googlechat_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestGoogleChatNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "googlechat",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestGoogleChatNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "googlechat",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/hipchat_test.go b/pkg/services/alerting/notifiers/hipchat_test.go
index 1597be12eb8b..57ad03ed7c21 100644
--- a/pkg/services/alerting/notifiers/hipchat_test.go
+++ b/pkg/services/alerting/notifiers/hipchat_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestHipChatNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "hipchat",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestHipChatNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "hipchat",
Settings: settingsJSON,
@@ -59,7 +59,7 @@ func TestHipChatNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "hipchat",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/kafka.go b/pkg/services/alerting/notifiers/kafka.go
index d7da05499b75..168b1646b16b 100644
--- a/pkg/services/alerting/notifiers/kafka.go
+++ b/pkg/services/alerting/notifiers/kafka.go
@@ -8,7 +8,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -32,7 +32,7 @@ func init() {
})
}
-func NewKafkaNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewKafkaNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
endpoint := model.Settings.Get("kafkaRestProxy").MustString()
if endpoint == "" {
return nil, alerting.ValidationError{Reason: "Could not find kafka rest proxy endpoint property in settings"}
@@ -101,7 +101,7 @@ func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
topicUrl := this.Endpoint + "/topics/" + this.Topic
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: topicUrl,
Body: string(body),
HttpMethod: "POST",
diff --git a/pkg/services/alerting/notifiers/kafka_test.go b/pkg/services/alerting/notifiers/kafka_test.go
index 045976cb14ba..03a343835e15 100644
--- a/pkg/services/alerting/notifiers/kafka_test.go
+++ b/pkg/services/alerting/notifiers/kafka_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestKafkaNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "kafka_testing",
Type: "kafka",
Settings: settingsJSON,
@@ -34,7 +34,7 @@ func TestKafkaNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "kafka_testing",
Type: "kafka",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/line.go b/pkg/services/alerting/notifiers/line.go
index edbec373becd..d8bf70f8b9c9 100644
--- a/pkg/services/alerting/notifiers/line.go
+++ b/pkg/services/alerting/notifiers/line.go
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -32,7 +32,7 @@ const (
lineNotifyUrl string = "https://notify-api.line.me/api/notify"
)
-func NewLINENotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewLINENotifier(model *models.AlertNotification) (alerting.Notifier, error) {
token := model.Settings.Get("token").MustString()
if token == "" {
return nil, alerting.ValidationError{Reason: "Could not find token in settings"}
@@ -56,7 +56,7 @@ func (this *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
var err error
switch evalContext.Rule.State {
- case m.AlertStateAlerting:
+ case models.AlertStateAlerting:
err = this.createAlert(evalContext)
}
return err
@@ -79,7 +79,7 @@ func (this *LineNotifier) createAlert(evalContext *alerting.EvalContext) error {
form.Add("imageFullsize", evalContext.ImagePublicUrl)
}
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: lineNotifyUrl,
HttpMethod: "POST",
HttpHeader: map[string]string{
diff --git a/pkg/services/alerting/notifiers/line_test.go b/pkg/services/alerting/notifiers/line_test.go
index 6630665e9924..69082d0e066e 100644
--- a/pkg/services/alerting/notifiers/line_test.go
+++ b/pkg/services/alerting/notifiers/line_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -14,7 +14,7 @@ func TestLineNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "line_testing",
Type: "line",
Settings: settingsJSON,
@@ -30,7 +30,7 @@ func TestLineNotifier(t *testing.T) {
"token": "abcdefgh0123456789"
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "line_testing",
Type: "line",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/opsgenie.go b/pkg/services/alerting/notifiers/opsgenie.go
index 84242ea97691..23dd453fe92e 100644
--- a/pkg/services/alerting/notifiers/opsgenie.go
+++ b/pkg/services/alerting/notifiers/opsgenie.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -44,7 +44,7 @@ var (
opsgenieAlertURL = "https://api.opsgenie.com/v2/alerts"
)
-func NewOpsGenieNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewOpsGenieNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
autoClose := model.Settings.Get("autoClose").MustBool(true)
apiKey := model.Settings.Get("apiKey").MustString()
apiUrl := model.Settings.Get("apiUrl").MustString()
@@ -76,11 +76,11 @@ func (this *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
var err error
switch evalContext.Rule.State {
- case m.AlertStateOK:
+ case models.AlertStateOK:
if this.AutoClose {
err = this.closeAlert(evalContext)
}
- case m.AlertStateAlerting:
+ case models.AlertStateAlerting:
err = this.createAlert(evalContext)
}
return err
@@ -115,7 +115,7 @@ func (this *OpsGenieNotifier) createAlert(evalContext *alerting.EvalContext) err
bodyJSON.Set("details", details)
body, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.ApiUrl,
Body: string(body),
HttpMethod: "POST",
@@ -139,7 +139,7 @@ func (this *OpsGenieNotifier) closeAlert(evalContext *alerting.EvalContext) erro
bodyJSON.Set("source", "Grafana")
body, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: fmt.Sprintf("%s/alertId-%d/close?identifierType=alias", this.ApiUrl, evalContext.Rule.Id),
Body: string(body),
HttpMethod: "POST",
diff --git a/pkg/services/alerting/notifiers/opsgenie_test.go b/pkg/services/alerting/notifiers/opsgenie_test.go
index 9dcb9f3c600e..e4954ed904af 100644
--- a/pkg/services/alerting/notifiers/opsgenie_test.go
+++ b/pkg/services/alerting/notifiers/opsgenie_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestOpsGenieNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "opsgenie_testing",
Type: "opsgenie",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestOpsGenieNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "opsgenie_testing",
Type: "opsgenie",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/pagerduty.go b/pkg/services/alerting/notifiers/pagerduty.go
index ab2a36fd86b2..2b60058ecd93 100644
--- a/pkg/services/alerting/notifiers/pagerduty.go
+++ b/pkg/services/alerting/notifiers/pagerduty.go
@@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -43,7 +43,7 @@ var (
pagerdutyEventApiUrl = "https://events.pagerduty.com/v2/enqueue"
)
-func NewPagerdutyNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewPagerdutyNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
autoResolve := model.Settings.Get("autoResolve").MustBool(false)
key := model.Settings.Get("integrationKey").MustString()
if key == "" {
@@ -67,13 +67,13 @@ type PagerdutyNotifier struct {
func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
- if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {
+ if evalContext.Rule.State == models.AlertStateOK && !this.AutoResolve {
this.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve)
return nil
}
eventType := "trigger"
- if evalContext.Rule.State == m.AlertStateOK {
+ if evalContext.Rule.State == models.AlertStateOK {
eventType = "resolve"
}
customData := triggMetrString
@@ -122,7 +122,7 @@ func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
body, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: pagerdutyEventApiUrl,
Body: string(body),
HttpMethod: "POST",
diff --git a/pkg/services/alerting/notifiers/pagerduty_test.go b/pkg/services/alerting/notifiers/pagerduty_test.go
index 1d2eeec4a522..1698ec7605ac 100644
--- a/pkg/services/alerting/notifiers/pagerduty_test.go
+++ b/pkg/services/alerting/notifiers/pagerduty_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -15,7 +15,7 @@ func TestPagerdutyNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "pageduty_testing",
Type: "pagerduty",
Settings: settingsJSON,
@@ -29,7 +29,7 @@ func TestPagerdutyNotifier(t *testing.T) {
json := `{ "integrationKey": "abcdefgh0123456789" }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "pagerduty_testing",
Type: "pagerduty",
Settings: settingsJSON,
@@ -53,7 +53,7 @@ func TestPagerdutyNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "pagerduty_testing",
Type: "pagerduty",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/pushover.go b/pkg/services/alerting/notifiers/pushover.go
index 0d23bbf6fb3c..a54fb1ee084d 100644
--- a/pkg/services/alerting/notifiers/pushover.go
+++ b/pkg/services/alerting/notifiers/pushover.go
@@ -10,7 +10,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -95,7 +95,7 @@ func init() {
})
}
-func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewPushoverNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
userKey := model.Settings.Get("userKey").MustString()
apiToken := model.Settings.Get("apiToken").MustString()
device := model.Settings.Get("device").MustString()
@@ -169,7 +169,7 @@ func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
return err
}
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: PUSHOVER_ENDPOINT,
HttpMethod: "POST",
HttpHeader: headers,
@@ -248,7 +248,7 @@ func (this *PushoverNotifier) genPushoverBody(evalContext *alerting.EvalContext,
// Add sound
sound := this.AlertingSound
- if evalContext.Rule.State == m.AlertStateOK {
+ if evalContext.Rule.State == models.AlertStateOK {
sound = this.OkSound
}
if sound != "default" {
diff --git a/pkg/services/alerting/notifiers/pushover_test.go b/pkg/services/alerting/notifiers/pushover_test.go
index 5228491cccd5..f862a500618a 100644
--- a/pkg/services/alerting/notifiers/pushover_test.go
+++ b/pkg/services/alerting/notifiers/pushover_test.go
@@ -2,12 +2,13 @@ package notifiers
import (
"context"
- "github.com/grafana/grafana/pkg/services/alerting"
"strings"
"testing"
+ "github.com/grafana/grafana/pkg/services/alerting"
+
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -19,7 +20,7 @@ func TestPushoverNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "Pushover",
Type: "pushover",
Settings: settingsJSON,
@@ -40,7 +41,7 @@ func TestPushoverNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "Pushover",
Type: "pushover",
Settings: settingsJSON,
@@ -73,7 +74,7 @@ func TestGenPushoverBody(t *testing.T) {
Convey("When alert is firing - should use siren sound", func() {
evalContext := alerting.NewEvalContext(context.Background(),
&alerting.Rule{
- State: m.AlertStateAlerting,
+ State: models.AlertStateAlerting,
})
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
@@ -84,7 +85,7 @@ func TestGenPushoverBody(t *testing.T) {
Convey("When alert is ok - should use success sound", func() {
evalContext := alerting.NewEvalContext(context.Background(),
&alerting.Rule{
- State: m.AlertStateOK,
+ State: models.AlertStateOK,
})
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
diff --git a/pkg/services/alerting/notifiers/sensu.go b/pkg/services/alerting/notifiers/sensu.go
index b018c53208e8..cad9fc2286a3 100644
--- a/pkg/services/alerting/notifiers/sensu.go
+++ b/pkg/services/alerting/notifiers/sensu.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -44,7 +44,7 @@ func init() {
}
-func NewSensuNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewSensuNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -117,7 +117,7 @@ func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
body, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.Url,
User: this.User,
Password: this.Password,
diff --git a/pkg/services/alerting/notifiers/sensu_test.go b/pkg/services/alerting/notifiers/sensu_test.go
index 40e3b1e1cc31..40d39a5d1c3f 100644
--- a/pkg/services/alerting/notifiers/sensu_test.go
+++ b/pkg/services/alerting/notifiers/sensu_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestSensuNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "sensu",
Type: "sensu",
Settings: settingsJSON,
@@ -35,7 +35,7 @@ func TestSensuNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "sensu",
Type: "sensu",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go
index 7754b7de71aa..117674448877 100644
--- a/pkg/services/alerting/notifiers/slack.go
+++ b/pkg/services/alerting/notifiers/slack.go
@@ -11,7 +11,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/setting"
)
@@ -99,7 +99,7 @@ func init() {
}
-func NewSlackNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewSlackNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -171,7 +171,7 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
}
message := this.Mention
- if evalContext.Rule.State != m.AlertStateOK { //don't add message when going back to alert state ok.
+ if evalContext.Rule.State != models.AlertStateOK { //don't add message when going back to alert state ok.
message += " " + evalContext.Rule.Message
}
image_url := ""
@@ -212,7 +212,7 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
body["icon_url"] = this.IconUrl
}
data, _ := json.Marshal(&body)
- cmd := &m.SendWebhookSync{Url: this.Url, Body: string(data)}
+ cmd := &models.SendWebhookSync{Url: this.Url, Body: string(data)}
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
this.log.Error("Failed to send slack notification", "error", err, "webhook", this.Name)
return err
@@ -235,7 +235,7 @@ func SlackFileUpload(evalContext *alerting.EvalContext, log log.Logger, url stri
if err != nil {
return err
}
- cmd := &m.SendWebhookSync{Url: url, Body: uploadBody.String(), HttpHeader: headers, HttpMethod: "POST"}
+ cmd := &models.SendWebhookSync{Url: url, Body: uploadBody.String(), HttpHeader: headers, HttpMethod: "POST"}
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
log.Error("Failed to upload slack image", "error", err, "webhook", "file.upload")
return err
diff --git a/pkg/services/alerting/notifiers/slack_test.go b/pkg/services/alerting/notifiers/slack_test.go
index 17362bc850b6..7dceb12676c9 100644
--- a/pkg/services/alerting/notifiers/slack_test.go
+++ b/pkg/services/alerting/notifiers/slack_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestSlackNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "slack",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestSlackNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "slack",
Settings: settingsJSON,
@@ -67,7 +67,7 @@ func TestSlackNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "slack",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/teams.go b/pkg/services/alerting/notifiers/teams.go
index e33a93f8a0cf..57f5d6e91c04 100644
--- a/pkg/services/alerting/notifiers/teams.go
+++ b/pkg/services/alerting/notifiers/teams.go
@@ -5,7 +5,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -26,7 +26,7 @@ func init() {
}
-func NewTeamsNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewTeamsNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -74,7 +74,7 @@ func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
}
message := ""
- if evalContext.Rule.State != m.AlertStateOK { //don't add message when going back to alert state ok.
+ if evalContext.Rule.State != models.AlertStateOK { //don't add message when going back to alert state ok.
message = evalContext.Rule.Message
}
@@ -126,7 +126,7 @@ func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
}
data, _ := json.Marshal(&body)
- cmd := &m.SendWebhookSync{Url: this.Url, Body: string(data)}
+ cmd := &models.SendWebhookSync{Url: this.Url, Body: string(data)}
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
this.log.Error("Failed to send teams notification", "error", err, "webhook", this.Name)
diff --git a/pkg/services/alerting/notifiers/teams_test.go b/pkg/services/alerting/notifiers/teams_test.go
index a96473507363..1dd35c899605 100644
--- a/pkg/services/alerting/notifiers/teams_test.go
+++ b/pkg/services/alerting/notifiers/teams_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestTeamsNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "teams",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestTeamsNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "teams",
Settings: settingsJSON,
@@ -55,7 +55,7 @@ func TestTeamsNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "teams",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/telegram.go b/pkg/services/alerting/notifiers/telegram.go
index a5876b77b228..741c0fe732c5 100644
--- a/pkg/services/alerting/notifiers/telegram.go
+++ b/pkg/services/alerting/notifiers/telegram.go
@@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -60,7 +60,7 @@ type TelegramNotifier struct {
log log.Logger
}
-func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewTelegramNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
if model.Settings == nil {
return nil, alerting.ValidationError{Reason: "No Settings Supplied"}
}
@@ -86,7 +86,7 @@ func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error)
}, nil
}
-func (this *TelegramNotifier) buildMessage(evalContext *alerting.EvalContext, sendImageInline bool) *m.SendWebhookSync {
+func (this *TelegramNotifier) buildMessage(evalContext *alerting.EvalContext, sendImageInline bool) *models.SendWebhookSync {
if sendImageInline {
cmd, err := this.buildMessageInlineImage(evalContext)
if err == nil {
@@ -98,7 +98,7 @@ func (this *TelegramNotifier) buildMessage(evalContext *alerting.EvalContext, se
return this.buildMessageLinkedImage(evalContext)
}
-func (this *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.EvalContext) *m.SendWebhookSync {
+func (this *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.EvalContext) *models.SendWebhookSync {
message := fmt.Sprintf("%s\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
ruleUrl, err := evalContext.GetRuleUrl()
@@ -122,7 +122,7 @@ func (this *TelegramNotifier) buildMessageLinkedImage(evalContext *alerting.Eval
return cmd
}
-func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.EvalContext) (*m.SendWebhookSync, error) {
+func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.EvalContext) (*models.SendWebhookSync, error) {
var imageFile *os.File
var err error
@@ -153,7 +153,7 @@ func (this *TelegramNotifier) buildMessageInlineImage(evalContext *alerting.Eval
return cmd, nil
}
-func (this *TelegramNotifier) generateTelegramCmd(message string, messageField string, apiAction string, extraConf func(writer *multipart.Writer)) *m.SendWebhookSync {
+func (this *TelegramNotifier) generateTelegramCmd(message string, messageField string, apiAction string, extraConf func(writer *multipart.Writer)) *models.SendWebhookSync {
var body bytes.Buffer
w := multipart.NewWriter(&body)
@@ -170,7 +170,7 @@ func (this *TelegramNotifier) generateTelegramCmd(message string, messageField s
this.log.Info("Sending telegram notification", "chat_id", this.ChatID, "bot_token", this.BotToken, "apiAction", apiAction)
url := fmt.Sprintf(telegramApiUrl, this.BotToken, apiAction)
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: url,
Body: body.String(),
HttpMethod: "POST",
@@ -227,7 +227,7 @@ func appendIfPossible(message string, extra string, sizeLimit int) string {
}
func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
- var cmd *m.SendWebhookSync
+ var cmd *models.SendWebhookSync
if evalContext.ImagePublicUrl == "" && this.UploadImage {
cmd = this.buildMessage(evalContext, true)
} else {
diff --git a/pkg/services/alerting/notifiers/telegram_test.go b/pkg/services/alerting/notifiers/telegram_test.go
index 9906a2ffd957..3559555439e5 100644
--- a/pkg/services/alerting/notifiers/telegram_test.go
+++ b/pkg/services/alerting/notifiers/telegram_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
)
@@ -18,7 +18,7 @@ func TestTelegramNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "telegram_testing",
Type: "telegram",
Settings: settingsJSON,
@@ -36,7 +36,7 @@ func TestTelegramNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "telegram_testing",
Type: "telegram",
Settings: settingsJSON,
@@ -57,7 +57,7 @@ func TestTelegramNotifier(t *testing.T) {
&alerting.Rule{
Name: "This is an alarm",
Message: "Some kind of message.",
- State: m.AlertStateOK,
+ State: models.AlertStateOK,
})
caption := generateImageCaption(evalContext, "http://grafa.url/abcdef", "")
@@ -74,7 +74,7 @@ func TestTelegramNotifier(t *testing.T) {
&alerting.Rule{
Name: "This is an alarm",
Message: "Some kind of message.",
- State: m.AlertStateOK,
+ State: models.AlertStateOK,
})
caption := generateImageCaption(evalContext,
@@ -92,7 +92,7 @@ func TestTelegramNotifier(t *testing.T) {
&alerting.Rule{
Name: "This is an alarm",
Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis scelerisque. Nulla ipsum ex, iaculis vitae vehicula sit amet, fermentum eu eros.",
- State: m.AlertStateOK,
+ State: models.AlertStateOK,
})
caption := generateImageCaption(evalContext,
@@ -109,7 +109,7 @@ func TestTelegramNotifier(t *testing.T) {
&alerting.Rule{
Name: "This is an alarm",
Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis sceleri",
- State: m.AlertStateOK,
+ State: models.AlertStateOK,
})
caption := generateImageCaption(evalContext,
diff --git a/pkg/services/alerting/notifiers/threema.go b/pkg/services/alerting/notifiers/threema.go
index 2360073d2281..6e4aa7bc946e 100644
--- a/pkg/services/alerting/notifiers/threema.go
+++ b/pkg/services/alerting/notifiers/threema.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -76,7 +76,7 @@ type ThreemaNotifier struct {
log log.Logger
}
-func NewThreemaNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewThreemaNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
if model.Settings == nil {
return nil, alerting.ValidationError{Reason: "No Settings Supplied"}
}
@@ -127,11 +127,11 @@ func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error
// Determine emoji
stateEmoji := ""
switch evalContext.Rule.State {
- case m.AlertStateOK:
+ case models.AlertStateOK:
stateEmoji = "\u2705 " // White Heavy Check Mark
- case m.AlertStateNoData:
+ case models.AlertStateNoData:
stateEmoji = "\u2753 " // Black Question Mark Ornament
- case m.AlertStateAlerting:
+ case models.AlertStateAlerting:
stateEmoji = "\u26A0 " // Warning sign
}
@@ -154,7 +154,7 @@ func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error
headers := map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
}
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: url,
Body: body,
HttpMethod: "POST",
diff --git a/pkg/services/alerting/notifiers/threema_test.go b/pkg/services/alerting/notifiers/threema_test.go
index 3f23730a249a..2c50b7d2058c 100644
--- a/pkg/services/alerting/notifiers/threema_test.go
+++ b/pkg/services/alerting/notifiers/threema_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
)
@@ -17,7 +17,7 @@ func TestThreemaNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "threema_testing",
Type: "threema",
Settings: settingsJSON,
@@ -36,7 +36,7 @@ func TestThreemaNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "threema_testing",
Type: "threema",
Settings: settingsJSON,
@@ -63,7 +63,7 @@ func TestThreemaNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "threema_testing",
Type: "threema",
Settings: settingsJSON,
@@ -83,7 +83,7 @@ func TestThreemaNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "threema_testing",
Type: "threema",
Settings: settingsJSON,
@@ -103,7 +103,7 @@ func TestThreemaNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "threema_testing",
Type: "threema",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/victorops_test.go b/pkg/services/alerting/notifiers/victorops_test.go
index 6ac806a82cc5..258d2900a22b 100644
--- a/pkg/services/alerting/notifiers/victorops_test.go
+++ b/pkg/services/alerting/notifiers/victorops_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestVictoropsNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "victorops_testing",
Type: "victorops",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestVictoropsNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "victorops_testing",
Type: "victorops",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/notifiers/webhook.go b/pkg/services/alerting/notifiers/webhook.go
index 7c582a41aeb8..31b5f7a82080 100644
--- a/pkg/services/alerting/notifiers/webhook.go
+++ b/pkg/services/alerting/notifiers/webhook.go
@@ -4,7 +4,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
)
@@ -40,7 +40,7 @@ func init() {
}
-func NewWebHookNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+func NewWebHookNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
@@ -90,7 +90,7 @@ func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
body, _ := bodyJSON.MarshalJSON()
- cmd := &m.SendWebhookSync{
+ cmd := &models.SendWebhookSync{
Url: this.Url,
User: this.User,
Password: this.Password,
diff --git a/pkg/services/alerting/notifiers/webhook_test.go b/pkg/services/alerting/notifiers/webhook_test.go
index b2d944eb6e94..af48f1f1aa6d 100644
--- a/pkg/services/alerting/notifiers/webhook_test.go
+++ b/pkg/services/alerting/notifiers/webhook_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
)
@@ -16,7 +16,7 @@ func TestWebhookNotifier(t *testing.T) {
json := `{ }`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "webhook",
Settings: settingsJSON,
@@ -33,7 +33,7 @@ func TestWebhookNotifier(t *testing.T) {
}`
settingsJSON, _ := simplejson.NewJson([]byte(json))
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: "ops",
Type: "webhook",
Settings: settingsJSON,
diff --git a/pkg/services/alerting/reader.go b/pkg/services/alerting/reader.go
index 3f033a746f27..0df826e9ea5d 100644
--- a/pkg/services/alerting/reader.go
+++ b/pkg/services/alerting/reader.go
@@ -7,7 +7,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
)
type RuleReader interface {
@@ -16,7 +16,6 @@ type RuleReader interface {
type DefaultRuleReader struct {
sync.RWMutex
- //serverID string
serverPosition int
clusterSize int
log log.Logger
@@ -40,7 +39,7 @@ func (arr *DefaultRuleReader) initReader() {
}
func (arr *DefaultRuleReader) Fetch() []*Rule {
- cmd := &m.GetAllAlertsQuery{}
+ cmd := &models.GetAllAlertsQuery{}
if err := bus.Dispatch(cmd); err != nil {
arr.log.Error("Could not load alerts", "error", err)
diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go
index 6f2669ff41a9..d82421d7506d 100644
--- a/pkg/services/alerting/result_handler.go
+++ b/pkg/services/alerting/result_handler.go
@@ -7,7 +7,8 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
+
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/rendering"
)
@@ -47,7 +48,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
if evalContext.ShouldUpdateAlertState() {
handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "prev state", evalContext.PrevAlertState)
- cmd := &m.SetAlertStateCommand{
+ cmd := &models.SetAlertStateCommand{
AlertId: evalContext.Rule.Id,
OrgId: evalContext.Rule.OrgId,
State: evalContext.Rule.State,
@@ -56,12 +57,12 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
}
if err := bus.Dispatch(cmd); err != nil {
- if err == m.ErrCannotChangeStateOnPausedAlert {
+ if err == models.ErrCannotChangeStateOnPausedAlert {
handler.log.Error("Cannot change state on alert that's paused", "error", err)
return err
}
- if err == m.ErrRequiresNewState {
+ if err == models.ErrRequiresNewState {
handler.log.Info("Alert already updated")
return nil
}
diff --git a/pkg/services/alerting/rule.go b/pkg/services/alerting/rule.go
index f62690a2d960..b5b6f4660e64 100644
--- a/pkg/services/alerting/rule.go
+++ b/pkg/services/alerting/rule.go
@@ -8,7 +8,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
)
var (
@@ -26,9 +26,9 @@ type Rule struct {
Message string
LastStateChange time.Time
For time.Duration
- NoDataState m.NoDataOption
- ExecutionErrorState m.ExecutionErrorOption
- State m.AlertStateType
+ NoDataState models.NoDataOption
+ ExecutionErrorState models.ExecutionErrorOption
+ State models.AlertStateType
Conditions []Condition
Notifications []string
@@ -103,7 +103,7 @@ func getTimeDurationStringToSeconds(str string) (int64, error) {
return int64(value * multiplier), nil
}
-func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) {
+func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) {
model := &Rule{}
model.Id = ruleDef.Id
model.OrgId = ruleDef.OrgId
@@ -114,8 +114,8 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) {
model.State = ruleDef.State
model.LastStateChange = ruleDef.NewStateDate
model.For = ruleDef.For
- model.NoDataState = m.NoDataOption(ruleDef.Settings.Get("noDataState").MustString("no_data"))
- model.ExecutionErrorState = m.ExecutionErrorOption(ruleDef.Settings.Get("executionErrorState").MustString("alerting"))
+ model.NoDataState = models.NoDataOption(ruleDef.Settings.Get("noDataState").MustString("no_data"))
+ model.ExecutionErrorState = models.ExecutionErrorOption(ruleDef.Settings.Get("executionErrorState").MustString("alerting"))
model.StateChanges = ruleDef.StateChanges
model.Frequency = ruleDef.Frequency
diff --git a/pkg/services/alerting/rule_test.go b/pkg/services/alerting/rule_test.go
index ca533c36210f..853fb5d17b5c 100644
--- a/pkg/services/alerting/rule_test.go
+++ b/pkg/services/alerting/rule_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
. "github.com/smartystreets/goconvey/convey"
)
@@ -60,10 +60,10 @@ func TestAlertRuleModel(t *testing.T) {
})
Convey("can construct alert rule model", func() {
- firstNotification := m.CreateAlertNotificationCommand{OrgId: 1, Name: "1"}
+ firstNotification := models.CreateAlertNotificationCommand{OrgId: 1, Name: "1"}
err := sqlstore.CreateAlertNotificationCommand(&firstNotification)
So(err, ShouldBeNil)
- secondNotification := m.CreateAlertNotificationCommand{Uid: "notifier2", OrgId: 1, Name: "2"}
+ secondNotification := models.CreateAlertNotificationCommand{Uid: "notifier2", OrgId: 1, Name: "2"}
err = sqlstore.CreateAlertNotificationCommand(&secondNotification)
So(err, ShouldBeNil)
@@ -92,7 +92,7 @@ func TestAlertRuleModel(t *testing.T) {
alertJSON, jsonErr := simplejson.NewJson([]byte(json))
So(jsonErr, ShouldBeNil)
- alert := &m.Alert{
+ alert := &models.Alert{
Id: 1,
OrgId: 1,
DashboardId: 1,
@@ -129,7 +129,7 @@ func TestAlertRuleModel(t *testing.T) {
alertJSON, jsonErr := simplejson.NewJson([]byte(json))
So(jsonErr, ShouldBeNil)
- alert := &m.Alert{
+ alert := &models.Alert{
Id: 1,
OrgId: 1,
DashboardId: 1,
@@ -167,7 +167,7 @@ func TestAlertRuleModel(t *testing.T) {
alertJSON, jsonErr := simplejson.NewJson([]byte(json))
So(jsonErr, ShouldBeNil)
- alert := &m.Alert{
+ alert := &models.Alert{
Id: 1,
OrgId: 1,
DashboardId: 1,
diff --git a/pkg/services/alerting/test_notification.go b/pkg/services/alerting/test_notification.go
index fbb1633d4a5b..5cb18d2b42ee 100644
--- a/pkg/services/alerting/test_notification.go
+++ b/pkg/services/alerting/test_notification.go
@@ -8,11 +8,11 @@ import (
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
)
type NotificationTestCommand struct {
- State m.AlertStateType
+ State models.AlertStateType
Name string
Type string
Settings *simplejson.Json
@@ -29,7 +29,7 @@ func init() {
func handleNotificationTestCommand(cmd *NotificationTestCommand) error {
notifier := NewNotificationService(nil).(*notificationService)
- model := &m.AlertNotification{
+ model := &models.AlertNotification{
Name: cmd.Name,
Type: cmd.Type,
Settings: cmd.Settings,
@@ -51,7 +51,7 @@ func createTestEvalContext(cmd *NotificationTestCommand) *EvalContext {
PanelId: 1,
Name: "Test notification",
Message: "Someone is testing the alert notification within grafana.",
- State: m.AlertStateAlerting,
+ State: models.AlertStateAlerting,
}
ctx := NewEvalContext(context.Background(), testRule)
diff --git a/pkg/services/alerting/test_rule.go b/pkg/services/alerting/test_rule.go
index 360ee065de0f..736dd287dbec 100644
--- a/pkg/services/alerting/test_rule.go
+++ b/pkg/services/alerting/test_rule.go
@@ -6,14 +6,14 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
)
type AlertTestCommand struct {
Dashboard *simplejson.Json
PanelId int64
OrgId int64
- User *m.SignedInUser
+ User *models.SignedInUser
Result *EvalContext
}
@@ -24,7 +24,7 @@ func init() {
func handleAlertTestCommand(cmd *AlertTestCommand) error {
- dash := m.NewDashboardFromJson(cmd.Dashboard)
+ dash := models.NewDashboardFromJson(cmd.Dashboard)
extractor := NewDashAlertExtractor(dash, cmd.OrgId, cmd.User)
alerts, err := extractor.GetAlerts()
From 34f9b3ff2b4a14121a4f2429d3c6d70e0e2026ac Mon Sep 17 00:00:00 2001
From: Dominik Prokop
Date: Tue, 14 May 2019 08:46:35 +0200
Subject: [PATCH 020/166] Explore: use @grafana/ui legend (#17027)
---
.../src/components/Graph/GraphLegend.tsx | 8 +-
.../src/components/Graph/GraphLegendItem.tsx | 35 +-
.../src/components/Legend/LegendList.tsx | 2 +-
.../components/Legend/LegendSeriesIcon.tsx | 32 +-
.../src/components/Legend/SeriesIcon.tsx | 9 +-
packages/grafana-ui/src/components/index.ts | 12 +-
public/app/features/explore/Graph.tsx | 108 +-
public/app/features/explore/Legend.tsx | 66 -
.../explore/__snapshots__/Graph.test.tsx.snap | 1150 ++++-------------
9 files changed, 390 insertions(+), 1032 deletions(-)
delete mode 100644 public/app/features/explore/Legend.tsx
diff --git a/packages/grafana-ui/src/components/Graph/GraphLegend.tsx b/packages/grafana-ui/src/components/Graph/GraphLegend.tsx
index 451acf4bac80..5b7a0251e052 100644
--- a/packages/grafana-ui/src/components/Graph/GraphLegend.tsx
+++ b/packages/grafana-ui/src/components/Graph/GraphLegend.tsx
@@ -14,10 +14,10 @@ interface GraphLegendProps extends LegendProps {
displayMode: LegendDisplayMode;
sortBy?: string;
sortDesc?: boolean;
- onSeriesColorChange: SeriesColorChangeHandler;
+ onSeriesColorChange?: SeriesColorChangeHandler;
onSeriesAxisToggle?: SeriesAxisToggleHandler;
- onToggleSort: (sortBy: string) => void;
- onLabelClick: (item: LegendItem, event: React.MouseEvent) => void;
+ onToggleSort?: (sortBy: string) => void;
+ onLabelClick?: (item: LegendItem, event: React.MouseEvent) => void;
}
export const GraphLegend: React.FunctionComponent = ({
@@ -116,3 +116,5 @@ export const GraphLegend: React.FunctionComponent = ({
/>
);
};
+
+GraphLegend.displayName = 'GraphLegend';
diff --git a/packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx b/packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx
index e116287d4d21..37371fe06644 100644
--- a/packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx
+++ b/packages/grafana-ui/src/components/Graph/GraphLegendItem.tsx
@@ -10,9 +10,9 @@ export interface GraphLegendItemProps {
key?: React.Key;
item: LegendItem;
className?: string;
- onLabelClick: (item: LegendItem, event: React.MouseEvent) => void;
- onSeriesColorChange: SeriesColorChangeHandler;
- onToggleAxis: () => void;
+ onLabelClick?: (item: LegendItem, event: React.MouseEvent) => void;
+ onSeriesColorChange?: SeriesColorChangeHandler;
+ onToggleAxis?: () => void;
}
export const GraphLegendListItem: React.FunctionComponent = ({
@@ -21,19 +21,31 @@ export const GraphLegendListItem: React.FunctionComponent
onToggleAxis,
onLabelClick,
}) => {
+ const theme = useContext(ThemeContext);
+
return (
<>
onSeriesColorChange(item.label, color)}
+ onColorChange={color => {
+ if (onSeriesColorChange) {
+ onSeriesColorChange(item.label, color);
+ }
+ }}
onToggleAxis={onToggleAxis}
yAxis={item.yAxis}
/>
onLabelClick(item, event)}
+ onClick={event => {
+ if (onLabelClick) {
+ onLabelClick(item, event);
+ }
+ }}
className={css`
cursor: pointer;
white-space: nowrap;
+ color: ${!item.isVisible && theme.colors.linkDisabled};
`}
>
{item.label}
@@ -74,13 +86,22 @@ export const GraphLegendTableRow: React.FunctionComponent
`}
>
onSeriesColorChange(item.label, color)}
+ onColorChange={color => {
+ if (onSeriesColorChange) {
+ onSeriesColorChange(item.label, color);
+ }
+ }}
onToggleAxis={onToggleAxis}
yAxis={item.yAxis}
/>
onLabelClick(item, event)}
+ onClick={event => {
+ if (onLabelClick) {
+ onLabelClick(item, event);
+ }
+ }}
className={css`
cursor: pointer;
white-space: nowrap;
diff --git a/packages/grafana-ui/src/components/Legend/LegendList.tsx b/packages/grafana-ui/src/components/Legend/LegendList.tsx
index d103aa3ed806..f220b10a5876 100644
--- a/packages/grafana-ui/src/components/Legend/LegendList.tsx
+++ b/packages/grafana-ui/src/components/Legend/LegendList.tsx
@@ -28,7 +28,7 @@ export const LegendList: React.FunctionComponent = ({
);
};
- const getItemKey = (item: LegendItem) => item.label;
+ const getItemKey = (item: LegendItem) => `${item.label}`;
const styles = {
wrapper: cx(
diff --git a/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx b/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
index 1913c2e500d2..787b818c0030 100644
--- a/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
+++ b/packages/grafana-ui/src/components/Legend/LegendSeriesIcon.tsx
@@ -1,8 +1,10 @@
import React from 'react';
+import { css, cx } from 'emotion';
import { SeriesColorPicker } from '../ColorPicker/ColorPicker';
-import { SeriesIcon } from './SeriesIcon';
+import { SeriesIcon, SeriesIconProps } from './SeriesIcon';
interface LegendSeriesIconProps {
+ disabled: boolean;
color: string;
yAxis: number;
onColorChange: (color: string) => void;
@@ -10,12 +12,36 @@ interface LegendSeriesIconProps {
}
export const LegendSeriesIcon: React.FunctionComponent = ({
+ disabled,
yAxis,
color,
onColorChange,
onToggleAxis,
}) => {
- return (
+ let iconProps: SeriesIconProps = {
+ color,
+ };
+
+ if (!disabled) {
+ iconProps = {
+ ...iconProps,
+ className: 'pointer',
+ };
+ }
+
+ return disabled ? (
+
+
+
+ ) : (
=
>
{({ ref, showColorPicker, hideColorPicker }) => (
-
+
)}
diff --git a/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx b/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
index 091d79f7fd0d..70709a0fcd5c 100644
--- a/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
+++ b/packages/grafana-ui/src/components/Legend/SeriesIcon.tsx
@@ -1,5 +1,10 @@
import React from 'react';
+import { cx } from 'emotion';
-export const SeriesIcon: React.FunctionComponent<{ color: string }> = ({ color }) => {
- return ;
+export interface SeriesIconProps {
+ color: string;
+ className?: string;
+}
+export const SeriesIcon: React.FunctionComponent = ({ color, className }) => {
+ return ;
};
diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts
index 5a4f58626c6a..860dd5a97ab0 100644
--- a/packages/grafana-ui/src/components/index.ts
+++ b/packages/grafana-ui/src/components/index.ts
@@ -45,10 +45,20 @@ export { TableInputCSV } from './Table/TableInputCSV';
export { BigValue } from './BigValue/BigValue';
export { Gauge } from './Gauge/Gauge';
export { Graph } from './Graph/Graph';
+export { GraphLegend } from './Graph/GraphLegend';
export { GraphWithLegend } from './Graph/GraphWithLegend';
export { BarGauge } from './BarGauge/BarGauge';
export { VizRepeater } from './VizRepeater/VizRepeater';
-export { LegendOptions, LegendBasicOptions, LegendRenderOptions, LegendList, LegendTable } from './Legend/Legend';
+export {
+ LegendOptions,
+ LegendBasicOptions,
+ LegendRenderOptions,
+ LegendList,
+ LegendTable,
+ LegendItem,
+ LegendPlacement,
+ LegendDisplayMode,
+} from './Legend/Legend';
// Panel editors
export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor';
export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper';
diff --git a/public/app/features/explore/Graph.tsx b/public/app/features/explore/Graph.tsx
index f9c48fc92c72..b5cdca318afa 100644
--- a/public/app/features/explore/Graph.tsx
+++ b/public/app/features/explore/Graph.tsx
@@ -1,17 +1,15 @@
import $ from 'jquery';
import React, { PureComponent } from 'react';
+import difference from 'lodash/difference';
import 'vendor/flot/jquery.flot';
import 'vendor/flot/jquery.flot.time';
import 'vendor/flot/jquery.flot.selection';
import 'vendor/flot/jquery.flot.stack';
-import { TimeZone, AbsoluteTimeRange } from '@grafana/ui';
+import { TimeZone, AbsoluteTimeRange, GraphLegend, LegendItem, LegendDisplayMode } from '@grafana/ui';
import TimeSeries from 'app/core/time_series2';
-import Legend from './Legend';
-import { equal, intersect } from './utils/set';
-
const MAX_NUMBER_OF_TIME_SERIES = 20;
// Copied from graph.ts
@@ -89,7 +87,7 @@ interface GraphState {
* Type parameter refers to the `alias` property of a `TimeSeries`.
* Consequently, all series sharing the same alias will share visibility state.
*/
- hiddenSeries: Set;
+ hiddenSeries: string[];
showAllTimeSeries: boolean;
}
@@ -98,11 +96,11 @@ export class Graph extends PureComponent {
dynamicOptions = null;
state = {
- hiddenSeries: new Set(),
+ hiddenSeries: [],
showAllTimeSeries: false,
};
- getGraphData() {
+ getGraphData(): TimeSeries[] {
const { data } = this.props;
return this.state.showAllTimeSeries ? data : data.slice(0, MAX_NUMBER_OF_TIME_SERIES);
@@ -121,7 +119,7 @@ export class Graph extends PureComponent {
prevProps.split !== this.props.split ||
prevProps.height !== this.props.height ||
prevProps.width !== this.props.width ||
- !equal(prevState.hiddenSeries, this.state.hiddenSeries)
+ prevState.hiddenSeries !== this.state.hiddenSeries
) {
this.draw();
}
@@ -168,38 +166,6 @@ export class Graph extends PureComponent {
);
};
- onToggleSeries = (series: TimeSeries, exclusive: boolean) => {
- this.setState((state, props) => {
- const { data, onToggleSeries } = props;
- const { hiddenSeries } = state;
-
- // Deduplicate series as visibility tracks the alias property
- const oneSeriesVisible = hiddenSeries.size === new Set(data.map(d => d.alias)).size - 1;
-
- let nextHiddenSeries = new Set();
- if (exclusive) {
- if (hiddenSeries.has(series.alias) || !oneSeriesVisible) {
- nextHiddenSeries = new Set(data.filter(d => d.alias !== series.alias).map(d => d.alias));
- }
- } else {
- // Prune hidden series no longer part of those available from the most recent query
- const availableSeries = new Set(data.map(d => d.alias));
- nextHiddenSeries = intersect(new Set(hiddenSeries), availableSeries);
- if (nextHiddenSeries.has(series.alias)) {
- nextHiddenSeries.delete(series.alias);
- } else {
- nextHiddenSeries.add(series.alias);
- }
- }
- if (onToggleSeries) {
- onToggleSeries(series.alias, nextHiddenSeries);
- }
- return {
- hiddenSeries: nextHiddenSeries,
- };
- }, this.draw);
- };
-
draw() {
const { userOptions = {} } = this.props;
const { hiddenSeries } = this.state;
@@ -210,7 +176,7 @@ export class Graph extends PureComponent {
if (data && data.length > 0) {
series = data
- .filter((ts: TimeSeries) => !hiddenSeries.has(ts.alias))
+ .filter((ts: TimeSeries) => hiddenSeries.indexOf(ts.alias) === -1)
.map((ts: TimeSeries) => ({
color: ts.color,
label: ts.label,
@@ -229,11 +195,57 @@ export class Graph extends PureComponent {
$.plot($el, series, options);
}
- render() {
- const { height = 100, id = 'graph' } = this.props;
+ getLegendItems = (): LegendItem[] => {
const { hiddenSeries } = this.state;
const data = this.getGraphData();
+ return data.map(series => {
+ return {
+ label: series.alias,
+ color: series.color,
+ isVisible: hiddenSeries.indexOf(series.alias) === -1,
+ yAxis: 1,
+ };
+ });
+ };
+
+ onSeriesToggle(label: string, event: React.MouseEvent) {
+ // This implementation is more or less a copy of GraphPanel's logic.
+ // TODO: we need to use Graph's panel controller or split it into smaller
+ // controllers to remove code duplication. Right now we cant easily use that, since Explore
+ // is not using SeriesData for graph yet
+
+ const exclusive = event.ctrlKey || event.metaKey || event.shiftKey;
+
+ this.setState((state, props) => {
+ const { data } = props;
+ let nextHiddenSeries = [];
+ if (exclusive) {
+ // Toggling series with key makes the series itself to toggle
+ if (state.hiddenSeries.indexOf(label) > -1) {
+ nextHiddenSeries = state.hiddenSeries.filter(series => series !== label);
+ } else {
+ nextHiddenSeries = state.hiddenSeries.concat([label]);
+ }
+ } else {
+ // Toggling series with out key toggles all the series but the clicked one
+ const allSeriesLabels = data.map(series => series.label);
+
+ if (state.hiddenSeries.length + 1 === allSeriesLabels.length) {
+ nextHiddenSeries = [];
+ } else {
+ nextHiddenSeries = difference(allSeriesLabels, [label]);
+ }
+ }
+
+ return {
+ hiddenSeries: nextHiddenSeries,
+ };
+ });
+ }
+
+ render() {
+ const { height = 100, id = 'graph' } = this.props;
return (
<>
{this.props.data && this.props.data.length > MAX_NUMBER_OF_TIME_SERIES && !this.state.showAllTimeSeries && (
@@ -246,7 +258,15 @@ export class Graph extends PureComponent {
)}
-
+
+ {
+ this.onSeriesToggle(item.label, event);
+ }}
+ />
>
);
}
diff --git a/public/app/features/explore/Legend.tsx b/public/app/features/explore/Legend.tsx
deleted file mode 100644
index 3b67aa74d917..000000000000
--- a/public/app/features/explore/Legend.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React, { MouseEvent, PureComponent } from 'react';
-import classNames from 'classnames';
-import { TimeSeries } from 'app/core/core';
-
-interface LegendProps {
- data: TimeSeries[];
- hiddenSeries: Set;
- onToggleSeries?: (series: TimeSeries, exclusive: boolean) => void;
-}
-
-interface LegendItemProps {
- hidden: boolean;
- onClickLabel?: (series: TimeSeries, event: MouseEvent) => void;
- series: TimeSeries;
-}
-
-class LegendItem extends PureComponent {
- onClickLabel = e => this.props.onClickLabel(this.props.series, e);
-
- render() {
- const { hidden, series } = this.props;
- const seriesClasses = classNames({
- 'graph-legend-series-hidden': hidden,
- });
- return (
-
- );
- }
-}
-
-export default class Legend extends PureComponent {
- static defaultProps = {
- onToggleSeries: () => {},
- };
-
- onClickLabel = (series: TimeSeries, event: MouseEvent) => {
- const { onToggleSeries } = this.props;
- const exclusive = event.ctrlKey || event.metaKey || event.shiftKey;
- onToggleSeries(series, !exclusive);
- };
-
- render() {
- const { data, hiddenSeries } = this.props;
- const items = data || [];
- return (
-
- {items.map((series, i) => (
-
- ))}
-
- );
- }
-}
diff --git a/public/app/features/explore/__snapshots__/Graph.test.tsx.snap b/public/app/features/explore/__snapshots__/Graph.test.tsx.snap
index c38fb26a2523..d43f94856055 100644
--- a/public/app/features/explore/__snapshots__/Graph.test.tsx.snap
+++ b/public/app/features/explore/__snapshots__/Graph.test.tsx.snap
@@ -11,450 +11,128 @@ exports[`Render should render component 1`] = `
}
}
/>
-
`;
@@ -484,473 +162,134 @@ exports[`Render should render component with disclaimer 1`] = `
}
}
/>
-
`;
@@ -966,10 +305,11 @@ exports[`Render should show query return no time series 1`] = `
}
}
/>
-
`;
From 79ac3fd699476328e538d4d1f73708fc0fc66715 Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Tue, 14 May 2019 10:18:28 +0300
Subject: [PATCH 021/166] Chore: remove use of `== false` (#17036)
Interestingly enough, golint or revive doesn't not prohibit
the use that construction :)
Ref #17035
---
pkg/middleware/auth_proxy.go | 6 +++---
pkg/middleware/auth_proxy/auth_proxy.go | 2 +-
pkg/services/ldap/settings.go | 7 ++++---
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/pkg/middleware/auth_proxy.go b/pkg/middleware/auth_proxy.go
index 6dc69dda8b5f..d3d8d8e77d8a 100644
--- a/pkg/middleware/auth_proxy.go
+++ b/pkg/middleware/auth_proxy.go
@@ -20,17 +20,17 @@ func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext,
})
// Bail if auth proxy is not enabled
- if auth.IsEnabled() == false {
+ if !auth.IsEnabled() {
return false
}
// If the there is no header - we can't move forward
- if auth.HasHeader() == false {
+ if !auth.HasHeader() {
return false
}
// Check if allowed to continue with this IP
- if result, err := auth.IsAllowedIP(); result == false {
+ if result, err := auth.IsAllowedIP(); !result {
ctx.Handle(407, err.Error(), err.DetailsError)
return true
}
diff --git a/pkg/middleware/auth_proxy/auth_proxy.go b/pkg/middleware/auth_proxy/auth_proxy.go
index b9e71d1b480d..98bacbeccf47 100644
--- a/pkg/middleware/auth_proxy/auth_proxy.go
+++ b/pkg/middleware/auth_proxy/auth_proxy.go
@@ -92,7 +92,7 @@ func New(options *Options) *AuthProxy {
func (auth *AuthProxy) IsEnabled() bool {
// Bail if the setting is not enabled
- if auth.enabled == false {
+ if !auth.enabled {
return false
}
diff --git a/pkg/services/ldap/settings.go b/pkg/services/ldap/settings.go
index de2da2402bfe..0a0f66d9d734 100644
--- a/pkg/services/ldap/settings.go
+++ b/pkg/services/ldap/settings.go
@@ -5,12 +5,12 @@ import (
"sync"
"github.com/BurntSushi/toml"
- "github.com/grafana/grafana/pkg/util/errutil"
"golang.org/x/xerrors"
"github.com/grafana/grafana/pkg/infra/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
+ "github.com/grafana/grafana/pkg/util/errutil"
)
type Config struct {
@@ -68,9 +68,10 @@ func IsEnabled() bool {
// ReloadConfig reads the config from the disc and caches it.
func ReloadConfig() error {
- if IsEnabled() == false {
+ if !IsEnabled() {
return nil
}
+
loadingMutex.Lock()
defer loadingMutex.Unlock()
@@ -82,7 +83,7 @@ func ReloadConfig() error {
// GetConfig returns the LDAP config if LDAP is enabled otherwise it returns nil. It returns either cached value of
// the config or it reads it and caches it first.
func GetConfig() (*Config, error) {
- if IsEnabled() == false {
+ if !IsEnabled() {
return nil, nil
}
From 51c99fc68d5830a27b5509665dc2b42b88ab00b3 Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Tue, 14 May 2019 10:30:05 +0200
Subject: [PATCH 022/166] Docs: adds note about removing session storage
(#17003)
closes #17000
---
docs/sources/installation/upgrading.md | 6 ++++++
docs/sources/tutorials/ha_setup.md | 3 ++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/docs/sources/installation/upgrading.md b/docs/sources/installation/upgrading.md
index bd2a5434b25f..22195cb9df50 100644
--- a/docs/sources/installation/upgrading.md
+++ b/docs/sources/installation/upgrading.md
@@ -169,3 +169,9 @@ configuration.
If you're embedding Grafana in a ``, `
From 74a31bd9b47f3c8663b08d9d164aa9d41393e4b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 14 May 2019 14:41:24 +0200
Subject: [PATCH 026/166] Dashboard: Fixes lazy loading & expanding collapsed
rows on mobile (#17055)
* Dashboard: Fixes lazy loading & expanding collapsing rows on mobile
Fixes #16978
* Updated dashboard tags
---
.../new_features_in_v62.json | 1337 +++++++++++++++++
.../dashboard/dashgrid/DashboardGrid.test.tsx | 90 ++
.../dashboard/dashgrid/DashboardGrid.tsx | 2 +-
.../dashgrid/DashboardGridDirective.ts | 4 -
.../__snapshots__/DashboardGrid.test.tsx.snap | 996 ++++++++++++
public/app/features/dashboard/index.ts | 2 -
6 files changed, 2424 insertions(+), 7 deletions(-)
create mode 100644 devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json
create mode 100644 public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx
delete mode 100644 public/app/features/dashboard/dashgrid/DashboardGridDirective.ts
create mode 100644 public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap
diff --git a/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json b/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json
new file mode 100644
index 000000000000..f3844bea1f39
--- /dev/null
+++ b/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json
@@ -0,0 +1,1337 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "links": [],
+ "panels": [
+ {
+ "content": "# v6.2 Feature Demos \n\nScroll down to view all demo panels. \n",
+ "gridPos": {
+ "h": 3,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 13,
+ "links": [],
+ "mode": "markdown",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "transparent": true,
+ "type": "text"
+ },
+ {
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 3
+ },
+ "id": 15,
+ "title": "Bar Gauge",
+ "type": "row"
+ },
+ {
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 4
+ },
+ "id": 7,
+ "links": [],
+ "options": {
+ "displayMode": "lcd",
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "decimals": null,
+ "max": 100,
+ "min": 0,
+ "unit": "watt"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "green",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "orange",
+ "index": 1,
+ "value": 40
+ },
+ {
+ "color": "red",
+ "index": 2,
+ "value": 80
+ }
+ ],
+ "values": false
+ },
+ "orientation": "vertical"
+ },
+ "pluginVersion": "6.2.0-pre",
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "B",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "C",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "D",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "E",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "10003,33333"
+ },
+ {
+ "refId": "F",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "G",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "H",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "100,100,100"
+ },
+ {
+ "refId": "I",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "J",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "K",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "L",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "M",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "N",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "O",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "P",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Retro LED mode",
+ "type": "bargauge"
+ },
+ {
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 10,
+ "w": 18,
+ "x": 0,
+ "y": 11
+ },
+ "id": 6,
+ "links": [],
+ "options": {
+ "displayMode": "gradient",
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "decimals": null,
+ "max": 100,
+ "min": 0,
+ "unit": "celsius"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "blue",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "green",
+ "index": 1,
+ "value": 20
+ },
+ {
+ "color": "orange",
+ "index": 2,
+ "value": 40
+ },
+ {
+ "color": "red",
+ "index": 3,
+ "value": 80
+ }
+ ],
+ "values": false
+ },
+ "orientation": "horizontal"
+ },
+ "pluginVersion": "6.2.0-pre",
+ "targets": [
+ {
+ "alias": "Inside",
+ "refId": "H",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "100,100,100"
+ },
+ {
+ "alias": "Outhouse",
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "Area B",
+ "refId": "B",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "Basement",
+ "refId": "C",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "Garage",
+ "refId": "D",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Gradient mode",
+ "type": "bargauge"
+ },
+ {
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 10,
+ "w": 6,
+ "x": 18,
+ "y": 11
+ },
+ "id": 8,
+ "links": [],
+ "options": {
+ "displayMode": "basic",
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "decimals": null,
+ "max": 100,
+ "min": 0,
+ "unit": "watt"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "blue",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "green",
+ "index": 1,
+ "value": 42.5
+ },
+ {
+ "color": "orange",
+ "index": 2,
+ "value": 80
+ },
+ {
+ "color": "red",
+ "index": 3,
+ "value": 90
+ }
+ ],
+ "values": false
+ },
+ "orientation": "horizontal"
+ },
+ "pluginVersion": "6.2.0-pre",
+ "targets": [
+ {
+ "refId": "H",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "100,100,100"
+ },
+ {
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "J",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "K",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "L",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "M",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "N",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "O",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "P",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Basic",
+ "type": "bargauge"
+ },
+ {
+ "collapsed": false,
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 21
+ },
+ "id": 36,
+ "panels": [],
+ "title": "Gauge multi series",
+ "type": "row"
+ },
+ {
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 6,
+ "w": 18,
+ "x": 0,
+ "y": 22
+ },
+ "id": 38,
+ "links": [],
+ "options": {
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "max": 100,
+ "min": 0,
+ "unit": "decgbytes"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "green",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "red",
+ "index": 1,
+ "value": 80
+ }
+ ],
+ "values": false
+ },
+ "orientation": "auto",
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true
+ },
+ "pluginVersion": "6.3.0-pre",
+ "targets": [
+ {
+ "alias": "sda1",
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "sda2",
+ "refId": "B",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "sda3",
+ "refId": "C",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "sda4",
+ "refId": "D",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "sda5",
+ "refId": "E",
+ "scenarioId": "random_walk"
+ },
+ {
+ "alias": "sda6",
+ "refId": "F",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "type": "gauge"
+ },
+ {
+ "content": " \n* The new Gauge and Bar Gauge repeat for every series, row or column your queries return.\n* Works great with table & time series data \n",
+ "gridPos": {
+ "h": 6,
+ "w": 6,
+ "x": 18,
+ "y": 22
+ },
+ "id": 40,
+ "links": [],
+ "mode": "markdown",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Multiple Series or Rows",
+ "transparent": true,
+ "type": "text"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 28
+ },
+ "id": 19,
+ "panels": [],
+ "title": "Panels With No Title",
+ "type": "row"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": true,
+ "colors": ["#299c46", "#73BF69", "#d44a3a"],
+ "datasource": "gdev-testdata",
+ "decimals": null,
+ "format": "ms",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 0,
+ "y": 29
+ },
+ "id": 20,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "pluginVersion": "6.2.0-pre",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "p99",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "type": "singlestat",
+ "valueFontSize": "120%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": true,
+ "colors": ["#299c46", "#73BF69", "#d44a3a"],
+ "datasource": "gdev-testdata",
+ "decimals": null,
+ "format": "ms",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 6,
+ "y": 29
+ },
+ "id": 23,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "pluginVersion": "6.2.0-pre",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "p95",
+ "prefixFontSize": "80%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "type": "singlestat",
+ "valueFontSize": "120%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": true,
+ "colors": ["#299c46", "#73BF69", "#d44a3a"],
+ "datasource": "gdev-testdata",
+ "decimals": null,
+ "format": "ms",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 12,
+ "y": 29
+ },
+ "id": 24,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "pluginVersion": "6.2.0-pre",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "p90",
+ "prefixFontSize": "80%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "type": "singlestat",
+ "valueFontSize": "120%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "content": "In v6.2 if you remove the panel title the visualization will take up all the space available. \nThe panel header menu will be overlayed on top if you hover over the panel so you can still move & edit the panel. ",
+ "gridPos": {
+ "h": 4,
+ "w": 6,
+ "x": 18,
+ "y": 29
+ },
+ "id": 42,
+ "links": [],
+ "mode": "markdown",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "transparent": true,
+ "type": "text"
+ },
+ {
+ "datasource": "gdev-testdata",
+ "gridPos": {
+ "h": 4,
+ "w": 24,
+ "x": 0,
+ "y": 33
+ },
+ "id": 17,
+ "links": [],
+ "options": {
+ "displayMode": "lcd",
+ "fieldOptions": {
+ "calcs": ["mean"],
+ "defaults": {
+ "max": 100,
+ "min": 0,
+ "title": "Completion rate",
+ "unit": "percent"
+ },
+ "mappings": [],
+ "override": {},
+ "thresholds": [
+ {
+ "color": "red",
+ "index": 0,
+ "value": null
+ },
+ {
+ "color": "red",
+ "index": 1,
+ "value": 80
+ }
+ ],
+ "values": false
+ },
+ "orientation": "horizontal"
+ },
+ "pluginVersion": "6.2.0-pre",
+ "targets": [
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "type": "bargauge"
+ },
+ {
+ "aliasColors": {
+ "A-series": "blue",
+ "B-series": "dark-purple",
+ "C-series": "purple",
+ "Q-series": "dark-blue"
+ },
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 3,
+ "gridPos": {
+ "h": 6,
+ "w": 24,
+ "x": 0,
+ "y": 37
+ },
+ "id": 26,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pluginVersion": "6.2.0-pre",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "Q",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "B",
+ "scenarioId": "random_walk"
+ },
+ {
+ "refId": "C",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 43
+ },
+ "id": 22,
+ "panels": [],
+ "title": "Lazy loading of panels",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 15,
+ "x": 0,
+ "y": 44
+ },
+ "id": 28,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Slow Query (5s)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "content": "\n# Lazy Loading\n\nAs you scroll down you will see that Grafana only loads & issues queries for these panels as they are scrolled in to view. \nThis greatly reduces the load on your data source backends if you have many long dashboards with many panels. \n\n\n",
+ "gridPos": {
+ "h": 40,
+ "w": 9,
+ "x": 15,
+ "y": 44
+ },
+ "id": 34,
+ "links": [],
+ "mode": "markdown",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "",
+ "transparent": true,
+ "type": "text"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 15,
+ "x": 0,
+ "y": 52
+ },
+ "id": 30,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Slow Query (5s)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 15,
+ "x": 0,
+ "y": 60
+ },
+ "id": 31,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Slow Query (5s)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 15,
+ "x": 0,
+ "y": 68
+ },
+ "id": 32,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Slow Query (5s)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 15,
+ "x": 0,
+ "y": 76
+ },
+ "id": 29,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Slow Query (5s)",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 18,
+ "style": "dark",
+ "tags": ["gdev", "demo"],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
+ "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ },
+ "timezone": "",
+ "title": "New Features in v6.2",
+ "uid": "ZvPm55mWk",
+ "version": 1
+}
diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx
new file mode 100644
index 000000000000..28845d4f463e
--- /dev/null
+++ b/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx
@@ -0,0 +1,90 @@
+import React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import { DashboardGrid, Props } from './DashboardGrid';
+import { DashboardModel } from '../state';
+
+interface ScenarioContext {
+ props: Props;
+ wrapper?: ShallowWrapper;
+ setup?: (fn: () => void) => void;
+ setProps: (props: Partial) => void;
+}
+
+function getTestDashboard(overrides?: any, metaOverrides?: any): DashboardModel {
+ const data = Object.assign(
+ {
+ title: 'My dashboard',
+ panels: [
+ {
+ id: 1,
+ type: 'graph',
+ title: 'My graph',
+ gridPos: { x: 0, y: 0, w: 24, h: 10 },
+ },
+ {
+ id: 2,
+ type: 'graph2',
+ title: 'My graph2',
+ gridPos: { x: 0, y: 10, w: 25, h: 10 },
+ },
+ {
+ id: 3,
+ type: 'graph3',
+ title: 'My graph3',
+ gridPos: { x: 0, y: 20, w: 25, h: 100 },
+ },
+ {
+ id: 4,
+ type: 'graph4',
+ title: 'My graph4',
+ gridPos: { x: 0, y: 120, w: 25, h: 10 },
+ },
+ ],
+ },
+ overrides
+ );
+
+ const meta = Object.assign({ canSave: true, canEdit: true }, metaOverrides);
+ return new DashboardModel(data, meta);
+}
+
+function dashboardGridScenario(description, scenarioFn: (ctx: ScenarioContext) => void) {
+ describe(description, () => {
+ let setupFn: () => void;
+
+ const ctx: ScenarioContext = {
+ setup: fn => {
+ setupFn = fn;
+ },
+ props: {
+ isEditing: false,
+ isFullscreen: false,
+ scrollTop: null,
+ dashboard: getTestDashboard(),
+ },
+ setProps: (props: Partial) => {
+ Object.assign(ctx.props, props);
+ if (ctx.wrapper) {
+ ctx.wrapper.setProps(ctx.props);
+ }
+ },
+ };
+
+ beforeEach(() => {
+ setupFn();
+ ctx.wrapper = shallow();
+ });
+
+ scenarioFn(ctx);
+ });
+}
+
+describe('DashboardGrid', () => {
+ dashboardGridScenario('Can render dashboard grid', ctx => {
+ ctx.setup(() => {});
+
+ it('Should render', () => {
+ expect(ctx.wrapper).toMatchSnapshot();
+ });
+ });
+});
diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
index 5651b99f4cfd..6f57f6f42e39 100644
--- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
+++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
@@ -205,7 +205,7 @@ export class DashboardGrid extends PureComponent {
return false;
}
- const top = parseInt(elem.style.top.replace('px', ''), 10);
+ const top = elem.offsetTop;
const height = panel.gridPos.h * GRID_CELL_HEIGHT + 40;
const bottom = top + height;
diff --git a/public/app/features/dashboard/dashgrid/DashboardGridDirective.ts b/public/app/features/dashboard/dashgrid/DashboardGridDirective.ts
deleted file mode 100644
index bb81c936b3ea..000000000000
--- a/public/app/features/dashboard/dashgrid/DashboardGridDirective.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { react2AngularDirective } from 'app/core/utils/react2angular';
-import DashboardGrid from './DashboardGrid';
-
-react2AngularDirective('dashboardGrid', DashboardGrid, [['dashboard', { watchDepth: 'reference' }]]);
diff --git a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap
new file mode 100644
index 000000000000..38172a3044f5
--- /dev/null
+++ b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap
@@ -0,0 +1,996 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/public/app/features/dashboard/index.ts b/public/app/features/dashboard/index.ts
index 5e31a49f40f8..1a326d73bd9e 100644
--- a/public/app/features/dashboard/index.ts
+++ b/public/app/features/dashboard/index.ts
@@ -1,5 +1,3 @@
-import './dashgrid/DashboardGridDirective';
-
// Services
import './services/UnsavedChangesSrv';
import './services/DashboardLoaderSrv';
From 6c7224c74db5dc2e929bc71a4066b3197b86ef03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 14 May 2019 15:28:09 +0200
Subject: [PATCH 027/166] InfoPopover: Fixes transclude undefined error
(#17063)
---
public/app/core/components/info_popover.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/app/core/components/info_popover.ts b/public/app/core/components/info_popover.ts
index 9c5694188d6e..704514659303 100644
--- a/public/app/core/components/info_popover.ts
+++ b/public/app/core/components/info_popover.ts
@@ -8,7 +8,7 @@ export function infoPopover() {
restrict: 'E',
template: '',
transclude: true,
- link: (scope: any, elem: any, attrs: any, transclude: any) => {
+ link: (scope: any, elem: any, attrs: any, ctrl: any, transclude: any) => {
const offset = attrs.offset || '0 -10px';
const position = attrs.position || 'right middle';
let classes = 'drop-help drop-hide-out-of-bounds';
From 3df94c6f279fbaf67a15dfb58b5c7b545c89b2e9 Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Tue, 14 May 2019 15:38:18 +0200
Subject: [PATCH 028/166] serverlock: run tests async should be more linear
time wise (#17059)
---
.../serverlock/serverlock_integration_test.go | 45 +++++++++----------
1 file changed, 20 insertions(+), 25 deletions(-)
diff --git a/pkg/infra/serverlock/serverlock_integration_test.go b/pkg/infra/serverlock/serverlock_integration_test.go
index 8bcd9c2ca256..60c3cf17875d 100644
--- a/pkg/infra/serverlock/serverlock_integration_test.go
+++ b/pkg/infra/serverlock/serverlock_integration_test.go
@@ -7,34 +7,29 @@ import (
"testing"
"time"
- . "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/assert"
)
func TestServerLok(t *testing.T) {
sl := createTestableServerLock(t)
- Convey("Server lock integration tests", t, func() {
- counter := 0
- var err error
- incCounter := func() { counter++ }
- atInterval := time.Second * 1
- ctx := context.Background()
-
- //this time `fn` should be executed
- So(sl.LockAndExecute(ctx, "test-operation", atInterval, incCounter), ShouldBeNil)
-
- //this should not execute `fn`
- So(sl.LockAndExecute(ctx, "test-operation", atInterval, incCounter), ShouldBeNil)
- So(sl.LockAndExecute(ctx, "test-operation", atInterval, incCounter), ShouldBeNil)
- So(sl.LockAndExecute(ctx, "test-operation", atInterval, incCounter), ShouldBeNil)
- So(sl.LockAndExecute(ctx, "test-operation", atInterval, incCounter), ShouldBeNil)
-
- // wait 5 second.
- <-time.After(atInterval * 2)
-
- // now `fn` should be executed again
- err = sl.LockAndExecute(ctx, "test-operation", atInterval, incCounter)
- So(err, ShouldBeNil)
- So(counter, ShouldEqual, 2)
- })
+ counter := 0
+ fn := func() { counter++ }
+ atInterval := time.Second * 1
+ ctx := context.Background()
+
+ //this time `fn` should be executed
+ assert.Nil(t, sl.LockAndExecute(ctx, "test-operation", atInterval, fn))
+
+ //this should not execute `fn`
+ assert.Nil(t, sl.LockAndExecute(ctx, "test-operation", atInterval, fn))
+ assert.Nil(t, sl.LockAndExecute(ctx, "test-operation", atInterval, fn))
+
+ // wait 2 second.
+ <-time.After(time.Second * 2)
+
+ // now `fn` should be executed again
+ err := sl.LockAndExecute(ctx, "test-operation", atInterval, fn)
+ assert.Nil(t, err)
+ assert.Equal(t, counter, 2)
}
From 68ad93f4108225c1c751b7812f4dbdcbfa80c962 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 14 May 2019 16:33:47 +0200
Subject: [PATCH 029/166] Gauge: Fix switching orientation issue when switching
from BarGauge to Gauge (#17064)
---
packages/grafana-ui/src/components/Gauge/Gauge.tsx | 2 +-
public/app/plugins/panel/gauge/GaugePanel.tsx | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx
index 026b9769a708..eb49891d298e 100644
--- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx
+++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx
@@ -76,7 +76,7 @@ export class Gauge extends PureComponent {
);
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
- const gaugeWidth = Math.min(dimension / 5, 40) / gaugeWidthReduceRatio;
+ const gaugeWidth = Math.min(dimension / 5.5, 40) / gaugeWidthReduceRatio;
const thresholdMarkersWidth = gaugeWidth / 5;
const fontSize = Math.min(dimension / 5.5, 100) * (value.text !== null ? this.getFontScale(value.text.length) : 1);
const thresholdLabelFontSize = fontSize / 2.5;
diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx
index 0c8240256838..b08b49f01bf4 100644
--- a/public/app/plugins/panel/gauge/GaugePanel.tsx
+++ b/public/app/plugins/panel/gauge/GaugePanel.tsx
@@ -5,7 +5,7 @@ import React, { PureComponent } from 'react';
import { config } from 'app/core/config';
// Components
-import { Gauge, FieldDisplay, getFieldDisplayValues } from '@grafana/ui';
+import { Gauge, FieldDisplay, getFieldDisplayValues, VizOrientation } from '@grafana/ui';
// Types
import { GaugeOptions } from './types';
@@ -43,7 +43,7 @@ export class GaugePanel extends PureComponent> {
};
render() {
- const { height, width, options, data, renderCounter } = this.props;
+ const { height, width, data, renderCounter } = this.props;
return (
> {
height={height}
source={data}
renderCounter={renderCounter}
- orientation={options.orientation}
+ orientation={VizOrientation.Auto}
/>
);
}
From 7333f7ca17932bfda5815cafe2a9c8fa1a9289a3 Mon Sep 17 00:00:00 2001
From: Ryan McKinley
Date: Tue, 14 May 2019 08:47:41 -0700
Subject: [PATCH 030/166] Streaming: support streaming in MetricsPanelCtrl
(#17034)
* support streaming in angular panels
* keep the same dashboard number
---
.../app/features/panel/metrics_panel_ctrl.ts | 41 ++++---
.../testdata/dashboards/streaming.json | 103 +++++++++++++++++-
2 files changed, 126 insertions(+), 18 deletions(-)
diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts
index 068aa03d0f9e..f7f4552d76b5 100644
--- a/public/app/features/panel/metrics_panel_ctrl.ts
+++ b/public/app/features/panel/metrics_panel_ctrl.ts
@@ -129,25 +129,36 @@ class MetricsPanelCtrl extends PanelCtrl {
if (data.state === LoadingState.Error) {
this.loading = false;
this.processDataError(data.error);
- } else if (data.state === LoadingState.Done) {
- this.loading = false;
+ return;
+ }
- // The result should already be processed, but just in case
- if (!data.legacy) {
- data.legacy = data.series.map(v => {
- if (isSeriesData(v)) {
- return toLegacyResponseData(v);
- }
- return v;
- });
- }
+ this.loading = false;
- // Make the results look like they came directly from a <6.2 datasource request
- // NOTE: any object other than 'data' is no longer supported supported
- this.handleQueryResult({
- data: data.legacy,
+ // The result should already be processed, but just in case
+ if (!data.legacy) {
+ data.legacy = data.series.map(v => {
+ if (isSeriesData(v)) {
+ return toLegacyResponseData(v);
+ }
+ return v;
});
}
+
+ if (data.request) {
+ const { range, timeInfo } = data.request;
+ if (range) {
+ this.range = range;
+ }
+ if (timeInfo) {
+ this.timeInfo = timeInfo;
+ }
+ }
+
+ // Make the results look like they came directly from a <6.2 datasource request
+ // NOTE: any object other than 'data' is no longer supported supported
+ this.handleQueryResult({
+ data: data.legacy,
+ });
},
};
diff --git a/public/app/plugins/datasource/testdata/dashboards/streaming.json b/public/app/plugins/datasource/testdata/dashboards/streaming.json
index b2f2e9ecb6bb..9f38d2096ef1 100644
--- a/public/app/plugins/datasource/testdata/dashboards/streaming.json
+++ b/public/app/plugins/datasource/testdata/dashboards/streaming.json
@@ -14,7 +14,13 @@
"type": "grafana",
"id": "grafana",
"name": "Grafana",
- "version": "6.2.0-pre"
+ "version": "6.3.0-pre"
+ },
+ {
+ "type": "panel",
+ "id": "graph",
+ "name": "Graph",
+ "version": ""
},
{
"type": "panel",
@@ -48,6 +54,97 @@
"id": null,
"links": [],
"panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 4,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "refId": "A",
+ "scenarioId": "streaming_client",
+ "stream": {
+ "noise": 2.2,
+ "speed": 100,
+ "spread": 3.5,
+ "type": "signal"
+ },
+ "stringInput": ""
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Angular Graph",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 2,
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
{
"datasource": "${DS_TESTDATA_DB}",
"description": "",
@@ -55,7 +152,7 @@
"h": 6,
"w": 24,
"x": 0,
- "y": 0
+ "y": 7
},
"id": 2,
"links": [],
@@ -110,5 +207,5 @@
"timezone": "",
"title": "Simple Streaming Example",
"uid": "TXSTREZ",
- "version": 2
+ "version": 3
}
From a0b6ef4d01e12cfca012126c06e193b83eb4f1a9 Mon Sep 17 00:00:00 2001
From: Ryan McKinley
Date: Tue, 14 May 2019 09:43:26 -0700
Subject: [PATCH 031/166] GraphPanel: use SeriesData directly (skip legacy
transformation) (#17037)
* support streaming in angular panels
* keep the same dashboard number
* skip the legacy data conversion
* fix tests
---
.../app/features/panel/metrics_panel_ctrl.ts | 48 ++++++++++++-------
public/app/plugins/panel/graph/module.ts | 12 ++++-
2 files changed, 43 insertions(+), 17 deletions(-)
diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts
index f7f4552d76b5..a03a431ae0fd 100644
--- a/public/app/features/panel/metrics_panel_ctrl.ts
+++ b/public/app/features/panel/metrics_panel_ctrl.ts
@@ -15,6 +15,7 @@ import {
PanelData,
LoadingState,
DataQueryResponse,
+ SeriesData,
} from '@grafana/ui';
import { Unsubscribable } from 'rxjs';
import { PanelModel } from 'app/features/dashboard/state';
@@ -37,6 +38,7 @@ class MetricsPanelCtrl extends PanelCtrl {
skipDataOnInit: boolean;
dataList: LegacyResponseData[];
querySubscription?: Unsubscribable;
+ dataFormat = PanelQueryRunnerFormat.legacy;
constructor($scope: any, $injector: any) {
super($scope, $injector);
@@ -134,16 +136,6 @@ class MetricsPanelCtrl extends PanelCtrl {
this.loading = false;
- // The result should already be processed, but just in case
- if (!data.legacy) {
- data.legacy = data.series.map(v => {
- if (isSeriesData(v)) {
- return toLegacyResponseData(v);
- }
- return v;
- });
- }
-
if (data.request) {
const { range, timeInfo } = data.request;
if (range) {
@@ -154,11 +146,25 @@ class MetricsPanelCtrl extends PanelCtrl {
}
}
- // Make the results look like they came directly from a <6.2 datasource request
- // NOTE: any object other than 'data' is no longer supported supported
- this.handleQueryResult({
- data: data.legacy,
- });
+ if (this.dataFormat === PanelQueryRunnerFormat.legacy) {
+ // The result should already be processed, but just in case
+ if (!data.legacy) {
+ data.legacy = data.series.map(v => {
+ if (isSeriesData(v)) {
+ return toLegacyResponseData(v);
+ }
+ return v;
+ });
+ }
+
+ // Make the results look like they came directly from a <6.2 datasource request
+ // NOTE: any object other than 'data' is no longer supported supported
+ this.handleQueryResult({
+ data: data.legacy,
+ });
+ } else {
+ this.handleSeriesData(data.series);
+ }
},
};
@@ -198,7 +204,7 @@ class MetricsPanelCtrl extends PanelCtrl {
const queryRunner = panel.getQueryRunner();
if (!this.querySubscription) {
- this.querySubscription = queryRunner.subscribe(this.panelDataObserver, PanelQueryRunnerFormat.legacy);
+ this.querySubscription = queryRunner.subscribe(this.panelDataObserver, this.dataFormat);
}
return queryRunner.run({
@@ -216,6 +222,16 @@ class MetricsPanelCtrl extends PanelCtrl {
});
}
+ handleSeriesData(data: SeriesData[]) {
+ this.loading = false;
+
+ if (this.dashboard && this.dashboard.snapshot) {
+ this.panel.snapshotData = data;
+ }
+
+ // Subclasses that asked for SeriesData will override
+ }
+
handleQueryResult(result: DataQueryResponse) {
this.loading = false;
diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts
index f34ed05039d7..6d5b0b6abc1e 100644
--- a/public/app/plugins/panel/graph/module.ts
+++ b/public/app/plugins/panel/graph/module.ts
@@ -13,6 +13,7 @@ import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
import { getColorFromHexRgbOrName, LegacyResponseData, SeriesData } from '@grafana/ui';
import { getProcessedSeriesData } from 'app/features/dashboard/state/PanelQueryState';
+import { PanelQueryRunnerFormat } from 'app/features/dashboard/state/PanelQueryRunner';
class GraphCtrl extends MetricsPanelCtrl {
static template = template;
@@ -128,6 +129,7 @@ class GraphCtrl extends MetricsPanelCtrl {
_.defaults(this.panel.legend, this.panelDefaults.legend);
_.defaults(this.panel.xaxis, this.panelDefaults.xaxis);
+ this.dataFormat = PanelQueryRunnerFormat.series;
this.processor = new DataProcessor(this.panel);
this.events.on('render', this.onRender.bind(this));
@@ -188,8 +190,16 @@ class GraphCtrl extends MetricsPanelCtrl {
this.render([]);
}
+ // This should only be called from the snapshot callback
onDataReceived(dataList: LegacyResponseData[]) {
- this.dataList = getProcessedSeriesData(dataList);
+ this.handleSeriesData(getProcessedSeriesData(dataList));
+ }
+
+ // Directly support SeriesData skipping event callbacks
+ handleSeriesData(data: SeriesData[]) {
+ super.handleSeriesData(data);
+
+ this.dataList = data;
this.seriesList = this.processor.getSeriesList({
dataList: this.dataList,
range: this.range,
From 238a929262a2f332420e908885d5430dfadae331 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 14 May 2019 18:47:35 +0200
Subject: [PATCH 032/166] Panels: Fixed alert icon position in panel header
(#17070)
---
public/sass/components/_panel_table.scss | 2 +-
public/sass/pages/_alerting.scss | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/public/sass/components/_panel_table.scss b/public/sass/components/_panel_table.scss
index e47e639a65ed..dbd76dcd3a15 100644
--- a/public/sass/components/_panel_table.scss
+++ b/public/sass/components/_panel_table.scss
@@ -123,7 +123,7 @@
}
.table-panel-table-header-inner {
- padding: 0.45em 0 0.45em 1.1em;
+ padding: 0.3em 0 0.45em 1.1em;
text-align: left;
color: $blue;
position: absolute;
diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss
index f285ab753ff1..28756756aa57 100644
--- a/public/sass/pages/_alerting.scss
+++ b/public/sass/pages/_alerting.scss
@@ -32,7 +32,7 @@
.panel-alert-icon:before {
content: '\e611';
position: relative;
- top: 5px;
+ top: 1px;
left: -3px;
}
}
From d8280b895df79585ec209ff288ea2eef20471664 Mon Sep 17 00:00:00 2001
From: Ryan McKinley
Date: Tue, 14 May 2019 22:20:27 -0700
Subject: [PATCH 033/166] InputDataSource: better empty value support (#17075)
---
.../datasource/input/InputDatasource.test.ts | 16 ++++++++-
.../datasource/input/InputDatasource.ts | 35 ++++++++++---------
.../datasource/input/InputQueryEditor.tsx | 6 ++--
3 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/public/app/plugins/datasource/input/InputDatasource.test.ts b/public/app/plugins/datasource/input/InputDatasource.test.ts
index 2a8412dca88d..e4c23bf47d0f 100644
--- a/public/app/plugins/datasource/input/InputDatasource.test.ts
+++ b/public/app/plugins/datasource/input/InputDatasource.test.ts
@@ -1,4 +1,4 @@
-import InputDatasource from './InputDatasource';
+import InputDatasource, { describeSeriesData } from './InputDatasource';
import { InputQuery, InputOptions } from './types';
import { readCSV, DataSourceInstanceSettings, PluginMeta } from '@grafana/ui';
import { getQueryOptions } from 'test/helpers/getQueryOptions';
@@ -31,4 +31,18 @@ describe('InputDatasource', () => {
});
});
});
+
+ test('SeriesData descriptions', () => {
+ expect(describeSeriesData([])).toEqual('');
+ expect(describeSeriesData(null)).toEqual('');
+ expect(
+ describeSeriesData([
+ {
+ name: 'x',
+ fields: [{ name: 'a' }],
+ rows: [],
+ },
+ ])
+ ).toEqual('1 Fields, 0 Rows');
+ });
});
diff --git a/public/app/plugins/datasource/input/InputDatasource.ts b/public/app/plugins/datasource/input/InputDatasource.ts
index f208260aa2ae..246ebeb7a05b 100644
--- a/public/app/plugins/datasource/input/InputDatasource.ts
+++ b/public/app/plugins/datasource/input/InputDatasource.ts
@@ -17,28 +17,14 @@ export class InputDatasource extends DataSourceApi {
this.data = instanceSettings.jsonData.data ? instanceSettings.jsonData.data : [];
}
- getDescription(data: SeriesData[]): string {
- if (!data) {
- return '';
- }
- if (data.length > 1) {
- const count = data.reduce((acc, series) => {
- return acc + series.rows.length;
- }, 0);
- return `${data.length} Series, ${count} Rows`;
- }
- const series = data[0];
- return `${series.fields.length} Fields, ${series.rows.length} Rows`;
- }
-
/**
* Convert a query to a simple text string
*/
getQueryDisplayText(query: InputQuery): string {
if (query.data) {
- return 'Panel Data: ' + this.getDescription(query.data);
+ return 'Panel Data: ' + describeSeriesData(query.data);
}
- return `Shared Data From: ${this.name} (${this.getDescription(this.data)})`;
+ return `Shared Data From: ${this.name} (${describeSeriesData(this.data)})`;
}
metricFindQuery(query: string, options?: any) {
@@ -96,4 +82,21 @@ export class InputDatasource extends DataSourceApi {
}
}
+export function describeSeriesData(data: SeriesData[]): string {
+ if (!data || !data.length) {
+ return '';
+ }
+ if (data.length > 1) {
+ const count = data.reduce((acc, series) => {
+ return acc + series.rows.length;
+ }, 0);
+ return `${data.length} Series, ${count} Rows`;
+ }
+ const series = data[0];
+ if (!series.fields) {
+ return 'Missing Fields';
+ }
+ return `${series.fields.length} Fields, ${series.rows.length} Rows`;
+}
+
export default InputDatasource;
diff --git a/public/app/plugins/datasource/input/InputQueryEditor.tsx b/public/app/plugins/datasource/input/InputQueryEditor.tsx
index 68e1cb5516eb..2e9a473f5d90 100644
--- a/public/app/plugins/datasource/input/InputQueryEditor.tsx
+++ b/public/app/plugins/datasource/input/InputQueryEditor.tsx
@@ -2,7 +2,7 @@
import React, { PureComponent } from 'react';
// Types
-import { InputDatasource } from './InputDatasource';
+import { InputDatasource, describeSeriesData } from './InputDatasource';
import { InputQuery, InputOptions } from './types';
import { FormLabel, Select, QueryEditorProps, SelectOptionItem, SeriesData, TableInputCSV, toCSV } from '@grafana/ui';
@@ -80,10 +80,10 @@ export class InputQueryEditor extends PureComponent {
{query.data ? (
- datasource.getDescription(query.data)
+ describeSeriesData(query.data)
) : (
- {name}: {datasource.getDescription(datasource.data)}
+ {name}: {describeSeriesData(datasource.data)}
)}
From d0ea98f6bd8087bfaae5d9deb72753f5596ece8c Mon Sep 17 00:00:00 2001
From: Brian Gann
Date: Wed, 15 May 2019 03:58:25 -0500
Subject: [PATCH 034/166] Build: Support publishing MSI to grafana.com (#17073)
* add test for msi, and support for publishing msi
* update arch and os in test
* Build: Fixed issues with os naming
---
scripts/build/release_publisher/publisher.go | 5 ++++
.../build/release_publisher/publisher_test.go | 28 +++++++++++++++++++
...enterprise-5.4.0-123pre1.windows-amd64.msi | 0
...ise-5.4.0-123pre1.windows-amd64.msi.sha256 | 1 +
4 files changed, 34 insertions(+)
create mode 100644 scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi
create mode 100644 scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256
diff --git a/scripts/build/release_publisher/publisher.go b/scripts/build/release_publisher/publisher.go
index c0d62724a4e4..da4d36e49d54 100644
--- a/scripts/build/release_publisher/publisher.go
+++ b/scripts/build/release_publisher/publisher.go
@@ -174,6 +174,11 @@ var completeBuildArtifactConfigurations = []buildArtifact{
arch: "amd64",
urlPostfix: ".windows-amd64.zip",
},
+ {
+ os: "win-installer",
+ arch: "amd64",
+ urlPostfix: ".windows-amd64.msi",
+ },
}
type artifactFilter struct {
diff --git a/scripts/build/release_publisher/publisher_test.go b/scripts/build/release_publisher/publisher_test.go
index 80d2cd477a47..7406eef74b61 100644
--- a/scripts/build/release_publisher/publisher_test.go
+++ b/scripts/build/release_publisher/publisher_test.go
@@ -74,6 +74,34 @@ func TestPreparingReleaseFromRemote(t *testing.T) {
baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana",
buildArtifacts: []buildArtifact{{"linux", "armv6", "_armhf.deb", "-rpi"}},
},
+ {
+ version: "v5.4.0-pre1asdf",
+ expectedVersion: "5.4.0-pre1asdf",
+ whatsNewURL: "https://whatsnews.foo/",
+ relNotesURL: "https://relnotes.foo/",
+ nightly: true,
+ expectedBeta: false,
+ expectedStable: false,
+ expectedArch: "amd64",
+ expectedOs: "win-installer",
+ expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.4.0-pre1asdf.windows-amd64.msi",
+ baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana",
+ buildArtifacts: []buildArtifact{{"win-installer", "amd64", ".windows-amd64.msi", ""}},
+ },
+ {
+ version: "v5.4.0-pre1asdf",
+ expectedVersion: "5.4.0-pre1asdf",
+ whatsNewURL: "https://whatsnews.foo/",
+ relNotesURL: "https://relnotes.foo/",
+ nightly: true,
+ expectedBeta: false,
+ expectedStable: false,
+ expectedArch: "amd64",
+ expectedOs: "win",
+ expectedURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.4.0-pre1asdf.windows-amd64.zip",
+ baseArchiveURL: "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana",
+ buildArtifacts: []buildArtifact{{"win", "amd64", ".windows-amd64.zip", ""}},
+ },
}
for _, test := range cases {
diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256 b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256
new file mode 100644
index 000000000000..c3068040269a
--- /dev/null
+++ b/scripts/build/release_publisher/testdata/grafana-enterprise-5.4.0-123pre1.windows-amd64.msi.sha256
@@ -0,0 +1 @@
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
From aed3d0d3adabfdae096aef0116530e365316e586 Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Wed, 15 May 2019 11:24:04 +0200
Subject: [PATCH 035/166] Remotecache: Avoid race condition in Set causing
error on insert. (#17082)
* remotecache: avoid race condition in set
since set called the database twice without transactions another
operation could insert a value before the first operation completed.
which would raise an error on insert since the data have been inserted
by the other request.
closes #17079
---
pkg/infra/remotecache/database_storage.go | 70 +++++++++++++----------
1 file changed, 39 insertions(+), 31 deletions(-)
diff --git a/pkg/infra/remotecache/database_storage.go b/pkg/infra/remotecache/database_storage.go
index 3e15bd9a2cd9..03ac0f0c5e8e 100644
--- a/pkg/infra/remotecache/database_storage.go
+++ b/pkg/infra/remotecache/database_storage.go
@@ -39,10 +39,14 @@ func (dc *databaseCache) Run(ctx context.Context) error {
}
func (dc *databaseCache) internalRunGC() {
- now := getTime().Unix()
- sql := `DELETE FROM cache_data WHERE (? - created_at) >= expires AND expires <> 0`
+ err := dc.SQLStore.WithDbSession(context.Background(), func(session *sqlstore.DBSession) error {
+ now := getTime().Unix()
+ sql := `DELETE FROM cache_data WHERE (? - created_at) >= expires AND expires <> 0`
+
+ _, err := session.Exec(sql, now)
+ return err
+ })
- _, err := dc.SQLStore.NewSession().Exec(sql, now)
if err != nil {
dc.log.Error("failed to run garbage collect", "error", err)
}
@@ -80,44 +84,48 @@ func (dc *databaseCache) Get(key string) (interface{}, error) {
}
func (dc *databaseCache) Set(key string, value interface{}, expire time.Duration) error {
- item := &cachedItem{Val: value}
- data, err := encodeGob(item)
- if err != nil {
- return err
- }
-
- session := dc.SQLStore.NewSession()
+ return dc.SQLStore.WithTransactionalDbSession(context.Background(), func(session *sqlstore.DBSession) error {
+ item := &cachedItem{Val: value}
+ data, err := encodeGob(item)
+ if err != nil {
+ return err
+ }
- var cacheHit CacheData
- has, err := session.Where("cache_key = ?", key).Get(&cacheHit)
- if err != nil {
- return err
- }
+ var cacheHit CacheData
+ has, err := session.Where("cache_key = ?", key).Get(&cacheHit)
+ if err != nil {
+ return err
+ }
- var expiresInSeconds int64
- if expire != 0 {
- expiresInSeconds = int64(expire) / int64(time.Second)
- }
+ var expiresInSeconds int64
+ if expire != 0 {
+ expiresInSeconds = int64(expire) / int64(time.Second)
+ }
- // insert or update depending on if item already exist
- if has {
- sql := `UPDATE cache_data SET data=?, created_at=?, expires=? WHERE cache_key=?`
- _, err = session.Exec(sql, data, getTime().Unix(), expiresInSeconds, key)
- } else {
- sql := `INSERT INTO cache_data (cache_key,data,created_at,expires) VALUES(?,?,?,?)`
- _, err = session.Exec(sql, key, data, getTime().Unix(), expiresInSeconds)
- }
+ // insert or update depending on if item already exist
+ if has {
+ sql := `UPDATE cache_data SET data=?, created_at=?, expires=? WHERE cache_key=?`
+ _, err = session.Exec(sql, data, getTime().Unix(), expiresInSeconds, key)
+ } else {
+ sql := `INSERT INTO cache_data (cache_key,data,created_at,expires) VALUES(?,?,?,?)`
+ _, err = session.Exec(sql, key, data, getTime().Unix(), expiresInSeconds)
+ }
- return err
+ return err
+ })
}
func (dc *databaseCache) Delete(key string) error {
- sql := "DELETE FROM cache_data WHERE cache_key=?"
- _, err := dc.SQLStore.NewSession().Exec(sql, key)
+ return dc.SQLStore.WithDbSession(context.Background(), func(session *sqlstore.DBSession) error {
+ sql := "DELETE FROM cache_data WHERE cache_key=?"
+ _, err := session.Exec(sql, key)
+
+ return err
+ })
- return err
}
+// CacheData is the struct representing the table in the database
type CacheData struct {
CacheKey string
Data []byte
From fdd421e24c38e92c9ea67d326ad56d00563713bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?=
Date: Wed, 15 May 2019 11:43:27 +0200
Subject: [PATCH 036/166] Explore: Fixes filtering in Prometheus queries when
clicking in Table (#17083)
Fixes: #17071
---
public/app/features/explore/Table.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/app/features/explore/Table.tsx b/public/app/features/explore/Table.tsx
index bbf338df8f90..8c1891de8755 100644
--- a/public/app/features/explore/Table.tsx
+++ b/public/app/features/explore/Table.tsx
@@ -26,7 +26,7 @@ export default class Table extends PureComponent {
if (e.target) {
const link = e.target as HTMLElement;
if (link.className === 'link') {
- const columnKey = column.Header;
+ const columnKey = column.Header().props.title;
const rowValue = rowInfo.row[columnKey];
this.props.onClickCell(columnKey, rowValue);
}
From c55e6016bfa6c0fe28125f503cf73b493554c1b9 Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Wed, 15 May 2019 12:20:17 +0200
Subject: [PATCH 037/166] backend: replace /pkg/errors with errutil (#17065)
---
go.mod | 2 +-
pkg/services/notifications/mailer.go | 12 +++++------
.../provisioning/dashboards/dashboard.go | 11 +++++-----
pkg/services/provisioning/provisioning.go | 10 +++++-----
pkg/services/provisioning/values/values.go | 5 +++--
pkg/util/errutil/errors.go | 20 ++++++++++++++++++-
vendor/github.com/robfig/cron/README.md | 2 +-
vendor/github.com/robfig/cron/doc.go | 2 +-
8 files changed, 42 insertions(+), 22 deletions(-)
diff --git a/go.mod b/go.mod
index fc72780af2f4..106d8b726617 100644
--- a/go.mod
+++ b/go.mod
@@ -48,7 +48,7 @@ require (
github.com/onsi/gomega v1.5.0 // indirect
github.com/opentracing/opentracing-go v1.1.0
github.com/patrickmn/go-cache v2.1.0+incompatible
- github.com/pkg/errors v0.8.1
+ github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.9.2
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90
github.com/prometheus/common v0.2.0
diff --git a/pkg/services/notifications/mailer.go b/pkg/services/notifications/mailer.go
index f88deb2bf960..584914f5ff71 100644
--- a/pkg/services/notifications/mailer.go
+++ b/pkg/services/notifications/mailer.go
@@ -12,9 +12,9 @@ import (
"net"
"strconv"
- m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
- "github.com/pkg/errors"
+ "github.com/grafana/grafana/pkg/util/errutil"
gomail "gopkg.in/mail.v2"
)
@@ -38,11 +38,11 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
e := dialer.DialAndSend(m)
if e != nil {
- err = errors.Wrapf(e, "Failed to send notification to email address: %s", address)
+ err = errutil.Wrapf(e, "Failed to send notification to email address: %s", address)
continue
}
- num += 1
+ num++
}
return num, err
@@ -83,9 +83,9 @@ func (ns *NotificationService) createDialer() (*gomail.Dialer, error) {
return d, nil
}
-func (ns *NotificationService) buildEmailMessage(cmd *m.SendEmailCommand) (*Message, error) {
+func (ns *NotificationService) buildEmailMessage(cmd *models.SendEmailCommand) (*Message, error) {
if !ns.Cfg.Smtp.Enabled {
- return nil, m.ErrSmtpNotEnabled
+ return nil, models.ErrSmtpNotEnabled
}
var buffer bytes.Buffer
diff --git a/pkg/services/provisioning/dashboards/dashboard.go b/pkg/services/provisioning/dashboards/dashboard.go
index 9cf2e3225f4d..094376e63e9a 100644
--- a/pkg/services/provisioning/dashboards/dashboard.go
+++ b/pkg/services/provisioning/dashboards/dashboard.go
@@ -3,8 +3,9 @@ package dashboards
import (
"context"
"fmt"
+
"github.com/grafana/grafana/pkg/infra/log"
- "github.com/pkg/errors"
+ "github.com/grafana/grafana/pkg/util/errutil"
)
type DashboardProvisionerImpl struct {
@@ -18,13 +19,13 @@ func NewDashboardProvisionerImpl(configDirectory string) (*DashboardProvisionerI
configs, err := cfgReader.readConfig()
if err != nil {
- return nil, errors.Wrap(err, "Failed to read dashboards config")
+ return nil, errutil.Wrap("Failed to read dashboards config", err)
}
fileReaders, err := getFileReaders(configs, logger)
if err != nil {
- return nil, errors.Wrap(err, "Failed to initialize file readers")
+ return nil, errutil.Wrap("Failed to initialize file readers", err)
}
d := &DashboardProvisionerImpl{
@@ -39,7 +40,7 @@ func (provider *DashboardProvisionerImpl) Provision() error {
for _, reader := range provider.fileReaders {
err := reader.startWalkingDisk()
if err != nil {
- return errors.Wrapf(err, "Failed to provision config %v", reader.Cfg.Name)
+ return errutil.Wrapf(err, "Failed to provision config %v", reader.Cfg.Name)
}
}
@@ -73,7 +74,7 @@ func getFileReaders(configs []*DashboardsAsConfig, logger log.Logger) ([]*fileRe
case "file":
fileReader, err := NewDashboardFileReader(config, logger.New("type", config.Type, "name", config.Name))
if err != nil {
- return nil, errors.Wrapf(err, "Failed to create file reader for config %v", config.Name)
+ return nil, errutil.Wrapf(err, "Failed to create file reader for config %v", config.Name)
}
readers = append(readers, fileReader)
default:
diff --git a/pkg/services/provisioning/provisioning.go b/pkg/services/provisioning/provisioning.go
index 7a88aa9e3391..025b5fe311d1 100644
--- a/pkg/services/provisioning/provisioning.go
+++ b/pkg/services/provisioning/provisioning.go
@@ -6,7 +6,7 @@ import (
"sync"
"github.com/grafana/grafana/pkg/infra/log"
- "github.com/pkg/errors"
+ "github.com/grafana/grafana/pkg/util/errutil"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
@@ -103,20 +103,20 @@ func (ps *provisioningServiceImpl) Run(ctx context.Context) error {
func (ps *provisioningServiceImpl) ProvisionDatasources() error {
datasourcePath := path.Join(ps.Cfg.ProvisioningPath, "datasources")
err := ps.provisionDatasources(datasourcePath)
- return errors.Wrap(err, "Datasource provisioning error")
+ return errutil.Wrap("Datasource provisioning error", err)
}
func (ps *provisioningServiceImpl) ProvisionNotifications() error {
alertNotificationsPath := path.Join(ps.Cfg.ProvisioningPath, "notifiers")
err := ps.provisionNotifiers(alertNotificationsPath)
- return errors.Wrap(err, "Alert notification provisioning error")
+ return errutil.Wrap("Alert notification provisioning error", err)
}
func (ps *provisioningServiceImpl) ProvisionDashboards() error {
dashboardPath := path.Join(ps.Cfg.ProvisioningPath, "dashboards")
dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath)
if err != nil {
- return errors.Wrap(err, "Failed to create provisioner")
+ return errutil.Wrap("Failed to create provisioner", err)
}
ps.mutex.Lock()
@@ -127,7 +127,7 @@ func (ps *provisioningServiceImpl) ProvisionDashboards() error {
if err := dashProvisioner.Provision(); err != nil {
// If we fail to provision with the new provisioner, mutex will unlock and the polling we restart with the
// old provisioner as we did not switch them yet.
- return errors.Wrap(err, "Failed to provision dashboards")
+ return errutil.Wrap("Failed to provision dashboards", err)
}
ps.dashboardProvisioner = dashProvisioner
return nil
diff --git a/pkg/services/provisioning/values/values.go b/pkg/services/provisioning/values/values.go
index 48c7cd6444e6..8c2aa96b20fa 100644
--- a/pkg/services/provisioning/values/values.go
+++ b/pkg/services/provisioning/values/values.go
@@ -11,10 +11,11 @@
package values
import (
- "github.com/pkg/errors"
"os"
"reflect"
"strconv"
+
+ "github.com/grafana/grafana/pkg/util/errutil"
)
type IntValue struct {
@@ -33,7 +34,7 @@ func (val *IntValue) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
val.Raw = interpolated.raw
val.value, err = strconv.Atoi(interpolated.value)
- return errors.Wrap(err, "cannot convert value int")
+ return errutil.Wrap("cannot convert value int", err)
}
func (val *IntValue) Value() int {
diff --git a/pkg/util/errutil/errors.go b/pkg/util/errutil/errors.go
index 37f26ff9564c..65edd11779b9 100644
--- a/pkg/util/errutil/errors.go
+++ b/pkg/util/errutil/errors.go
@@ -1,11 +1,29 @@
package errutil
-import "golang.org/x/xerrors"
+import (
+ "fmt"
+
+ "golang.org/x/xerrors"
+)
// Wrap is a simple wrapper around Errorf that is doing error wrapping. You can read how that works in
// https://godoc.org/golang.org/x/xerrors#Errorf but its API is very implicit which is a reason for this wrapper.
// There is also a discussion (https://github.com/golang/go/issues/29934) where many comments make arguments for such
// wrapper so hopefully it will be added in the standard lib later.
func Wrap(message string, err error) error {
+ if err == nil {
+ return nil
+ }
+
return xerrors.Errorf("%v: %w", message, err)
}
+
+// Wrapf is a simple wrapper around Errorf that is doing error wrapping
+// Wrapf allows you to send a format and args instead of just a message.
+func Wrapf(err error, message string, a ...interface{}) error {
+ if err == nil {
+ return nil
+ }
+
+ return Wrap(fmt.Sprintf(message, a...), err)
+}
diff --git a/vendor/github.com/robfig/cron/README.md b/vendor/github.com/robfig/cron/README.md
index 4e0ae1c25f39..ec40c95fcb9d 100644
--- a/vendor/github.com/robfig/cron/README.md
+++ b/vendor/github.com/robfig/cron/README.md
@@ -1,4 +1,4 @@
-[![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron)
+[![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron)
[![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron)
# cron
diff --git a/vendor/github.com/robfig/cron/doc.go b/vendor/github.com/robfig/cron/doc.go
index 1ce84f7bf462..d02ec2f3b563 100644
--- a/vendor/github.com/robfig/cron/doc.go
+++ b/vendor/github.com/robfig/cron/doc.go
@@ -84,7 +84,7 @@ You may use one of several pre-defined schedules in place of a cron expression.
Intervals
-You may also schedule a job to execute at fixed intervals, starting at the time it's added
+You may also schedule a job to execute at fixed intervals, starting at the time it's added
or cron is run. This is supported by formatting the cron spec like this:
@every
From b8e67a2a4c5adc6ec38cdae36222c6f60b13d5ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Wed, 15 May 2019 13:54:36 +0200
Subject: [PATCH 038/166] Release: Updated changelog
---
CHANGELOG.md | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7183196d91f4..3e8da7947b7b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,31 @@
# 6.2.0 (unreleased)
+# 6.2.0-beta2 (2019-05-15)
+
+### Features / Enhancements
+ * **Plugins**: Support templated urls in plugin routes. [#16599](https://github.com/grafana/grafana/pull/16599), [@briangann](https://github.com/briangann)
+ * **Packaging**: New MSI windows installer package**. [#17073](https://github.com/grafana/grafana/pull/17073), [@briangann](https://github.com/briangann)
+
+ ### Bug Fixes
+ * **Dashboard**: Fixes blank dashboard after window resize with panel without title. [#16942](https://github.com/grafana/grafana/pull/16942), [@torkelo](https://github.com/torkelo)
+ * **Dashboard**: Fixes lazy loading & expanding collapsed rows on mobile. [#17055](https://github.com/grafana/grafana/pull/17055), [@torkelo](https://github.com/torkelo)
+ * **Dashboard**: Fixes scrolling issues for Edge browser. [#17033](https://github.com/grafana/grafana/pull/17033), [@jschill](https://github.com/jschill)
+ * **Dashboard**: Show refresh button in first kiosk(tv) mode. [#17032](https://github.com/grafana/grafana/pull/17032), [@torkelo](https://github.com/torkelo)
+ * **Explore**: Fix empty result from datasource should render logs container. [#16999](https://github.com/grafana/grafana/pull/16999), [@marefr](https://github.com/marefr)
+ * **Explore**: Fixes so clicking in a Prometheus Table the query is filtered by clicked value. [#17083](https://github.com/grafana/grafana/pull/17083), [@hugohaggmark](https://github.com/hugohaggmark)
+ * **Explore**: Makes it possible to zoom in Explore/Loki/Graph without exception. [#16991](https://github.com/grafana/grafana/pull/16991), [@hugohaggmark](https://github.com/hugohaggmark)
+ * **Gauge**: Fixes orientation issue after switching from BarGauge to Gauge. [#17064](https://github.com/grafana/grafana/pull/17064), [@torkelo](https://github.com/torkelo)
+ * **GettingStarted**: Fixes layout issues in getting started panel. [#16941](https://github.com/grafana/grafana/pull/16941), [@torkelo](https://github.com/torkelo)
+ * **InfluxDB**: Fix HTTP method should default to GET. [#16949](https://github.com/grafana/grafana/pull/16949), [@StephenSorriaux](https://github.com/StephenSorriaux)
+ * **Panels**: Fixed alert icon position in panel header. [#17070](https://github.com/grafana/grafana/pull/17070), [@torkelo](https://github.com/torkelo)
+ * **Panels**: Fixes panel error tooltip not showing. [#16993](https://github.com/grafana/grafana/pull/16993), [@torkelo](https://github.com/torkelo)
+ * **Plugins**: Fix how datemath utils are exposed to plugins. [#16976](https://github.com/grafana/grafana/pull/16976), [@marefr](https://github.com/marefr)
+ * **Singlestat**: fixed centering issue for very small panels. [#16944](https://github.com/grafana/grafana/pull/16944), [@torkelo](https://github.com/torkelo)
+ * **Search**: Scroll issue in dashboard search in latest Chrome. [#17054](https://github.com/grafana/grafana/pull/17054), [@jschill](https://github.com/jschill)
+ * **Docker**: Prevent a permission denied error when writing files to the default provisioning directory. [#16831](https://github.com/grafana/grafana/pull/16831), [@wmedlar](https://github.com/wmedlar)
+ * **Gauge**: Adds background shade to gauge track and improves height usage. [#17019](https://github.com/grafana/grafana/pull/17019), [@torkelo](https://github.com/torkelo)
+ * **RemoteCache**: Avoid race condition in Set causing error on insert. . [#17082](https://github.com/grafana/grafana/pull/17082), [@bergquist](https://github.com/bergquist)
+
# 6.2.0-beta1 (2019-05-07)
### Features / Enhancements
From f30e5cce59f54cfdbabb72ed508dad8edfccdd35 Mon Sep 17 00:00:00 2001
From: Johannes Schill
Date: Wed, 15 May 2019 14:40:53 +0200
Subject: [PATCH 039/166] Chore: Bump ts-node to 8.1.0 (#17093)
---
package.json | 2 +-
tsconfig.json | 3 ++-
yarn.lock | 8 ++++----
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index 85c953f907a0..3efe98d9453c 100644
--- a/package.json
+++ b/package.json
@@ -124,7 +124,7 @@
"terser-webpack-plugin": "1.2.3",
"ts-jest": "24.0.1",
"ts-loader": "5.3.3",
- "ts-node": "8.0.2",
+ "ts-node": "8.1.0",
"tslib": "1.9.3",
"tslint": "5.14.0",
"tslint-loader": "3.5.4",
diff --git a/tsconfig.json b/tsconfig.json
index 7eaffeac0783..d58fc1aa6571 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -32,7 +32,8 @@
"app": ["app"],
"sass": ["sass"]
},
- "skipLibCheck": true
+ "skipLibCheck": true,
+ "preserveSymlinks": true
},
"include": [
"public/app/**/*.ts",
diff --git a/yarn.lock b/yarn.lock
index d78a984dd926..3de68fd75d66 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16947,10 +16947,10 @@ ts-loader@5.3.3:
micromatch "^3.1.4"
semver "^5.0.1"
-ts-node@8.0.2:
- version "8.0.2"
- resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.0.2.tgz#9ecdf8d782a0ca4c80d1d641cbb236af4ac1b756"
- integrity sha512-MosTrinKmaAcWgO8tqMjMJB22h+sp3Rd1i4fdoWY4mhBDekOwIAKI/bzmRi7IcbCmjquccYg2gcF6NBkLgr0Tw==
+ts-node@8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.1.0.tgz#8c4b37036abd448577db22a061fd7a67d47e658e"
+ integrity sha512-34jpuOrxDuf+O6iW1JpgTRDFynUZ1iEqtYruBqh35gICNjN8x+LpVcPAcwzLPi9VU6mdA3ym+x233nZmZp445A==
dependencies:
arg "^4.1.0"
diff "^3.1.0"
From 8483a8366f828cb5fdccf51c5e68186143f48cb8 Mon Sep 17 00:00:00 2001
From: Johannes Schill
Date: Wed, 15 May 2019 14:41:31 +0200
Subject: [PATCH 040/166] Chore: Bump jest to 24.8.0 (#17094)
---
package.json | 8 +++----
yarn.lock | 60 ++++++++++++++++++----------------------------------
2 files changed, 25 insertions(+), 43 deletions(-)
diff --git a/package.json b/package.json
index 3efe98d9453c..c7cc8a83644e 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
"@types/enzyme": "3.9.0",
"@types/expect-puppeteer": "3.3.1",
"@types/inquirer": "0.0.43",
- "@types/jest": "24.0.11",
+ "@types/jest": "24.0.13",
"@types/jquery": "1.10.35",
"@types/lodash": "4.14.123",
"@types/node": "11.13.4",
@@ -46,7 +46,7 @@
"autoprefixer": "9.5.0",
"axios": "0.18.0",
"babel-core": "7.0.0-bridge.0",
- "babel-jest": "24.6.0",
+ "babel-jest": "24.8.0",
"babel-loader": "8.0.5",
"babel-plugin-angularjs-annotate": "0.10.0",
"chalk": "2.4.2",
@@ -84,7 +84,7 @@
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"inquirer": "6.2.2",
- "jest": "24.6.0",
+ "jest": "24.8.0",
"jest-date-mock": "1.0.7",
"lint-staged": "8.1.5",
"load-grunt-tasks": "3.5.2",
@@ -122,7 +122,7 @@
"systemjs": "0.20.19",
"systemjs-plugin-css": "0.1.37",
"terser-webpack-plugin": "1.2.3",
- "ts-jest": "24.0.1",
+ "ts-jest": "24.0.2",
"ts-loader": "5.3.3",
"ts-node": "8.1.0",
"tslib": "1.9.3",
diff --git a/yarn.lock b/yarn.lock
index 3de68fd75d66..1710b7e87102 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1289,7 +1289,7 @@
jest-runner "^24.8.0"
jest-runtime "^24.8.0"
-"@jest/transform@^24.6.0", "@jest/transform@^24.8.0":
+"@jest/transform@^24.8.0":
version "24.8.0"
resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.8.0.tgz#628fb99dce4f9d254c6fd9341e3eea262e06fef5"
integrity sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==
@@ -1310,7 +1310,7 @@
source-map "^0.6.1"
write-file-atomic "2.4.1"
-"@jest/types@^24.5.0", "@jest/types@^24.6.0", "@jest/types@^24.8.0":
+"@jest/types@^24.5.0", "@jest/types@^24.8.0":
version "24.8.0"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad"
integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==
@@ -2272,10 +2272,10 @@
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.14.tgz#37daaf78069e7948520474c87b80092ea912520a"
integrity sha512-Q5hTcfdudEL2yOmluA1zaSyPbzWPmJ3XfSWeP3RyoYvS9hnje1ZyagrZOuQ6+1nQC1Gw+7gap3pLNL3xL6UBug==
-"@types/jest@24.0.11":
- version "24.0.11"
- resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.11.tgz#1f099bea332c228ea6505a88159bfa86a5858340"
- integrity sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==
+"@types/jest@24.0.13":
+ version "24.0.13"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.13.tgz#10f50b64cb05fb02411fbba49e9042a3a11da3f9"
+ integrity sha512-3m6RPnO35r7Dg+uMLj1+xfZaOgIHHHut61djNjzwExXN4/Pm9has9C6I1KMYSfz7mahDhWUOVg4HW/nZdv5Pww==
dependencies:
"@types/jest-diff" "*"
@@ -2284,10 +2284,10 @@
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-1.10.35.tgz#4e5c2b1e5b3bf0b863efb8c5e70081f52e6c9518"
integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg==
-"@types/lodash@4.14.123":
- version "4.14.123"
- resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d"
- integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q==
+"@types/lodash@4.14.119", "@types/lodash@4.14.123":
+ version "4.14.119"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39"
+ integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw==
"@types/minimatch@*":
version "3.0.3"
@@ -3567,20 +3567,7 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0:
resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d"
integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==
-babel-jest@24.6.0:
- version "24.6.0"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.6.0.tgz#58aa1e6a3d72fdd986644a92529f0ec7d2f1cf61"
- integrity sha512-HpI/orChKlJZbWC2p52ghWeK+UYqU9ql+zYw+ctOr3vIuPZowcSL13RwReW5ZeYKxsRr8dZmQozGvPX93Gw1tw==
- dependencies:
- "@jest/transform" "^24.6.0"
- "@jest/types" "^24.6.0"
- "@types/babel__core" "^7.1.0"
- babel-plugin-istanbul "^5.1.0"
- babel-preset-jest "^24.6.0"
- chalk "^2.4.2"
- slash "^2.0.0"
-
-babel-jest@^24.8.0:
+babel-jest@24.8.0, babel-jest@^24.8.0:
version "24.8.0"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.8.0.tgz#5c15ff2b28e20b0f45df43fe6b7f2aae93dba589"
integrity sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==
@@ -4478,11 +4465,6 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
-caniuse-db@1.0.30000772:
- version "1.0.30000772"
- resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
- integrity sha1-UarokXaChureSj2DGep21qAbUSs=
-
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000947, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000963:
version "1.0.30000966"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000966.tgz#f3c6fefacfbfbfb981df6dfa68f2aae7bff41b64"
@@ -9770,7 +9752,7 @@ jest-changed-files@^24.8.0:
execa "^1.0.0"
throat "^4.0.0"
-jest-cli@^24.6.0:
+jest-cli@^24.8.0:
version "24.8.0"
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.8.0.tgz#b075ac914492ed114fa338ade7362a301693e989"
integrity sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==
@@ -10110,13 +10092,13 @@ jest-worker@^24.0.0, jest-worker@^24.6.0:
merge-stream "^1.0.1"
supports-color "^6.1.0"
-jest@24.6.0:
- version "24.6.0"
- resolved "https://registry.yarnpkg.com/jest/-/jest-24.6.0.tgz#133e46c3f92450402e5b6737f5a07620c3f8201e"
- integrity sha512-09Y/1FUQIGRVY2hdt0VpiL5mH0MGKeNM+Rhd1qWUZEBI/HwI6upHQR5XxlTm5d0BpXvhB/8bDpHu5ehL7JGi1g==
+jest@24.8.0:
+ version "24.8.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-24.8.0.tgz#d5dff1984d0d1002196e9b7f12f75af1b2809081"
+ integrity sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==
dependencies:
import-local "^2.0.0"
- jest-cli "^24.6.0"
+ jest-cli "^24.8.0"
jquery@3.4.0:
version "3.4.0"
@@ -16921,10 +16903,10 @@ tryor@~0.1.2:
resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b"
integrity sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=
-ts-jest@24.0.1:
- version "24.0.1"
- resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.0.1.tgz#77258061cc354c3fa8616b8ac03baa0f8580f854"
- integrity sha512-mgNZmYPuGBNgYpUzchI7vdSr6zATQI0TrSyzREnXHuPCvlW8T1DQ/fdscgx4ivS5vAMUGUaoxGdWIVHC5I8imw==
+ts-jest@24.0.2:
+ version "24.0.2"
+ resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.0.2.tgz#8dde6cece97c31c03e80e474c749753ffd27194d"
+ integrity sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==
dependencies:
bs-logger "0.x"
buffer-from "1.x"
From be66ed9dabcc7d8d76510b39330d523e3efe1da2 Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Thu, 16 May 2019 00:29:26 +0300
Subject: [PATCH 041/166] Chore: explore possibilities of using makefile
(#17039)
* Chore: explore possibilities of using makefile
This is an exploratory commit - I wanted to see how
revive/gosec linters could be integrated with makefile and our build scripts.
Looks better then I expected :)
* Chore: make revive happy
Revive execution was not supplied with path, if you restore there is couple
errors that were popping up - so I fixed them
* Chore: make revive happy
---
.gitignore | 6 +-
Makefile | 26 +++++++-
pkg/cmd/grafana-cli/services/services.go | 12 ++--
pkg/cmd/grafana-cli/utils/grafana_path.go | 10 +--
pkg/services/login/errors.go | 2 +-
.../provisioning/dashboards/dashboard_mock.go | 6 +-
.../provisioning/provisioning_mock.go | 12 ++--
pkg/services/provisioning/values/values.go | 2 +-
scripts/backend-lint.sh | 11 +---
scripts/{ => go/configs}/gosec.json | 0
scripts/{ => go/configs}/revive.toml | 0
scripts/go/go.mod | 8 +++
scripts/go/go.sum | 61 +++++++++++++++++++
13 files changed, 119 insertions(+), 37 deletions(-)
rename scripts/{ => go/configs}/gosec.json (100%)
rename scripts/{ => go/configs}/revive.toml (100%)
create mode 100644 scripts/go/go.mod
create mode 100644 scripts/go/go.sum
diff --git a/.gitignore b/.gitignore
index 50bfceb36861..2df94cb35c98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,10 +79,12 @@ debug.test
/scripts/build/release_publisher/release_publisher
*.patch
-
# Ignoring frontend packages specifics
/packages/**/dist
/packages/**/compiled
/packages/**/.rpt2_cache
-theOutput/
\ No newline at end of file
+theOutput/
+
+# Ignore go local build dependencies
+/scripts/go/bin/**
diff --git a/Makefile b/Makefile
index 2e06a9569651..8cfc7ce26812 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,9 @@
-include local/Makefile
-.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go test-go test-js test run clean
+.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go test-go test-js test run clean gosec revive
+
+GO := GO111MODULE=on go
+GO_FILES := ./pkg/...
all: deps build
@@ -66,3 +69,24 @@ clean:
node_modules: package.json yarn.lock
@echo "install frontend dependencies"
yarn install --pure-lockfile --no-progress
+
+scripts/go/bin/revive: scripts/go/go.mod
+ @cd scripts/go; \
+ $(GO) build -o ./bin/revive github.com/mgechev/revive
+
+scripts/go/bin/gosec: scripts/go/go.mod
+ @cd scripts/go; \
+ $(GO) build -o ./bin/gosec github.com/securego/gosec/cmd/gosec
+
+revive: scripts/go/bin/revive
+ @scripts/go/bin/revive \
+ -formatter stylish \
+ -config ./scripts/go/configs/revive.toml \
+ $(GO_FILES)
+
+# TODO recheck the rules and leave only necessary exclusions
+gosec: scripts/go/bin/gosec
+ @scripts/go/bin/gosec -quiet \
+ -exclude=G104,G107,G201,G202,G204,G301,G304,G401,G402,G501 \
+ -conf=./scripts/go/configs/gosec.json \
+ $(GO_FILES)
diff --git a/pkg/cmd/grafana-cli/services/services.go b/pkg/cmd/grafana-cli/services/services.go
index 1bc1fb7fb105..4501d4f6e4ee 100644
--- a/pkg/cmd/grafana-cli/services/services.go
+++ b/pkg/cmd/grafana-cli/services/services.go
@@ -18,10 +18,10 @@ import (
)
var (
- IoHelper m.IoUtil = IoUtilImp{}
- HttpClient http.Client
- grafanaVersion string
- NotFoundError = errors.New("404 not found error")
+ IoHelper m.IoUtil = IoUtilImp{}
+ HttpClient http.Client
+ grafanaVersion string
+ ErrNotFoundError = errors.New("404 not found error")
)
func Init(version string, skipTLSVerify bool) {
@@ -131,7 +131,7 @@ func GetPlugin(pluginId, repoUrl string) (m.Plugin, error) {
if err != nil {
logger.Info("Failed to send request: ", err)
- if err == NotFoundError {
+ if err == ErrNotFoundError {
return m.Plugin{}, fmt.Errorf("Failed to find requested plugin, check if the plugin_id is correct. error: %v", err)
}
return m.Plugin{}, fmt.Errorf("Failed to send request. error: %v", err)
@@ -174,7 +174,7 @@ func sendRequest(repoUrl string, subPaths ...string) ([]byte, error) {
}
if res.StatusCode == 404 {
- return []byte{}, NotFoundError
+ return []byte{}, ErrNotFoundError
}
if res.StatusCode/100 != 2 {
return []byte{}, fmt.Errorf("Api returned invalid status: %s", res.Status)
diff --git a/pkg/cmd/grafana-cli/utils/grafana_path.go b/pkg/cmd/grafana-cli/utils/grafana_path.go
index 956a8c26b596..373120a50c40 100644
--- a/pkg/cmd/grafana-cli/utils/grafana_path.go
+++ b/pkg/cmd/grafana-cli/utils/grafana_path.go
@@ -31,12 +31,12 @@ func isDevEnvironment() bool {
defaultsPath := filepath.Join(exPath, "../conf/defaults.ini")
_, err = os.Stat(defaultsPath)
return err == nil
- } else {
- // But at the same time there are per platform directories that contain the binaries and can also be used.
- defaultsPath := filepath.Join(exPath, "../../conf/defaults.ini")
- _, err = os.Stat(defaultsPath)
- return err == nil
}
+
+ // But at the same time there are per platform directories that contain the binaries and can also be used.
+ defaultsPath := filepath.Join(exPath, "../../conf/defaults.ini")
+ _, err = os.Stat(defaultsPath)
+ return err == nil
}
func returnOsDefault(currentOs string) string {
diff --git a/pkg/services/login/errors.go b/pkg/services/login/errors.go
index 0889b4613a6b..dbf15ea76cd4 100644
--- a/pkg/services/login/errors.go
+++ b/pkg/services/login/errors.go
@@ -9,7 +9,7 @@ var (
ErrProviderDeniedRequest = errors.New("Login provider denied login request")
ErrSignUpNotAllowed = errors.New("Signup is not allowed for this adapter")
ErrTooManyLoginAttempts = errors.New("Too many consecutive incorrect login attempts for user. Login for user temporarily blocked")
- ErrPasswordEmpty = errors.New("No password provided.")
+ ErrPasswordEmpty = errors.New("No password provided")
ErrUsersQuotaReached = errors.New("Users quota reached")
ErrGettingUserQuota = errors.New("Error getting user quota")
)
diff --git a/pkg/services/provisioning/dashboards/dashboard_mock.go b/pkg/services/provisioning/dashboards/dashboard_mock.go
index 303338106e63..2ad13744b295 100644
--- a/pkg/services/provisioning/dashboards/dashboard_mock.go
+++ b/pkg/services/provisioning/dashboards/dashboard_mock.go
@@ -25,9 +25,8 @@ func (dpm *DashboardProvisionerMock) Provision() error {
dpm.Calls.Provision = append(dpm.Calls.Provision, nil)
if dpm.ProvisionFunc != nil {
return dpm.ProvisionFunc()
- } else {
- return nil
}
+ return nil
}
func (dpm *DashboardProvisionerMock) PollChanges(ctx context.Context) {
@@ -41,7 +40,6 @@ func (dpm *DashboardProvisionerMock) GetProvisionerResolvedPath(name string) str
dpm.Calls.PollChanges = append(dpm.Calls.GetProvisionerResolvedPath, name)
if dpm.GetProvisionerResolvedPathFunc != nil {
return dpm.GetProvisionerResolvedPathFunc(name)
- } else {
- return ""
}
+ return ""
}
diff --git a/pkg/services/provisioning/provisioning_mock.go b/pkg/services/provisioning/provisioning_mock.go
index 7977e59b2e47..dc487f566465 100644
--- a/pkg/services/provisioning/provisioning_mock.go
+++ b/pkg/services/provisioning/provisioning_mock.go
@@ -25,34 +25,30 @@ func (mock *ProvisioningServiceMock) ProvisionDatasources() error {
mock.Calls.ProvisionDatasources = append(mock.Calls.ProvisionDatasources, nil)
if mock.ProvisionDatasourcesFunc != nil {
return mock.ProvisionDatasourcesFunc()
- } else {
- return nil
}
+ return nil
}
func (mock *ProvisioningServiceMock) ProvisionNotifications() error {
mock.Calls.ProvisionNotifications = append(mock.Calls.ProvisionNotifications, nil)
if mock.ProvisionNotificationsFunc != nil {
return mock.ProvisionNotificationsFunc()
- } else {
- return nil
}
+ return nil
}
func (mock *ProvisioningServiceMock) ProvisionDashboards() error {
mock.Calls.ProvisionDashboards = append(mock.Calls.ProvisionDashboards, nil)
if mock.ProvisionDashboardsFunc != nil {
return mock.ProvisionDashboardsFunc()
- } else {
- return nil
}
+ return nil
}
func (mock *ProvisioningServiceMock) GetDashboardProvisionerResolvedPath(name string) string {
mock.Calls.GetDashboardProvisionerResolvedPath = append(mock.Calls.GetDashboardProvisionerResolvedPath, name)
if mock.GetDashboardProvisionerResolvedPathFunc != nil {
return mock.GetDashboardProvisionerResolvedPathFunc(name)
- } else {
- return ""
}
+ return ""
}
diff --git a/pkg/services/provisioning/values/values.go b/pkg/services/provisioning/values/values.go
index 8c2aa96b20fa..63da76a9338d 100644
--- a/pkg/services/provisioning/values/values.go
+++ b/pkg/services/provisioning/values/values.go
@@ -1,4 +1,4 @@
-// A set of value types to use in provisioning. They add custom unmarshaling logic that puts the string values
+// Package values is a set of value types to use in provisioning. They add custom unmarshaling logic that puts the string values
// through os.ExpandEnv.
// Usage:
// type Data struct {
diff --git a/scripts/backend-lint.sh b/scripts/backend-lint.sh
index 5c337d2d2ea3..b447071efa0e 100755
--- a/scripts/backend-lint.sh
+++ b/scripts/backend-lint.sh
@@ -14,8 +14,6 @@ function exit_if_fail {
go get -u github.com/alecthomas/gometalinter
go get -u github.com/jgautheron/goconst/cmd/goconst
go get -u honnef.co/go/tools/cmd/staticcheck
-go get -u github.com/mgechev/revive
-go get -u github.com/securego/gosec/cmd/gosec/...
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
# use gometalinter when lints are not available in golangci or
@@ -36,10 +34,5 @@ exit_if_fail golangci-lint run --deadline 10m --disable-all \
exit_if_fail go vet ./pkg/...
-exit_if_fail revive -formatter stylish -config ./scripts/revive.toml
-
-# TODO recheck the rules and leave only necessary exclusions
-exit_if_fail gosec -quiet \
- -exclude=G104,G107,G201,G202,G204,G301,G304,G401,G402,G501 \
- -conf=./scripts/gosec.json \
- ./pkg/...
+exit_if_fail make revive
+exit_if_fail make gosec
diff --git a/scripts/gosec.json b/scripts/go/configs/gosec.json
similarity index 100%
rename from scripts/gosec.json
rename to scripts/go/configs/gosec.json
diff --git a/scripts/revive.toml b/scripts/go/configs/revive.toml
similarity index 100%
rename from scripts/revive.toml
rename to scripts/go/configs/revive.toml
diff --git a/scripts/go/go.mod b/scripts/go/go.mod
new file mode 100644
index 000000000000..9ad4ef6b1323
--- /dev/null
+++ b/scripts/go/go.mod
@@ -0,0 +1,8 @@
+module github.com/grafana/grafana/scripts/go
+
+go 1.12
+
+require (
+ github.com/mgechev/revive v0.0.0-20190505013521-22b849f28677 // indirect
+ github.com/securego/gosec v0.0.0-20190510081509-ee80733faf72 // indirect
+)
diff --git a/scripts/go/go.sum b/scripts/go/go.sum
new file mode 100644
index 000000000000..7c69bd63684f
--- /dev/null
+++ b/scripts/go/go.sum
@@ -0,0 +1,61 @@
+github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
+github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
+github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
+github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mgechev/dots v0.0.0-20180605013149-8e09d8ea2757 h1:KTwJ7Lo3KDKMknRYN5JEFRGIM4IkG59QjFFM2mxsMEU=
+github.com/mgechev/dots v0.0.0-20180605013149-8e09d8ea2757/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
+github.com/mgechev/revive v0.0.0-20190505013521-22b849f28677 h1:iOfPBHjVwDHHeKUerCrX3JRUU9YbRewSTzirEYE0x78=
+github.com/mgechev/revive v0.0.0-20190505013521-22b849f28677/go.mod h1:pVHj2KvxEhotJ6Lmr7zb3YgNMX1QKt8cyp6fdPHOrzU=
+github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
+github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
+github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
+github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc h1:rQ1O4ZLYR2xXHXgBCCfIIGnuZ0lidMQw2S5n1oOv+Wg=
+github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+github.com/securego/gosec v0.0.0-20190510081509-ee80733faf72 h1:403cuEGt3niQPAau/if+Fp34KjBzMKGejdBCu486W3s=
+github.com/securego/gosec v0.0.0-20190510081509-ee80733faf72/go.mod h1:shk+oGa7JTGg9taMxXk2skTwpt9KQAbryuwFIHCm/fw=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1 h1:dzEuQYa6+a3gROnSlgly5ERUm4SZKJt+dh+4iSbO+bI=
+golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
+golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
From e6001f57a249678469a60f076cc6530827f7c7dd Mon Sep 17 00:00:00 2001
From: Marcus Efraimsson
Date: Thu, 16 May 2019 09:52:22 +0200
Subject: [PATCH 042/166] Explore: Allow switching between metrics and logs
(#16959)
Adds basic support for switching between Metrics and Logs in Explore.
Currently only test datasource that supports both Metrics and Logs.
Summary of changes:
* Moves mode (Metric, Logs) selection to the left of datasource
picker and add some quick styling.
* Only trigger change in ToggleButton if not selected
* Set correct mode if datasource only supports logs
Closes #16808
---
.../ToggleButtonGroup/ToggleButtonGroup.tsx | 2 +-
public/app/features/explore/Explore.tsx | 25 +++---
.../app/features/explore/ExploreToolbar.tsx | 78 ++++++++++++++++++-
public/app/features/explore/QueryEditor.tsx | 3 +-
public/app/features/explore/QueryRow.tsx | 1 +
.../app/features/explore/state/actionTypes.ts | 11 +++
public/app/features/explore/state/actions.ts | 22 ++++--
.../features/explore/state/reducers.test.ts | 15 ++++
public/app/features/explore/state/reducers.ts | 29 ++++++-
.../plugins/datasource/testdata/plugin.json | 1 +
public/app/types/explore.ts | 7 ++
public/sass/pages/_explore.scss | 19 +++++
12 files changed, 187 insertions(+), 26 deletions(-)
diff --git a/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx b/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx
index ce1f80c8dcae..07a39cfd1094 100644
--- a/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx
+++ b/public/app/core/components/ToggleButtonGroup/ToggleButtonGroup.tsx
@@ -39,7 +39,7 @@ export const ToggleButton: FC = ({
}) => {
const onClick = (event: React.SyntheticEvent) => {
event.stopPropagation();
- if (onChange) {
+ if (!selected && onChange) {
onChange(value);
}
};
diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx
index 895ae0eeec87..21e047399cd6 100644
--- a/public/app/features/explore/Explore.tsx
+++ b/public/app/features/explore/Explore.tsx
@@ -39,6 +39,7 @@ import {
ExploreId,
ExploreUpdateState,
ExploreUIState,
+ ExploreMode,
} from 'app/types/explore';
import { StoreState } from 'app/types';
import {
@@ -79,15 +80,13 @@ interface ExploreProps {
setQueries: typeof setQueries;
split: boolean;
showingStartPage?: boolean;
- supportsGraph: boolean | null;
- supportsLogs: boolean | null;
- supportsTable: boolean | null;
queryKeys: string[];
initialDatasource: string;
initialQueries: DataQuery[];
initialRange: RawTimeRange;
initialUI: ExploreUIState;
queryErrors: DataQueryError[];
+ mode: ExploreMode;
}
/**
@@ -234,11 +233,9 @@ export class Explore extends React.PureComponent {
exploreId,
showingStartPage,
split,
- supportsGraph,
- supportsLogs,
- supportsTable,
queryKeys,
queryErrors,
+ mode,
} = this.props;
const exploreClass = split ? 'explore explore-split' : 'explore';
@@ -273,9 +270,11 @@ export class Explore extends React.PureComponent {
{showingStartPage && }
{!showingStartPage && (
<>
- {supportsGraph && !supportsLogs && }
- {supportsTable && }
- {supportsLogs && (
+ {mode === ExploreMode.Metrics && }
+ {mode === ExploreMode.Metrics && (
+
+ )}
+ {mode === ExploreMode.Logs && (
>;
+ selectedModeOption: SelectOptionItem;
}
interface DispatchProps {
@@ -70,6 +81,7 @@ interface DispatchProps {
closeSplit: typeof splitClose;
split: typeof splitOpen;
changeRefreshInterval: typeof changeRefreshInterval;
+ changeMode: typeof changeMode;
}
type Props = StateProps & DispatchProps & OwnProps;
@@ -100,6 +112,11 @@ export class UnConnectedExploreToolbar extends PureComponent {
changeRefreshInterval(exploreId, item);
};
+ onModeChange = (mode: ExploreMode) => {
+ const { changeMode, exploreId } = this.props;
+ changeMode(exploreId, mode);
+ };
+
render() {
const {
datasourceMissing,
@@ -115,6 +132,8 @@ export class UnConnectedExploreToolbar extends PureComponent {
refreshInterval,
onChangeTime,
split,
+ supportedModeOptions,
+ selectedModeOption,
} = this.props;
return (
@@ -147,8 +166,31 @@ export class UnConnectedExploreToolbar extends PureComponent {
current={selectedDatasource}
/>
+ {supportedModeOptions.length > 1 ? (
+
+
+
+ {'Metrics'}
+
+
+ {'Logs'}
+
+
+
+ ) : null}
) : null}
+
{exploreId === 'left' && !splitted ? (
{createResponsiveButton({
@@ -208,12 +250,41 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps
graphIsLoading,
logIsLoading,
tableIsLoading,
+ supportedModes,
+ mode,
} = exploreItem;
const selectedDatasource = datasourceInstance
? exploreDatasources.find(datasource => datasource.name === datasourceInstance.name)
: undefined;
const loading = graphIsLoading || logIsLoading || tableIsLoading;
+ const supportedModeOptions: Array
> = [];
+ let selectedModeOption = null;
+ for (const supportedMode of supportedModes) {
+ switch (supportedMode) {
+ case ExploreMode.Metrics:
+ const option1 = {
+ value: ExploreMode.Metrics,
+ label: ExploreMode.Metrics,
+ };
+ supportedModeOptions.push(option1);
+ if (mode === ExploreMode.Metrics) {
+ selectedModeOption = option1;
+ }
+ break;
+ case ExploreMode.Logs:
+ const option2 = {
+ value: ExploreMode.Logs,
+ label: ExploreMode.Logs,
+ };
+ supportedModeOptions.push(option2);
+ if (mode === ExploreMode.Logs) {
+ selectedModeOption = option2;
+ }
+ break;
+ }
+ }
+
return {
datasourceMissing,
exploreDatasources,
@@ -223,6 +294,8 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps
selectedDatasource,
splitted,
refreshInterval,
+ supportedModeOptions,
+ selectedModeOption,
};
};
@@ -233,6 +306,7 @@ const mapDispatchToProps: DispatchProps = {
runQueries,
closeSplit: splitClose,
split: splitOpen,
+ changeMode: changeMode,
};
export const ExploreToolbar = hot(module)(
diff --git a/public/app/features/explore/QueryEditor.tsx b/public/app/features/explore/QueryEditor.tsx
index 30f876886c19..54927b8cc91b 100644
--- a/public/app/features/explore/QueryEditor.tsx
+++ b/public/app/features/explore/QueryEditor.tsx
@@ -35,7 +35,7 @@ export default class QueryEditor extends PureComponent {
const loader = getAngularLoader();
const template = ' ';
- const target = { datasource: datasource.name, ...initialQuery };
+ const target = { ...initialQuery };
const scopeProps = {
ctrl: {
datasource,
@@ -60,6 +60,7 @@ export default class QueryEditor extends PureComponent {
this.component = loader.load(this.element, scopeProps, template);
setTimeout(() => {
this.props.onQueryChange(target);
+ this.props.onExecuteQuery();
}, 1);
}
diff --git a/public/app/features/explore/QueryRow.tsx b/public/app/features/explore/QueryRow.tsx
index fc4a5e025e94..2a0429dbd971 100644
--- a/public/app/features/explore/QueryRow.tsx
+++ b/public/app/features/explore/QueryRow.tsx
@@ -38,6 +38,7 @@ interface QueryRowProps extends PropsFromParent {
addQueryRow: typeof addQueryRow;
changeQuery: typeof changeQuery;
className?: string;
+ exploreId: ExploreId;
datasourceInstance: ExploreDataSourceApi;
datasourceStatus: DataSourceStatus;
highlightLogsExpressionAction: typeof highlightLogsExpressionAction;
diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts
index cce8d649c9d2..225a672ae2e5 100644
--- a/public/app/features/explore/state/actionTypes.ts
+++ b/public/app/features/explore/state/actionTypes.ts
@@ -18,6 +18,7 @@ import {
ResultType,
QueryTransaction,
ExploreUIState,
+ ExploreMode,
} from 'app/types/explore';
import { actionCreatorFactory, noPayloadActionCreatorFactory, ActionOf } from 'app/core/redux/actionCreatorFactory';
@@ -49,6 +50,11 @@ export interface AddQueryRowPayload {
query: DataQuery;
}
+export interface ChangeModePayload {
+ exploreId: ExploreId;
+ mode: ExploreMode;
+}
+
export interface ChangeQueryPayload {
exploreId: ExploreId;
query: DataQuery;
@@ -245,6 +251,11 @@ export const addQueryRowAction = actionCreatorFactory('explo
*/
export const changeDatasourceAction = noPayloadActionCreatorFactory('explore/CHANGE_DATASOURCE').create();
+/**
+ * Change the mode of Explore.
+ */
+export const changeModeAction = actionCreatorFactory('explore/CHANGE_MODE').create();
+
/**
* Query change handler for the query row with the given index.
* If `override` is reset the query modifications and run the queries. Use this to set queries via a link.
diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts
index 12b1a1b8b69d..838685d5a47d 100644
--- a/public/app/features/explore/state/actions.ts
+++ b/public/app/features/explore/state/actions.ts
@@ -44,6 +44,7 @@ import {
QueryOptions,
ExploreUIState,
QueryTransaction,
+ ExploreMode,
} from 'app/types/explore';
import {
updateDatasourceInstanceAction,
@@ -85,6 +86,7 @@ import {
queryStartAction,
historyUpdatedAction,
resetQueryErrorAction,
+ changeModeAction,
} from './actionTypes';
import { ActionOf, ActionCreator } from 'app/core/redux/actionCreatorFactory';
import { LogsDedupStrategy } from 'app/core/logs_model';
@@ -140,6 +142,16 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun
};
}
+/**
+ * Change the display mode in Explore.
+ */
+export function changeMode(exploreId: ExploreId, mode: ExploreMode): ThunkResult {
+ return dispatch => {
+ dispatch(changeModeAction({ exploreId, mode }));
+ dispatch(runQueries(exploreId));
+ };
+}
+
/**
* Query change handler for the query row with the given index.
* If `override` is reset the query modifications and run the queries. Use this to set queries via a link.
@@ -509,11 +521,9 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkRe
showingLogs,
showingGraph,
showingTable,
- supportsGraph,
- supportsLogs,
- supportsTable,
datasourceError,
containerWidth,
+ mode,
} = getState().explore[exploreId];
if (datasourceError) {
@@ -533,7 +543,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkRe
dispatch(runQueriesAction({ exploreId }));
// Keep table queries first since they need to return quickly
- if ((ignoreUIState || showingTable) && supportsTable) {
+ if ((ignoreUIState || showingTable) && mode === ExploreMode.Metrics) {
dispatch(
runQueriesForType(exploreId, 'Table', {
interval,
@@ -543,7 +553,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkRe
})
);
}
- if ((ignoreUIState || showingGraph) && supportsGraph) {
+ if ((ignoreUIState || showingGraph) && mode === ExploreMode.Metrics) {
dispatch(
runQueriesForType(exploreId, 'Graph', {
interval,
@@ -553,7 +563,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkRe
})
);
}
- if ((ignoreUIState || showingLogs) && supportsLogs) {
+ if ((ignoreUIState || showingLogs) && mode === ExploreMode.Logs) {
dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' }));
}
diff --git a/public/app/features/explore/state/reducers.test.ts b/public/app/features/explore/state/reducers.test.ts
index 428d208e17a8..c5ee8dbb7798 100644
--- a/public/app/features/explore/state/reducers.test.ts
+++ b/public/app/features/explore/state/reducers.test.ts
@@ -12,6 +12,7 @@ import {
ExploreState,
QueryTransaction,
RangeScanner,
+ ExploreMode,
} from 'app/types/explore';
import { reducerTester } from 'test/core/redux/reducerTester';
import {
@@ -23,6 +24,7 @@ import {
updateDatasourceInstanceAction,
splitOpenAction,
splitCloseAction,
+ changeModeAction,
} from './actionTypes';
import { Reducer } from 'redux';
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
@@ -122,6 +124,17 @@ describe('Explore item reducer', () => {
.thenStateShouldEqual(expectedState);
});
});
+
+ describe('when changeDataType is dispatched', () => {
+ it('then it should set correct state', () => {
+ reducerTester()
+ .givenReducer(itemReducer, {})
+ .whenActionIsDispatched(changeModeAction({ exploreId: ExploreId.left, mode: ExploreMode.Logs }))
+ .thenStateShouldEqual({
+ mode: ExploreMode.Logs,
+ });
+ });
+ });
});
describe('changing datasource', () => {
@@ -160,6 +173,8 @@ describe('Explore item reducer', () => {
showingStartPage: true,
queries,
queryKeys,
+ supportedModes: [ExploreMode.Metrics, ExploreMode.Logs],
+ mode: ExploreMode.Metrics,
};
reducerTester()
diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts
index 9807bcb8ad85..f0847360cbe3 100644
--- a/public/app/features/explore/state/reducers.ts
+++ b/public/app/features/explore/state/reducers.ts
@@ -8,7 +8,7 @@ import {
DEFAULT_UI_STATE,
generateNewKeyAndAddRefIdIfMissing,
} from 'app/core/utils/explore';
-import { ExploreItemState, ExploreState, ExploreId, ExploreUpdateState } from 'app/types/explore';
+import { ExploreItemState, ExploreState, ExploreId, ExploreUpdateState, ExploreMode } from 'app/types/explore';
import { DataQuery } from '@grafana/ui/src/types';
import {
HigherOrderAction,
@@ -22,6 +22,7 @@ import {
runQueriesAction,
historyUpdatedAction,
resetQueryErrorAction,
+ changeModeAction,
} from './actionTypes';
import { reducerFactory } from 'app/core/redux';
import {
@@ -107,6 +108,8 @@ export const makeExploreItemState = (): ExploreItemState => ({
update: makeInitialUpdateState(),
queryErrors: [],
latency: 0,
+ supportedModes: [],
+ mode: null,
});
/**
@@ -165,6 +168,13 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
return { ...state, containerWidth };
},
})
+ .addMapper({
+ filter: changeModeAction,
+ mapper: (state, action): ExploreItemState => {
+ const mode = action.payload.mode;
+ return { ...state, mode };
+ },
+ })
.addMapper({
filter: changeTimeAction,
mapper: (state, action): ExploreItemState => {
@@ -226,6 +236,21 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
const supportsLogs = datasourceInstance.meta.logs;
const supportsTable = datasourceInstance.meta.tables;
+ let mode = ExploreMode.Metrics;
+ const supportedModes: ExploreMode[] = [];
+
+ if (supportsGraph) {
+ supportedModes.push(ExploreMode.Metrics);
+ }
+
+ if (supportsLogs) {
+ supportedModes.push(ExploreMode.Logs);
+ }
+
+ if (supportedModes.length === 1) {
+ mode = supportedModes[0];
+ }
+
// Custom components
const StartPage = datasourceInstance.components.ExploreStartPage;
@@ -243,6 +268,8 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
StartPage,
showingStartPage: Boolean(StartPage),
queryKeys: getQueryKeys(state.queries, datasourceInstance),
+ supportedModes,
+ mode,
};
},
})
diff --git a/public/app/plugins/datasource/testdata/plugin.json b/public/app/plugins/datasource/testdata/plugin.json
index 96c23b196659..f34498957be2 100644
--- a/public/app/plugins/datasource/testdata/plugin.json
+++ b/public/app/plugins/datasource/testdata/plugin.json
@@ -4,6 +4,7 @@
"id": "testdata",
"metrics": true,
+ "logs": true,
"alerting": true,
"annotations": true,
diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts
index 404e53c28320..a828cbf9d3ad 100644
--- a/public/app/types/explore.ts
+++ b/public/app/types/explore.ts
@@ -17,6 +17,11 @@ import { Emitter, TimeSeries } from 'app/core/core';
import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model';
import TableModel from 'app/core/table_model';
+export enum ExploreMode {
+ Metrics = 'Metrics',
+ Logs = 'Logs',
+}
+
export interface CompletionItem {
/**
* The label of this completion item. By default
@@ -258,6 +263,8 @@ export interface ExploreItemState {
queryErrors: DataQueryError[];
latency: number;
+ supportedModes: ExploreMode[];
+ mode: ExploreMode;
}
export interface ExploreUpdateState {
diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss
index dba0614e11b0..d77992038327 100644
--- a/public/sass/pages/_explore.scss
+++ b/public/sass/pages/_explore.scss
@@ -92,6 +92,7 @@
.explore-toolbar-content-item:first-child {
padding-left: $dashboard-padding;
margin-right: auto;
+ display: flex;
}
@media only screen and (max-width: 1545px) {
@@ -413,3 +414,21 @@
margin: $space-xs 0;
cursor: pointer;
}
+
+.query-type-toggle {
+ margin-left: 5px;
+
+ .toggle-button-group {
+ padding-top: 2px;
+ }
+
+ .btn.active {
+ background-color: $input-bg;
+ background-image: none;
+ background-clip: padding-box;
+ border: $input-border;
+ border-radius: $input-border-radius;
+ @include box-shadow($input-box-shadow);
+ color: $input-color;
+ }
+}
From b11eeadbd98b41f84171d39e9ba41c7e3ce0f460 Mon Sep 17 00:00:00 2001
From: Johannes Schill
Date: Thu, 16 May 2019 11:02:21 +0200
Subject: [PATCH 043/166] fix: Initial url update in Explore should replace
existing url history #17030 (#17061)
---
public/app/features/explore/state/actions.ts | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts
index 838685d5a47d..310f310e6710 100644
--- a/public/app/features/explore/state/actions.ts
+++ b/public/app/features/explore/state/actions.ts
@@ -119,7 +119,7 @@ export function addQueryRow(exploreId: ExploreId, index: number): ThunkResult {
+export function changeDatasource(exploreId: ExploreId, datasource: string, replaceUrl = false): ThunkResult {
return async (dispatch, getState) => {
let newDataSourceInstance: DataSourceApi = null;
@@ -137,8 +137,7 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun
dispatch(updateDatasourceInstanceAction({ exploreId, datasourceInstance: newDataSourceInstance }));
await dispatch(loadDatasource(exploreId, newDataSourceInstance));
-
- dispatch(runQueries(exploreId));
+ dispatch(runQueries(exploreId, false, replaceUrl));
};
}
@@ -244,7 +243,7 @@ export function loadExploreDatasourcesAndSetDatasource(
dispatch(loadExploreDatasources({ exploreId, exploreDatasources }));
if (exploreDatasources.length >= 1) {
- dispatch(changeDatasource(exploreId, datasourceName));
+ dispatch(changeDatasource(exploreId, datasourceName, true));
} else {
dispatch(loadDatasourceMissingAction({ exploreId }));
}
@@ -513,7 +512,7 @@ export function processQueryResults(
/**
* Main action to run queries and dispatches sub-actions based on which result viewers are active
*/
-export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkResult {
+export function runQueries(exploreId: ExploreId, ignoreUIState = false, replaceUrl = false): ThunkResult {
return (dispatch, getState) => {
const {
datasourceInstance,
@@ -533,7 +532,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkRe
if (!hasNonEmptyQuery(queries)) {
dispatch(clearQueriesAction({ exploreId }));
- dispatch(stateSave()); // Remember to saves to state and update location
+ dispatch(stateSave(replaceUrl)); // Remember to save to state and update location
return;
}
@@ -567,7 +566,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false): ThunkRe
dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' }));
}
- dispatch(stateSave());
+ dispatch(stateSave(replaceUrl));
};
}
@@ -691,7 +690,7 @@ const toRawTimeRange = (range: TimeRange): RawTimeRange => {
* Saves Explore state to URL using the `left` and `right` parameters.
* If split view is not active, `right` will not be set.
*/
-export function stateSave(): ThunkResult {
+export function stateSave(replaceUrl = false): ThunkResult {
return (dispatch, getState) => {
const { left, right, split } = getState().explore;
const urlStates: { [index: string]: string } = {};
@@ -723,7 +722,7 @@ export function stateSave(): ThunkResult {
urlStates.right = serializeStateToUrlParam(rightUrlState, true);
}
- dispatch(updateLocation({ query: urlStates }));
+ dispatch(updateLocation({ query: urlStates, replace: replaceUrl }));
};
}
From 8196642f25750f31f38c323ed419d22d77ce97ef Mon Sep 17 00:00:00 2001
From: Jannik F
Date: Thu, 16 May 2019 11:11:08 +0200
Subject: [PATCH 044/166] Adjusted documentation for gcs to reflect the code
(#16947)
The key name for the bucket in GCS configuration is called bucket, not bucket name or bucket_name.
---
docs/sources/installation/configuration.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md
index 6e249a7130e0..a865234ebeba 100644
--- a/docs/sources/installation/configuration.md
+++ b/docs/sources/installation/configuration.md
@@ -579,7 +579,7 @@ Service Account keys can be created and downloaded from https://console.develope
Service Account should have "Storage Object Writer" role. The access control model of the bucket needs to be "Set object-level and bucket-level permissions". Grafana itself will make the images public readable.
-### bucket name
+### bucket
Bucket Name on Google Cloud Storage.
### path
From 2904e2d9fe256a55048ecdd6376ef55afed619ba Mon Sep 17 00:00:00 2001
From: Carl Bergquist
Date: Thu, 16 May 2019 11:41:57 +0200
Subject: [PATCH 045/166] Alertmanager: Replace illegal chars with underscore
in label names (#17002)
closes #16624
---
.../alerting/notifiers/alertmanager.go | 45 ++++++++++++-------
.../alerting/notifiers/alertmanager_test.go | 27 +++++++++--
2 files changed, 54 insertions(+), 18 deletions(-)
diff --git a/pkg/services/alerting/notifiers/alertmanager.go b/pkg/services/alerting/notifiers/alertmanager.go
index b90f9e65792c..bc2807d0d3cd 100644
--- a/pkg/services/alerting/notifiers/alertmanager.go
+++ b/pkg/services/alerting/notifiers/alertmanager.go
@@ -2,6 +2,7 @@ package notifiers
import (
"context"
+ "regexp"
"time"
"github.com/grafana/grafana/pkg/bus"
@@ -27,6 +28,7 @@ func init() {
})
}
+// NewAlertmanagerNotifier returns a new Alertmanager notifier
func NewAlertmanagerNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
url := model.Settings.Get("url").MustString()
if url == "" {
@@ -35,38 +37,42 @@ func NewAlertmanagerNotifier(model *models.AlertNotification) (alerting.Notifier
return &AlertmanagerNotifier{
NotifierBase: NewNotifierBase(model),
- Url: url,
+ URL: url,
log: log.New("alerting.notifier.prometheus-alertmanager"),
}, nil
}
+// AlertmanagerNotifier sends alert notifications to the alert manager
type AlertmanagerNotifier struct {
NotifierBase
- Url string
+ URL string
log log.Logger
}
-func (this *AlertmanagerNotifier) ShouldNotify(ctx context.Context, evalContext *alerting.EvalContext, notificationState *models.AlertNotificationState) bool {
- this.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)
+// ShouldNotify returns true if the notifiers should be used depending on state
+func (am *AlertmanagerNotifier) ShouldNotify(ctx context.Context, evalContext *alerting.EvalContext, notificationState *models.AlertNotificationState) bool {
+ am.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)
// Do not notify when we become OK for the first time.
if (evalContext.PrevAlertState == models.AlertStatePending) && (evalContext.Rule.State == models.AlertStateOK) {
return false
}
+
// Notify on Alerting -> OK to resolve before alertmanager timeout.
if (evalContext.PrevAlertState == models.AlertStateAlerting) && (evalContext.Rule.State == models.AlertStateOK) {
return true
}
+
return evalContext.Rule.State == models.AlertStateAlerting
}
-func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleUrl string) *simplejson.Json {
+func (am *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleURL string) *simplejson.Json {
alertJSON := simplejson.New()
alertJSON.Set("startsAt", evalContext.StartTime.UTC().Format(time.RFC3339))
if evalContext.Rule.State == models.AlertStateOK {
alertJSON.Set("endsAt", time.Now().UTC().Format(time.RFC3339))
}
- alertJSON.Set("generatorURL", ruleUrl)
+ alertJSON.Set("generatorURL", ruleURL)
// Annotations (summary and description are very commonly used).
alertJSON.SetPath([]string{"annotations", "summary"}, evalContext.Rule.Name)
@@ -94,7 +100,7 @@ func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext,
tags["metric"] = match.Metric
} else {
for k, v := range match.Tags {
- tags[k] = v
+ tags[replaceIllegalCharsInLabelname(k)] = v
}
}
}
@@ -103,25 +109,26 @@ func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext,
return alertJSON
}
-func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error {
- this.log.Info("Sending Alertmanager alert", "ruleId", evalContext.Rule.Id, "notification", this.Name)
+// Notify sends alert notifications to the alert manager
+func (am *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error {
+ am.log.Info("Sending Alertmanager alert", "ruleId", evalContext.Rule.Id, "notification", am.Name)
- ruleUrl, err := evalContext.GetRuleUrl()
+ ruleURL, err := evalContext.GetRuleUrl()
if err != nil {
- this.log.Error("Failed get rule link", "error", err)
+ am.log.Error("Failed get rule link", "error", err)
return err
}
// Send one alert per matching series.
alerts := make([]interface{}, 0)
for _, match := range evalContext.EvalMatches {
- alert := this.createAlert(evalContext, match, ruleUrl)
+ alert := am.createAlert(evalContext, match, ruleURL)
alerts = append(alerts, alert)
}
// This happens on ExecutionError or NoData
if len(alerts) == 0 {
- alert := this.createAlert(evalContext, nil, ruleUrl)
+ alert := am.createAlert(evalContext, nil, ruleURL)
alerts = append(alerts, alert)
}
@@ -129,15 +136,23 @@ func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) erro
body, _ := bodyJSON.MarshalJSON()
cmd := &models.SendWebhookSync{
- Url: this.Url + "/api/v1/alerts",
+ Url: am.URL + "/api/v1/alerts",
HttpMethod: "POST",
Body: string(body),
}
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
- this.log.Error("Failed to send alertmanager", "error", err, "alertmanager", this.Name)
+ am.log.Error("Failed to send alertmanager", "error", err, "alertmanager", am.Name)
return err
}
return nil
}
+
+// regexp that matches all none valid label name characters
+// https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
+var labelNamePattern = regexp.MustCompile(`[^a-zA-Z0-9_]`)
+
+func replaceIllegalCharsInLabelname(input string) string {
+ return labelNamePattern.ReplaceAllString(input, "_")
+}
diff --git a/pkg/services/alerting/notifiers/alertmanager_test.go b/pkg/services/alerting/notifiers/alertmanager_test.go
index acf039fbf68e..32379caac31f 100644
--- a/pkg/services/alerting/notifiers/alertmanager_test.go
+++ b/pkg/services/alerting/notifiers/alertmanager_test.go
@@ -4,14 +4,35 @@ import (
"context"
"testing"
+ "github.com/stretchr/testify/assert"
+
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
-
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
)
+func TestReplaceIllegalCharswithUnderscore(t *testing.T) {
+ cases := []struct {
+ input string
+ expected string
+ }{
+ {
+ input: "foobar",
+ expected: "foobar",
+ },
+ {
+ input: `foo.,\][!?#="~*^&+|<>\'bar09_09`,
+ expected: "foo____________________bar09_09",
+ },
+ }
+
+ for _, c := range cases {
+ assert.Equal(t, replaceIllegalCharsInLabelname(c.input), c.expected)
+ }
+}
+
func TestWhenAlertManagerShouldNotify(t *testing.T) {
tcs := []struct {
prevState models.AlertStateType
@@ -43,7 +64,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) {
for _, tc := range tcs {
am := &AlertmanagerNotifier{log: log.New("test.logger")}
- evalContext := alerting.NewEvalContext(context.TODO(), &alerting.Rule{
+ evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
State: tc.prevState,
})
@@ -88,7 +109,7 @@ func TestAlertmanagerNotifier(t *testing.T) {
alertmanagerNotifier := not.(*AlertmanagerNotifier)
So(err, ShouldBeNil)
- So(alertmanagerNotifier.Url, ShouldEqual, "http://127.0.0.1:9093/")
+ So(alertmanagerNotifier.URL, ShouldEqual, "http://127.0.0.1:9093/")
})
})
})
From 3dbc3251d1ce731b1a67f1d3285dc37e2c7cce91 Mon Sep 17 00:00:00 2001
From: Andrej Ocenas
Date: Thu, 16 May 2019 12:39:59 +0200
Subject: [PATCH 046/166] Chore: Deduplicate sqlstore transaction code (#17069)
* Deduplicate transaction code
* More deduplication
---
pkg/services/sqlstore/session.go | 15 +++++++
pkg/services/sqlstore/sqlstore.go | 59 ---------------------------
pkg/services/sqlstore/transactions.go | 57 +++++++-------------------
3 files changed, 29 insertions(+), 102 deletions(-)
diff --git a/pkg/services/sqlstore/session.go b/pkg/services/sqlstore/session.go
index 29d7392678fe..a705af39da3b 100644
--- a/pkg/services/sqlstore/session.go
+++ b/pkg/services/sqlstore/session.go
@@ -18,6 +18,11 @@ func (sess *DBSession) publishAfterCommit(msg interface{}) {
sess.events = append(sess.events, msg)
}
+// NewSession returns a new DBSession
+func (ss *SqlStore) NewSession() *DBSession {
+ return &DBSession{Session: ss.engine.NewSession()}
+}
+
func newSession() *DBSession {
return &DBSession{Session: x.NewSession()}
}
@@ -41,6 +46,16 @@ func startSession(ctx context.Context, engine *xorm.Engine, beginTran bool) (*DB
return newSess, nil
}
+// WithDbSession calls the callback with an session attached to the context.
+func (ss *SqlStore) WithDbSession(ctx context.Context, callback dbTransactionFunc) error {
+ sess, err := startSession(ctx, ss.engine, false)
+ if err != nil {
+ return err
+ }
+
+ return callback(sess)
+}
+
func withDbSession(ctx context.Context, callback dbTransactionFunc) error {
sess, err := startSession(ctx, x, false)
if err != nil {
diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go
index 97b9080ffcfd..c78095317410 100644
--- a/pkg/services/sqlstore/sqlstore.go
+++ b/pkg/services/sqlstore/sqlstore.go
@@ -26,7 +26,6 @@ import (
_ "github.com/grafana/grafana/pkg/tsdb/mssql"
"github.com/grafana/grafana/pkg/util"
_ "github.com/lib/pq"
- sqlite3 "github.com/mattn/go-sqlite3"
)
var (
@@ -58,64 +57,6 @@ type SqlStore struct {
skipEnsureAdmin bool
}
-// NewSession returns a new DBSession
-func (ss *SqlStore) NewSession() *DBSession {
- return &DBSession{Session: ss.engine.NewSession()}
-}
-
-// WithDbSession calls the callback with an session attached to the context.
-func (ss *SqlStore) WithDbSession(ctx context.Context, callback dbTransactionFunc) error {
- sess, err := startSession(ctx, ss.engine, false)
- if err != nil {
- return err
- }
-
- return callback(sess)
-}
-
-// WithTransactionalDbSession calls the callback with an session within a transaction
-func (ss *SqlStore) WithTransactionalDbSession(ctx context.Context, callback dbTransactionFunc) error {
- return ss.inTransactionWithRetryCtx(ctx, callback, 0)
-}
-
-func (ss *SqlStore) inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error {
- sess, err := startSession(ctx, ss.engine, true)
- if err != nil {
- return err
- }
-
- defer sess.Close()
-
- err = callback(sess)
-
- // special handling of database locked errors for sqlite, then we can retry 3 times
- if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
- if sqlError.Code == sqlite3.ErrLocked {
- sess.Rollback()
- time.Sleep(time.Millisecond * time.Duration(10))
- sqlog.Info("Database table locked, sleeping then retrying", "retry", retry)
- return ss.inTransactionWithRetryCtx(ctx, callback, retry+1)
- }
- }
-
- if err != nil {
- sess.Rollback()
- return err
- } else if err = sess.Commit(); err != nil {
- return err
- }
-
- if len(sess.events) > 0 {
- for _, e := range sess.events {
- if err = bus.Publish(e); err != nil {
- log.Error(3, "Failed to publish event after commit. error: %v", err)
- }
- }
- }
-
- return nil
-}
-
func (ss *SqlStore) Init() error {
ss.log = log.New("sqlstore")
ss.readConfig()
diff --git a/pkg/services/sqlstore/transactions.go b/pkg/services/sqlstore/transactions.go
index ed8b6a2309fd..0ecb0938d4f7 100644
--- a/pkg/services/sqlstore/transactions.go
+++ b/pkg/services/sqlstore/transactions.go
@@ -4,63 +4,34 @@ import (
"context"
"time"
+ "github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
sqlite3 "github.com/mattn/go-sqlite3"
)
+// WithTransactionalDbSession calls the callback with an session within a transaction
+func (ss *SqlStore) WithTransactionalDbSession(ctx context.Context, callback dbTransactionFunc) error {
+ return inTransactionWithRetryCtx(ss.engine, ctx, callback, 0)
+}
+
func (ss *SqlStore) InTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
return ss.inTransactionWithRetry(ctx, fn, 0)
}
func (ss *SqlStore) inTransactionWithRetry(ctx context.Context, fn func(ctx context.Context) error, retry int) error {
- sess, err := startSession(ctx, ss.engine, true)
- if err != nil {
- return err
- }
-
- defer sess.Close()
-
- withValue := context.WithValue(ctx, ContextSessionName, sess)
-
- err = fn(withValue)
-
- // special handling of database locked errors for sqlite, then we can retry 3 times
- if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
- if sqlError.Code == sqlite3.ErrLocked {
- sess.Rollback()
- time.Sleep(time.Millisecond * time.Duration(10))
- ss.log.Info("Database table locked, sleeping then retrying", "retry", retry)
- return ss.inTransactionWithRetry(ctx, fn, retry+1)
- }
- }
-
- if err != nil {
- sess.Rollback()
- return err
- }
-
- if err = sess.Commit(); err != nil {
- return err
- }
-
- if len(sess.events) > 0 {
- for _, e := range sess.events {
- if err = bus.Publish(e); err != nil {
- ss.log.Error("Failed to publish event after commit", err)
- }
- }
- }
-
- return nil
+ return inTransactionWithRetryCtx(ss.engine, ctx, func(sess *DBSession) error {
+ withValue := context.WithValue(ctx, ContextSessionName, sess)
+ return fn(withValue)
+ }, retry)
}
func inTransactionWithRetry(callback dbTransactionFunc, retry int) error {
- return inTransactionWithRetryCtx(context.Background(), callback, retry)
+ return inTransactionWithRetryCtx(x, context.Background(), callback, retry)
}
-func inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error {
- sess, err := startSession(ctx, x, true)
+func inTransactionWithRetryCtx(engine *xorm.Engine, ctx context.Context, callback dbTransactionFunc, retry int) error {
+ sess, err := startSession(ctx, engine, true)
if err != nil {
return err
}
@@ -102,5 +73,5 @@ func inTransaction(callback dbTransactionFunc) error {
}
func inTransactionCtx(ctx context.Context, callback dbTransactionFunc) error {
- return inTransactionWithRetryCtx(ctx, callback, 0)
+ return inTransactionWithRetryCtx(x, ctx, callback, 0)
}
From 73e405978bc8bebcbfd991d1522c5b097b9f3c06 Mon Sep 17 00:00:00 2001
From: Tom Petr
Date: Thu, 16 May 2019 07:45:23 -0400
Subject: [PATCH 047/166] add support for periodically reloading mysql client
certs (#14892)
---
pkg/services/sqlstore/sqlstore.go | 2 +-
pkg/services/sqlstore/tls_mysql.go | 24 +++++++++++++-----------
2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go
index c78095317410..44d0f545bfcc 100644
--- a/pkg/services/sqlstore/sqlstore.go
+++ b/pkg/services/sqlstore/sqlstore.go
@@ -171,7 +171,7 @@ func (ss *SqlStore) buildConnectionString() (string, error) {
ss.dbCfg.User, ss.dbCfg.Pwd, protocol, ss.dbCfg.Host, ss.dbCfg.Name)
if ss.dbCfg.SslMode == "true" || ss.dbCfg.SslMode == "skip-verify" {
- tlsCert, err := makeCert("custom", ss.dbCfg)
+ tlsCert, err := makeCert(ss.dbCfg)
if err != nil {
return "", err
}
diff --git a/pkg/services/sqlstore/tls_mysql.go b/pkg/services/sqlstore/tls_mysql.go
index 3697135c717e..0ba3d6da8d52 100644
--- a/pkg/services/sqlstore/tls_mysql.go
+++ b/pkg/services/sqlstore/tls_mysql.go
@@ -5,9 +5,13 @@ import (
"crypto/x509"
"fmt"
"io/ioutil"
+
+ "github.com/grafana/grafana/pkg/infra/log"
)
-func makeCert(tlsPoolName string, config DatabaseConfig) (*tls.Config, error) {
+var tlslog = log.New("tls_mysql")
+
+func makeCert(config DatabaseConfig) (*tls.Config, error) {
rootCertPool := x509.NewCertPool()
pem, err := ioutil.ReadFile(config.CaCertPath)
if err != nil {
@@ -16,18 +20,16 @@ func makeCert(tlsPoolName string, config DatabaseConfig) (*tls.Config, error) {
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
return nil, err
}
- clientCert := make([]tls.Certificate, 0, 1)
- if config.ClientCertPath != "" && config.ClientKeyPath != "" {
- certs, err := tls.LoadX509KeyPair(config.ClientCertPath, config.ClientKeyPath)
- if err != nil {
- return nil, err
- }
- clientCert = append(clientCert, certs)
- }
tlsConfig := &tls.Config{
- RootCAs: rootCertPool,
- Certificates: clientCert,
+ RootCAs: rootCertPool,
+ }
+ if config.ClientCertPath != "" && config.ClientKeyPath != "" {
+ tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
+ tlslog.Debug("Loading client certificate")
+ cert, err := tls.LoadX509KeyPair(config.ClientCertPath, config.ClientKeyPath)
+ return &cert, err
+ }
}
tlsConfig.ServerName = config.ServerCertName
if config.SslMode == "skip-verify" {
From 9bf577d1257e6dbb910515211885492e710588af Mon Sep 17 00:00:00 2001
From: Dominik Prokop
Date: Thu, 16 May 2019 16:39:50 +0200
Subject: [PATCH 048/166] Chore: Update grafana-ui version to 6.2.0-alpha.0
(#17109)
---
packages/grafana-ui/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json
index 7db50f75168f..a62de9ca003e 100644
--- a/packages/grafana-ui/package.json
+++ b/packages/grafana-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@grafana/ui",
- "version": "6.0.1-alpha.0",
+ "version": "6.2.0-alpha.0",
"description": "Grafana Components Library",
"keywords": [
"typescript",
From 0a9863a8b4edeced8aab26c2cc1fd0cbb206cefd Mon Sep 17 00:00:00 2001
From: Shavonn Brown
Date: Thu, 16 May 2019 15:16:05 -0400
Subject: [PATCH 049/166] GraphPanel: Don't sort series when legend table &
sort column is not visible (#17095)
* Fix: if current sort key is not active column, do not use for sort and select next available active (#16980)
* Fix: only sort if sortkey is active column and table is active (#16980)
* Fix: sorting stacked series as legend. current descending order test (#16980)
* Fix: existing sort tests and added additional to prevent bug from resurfacing (#16980)
---
.../app/plugins/panel/graph/Legend/Legend.tsx | 2 +-
public/app/plugins/panel/graph/graph.ts | 4 +--
.../plugins/panel/graph/specs/graph.test.ts | 27 +++++++++++++++++--
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/public/app/plugins/panel/graph/Legend/Legend.tsx b/public/app/plugins/panel/graph/Legend/Legend.tsx
index c20623f8c0ed..b60755311a02 100644
--- a/public/app/plugins/panel/graph/Legend/Legend.tsx
+++ b/public/app/plugins/panel/graph/Legend/Legend.tsx
@@ -89,7 +89,7 @@ export class GraphLegend extends PureComponent {
sortLegend() {
let seriesList: TimeSeries[] = [...this.props.seriesList] || [];
- if (this.props.sort) {
+ if (this.props.sort && this.props[this.props.sort] && this.props.alignAsTable) {
seriesList = _.sortBy(seriesList, series => {
let sort = series.stats[this.props.sort];
if (sort === null) {
diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts
index dad49788eb09..315f9b35cd53 100644
--- a/public/app/plugins/panel/graph/graph.ts
+++ b/public/app/plugins/panel/graph/graph.ts
@@ -461,9 +461,9 @@ class GraphElement {
sortSeries(series, panel) {
const sortBy = panel.legend.sort;
const sortOrder = panel.legend.sortDesc;
- const haveSortBy = sortBy !== null && sortBy !== undefined;
+ const haveSortBy = sortBy !== null && sortBy !== undefined && panel.legend[sortBy];
const haveSortOrder = sortOrder !== null && sortOrder !== undefined;
- const shouldSortBy = panel.stack && haveSortBy && haveSortOrder;
+ const shouldSortBy = panel.stack && haveSortBy && haveSortOrder && panel.legend.alignAsTable;
const sortDesc = panel.legend.sortDesc === true ? -1 : 1;
if (shouldSortBy) {
diff --git a/public/app/plugins/panel/graph/specs/graph.test.ts b/public/app/plugins/panel/graph/specs/graph.test.ts
index aa022c686079..ea340f38fed0 100644
--- a/public/app/plugins/panel/graph/specs/graph.test.ts
+++ b/public/app/plugins/panel/graph/specs/graph.test.ts
@@ -167,8 +167,11 @@ describe('grafanaGraph', () => {
describe('sorting stacked series as legend. min descending order', () => {
beforeEach(() => {
setupCtx(() => {
- ctrl.panel.legend.sort = 'min';
+ const sortKey = 'min';
+ ctrl.panel.legend.sort = sortKey;
ctrl.panel.legend.sortDesc = true;
+ ctrl.panel.legend.alignAsTable = true;
+ ctrl.panel.legend[sortKey] = true;
ctrl.panel.stack = true;
});
});
@@ -210,8 +213,11 @@ describe('grafanaGraph', () => {
describe('sorting stacked series as legend. current descending order', () => {
beforeEach(() => {
setupCtx(() => {
- ctrl.panel.legend.sort = 'current';
+ const sortKey = 'current';
+ ctrl.panel.legend.sort = sortKey;
ctrl.panel.legend.sortDesc = true;
+ ctrl.panel.legend.alignAsTable = true;
+ ctrl.panel.legend[sortKey] = true;
ctrl.panel.stack = true;
});
});
@@ -222,6 +228,23 @@ describe('grafanaGraph', () => {
});
});
+ describe('stacked series should not sort if legend is not as table or sort key column is not visible', () => {
+ beforeEach(() => {
+ setupCtx(() => {
+ const sortKey = 'min';
+ ctrl.panel.legend.sort = sortKey;
+ ctrl.panel.legend.sortDesc = true;
+ ctrl.panel.legend.alignAsTable = false;
+ ctrl.panel.legend[sortKey] = false;
+ ctrl.panel.stack = true;
+ });
+ });
+ it('highest value should be first', () => {
+ expect(ctx.plotData[0].alias).toBe('series1');
+ expect(ctx.plotData[1].alias).toBe('series2');
+ });
+ });
+
describe('when logBase is log 10', () => {
beforeEach(() => {
setupCtx(() => {
From 756da8a1348254f81ecaed9d956f1c729ec05912 Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Fri, 17 May 2019 08:35:37 +0300
Subject: [PATCH 050/166] Fix: tighten revive exit code & make it happy
(#17127)
* Revive should fail the build
* Fix the associated errors
---
pkg/services/sqlstore/transactions.go | 10 +++++-----
scripts/go/configs/revive.toml | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/pkg/services/sqlstore/transactions.go b/pkg/services/sqlstore/transactions.go
index 0ecb0938d4f7..a0f648043399 100644
--- a/pkg/services/sqlstore/transactions.go
+++ b/pkg/services/sqlstore/transactions.go
@@ -12,7 +12,7 @@ import (
// WithTransactionalDbSession calls the callback with an session within a transaction
func (ss *SqlStore) WithTransactionalDbSession(ctx context.Context, callback dbTransactionFunc) error {
- return inTransactionWithRetryCtx(ss.engine, ctx, callback, 0)
+ return inTransactionWithRetryCtx(ctx, ss.engine, callback, 0)
}
func (ss *SqlStore) InTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
@@ -20,17 +20,17 @@ func (ss *SqlStore) InTransaction(ctx context.Context, fn func(ctx context.Conte
}
func (ss *SqlStore) inTransactionWithRetry(ctx context.Context, fn func(ctx context.Context) error, retry int) error {
- return inTransactionWithRetryCtx(ss.engine, ctx, func(sess *DBSession) error {
+ return inTransactionWithRetryCtx(ctx, ss.engine, func(sess *DBSession) error {
withValue := context.WithValue(ctx, ContextSessionName, sess)
return fn(withValue)
}, retry)
}
func inTransactionWithRetry(callback dbTransactionFunc, retry int) error {
- return inTransactionWithRetryCtx(x, context.Background(), callback, retry)
+ return inTransactionWithRetryCtx(context.Background(), x, callback, retry)
}
-func inTransactionWithRetryCtx(engine *xorm.Engine, ctx context.Context, callback dbTransactionFunc, retry int) error {
+func inTransactionWithRetryCtx(ctx context.Context, engine *xorm.Engine, callback dbTransactionFunc, retry int) error {
sess, err := startSession(ctx, engine, true)
if err != nil {
return err
@@ -73,5 +73,5 @@ func inTransaction(callback dbTransactionFunc) error {
}
func inTransactionCtx(ctx context.Context, callback dbTransactionFunc) error {
- return inTransactionWithRetryCtx(x, ctx, callback, 0)
+ return inTransactionWithRetryCtx(ctx, x, callback, 0)
}
diff --git a/scripts/go/configs/revive.toml b/scripts/go/configs/revive.toml
index 2d4410ee5489..a40486c31333 100644
--- a/scripts/go/configs/revive.toml
+++ b/scripts/go/configs/revive.toml
@@ -1,7 +1,7 @@
ignoreGeneratedHeader = false
severity = "error"
confidence = 0.8
-errorCode = 0
+errorCode = 1
[rule.context-as-argument]
[rule.error-return]
From 32cdab4cbfc1fe5e7fdd1d1b6344db3305a85298 Mon Sep 17 00:00:00 2001
From: Marcus Efraimsson
Date: Fri, 17 May 2019 10:11:55 +0200
Subject: [PATCH 051/166] explore: make sure datasource is added to target
(#17116)
Fixes a regression introduced in #16959 which removed datasource
property from changed query for angular query editors having the
result of loading explore URL's without datasource loaded the
default query in query editor and Explore.
Ref #16808
---
public/app/features/explore/QueryEditor.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/app/features/explore/QueryEditor.tsx b/public/app/features/explore/QueryEditor.tsx
index 54927b8cc91b..5689f67ee13b 100644
--- a/public/app/features/explore/QueryEditor.tsx
+++ b/public/app/features/explore/QueryEditor.tsx
@@ -35,7 +35,7 @@ export default class QueryEditor extends PureComponent {
const loader = getAngularLoader();
const template = ' ';
- const target = { ...initialQuery };
+ const target = { datasource: datasource.name, ...initialQuery };
const scopeProps = {
ctrl: {
datasource,
From 1a80885180a38018d6b5f41cb462958fe519bd31 Mon Sep 17 00:00:00 2001
From: Marcus Efraimsson
Date: Fri, 17 May 2019 12:45:11 +0200
Subject: [PATCH 052/166] explore: fix issues when loading and both graph/table
are collapsed (#17113)
Removes the functionality of being able to collapse/expand the logs
container.
When both graph and table are collapsed and you reload the page
then the start page should not be displayed.
When both graph and table are collapsed and you reload the page
then the graph and table panels should be displayed.
Fix so that reducer tests are run. On of the test used fit() instead of
it() which had the consequence of only 1 reducer test was executed
and the rest skipped. There was some failing tests that now is
updated and now passes.
Fixes #17098
---
.../app/features/explore/GraphContainer.tsx | 28 +++----
public/app/features/explore/LogsContainer.tsx | 15 +---
public/app/features/explore/Panel.tsx | 17 +++-
.../app/features/explore/TableContainer.tsx | 8 +-
.../app/features/explore/state/actionTypes.ts | 9 ---
public/app/features/explore/state/actions.ts | 22 +----
.../features/explore/state/reducers.test.ts | 81 ++++++++-----------
public/app/features/explore/state/reducers.ts | 5 +-
.../app/features/explore/state/selectors.ts | 3 +-
public/app/types/explore.ts | 4 -
public/sass/pages/_explore.scss | 19 ++++-
11 files changed, 86 insertions(+), 125 deletions(-)
diff --git a/public/app/features/explore/GraphContainer.tsx b/public/app/features/explore/GraphContainer.tsx
index 7033473a33b3..0fba2ae6ded4 100644
--- a/public/app/features/explore/GraphContainer.tsx
+++ b/public/app/features/explore/GraphContainer.tsx
@@ -46,22 +46,20 @@ export class GraphContainer extends PureComponent {
const graphHeight = showingGraph && showingTable ? 200 : 400;
const timeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
- if (!graphResult) {
- return null;
- }
-
return (
-
-
+
+ {graphResult && (
+
+ )}
);
}
diff --git a/public/app/features/explore/LogsContainer.tsx b/public/app/features/explore/LogsContainer.tsx
index 83cce42c7d19..7356fb15c178 100644
--- a/public/app/features/explore/LogsContainer.tsx
+++ b/public/app/features/explore/LogsContainer.tsx
@@ -7,7 +7,7 @@ import { ExploreId, ExploreItemState } from 'app/types/explore';
import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model';
import { StoreState } from 'app/types';
-import { toggleLogs, changeDedupStrategy, changeTime } from './state/actions';
+import { changeDedupStrategy, changeTime } from './state/actions';
import Logs from './Logs';
import Panel from './Panel';
import { toggleLogLevelAction } from 'app/features/explore/state/actionTypes';
@@ -27,8 +27,6 @@ interface LogsContainerProps {
timeZone: TimeZone;
scanning?: boolean;
scanRange?: RawTimeRange;
- showingLogs: boolean;
- toggleLogs: typeof toggleLogs;
toggleLogLevelAction: typeof toggleLogLevelAction;
changeDedupStrategy: typeof changeDedupStrategy;
dedupStrategy: LogsDedupStrategy;
@@ -48,10 +46,6 @@ export class LogsContainer extends PureComponent {
changeTime(exploreId, range);
};
- onClickLogsButton = () => {
- this.props.toggleLogs(this.props.exploreId, this.props.showingLogs);
- };
-
handleDedupStrategyChange = (dedupStrategy: LogsDedupStrategy) => {
this.props.changeDedupStrategy(this.props.exploreId, dedupStrategy);
};
@@ -76,7 +70,6 @@ export class LogsContainer extends PureComponent {
onStopScanning,
range,
timeZone,
- showingLogs,
scanning,
scanRange,
width,
@@ -84,7 +77,7 @@ export class LogsContainer extends PureComponent {
} = this.props;
return (
-
+
void;
+ collapsible?: boolean;
+ onToggle?: (isOpen: boolean) => void;
}
export default class Panel extends PureComponent {
- onClickToggle = () => this.props.onToggle(!this.props.isOpen);
+ onClickToggle = () => {
+ const { onToggle, isOpen } = this.props;
+ if (onToggle) {
+ onToggle(!isOpen);
+ }
+ };
render() {
- const { isOpen, loading } = this.props;
+ const { isOpen, loading, collapsible } = this.props;
+ const panelClass = collapsible
+ ? 'explore-panel explore-panel--collapsible panel-container'
+ : 'explore-panel panel-container';
const iconClass = isOpen ? 'fa fa-caret-up' : 'fa fa-caret-down';
const loaderClass = loading ? 'explore-panel__loader explore-panel__loader--active' : 'explore-panel__loader';
return (
-
+
diff --git a/public/app/features/explore/TableContainer.tsx b/public/app/features/explore/TableContainer.tsx
index 78f190a05cb8..18ee70d8ee20 100644
--- a/public/app/features/explore/TableContainer.tsx
+++ b/public/app/features/explore/TableContainer.tsx
@@ -27,13 +27,9 @@ export class TableContainer extends PureComponent
{
render() {
const { loading, onClickCell, showingTable, tableResult } = this.props;
- if (!tableResult) {
- return null;
- }
-
return (
-
-
+
+ {tableResult && }
);
}
diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts
index 225a672ae2e5..ff7fdcb55dec 100644
--- a/public/app/features/explore/state/actionTypes.ts
+++ b/public/app/features/explore/state/actionTypes.ts
@@ -204,10 +204,6 @@ export interface ToggleGraphPayload {
exploreId: ExploreId;
}
-export interface ToggleLogsPayload {
- exploreId: ExploreId;
-}
-
export interface UpdateUIStatePayload extends Partial {
exploreId: ExploreId;
}
@@ -412,11 +408,6 @@ export const toggleTableAction = actionCreatorFactory('explo
*/
export const toggleGraphAction = actionCreatorFactory('explore/TOGGLE_GRAPH').create();
-/**
- * Expand/collapse the logs result viewer. When collapsed, log queries won't be run.
- */
-export const toggleLogsAction = actionCreatorFactory('explore/TOGGLE_LOGS').create();
-
/**
* Updates datasource instance before datasouce loading has started
*/
diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts
index 310f310e6710..c1157e01c6ae 100644
--- a/public/app/features/explore/state/actions.ts
+++ b/public/app/features/explore/state/actions.ts
@@ -72,10 +72,8 @@ import {
splitOpenAction,
addQueryRowAction,
toggleGraphAction,
- toggleLogsAction,
toggleTableAction,
ToggleGraphPayload,
- ToggleLogsPayload,
ToggleTablePayload,
updateUIStateAction,
runQueriesAction,
@@ -517,7 +515,6 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false, replaceU
const {
datasourceInstance,
queries,
- showingLogs,
showingGraph,
showingTable,
datasourceError,
@@ -562,7 +559,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false, replaceU
})
);
}
- if ((ignoreUIState || showingLogs) && mode === ExploreMode.Logs) {
+ if (mode === ExploreMode.Logs) {
dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' }));
}
@@ -700,7 +697,7 @@ export function stateSave(replaceUrl = false): ThunkResult {
range: toRawTimeRange(left.range),
ui: {
showingGraph: left.showingGraph,
- showingLogs: left.showingLogs,
+ showingLogs: true,
showingTable: left.showingTable,
dedupStrategy: left.dedupStrategy,
},
@@ -713,7 +710,7 @@ export function stateSave(replaceUrl = false): ThunkResult {
range: toRawTimeRange(right.range),
ui: {
showingGraph: right.showingGraph,
- showingLogs: right.showingLogs,
+ showingLogs: true,
showingTable: right.showingTable,
dedupStrategy: right.dedupStrategy,
},
@@ -731,10 +728,7 @@ export function stateSave(replaceUrl = false): ThunkResult {
* queries won't be run
*/
const togglePanelActionCreator = (
- actionCreator:
- | ActionCreator
- | ActionCreator
- | ActionCreator
+ actionCreator: ActionCreator | ActionCreator
) => (exploreId: ExploreId, isPanelVisible: boolean): ThunkResult => {
return dispatch => {
let uiFragmentStateUpdate: Partial;
@@ -744,9 +738,6 @@ const togglePanelActionCreator = (
case toggleGraphAction.type:
uiFragmentStateUpdate = { showingGraph: !isPanelVisible };
break;
- case toggleLogsAction.type:
- uiFragmentStateUpdate = { showingLogs: !isPanelVisible };
- break;
case toggleTableAction.type:
uiFragmentStateUpdate = { showingTable: !isPanelVisible };
break;
@@ -766,11 +757,6 @@ const togglePanelActionCreator = (
*/
export const toggleGraph = togglePanelActionCreator(toggleGraphAction);
-/**
- * Expand/collapse the logs result viewer. When collapsed, log queries won't be run.
- */
-export const toggleLogs = togglePanelActionCreator(toggleLogsAction);
-
/**
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
*/
diff --git a/public/app/features/explore/state/reducers.test.ts b/public/app/features/explore/state/reducers.test.ts
index c5ee8dbb7798..5af71c29f8df 100644
--- a/public/app/features/explore/state/reducers.test.ts
+++ b/public/app/features/explore/state/reducers.test.ts
@@ -10,7 +10,6 @@ import {
ExploreItemState,
ExploreUrlState,
ExploreState,
- QueryTransaction,
RangeScanner,
ExploreMode,
} from 'app/types/explore';
@@ -25,6 +24,7 @@ import {
splitOpenAction,
splitCloseAction,
changeModeAction,
+ runQueriesAction,
} from './actionTypes';
import { Reducer } from 'redux';
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
@@ -36,7 +36,7 @@ import { DataSourceApi, DataQuery } from '@grafana/ui';
describe('Explore item reducer', () => {
describe('scanning', () => {
- test('should start scanning', () => {
+ it('should start scanning', () => {
const scanner = jest.fn();
const initalState = {
...makeExploreItemState(),
@@ -53,7 +53,7 @@ describe('Explore item reducer', () => {
scanner,
});
});
- test('should stop scanning', () => {
+ it('should stop scanning', () => {
const scanner = jest.fn();
const initalState = {
...makeExploreItemState(),
@@ -96,7 +96,6 @@ describe('Explore item reducer', () => {
describe('when testDataSourceFailureAction is dispatched', () => {
it('then it should set correct state', () => {
const error = 'some error';
- const queryTransactions: QueryTransaction[] = [];
const initalState: Partial = {
datasourceError: null,
graphResult: [],
@@ -111,7 +110,6 @@ describe('Explore item reducer', () => {
};
const expectedState = {
datasourceError: error,
- queryTransactions,
graphResult: undefined as any[],
tableResult: undefined as TableModel,
logsResult: undefined as LogsModel,
@@ -144,9 +142,9 @@ describe('Explore item reducer', () => {
const StartPage = {};
const datasourceInstance = {
meta: {
- metrics: {},
- logs: {},
- tables: {},
+ metrics: true,
+ logs: true,
+ tables: true,
},
components: {
ExploreStartPage: StartPage,
@@ -175,6 +173,11 @@ describe('Explore item reducer', () => {
queryKeys,
supportedModes: [ExploreMode.Metrics, ExploreMode.Logs],
mode: ExploreMode.Metrics,
+ graphIsLoading: false,
+ tableIsLoading: false,
+ logIsLoading: false,
+ latency: 0,
+ queryErrors: [],
};
reducerTester()
@@ -185,6 +188,28 @@ describe('Explore item reducer', () => {
});
});
});
+
+ describe('run queries', () => {
+ describe('when runQueriesAction is dispatched', () => {
+ it('then it should set correct state', () => {
+ const initalState: Partial = {
+ showingStartPage: true,
+ };
+ const expectedState = {
+ queryIntervals: {
+ interval: '1s',
+ intervalMs: 1000,
+ },
+ showingStartPage: false,
+ };
+
+ reducerTester()
+ .givenReducer(itemReducer, initalState)
+ .whenActionIsDispatched(runQueriesAction({ exploreId: ExploreId.left }))
+ .thenStateShouldEqual(expectedState);
+ });
+ });
+ });
});
export const setup = (urlStateOverrides?: any) => {
@@ -529,46 +554,8 @@ describe('Explore reducer', () => {
});
});
- describe('and refreshInterval differs', () => {
- it('then it should return update refreshInterval', () => {
- const { initalState, serializedUrlState } = setup();
- const expectedState = {
- ...initalState,
- left: {
- ...initalState.left,
- update: {
- ...initalState.left.update,
- refreshInterval: true,
- },
- },
- };
- const stateWithDifferentDataSource = {
- ...initalState,
- left: {
- ...initalState.left,
- urlState: {
- ...initalState.left.urlState,
- refreshInterval: '5s',
- },
- },
- };
-
- reducerTester()
- .givenReducer(exploreReducer, stateWithDifferentDataSource)
- .whenActionIsDispatched(
- updateLocation({
- query: {
- left: serializedUrlState,
- },
- path: '/explore',
- })
- )
- .thenStateShouldEqual(expectedState);
- });
- });
-
describe('and nothing differs', () => {
- fit('then it should return update ui', () => {
+ it('then it should return update ui', () => {
const { initalState, serializedUrlState } = setup();
const expectedState = { ...initalState };
diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts
index f0847360cbe3..743294f0e74c 100644
--- a/public/app/features/explore/state/reducers.ts
+++ b/public/app/features/explore/state/reducers.ts
@@ -95,7 +95,6 @@ export const makeExploreItemState = (): ExploreItemState => ({
scanning: false,
scanRange: null,
showingGraph: true,
- showingLogs: true,
showingTable: true,
graphIsLoading: false,
logIsLoading: false,
@@ -351,7 +350,6 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
logsResult: resultType === 'Logs' ? null : state.logsResult,
latency: 0,
queryErrors,
- showingStartPage: false,
graphIsLoading: resultType === 'Graph' ? false : state.graphIsLoading,
logIsLoading: resultType === 'Logs' ? false : state.logIsLoading,
tableIsLoading: resultType === 'Table' ? false : state.tableIsLoading,
@@ -371,7 +369,6 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
graphIsLoading: resultType === 'Graph' ? true : state.graphIsLoading,
logIsLoading: resultType === 'Logs' ? true : state.logIsLoading,
tableIsLoading: resultType === 'Table' ? true : state.tableIsLoading,
- showingStartPage: false,
update: makeInitialUpdateState(),
};
},
@@ -392,7 +389,6 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
graphIsLoading: false,
logIsLoading: false,
tableIsLoading: false,
- showingStartPage: false,
update: makeInitialUpdateState(),
};
},
@@ -543,6 +539,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta
return {
...state,
queryIntervals,
+ showingStartPage: false,
};
},
})
diff --git a/public/app/features/explore/state/selectors.ts b/public/app/features/explore/state/selectors.ts
index fff52651646c..6925e706d4f4 100644
--- a/public/app/features/explore/state/selectors.ts
+++ b/public/app/features/explore/state/selectors.ts
@@ -3,10 +3,9 @@ import { ExploreItemState } from 'app/types';
import { filterLogLevels, dedupLogRows } from 'app/core/logs_model';
export const exploreItemUIStateSelector = (itemState: ExploreItemState) => {
- const { showingGraph, showingLogs, showingTable, showingStartPage, dedupStrategy } = itemState;
+ const { showingGraph, showingTable, showingStartPage, dedupStrategy } = itemState;
return {
showingGraph,
- showingLogs,
showingTable,
showingStartPage,
dedupStrategy,
diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts
index a828cbf9d3ad..6f70ecaa25bb 100644
--- a/public/app/types/explore.ts
+++ b/public/app/types/explore.ts
@@ -204,10 +204,6 @@ export interface ExploreItemState {
* True if graph result viewer is expanded. Query runs will contain graph queries.
*/
showingGraph: boolean;
- /**
- * True if logs result viewer is expanded. Query runs will contain logs queries.
- */
- showingLogs: boolean;
/**
* True StartPage needs to be shown. Typically set to `false` once queries have been run.
*/
diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss
index d77992038327..26401522118b 100644
--- a/public/sass/pages/_explore.scss
+++ b/public/sass/pages/_explore.scss
@@ -164,7 +164,7 @@
.explore-panel__header {
padding: $space-sm $space-md 0 $space-md;
display: flex;
- cursor: pointer;
+ cursor: inherit;
transition: all 0.1s linear;
}
@@ -176,9 +176,20 @@
}
.explore-panel__header-buttons {
- margin-right: $space-sm;
- font-size: $font-size-lg;
- line-height: $font-size-h6;
+ display: none;
+}
+
+.explore-panel--collapsible {
+ .explore-panel__header {
+ cursor: pointer;
+ }
+
+ .explore-panel__header-buttons {
+ margin-right: $space-sm;
+ font-size: $font-size-lg;
+ line-height: $font-size-h6;
+ display: inherit;
+ }
}
.time-series-disclaimer {
From 35f227de11fd2b3f542f920571ea93a7890ad11c Mon Sep 17 00:00:00 2001
From: Oleg Gaidarenko
Date: Fri, 17 May 2019 14:57:26 +0300
Subject: [PATCH 053/166] Feature: LDAP refactoring (#16950)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* incapsulates multipleldap logic under one module
* abstracts users upsert and get logic
* changes some of the text error messages and import sort sequence
* heavily refactors the LDAP module – LDAP module now only deals with LDAP related behaviour
* integrates affected auth_proxy module and their tests
* refactoring of the auth_proxy logic
---
go.mod | 1 +
go.sum | 2 +
pkg/extensions/main.go | 1 +
pkg/login/auth.go | 15 +-
pkg/login/auth_test.go | 32 +-
pkg/login/ldap_login.go | 38 +-
pkg/login/ldap_login_test.go | 106 ++--
pkg/middleware/auth_proxy.go | 6 +-
pkg/middleware/auth_proxy/auth_proxy.go | 136 ++--
pkg/middleware/auth_proxy/auth_proxy_test.go | 92 ++-
pkg/middleware/middleware.go | 3 +-
pkg/services/ldap/hooks.go | 5 -
pkg/services/ldap/ldap.go | 582 ++++++++---------
pkg/services/ldap/ldap_helpers_test.go | 140 +++++
pkg/services/ldap/ldap_login_test.go | 120 +++-
pkg/services/ldap/ldap_test.go | 583 ++++--------------
pkg/services/ldap/settings.go | 6 +-
pkg/services/ldap/test.go | 49 +-
pkg/services/multildap/multildap.go | 204 ++++++
pkg/services/sqlstore/sqlstore.go | 10 +-
pkg/services/user/user.go | 39 ++
pkg/setting/setting.go | 1 +
.../brianvoe/gofakeit/BENCHMARKS.md | 134 ++++
.../brianvoe/gofakeit/CODE_OF_CONDUCT.md | 46 ++
.../brianvoe/gofakeit/CONTRIBUTING.md | 1 +
.../github.com/brianvoe/gofakeit/LICENSE.txt | 20 +
vendor/github.com/brianvoe/gofakeit/README.md | 254 ++++++++
vendor/github.com/brianvoe/gofakeit/TODO.txt | 3 +
.../github.com/brianvoe/gofakeit/address.go | 131 ++++
vendor/github.com/brianvoe/gofakeit/beer.go | 45 ++
vendor/github.com/brianvoe/gofakeit/bool.go | 10 +
vendor/github.com/brianvoe/gofakeit/color.go | 44 ++
.../github.com/brianvoe/gofakeit/company.go | 30 +
.../github.com/brianvoe/gofakeit/contact.go | 40 ++
.../github.com/brianvoe/gofakeit/currency.go | 38 ++
.../brianvoe/gofakeit/data/address.go | 15 +
.../github.com/brianvoe/gofakeit/data/beer.go | 10 +
.../brianvoe/gofakeit/data/colors.go | 7 +
.../brianvoe/gofakeit/data/company.go | 9 +
.../brianvoe/gofakeit/data/computer.go | 8 +
.../brianvoe/gofakeit/data/contact.go | 6 +
.../brianvoe/gofakeit/data/currency.go | 7 +
.../github.com/brianvoe/gofakeit/data/data.go | 28 +
.../brianvoe/gofakeit/data/datetime.go | 9 +
.../brianvoe/gofakeit/data/files.go | 7 +
.../brianvoe/gofakeit/data/hacker.go | 20 +
.../brianvoe/gofakeit/data/hipster.go | 6 +
.../brianvoe/gofakeit/data/internet.go | 8 +
.../github.com/brianvoe/gofakeit/data/job.go | 8 +
.../brianvoe/gofakeit/data/log_level.go | 8 +
.../brianvoe/gofakeit/data/lorem.go | 6 +
.../brianvoe/gofakeit/data/payment.go | 20 +
.../brianvoe/gofakeit/data/person.go | 9 +
.../brianvoe/gofakeit/data/status_code.go | 7 +
.../brianvoe/gofakeit/data/vehicle.go | 10 +
.../github.com/brianvoe/gofakeit/datetime.go | 77 +++
vendor/github.com/brianvoe/gofakeit/doc.go | 10 +
vendor/github.com/brianvoe/gofakeit/faker.go | 15 +
vendor/github.com/brianvoe/gofakeit/file.go | 11 +
.../github.com/brianvoe/gofakeit/generate.go | 41 ++
vendor/github.com/brianvoe/gofakeit/hacker.go | 35 ++
.../github.com/brianvoe/gofakeit/hipster.go | 20 +
vendor/github.com/brianvoe/gofakeit/image.go | 8 +
.../github.com/brianvoe/gofakeit/internet.go | 55 ++
vendor/github.com/brianvoe/gofakeit/job.go | 34 +
.../github.com/brianvoe/gofakeit/log_level.go | 15 +
vendor/github.com/brianvoe/gofakeit/logo.png | Bin 0 -> 36022 bytes
vendor/github.com/brianvoe/gofakeit/misc.go | 132 ++++
vendor/github.com/brianvoe/gofakeit/name.go | 26 +
vendor/github.com/brianvoe/gofakeit/number.go | 84 +++
.../github.com/brianvoe/gofakeit/password.go | 68 ++
.../github.com/brianvoe/gofakeit/payment.go | 81 +++
vendor/github.com/brianvoe/gofakeit/person.go | 45 ++
.../brianvoe/gofakeit/status_code.go | 11 +
vendor/github.com/brianvoe/gofakeit/string.go | 48 ++
vendor/github.com/brianvoe/gofakeit/struct.go | 87 +++
vendor/github.com/brianvoe/gofakeit/unique.go | 34 +
.../brianvoe/gofakeit/user_agent.go | 92 +++
.../github.com/brianvoe/gofakeit/vehicle.go | 55 ++
vendor/github.com/brianvoe/gofakeit/words.go | 100 +++
vendor/github.com/robfig/cron/README.md | 2 +-
vendor/github.com/robfig/cron/doc.go | 2 +-
vendor/modules.txt | 3 +
83 files changed, 3375 insertions(+), 991 deletions(-)
delete mode 100644 pkg/services/ldap/hooks.go
create mode 100644 pkg/services/ldap/ldap_helpers_test.go
create mode 100644 pkg/services/multildap/multildap.go
create mode 100644 pkg/services/user/user.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md
create mode 100644 vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md
create mode 100644 vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md
create mode 100644 vendor/github.com/brianvoe/gofakeit/LICENSE.txt
create mode 100644 vendor/github.com/brianvoe/gofakeit/README.md
create mode 100644 vendor/github.com/brianvoe/gofakeit/TODO.txt
create mode 100644 vendor/github.com/brianvoe/gofakeit/address.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/beer.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/bool.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/color.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/company.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/contact.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/currency.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/address.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/beer.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/colors.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/company.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/computer.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/contact.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/currency.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/data.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/datetime.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/files.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/hacker.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/hipster.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/internet.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/job.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/log_level.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/lorem.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/payment.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/person.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/status_code.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/data/vehicle.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/datetime.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/doc.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/faker.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/file.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/generate.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/hacker.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/hipster.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/image.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/internet.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/job.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/log_level.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/logo.png
create mode 100644 vendor/github.com/brianvoe/gofakeit/misc.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/name.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/number.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/password.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/payment.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/person.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/status_code.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/string.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/struct.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/unique.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/user_agent.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/vehicle.go
create mode 100644 vendor/github.com/brianvoe/gofakeit/words.go
diff --git a/go.mod b/go.mod
index 106d8b726617..619f5183a5c7 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
github.com/aws/aws-sdk-go v1.18.5
github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737
+ github.com/brianvoe/gofakeit v3.17.0+incompatible
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/codegangsta/cli v1.20.0
github.com/davecgh/go-spew v1.1.1
diff --git a/go.sum b/go.sum
index 55223ecbc74e..3c77812fbf7b 100644
--- a/go.sum
+++ b/go.sum
@@ -16,6 +16,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVxvoa702s91Zl5oREZTrR3yv+tXrrX7G/g=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/brianvoe/gofakeit v3.17.0+incompatible h1:C1+30+c0GtjgGDtRC+iePZeP1WMiwsWCELNJhmc7aIc=
+github.com/brianvoe/gofakeit v3.17.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
diff --git a/pkg/extensions/main.go b/pkg/extensions/main.go
index 6ee742a4d8e3..cbe9ec2b7b07 100644
--- a/pkg/extensions/main.go
+++ b/pkg/extensions/main.go
@@ -1,6 +1,7 @@
package extensions
import (
+ _ "github.com/brianvoe/gofakeit"
_ "github.com/gobwas/glob"
_ "github.com/robfig/cron"
_ "gopkg.in/square/go-jose.v2"
diff --git a/pkg/login/auth.go b/pkg/login/auth.go
index 56f614d92dea..51eda933c6e1 100644
--- a/pkg/login/auth.go
+++ b/pkg/login/auth.go
@@ -4,8 +4,8 @@ import (
"errors"
"github.com/grafana/grafana/pkg/bus"
- m "github.com/grafana/grafana/pkg/models"
- LDAP "github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/ldap"
)
var (
@@ -25,7 +25,8 @@ func Init() {
bus.AddHandler("auth", AuthenticateUser)
}
-func AuthenticateUser(query *m.LoginUserQuery) error {
+// AuthenticateUser authenticates the user via username & password
+func AuthenticateUser(query *models.LoginUserQuery) error {
if err := validateLoginAttempts(query.Username); err != nil {
return err
}
@@ -35,24 +36,24 @@ func AuthenticateUser(query *m.LoginUserQuery) error {
}
err := loginUsingGrafanaDB(query)
- if err == nil || (err != m.ErrUserNotFound && err != ErrInvalidCredentials) {
+ if err == nil || (err != models.ErrUserNotFound && err != ErrInvalidCredentials) {
return err
}
ldapEnabled, ldapErr := loginUsingLdap(query)
if ldapEnabled {
- if ldapErr == nil || ldapErr != LDAP.ErrInvalidCredentials {
+ if ldapErr == nil || ldapErr != ldap.ErrInvalidCredentials {
return ldapErr
}
err = ldapErr
}
- if err == ErrInvalidCredentials || err == LDAP.ErrInvalidCredentials {
+ if err == ErrInvalidCredentials || err == ldap.ErrInvalidCredentials {
saveInvalidLoginAttempt(query)
}
- if err == m.ErrUserNotFound {
+ if err == models.ErrUserNotFound {
return ErrInvalidCredentials
}
diff --git a/pkg/login/auth_test.go b/pkg/login/auth_test.go
index 85ad3bc07dce..658560b0ce1d 100644
--- a/pkg/login/auth_test.go
+++ b/pkg/login/auth_test.go
@@ -6,8 +6,8 @@ import (
. "github.com/smartystreets/goconvey/convey"
- m "github.com/grafana/grafana/pkg/models"
- LDAP "github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/ldap"
)
func TestAuthenticateUser(t *testing.T) {
@@ -17,7 +17,7 @@ func TestAuthenticateUser(t *testing.T) {
mockLoginUsingGrafanaDB(nil, sc)
mockLoginUsingLdap(false, nil, sc)
- loginQuery := m.LoginUserQuery{
+ loginQuery := models.LoginUserQuery{
Username: "user",
Password: "",
}
@@ -84,7 +84,7 @@ func TestAuthenticateUser(t *testing.T) {
authScenario("When a non-existing grafana user authenticate and ldap disabled", func(sc *authScenarioContext) {
mockLoginAttemptValidation(nil, sc)
- mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
+ mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
mockLoginUsingLdap(false, nil, sc)
mockSaveInvalidLoginAttempt(sc)
@@ -101,14 +101,14 @@ func TestAuthenticateUser(t *testing.T) {
authScenario("When a non-existing grafana user authenticate and invalid ldap credentials", func(sc *authScenarioContext) {
mockLoginAttemptValidation(nil, sc)
- mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
- mockLoginUsingLdap(true, LDAP.ErrInvalidCredentials, sc)
+ mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
+ mockLoginUsingLdap(true, ldap.ErrInvalidCredentials, sc)
mockSaveInvalidLoginAttempt(sc)
err := AuthenticateUser(sc.loginUserQuery)
Convey("it should result in", func() {
- So(err, ShouldEqual, LDAP.ErrInvalidCredentials)
+ So(err, ShouldEqual, ldap.ErrInvalidCredentials)
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
So(sc.ldapLoginWasCalled, ShouldBeTrue)
@@ -118,7 +118,7 @@ func TestAuthenticateUser(t *testing.T) {
authScenario("When a non-existing grafana user authenticate and valid ldap credentials", func(sc *authScenarioContext) {
mockLoginAttemptValidation(nil, sc)
- mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
+ mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
mockLoginUsingLdap(true, nil, sc)
mockSaveInvalidLoginAttempt(sc)
@@ -136,7 +136,7 @@ func TestAuthenticateUser(t *testing.T) {
authScenario("When a non-existing grafana user authenticate and ldap returns unexpected error", func(sc *authScenarioContext) {
customErr := errors.New("custom")
mockLoginAttemptValidation(nil, sc)
- mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
+ mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
mockLoginUsingLdap(true, customErr, sc)
mockSaveInvalidLoginAttempt(sc)
@@ -154,13 +154,13 @@ func TestAuthenticateUser(t *testing.T) {
authScenario("When grafana user authenticate with invalid credentials and invalid ldap credentials", func(sc *authScenarioContext) {
mockLoginAttemptValidation(nil, sc)
mockLoginUsingGrafanaDB(ErrInvalidCredentials, sc)
- mockLoginUsingLdap(true, LDAP.ErrInvalidCredentials, sc)
+ mockLoginUsingLdap(true, ldap.ErrInvalidCredentials, sc)
mockSaveInvalidLoginAttempt(sc)
err := AuthenticateUser(sc.loginUserQuery)
Convey("it should result in", func() {
- So(err, ShouldEqual, LDAP.ErrInvalidCredentials)
+ So(err, ShouldEqual, ldap.ErrInvalidCredentials)
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
So(sc.ldapLoginWasCalled, ShouldBeTrue)
@@ -171,7 +171,7 @@ func TestAuthenticateUser(t *testing.T) {
}
type authScenarioContext struct {
- loginUserQuery *m.LoginUserQuery
+ loginUserQuery *models.LoginUserQuery
grafanaLoginWasCalled bool
ldapLoginWasCalled bool
loginAttemptValidationWasCalled bool
@@ -181,14 +181,14 @@ type authScenarioContext struct {
type authScenarioFunc func(sc *authScenarioContext)
func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) {
- loginUsingGrafanaDB = func(query *m.LoginUserQuery) error {
+ loginUsingGrafanaDB = func(query *models.LoginUserQuery) error {
sc.grafanaLoginWasCalled = true
return err
}
}
func mockLoginUsingLdap(enabled bool, err error, sc *authScenarioContext) {
- loginUsingLdap = func(query *m.LoginUserQuery) (bool, error) {
+ loginUsingLdap = func(query *models.LoginUserQuery) (bool, error) {
sc.ldapLoginWasCalled = true
return enabled, err
}
@@ -202,7 +202,7 @@ func mockLoginAttemptValidation(err error, sc *authScenarioContext) {
}
func mockSaveInvalidLoginAttempt(sc *authScenarioContext) {
- saveInvalidLoginAttempt = func(query *m.LoginUserQuery) {
+ saveInvalidLoginAttempt = func(query *models.LoginUserQuery) {
sc.saveInvalidLoginAttemptWasCalled = true
}
}
@@ -215,7 +215,7 @@ func authScenario(desc string, fn authScenarioFunc) {
origSaveInvalidLoginAttempt := saveInvalidLoginAttempt
sc := &authScenarioContext{
- loginUserQuery: &m.LoginUserQuery{
+ loginUserQuery: &models.LoginUserQuery{
Username: "user",
Password: "pwd",
IpAddress: "192.168.1.1:56433",
diff --git a/pkg/login/ldap_login.go b/pkg/login/ldap_login.go
index abd861cbe6dd..0b41e612a2ee 100644
--- a/pkg/login/ldap_login.go
+++ b/pkg/login/ldap_login.go
@@ -2,13 +2,20 @@ package login
import (
"github.com/grafana/grafana/pkg/models"
- LDAP "github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/services/multildap"
+ "github.com/grafana/grafana/pkg/services/user"
+ "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
)
-var newLDAP = LDAP.New
-var getLDAPConfig = LDAP.GetConfig
-var isLDAPEnabled = LDAP.IsEnabled
+// getLDAPConfig gets LDAP config
+var getLDAPConfig = multildap.GetConfig
+
+// isLDAPEnabled checks if LDAP is enabled
+var isLDAPEnabled = multildap.IsEnabled
+
+// newLDAP creates multiple LDAP instance
+var newLDAP = multildap.New
// loginUsingLdap logs in user using LDAP. It returns whether LDAP is enabled and optional error and query arg will be
// populated with the logged in user if successful.
@@ -23,18 +30,21 @@ var loginUsingLdap = func(query *models.LoginUserQuery) (bool, error) {
if err != nil {
return true, errutil.Wrap("Failed to get LDAP config", err)
}
- if len(config.Servers) == 0 {
- return true, ErrNoLDAPServers
- }
- for _, server := range config.Servers {
- auth := newLDAP(server)
+ externalUser, err := newLDAP(config.Servers).Login(query)
+ if err != nil {
+ return true, err
+ }
- err := auth.Login(query)
- if err == nil || err != LDAP.ErrInvalidCredentials {
- return true, err
- }
+ login, err := user.Upsert(&user.UpsertArgs{
+ ExternalUser: externalUser,
+ SignupAllowed: setting.LdapAllowSignup,
+ })
+ if err != nil {
+ return true, err
}
- return true, LDAP.ErrInvalidCredentials
+ query.User = login
+
+ return true, nil
}
diff --git a/pkg/login/ldap_login_test.go b/pkg/login/ldap_login_test.go
index 3ea82d0a8ed6..b93a4890bc31 100644
--- a/pkg/login/ldap_login_test.go
+++ b/pkg/login/ldap_login_test.go
@@ -6,8 +6,9 @@ import (
. "github.com/smartystreets/goconvey/convey"
- m "github.com/grafana/grafana/pkg/models"
- LDAP "github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/services/multildap"
"github.com/grafana/grafana/pkg/setting"
)
@@ -18,11 +19,11 @@ func TestLdapLogin(t *testing.T) {
Convey("Given ldap enabled and no server configured", func() {
setting.LdapEnabled = true
- ldapLoginScenario("When login", func(sc *ldapLoginScenarioContext) {
+ LDAPLoginScenario("When login", func(sc *LDAPLoginScenarioContext) {
sc.withLoginResult(false)
- getLDAPConfig = func() (*LDAP.Config, error) {
- config := &LDAP.Config{
- Servers: []*LDAP.ServerConfig{},
+ getLDAPConfig = func() (*ldap.Config, error) {
+ config := &ldap.Config{
+ Servers: []*ldap.ServerConfig{},
}
return config, nil
@@ -35,11 +36,11 @@ func TestLdapLogin(t *testing.T) {
})
Convey("it should return no LDAP servers error", func() {
- So(err, ShouldEqual, ErrNoLDAPServers)
+ So(err, ShouldEqual, errTest)
})
Convey("it should not call ldap login", func() {
- So(sc.ldapAuthenticatorMock.loginCalled, ShouldBeFalse)
+ So(sc.LDAPAuthenticatorMock.loginCalled, ShouldBeTrue)
})
})
})
@@ -47,9 +48,9 @@ func TestLdapLogin(t *testing.T) {
Convey("Given ldap disabled", func() {
setting.LdapEnabled = false
- ldapLoginScenario("When login", func(sc *ldapLoginScenarioContext) {
+ LDAPLoginScenario("When login", func(sc *LDAPLoginScenarioContext) {
sc.withLoginResult(false)
- enabled, err := loginUsingLdap(&m.LoginUserQuery{
+ enabled, err := loginUsingLdap(&models.LoginUserQuery{
Username: "user",
Password: "pwd",
})
@@ -63,75 +64,88 @@ func TestLdapLogin(t *testing.T) {
})
Convey("it should not call ldap login", func() {
- So(sc.ldapAuthenticatorMock.loginCalled, ShouldBeFalse)
+ So(sc.LDAPAuthenticatorMock.loginCalled, ShouldBeFalse)
})
})
})
})
}
-func mockLdapAuthenticator(valid bool) *mockAuth {
- mock := &mockAuth{
- validLogin: valid,
- }
-
- newLDAP = func(server *LDAP.ServerConfig) LDAP.IAuth {
- return mock
- }
-
- return mock
-}
-
type mockAuth struct {
validLogin bool
loginCalled bool
}
-func (auth *mockAuth) Login(query *m.LoginUserQuery) error {
+func (auth *mockAuth) Login(query *models.LoginUserQuery) (
+ *models.ExternalUserInfo,
+ error,
+) {
auth.loginCalled = true
if !auth.validLogin {
- return errTest
+ return nil, errTest
}
- return nil
+ return nil, nil
}
-func (auth *mockAuth) Users() ([]*LDAP.UserInfo, error) {
+func (auth *mockAuth) Users(logins []string) (
+ []*models.ExternalUserInfo,
+ error,
+) {
return nil, nil
}
-func (auth *mockAuth) SyncUser(query *m.LoginUserQuery) error {
+func (auth *mockAuth) User(login string) (
+ *models.ExternalUserInfo,
+ error,
+) {
+ return nil, nil
+}
+
+func (auth *mockAuth) Add(dn string, values map[string][]string) error {
return nil
}
-func (auth *mockAuth) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LDAP.UserInfo) (*m.User, error) {
- return nil, nil
+func (auth *mockAuth) Remove(dn string) error {
+ return nil
+}
+
+func mockLDAPAuthenticator(valid bool) *mockAuth {
+ mock := &mockAuth{
+ validLogin: valid,
+ }
+
+ newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP {
+ return mock
+ }
+
+ return mock
}
-type ldapLoginScenarioContext struct {
- loginUserQuery *m.LoginUserQuery
- ldapAuthenticatorMock *mockAuth
+type LDAPLoginScenarioContext struct {
+ loginUserQuery *models.LoginUserQuery
+ LDAPAuthenticatorMock *mockAuth
}
-type ldapLoginScenarioFunc func(c *ldapLoginScenarioContext)
+type LDAPLoginScenarioFunc func(c *LDAPLoginScenarioContext)
-func ldapLoginScenario(desc string, fn ldapLoginScenarioFunc) {
+func LDAPLoginScenario(desc string, fn LDAPLoginScenarioFunc) {
Convey(desc, func() {
mock := &mockAuth{}
- sc := &ldapLoginScenarioContext{
- loginUserQuery: &m.LoginUserQuery{
+ sc := &LDAPLoginScenarioContext{
+ loginUserQuery: &models.LoginUserQuery{
Username: "user",
Password: "pwd",
IpAddress: "192.168.1.1:56433",
},
- ldapAuthenticatorMock: mock,
+ LDAPAuthenticatorMock: mock,
}
- getLDAPConfig = func() (*LDAP.Config, error) {
- config := &LDAP.Config{
- Servers: []*LDAP.ServerConfig{
+ getLDAPConfig = func() (*ldap.Config, error) {
+ config := &ldap.Config{
+ Servers: []*ldap.ServerConfig{
{
Host: "",
},
@@ -141,19 +155,19 @@ func ldapLoginScenario(desc string, fn ldapLoginScenarioFunc) {
return config, nil
}
- newLDAP = func(server *LDAP.ServerConfig) LDAP.IAuth {
+ newLDAP = func(server []*ldap.ServerConfig) multildap.IMultiLDAP {
return mock
}
defer func() {
- newLDAP = LDAP.New
- getLDAPConfig = LDAP.GetConfig
+ newLDAP = multildap.New
+ getLDAPConfig = multildap.GetConfig
}()
fn(sc)
})
}
-func (sc *ldapLoginScenarioContext) withLoginResult(valid bool) {
- sc.ldapAuthenticatorMock = mockLdapAuthenticator(valid)
+func (sc *LDAPLoginScenarioContext) withLoginResult(valid bool) {
+ sc.LDAPAuthenticatorMock = mockLDAPAuthenticator(valid)
}
diff --git a/pkg/middleware/auth_proxy.go b/pkg/middleware/auth_proxy.go
index d3d8d8e77d8a..890fd5e4f24b 100644
--- a/pkg/middleware/auth_proxy.go
+++ b/pkg/middleware/auth_proxy.go
@@ -35,8 +35,8 @@ func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext,
return true
}
- // Try to get user id from various sources
- id, err := auth.GetUserID()
+ // Try to log in user from various providers
+ id, err := auth.Login()
if err != nil {
ctx.Handle(500, err.Error(), err.DetailsError)
return true
@@ -54,7 +54,7 @@ func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext,
ctx.IsSignedIn = true
// Remember user data it in cache
- if err := auth.Remember(); err != nil {
+ if err := auth.Remember(id); err != nil {
ctx.Handle(500, err.Error(), err.DetailsError)
return true
}
diff --git a/pkg/middleware/auth_proxy/auth_proxy.go b/pkg/middleware/auth_proxy/auth_proxy.go
index 98bacbeccf47..4cb2de38c7f1 100644
--- a/pkg/middleware/auth_proxy/auth_proxy.go
+++ b/pkg/middleware/auth_proxy/auth_proxy.go
@@ -12,6 +12,8 @@ import (
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/services/multildap"
+ "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
)
@@ -21,10 +23,14 @@ const (
CachePrefix = "auth-proxy-sync-ttl:%s"
)
-var (
- getLDAPConfig = ldap.GetConfig
- isLDAPEnabled = ldap.IsEnabled
-)
+// getLDAPConfig gets LDAP config
+var getLDAPConfig = ldap.GetConfig
+
+// isLDAPEnabled checks if LDAP is enabled
+var isLDAPEnabled = ldap.IsEnabled
+
+// newLDAP creates multiple LDAP instance
+var newLDAP = multildap.New
// AuthProxy struct
type AuthProxy struct {
@@ -33,13 +39,13 @@ type AuthProxy struct {
orgID int64
header string
- LDAP func(server *ldap.ServerConfig) ldap.IAuth
-
- enabled bool
- whitelistIP string
- headerType string
- headers map[string]string
- cacheTTL int
+ enabled bool
+ LdapAllowSignup bool
+ AuthProxyAutoSignUp bool
+ whitelistIP string
+ headerType string
+ headers map[string]string
+ cacheTTL int
}
// Error auth proxy specific error
@@ -78,13 +84,13 @@ func New(options *Options) *AuthProxy {
orgID: options.OrgID,
header: header,
- LDAP: ldap.New,
-
- enabled: setting.AuthProxyEnabled,
- headerType: setting.AuthProxyHeaderProperty,
- headers: setting.AuthProxyHeaders,
- whitelistIP: setting.AuthProxyWhitelist,
- cacheTTL: setting.AuthProxyLdapSyncTtl,
+ enabled: setting.AuthProxyEnabled,
+ headerType: setting.AuthProxyHeaderProperty,
+ headers: setting.AuthProxyHeaders,
+ whitelistIP: setting.AuthProxyWhitelist,
+ cacheTTL: setting.AuthProxyLdapSyncTtl,
+ LdapAllowSignup: setting.LdapAllowSignup,
+ AuthProxyAutoSignUp: setting.AuthProxyAutoSignUp,
}
}
@@ -144,34 +150,22 @@ func (auth *AuthProxy) IsAllowedIP() (bool, *Error) {
return false, newError("Proxy authentication required", err)
}
-// InCache checks if we have user in cache
-func (auth *AuthProxy) InCache() bool {
- userID, _ := auth.GetUserIDViaCache()
-
- if userID == 0 {
- return false
- }
-
- return true
-}
-
// getKey forms a key for the cache
func (auth *AuthProxy) getKey() string {
return fmt.Sprintf(CachePrefix, auth.header)
}
-// GetUserID gets user id with whatever means possible
-func (auth *AuthProxy) GetUserID() (int64, *Error) {
- if auth.InCache() {
+// Login logs in user id with whatever means possible
+func (auth *AuthProxy) Login() (int64, *Error) {
+ id, _ := auth.GetUserViaCache()
+ if id != 0 {
// Error here means absent cache - we don't need to handle that
- id, _ := auth.GetUserIDViaCache()
-
return id, nil
}
if isLDAPEnabled() {
- id, err := auth.GetUserIDViaLDAP()
+ id, err := auth.LoginViaLDAP()
if err == ldap.ErrInvalidCredentials {
return 0, newError(
@@ -181,16 +175,16 @@ func (auth *AuthProxy) GetUserID() (int64, *Error) {
}
if err != nil {
- return 0, newError("Failed to sync user", err)
+ return 0, newError("Failed to get the user", err)
}
return id, nil
}
- id, err := auth.GetUserIDViaHeader()
+ id, err := auth.LoginViaHeader()
if err != nil {
return 0, newError(
- "Failed to login as user specified in auth proxy header",
+ "Failed to log in as user, specified in auth proxy header",
err,
)
}
@@ -198,8 +192,8 @@ func (auth *AuthProxy) GetUserID() (int64, *Error) {
return id, nil
}
-// GetUserIDViaCache gets the user from cache
-func (auth *AuthProxy) GetUserIDViaCache() (int64, error) {
+// GetUserViaCache gets user id from cache
+func (auth *AuthProxy) GetUserViaCache() (int64, error) {
var (
cacheKey = auth.getKey()
userID, err = auth.store.Get(cacheKey)
@@ -212,33 +206,34 @@ func (auth *AuthProxy) GetUserIDViaCache() (int64, error) {
return userID.(int64), nil
}
-// GetUserIDViaLDAP gets user via LDAP request
-func (auth *AuthProxy) GetUserIDViaLDAP() (int64, *Error) {
- query := &models.LoginUserQuery{
- ReqContext: auth.ctx,
- Username: auth.header,
- }
-
+// LoginViaLDAP logs in user via LDAP request
+func (auth *AuthProxy) LoginViaLDAP() (int64, *Error) {
config, err := getLDAPConfig()
if err != nil {
return 0, newError("Failed to get LDAP config", nil)
}
- if len(config.Servers) == 0 {
- return 0, newError("No LDAP servers available", nil)
+
+ extUser, err := newLDAP(config.Servers).User(auth.header)
+ if err != nil {
+ return 0, newError(err.Error(), nil)
}
- for _, server := range config.Servers {
- author := auth.LDAP(server)
- if err := author.SyncUser(query); err != nil {
- return 0, newError(err.Error(), nil)
- }
+ // Have to sync grafana and LDAP user during log in
+ user, err := user.Upsert(&user.UpsertArgs{
+ ReqContext: auth.ctx,
+ SignupAllowed: auth.LdapAllowSignup,
+ ExternalUser: extUser,
+ })
+ if err != nil {
+ return 0, newError(err.Error(), nil)
}
- return query.User.Id, nil
+ return user.Id, nil
}
-// GetUserIDViaHeader gets user from the header only
-func (auth *AuthProxy) GetUserIDViaHeader() (int64, error) {
+// LoginViaHeader logs in user from the header only
+// TODO: refactor - cyclomatic complexity should be much lower
+func (auth *AuthProxy) LoginViaHeader() (int64, error) {
extUser := &models.ExternalUserInfo{
AuthModule: "authproxy",
AuthId: auth.header,
@@ -269,18 +264,16 @@ func (auth *AuthProxy) GetUserIDViaHeader() (int64, error) {
}
}
- // add/update user in grafana
- cmd := &models.UpsertUserCommand{
+ result, err := user.Upsert(&user.UpsertArgs{
ReqContext: auth.ctx,
+ SignupAllowed: true,
ExternalUser: extUser,
- SignupAllowed: setting.AuthProxyAutoSignUp,
- }
- err := bus.Dispatch(cmd)
+ })
if err != nil {
return 0, err
}
- return cmd.Result.Id, nil
+ return result.Id, nil
}
// GetSignedUser get full signed user info
@@ -298,21 +291,18 @@ func (auth *AuthProxy) GetSignedUser(userID int64) (*models.SignedInUser, *Error
}
// Remember user in cache
-func (auth *AuthProxy) Remember() *Error {
+func (auth *AuthProxy) Remember(id int64) *Error {
+ key := auth.getKey()
- // Make sure we do not rewrite the expiration time
- if auth.InCache() {
+ // Check if user already in cache
+ userID, _ := auth.store.Get(key)
+ if userID != nil {
return nil
}
- var (
- key = auth.getKey()
- value, _ = auth.GetUserIDViaCache()
- expiration = time.Duration(-auth.cacheTTL) * time.Minute
-
- err = auth.store.Set(key, value, expiration)
- )
+ expiration := time.Duration(-auth.cacheTTL) * time.Minute
+ err := auth.store.Set(key, id, expiration)
if err != nil {
return newError(err.Error(), nil)
}
diff --git a/pkg/middleware/auth_proxy/auth_proxy_test.go b/pkg/middleware/auth_proxy/auth_proxy_test.go
index fbddca81d401..4a1edc66ba5a 100644
--- a/pkg/middleware/auth_proxy/auth_proxy_test.go
+++ b/pkg/middleware/auth_proxy/auth_proxy_test.go
@@ -1,6 +1,7 @@
package authproxy
import (
+ "errors"
"fmt"
"net/http"
"testing"
@@ -8,24 +9,40 @@ import (
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/macaron.v1"
+ "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
+ "github.com/grafana/grafana/pkg/services/multildap"
"github.com/grafana/grafana/pkg/setting"
)
-type TestLDAP struct {
- ldap.Auth
- ID int64
- syncCalled bool
+type TestMultiLDAP struct {
+ multildap.MultiLDAP
+ ID int64
+ userCalled bool
+ loginCalled bool
}
-func (stub *TestLDAP) SyncUser(query *models.LoginUserQuery) error {
- stub.syncCalled = true
- query.User = &models.User{
- Id: stub.ID,
+func (stub *TestMultiLDAP) Login(query *models.LoginUserQuery) (
+ *models.ExternalUserInfo, error,
+) {
+ stub.loginCalled = true
+ result := &models.ExternalUserInfo{
+ UserId: stub.ID,
}
- return nil
+ return result, nil
+}
+
+func (stub *TestMultiLDAP) User(login string) (
+ *models.ExternalUserInfo,
+ error,
+) {
+ stub.userCalled = true
+ result := &models.ExternalUserInfo{
+ UserId: stub.ID,
+ }
+ return result, nil
}
func TestMiddlewareContext(t *testing.T) {
@@ -44,7 +61,7 @@ func TestMiddlewareContext(t *testing.T) {
},
}
- Convey("gets data from the cache", func() {
+ Convey("logs in user from the cache", func() {
store := remotecache.NewFakeStore(t)
key := fmt.Sprintf(CachePrefix, name)
store.Set(key, int64(33), 0)
@@ -55,53 +72,64 @@ func TestMiddlewareContext(t *testing.T) {
OrgID: 4,
})
- id, err := auth.GetUserID()
+ id, err := auth.Login()
So(err, ShouldBeNil)
So(id, ShouldEqual, 33)
})
Convey("LDAP", func() {
- Convey("gets data from the LDAP", func() {
+ Convey("logs in via LDAP", func() {
+ bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
+ cmd.Result = &models.User{
+ Id: 42,
+ }
+
+ return nil
+ })
+
isLDAPEnabled = func() bool {
return true
}
+ stub := &TestMultiLDAP{
+ ID: 42,
+ }
+
getLDAPConfig = func() (*ldap.Config, error) {
config := &ldap.Config{
Servers: []*ldap.ServerConfig{
- {},
+ {
+ SearchBaseDNs: []string{"BaseDNHere"},
+ },
},
}
return config, nil
}
+ newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP {
+ return stub
+ }
+
defer func() {
+ newLDAP = multildap.New
isLDAPEnabled = ldap.IsEnabled
getLDAPConfig = ldap.GetConfig
}()
store := remotecache.NewFakeStore(t)
- auth := New(&Options{
+ server := New(&Options{
Store: store,
Ctx: ctx,
OrgID: 4,
})
- stub := &TestLDAP{
- ID: 42,
- }
-
- auth.LDAP = func(server *ldap.ServerConfig) ldap.IAuth {
- return stub
- }
-
- id, err := auth.GetUserID()
+ id, err := server.Login()
So(err, ShouldBeNil)
So(id, ShouldEqual, 42)
- So(stub.syncCalled, ShouldEqual, true)
+ So(stub.userCalled, ShouldEqual, true)
})
Convey("gets nice error if ldap is enabled but not configured", func() {
@@ -110,13 +138,11 @@ func TestMiddlewareContext(t *testing.T) {
}
getLDAPConfig = func() (*ldap.Config, error) {
- config := &ldap.Config{
- Servers: []*ldap.ServerConfig{},
- }
- return config, nil
+ return nil, errors.New("Something went wrong")
}
defer func() {
+ newLDAP = multildap.New
isLDAPEnabled = ldap.IsEnabled
getLDAPConfig = ldap.GetConfig
}()
@@ -129,20 +155,20 @@ func TestMiddlewareContext(t *testing.T) {
OrgID: 4,
})
- stub := &TestLDAP{
+ stub := &TestMultiLDAP{
ID: 42,
}
- auth.LDAP = func(server *ldap.ServerConfig) ldap.IAuth {
+ newLDAP = func(servers []*ldap.ServerConfig) multildap.IMultiLDAP {
return stub
}
- id, err := auth.GetUserID()
+ id, err := auth.Login()
So(err, ShouldNotBeNil)
- So(err.Error(), ShouldContainSubstring, "Failed to sync user")
+ So(err.Error(), ShouldContainSubstring, "Failed to get the user")
So(id, ShouldNotEqual, 42)
- So(stub.syncCalled, ShouldEqual, false)
+ So(stub.loginCalled, ShouldEqual, false)
})
})
diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go
index ec7194a7fc66..1b465e7a4e54 100644
--- a/pkg/middleware/middleware.go
+++ b/pkg/middleware/middleware.go
@@ -7,6 +7,8 @@ import (
"strings"
"time"
+ macaron "gopkg.in/macaron.v1"
+
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
"github.com/grafana/grafana/pkg/infra/log"
@@ -14,7 +16,6 @@ import (
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
- macaron "gopkg.in/macaron.v1"
)
var (
diff --git a/pkg/services/ldap/hooks.go b/pkg/services/ldap/hooks.go
deleted file mode 100644
index ece98e5e73b0..000000000000
--- a/pkg/services/ldap/hooks.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package ldap
-
-var (
- hookDial func(*Auth) error
-)
diff --git a/pkg/services/ldap/ldap.go b/pkg/services/ldap/ldap.go
index d6b5b69d7525..20953db97c31 100644
--- a/pkg/services/ldap/ldap.go
+++ b/pkg/services/ldap/ldap.go
@@ -8,39 +8,38 @@ import (
"io/ioutil"
"strings"
- "github.com/davecgh/go-spew/spew"
- LDAP "gopkg.in/ldap.v3"
+ "gopkg.in/ldap.v3"
- "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- models "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/setting"
+ "github.com/grafana/grafana/pkg/models"
)
// IConnection is interface for LDAP connection manipulation
type IConnection interface {
Bind(username, password string) error
UnauthenticatedBind(username string) error
- Search(*LDAP.SearchRequest) (*LDAP.SearchResult, error)
+ Add(*ldap.AddRequest) error
+ Del(*ldap.DelRequest) error
+ Search(*ldap.SearchRequest) (*ldap.SearchResult, error)
StartTLS(*tls.Config) error
Close()
}
-// IAuth is interface for LDAP authorization
-type IAuth interface {
- Login(query *models.LoginUserQuery) error
- SyncUser(query *models.LoginUserQuery) error
- GetGrafanaUserFor(
- ctx *models.ReqContext,
- user *UserInfo,
- ) (*models.User, error)
- Users() ([]*UserInfo, error)
+// IServer is interface for LDAP authorization
+type IServer interface {
+ Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error)
+ Add(string, map[string][]string) error
+ Remove(string) error
+ Users([]string) ([]*models.ExternalUserInfo, error)
+ ExtractGrafanaUser(*UserInfo) (*models.ExternalUserInfo, error)
+ Dial() error
+ Close()
}
-// Auth is basic struct of LDAP authorization
-type Auth struct {
- server *ServerConfig
- conn IConnection
+// Server is basic struct of LDAP authorization
+type Server struct {
+ config *ServerConfig
+ connection IConnection
requireSecondBind bool
log log.Logger
}
@@ -52,28 +51,24 @@ var (
)
var dial = func(network, addr string) (IConnection, error) {
- return LDAP.Dial(network, addr)
+ return ldap.Dial(network, addr)
}
// New creates the new LDAP auth
-func New(server *ServerConfig) IAuth {
- return &Auth{
- server: server,
+func New(config *ServerConfig) IServer {
+ return &Server{
+ config: config,
log: log.New("ldap"),
}
}
// Dial dials in the LDAP
-func (auth *Auth) Dial() error {
- if hookDial != nil {
- return hookDial(auth)
- }
-
+func (server *Server) Dial() error {
var err error
var certPool *x509.CertPool
- if auth.server.RootCACert != "" {
+ if server.config.RootCACert != "" {
certPool = x509.NewCertPool()
- for _, caCertFile := range strings.Split(auth.server.RootCACert, " ") {
+ for _, caCertFile := range strings.Split(server.config.RootCACert, " ") {
pem, err := ioutil.ReadFile(caCertFile)
if err != nil {
return err
@@ -84,35 +79,35 @@ func (auth *Auth) Dial() error {
}
}
var clientCert tls.Certificate
- if auth.server.ClientCert != "" && auth.server.ClientKey != "" {
- clientCert, err = tls.LoadX509KeyPair(auth.server.ClientCert, auth.server.ClientKey)
+ if server.config.ClientCert != "" && server.config.ClientKey != "" {
+ clientCert, err = tls.LoadX509KeyPair(server.config.ClientCert, server.config.ClientKey)
if err != nil {
return err
}
}
- for _, host := range strings.Split(auth.server.Host, " ") {
- address := fmt.Sprintf("%s:%d", host, auth.server.Port)
- if auth.server.UseSSL {
+ for _, host := range strings.Split(server.config.Host, " ") {
+ address := fmt.Sprintf("%s:%d", host, server.config.Port)
+ if server.config.UseSSL {
tlsCfg := &tls.Config{
- InsecureSkipVerify: auth.server.SkipVerifySSL,
+ InsecureSkipVerify: server.config.SkipVerifySSL,
ServerName: host,
RootCAs: certPool,
}
if len(clientCert.Certificate) > 0 {
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
}
- if auth.server.StartTLS {
- auth.conn, err = dial("tcp", address)
+ if server.config.StartTLS {
+ server.connection, err = dial("tcp", address)
if err == nil {
- if err = auth.conn.StartTLS(tlsCfg); err == nil {
+ if err = server.connection.StartTLS(tlsCfg); err == nil {
return nil
}
}
} else {
- auth.conn, err = LDAP.DialTLS("tcp", address, tlsCfg)
+ server.connection, err = ldap.DialTLS("tcp", address, tlsCfg)
}
} else {
- auth.conn, err = dial("tcp", address)
+ server.connection, err = dial("tcp", address)
}
if err == nil {
@@ -122,91 +117,206 @@ func (auth *Auth) Dial() error {
return err
}
-// Login logs in the user
-func (auth *Auth) Login(query *models.LoginUserQuery) error {
- // connect to ldap server
- if err := auth.Dial(); err != nil {
- return err
- }
- defer auth.conn.Close()
+// Close closes the LDAP connection
+func (server *Server) Close() {
+ server.connection.Close()
+}
- // perform initial authentication
- if err := auth.initialBind(query.Username, query.Password); err != nil {
- return err
+// Login intialBinds the user, search it and then serialize it
+func (server *Server) Login(query *models.LoginUserQuery) (
+ *models.ExternalUserInfo, error,
+) {
+
+ // Perform initial authentication
+ err := server.intialBind(query.Username, query.Password)
+ if err != nil {
+ return nil, err
}
- // find user entry & attributes
- user, err := auth.searchForUser(query.Username)
+ // Find user entry & attributes
+ users, err := server.Users([]string{query.Username})
if err != nil {
- return err
+ return nil, err
}
- auth.log.Debug("Ldap User found", "info", spew.Sdump(user))
+ // If we couldn't find the user -
+ // we should show incorrect credentials err
+ if len(users) == 0 {
+ return nil, ErrInvalidCredentials
+ }
- // check if a second user bind is needed
- if auth.requireSecondBind {
- err = auth.secondBind(user, query.Password)
+ // Check if a second user bind is needed
+ user := users[0]
+ if server.requireSecondBind {
+ err = server.secondBind(user, query.Password)
if err != nil {
- return err
+ return nil, err
}
}
- grafanaUser, err := auth.GetGrafanaUserFor(query.ReqContext, user)
+ return user, nil
+}
+
+// Add adds stuff to LDAP
+func (server *Server) Add(dn string, values map[string][]string) error {
+ err := server.intialBind(
+ server.config.BindDN,
+ server.config.BindPassword,
+ )
if err != nil {
return err
}
- query.User = grafanaUser
- return nil
-}
+ attributes := make([]ldap.Attribute, 0)
+ for key, value := range values {
+ attributes = append(attributes, ldap.Attribute{
+ Type: key,
+ Vals: value,
+ })
+ }
-// SyncUser syncs user with Grafana
-func (auth *Auth) SyncUser(query *models.LoginUserQuery) error {
- // connect to ldap server
- err := auth.Dial()
+ request := &ldap.AddRequest{
+ DN: dn,
+ Attributes: attributes,
+ }
+
+ err = server.connection.Add(request)
if err != nil {
return err
}
- defer auth.conn.Close()
- err = auth.serverBind()
+ return nil
+}
+
+// Remove removes stuff from LDAP
+func (server *Server) Remove(dn string) error {
+ err := server.intialBind(
+ server.config.BindDN,
+ server.config.BindPassword,
+ )
if err != nil {
return err
}
- // find user entry & attributes
- user, err := auth.searchForUser(query.Username)
+ request := ldap.NewDelRequest(dn, nil)
+ err = server.connection.Del(request)
if err != nil {
- auth.log.Error("Failed searching for user in ldap", "error", err)
return err
}
- auth.log.Debug("Ldap User found", "info", spew.Sdump(user))
+ return nil
+}
+
+// Users gets LDAP users
+func (server *Server) Users(logins []string) (
+ []*models.ExternalUserInfo,
+ error,
+) {
+ var result *ldap.SearchResult
+ var err error
+ var config = server.config
- grafanaUser, err := auth.GetGrafanaUserFor(query.ReqContext, user)
+ for _, base := range config.SearchBaseDNs {
+ result, err = server.connection.Search(
+ server.getSearchRequest(base, logins),
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ if len(result.Entries) > 0 {
+ break
+ }
+ }
+
+ serializedUsers, err := server.serializeUsers(result)
if err != nil {
- return err
+ return nil, err
+ }
+
+ return serializedUsers, nil
+}
+
+// ExtractGrafanaUser extracts external user info from LDAP user
+func (server *Server) ExtractGrafanaUser(user *UserInfo) (*models.ExternalUserInfo, error) {
+ result := server.buildGrafanaUser(user)
+ if err := server.validateGrafanaUser(result); err != nil {
+ return nil, err
+ }
+
+ return result, nil
+}
+
+// validateGrafanaUser validates user access.
+// If there are no ldap group mappings access is true
+// otherwise a single group must match
+func (server *Server) validateGrafanaUser(user *models.ExternalUserInfo) error {
+ if len(server.config.Groups) > 0 && len(user.OrgRoles) < 1 {
+ server.log.Error(
+ "user does not belong in any of the specified LDAP groups",
+ "username", user.Login,
+ "groups", user.Groups,
+ )
+ return ErrInvalidCredentials
}
- query.User = grafanaUser
return nil
}
-func (auth *Auth) GetGrafanaUserFor(
- ctx *models.ReqContext,
- user *UserInfo,
-) (*models.User, error) {
+// getSearchRequest returns LDAP search request for users
+func (server *Server) getSearchRequest(
+ base string,
+ logins []string,
+) *ldap.SearchRequest {
+ attributes := []string{}
+
+ inputs := server.config.Attr
+ attributes = appendIfNotEmpty(
+ attributes,
+ inputs.Username,
+ inputs.Surname,
+ inputs.Email,
+ inputs.Name,
+ inputs.MemberOf,
+ )
+
+ search := ""
+ for _, login := range logins {
+ query := strings.Replace(
+ server.config.SearchFilter,
+ "%s", ldap.EscapeFilter(login),
+ -1,
+ )
+
+ search = search + query
+ }
+
+ filter := fmt.Sprintf("(|%s)", search)
+
+ return &ldap.SearchRequest{
+ BaseDN: base,
+ Scope: ldap.ScopeWholeSubtree,
+ DerefAliases: ldap.NeverDerefAliases,
+ Attributes: attributes,
+ Filter: filter,
+ }
+}
+
+// buildGrafanaUser extracts info from UserInfo model to ExternalUserInfo
+func (server *Server) buildGrafanaUser(user *UserInfo) *models.ExternalUserInfo {
extUser := &models.ExternalUserInfo{
AuthModule: "ldap",
AuthId: user.DN,
- Name: fmt.Sprintf("%s %s", user.FirstName, user.LastName),
- Login: user.Username,
- Email: user.Email,
- Groups: user.MemberOf,
- OrgRoles: map[int64]models.RoleType{},
+ Name: strings.TrimSpace(
+ fmt.Sprintf("%s %s", user.FirstName, user.LastName),
+ ),
+ Login: user.Username,
+ Email: user.Email,
+ Groups: user.MemberOf,
+ OrgRoles: map[int64]models.RoleType{},
}
- for _, group := range auth.server.Groups {
+ for _, group := range server.config.Groups {
// only use the first match for each org
if extUser.OrgRoles[group.OrgId] != "" {
continue
@@ -220,49 +330,28 @@ func (auth *Auth) GetGrafanaUserFor(
}
}
- // validate that the user has access
- // if there are no ldap group mappings access is true
- // otherwise a single group must match
- if len(auth.server.Groups) > 0 && len(extUser.OrgRoles) < 1 {
- auth.log.Info(
- "Ldap Auth: user does not belong in any of the specified ldap groups",
- "username", user.Username,
- "groups", user.MemberOf,
- )
- return nil, ErrInvalidCredentials
- }
-
- // add/update user in grafana
- upsertUserCmd := &models.UpsertUserCommand{
- ReqContext: ctx,
- ExternalUser: extUser,
- SignupAllowed: setting.LdapAllowSignup,
- }
-
- err := bus.Dispatch(upsertUserCmd)
- if err != nil {
- return nil, err
- }
-
- return upsertUserCmd.Result, nil
+ return extUser
}
-func (auth *Auth) serverBind() error {
+func (server *Server) serverBind() error {
bindFn := func() error {
- return auth.conn.Bind(auth.server.BindDN, auth.server.BindPassword)
+ return server.connection.Bind(
+ server.config.BindDN,
+ server.config.BindPassword,
+ )
}
- if auth.server.BindPassword == "" {
+ if server.config.BindPassword == "" {
bindFn = func() error {
- return auth.conn.UnauthenticatedBind(auth.server.BindDN)
+ return server.connection.UnauthenticatedBind(server.config.BindDN)
}
}
// bind_dn and bind_password to bind
if err := bindFn(); err != nil {
- auth.log.Info("LDAP initial bind failed, %v", err)
+ server.log.Info("LDAP initial bind failed, %v", err)
- if ldapErr, ok := err.(*LDAP.Error); ok {
+ if ldapErr, ok := err.(*ldap.Error); ok {
if ldapErr.ResultCode == 49 {
return ErrInvalidCredentials
}
@@ -273,11 +362,15 @@ func (auth *Auth) serverBind() error {
return nil
}
-func (auth *Auth) secondBind(user *UserInfo, userPassword string) error {
- if err := auth.conn.Bind(user.DN, userPassword); err != nil {
- auth.log.Info("Second bind failed", "error", err)
+func (server *Server) secondBind(
+ user *models.ExternalUserInfo,
+ userPassword string,
+) error {
+ err := server.connection.Bind(user.AuthId, userPassword)
+ if err != nil {
+ server.log.Info("Second bind failed", "error", err)
- if ldapErr, ok := err.(*LDAP.Error); ok {
+ if ldapErr, ok := err.(*ldap.Error); ok {
if ldapErr.ResultCode == 49 {
return ErrInvalidCredentials
}
@@ -288,31 +381,31 @@ func (auth *Auth) secondBind(user *UserInfo, userPassword string) error {
return nil
}
-func (auth *Auth) initialBind(username, userPassword string) error {
- if auth.server.BindPassword != "" || auth.server.BindDN == "" {
- userPassword = auth.server.BindPassword
- auth.requireSecondBind = true
+func (server *Server) intialBind(username, userPassword string) error {
+ if server.config.BindPassword != "" || server.config.BindDN == "" {
+ userPassword = server.config.BindPassword
+ server.requireSecondBind = true
}
- bindPath := auth.server.BindDN
+ bindPath := server.config.BindDN
if strings.Contains(bindPath, "%s") {
- bindPath = fmt.Sprintf(auth.server.BindDN, username)
+ bindPath = fmt.Sprintf(server.config.BindDN, username)
}
bindFn := func() error {
- return auth.conn.Bind(bindPath, userPassword)
+ return server.connection.Bind(bindPath, userPassword)
}
if userPassword == "" {
bindFn = func() error {
- return auth.conn.UnauthenticatedBind(bindPath)
+ return server.connection.UnauthenticatedBind(bindPath)
}
}
if err := bindFn(); err != nil {
- auth.log.Info("Initial bind failed", "error", err)
+ server.log.Info("Initial bind failed", "error", err)
- if ldapErr, ok := err.(*LDAP.Error); ok {
+ if ldapErr, ok := err.(*ldap.Error); ok {
if ldapErr.ResultCode == 49 {
return ErrInvalidCredentials
}
@@ -323,199 +416,124 @@ func (auth *Auth) initialBind(username, userPassword string) error {
return nil
}
-func (auth *Auth) searchForUser(username string) (*UserInfo, error) {
- var searchResult *LDAP.SearchResult
- var err error
-
- for _, searchBase := range auth.server.SearchBaseDNs {
- attributes := make([]string, 0)
- inputs := auth.server.Attr
- attributes = appendIfNotEmpty(attributes,
- inputs.Username,
- inputs.Surname,
- inputs.Email,
- inputs.Name,
- inputs.MemberOf)
-
- searchReq := LDAP.SearchRequest{
- BaseDN: searchBase,
- Scope: LDAP.ScopeWholeSubtree,
- DerefAliases: LDAP.NeverDerefAliases,
- Attributes: attributes,
- Filter: strings.Replace(
- auth.server.SearchFilter,
- "%s", LDAP.EscapeFilter(username),
- -1,
- ),
- }
-
- auth.log.Debug("Ldap Search For User Request", "info", spew.Sdump(searchReq))
-
- searchResult, err = auth.conn.Search(&searchReq)
- if err != nil {
- return nil, err
- }
-
- if len(searchResult.Entries) > 0 {
- break
- }
- }
-
- if len(searchResult.Entries) == 0 {
- return nil, ErrInvalidCredentials
- }
-
- if len(searchResult.Entries) > 1 {
- return nil, errors.New("Ldap search matched more than one entry, please review your filter setting")
- }
-
+// requestMemberOf use this function when POSIX LDAP schema does not support memberOf, so it manually search the groups
+func (server *Server) requestMemberOf(searchResult *ldap.SearchResult) ([]string, error) {
var memberOf []string
- if auth.server.GroupSearchFilter == "" {
- memberOf = getLdapAttrArray(auth.server.Attr.MemberOf, searchResult)
- } else {
- // If we are using a POSIX LDAP schema it won't support memberOf, so we manually search the groups
- var groupSearchResult *LDAP.SearchResult
- for _, groupSearchBase := range auth.server.GroupSearchBaseDNs {
- var filter_replace string
- if auth.server.GroupSearchFilterUserAttribute == "" {
- filter_replace = getLdapAttr(auth.server.Attr.Username, searchResult)
- } else {
- filter_replace = getLdapAttr(auth.server.GroupSearchFilterUserAttribute, searchResult)
- }
-
- filter := strings.Replace(
- auth.server.GroupSearchFilter, "%s",
- LDAP.EscapeFilter(filter_replace),
- -1,
- )
-
- auth.log.Info("Searching for user's groups", "filter", filter)
-
- // support old way of reading settings
- groupIdAttribute := auth.server.Attr.MemberOf
- // but prefer dn attribute if default settings are used
- if groupIdAttribute == "" || groupIdAttribute == "memberOf" {
- groupIdAttribute = "dn"
- }
-
- groupSearchReq := LDAP.SearchRequest{
- BaseDN: groupSearchBase,
- Scope: LDAP.ScopeWholeSubtree,
- DerefAliases: LDAP.NeverDerefAliases,
- Attributes: []string{groupIdAttribute},
- Filter: filter,
- }
-
- groupSearchResult, err = auth.conn.Search(&groupSearchReq)
- if err != nil {
- return nil, err
- }
- if len(groupSearchResult.Entries) > 0 {
- for i := range groupSearchResult.Entries {
- memberOf = append(memberOf, getLdapAttrN(groupIdAttribute, groupSearchResult, i))
- }
- break
- }
+ for _, groupSearchBase := range server.config.GroupSearchBaseDNs {
+ var filterReplace string
+ if server.config.GroupSearchFilterUserAttribute == "" {
+ filterReplace = getLdapAttr(server.config.Attr.Username, searchResult)
+ } else {
+ filterReplace = getLdapAttr(server.config.GroupSearchFilterUserAttribute, searchResult)
}
- }
-
- return &UserInfo{
- DN: searchResult.Entries[0].DN,
- LastName: getLdapAttr(auth.server.Attr.Surname, searchResult),
- FirstName: getLdapAttr(auth.server.Attr.Name, searchResult),
- Username: getLdapAttr(auth.server.Attr.Username, searchResult),
- Email: getLdapAttr(auth.server.Attr.Email, searchResult),
- MemberOf: memberOf,
- }, nil
-}
-func (ldap *Auth) Users() ([]*UserInfo, error) {
- var result *LDAP.SearchResult
- var err error
- server := ldap.server
-
- if err := ldap.Dial(); err != nil {
- return nil, err
- }
- defer ldap.conn.Close()
-
- for _, base := range server.SearchBaseDNs {
- attributes := make([]string, 0)
- inputs := server.Attr
- attributes = appendIfNotEmpty(
- attributes,
- inputs.Username,
- inputs.Surname,
- inputs.Email,
- inputs.Name,
- inputs.MemberOf,
+ filter := strings.Replace(
+ server.config.GroupSearchFilter, "%s",
+ ldap.EscapeFilter(filterReplace),
+ -1,
)
- req := LDAP.SearchRequest{
- BaseDN: base,
- Scope: LDAP.ScopeWholeSubtree,
- DerefAliases: LDAP.NeverDerefAliases,
- Attributes: attributes,
+ server.log.Info("Searching for user's groups", "filter", filter)
+
+ // support old way of reading settings
+ groupIDAttribute := server.config.Attr.MemberOf
+ // but prefer dn attribute if default settings are used
+ if groupIDAttribute == "" || groupIDAttribute == "memberOf" {
+ groupIDAttribute = "dn"
+ }
- // Doing a star here to get all the users in one go
- Filter: strings.Replace(server.SearchFilter, "%s", "*", -1),
+ groupSearchReq := ldap.SearchRequest{
+ BaseDN: groupSearchBase,
+ Scope: ldap.ScopeWholeSubtree,
+ DerefAliases: ldap.NeverDerefAliases,
+ Attributes: []string{groupIDAttribute},
+ Filter: filter,
}
- result, err = ldap.conn.Search(&req)
+ groupSearchResult, err := server.connection.Search(&groupSearchReq)
if err != nil {
return nil, err
}
- if len(result.Entries) > 0 {
+ if len(groupSearchResult.Entries) > 0 {
+ for i := range groupSearchResult.Entries {
+ memberOf = append(memberOf, getLdapAttrN(groupIDAttribute, groupSearchResult, i))
+ }
break
}
}
- return ldap.serializeUsers(result), nil
+ return memberOf, nil
}
-func (ldap *Auth) serializeUsers(users *LDAP.SearchResult) []*UserInfo {
- var serialized []*UserInfo
+// serializeUsers serializes the users
+// from LDAP result to ExternalInfo struct
+func (server *Server) serializeUsers(
+ users *ldap.SearchResult,
+) ([]*models.ExternalUserInfo, error) {
+ var serialized []*models.ExternalUserInfo
for index := range users.Entries {
- serialize := &UserInfo{
+ memberOf, err := server.getMemberOf(users)
+ if err != nil {
+ return nil, err
+ }
+
+ userInfo := &UserInfo{
DN: getLdapAttrN(
"dn",
users,
index,
),
LastName: getLdapAttrN(
- ldap.server.Attr.Surname,
+ server.config.Attr.Surname,
users,
index,
),
FirstName: getLdapAttrN(
- ldap.server.Attr.Name,
+ server.config.Attr.Name,
users,
index,
),
Username: getLdapAttrN(
- ldap.server.Attr.Username,
+ server.config.Attr.Username,
users,
index,
),
Email: getLdapAttrN(
- ldap.server.Attr.Email,
- users,
- index,
- ),
- MemberOf: getLdapAttrArrayN(
- ldap.server.Attr.MemberOf,
+ server.config.Attr.Email,
users,
index,
),
+ MemberOf: memberOf,
}
- serialized = append(serialized, serialize)
+ serialized = append(
+ serialized,
+ server.buildGrafanaUser(userInfo),
+ )
+ }
+
+ return serialized, nil
+}
+
+// getMemberOf finds memberOf property or request it
+func (server *Server) getMemberOf(search *ldap.SearchResult) (
+ []string, error,
+) {
+ if server.config.GroupSearchFilter == "" {
+ memberOf := getLdapAttrArray(server.config.Attr.MemberOf, search)
+
+ return memberOf, nil
+ }
+
+ memberOf, err := server.requestMemberOf(search)
+ if err != nil {
+ return nil, err
}
- return serialized
+ return memberOf, nil
}
func appendIfNotEmpty(slice []string, values ...string) []string {
@@ -527,11 +545,11 @@ func appendIfNotEmpty(slice []string, values ...string) []string {
return slice
}
-func getLdapAttr(name string, result *LDAP.SearchResult) string {
+func getLdapAttr(name string, result *ldap.SearchResult) string {
return getLdapAttrN(name, result, 0)
}
-func getLdapAttrN(name string, result *LDAP.SearchResult, n int) string {
+func getLdapAttrN(name string, result *ldap.SearchResult, n int) string {
if strings.ToLower(name) == "dn" {
return result.Entries[n].DN
}
@@ -545,11 +563,11 @@ func getLdapAttrN(name string, result *LDAP.SearchResult, n int) string {
return ""
}
-func getLdapAttrArray(name string, result *LDAP.SearchResult) []string {
+func getLdapAttrArray(name string, result *ldap.SearchResult) []string {
return getLdapAttrArrayN(name, result, 0)
}
-func getLdapAttrArrayN(name string, result *LDAP.SearchResult, n int) []string {
+func getLdapAttrArrayN(name string, result *ldap.SearchResult, n int) []string {
for _, attr := range result.Entries[n].Attributes {
if attr.Name == name {
return attr.Values
diff --git a/pkg/services/ldap/ldap_helpers_test.go b/pkg/services/ldap/ldap_helpers_test.go
new file mode 100644
index 000000000000..d995d0fd6992
--- /dev/null
+++ b/pkg/services/ldap/ldap_helpers_test.go
@@ -0,0 +1,140 @@
+package ldap
+
+import (
+ "testing"
+
+ . "github.com/smartystreets/goconvey/convey"
+ "gopkg.in/ldap.v3"
+
+ "github.com/grafana/grafana/pkg/infra/log"
+)
+
+func TestLDAPHelpers(t *testing.T) {
+ Convey("serializeUsers()", t, func() {
+ Convey("simple case", func() {
+ server := &Server{
+ config: &ServerConfig{
+ Attr: AttributeMap{
+ Username: "username",
+ Name: "name",
+ MemberOf: "memberof",
+ Email: "email",
+ },
+ SearchBaseDNs: []string{"BaseDNHere"},
+ },
+ connection: &mockConnection{},
+ log: log.New("test-logger"),
+ }
+
+ entry := ldap.Entry{
+ DN: "dn", Attributes: []*ldap.EntryAttribute{
+ {Name: "username", Values: []string{"roelgerrits"}},
+ {Name: "surname", Values: []string{"Gerrits"}},
+ {Name: "email", Values: []string{"roel@test.com"}},
+ {Name: "name", Values: []string{"Roel"}},
+ {Name: "memberof", Values: []string{"admins"}},
+ }}
+ users := &ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
+
+ result, err := server.serializeUsers(users)
+
+ So(err, ShouldBeNil)
+ So(result[0].Login, ShouldEqual, "roelgerrits")
+ So(result[0].Email, ShouldEqual, "roel@test.com")
+ So(result[0].Groups, ShouldContain, "admins")
+ })
+
+ Convey("without lastname", func() {
+ server := &Server{
+ config: &ServerConfig{
+ Attr: AttributeMap{
+ Username: "username",
+ Name: "name",
+ MemberOf: "memberof",
+ Email: "email",
+ },
+ SearchBaseDNs: []string{"BaseDNHere"},
+ },
+ connection: &mockConnection{},
+ log: log.New("test-logger"),
+ }
+
+ entry := ldap.Entry{
+ DN: "dn", Attributes: []*ldap.EntryAttribute{
+ {Name: "username", Values: []string{"roelgerrits"}},
+ {Name: "email", Values: []string{"roel@test.com"}},
+ {Name: "name", Values: []string{"Roel"}},
+ {Name: "memberof", Values: []string{"admins"}},
+ }}
+ users := &ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
+
+ result, err := server.serializeUsers(users)
+
+ So(err, ShouldBeNil)
+ So(result[0].Name, ShouldEqual, "Roel")
+ })
+ })
+
+ Convey("serverBind()", t, func() {
+ Convey("Given bind dn and password configured", func() {
+ connection := &mockConnection{}
+ var actualUsername, actualPassword string
+ connection.bindProvider = func(username, password string) error {
+ actualUsername = username
+ actualPassword = password
+ return nil
+ }
+ server := &Server{
+ connection: connection,
+ config: &ServerConfig{
+ BindDN: "o=users,dc=grafana,dc=org",
+ BindPassword: "bindpwd",
+ },
+ }
+ err := server.serverBind()
+ So(err, ShouldBeNil)
+ So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org")
+ So(actualPassword, ShouldEqual, "bindpwd")
+ })
+
+ Convey("Given bind dn configured", func() {
+ connection := &mockConnection{}
+ unauthenticatedBindWasCalled := false
+ var actualUsername string
+ connection.unauthenticatedBindProvider = func(username string) error {
+ unauthenticatedBindWasCalled = true
+ actualUsername = username
+ return nil
+ }
+ server := &Server{
+ connection: connection,
+ config: &ServerConfig{
+ BindDN: "o=users,dc=grafana,dc=org",
+ },
+ }
+ err := server.serverBind()
+ So(err, ShouldBeNil)
+ So(unauthenticatedBindWasCalled, ShouldBeTrue)
+ So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org")
+ })
+
+ Convey("Given empty bind dn and password", func() {
+ connection := &mockConnection{}
+ unauthenticatedBindWasCalled := false
+ var actualUsername string
+ connection.unauthenticatedBindProvider = func(username string) error {
+ unauthenticatedBindWasCalled = true
+ actualUsername = username
+ return nil
+ }
+ server := &Server{
+ connection: connection,
+ config: &ServerConfig{},
+ }
+ err := server.serverBind()
+ So(err, ShouldBeNil)
+ So(unauthenticatedBindWasCalled, ShouldBeTrue)
+ So(actualUsername, ShouldBeEmpty)
+ })
+ })
+}
diff --git a/pkg/services/ldap/ldap_login_test.go b/pkg/services/ldap/ldap_login_test.go
index b8dd502667ea..9da82cc6e981 100644
--- a/pkg/services/ldap/ldap_login_test.go
+++ b/pkg/services/ldap/ldap_login_test.go
@@ -7,23 +7,94 @@ import (
"gopkg.in/ldap.v3"
"github.com/grafana/grafana/pkg/infra/log"
+ "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/user"
)
-func TestLdapLogin(t *testing.T) {
- Convey("Login using ldap", t, func() {
- AuthScenario("When login with invalid credentials", func(scenario *scenarioContext) {
- conn := &mockLdapConn{}
+func TestLDAPLogin(t *testing.T) {
+ Convey("Login()", t, func() {
+ authScenario("When user is log in and updated", func(sc *scenarioContext) {
+ // arrange
+ mockConnection := &mockConnection{}
+
+ auth := &Server{
+ config: &ServerConfig{
+ Host: "",
+ RootCACert: "",
+ Groups: []*GroupToOrgRole{
+ {GroupDN: "*", OrgRole: "Admin"},
+ },
+ Attr: AttributeMap{
+ Username: "username",
+ Surname: "surname",
+ Email: "email",
+ Name: "name",
+ MemberOf: "memberof",
+ },
+ SearchBaseDNs: []string{"BaseDNHere"},
+ },
+ connection: mockConnection,
+ log: log.New("test-logger"),
+ }
+
+ entry := ldap.Entry{
+ DN: "dn", Attributes: []*ldap.EntryAttribute{
+ {Name: "username", Values: []string{"roelgerrits"}},
+ {Name: "surname", Values: []string{"Gerrits"}},
+ {Name: "email", Values: []string{"roel@test.com"}},
+ {Name: "name", Values: []string{"Roel"}},
+ {Name: "memberof", Values: []string{"admins"}},
+ }}
+ result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
+ mockConnection.setSearchResult(&result)
+
+ query := &models.LoginUserQuery{
+ Username: "roelgerrits",
+ }
+
+ sc.userQueryReturns(&models.User{
+ Id: 1,
+ Email: "roel@test.net",
+ Name: "Roel Gerrits",
+ Login: "roelgerrits",
+ })
+ sc.userOrgsQueryReturns([]*models.UserOrgDTO{})
+
+ // act
+ extUser, _ := auth.Login(query)
+ userInfo, err := user.Upsert(&user.UpsertArgs{
+ SignupAllowed: true,
+ ExternalUser: extUser,
+ })
+
+ // assert
+
+ // Check absence of the error
+ So(err, ShouldBeNil)
+
+ // User should be searched in ldap
+ So(mockConnection.searchCalled, ShouldBeTrue)
+
+ // Info should be updated (email differs)
+ So(userInfo.Email, ShouldEqual, "roel@test.com")
+
+ // User should have admin privileges
+ So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin")
+ })
+
+ authScenario("When login with invalid credentials", func(scenario *scenarioContext) {
+ connection := &mockConnection{}
entry := ldap.Entry{}
result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
- conn.setSearchResult(&result)
+ connection.setSearchResult(&result)
- conn.bindProvider = func(username, password string) error {
+ connection.bindProvider = func(username, password string) error {
return &ldap.Error{
ResultCode: 49,
}
}
- auth := &Auth{
- server: &ServerConfig{
+ auth := &Server{
+ config: &ServerConfig{
Attr: AttributeMap{
Username: "username",
Name: "name",
@@ -31,19 +102,19 @@ func TestLdapLogin(t *testing.T) {
},
SearchBaseDNs: []string{"BaseDNHere"},
},
- conn: conn,
- log: log.New("test-logger"),
+ connection: connection,
+ log: log.New("test-logger"),
}
- err := auth.Login(scenario.loginUserQuery)
+ _, err := auth.Login(scenario.loginUserQuery)
Convey("it should return invalid credentials error", func() {
So(err, ShouldEqual, ErrInvalidCredentials)
})
})
- AuthScenario("When login with valid credentials", func(scenario *scenarioContext) {
- conn := &mockLdapConn{}
+ authScenario("When login with valid credentials", func(scenario *scenarioContext) {
+ connection := &mockConnection{}
entry := ldap.Entry{
DN: "dn", Attributes: []*ldap.EntryAttribute{
{Name: "username", Values: []string{"markelog"}},
@@ -54,13 +125,13 @@ func TestLdapLogin(t *testing.T) {
},
}
result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
- conn.setSearchResult(&result)
+ connection.setSearchResult(&result)
- conn.bindProvider = func(username, password string) error {
+ connection.bindProvider = func(username, password string) error {
return nil
}
- auth := &Auth{
- server: &ServerConfig{
+ auth := &Server{
+ config: &ServerConfig{
Attr: AttributeMap{
Username: "username",
Name: "name",
@@ -68,19 +139,14 @@ func TestLdapLogin(t *testing.T) {
},
SearchBaseDNs: []string{"BaseDNHere"},
},
- conn: conn,
- log: log.New("test-logger"),
+ connection: connection,
+ log: log.New("test-logger"),
}
- err := auth.Login(scenario.loginUserQuery)
+ resp, err := auth.Login(scenario.loginUserQuery)
- Convey("it should not return error", func() {
- So(err, ShouldBeNil)
- })
-
- Convey("it should get user", func() {
- So(scenario.loginUserQuery.User.Login, ShouldEqual, "markelog")
- })
+ So(err, ShouldBeNil)
+ So(resp.Login, ShouldEqual, "markelog")
})
})
}
diff --git a/pkg/services/ldap/ldap_test.go b/pkg/services/ldap/ldap_test.go
index 4da041ae1642..266fe22a4fc7 100644
--- a/pkg/services/ldap/ldap_test.go
+++ b/pkg/services/ldap/ldap_test.go
@@ -1,496 +1,157 @@
package ldap
import (
- "context"
"testing"
. "github.com/smartystreets/goconvey/convey"
- "gopkg.in/ldap.v3"
+ ldap "gopkg.in/ldap.v3"
- "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
- m "github.com/grafana/grafana/pkg/models"
)
func TestAuth(t *testing.T) {
- Convey("initialBind", t, func() {
- Convey("Given bind dn and password configured", func() {
- conn := &mockLdapConn{}
- var actualUsername, actualPassword string
- conn.bindProvider = func(username, password string) error {
- actualUsername = username
- actualPassword = password
- return nil
- }
- Auth := &Auth{
- conn: conn,
- server: &ServerConfig{
- BindDN: "cn=%s,o=users,dc=grafana,dc=org",
- BindPassword: "bindpwd",
- },
- }
- err := Auth.initialBind("user", "pwd")
- So(err, ShouldBeNil)
- So(Auth.requireSecondBind, ShouldBeTrue)
- So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org")
- So(actualPassword, ShouldEqual, "bindpwd")
- })
+ Convey("Add()", t, func() {
+ connection := &mockConnection{}
- Convey("Given bind dn configured", func() {
- conn := &mockLdapConn{}
- var actualUsername, actualPassword string
- conn.bindProvider = func(username, password string) error {
- actualUsername = username
- actualPassword = password
- return nil
- }
- Auth := &Auth{
- conn: conn,
- server: &ServerConfig{
- BindDN: "cn=%s,o=users,dc=grafana,dc=org",
- },
- }
- err := Auth.initialBind("user", "pwd")
- So(err, ShouldBeNil)
- So(Auth.requireSecondBind, ShouldBeFalse)
- So(actualUsername, ShouldEqual, "cn=user,o=users,dc=grafana,dc=org")
- So(actualPassword, ShouldEqual, "pwd")
- })
+ auth := &Server{
+ config: &ServerConfig{
+ SearchBaseDNs: []string{"BaseDNHere"},
+ },
+ connection: connection,
+ log: log.New("test-logger"),
+ }
- Convey("Given empty bind dn and password", func() {
- conn := &mockLdapConn{}
- unauthenticatedBindWasCalled := false
- var actualUsername string
- conn.unauthenticatedBindProvider = func(username string) error {
- unauthenticatedBindWasCalled = true
- actualUsername = username
- return nil
- }
- Auth := &Auth{
- conn: conn,
- server: &ServerConfig{},
- }
- err := Auth.initialBind("user", "pwd")
- So(err, ShouldBeNil)
- So(Auth.requireSecondBind, ShouldBeTrue)
- So(unauthenticatedBindWasCalled, ShouldBeTrue)
- So(actualUsername, ShouldBeEmpty)
- })
- })
+ Convey("Adds user", func() {
+ err := auth.Add(
+ "cn=ldap-tuz,ou=users,dc=grafana,dc=org",
+ map[string][]string{
+ "mail": {"ldap-viewer@grafana.com"},
+ "userPassword": {"grafana"},
+ "objectClass": {
+ "person",
+ "top",
+ "inetOrgPerson",
+ "organizationalPerson",
+ },
+ "sn": {"ldap-tuz"},
+ "cn": {"ldap-tuz"},
+ },
+ )
+
+ hasMail := false
+ hasUserPassword := false
+ hasObjectClass := false
+ hasSN := false
+ hasCN := false
- Convey("serverBind", t, func() {
- Convey("Given bind dn and password configured", func() {
- conn := &mockLdapConn{}
- var actualUsername, actualPassword string
- conn.bindProvider = func(username, password string) error {
- actualUsername = username
- actualPassword = password
- return nil
- }
- Auth := &Auth{
- conn: conn,
- server: &ServerConfig{
- BindDN: "o=users,dc=grafana,dc=org",
- BindPassword: "bindpwd",
- },
- }
- err := Auth.serverBind()
So(err, ShouldBeNil)
- So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org")
- So(actualPassword, ShouldEqual, "bindpwd")
- })
-
- Convey("Given bind dn configured", func() {
- conn := &mockLdapConn{}
- unauthenticatedBindWasCalled := false
- var actualUsername string
- conn.unauthenticatedBindProvider = func(username string) error {
- unauthenticatedBindWasCalled = true
- actualUsername = username
- return nil
- }
- Auth := &Auth{
- conn: conn,
- server: &ServerConfig{
- BindDN: "o=users,dc=grafana,dc=org",
- },
+ So(connection.addParams.Controls, ShouldBeNil)
+ So(connection.addCalled, ShouldBeTrue)
+ So(
+ connection.addParams.DN,
+ ShouldEqual,
+ "cn=ldap-tuz,ou=users,dc=grafana,dc=org",
+ )
+
+ attrs := connection.addParams.Attributes
+ for _, value := range attrs {
+ if value.Type == "mail" {
+ So(value.Vals, ShouldContain, "ldap-viewer@grafana.com")
+ hasMail = true
+ }
+
+ if value.Type == "userPassword" {
+ hasUserPassword = true
+ So(value.Vals, ShouldContain, "grafana")
+ }
+
+ if value.Type == "objectClass" {
+ hasObjectClass = true
+ So(value.Vals, ShouldContain, "person")
+ So(value.Vals, ShouldContain, "top")
+ So(value.Vals, ShouldContain, "inetOrgPerson")
+ So(value.Vals, ShouldContain, "organizationalPerson")
+ }
+
+ if value.Type == "sn" {
+ hasSN = true
+ So(value.Vals, ShouldContain, "ldap-tuz")
+ }
+
+ if value.Type == "cn" {
+ hasCN = true
+ So(value.Vals, ShouldContain, "ldap-tuz")
+ }
}
- err := Auth.serverBind()
- So(err, ShouldBeNil)
- So(unauthenticatedBindWasCalled, ShouldBeTrue)
- So(actualUsername, ShouldEqual, "o=users,dc=grafana,dc=org")
- })
- Convey("Given empty bind dn and password", func() {
- conn := &mockLdapConn{}
- unauthenticatedBindWasCalled := false
- var actualUsername string
- conn.unauthenticatedBindProvider = func(username string) error {
- unauthenticatedBindWasCalled = true
- actualUsername = username
- return nil
- }
- Auth := &Auth{
- conn: conn,
- server: &ServerConfig{},
- }
- err := Auth.serverBind()
- So(err, ShouldBeNil)
- So(unauthenticatedBindWasCalled, ShouldBeTrue)
- So(actualUsername, ShouldBeEmpty)
+ So(hasMail, ShouldBeTrue)
+ So(hasUserPassword, ShouldBeTrue)
+ So(hasObjectClass, ShouldBeTrue)
+ So(hasSN, ShouldBeTrue)
+ So(hasCN, ShouldBeTrue)
})
})
- Convey("When translating ldap user to grafana user", t, func() {
+ Convey("Remove()", t, func() {
+ connection := &mockConnection{}
- var user1 = &m.User{}
-
- bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.UpsertUserCommand) error {
- cmd.Result = user1
- cmd.Result.Login = "torkelo"
- return nil
- })
-
- Convey("Given no ldap group map match", func() {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{{}},
- })
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{})
-
- So(err, ShouldEqual, ErrInvalidCredentials)
- })
-
- AuthScenario("Given wildcard group match", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "*", OrgRole: "Admin"},
- },
- })
-
- sc.userQueryReturns(user1)
-
- result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{})
- So(err, ShouldBeNil)
- So(result, ShouldEqual, user1)
- })
-
- AuthScenario("Given exact group match", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=users", OrgRole: "Admin"},
- },
- })
-
- sc.userQueryReturns(user1)
-
- result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{MemberOf: []string{"cn=users"}})
- So(err, ShouldBeNil)
- So(result, ShouldEqual, user1)
- })
-
- AuthScenario("Given group match with different case", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=users", OrgRole: "Admin"},
- },
- })
-
- sc.userQueryReturns(user1)
-
- result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{MemberOf: []string{"CN=users"}})
- So(err, ShouldBeNil)
- So(result, ShouldEqual, user1)
- })
-
- AuthScenario("Given no existing grafana user", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=admin", OrgRole: "Admin"},
- {GroupDN: "cn=editor", OrgRole: "Editor"},
- {GroupDN: "*", OrgRole: "Viewer"},
- },
- })
-
- sc.userQueryReturns(nil)
-
- result, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- DN: "torkelo",
- Username: "torkelo",
- Email: "my@email.com",
- MemberOf: []string{"cn=editor"},
- })
-
- So(err, ShouldBeNil)
-
- Convey("Should return new user", func() {
- So(result.Login, ShouldEqual, "torkelo")
- })
-
- Convey("Should set isGrafanaAdmin to false by default", func() {
- So(result.IsAdmin, ShouldBeFalse)
- })
-
- })
-
- })
-
- Convey("When syncing ldap groups to grafana org roles", t, func() {
- AuthScenario("given no current user orgs", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=users", OrgRole: "Admin"},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=users"},
- })
-
- Convey("Should create new org user", func() {
- So(err, ShouldBeNil)
- So(sc.addOrgUserCmd, ShouldNotBeNil)
- So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
- })
- })
-
- AuthScenario("given different current org role", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=users", OrgId: 1, OrgRole: "Admin"},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}})
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=users"},
- })
-
- Convey("Should update org role", func() {
- So(err, ShouldBeNil)
- So(sc.updateOrgUserCmd, ShouldNotBeNil)
- So(sc.updateOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
- So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
- })
- })
-
- AuthScenario("given current org role is removed in ldap", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=users", OrgId: 2, OrgRole: "Admin"},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{
- {OrgId: 1, Role: m.ROLE_EDITOR},
- {OrgId: 2, Role: m.ROLE_EDITOR},
- })
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=users"},
- })
-
- Convey("Should remove org role", func() {
- So(err, ShouldBeNil)
- So(sc.removeOrgUserCmd, ShouldNotBeNil)
- So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 2)
- })
- })
-
- AuthScenario("given org role is updated in config", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=admin", OrgId: 1, OrgRole: "Admin"},
- {GroupDN: "cn=users", OrgId: 1, OrgRole: "Viewer"},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}})
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=users"},
- })
-
- Convey("Should update org role", func() {
- So(err, ShouldBeNil)
- So(sc.removeOrgUserCmd, ShouldBeNil)
- So(sc.updateOrgUserCmd, ShouldNotBeNil)
- So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
- })
- })
-
- AuthScenario("given multiple matching ldap groups", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"},
- {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_ADMIN}})
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=admins"},
- })
-
- Convey("Should take first match, and ignore subsequent matches", func() {
- So(err, ShouldBeNil)
- So(sc.updateOrgUserCmd, ShouldBeNil)
- So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
- })
- })
-
- AuthScenario("given multiple matching ldap groups and no existing groups", func(sc *scenarioContext) {
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"},
- {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=admins"},
- })
-
- Convey("Should take first match, and ignore subsequent matches", func() {
- So(err, ShouldBeNil)
- So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
- So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
- })
-
- Convey("Should not update permissions unless specified", func() {
- So(err, ShouldBeNil)
- So(sc.updateUserPermissionsCmd, ShouldBeNil)
- })
- })
-
- AuthScenario("given ldap groups with grafana_admin=true", func(sc *scenarioContext) {
- trueVal := true
-
- Auth := New(&ServerConfig{
- Groups: []*GroupToOrgRole{
- {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin", IsGrafanaAdmin: &trueVal},
- },
- })
-
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
- _, err := Auth.GetGrafanaUserFor(nil, &UserInfo{
- MemberOf: []string{"cn=admins"},
- })
-
- Convey("Should create user with admin set to true", func() {
- So(err, ShouldBeNil)
- So(sc.updateUserPermissionsCmd.IsGrafanaAdmin, ShouldBeTrue)
- })
- })
- })
-
- Convey("When calling SyncUser", t, func() {
- mockLdapConnection := &mockLdapConn{}
-
- auth := &Auth{
- server: &ServerConfig{
- Host: "",
- RootCACert: "",
- Groups: []*GroupToOrgRole{
- {GroupDN: "*", OrgRole: "Admin"},
- },
- Attr: AttributeMap{
- Username: "username",
- Surname: "surname",
- Email: "email",
- Name: "name",
- MemberOf: "memberof",
- },
+ auth := &Server{
+ config: &ServerConfig{
SearchBaseDNs: []string{"BaseDNHere"},
},
- conn: mockLdapConnection,
- log: log.New("test-logger"),
- }
-
- dialCalled := false
- dial = func(network, addr string) (IConnection, error) {
- dialCalled = true
- return mockLdapConnection, nil
+ connection: connection,
+ log: log.New("test-logger"),
}
- entry := ldap.Entry{
- DN: "dn", Attributes: []*ldap.EntryAttribute{
- {Name: "username", Values: []string{"roelgerrits"}},
- {Name: "surname", Values: []string{"Gerrits"}},
- {Name: "email", Values: []string{"roel@test.com"}},
- {Name: "name", Values: []string{"Roel"}},
- {Name: "memberof", Values: []string{"admins"}},
- }}
- result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
- mockLdapConnection.setSearchResult(&result)
+ Convey("Removes the user", func() {
+ dn := "cn=ldap-tuz,ou=users,dc=grafana,dc=org"
+ err := auth.Remove(dn)
- AuthScenario("When ldapUser found call syncInfo and orgRoles", func(sc *scenarioContext) {
- // arrange
- query := &m.LoginUserQuery{
- Username: "roelgerrits",
- }
-
- hookDial = nil
-
- sc.userQueryReturns(&m.User{
- Id: 1,
- Email: "roel@test.net",
- Name: "Roel Gerrits",
- Login: "roelgerrits",
- })
- sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
-
- // act
- syncErrResult := auth.SyncUser(query)
-
- // assert
- So(dialCalled, ShouldBeTrue)
- So(syncErrResult, ShouldBeNil)
- // User should be searched in ldap
- So(mockLdapConnection.searchCalled, ShouldBeTrue)
- // Info should be updated (email differs)
- So(sc.updateUserCmd.Email, ShouldEqual, "roel@test.com")
- // User should have admin privileges
- So(sc.addOrgUserCmd.UserId, ShouldEqual, 1)
- So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin")
+ So(err, ShouldBeNil)
+ So(connection.delCalled, ShouldBeTrue)
+ So(connection.delParams.Controls, ShouldBeNil)
+ So(connection.delParams.DN, ShouldEqual, dn)
})
})
- Convey("When searching for a user and not all five attributes are mapped", t, func() {
- mockLdapConnection := &mockLdapConn{}
- entry := ldap.Entry{
- DN: "dn", Attributes: []*ldap.EntryAttribute{
- {Name: "username", Values: []string{"roelgerrits"}},
- {Name: "surname", Values: []string{"Gerrits"}},
- {Name: "email", Values: []string{"roel@test.com"}},
- {Name: "name", Values: []string{"Roel"}},
- {Name: "memberof", Values: []string{"admins"}},
- }}
- result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
- mockLdapConnection.setSearchResult(&result)
-
- // Set up attribute map without surname and email
- Auth := &Auth{
- server: &ServerConfig{
- Attr: AttributeMap{
- Username: "username",
- Name: "name",
- MemberOf: "memberof",
- },
- SearchBaseDNs: []string{"BaseDNHere"},
- },
- conn: mockLdapConnection,
- log: log.New("test-logger"),
- }
+ Convey("Users()", t, func() {
+ Convey("find one user", func() {
+ mockConnection := &mockConnection{}
+ entry := ldap.Entry{
+ DN: "dn", Attributes: []*ldap.EntryAttribute{
+ {Name: "username", Values: []string{"roelgerrits"}},
+ {Name: "surname", Values: []string{"Gerrits"}},
+ {Name: "email", Values: []string{"roel@test.com"}},
+ {Name: "name", Values: []string{"Roel"}},
+ {Name: "memberof", Values: []string{"admins"}},
+ }}
+ result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
+ mockConnection.setSearchResult(&result)
+
+ // Set up attribute map without surname and email
+ server := &Server{
+ config: &ServerConfig{
+ Attr: AttributeMap{
+ Username: "username",
+ Name: "name",
+ MemberOf: "memberof",
+ },
+ SearchBaseDNs: []string{"BaseDNHere"},
+ },
+ connection: mockConnection,
+ log: log.New("test-logger"),
+ }
- searchResult, err := Auth.searchForUser("roelgerrits")
+ searchResult, err := server.Users([]string{"roelgerrits"})
- So(err, ShouldBeNil)
- So(searchResult, ShouldNotBeNil)
+ So(err, ShouldBeNil)
+ So(searchResult, ShouldNotBeNil)
- // User should be searched in ldap
- So(mockLdapConnection.searchCalled, ShouldBeTrue)
+ // User should be searched in ldap
+ So(mockConnection.searchCalled, ShouldBeTrue)
- // No empty attributes should be added to the search request
- So(len(mockLdapConnection.searchAttributes), ShouldEqual, 3)
+ // No empty attributes should be added to the search request
+ So(len(mockConnection.searchAttributes), ShouldEqual, 3)
+ })
})
}
diff --git a/pkg/services/ldap/settings.go b/pkg/services/ldap/settings.go
index 0a0f66d9d734..15f1583fea71 100644
--- a/pkg/services/ldap/settings.go
+++ b/pkg/services/ldap/settings.go
@@ -13,10 +13,12 @@ import (
"github.com/grafana/grafana/pkg/util/errutil"
)
+// Config holds list of connections to LDAP
type Config struct {
Servers []*ServerConfig `toml:"servers"`
}
+// ServerConfig holds connection data to LDAP
type ServerConfig struct {
Host string `toml:"host"`
Port int `toml:"port"`
@@ -108,11 +110,11 @@ func readConfig(configFile string) (*Config, error) {
_, err := toml.DecodeFile(configFile, result)
if err != nil {
- return nil, errutil.Wrap("Failed to load ldap config file", err)
+ return nil, errutil.Wrap("Failed to load LDAP config file", err)
}
if len(result.Servers) == 0 {
- return nil, xerrors.New("ldap enabled but no ldap servers defined in config file")
+ return nil, xerrors.New("LDAP enabled but no LDAP servers defined in config file")
}
// set default org id
diff --git a/pkg/services/ldap/test.go b/pkg/services/ldap/test.go
index 98d169b9a1ad..07fd9c6317c5 100644
--- a/pkg/services/ldap/test.go
+++ b/pkg/services/ldap/test.go
@@ -12,15 +12,22 @@ import (
"github.com/grafana/grafana/pkg/services/login"
)
-type mockLdapConn struct {
- result *ldap.SearchResult
- searchCalled bool
- searchAttributes []string
+type mockConnection struct {
+ searchResult *ldap.SearchResult
+ searchCalled bool
+ searchAttributes []string
+
+ addParams *ldap.AddRequest
+ addCalled bool
+
+ delParams *ldap.DelRequest
+ delCalled bool
+
bindProvider func(username, password string) error
unauthenticatedBindProvider func(username string) error
}
-func (c *mockLdapConn) Bind(username, password string) error {
+func (c *mockConnection) Bind(username, password string) error {
if c.bindProvider != nil {
return c.bindProvider(username, password)
}
@@ -28,7 +35,7 @@ func (c *mockLdapConn) Bind(username, password string) error {
return nil
}
-func (c *mockLdapConn) UnauthenticatedBind(username string) error {
+func (c *mockConnection) UnauthenticatedBind(username string) error {
if c.unauthenticatedBindProvider != nil {
return c.unauthenticatedBindProvider(username)
}
@@ -36,23 +43,35 @@ func (c *mockLdapConn) UnauthenticatedBind(username string) error {
return nil
}
-func (c *mockLdapConn) Close() {}
+func (c *mockConnection) Close() {}
-func (c *mockLdapConn) setSearchResult(result *ldap.SearchResult) {
- c.result = result
+func (c *mockConnection) setSearchResult(result *ldap.SearchResult) {
+ c.searchResult = result
}
-func (c *mockLdapConn) Search(sr *ldap.SearchRequest) (*ldap.SearchResult, error) {
+func (c *mockConnection) Search(sr *ldap.SearchRequest) (*ldap.SearchResult, error) {
c.searchCalled = true
c.searchAttributes = sr.Attributes
- return c.result, nil
+ return c.searchResult, nil
+}
+
+func (c *mockConnection) Add(request *ldap.AddRequest) error {
+ c.addCalled = true
+ c.addParams = request
+ return nil
}
-func (c *mockLdapConn) StartTLS(*tls.Config) error {
+func (c *mockConnection) Del(request *ldap.DelRequest) error {
+ c.delCalled = true
+ c.delParams = request
return nil
}
-func AuthScenario(desc string, fn scenarioFunc) {
+func (c *mockConnection) StartTLS(*tls.Config) error {
+ return nil
+}
+
+func authScenario(desc string, fn scenarioFunc) {
Convey(desc, func() {
defer bus.ClearBusHandlers()
@@ -64,10 +83,6 @@ func AuthScenario(desc string, fn scenarioFunc) {
},
}
- hookDial = func(auth *Auth) error {
- return nil
- }
-
loginService := &login.LoginService{
Bus: bus.GetBus(),
}
diff --git a/pkg/services/multildap/multildap.go b/pkg/services/multildap/multildap.go
new file mode 100644
index 000000000000..1b309c646e17
--- /dev/null
+++ b/pkg/services/multildap/multildap.go
@@ -0,0 +1,204 @@
+package multildap
+
+import (
+ "errors"
+
+ "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/ldap"
+)
+
+// GetConfig gets LDAP config
+var GetConfig = ldap.GetConfig
+
+// IsEnabled checks if LDAP is enabled
+var IsEnabled = ldap.IsEnabled
+
+// ErrInvalidCredentials is returned if username and password do not match
+var ErrInvalidCredentials = ldap.ErrInvalidCredentials
+
+// ErrNoLDAPServers is returned when there is no LDAP servers specified
+var ErrNoLDAPServers = errors.New("No LDAP servers are configured")
+
+// ErrDidNotFindUser if request for user is unsuccessful
+var ErrDidNotFindUser = errors.New("Did not find a user")
+
+// IMultiLDAP is interface for MultiLDAP
+type IMultiLDAP interface {
+ Login(query *models.LoginUserQuery) (
+ *models.ExternalUserInfo, error,
+ )
+
+ Users(logins []string) (
+ []*models.ExternalUserInfo, error,
+ )
+
+ User(login string) (
+ *models.ExternalUserInfo, error,
+ )
+
+ Add(dn string, values map[string][]string) error
+ Remove(dn string) error
+}
+
+// MultiLDAP is basic struct of LDAP authorization
+type MultiLDAP struct {
+ configs []*ldap.ServerConfig
+}
+
+// New creates the new LDAP auth
+func New(configs []*ldap.ServerConfig) IMultiLDAP {
+ return &MultiLDAP{
+ configs: configs,
+ }
+}
+
+// Add adds user to the *first* defined LDAP
+func (multiples *MultiLDAP) Add(
+ dn string,
+ values map[string][]string,
+) error {
+ if len(multiples.configs) == 0 {
+ return ErrNoLDAPServers
+ }
+
+ config := multiples.configs[0]
+ ldap := ldap.New(config)
+
+ if err := ldap.Dial(); err != nil {
+ return err
+ }
+
+ defer ldap.Close()
+
+ err := ldap.Add(dn, values)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Remove removes user from the *first* defined LDAP
+func (multiples *MultiLDAP) Remove(dn string) error {
+ if len(multiples.configs) == 0 {
+ return ErrNoLDAPServers
+ }
+
+ config := multiples.configs[0]
+ ldap := ldap.New(config)
+
+ if err := ldap.Dial(); err != nil {
+ return err
+ }
+
+ defer ldap.Close()
+
+ err := ldap.Remove(dn)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Login tries to log in the user in multiples LDAP
+func (multiples *MultiLDAP) Login(query *models.LoginUserQuery) (
+ *models.ExternalUserInfo, error,
+) {
+ if len(multiples.configs) == 0 {
+ return nil, ErrNoLDAPServers
+ }
+
+ for _, config := range multiples.configs {
+ server := ldap.New(config)
+
+ if err := server.Dial(); err != nil {
+ return nil, err
+ }
+
+ defer server.Close()
+
+ user, err := server.Login(query)
+
+ if user != nil {
+ return user, nil
+ }
+
+ // Continue if we couldn't find the user
+ if err == ErrInvalidCredentials {
+ continue
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ return user, nil
+ }
+
+ // Return invalid credentials if we couldn't find the user anywhere
+ return nil, ErrInvalidCredentials
+}
+
+// User gets a user by login
+func (multiples *MultiLDAP) User(login string) (
+ *models.ExternalUserInfo,
+ error,
+) {
+
+ if len(multiples.configs) == 0 {
+ return nil, ErrNoLDAPServers
+ }
+
+ search := []string{login}
+ for _, config := range multiples.configs {
+ server := ldap.New(config)
+
+ if err := server.Dial(); err != nil {
+ return nil, err
+ }
+
+ defer server.Close()
+
+ users, err := server.Users(search)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(users) != 0 {
+ return users[0], nil
+ }
+ }
+
+ return nil, ErrDidNotFindUser
+}
+
+// Users gets users from multiple LDAP servers
+func (multiples *MultiLDAP) Users(logins []string) (
+ []*models.ExternalUserInfo,
+ error,
+) {
+ var result []*models.ExternalUserInfo
+
+ if len(multiples.configs) == 0 {
+ return nil, ErrNoLDAPServers
+ }
+
+ for _, config := range multiples.configs {
+ server := ldap.New(config)
+
+ if err := server.Dial(); err != nil {
+ return nil, err
+ }
+
+ defer server.Close()
+
+ users, err := server.Users(logins)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, users...)
+ }
+
+ return result, nil
+}
diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go
index 44d0f545bfcc..58bcc5578593 100644
--- a/pkg/services/sqlstore/sqlstore.go
+++ b/pkg/services/sqlstore/sqlstore.go
@@ -8,7 +8,6 @@ import (
"path"
"path/filepath"
"strings"
- "testing"
"time"
"github.com/go-sql-driver/mysql"
@@ -280,7 +279,14 @@ func (ss *SqlStore) readConfig() {
ss.dbCfg.CacheMode = sec.Key("cache_mode").MustString("private")
}
-func InitTestDB(t *testing.T) *SqlStore {
+// Interface of arguments for testing db
+type ITestDB interface {
+ Helper()
+ Fatalf(format string, args ...interface{})
+}
+
+// InitTestDB initiliaze test DB
+func InitTestDB(t ITestDB) *SqlStore {
t.Helper()
sqlstore := &SqlStore{}
sqlstore.skipEnsureAdmin = true
diff --git a/pkg/services/user/user.go b/pkg/services/user/user.go
new file mode 100644
index 000000000000..94762c811b08
--- /dev/null
+++ b/pkg/services/user/user.go
@@ -0,0 +1,39 @@
+package user
+
+import (
+ "github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/models"
+)
+
+// UpsertArgs are object for Upsert method
+type UpsertArgs struct {
+ ReqContext *models.ReqContext
+ ExternalUser *models.ExternalUserInfo
+ SignupAllowed bool
+}
+
+// Upsert add/update grafana user
+func Upsert(args *UpsertArgs) (*models.User, error) {
+ query := &models.UpsertUserCommand{
+ ReqContext: args.ReqContext,
+ ExternalUser: args.ExternalUser,
+ SignupAllowed: args.SignupAllowed,
+ }
+ err := bus.Dispatch(query)
+ if err != nil {
+ return nil, err
+ }
+
+ return query.Result, nil
+}
+
+// Get the users
+func Get(
+ query *models.SearchUsersQuery,
+) ([]*models.UserSearchHitDTO, error) {
+ if err := bus.Dispatch(query); err != nil {
+ return nil, err
+ }
+
+ return query.Result.Users, nil
+}
diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go
index 192f300021b8..194617f3bcb5 100644
--- a/pkg/setting/setting.go
+++ b/pkg/setting/setting.go
@@ -805,6 +805,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
// auth proxy
authProxy := iniFile.Section("auth.proxy")
AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
+
AuthProxyHeaderName, err = valueAsString(authProxy, "header_name", "")
if err != nil {
return err
diff --git a/vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md b/vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md
new file mode 100644
index 000000000000..ec6e6d7a3767
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/BENCHMARKS.md
@@ -0,0 +1,134 @@
+go test -bench=. -benchmem
+goos: darwin
+goarch: amd64
+pkg: github.com/brianvoe/gofakeit
+Table generated with tablesgenerator.com/markdown_tables
+
+| Benchmark | Ops | CPU | MEM | MEM alloc |
+|---------------------------------|-----------|-------------|------------|--------------|
+| BenchmarkAddress-4 | 1000000 | 1998 ns/op | 248 B/op | 7 allocs/op |
+| BenchmarkStreet-4 | 1000000 | 1278 ns/op | 62 B/op | 3 allocs/op |
+| BenchmarkStreetNumber-4 | 5000000 | 344 ns/op | 36 B/op | 2 allocs/op |
+| BenchmarkStreetPrefix-4 | 10000000 | 121 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkStreetName-4 | 10000000 | 122 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkStreetSuffix-4 | 10000000 | 122 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkCity-4 | 5000000 | 326 ns/op | 15 B/op | 1 allocs/op |
+| BenchmarkState-4 | 10000000 | 120 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkStateAbr-4 | 10000000 | 122 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkZip-4 | 5000000 | 315 ns/op | 5 B/op | 1 allocs/op |
+| BenchmarkCountry-4 | 10000000 | 126 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkCountryAbr-4 | 10000000 | 123 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLatitude-4 | 100000000 | 23.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLongitude-4 | 100000000 | 23.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLatitudeInRange-4 | 50000000 | 27.7 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLongitudeInRange-4 | 50000000 | 27.8 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBeerName-4 | 20000000 | 104 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBeerStyle-4 | 10000000 | 119 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBeerHop-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBeerYeast-4 | 20000000 | 106 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBeerMalt-4 | 20000000 | 114 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBeerIbu-4 | 20000000 | 71.0 ns/op | 8 B/op | 1 allocs/op |
+| BenchmarkBeerAlcohol-4 | 5000000 | 335 ns/op | 40 B/op | 3 allocs/op |
+| BenchmarkBeerBlg-4 | 5000000 | 338 ns/op | 48 B/op | 3 allocs/op |
+| BenchmarkBool-4 | 50000000 | 34.2 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkColor-4 | 20000000 | 112 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkSafeColor-4 | 20000000 | 102 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHexColor-4 | 3000000 | 491 ns/op | 24 B/op | 3 allocs/op |
+| BenchmarkRGBColor-4 | 20000000 | 103 ns/op | 32 B/op | 1 allocs/op |
+| BenchmarkCompany-4 | 5000000 | 353 ns/op | 22 B/op | 1 allocs/op |
+| BenchmarkCompanySuffix-4 | 20000000 | 89.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBuzzWord-4 | 20000000 | 99.0 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBS-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkContact-4 | 1000000 | 1121 ns/op | 178 B/op | 7 allocs/op |
+| BenchmarkPhone-4 | 5000000 | 346 ns/op | 16 B/op | 1 allocs/op |
+| BenchmarkPhoneFormatted-4 | 3000000 | 456 ns/op | 16 B/op | 1 allocs/op |
+| BenchmarkEmail-4 | 2000000 | 715 ns/op | 130 B/op | 5 allocs/op |
+| BenchmarkCurrency-4 | 10000000 | 125 ns/op | 32 B/op | 1 allocs/op |
+| BenchmarkCurrencyShort-4 | 20000000 | 104 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkCurrencyLong-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkPrice-4 | 50000000 | 27.2 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkDate-4 | 5000000 | 371 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkDateRange-4 | 10000000 | 238 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkMonth-4 | 30000000 | 44.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkDay-4 | 50000000 | 39.2 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkWeekDay-4 | 30000000 | 44.7 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkYear-4 | 20000000 | 115 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHour-4 | 30000000 | 39.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkMinute-4 | 50000000 | 40.4 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkSecond-4 | 30000000 | 40.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkNanoSecond-4 | 30000000 | 42.2 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkTimeZone-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkTimeZoneFull-4 | 20000000 | 118 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkTimeZoneAbv-4 | 20000000 | 105 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkTimeZoneOffset-4 | 10000000 | 147 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkMimeType-4 | 20000000 | 99.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkExtension-4 | 20000000 | 109 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkGenerate-4 | 1000000 | 1588 ns/op | 414 B/op | 11 allocs/op |
+| BenchmarkHackerPhrase-4 | 300000 | 4576 ns/op | 2295 B/op | 26 allocs/op |
+| BenchmarkHackerAbbreviation-4 | 20000000 | 101 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHackerAdjective-4 | 20000000 | 101 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHackerNoun-4 | 20000000 | 104 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHackerVerb-4 | 20000000 | 113 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHackerIngverb-4 | 20000000 | 98.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHipsterWord-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHipsterSentence-4 | 1000000 | 1636 ns/op | 353 B/op | 3 allocs/op |
+| BenchmarkHipsterParagraph-4 | 50000 | 31677 ns/op | 12351 B/op | 64 allocs/op |
+| BenchmarkImageURL-4 | 20000000 | 108 ns/op | 38 B/op | 3 allocs/op |
+| BenchmarkDomainName-4 | 3000000 | 491 ns/op | 76 B/op | 3 allocs/op |
+| BenchmarkDomainSuffix-4 | 20000000 | 99.4 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkURL-4 | 1000000 | 1201 ns/op | 278 B/op | 8 allocs/op |
+| BenchmarkHTTPMethod-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkIPv4Address-4 | 3000000 | 407 ns/op | 48 B/op | 5 allocs/op |
+| BenchmarkIPv6Address-4 | 3000000 | 552 ns/op | 96 B/op | 7 allocs/op |
+| BenchmarkUsername-4 | 5000000 | 307 ns/op | 16 B/op | 2 allocs/op |
+| BenchmarkJob-4 | 2000000 | 726 ns/op | 86 B/op | 2 allocs/op |
+| BenchmarkJobTitle-4 | 20000000 | 98.7 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkJobDescriptor-4 | 20000000 | 98.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkJobLevel-4 | 20000000 | 110 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLogLevel-4 | 20000000 | 107 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkReplaceWithNumbers-4 | 3000000 | 570 ns/op | 32 B/op | 1 allocs/op |
+| BenchmarkName-4 | 5000000 | 285 ns/op | 17 B/op | 1 allocs/op |
+| BenchmarkFirstName-4 | 20000000 | 102 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLastName-4 | 20000000 | 100 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkNamePrefix-4 | 20000000 | 98.0 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkNameSuffix-4 | 20000000 | 109 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkNumber-4 | 50000000 | 34.5 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkUint8-4 | 50000000 | 28.5 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkUint16-4 | 50000000 | 28.5 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkUint32-4 | 50000000 | 27.0 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkUint64-4 | 50000000 | 34.6 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkInt8-4 | 50000000 | 28.5 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkInt16-4 | 50000000 | 28.4 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkInt32-4 | 50000000 | 27.0 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkInt64-4 | 50000000 | 34.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkFloat32-4 | 50000000 | 27.7 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkFloat32Range-4 | 50000000 | 27.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkFloat64-4 | 50000000 | 25.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkFloat64Range-4 | 50000000 | 26.5 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkNumerify-4 | 5000000 | 354 ns/op | 16 B/op | 1 allocs/op |
+| BenchmarkShuffleInts-4 | 10000000 | 226 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkPassword-4 | 2000000 | 655 ns/op | 304 B/op | 6 allocs/op |
+| BenchmarkCreditCard-4 | 2000000 | 997 ns/op | 88 B/op | 4 allocs/op |
+| BenchmarkCreditCardType-4 | 20000000 | 92.7 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkCreditCardNumber-4 | 3000000 | 572 ns/op | 16 B/op | 1 allocs/op |
+| BenchmarkCreditCardNumberLuhn-4 | 300000 | 5815 ns/op | 159 B/op | 9 allocs/op |
+| BenchmarkCreditCardExp-4 | 10000000 | 129 ns/op | 5 B/op | 1 allocs/op |
+| BenchmarkCreditCardCvv-4 | 10000000 | 128 ns/op | 3 B/op | 1 allocs/op |
+| BenchmarkSSN-4 | 20000000 | 84.2 ns/op | 16 B/op | 1 allocs/op |
+| BenchmarkGender-4 | 50000000 | 38.0 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkPerson-4 | 300000 | 5563 ns/op | 805 B/op | 26 allocs/op |
+| BenchmarkSimpleStatusCode-4 | 20000000 | 72.9 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkStatusCode-4 | 20000000 | 75.8 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLetter-4 | 50000000 | 38.4 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkDigit-4 | 50000000 | 38.2 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkLexify-4 | 10000000 | 222 ns/op | 8 B/op | 1 allocs/op |
+| BenchmarkShuffleStrings-4 | 10000000 | 197 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkUUID-4 | 20000000 | 106 ns/op | 48 B/op | 1 allocs/op |
+| BenchmarkUserAgent-4 | 1000000 | 1236 ns/op | 305 B/op | 5 allocs/op |
+| BenchmarkChromeUserAgent-4 | 2000000 | 881 ns/op | 188 B/op | 5 allocs/op |
+| BenchmarkFirefoxUserAgent-4 | 1000000 | 1595 ns/op | 386 B/op | 7 allocs/op |
+| BenchmarkSafariUserAgent-4 | 1000000 | 1396 ns/op | 551 B/op | 7 allocs/op |
+| BenchmarkOperaUserAgent-4 | 2000000 | 950 ns/op | 216 B/op | 5 allocs/op |
+| BenchmarkWord-4 | 20000000 | 99.1 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkSentence-4 | 1000000 | 1540 ns/op | 277 B/op | 2 allocs/op |
+| BenchmarkParagraph-4 | 50000 | 30978 ns/op | 11006 B/op | 61 allocs/op |
\ No newline at end of file
diff --git a/vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md b/vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000000..99d12c90fecf
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at brian@webiswhatido.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md b/vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md
new file mode 100644
index 000000000000..5a4812c28ee8
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/CONTRIBUTING.md
@@ -0,0 +1 @@
+# Make a pull request and submit it and ill take a look at it. Thanks!
diff --git a/vendor/github.com/brianvoe/gofakeit/LICENSE.txt b/vendor/github.com/brianvoe/gofakeit/LICENSE.txt
new file mode 100644
index 000000000000..21984c9d5eaa
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) [year] [fullname]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/brianvoe/gofakeit/README.md b/vendor/github.com/brianvoe/gofakeit/README.md
new file mode 100644
index 000000000000..4e3723fd5117
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/README.md
@@ -0,0 +1,254 @@
+![alt text](https://raw.githubusercontent.com/brianvoe/gofakeit/master/logo.png)
+
+# gofakeit [![Go Report Card](https://goreportcard.com/badge/github.com/brianvoe/gofakeit)](https://goreportcard.com/report/github.com/brianvoe/gofakeit) [![Build Status](https://travis-ci.org/brianvoe/gofakeit.svg?branch=master)](https://travis-ci.org/brianvoe/gofakeit) [![codecov.io](https://codecov.io/github/brianvoe/gofakeit/branch/master/graph/badge.svg)](https://codecov.io/github/brianvoe/gofakeit) [![GoDoc](https://godoc.org/github.com/brianvoe/gofakeit?status.svg)](https://godoc.org/github.com/brianvoe/gofakeit) [![license](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/brianvoe/gofakeit/master/LICENSE.txt)
+Random data generator written in go
+
+
+
+### Features
+- Every function has an example and a benchmark,
+[see benchmarks](https://github.com/brianvoe/gofakeit/blob/master/BENCHMARKS.md)
+- Zero dependencies
+- Randomizes user defined structs
+- Numerous functions for regular use
+
+### 120+ Functions!!!
+If there is something that is generic enough missing from this package [add an issue](https://github.com/brianvoe/gofakeit/issues) and let me know what you need.
+Most of the time i'll add it!
+
+## Person
+```go
+Person() *PersonInfo
+Name() string
+NamePrefix() string
+NameSuffix() string
+FirstName() string
+LastName() string
+Gender() string
+SSN() string
+Contact() *ContactInfo
+Email() string
+Phone() string
+PhoneFormatted() string
+Username() string
+Password(lower bool, upper bool, numeric bool, special bool, space bool, num int) string
+```
+
+## Address
+```go
+Address() *AddressInfo
+City() string
+Country() string
+CountryAbr() string
+State() string
+StateAbr() string
+StatusCode() string
+Street() string
+StreetName() string
+StreetNumber() string
+StreetPrefix() string
+StreetSuffix() string
+Zip() string
+Latitude() float64
+LatitudeInRange() (float64, error)
+Longitude() float64
+LongitudeInRange() (float64, error)
+```
+
+## Beer
+```go
+BeerAlcohol() string
+BeerBlg() string
+BeerHop() string
+BeerIbu() string
+BeerMalt() string
+BeerName() string
+BeerStyle() string
+BeerYeast() string
+```
+
+## Cars
+```go
+Vehicle() *VehicleInfo
+CarMaker() string
+CarModel() string
+VehicleType() string
+FuelType() string
+TransmissionGearType() string
+```
+
+## Words
+```go
+Word() string
+Sentence(wordCount int) string
+Paragraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string
+Question() string
+Quote() string
+```
+
+## Misc
+```go
+Struct(v interface{})
+Generate() string
+Bool() bool
+UUID() string
+```
+
+## Colors
+```go
+Color() string
+HexColor() string
+RGBColor() string
+SafeColor() string
+```
+
+## Internet
+```go
+URL() string
+ImageURL(width int, height int) string
+DomainName() string
+DomainSuffix() string
+IPv4Address() string
+IPv6Address() string
+SimpleStatusCode() int
+LogLevel(logType string) string
+HTTPMethod() string
+UserAgent() string
+ChromeUserAgent() string
+FirefoxUserAgent() string
+OperaUserAgent() string
+SafariUserAgent() string
+```
+
+## Date/Time
+```go
+Date() time.Time
+DateRange(start, end time.Time) time.Time
+NanoSecond() int
+Second() int
+Minute() int
+Hour() int
+Month() string
+Day() int
+WeekDay() string
+Year() int
+TimeZone() string
+TimeZoneAbv() string
+TimeZoneFull() string
+TimeZoneOffset() float32
+```
+
+## Payment
+```go
+Price(min, max float64) float64
+CreditCard() *CreditCardInfo
+CreditCardCvv() string
+CreditCardExp() string
+CreditCardNumber() int
+CreditCardNumberLuhn() int
+CreditCardType() string
+Currency() *CurrencyInfo
+CurrencyLong() string
+CurrencyShort() string
+```
+
+## Company
+```go
+BS() string
+BuzzWord() string
+Company() string
+CompanySuffix() string
+Job() *JobInfo
+JobDescriptor() string
+JobLevel() string
+JobTitle() string
+```
+
+## Hacker
+```go
+HackerAbbreviation() string
+HackerAdjective() string
+HackerIngverb() string
+HackerNoun() string
+HackerPhrase() string
+HackerVerb() string
+```
+
+## Hipster
+```go
+HipsterWord() string
+HipsterSentence(wordCount int) string
+HipsterParagraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string
+```
+
+## File
+```go
+Extension() string
+MimeType() string
+```
+
+## Numbers
+```go
+Number(min int, max int) int
+Numerify(str string) string
+Int8() int8
+Int16() int16
+Int32() int32
+Int64() int64
+Uint8() uint8
+Uint16() uint16
+Uint32() uint32
+Uint64() uint64
+Float32() float32
+Float32Range(min, max float32) float32
+Float64() float64
+Float64Range(min, max float64) float64
+ShuffleInts(a []int)
+```
+
+## String
+```go
+Digit() string
+Letter() string
+Lexify(str string) string
+RandString(a []string) string
+ShuffleStrings(a []string)
+```
+
+## Documentation
+[![GoDoc](https://godoc.org/github.com/brianvoe/gofakeit?status.svg)](https://godoc.org/github.com/brianvoe/gofakeit)
+
+## Example
+```go
+import "github.com/brianvoe/gofakeit"
+
+gofakeit.Name() // Markus Moen
+gofakeit.Email() // alaynawuckert@kozey.biz
+gofakeit.Phone() // (570)245-7485
+gofakeit.BS() // front-end
+gofakeit.BeerName() // Duvel
+gofakeit.Color() // MediumOrchid
+gofakeit.Company() // Moen, Pagac and Wuckert
+gofakeit.CreditCardNumber() // 4287271570245748
+gofakeit.HackerPhrase() // Connecting the array won't do anything, we need to generate the haptic COM driver!
+gofakeit.JobTitle() // Director
+gofakeit.Password(true, true, true, true, true, 32) // WV10MzLxq2DX79w1omH97_0ga59j8!kj
+gofakeit.CurrencyShort() // USD
+// 120+ more!!!
+
+// Create structs with random injected data
+type Foo struct {
+ Bar string
+ Baz string
+ Int int
+ Pointer *int
+ Skip *string `fake:"skip"` // Set to "skip" to not generate data for
+}
+var f Foo
+gofakeit.Struct(&f)
+fmt.Printf("f.Bar:%s\n", f.Bar) // f.Bar:hrukpttuezptneuvunh
+fmt.Printf("f.Baz:%s\n", f.Baz) // f.Baz:uksqvgzadxlgghejkmv
+fmt.Printf("f.Int:%d\n", f.Int) // f.Int:-7825289004089916589
+fmt.Printf("f.Pointer:%d\n", *f.Pointer) // f.Pointer:-343806609094473732
+fmt.Printf("f.Skip:%v\n", f.Skip) // f.Skip:
+```
diff --git a/vendor/github.com/brianvoe/gofakeit/TODO.txt b/vendor/github.com/brianvoe/gofakeit/TODO.txt
new file mode 100644
index 000000000000..7a492842136b
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/TODO.txt
@@ -0,0 +1,3 @@
+* Take a look at [chance.js](http://chancejs.com/) and see if i missed anything.
+* Look into [National Baby Name List](http://www.ssa.gov/oact/babynames/limits.html) and see if that makes sense to replace over what we currently have.
+* Look at [data list](https://github.com/dariusk/corpora/tree/master/data) and see if it makes sense to add that data in or if it seems unncessary.
diff --git a/vendor/github.com/brianvoe/gofakeit/address.go b/vendor/github.com/brianvoe/gofakeit/address.go
new file mode 100644
index 000000000000..82fc6b00e191
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/address.go
@@ -0,0 +1,131 @@
+package gofakeit
+
+import (
+ "errors"
+ "math/rand"
+ "strings"
+)
+
+// AddressInfo is a struct full of address information
+type AddressInfo struct {
+ Address string
+ Street string
+ City string
+ State string
+ Zip string
+ Country string
+ Latitude float64
+ Longitude float64
+}
+
+// Address will generate a struct of address information
+func Address() *AddressInfo {
+ street := Street()
+ city := City()
+ state := State()
+ zip := Zip()
+
+ return &AddressInfo{
+ Address: street + ", " + city + ", " + state + " " + zip,
+ Street: street,
+ City: city,
+ State: state,
+ Zip: zip,
+ Country: Country(),
+ Latitude: Latitude(),
+ Longitude: Longitude(),
+ }
+}
+
+// Street will generate a random address street string
+func Street() (street string) {
+ switch randInt := randIntRange(1, 2); randInt {
+ case 1:
+ street = StreetNumber() + " " + StreetPrefix() + " " + StreetName() + StreetSuffix()
+ case 2:
+ street = StreetNumber() + " " + StreetName() + StreetSuffix()
+ }
+
+ return
+}
+
+// StreetNumber will generate a random address street number string
+func StreetNumber() string {
+ return strings.TrimLeft(replaceWithNumbers(getRandValue([]string{"address", "number"})), "0")
+}
+
+// StreetPrefix will generate a random address street prefix string
+func StreetPrefix() string {
+ return getRandValue([]string{"address", "street_prefix"})
+}
+
+// StreetName will generate a random address street name string
+func StreetName() string {
+ return getRandValue([]string{"address", "street_name"})
+}
+
+// StreetSuffix will generate a random address street suffix string
+func StreetSuffix() string {
+ return getRandValue([]string{"address", "street_suffix"})
+}
+
+// City will generate a random city string
+func City() (city string) {
+ switch randInt := randIntRange(1, 3); randInt {
+ case 1:
+ city = FirstName() + StreetSuffix()
+ case 2:
+ city = LastName() + StreetSuffix()
+ case 3:
+ city = StreetPrefix() + " " + LastName()
+ }
+
+ return
+}
+
+// State will generate a random state string
+func State() string {
+ return getRandValue([]string{"address", "state"})
+}
+
+// StateAbr will generate a random abbreviated state string
+func StateAbr() string {
+ return getRandValue([]string{"address", "state_abr"})
+}
+
+// Zip will generate a random Zip code string
+func Zip() string {
+ return replaceWithNumbers(getRandValue([]string{"address", "zip"}))
+}
+
+// Country will generate a random country string
+func Country() string {
+ return getRandValue([]string{"address", "country"})
+}
+
+// CountryAbr will generate a random abbreviated country string
+func CountryAbr() string {
+ return getRandValue([]string{"address", "country_abr"})
+}
+
+// Latitude will generate a random latitude float64
+func Latitude() float64 { return (rand.Float64() * 180) - 90 }
+
+// LatitudeInRange will generate a random latitude within the input range
+func LatitudeInRange(min, max float64) (float64, error) {
+ if min > max || min < -90 || min > 90 || max < -90 || max > 90 {
+ return 0, errors.New("input range is invalid")
+ }
+ return randFloat64Range(min, max), nil
+}
+
+// Longitude will generate a random longitude float64
+func Longitude() float64 { return (rand.Float64() * 360) - 180 }
+
+// LongitudeInRange will generate a random longitude within the input range
+func LongitudeInRange(min, max float64) (float64, error) {
+ if min > max || min < -180 || min > 180 || max < -180 || max > 180 {
+ return 0, errors.New("input range is invalid")
+ }
+ return randFloat64Range(min, max), nil
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/beer.go b/vendor/github.com/brianvoe/gofakeit/beer.go
new file mode 100644
index 000000000000..53297d537809
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/beer.go
@@ -0,0 +1,45 @@
+package gofakeit
+
+import "strconv"
+
+// Faker::Beer.blg #=> "18.5°Blg"
+
+// BeerName will return a random beer name
+func BeerName() string {
+ return getRandValue([]string{"beer", "name"})
+}
+
+// BeerStyle will return a random beer style
+func BeerStyle() string {
+ return getRandValue([]string{"beer", "style"})
+}
+
+// BeerHop will return a random beer hop
+func BeerHop() string {
+ return getRandValue([]string{"beer", "hop"})
+}
+
+// BeerYeast will return a random beer yeast
+func BeerYeast() string {
+ return getRandValue([]string{"beer", "yeast"})
+}
+
+// BeerMalt will return a random beer malt
+func BeerMalt() string {
+ return getRandValue([]string{"beer", "malt"})
+}
+
+// BeerIbu will return a random beer ibu value between 10 and 100
+func BeerIbu() string {
+ return strconv.Itoa(randIntRange(10, 100)) + " IBU"
+}
+
+// BeerAlcohol will return a random beer alcohol level between 2.0 and 10.0
+func BeerAlcohol() string {
+ return strconv.FormatFloat(randFloat64Range(2.0, 10.0), 'f', 1, 64) + "%"
+}
+
+// BeerBlg will return a random beer blg between 5.0 and 20.0
+func BeerBlg() string {
+ return strconv.FormatFloat(randFloat64Range(5.0, 20.0), 'f', 1, 64) + "°Blg"
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/bool.go b/vendor/github.com/brianvoe/gofakeit/bool.go
new file mode 100644
index 000000000000..f63eeedd3241
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/bool.go
@@ -0,0 +1,10 @@
+package gofakeit
+
+// Bool will generate a random boolean value
+func Bool() bool {
+ if randIntRange(0, 1) == 1 {
+ return true
+ }
+
+ return false
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/color.go b/vendor/github.com/brianvoe/gofakeit/color.go
new file mode 100644
index 000000000000..63a737e99a62
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/color.go
@@ -0,0 +1,44 @@
+package gofakeit
+
+import "math/rand"
+
+// Color will generate a random color string
+func Color() string {
+ return getRandValue([]string{"color", "full"})
+}
+
+// SafeColor will generate a random safe color string
+func SafeColor() string {
+ return getRandValue([]string{"color", "safe"})
+}
+
+// HexColor will generate a random hexadecimal color string
+func HexColor() string {
+ color := make([]byte, 6)
+ hashQuestion := []byte("?#")
+ for i := 0; i < 6; i++ {
+ color[i] = hashQuestion[rand.Intn(2)]
+ }
+
+ return "#" + replaceWithLetters(replaceWithNumbers(string(color)))
+
+ // color := ""
+ // for i := 1; i <= 6; i++ {
+ // color += RandString([]string{"?", "#"})
+ // }
+
+ // // Replace # with number
+ // color = replaceWithNumbers(color)
+
+ // // Replace ? with letter
+ // for strings.Count(color, "?") > 0 {
+ // color = strings.Replace(color, "?", RandString(letters), 1)
+ // }
+
+ // return "#" + color
+}
+
+// RGBColor will generate a random int slice color
+func RGBColor() []int {
+ return []int{randIntRange(0, 255), randIntRange(0, 255), randIntRange(0, 255)}
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/company.go b/vendor/github.com/brianvoe/gofakeit/company.go
new file mode 100644
index 000000000000..abdb2aa698f1
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/company.go
@@ -0,0 +1,30 @@
+package gofakeit
+
+// Company will generate a random company name string
+func Company() (company string) {
+ switch randInt := randIntRange(1, 3); randInt {
+ case 1:
+ company = LastName() + ", " + LastName() + " and " + LastName()
+ case 2:
+ company = LastName() + "-" + LastName()
+ case 3:
+ company = LastName() + " " + CompanySuffix()
+ }
+
+ return
+}
+
+// CompanySuffix will generate a random company suffix string
+func CompanySuffix() string {
+ return getRandValue([]string{"company", "suffix"})
+}
+
+// BuzzWord will generate a random company buzz word string
+func BuzzWord() string {
+ return getRandValue([]string{"company", "buzzwords"})
+}
+
+// BS will generate a random company bs string
+func BS() string {
+ return getRandValue([]string{"company", "bs"})
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/contact.go b/vendor/github.com/brianvoe/gofakeit/contact.go
new file mode 100644
index 000000000000..1eb0ae05303d
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/contact.go
@@ -0,0 +1,40 @@
+package gofakeit
+
+import (
+ "strings"
+)
+
+// ContactInfo struct full of contact info
+type ContactInfo struct {
+ Phone string
+ Email string
+}
+
+// Contact will generate a struct with information randomly populated contact information
+func Contact() *ContactInfo {
+ return &ContactInfo{
+ Phone: Phone(),
+ Email: Email(),
+ }
+}
+
+// Phone will generate a random phone number string
+func Phone() string {
+ return replaceWithNumbers("##########")
+}
+
+// PhoneFormatted will generate a random phone number string
+func PhoneFormatted() string {
+ return replaceWithNumbers(getRandValue([]string{"contact", "phone"}))
+}
+
+// Email will generate a random email string
+func Email() string {
+ var email string
+
+ email = getRandValue([]string{"person", "first"}) + getRandValue([]string{"person", "last"})
+ email += "@"
+ email += getRandValue([]string{"person", "last"}) + "." + getRandValue([]string{"internet", "domain_suffix"})
+
+ return strings.ToLower(email)
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/currency.go b/vendor/github.com/brianvoe/gofakeit/currency.go
new file mode 100644
index 000000000000..c25e4d62a7aa
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/currency.go
@@ -0,0 +1,38 @@
+package gofakeit
+
+import (
+ "math"
+ "math/rand"
+
+ "github.com/brianvoe/gofakeit/data"
+)
+
+// CurrencyInfo is a struct of currency information
+type CurrencyInfo struct {
+ Short string
+ Long string
+}
+
+// Currency will generate a struct with random currency information
+func Currency() *CurrencyInfo {
+ index := rand.Intn(len(data.Data["currency"]["short"]))
+ return &CurrencyInfo{
+ Short: data.Data["currency"]["short"][index],
+ Long: data.Data["currency"]["long"][index],
+ }
+}
+
+// CurrencyShort will generate a random short currency value
+func CurrencyShort() string {
+ return getRandValue([]string{"currency", "short"})
+}
+
+// CurrencyLong will generate a random long currency name
+func CurrencyLong() string {
+ return getRandValue([]string{"currency", "long"})
+}
+
+// Price will take in a min and max value and return a formatted price
+func Price(min, max float64) float64 {
+ return math.Floor(randFloat64Range(min, max)*100) / 100
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/address.go b/vendor/github.com/brianvoe/gofakeit/data/address.go
new file mode 100644
index 000000000000..671cdda91375
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/address.go
@@ -0,0 +1,15 @@
+package data
+
+// Address consists of address information
+var Address = map[string][]string{
+ "number": {"#####", "####", "###"},
+ "street_prefix": {"North", "East", "West", "South", "New", "Lake", "Port"},
+ "street_name": {"Alley", "Avenue", "Branch", "Bridge", "Brook", "Brooks", "Burg", "Burgs", "Bypass", "Camp", "Canyon", "Cape", "Causeway", "Center", "Centers", "Circle", "Circles", "Cliff", "Cliffs", "Club", "Common", "Corner", "Corners", "Course", "Court", "Courts", "Cove", "Coves", "Creek", "Crescent", "Crest", "Crossing", "Crossroad", "Curve", "Dale", "Dam", "Divide", "Drive", "Drive", "Drives", "Estate", "Estates", "Expressway", "Extension", "Extensions", "Fall", "Falls", "Ferry", "Field", "Fields", "Flat", "Flats", "Ford", "Fords", "Forest", "Forge", "Forges", "Fork", "Forks", "Fort", "Freeway", "Garden", "Gardens", "Gateway", "Glen", "Glens", "Green", "Greens", "Grove", "Groves", "Harbor", "Harbors", "Haven", "Heights", "Highway", "Hill", "Hills", "Hollow", "Inlet", "Inlet", "Island", "Island", "Islands", "Islands", "Isle", "Isle", "Junction", "Junctions", "Key", "Keys", "Knoll", "Knolls", "Lake", "Lakes", "Land", "Landing", "Lane", "Light", "Lights", "Loaf", "Lock", "Locks", "Locks", "Lodge", "Lodge", "Loop", "Mall", "Manor", "Manors", "Meadow", "Meadows", "Mews", "Mill", "Mills", "Mission", "Mission", "Motorway", "Mount", "Mountain", "Mountain", "Mountains", "Mountains", "Neck", "Orchard", "Oval", "Overpass", "Park", "Parks", "Parkway", "Parkways", "Pass", "Passage", "Path", "Pike", "Pine", "Pines", "Place", "Plain", "Plains", "Plains", "Plaza", "Plaza", "Point", "Points", "Port", "Port", "Ports", "Ports", "Prairie", "Prairie", "Radial", "Ramp", "Ranch", "Rapid", "Rapids", "Rest", "Ridge", "Ridges", "River", "Road", "Road", "Roads", "Roads", "Route", "Row", "Rue", "Run", "Shoal", "Shoals", "Shore", "Shores", "Skyway", "Spring", "Springs", "Springs", "Spur", "Spurs", "Square", "Square", "Squares", "Squares", "Station", "Station", "Stravenue", "Stravenue", "Stream", "Stream", "Street", "Street", "Streets", "Summit", "Summit", "Terrace", "Throughway", "Trace", "Track", "Trafficway", "Trail", "Trail", "Tunnel", "Tunnel", "Turnpike", "Turnpike", "Underpass", "Union", "Unions", "Valley", "Valleys", "Via", "Viaduct", "View", "Views", "Village", "Village", "Villages", "Ville", "Vista", "Vista", "Walk", "Walks", "Wall", "Way", "Ways", "Well", "Wells"},
+ "street_suffix": {"town", "ton", "land", "ville", "berg", "burgh", "borough", "bury", "view", "port", "mouth", "stad", "furt", "chester", "mouth", "fort", "haven", "side", "shire"},
+ "city": {"{address.street_prefix} {name.first}{address.street_suffix}", "{address.street_prefix} {name.first}", "{name.first}{address.street_suffix}", "{name.last}{address.street_suffix}"},
+ "state": {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"},
+ "state_abr": {"AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY", "AE", "AA", "AP"},
+ "zip": {"#####"},
+ "country": {"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "British Virgin Islands", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", "Congo", "Cook Islands", "Costa Rica", "Cote Divoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Faroe Islands", "Falkland Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard Island and McDonald Islands", "Holy See (Vatican City State)", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea", "Korea", "Kuwait", "Kyrgyz Republic", "Lao Peoples Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands Antilles", "Netherlands", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Palestinian Territory", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", "Saint Martin", "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia (Slovak Republic)", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard & Jan Mayen Islands", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States of America", "United States Minor Outlying Islands", "United States Virgin Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"},
+ "country_abr": {"AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CK", "CR", "CI", "HR", "CU", "CY", "CZ", "DK", "DJ", "DM", "DO", "TL", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "FX", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GW", "GY", "HT", "HM", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IL", "IT", "JM", "JP", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "NO", "OM", "PK", "PW", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "KN", "LC", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "SI", "SB", "SO", "ZA", "ES", "LK", "SH", "PM", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VA", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "YU", "ZR", "ZM", "ZW"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/beer.go b/vendor/github.com/brianvoe/gofakeit/data/beer.go
new file mode 100644
index 000000000000..1192907d5f29
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/beer.go
@@ -0,0 +1,10 @@
+package data
+
+// Beer consists of various beer information
+var Beer = map[string][]string{
+ "name": {"Pliny The Elder", "Founders Kentucky Breakfast", "Trappistes Rochefort 10", "HopSlam Ale", "Stone Imperial Russian Stout", "St. Bernardus Abt 12", "Founders Breakfast Stout", "Weihenstephaner Hefeweissbier", "Péché Mortel", "Celebrator Doppelbock", "Duvel", "Dreadnaught IPA", "Nugget Nectar", "La Fin Du Monde", "Bourbon County Stout", "Old Rasputin Russian Imperial Stout", "Two Hearted Ale", "Ruination IPA", "Schneider Aventinus", "Double Bastard Ale", "90 Minute IPA", "Hop Rod Rye", "Trappistes Rochefort 8", "Chimay Grande Réserve", "Stone IPA", "Arrogant Bastard Ale", "Edmund Fitzgerald Porter", "Chocolate St", "Oak Aged Yeti Imperial Stout", "Ten FIDY", "Storm King Stout", "Shakespeare Oatmeal", "Alpha King Pale Ale", "Westmalle Trappist Tripel", "Samuel Smith’s Imperial IPA", "Yeti Imperial Stout", "Hennepin", "Samuel Smith’s Oatmeal Stout", "Brooklyn Black", "Oaked Arrogant Bastard Ale", "Sublimely Self-Righteous Ale", "Trois Pistoles", "Bell’s Expedition", "Sierra Nevada Celebration Ale", "Sierra Nevada Bigfoot Barleywine Style Ale", "Racer 5 India Pale Ale, Bear Republic Bre", "Orval Trappist Ale", "Hercules Double IPA", "Maharaj", "Maudite"},
+ "hop": {"Ahtanum", "Amarillo", "Bitter Gold", "Bravo", "Brewer’s Gold", "Bullion", "Cascade", "Cashmere", "Centennial", "Chelan", "Chinook", "Citra", "Cluster", "Columbia", "Columbus", "Comet", "Crystal", "Equinox", "Eroica", "Fuggle", "Galena", "Glacier", "Golding", "Hallertau", "Horizon", "Liberty", "Magnum", "Millennium", "Mosaic", "Mt. Hood", "Mt. Rainier", "Newport", "Northern Brewer", "Nugget", "Olympic", "Palisade", "Perle", "Saaz", "Santiam", "Simcoe", "Sorachi Ace", "Sterling", "Summit", "Tahoma", "Tettnang", "TriplePearl", "Ultra", "Vanguard", "Warrior", "Willamette", "Yakima Gol"},
+ "yeast": {"1007 - German Ale", "1010 - American Wheat", "1028 - London Ale", "1056 - American Ale", "1084 - Irish Ale", "1098 - British Ale", "1099 - Whitbread Ale", "1187 - Ringwood Ale", "1272 - American Ale II", "1275 - Thames Valley Ale", "1318 - London Ale III", "1332 - Northwest Ale", "1335 - British Ale II", "1450 - Dennys Favorite 50", "1469 - West Yorkshire Ale", "1728 - Scottish Ale", "1968 - London ESB Ale", "2565 - Kölsch", "1214 - Belgian Abbey", "1388 - Belgian Strong Ale", "1762 - Belgian Abbey II", "3056 - Bavarian Wheat Blend", "3068 - Weihenstephan Weizen", "3278 - Belgian Lambic Blend", "3333 - German Wheat", "3463 - Forbidden Fruit", "3522 - Belgian Ardennes", "3638 - Bavarian Wheat", "3711 - French Saison", "3724 - Belgian Saison", "3763 - Roeselare Ale Blend", "3787 - Trappist High Gravity", "3942 - Belgian Wheat", "3944 - Belgian Witbier", "2000 - Budvar Lager", "2001 - Urquell Lager", "2007 - Pilsen Lager", "2035 - American Lager", "2042 - Danish Lager", "2112 - California Lager", "2124 - Bohemian Lager", "2206 - Bavarian Lager", "2278 - Czech Pils", "2308 - Munich Lager", "2633 - Octoberfest Lager Blend", "5112 - Brettanomyces bruxellensis", "5335 - Lactobacillus", "5526 - Brettanomyces lambicus", "5733 - Pediococcus"},
+ "malt": {"Black malt", "Caramel", "Carapils", "Chocolate", "Munich", "Caramel", "Carapils", "Chocolate malt", "Munich", "Pale", "Roasted barley", "Rye malt", "Special roast", "Victory", "Vienna", "Wheat mal"},
+ "style": {"Light Lager", "Pilsner", "European Amber Lager", "Dark Lager", "Bock", "Light Hybrid Beer", "Amber Hybrid Beer", "English Pale Ale", "Scottish And Irish Ale", "Merican Ale", "English Brown Ale", "Porter", "Stout", "India Pale Ale", "German Wheat And Rye Beer", "Belgian And French Ale", "Sour Ale", "Belgian Strong Ale", "Strong Ale", "Fruit Beer", "Vegetable Beer", "Smoke-flavored", "Wood-aged Beer"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/colors.go b/vendor/github.com/brianvoe/gofakeit/data/colors.go
new file mode 100644
index 000000000000..3aca817d69f3
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/colors.go
@@ -0,0 +1,7 @@
+package data
+
+// Colors consists of color information
+var Colors = map[string][]string{
+ "safe": {"black", "maroon", "green", "navy", "olive", "purple", "teal", "lime", "blue", "silver", "gray", "yellow", "fuchsia", "aqua", "white"},
+ "full": {"AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "Darkorange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed ", "Indigo ", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGray", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/company.go b/vendor/github.com/brianvoe/gofakeit/data/company.go
new file mode 100644
index 000000000000..b2a3790c7c68
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/company.go
@@ -0,0 +1,9 @@
+package data
+
+// Company consists of company information
+var Company = map[string][]string{
+ "name": {"{person.last} {company.suffix}", "{person.last}-{person.last}", "{person.last}, {person.last} and {person.last}"},
+ "suffix": {"Inc", "and Sons", "LLC", "Group"},
+ "buzzwords": {"Adaptive", "Advanced", "Ameliorated", "Assimilated", "Automated", "Balanced", "Business-focused", "Centralized", "Cloned", "Compatible", "Configurable", "Cross-group", "Cross-platform", "Customer-focused", "Customizable", "De-engineered", "Decentralized", "Devolved", "Digitized", "Distributed", "Diverse", "Down-sized", "Enhanced", "Enterprise-wide", "Ergonomic", "Exclusive", "Expanded", "Extended", "Face to face", "Focused", "Front-line", "Fully-configurable", "Function-based", "Fundamental", "Future-proofed", "Grass-roots", "Horizontal", "Implemented", "Innovative", "Integrated", "Intuitive", "Inverse", "Managed", "Mandatory", "Monitored", "Multi-channelled", "Multi-lateral", "Multi-layered", "Multi-tiered", "Networked", "Object-based", "Open-architected", "Open-source", "Operative", "Optimized", "Optional", "Organic", "Organized", "Persevering", "Persistent", "Phased", "Polarised", "Pre-emptive", "Proactive", "Profit-focused", "Profound", "Programmable", "Progressive", "Public-key", "Quality-focused", "Re-contextualized", "Re-engineered", "Reactive", "Realigned", "Reduced", "Reverse-engineered", "Right-sized", "Robust", "Seamless", "Secured", "Self-enabling", "Sharable", "Stand-alone", "Streamlined", "Switchable", "Synchronised", "Synergistic", "Synergized", "Team-oriented", "Total", "Triple-buffered", "Universal", "Up-sized", "Upgradable", "User-centric", "User-friendly", "Versatile", "Virtual", "Vision-oriented", "Visionary", "24 hour", "24/7", "3rd generation", "4th generation", "5th generation", "6th generation", "actuating", "analyzing", "asymmetric", "asynchronous", "attitude-oriented", "background", "bandwidth-monitored", "bi-directional", "bifurcated", "bottom-line", "clear-thinking", "client-driven", "client-server", "coherent", "cohesive", "composite", "content-based", "context-sensitive", "contextually-based", "dedicated", "demand-driven", "didactic", "directional", "discrete", "disintermediate", "dynamic", "eco-centric", "empowering", "encompassing", "even-keeled", "executive", "explicit", "exuding", "fault-tolerant", "foreground", "fresh-thinking", "full-range", "global", "grid-enabled", "heuristic", "high-level", "holistic", "homogeneous", "human-resource", "hybrid", "impactful", "incremental", "intangible", "interactive", "intermediate", "leading edge", "local", "logistical", "maximized", "methodical", "mission-critical", "mobile", "modular", "motivating", "multi-state", "multi-tasking", "multimedia", "national", "needs-based", "neutral", "next generation", "non-volatile", "object-oriented", "optimal", "optimizing", "radical", "real-time", "reciprocal", "regional", "responsive", "scalable", "secondary", "solution-oriented", "stable", "static", "system-worthy", "systematic", "systemic", "tangible", "tertiary", "transitional", "uniform", "upward-trending", "user-facing", "value-added", "web-enabled", "well-modulated", "zero administration", "zero defect", "zero tolerance", "Graphic Interface", "Graphical User Interface", "ability", "access", "adapter", "algorithm", "alliance", "analyzer", "application", "approach", "architecture", "archive", "array", "artificial intelligence", "attitude", "benchmark", "budgetary management", "capability", "capacity", "challenge", "circuit", "collaboration", "complexity", "concept", "conglomeration", "contingency", "core", "customer loyalty", "data-warehouse", "database", "definition", "emulation", "encoding", "encryption", "extranet", "firmware", "flexibility", "focus group", "forecast", "frame", "framework", "function", "functionalities", "groupware", "hardware", "help-desk", "hierarchy", "hub", "implementation", "info-mediaries", "infrastructure", "initiative", "installation", "instruction set", "interface", "internet solution", "intranet", "knowledge base", "knowledge user", "leverage", "local area network", "matrices", "matrix", "methodology", "middleware", "migration", "model", "moderator", "monitoring", "moratorium", "neural-net", "open architecture", "open system", "orchestration", "paradigm", "parallelism", "policy", "portal", "pricing structure", "process improvement", "product", "productivity", "project", "projection", "protocol", "secured line", "service-desk", "software", "solution", "standardization", "strategy", "structure", "success", "superstructure", "support", "synergy", "system engine", "task-force", "throughput", "time-frame", "toolset", "utilisation", "website", "workforce"},
+ "bs": {"aggregate", "architect", "benchmark", "brand", "cultivate", "deliver", "deploy", "disintermediate", "drive", "e-enable", "embrace", "empower", "enable", "engage", "engineer", "enhance", "envisioneer", "evolve", "expedite", "exploit", "extend", "facilitate", "generate", "grow", "harness", "implement", "incentivize", "incubate", "innovate", "integrate", "iterate", "leverage", "matrix", "maximize", "mesh", "monetize", "morph", "optimize", "orchestrate", "productize", "recontextualize", "redefine", "reintermediate", "reinvent", "repurpose", "revolutionize", "scale", "seize", "strategize", "streamline", "syndicate", "synergize", "synthesize", "target", "transform", "transition", "unleash", "utilize", "visualize", "whiteboard", "24/365", "24/7", "B2B", "B2C", "back-end", "best-of-breed", "bleeding-edge", "bricks-and-clicks", "clicks-and-mortar", "collaborative", "compelling", "cross-media", "cross-platform", "customized", "cutting-edge", "distributed", "dot-com", "dynamic", "e-business", "efficient", "end-to-end", "enterprise", "extensible", "frictionless", "front-end", "global", "granular", "holistic", "impactful", "innovative", "integrated", "interactive", "intuitive", "killer", "leading-edge", "magnetic", "mission-critical", "next-generation", "one-to-one", "open-source", "out-of-the-box", "plug-and-play", "proactive", "real-time", "revolutionary", "rich", "robust", "scalable", "seamless", "sexy", "sticky", "strategic", "synergistic", "transparent", "turn-key", "ubiquitous", "user-centric", "value-added", "vertical", "viral", "virtual", "visionary", "web-enabled", "wireless", "world-class", "ROI", "action-items", "applications", "architectures", "bandwidth", "channels", "communities", "content", "convergence", "deliverables", "e-business", "e-commerce", "e-markets", "e-services", "e-tailers", "experiences", "eyeballs", "functionalities", "infomediaries", "infrastructures", "initiatives", "interfaces", "markets", "methodologies", "metrics", "mindshare", "models", "networks", "niches", "paradigms", "partnerships", "platforms", "portals", "relationships", "schemas", "solutions", "supply-chains", "synergies", "systems", "technologies", "users", "vortals", "web services", "web-readiness"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/computer.go b/vendor/github.com/brianvoe/gofakeit/data/computer.go
new file mode 100644
index 000000000000..b682c6f820cc
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/computer.go
@@ -0,0 +1,8 @@
+package data
+
+// Computer consists of computer information
+var Computer = map[string][]string{
+ "linux_processor": {"i686", "x86_64"},
+ "mac_processor": {"Intel", "PPC", "U; Intel", "U; PPC"},
+ "windows_platform": {"Windows NT 6.2", "Windows NT 6.1", "Windows NT 6.0", "Windows NT 5.2", "Windows NT 5.1", "Windows NT 5.01", "Windows NT 5.0", "Windows NT 4.0", "Windows 98; Win 9x 4.90", "Windows 98", "Windows 95", "Windows CE"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/contact.go b/vendor/github.com/brianvoe/gofakeit/data/contact.go
new file mode 100644
index 000000000000..88b957961dbb
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/contact.go
@@ -0,0 +1,6 @@
+package data
+
+// Contact consists of contact information
+var Contact = map[string][]string{
+ "phone": {"###-###-####", "(###)###-####", "1-###-###-####", "###.###.####"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/currency.go b/vendor/github.com/brianvoe/gofakeit/data/currency.go
new file mode 100644
index 000000000000..13b8019973ca
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/currency.go
@@ -0,0 +1,7 @@
+package data
+
+// Currency consists of currency information
+var Currency = map[string][]string{
+ "short": {"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTN", "BWP", "BYR", "BZD", "CAD", "CDF", "CHF", "CLP", "CNY", "COP", "CRC", "CUC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GGP", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SPL", "SRD", "STD", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TVD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VND", "VUV", "WST", "XAF", "XCD", "XDR", "XOF", "XPF", "YER", "ZAR", "ZMW", "ZWD"},
+ "long": {"United Arab Emirates Dirham", "Afghanistan Afghani", "Albania Lek", "Armenia Dram", "Netherlands Antilles Guilder", "Angola Kwanza", "Argentina Peso", "Australia Dollar", "Aruba Guilder", "Azerbaijan New Manat", "Bosnia and Herzegovina Convertible Marka", "Barbados Dollar", "Bangladesh Taka", "Bulgaria Lev", "Bahrain Dinar", "Burundi Franc", "Bermuda Dollar", "Brunei Darussalam Dollar", "Bolivia Boliviano", "Brazil Real", "Bahamas Dollar", "Bhutan Ngultrum", "Botswana Pula", "Belarus Ruble", "Belize Dollar", "Canada Dollar", "Congo/Kinshasa Franc", "Switzerland Franc", "Chile Peso", "China Yuan Renminbi", "Colombia Peso", "Costa Rica Colon", "Cuba Convertible Peso", "Cuba Peso", "Cape Verde Escudo", "Czech Republic Koruna", "Djibouti Franc", "Denmark Krone", "Dominican Republic Peso", "Algeria Dinar", "Egypt Pound", "Eritrea Nakfa", "Ethiopia Birr", "Euro Member Countries", "Fiji Dollar", "Falkland Islands (Malvinas) Pound", "United Kingdom Pound", "Georgia Lari", "Guernsey Pound", "Ghana Cedi", "Gibraltar Pound", "Gambia Dalasi", "Guinea Franc", "Guatemala Quetzal", "Guyana Dollar", "Hong Kong Dollar", "Honduras Lempira", "Croatia Kuna", "Haiti Gourde", "Hungary Forint", "Indonesia Rupiah", "Israel Shekel", "Isle of Man Pound", "India Rupee", "Iraq Dinar", "Iran Rial", "Iceland Krona", "Jersey Pound", "Jamaica Dollar", "Jordan Dinar", "Japan Yen", "Kenya Shilling", "Kyrgyzstan Som", "Cambodia Riel", "Comoros Franc", "Korea (North) Won", "Korea (South) Won", "Kuwait Dinar", "Cayman Islands Dollar", "Kazakhstan Tenge", "Laos Kip", "Lebanon Pound", "Sri Lanka Rupee", "Liberia Dollar", "Lesotho Loti", "Lithuania Litas", "Libya Dinar", "Morocco Dirham", "Moldova Leu", "Madagascar Ariary", "Macedonia Denar", "Myanmar (Burma) Kyat", "Mongolia Tughrik", "Macau Pataca", "Mauritania Ouguiya", "Mauritius Rupee", "Maldives (Maldive Islands) Rufiyaa", "Malawi Kwacha", "Mexico Peso", "Malaysia Ringgit", "Mozambique Metical", "Namibia Dollar", "Nigeria Naira", "Nicaragua Cordoba", "Norway Krone", "Nepal Rupee", "New Zealand Dollar", "Oman Rial", "Panama Balboa", "Peru Nuevo Sol", "Papua New Guinea Kina", "Philippines Peso", "Pakistan Rupee", "Poland Zloty", "Paraguay Guarani", "Qatar Riyal", "Romania New Leu", "Serbia Dinar", "Russia Ruble", "Rwanda Franc", "Saudi Arabia Riyal", "Solomon Islands Dollar", "Seychelles Rupee", "Sudan Pound", "Sweden Krona", "Singapore Dollar", "Saint Helena Pound", "Sierra Leone Leone", "Somalia Shilling", "Seborga Luigino", "Suriname Dollar", "São Tomé and PrÃncipe Dobra", "El Salvador Colon", "Syria Pound", "Swaziland Lilangeni", "Thailand Baht", "Tajikistan Somoni", "Turkmenistan Manat", "Tunisia Dinar", "Tonga Pa'anga", "Turkey Lira", "Trinidad and Tobago Dollar", "Tuvalu Dollar", "Taiwan New Dollar", "Tanzania Shilling", "Ukraine Hryvnia", "Uganda Shilling", "United States Dollar", "Uruguay Peso", "Uzbekistan Som", "Venezuela Bolivar", "Viet Nam Dong", "Vanuatu Vatu", "Samoa Tala", "Communauté Financière Africaine (BEAC) CFA Franc BEAC", "East Caribbean Dollar", "International Monetary Fund (IMF) Special Drawing Rights", "Communauté Financière Africaine (BCEAO) Franc", "Comptoirs Français du Pacifique (CFP) Franc", "Yemen Rial", "South Africa Rand", "Zambia Kwacha", "Zimbabwe Dollar"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/data.go b/vendor/github.com/brianvoe/gofakeit/data/data.go
new file mode 100644
index 000000000000..d751c9994356
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/data.go
@@ -0,0 +1,28 @@
+package data
+
+// Data consists of the main set of fake information
+var Data = map[string]map[string][]string{
+ "person": Person,
+ "contact": Contact,
+ "address": Address,
+ "company": Company,
+ "job": Job,
+ "lorem": Lorem,
+ "internet": Internet,
+ "file": Files,
+ "color": Colors,
+ "computer": Computer,
+ "payment": Payment,
+ "hipster": Hipster,
+ "beer": Beer,
+ "hacker": Hacker,
+ "currency": Currency,
+ "log_level": LogLevels,
+ "timezone": TimeZone,
+ "vehicle": Vehicle,
+}
+
+// IntData consists of the main set of fake information (integer only)
+var IntData = map[string]map[string][]int{
+ "status_code": StatusCodes,
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/datetime.go b/vendor/github.com/brianvoe/gofakeit/data/datetime.go
new file mode 100644
index 000000000000..3347120a67e2
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/datetime.go
@@ -0,0 +1,9 @@
+package data
+
+// TimeZone is an array of short and long timezones
+var TimeZone = map[string][]string{
+ "offset": {"-12", "-11", "-10", "-8", "-7", "-7", "-8", "-7", "-6", "-6", "-6", "-5", "-5", "-6", "-5", "-4", "-4", "-4.5", "-4", "-3", "-4", "-4", "-4", "-2.5", "-3", "-3", "-3", "-3", "-3", "-3", "-2", "-1", "0", "-1", "1", "0", "0", "1", "1", "0", "2", "2", "2", "2", "1", "1", "3", "3", "2", "3", "3", "2", "3", "3", "3", "2", "3", "3", "3", "3", "3", "3", "4", "4.5", "4", "5", "4", "4", "4", "4.5", "5", "5", "5", "5.5", "5.5", "5.75", "6", "6", "6.5", "7", "7", "8", "8", "8", "8", "8", "8", "9", "9", "9", "9.5", "9.5", "10", "10", "10", "10", "10", "11", "11", "12", "12", "12", "12", "13", "13", "13"},
+ "abr": {"DST", "U", "HST", "AKDT", "PDT", "PDT", "PST", "UMST", "MDT", "MDT", "CAST", "CDT", "CDT", "CCST", "SPST", "EDT", "UEDT", "VST", "PYT", "ADT", "CBST", "SWST", "PSST", "NDT", "ESAST", "AST", "SEST", "GDT", "MST", "BST", "U", "MDT", "ADT", "CVST", "MDT", "UTC", "GMT", "BST", "GDT", "GST", "WEDT", "CEDT", "RDT", "CEDT", "WCAST", "NST", "GDT", "MEDT", "EST", "SDT", "EEDT", "SAST", "FDT", "TDT", "JDT", "LST", "JST", "AST", "KST", "AST", "EAST", "MSK", "SAMT", "IDT", "AST", "ADT", "MST", "GST", "CST", "AST", "WAST", "YEKT", "PKT", "IST", "SLST", "NST", "CAST", "BST", "MST", "SAST", "NCAST", "CST", "NAST", "MPST", "WAST", "TST", "UST", "NAEST", "JST", "KST", "CAST", "ACST", "EAST", "AEST", "WPST", "TST", "YST", "CPST", "VST", "NZST", "U", "FST", "MST", "KDT", "TST", "SST"},
+ "text": {"Dateline Standard Time", "UTC-11", "Hawaiian Standard Time", "Alaskan Standard Time", "Pacific Standard Time (Mexico)", "Pacific Daylight Time", "Pacific Standard Time", "US Mountain Standard Time", "Mountain Standard Time (Mexico)", "Mountain Standard Time", "Central America Standard Time", "Central Standard Time", "Central Standard Time (Mexico)", "Canada Central Standard Time", "SA Pacific Standard Time", "Eastern Standard Time", "US Eastern Standard Time", "Venezuela Standard Time", "Paraguay Standard Time", "Atlantic Standard Time", "Central Brazilian Standard Time", "SA Western Standard Time", "Pacific SA Standard Time", "Newfoundland Standard Time", "E. South America Standard Time", "Argentina Standard Time", "SA Eastern Standard Time", "Greenland Standard Time", "Montevideo Standard Time", "Bahia Standard Time", "UTC-02", "Mid-Atlantic Standard Time", "Azores Standard Time", "Cape Verde Standard Time", "Morocco Standard Time", "UTC", "Greenwich Mean Time", "British Summer Time", "GMT Standard Time", "Greenwich Standard Time", "W. Europe Standard Time", "Central Europe Standard Time", "Romance Standard Time", "Central European Standard Time", "W. Central Africa Standard Time", "Namibia Standard Time", "GTB Standard Time", "Middle East Standard Time", "Egypt Standard Time", "Syria Standard Time", "E. Europe Standard Time", "South Africa Standard Time", "FLE Standard Time", "Turkey Standard Time", "Israel Standard Time", "Libya Standard Time", "Jordan Standard Time", "Arabic Standard Time", "Kaliningrad Standard Time", "Arab Standard Time", "E. Africa Standard Time", "Moscow Standard Time", "Samara Time", "Iran Standard Time", "Arabian Standard Time", "Azerbaijan Standard Time", "Mauritius Standard Time", "Georgian Standard Time", "Caucasus Standard Time", "Afghanistan Standard Time", "West Asia Standard Time", "Yekaterinburg Time", "Pakistan Standard Time", "India Standard Time", "Sri Lanka Standard Time", "Nepal Standard Time", "Central Asia Standard Time", "Bangladesh Standard Time", "Myanmar Standard Time", "SE Asia Standard Time", "N. Central Asia Standard Time", "China Standard Time", "North Asia Standard Time", "Singapore Standard Time", "W. Australia Standard Time", "Taipei Standard Time", "Ulaanbaatar Standard Time", "North Asia East Standard Time", "Japan Standard Time", "Korea Standard Time", "Cen. Australia Standard Time", "AUS Central Standard Time", "E. Australia Standard Time", "AUS Eastern Standard Time", "West Pacific Standard Time", "Tasmania Standard Time", "Yakutsk Standard Time", "Central Pacific Standard Time", "Vladivostok Standard Time", "New Zealand Standard Time", "UTC+12", "Fiji Standard Time", "Magadan Standard Time", "Kamchatka Standard Time", "Tonga Standard Time", "Samoa Standard Time"},
+ "full": {"(UTC-12:00) International Date Line West", "(UTC-11:00) Coordinated Universal Time-11", "(UTC-10:00) Hawaii", "(UTC-09:00) Alaska", "(UTC-08:00) Baja California", "(UTC-07:00) Pacific Time (US & Canada)", "(UTC-08:00) Pacific Time (US & Canada)", "(UTC-07:00) Arizona", "(UTC-07:00) Chihuahua, La Paz, Mazatlan", "(UTC-07:00) Mountain Time (US & Canada)", "(UTC-06:00) Central America", "(UTC-06:00) Central Time (US & Canada)", "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "(UTC-06:00) Saskatchewan", "(UTC-05:00) Bogota, Lima, Quito", "(UTC-05:00) Eastern Time (US & Canada)", "(UTC-05:00) Indiana (East)", "(UTC-04:30) Caracas", "(UTC-04:00) Asuncion", "(UTC-04:00) Atlantic Time (Canada)", "(UTC-04:00) Cuiaba", "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan", "(UTC-04:00) Santiago", "(UTC-03:30) Newfoundland", "(UTC-03:00) Brasilia", "(UTC-03:00) Buenos Aires", "(UTC-03:00) Cayenne, Fortaleza", "(UTC-03:00) Greenland", "(UTC-03:00) Montevideo", "(UTC-03:00) Salvador", "(UTC-02:00) Coordinated Universal Time-02", "(UTC-02:00) Mid-Atlantic - Old", "(UTC-01:00) Azores", "(UTC-01:00) Cape Verde Is.", "(UTC) Casablanca", "(UTC) Coordinated Universal Time", "(UTC) Edinburgh, London", "(UTC+01:00) Edinburgh, London", "(UTC) Dublin, Lisbon", "(UTC) Monrovia, Reykjavik", "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", "(UTC+01:00) West Central Africa", "(UTC+01:00) Windhoek", "(UTC+02:00) Athens, Bucharest", "(UTC+02:00) Beirut", "(UTC+02:00) Cairo", "(UTC+02:00) Damascus", "(UTC+02:00) E. Europe", "(UTC+02:00) Harare, Pretoria", "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", "(UTC+03:00) Istanbul", "(UTC+02:00) Jerusalem", "(UTC+02:00) Tripoli", "(UTC+03:00) Amman", "(UTC+03:00) Baghdad", "(UTC+03:00) Kaliningrad, Minsk", "(UTC+03:00) Kuwait, Riyadh", "(UTC+03:00) Nairobi", "(UTC+03:00) Moscow, St. Petersburg, Volgograd", "(UTC+04:00) Samara, Ulyanovsk, Saratov", "(UTC+03:30) Tehran", "(UTC+04:00) Abu Dhabi, Muscat", "(UTC+04:00) Baku", "(UTC+04:00) Port Louis", "(UTC+04:00) Tbilisi", "(UTC+04:00) Yerevan", "(UTC+04:30) Kabul", "(UTC+05:00) Ashgabat, Tashkent", "(UTC+05:00) Yekaterinburg", "(UTC+05:00) Islamabad, Karachi", "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", "(UTC+05:30) Sri Jayawardenepura", "(UTC+05:45) Kathmandu", "(UTC+06:00) Astana", "(UTC+06:00) Dhaka", "(UTC+06:30) Yangon (Rangoon)", "(UTC+07:00) Bangkok, Hanoi, Jakarta", "(UTC+07:00) Novosibirsk", "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", "(UTC+08:00) Krasnoyarsk", "(UTC+08:00) Kuala Lumpur, Singapore", "(UTC+08:00) Perth", "(UTC+08:00) Taipei", "(UTC+08:00) Ulaanbaatar", "(UTC+09:00) Irkutsk", "(UTC+09:00) Osaka, Sapporo, Tokyo", "(UTC+09:00) Seoul", "(UTC+09:30) Adelaide", "(UTC+09:30) Darwin", "(UTC+10:00) Brisbane", "(UTC+10:00) Canberra, Melbourne, Sydney", "(UTC+10:00) Guam, Port Moresby", "(UTC+10:00) Hobart", "(UTC+10:00) Yakutsk", "(UTC+11:00) Solomon Is., New Caledonia", "(UTC+11:00) Vladivostok", "(UTC+12:00) Auckland, Wellington", "(UTC+12:00) Coordinated Universal Time+12", "(UTC+12:00) Fiji", "(UTC+12:00) Magadan", "(UTC+12:00) Petropavlovsk-Kamchatsky - Old", "(UTC+13:00) Nuku'alofa", "(UTC+13:00) Samoa"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/files.go b/vendor/github.com/brianvoe/gofakeit/data/files.go
new file mode 100644
index 000000000000..363b840017f5
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/files.go
@@ -0,0 +1,7 @@
+package data
+
+// Files consists of file information
+var Files = map[string][]string{
+ "mime_type": {"x-world/x-3dmf", "application/octet-stream", "application/x-authorware-bin", "application/x-authorware-map", "application/x-authorware-seg", "text/vnd.abc", "text/html", "video/animaflex", "application/postscript", "audio/aiff", "audio/x-aiff", "audio/aiff", "audio/x-aiff", "audio/aiff", "audio/x-aiff", "application/x-aim", "text/x-audiosoft-intra", "application/x-navi-animation", "application/x-nokia-9000-communicator-add-on-software", "application/mime", "application/octet-stream", "application/arj", "application/octet-stream", "image/x-jg", "video/x-ms-asf", "text/x-asm", "text/asp", "application/x-mplayer2", "video/x-ms-asf", "video/x-ms-asf-plugin", "audio/basic", "audio/x-au", "application/x-troff-msvideo", "video/avi", "video/msvideo", "video/x-msvideo", "video/avs-video", "application/x-bcpio", "application/mac-binary", "application/macbinary", "application/octet-stream", "application/x-binary", "application/x-macbinary", "image/bmp", "image/bmp", "image/x-windows-bmp", "application/book", "application/book", "application/x-bzip2", "application/x-bsh", "application/x-bzip", "application/x-bzip2", "text/plain", "text/x-c", "text/plain", "application/vnd.ms-pki.seccat", "text/plain", "text/x-c", "application/clariscad", "application/x-cocoa", "application/cdf", "application/x-cdf", "application/x-netcdf", "application/pkix-cert", "application/x-x509-ca-cert", "application/x-chat", "application/x-chat", "application/java", "application/java-byte-code", "application/x-java-class", "application/octet-stream", "text/plain", "text/plain", "application/x-cpio", "text/x-c", "application/mac-compactpro", "application/x-compactpro", "application/x-cpt", "application/pkcs-crl", "application/pkix-crl", "application/pkix-cert", "application/x-x509-ca-cert", "application/x-x509-user-cert", "application/x-csh", "text/x-script.csh", "application/x-pointplus", "text/css", "text/plain", "application/x-director", "application/x-deepv", "text/plain", "application/x-x509-ca-cert", "video/x-dv", "application/x-director", "video/dl", "video/x-dl", "application/msword", "application/msword", "application/commonground", "application/drafting", "application/octet-stream", "video/x-dv", "application/x-dvi", "drawing/x-dwf (old)", "model/vnd.dwf", "application/acad", "image/vnd.dwg", "image/x-dwg", "application/dxf", "image/vnd.dwg", "image/x-dwg", "application/x-director", "text/x-script.elisp", "application/x-bytecode.elisp (compiled elisp)", "application/x-elc", "application/x-envoy", "application/postscript", "application/x-esrehber", "text/x-setext", "application/envoy", "application/x-envoy", "application/octet-stream", "text/plain", "text/x-fortran", "text/x-fortran", "text/plain", "text/x-fortran", "application/vnd.fdf", "application/fractals", "image/fif", "video/fli", "video/x-fli", "image/florian", "text/vnd.fmi.flexstor", "video/x-atomic3d-feature", "text/plain", "text/x-fortran", "image/vnd.fpx", "image/vnd.net-fpx", "application/freeloader", "audio/make", "text/plain", "image/g3fax", "image/gif", "video/gl", "video/x-gl", "audio/x-gsm", "audio/x-gsm", "application/x-gsp", "application/x-gss", "application/x-gtar", "application/x-compressed", "application/x-gzip", "application/x-gzip", "multipart/x-gzip", "text/plain", "text/x-h", "application/x-hdf", "application/x-helpfile", "application/vnd.hp-hpgl", "text/plain", "text/x-h", "text/x-script", "application/hlp", "application/x-helpfile", "application/x-winhelp", "application/vnd.hp-hpgl", "application/vnd.hp-hpgl", "application/binhex", "application/binhex4", "application/mac-binhex", "application/mac-binhex40", "application/x-binhex40", "application/x-mac-binhex40", "application/hta", "text/x-component", "text/html", "text/html", "text/html", "text/webviewhtml", "text/html", "x-conference/x-cooltalk", "image/x-icon", "text/plain", "image/ief", "image/ief", "application/iges", "model/iges", "application/iges", "model/iges", "application/x-ima", "application/x-httpd-imap", "application/inf", "application/x-internett-signup", "application/x-ip2", "video/x-isvideo", "audio/it", "application/x-inventor", "i-world/i-vrml", "application/x-livescreen", "audio/x-jam", "text/plain", "text/x-java-source", "text/plain", "text/x-java-source", "application/x-java-commerce", "image/jpeg", "image/pjpeg", "image/jpeg", "image/jpeg", "image/pjpeg", "image/jpeg", "image/pjpeg", "image/jpeg", "image/pjpeg", "image/x-jps", "application/x-javascript", "image/jutvision", "audio/midi", "music/x-karaoke", "application/x-ksh", "text/x-script.ksh", "audio/nspaudio", "audio/x-nspaudio", "audio/x-liveaudio", "application/x-latex", "application/lha", "application/octet-stream", "application/x-lha", "application/octet-stream", "text/plain", "audio/nspaudio", "audio/x-nspaudio", "text/plain", "application/x-lisp", "text/x-script.lisp", "text/plain", "text/x-la-asf", "application/x-latex", "application/octet-stream", "application/x-lzh", "application/lzx", "application/octet-stream", "application/x-lzx", "text/plain", "text/x-m", "video/mpeg", "audio/mpeg", "video/mpeg", "audio/x-mpequrl", "application/x-troff-man", "application/x-navimap", "text/plain", "application/mbedlet", "application/mcad", "application/x-mathcad", "image/vasa", "text/mcf", "application/netmc", "application/x-troff-me", "message/rfc822", "message/rfc822", "application/x-midi", "audio/midi", "audio/x-mid", "audio/x-midi", "music/crescendo", "x-music/x-midi", "application/x-midi", "audio/midi", "audio/x-mid", "audio/x-midi", "music/crescendo", "x-music/x-midi", "application/x-frame", "application/x-mif", "message/rfc822", "www/mime", "video/x-motion-jpeg", "application/base64", "application/x-meme", "application/base64", "audio/mod", "audio/x-mod", "video/quicktime", "video/quicktime", "video/x-sgi-movie", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "video/x-mpeq2a", "audio/mpeg3", "audio/x-mpeg-3", "video/mpeg", "video/x-mpeg", "audio/mpeg", "video/mpeg", "application/x-project", "video/mpeg", "video/mpeg", "audio/mpeg", "video/mpeg", "audio/mpeg", "application/vnd.ms-project", "application/x-project", "application/x-project", "application/x-project", "application/marc", "application/x-troff-ms", "video/x-sgi-movie", "audio/make", "application/x-vnd.audioexplosion.mzz", "image/naplps", "image/naplps", "application/x-netcdf", "application/vnd.nokia.configuration-message", "image/x-niff", "image/x-niff", "application/x-mix-transfer", "application/x-conference", "application/x-navidoc", "application/octet-stream", "application/oda", "application/x-omc", "application/x-omcdatamaker", "application/x-omcregerator", "text/x-pascal", "application/pkcs10", "application/x-pkcs10", "application/pkcs-12", "application/x-pkcs12", "application/x-pkcs7-signature", "application/pkcs7-mime", "application/x-pkcs7-mime", "application/pkcs7-mime", "application/x-pkcs7-mime", "application/x-pkcs7-certreqresp", "application/pkcs7-signature", "application/pro_eng", "text/pascal", "image/x-portable-bitmap", "application/vnd.hp-pcl", "application/x-pcl", "image/x-pict", "image/x-pcx", "chemical/x-pdb", "application/pdf", "audio/make", "audio/make.my.funk", "image/x-portable-graymap", "image/x-portable-greymap", "image/pict", "image/pict", "application/x-newton-compatible-pkg", "application/vnd.ms-pki.pko", "text/plain", "text/x-script.perl", "application/x-pixclscript", "image/x-xpixmap", "text/x-script.perl-module", "application/x-pagemaker", "application/x-pagemaker", "image/png", "application/x-portable-anymap", "image/x-portable-anymap", "application/mspowerpoint", "application/vnd.ms-powerpoint", "model/x-pov", "application/vnd.ms-powerpoint", "image/x-portable-pixmap", "application/mspowerpoint", "application/vnd.ms-powerpoint", "application/mspowerpoint", "application/powerpoint", "application/vnd.ms-powerpoint", "application/x-mspowerpoint", "application/mspowerpoint", "application/x-freelance", "application/pro_eng", "application/postscript", "application/octet-stream", "paleovu/x-pv", "application/vnd.ms-powerpoint", "text/x-script.phyton", "application/x-bytecode.python", "audio/vnd.qcelp", "x-world/x-3dmf", "x-world/x-3dmf", "image/x-quicktime", "video/quicktime", "video/x-qtc", "image/x-quicktime", "image/x-quicktime", "audio/x-pn-realaudio", "audio/x-pn-realaudio-plugin", "audio/x-realaudio", "audio/x-pn-realaudio", "application/x-cmu-raster", "image/cmu-raster", "image/x-cmu-raster", "image/cmu-raster", "text/x-script.rexx", "image/vnd.rn-realflash", "image/x-rgb", "application/vnd.rn-realmedia", "audio/x-pn-realaudio", "audio/mid", "audio/x-pn-realaudio", "audio/x-pn-realaudio", "audio/x-pn-realaudio-plugin", "application/ringing-tones", "application/vnd.nokia.ringing-tone", "application/vnd.rn-realplayer", "application/x-troff", "image/vnd.rn-realpix", "audio/x-pn-realaudio-plugin", "text/richtext", "text/vnd.rn-realtext", "application/rtf", "application/x-rtf", "text/richtext", "application/rtf", "text/richtext", "video/vnd.rn-realvideo", "text/x-asm", "audio/s3m", "application/octet-stream", "application/x-tbook", "application/x-lotusscreencam", "text/x-script.guile", "text/x-script.scheme", "video/x-scm", "text/plain", "application/sdp", "application/x-sdp", "application/sounder", "application/sea", "application/x-sea", "application/set", "text/sgml", "text/x-sgml", "text/sgml", "text/x-sgml", "application/x-bsh", "application/x-sh", "application/x-shar", "text/x-script.sh", "application/x-bsh", "application/x-shar", "text/html", "text/x-server-parsed-html", "audio/x-psid", "application/x-sit", "application/x-stuffit", "application/x-koan", "application/x-koan", "application/x-koan", "application/x-koan", "application/x-seelogo", "application/smil", "application/smil", "audio/basic", "audio/x-adpcm", "application/solids", "application/x-pkcs7-certificates", "text/x-speech", "application/futuresplash", "application/x-sprite", "application/x-sprite", "application/x-wais-source", "text/x-server-parsed-html", "application/streamingmedia", "application/vnd.ms-pki.certstore", "application/step", "application/sla", "application/vnd.ms-pki.stl", "application/x-navistyle", "application/step", "application/x-sv4cpio", "application/x-sv4crc", "image/vnd.dwg", "image/x-dwg", "application/x-world", "x-world/x-svr", "application/x-shockwave-flash", "application/x-troff", "text/x-speech", "application/x-tar", "application/toolbook", "application/x-tbook", "application/x-tcl", "text/x-script.tcl", "text/x-script.tcsh", "application/x-tex", "application/x-texinfo", "application/x-texinfo", "application/plain", "text/plain", "application/gnutar", "application/x-compressed", "image/tiff", "image/x-tiff", "image/tiff", "image/x-tiff", "application/x-troff", "audio/tsp-audio", "application/dsptype", "audio/tsplayer", "text/tab-separated-values", "image/florian", "text/plain", "text/x-uil", "text/uri-list", "text/uri-list", "application/i-deas", "text/uri-list", "text/uri-list", "application/x-ustar", "multipart/x-ustar", "application/octet-stream", "text/x-uuencode", "text/x-uuencode", "application/x-cdlink", "text/x-vcalendar", "application/vda", "video/vdo", "application/groupwise", "video/vivo", "video/vnd.vivo", "video/vivo", "video/vnd.vivo", "application/vocaltec-media-desc", "application/vocaltec-media-file", "audio/voc", "audio/x-voc", "video/vosaic", "audio/voxware", "audio/x-twinvq-plugin", "audio/x-twinvq", "audio/x-twinvq-plugin", "application/x-vrml", "model/vrml", "x-world/x-vrml", "x-world/x-vrt", "application/x-visio", "application/x-visio", "application/x-visio", "application/wordperfect6.0", "application/wordperfect6.1", "application/msword", "audio/wav", "audio/x-wav", "application/x-qpro", "image/vnd.wap.wbmp", "application/vnd.xara", "application/msword", "application/x-123", "windows/metafile", "text/vnd.wap.wml", "application/vnd.wap.wmlc", "text/vnd.wap.wmlscript", "application/vnd.wap.wmlscriptc", "application/msword", "application/wordperfect", "application/wordperfect", "application/wordperfect6.0", "application/wordperfect", "application/wordperfect", "application/x-wpwin", "application/x-lotus", "application/mswrite", "application/x-wri", "application/x-world", "model/vrml", "x-world/x-vrml", "model/vrml", "x-world/x-vrml", "text/scriplet", "application/x-wais-source", "application/x-wintalk", "image/x-xbitmap", "image/x-xbm", "image/xbm", "video/x-amt-demorun", "xgl/drawing", "image/vnd.xiff", "application/excel", "application/excel", "application/x-excel", "application/x-msexcel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/x-excel", "application/excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/x-msexcel", "application/excel", "application/x-excel", "application/excel", "application/x-excel", "application/excel", "application/vnd.ms-excel", "application/x-excel", "application/x-msexcel", "audio/xm", "application/xml", "text/xml", "xgl/movie", "application/x-vnd.ls-xpix", "image/x-xpixmap", "image/xpm", "image/png", "video/x-amt-showrun", "image/x-xwd", "image/x-xwindowdump", "chemical/x-pdb", "application/x-compress", "application/x-compressed", "application/x-compressed", "application/x-zip-compressed", "application/zip", "multipart/x-zip", "application/octet-stream", "text/x-script.zsh"},
+ "extension": {"doc", "docx", "log", "msg", "odt", "pages", "rtf", "tex", "txt", "wpd", "wps", "csv", "dat", "gbr", "ged", "key", "keychain", "pps", "ppt", "pptx", "sdf", "tar", "vcf", "xml", "aif", "iff", "mid", "mpa", "ra", "wav", "wma", "asf", "asx", "avi", "flv", "mov", "mpg", "rm", "srt", "swf", "vob", "wmv", "max", "obj", "bmp", "dds", "gif", "jpg", "png", "psd", "pspimage", "tga", "thm", "tif", "tiff", "yuv", "ai", "eps", "ps", "svg", "indd", "pct", "pdf", "xlr", "xls", "xlsx", "accdb", "db", "dbf", "mdb", "pdb", "sql", "apk", "app", "bat", "cgi", "com", "exe", "gadget", "jar", "pif", "vb", "wsf", "dem", "gam", "nes", "rom", "sav", "dwg", "dxf", "gpx", "kml", "kmz", "asp", "aspx", "cer", "cfm", "csr", "css", "htm", "html", "js", "jsp", "php", "rss", "xhtml", "crx", "plugin", "fnt", "fon", "otf", "ttf", "cab", "cpl", "cur", "deskthemepack", "dll", "dmp", "drv", "icns", "ico", "lnk", "sys", "cfg", "ini", "prf", "hqx", "mim", "uue", "cbr", "deb", "gz", "pkg", "rar", "rpm", "sitx", "gz", "zip", "zipx", "bin", "cue", "dmg", "iso", "mdf", "toast", "vcd", "class", "cpp", "cs", "dtd", "fla", "java", "lua", "pl", "py", "sh", "sln", "swift", "vcxproj", "xcodeproj", "bak", "tmp", "crdownload", "ics", "msi", "part", "torrent"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/hacker.go b/vendor/github.com/brianvoe/gofakeit/data/hacker.go
new file mode 100644
index 000000000000..4735f7d560af
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/hacker.go
@@ -0,0 +1,20 @@
+package data
+
+// Hacker consists of random hacker phrases
+var Hacker = map[string][]string{
+ "abbreviation": {"TCP", "HTTP", "SDD", "RAM", "GB", "CSS", "SSL", "AGP", "SQL", "FTP", "PCI", "AI", "ADP", "RSS", "XML", "EXE", "COM", "HDD", "THX", "SMTP", "SMS", "USB", "PNG", "SAS", "IB", "SCSI", "JSON", "XSS", "JBOD"},
+ "adjective": {"auxiliary", "primary", "back-end", "digital", "open-source", "virtual", "cross-platform", "redundant", "online", "haptic", "multi-byte", "bluetooth", "wireless", "1080p", "neural", "optical", "solid state", "mobile"},
+ "noun": {"driver", "protocol", "bandwidth", "panel", "microchip", "program", "port", "card", "array", "interface", "system", "sensor", "firewall", "hard drive", "pixel", "alarm", "feed", "monitor", "application", "transmitter", "bus", "circuit", "capacitor", "matrix"},
+ "verb": {"back up", "bypass", "hack", "override", "compress", "copy", "navigate", "index", "connect", "generate", "quantify", "calculate", "synthesize", "input", "transmit", "program", "reboot", "parse"},
+ "ingverb": {"backing up", "bypassing", "hacking", "overriding", "compressing", "copying", "navigating", "indexing", "connecting", "generating", "quantifying", "calculating", "synthesizing", "transmitting", "programming", "parsing"},
+ "phrase": {
+ "If we {hacker.verb} the {hacker.noun}, we can get to the {hacker.abbreviation} {hacker.noun} through the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!",
+ "We need to {hacker.verb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!",
+ "Try to {hacker.verb} the {hacker.abbreviation} {hacker.noun}, maybe it will {hacker.verb} the {hacker.adjective} {hacker.noun}!",
+ "You can't {hacker.verb} the {hacker.noun} without {hacker.ingverb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!",
+ "Use the {hacker.adjective} {hacker.abbreviation} {hacker.noun}, then you can {hacker.verb} the {hacker.adjective} {hacker.noun}!",
+ "The {hacker.abbreviation} {hacker.noun} is down, {hacker.verb} the {hacker.adjective} {hacker.noun} so we can {hacker.verb} the {hacker.abbreviation} {hacker.noun}!",
+ "{hacker.ingverb} the {hacker.noun} won't do anything, we need to {hacker.verb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}!",
+ "I'll {hacker.verb} the {hacker.adjective} {hacker.abbreviation} {hacker.noun}, that should {hacker.verb} the {hacker.abbreviation} {hacker.noun}!",
+ },
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/hipster.go b/vendor/github.com/brianvoe/gofakeit/data/hipster.go
new file mode 100644
index 000000000000..f036f4639bc8
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/hipster.go
@@ -0,0 +1,6 @@
+package data
+
+// Hipster consists of random hipster words
+var Hipster = map[string][]string{
+ "word": {"Wes Anderson", "chicharrones", "narwhal", "food truck", "marfa", "aesthetic", "keytar", "art party", "sustainable", "forage", "mlkshk", "gentrify", "locavore", "swag", "hoodie", "microdosing", "VHS", "before they sold out", "pabst", "plaid", "Thundercats", "freegan", "scenester", "hella", "occupy", "truffaut", "raw denim", "beard", "post-ironic", "photo booth", "twee", "90's", "pitchfork", "cray", "cornhole", "kale chips", "pour-over", "yr", "five dollar toast", "kombucha", "you probably haven't heard of them", "mustache", "fixie", "try-hard", "franzen", "kitsch", "austin", "stumptown", "keffiyeh", "whatever", "tumblr", "DIY", "shoreditch", "biodiesel", "vegan", "pop-up", "banjo", "kogi", "cold-pressed", "letterpress", "chambray", "butcher", "synth", "trust fund", "hammock", "farm-to-table", "intelligentsia", "loko", "ugh", "offal", "poutine", "gastropub", "Godard", "jean shorts", "sriracha", "dreamcatcher", "leggings", "fashion axe", "church-key", "meggings", "tote bag", "disrupt", "readymade", "helvetica", "flannel", "meh", "roof", "hashtag", "knausgaard", "cronut", "schlitz", "green juice", "waistcoat", "normcore", "viral", "ethical", "actually", "fingerstache", "humblebrag", "deep v", "wayfarers", "tacos", "taxidermy", "selvage", "put a bird on it", "ramps", "portland", "retro", "kickstarter", "bushwick", "brunch", "distillery", "migas", "flexitarian", "XOXO", "small batch", "messenger bag", "heirloom", "tofu", "bicycle rights", "bespoke", "salvia", "wolf", "selfies", "echo", "park", "listicle", "craft beer", "chartreuse", "sartorial", "pinterest", "mumblecore", "kinfolk", "vinyl", "etsy", "umami", "8-bit", "polaroid", "banh mi", "crucifix", "bitters", "brooklyn", "PBR&B", "drinking", "vinegar", "squid", "tattooed", "skateboard", "vice", "authentic", "literally", "lomo", "celiac", "health", "goth", "artisan", "chillwave", "blue bottle", "pickled", "next level", "neutra", "organic", "Yuccie", "paleo", "blog", "single-origin coffee", "seitan", "street", "gluten-free", "mixtape", "venmo", "irony", "everyday", "carry", "slow-carb", "3 wolf moon", "direct trade", "lo-fi", "tousled", "tilde", "semiotics", "cred", "chia", "master", "cleanse", "ennui", "quinoa", "pug", "iPhone", "fanny pack", "cliche", "cardigan", "asymmetrical", "meditation", "YOLO", "typewriter", "pork belly", "shabby chic", "+1", "lumbersexual", "williamsburg"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/internet.go b/vendor/github.com/brianvoe/gofakeit/data/internet.go
new file mode 100644
index 000000000000..1f16db95c765
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/internet.go
@@ -0,0 +1,8 @@
+package data
+
+// Internet consists of various internet information
+var Internet = map[string][]string{
+ "browser": {"firefox", "chrome", "internetExplorer", "opera", "safari"},
+ "domain_suffix": {"com", "biz", "info", "name", "net", "org", "io"},
+ "http_method": {"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/job.go b/vendor/github.com/brianvoe/gofakeit/data/job.go
new file mode 100644
index 000000000000..905dd74ee023
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/job.go
@@ -0,0 +1,8 @@
+package data
+
+// Job consists of job data
+var Job = map[string][]string{
+ "title": {"Administrator", "Agent", "Analyst", "Architect", "Assistant", "Associate", "Consultant", "Coordinator", "Designer", "Developer", "Director", "Engineer", "Executive", "Facilitator", "Liaison", "Manager", "Officer", "Orchestrator", "Planner", "Producer", "Representative", "Specialist", "Strategist", "Supervisor", "Technician"},
+ "descriptor": {"Central", "Chief", "Corporate", "Customer", "Direct", "District", "Dynamic", "Dynamic", "Forward", "Future", "Global", "Human", "Internal", "International", "Investor", "Lead", "Legacy", "National", "Principal", "Product", "Regional", "Senior"},
+ "level": {"Accountability", "Accounts", "Applications", "Assurance", "Brand", "Branding", "Communications", "Configuration", "Creative", "Data", "Directives", "Division", "Factors", "Functionality", "Group", "Identity", "Implementation", "Infrastructure", "Integration", "Interactions", "Intranet", "Marketing", "Markets", "Metrics", "Mobility", "Operations", "Optimization", "Paradigm", "Program", "Quality", "Research", "Response", "Security", "Solutions", "Tactics", "Usability", "Web"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/log_level.go b/vendor/github.com/brianvoe/gofakeit/data/log_level.go
new file mode 100644
index 000000000000..01d98b63c6b6
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/log_level.go
@@ -0,0 +1,8 @@
+package data
+
+// LogLevels consists of log levels for several types
+var LogLevels = map[string][]string{
+ "general": {"error", "warning", "info", "fatal", "trace", "debug"},
+ "syslog": {"emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"},
+ "apache": {"emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace1-8"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/lorem.go b/vendor/github.com/brianvoe/gofakeit/data/lorem.go
new file mode 100644
index 000000000000..b0a8f8a1378f
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/lorem.go
@@ -0,0 +1,6 @@
+package data
+
+// Lorem consists of lorem ipsum information
+var Lorem = map[string][]string{
+ "word": {"alias", "consequatur", "aut", "perferendis", "sit", "voluptatem", "accusantium", "doloremque", "aperiam", "eaque", "ipsa", "quae", "ab", "illo", "inventore", "veritatis", "et", "quasi", "architecto", "beatae", "vitae", "dicta", "sunt", "explicabo", "aspernatur", "aut", "odit", "aut", "fugit", "sed", "quia", "consequuntur", "magni", "dolores", "eos", "qui", "ratione", "voluptatem", "sequi", "nesciunt", "neque", "dolorem", "ipsum", "quia", "dolor", "sit", "amet", "consectetur", "adipisci", "velit", "sed", "quia", "non", "numquam", "eius", "modi", "tempora", "incidunt", "ut", "labore", "et", "dolore", "magnam", "aliquam", "quaerat", "voluptatem", "ut", "enim", "ad", "minima", "veniam", "quis", "nostrum", "exercitationem", "ullam", "corporis", "nemo", "enim", "ipsam", "voluptatem", "quia", "voluptas", "sit", "suscipit", "laboriosam", "nisi", "ut", "aliquid", "ex", "ea", "commodi", "consequatur", "quis", "autem", "vel", "eum", "iure", "reprehenderit", "qui", "in", "ea", "voluptate", "velit", "esse", "quam", "nihil", "molestiae", "et", "iusto", "odio", "dignissimos", "ducimus", "qui", "blanditiis", "praesentium", "laudantium", "totam", "rem", "voluptatum", "deleniti", "atque", "corrupti", "quos", "dolores", "et", "quas", "molestias", "excepturi", "sint", "occaecati", "cupiditate", "non", "provident", "sed", "ut", "perspiciatis", "unde", "omnis", "iste", "natus", "error", "similique", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollitia", "animi", "id", "est", "laborum", "et", "dolorum", "fuga", "et", "harum", "quidem", "rerum", "facilis", "est", "et", "expedita", "distinctio", "nam", "libero", "tempore", "cum", "soluta", "nobis", "est", "eligendi", "optio", "cumque", "nihil", "impedit", "quo", "porro", "quisquam", "est", "qui", "minus", "id", "quod", "maxime", "placeat", "facere", "possimus", "omnis", "voluptas", "assumenda", "est", "omnis", "dolor", "repellendus", "temporibus", "autem", "quibusdam", "et", "aut", "consequatur", "vel", "illum", "qui", "dolorem", "eum", "fugiat", "quo", "voluptas", "nulla", "pariatur", "at", "vero", "eos", "et", "accusamus", "officiis", "debitis", "aut", "rerum", "necessitatibus", "saepe", "eveniet", "ut", "et", "voluptates", "repudiandae", "sint", "et", "molestiae", "non", "recusandae", "itaque", "earum", "rerum", "hic", "tenetur", "a", "sapiente", "delectus", "ut", "aut", "reiciendis", "voluptatibus", "maiores", "doloribus", "asperiores", "repellat"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/payment.go b/vendor/github.com/brianvoe/gofakeit/data/payment.go
new file mode 100644
index 000000000000..e50903a72af6
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/payment.go
@@ -0,0 +1,20 @@
+package data
+
+// Payment contains payment information
+var Payment = map[string][]string{
+ "card_type": {"Visa", "MasterCard", "American Express", "Discover"},
+ "number": {
+ // Visa
+ "4###############",
+ "4###############",
+ // Mastercard
+ "222100##########",
+ "272099##########",
+ // American Express
+ "34#############",
+ "37#############",
+ // Discover
+ "65##############",
+ "65##############",
+ },
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/person.go b/vendor/github.com/brianvoe/gofakeit/data/person.go
new file mode 100644
index 000000000000..129b59ba6e3c
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/person.go
@@ -0,0 +1,9 @@
+package data
+
+// Person consists of a slice of people information
+var Person = map[string][]string{
+ "prefix": {"Mr.", "Mrs.", "Ms.", "Miss", "Dr."},
+ "suffix": {"Jr.", "Sr.", "I", "II", "III", "IV", "V", "MD", "DDS", "PhD", "DVM"},
+ "first": {"Aaliyah", "Aaron", "Abagail", "Abbey", "Abbie", "Abbigail", "Abby", "Abdiel", "Abdul", "Abdullah", "Abe", "Abel", "Abelardo", "Abigail", "Abigale", "Abigayle", "Abner", "Abraham", "Ada", "Adah", "Adalberto", "Adaline", "Adam", "Adan", "Addie", "Addison", "Adela", "Adelbert", "Adele", "Adelia", "Adeline", "Adell", "Adella", "Adelle", "Aditya", "Adolf", "Adolfo", "Adolph", "Adolphus", "Adonis", "Adrain", "Adrian", "Adriana", "Adrianna", "Adriel", "Adrien", "Adrienne", "Afton", "Aglae", "Agnes", "Agustin", "Agustina", "Ahmad", "Ahmed", "Aida", "Aidan", "Aiden", "Aileen", "Aimee", "Aisha", "Aiyana", "Akeem", "Al", "Alaina", "Alan", "Alana", "Alanis", "Alanna", "Alayna", "Alba", "Albert", "Alberta", "Albertha", "Alberto", "Albin", "Albina", "Alda", "Alden", "Alec", "Aleen", "Alejandra", "Alejandrin", "Alek", "Alena", "Alene", "Alessandra", "Alessandro", "Alessia", "Aletha", "Alex", "Alexa", "Alexander", "Alexandra", "Alexandre", "Alexandrea", "Alexandria", "Alexandrine", "Alexandro", "Alexane", "Alexanne", "Alexie", "Alexis", "Alexys", "Alexzander", "Alf", "Alfonso", "Alfonzo", "Alford", "Alfred", "Alfreda", "Alfredo", "Ali", "Alia", "Alice", "Alicia", "Alisa", "Alisha", "Alison", "Alivia", "Aliya", "Aliyah", "Aliza", "Alize", "Allan", "Allen", "Allene", "Allie", "Allison", "Ally", "Alphonso", "Alta", "Althea", "Alva", "Alvah", "Alvena", "Alvera", "Alverta", "Alvina", "Alvis", "Alyce", "Alycia", "Alysa", "Alysha", "Alyson", "Alysson", "Amalia", "Amanda", "Amani", "Amara", "Amari", "Amaya", "Amber", "Ambrose", "Amelia", "Amelie", "Amely", "America", "Americo", "Amie", "Amina", "Amir", "Amira", "Amiya", "Amos", "Amparo", "Amy", "Amya", "Ana", "Anabel", "Anabelle", "Anahi", "Anais", "Anastacio", "Anastasia", "Anderson", "Andre", "Andreane", "Andreanne", "Andres", "Andrew", "Andy", "Angel", "Angela", "Angelica", "Angelina", "Angeline", "Angelita", "Angelo", "Angie", "Angus", "Anibal", "Anika", "Anissa", "Anita", "Aniya", "Aniyah", "Anjali", "Anna", "Annabel", "Annabell", "Annabelle", "Annalise", "Annamae", "Annamarie", "Anne", "Annetta", "Annette", "Annie", "Ansel", "Ansley", "Anthony", "Antoinette", "Antone", "Antonetta", "Antonette", "Antonia", "Antonietta", "Antonina", "Antonio", "Antwan", "Antwon", "Anya", "April", "Ara", "Araceli", "Aracely", "Arch", "Archibald", "Ardella", "Arden", "Ardith", "Arely", "Ari", "Ariane", "Arianna", "Aric", "Ariel", "Arielle", "Arjun", "Arlene", "Arlie", "Arlo", "Armand", "Armando", "Armani", "Arnaldo", "Arne", "Arno", "Arnold", "Arnoldo", "Arnulfo", "Aron", "Art", "Arthur", "Arturo", "Arvel", "Arvid", "Arvilla", "Aryanna", "Asa", "Asha", "Ashlee", "Ashleigh", "Ashley", "Ashly", "Ashlynn", "Ashton", "Ashtyn", "Asia", "Assunta", "Astrid", "Athena", "Aubree", "Aubrey", "Audie", "Audra", "Audreanne", "Audrey", "August", "Augusta", "Augustine", "Augustus", "Aurelia", "Aurelie", "Aurelio", "Aurore", "Austen", "Austin", "Austyn", "Autumn", "Ava", "Avery", "Avis", "Axel", "Ayana", "Ayden", "Ayla", "Aylin", "Baby", "Bailee", "Bailey", "Barbara", "Barney", "Baron", "Barrett", "Barry", "Bart", "Bartholome", "Barton", "Baylee", "Beatrice", "Beau", "Beaulah", "Bell", "Bella", "Belle", "Ben", "Benedict", "Benjamin", "Bennett", "Bennie", "Benny", "Benton", "Berenice", "Bernadette", "Bernadine", "Bernard", "Bernardo", "Berneice", "Bernhard", "Bernice", "Bernie", "Berniece", "Bernita", "Berry", "Bert", "Berta", "Bertha", "Bertram", "Bertrand", "Beryl", "Bessie", "Beth", "Bethany", "Bethel", "Betsy", "Bette", "Bettie", "Betty", "Bettye", "Beulah", "Beverly", "Bianka", "Bill", "Billie", "Billy", "Birdie", "Blair", "Blaise", "Blake", "Blanca", "Blanche", "Blaze", "Bo", "Bobbie", "Bobby", "Bonita", "Bonnie", "Boris", "Boyd", "Brad", "Braden", "Bradford", "Bradley", "Bradly", "Brady", "Braeden", "Brain", "Brandi", "Brando", "Brandon", "Brandt", "Brandy", "Brandyn", "Brannon", "Branson", "Brant", "Braulio", "Braxton", "Brayan", "Breana", "Breanna", "Breanne", "Brenda", "Brendan", "Brenden", "Brendon", "Brenna", "Brennan", "Brennon", "Brent", "Bret", "Brett", "Bria", "Brian", "Briana", "Brianne", "Brice", "Bridget", "Bridgette", "Bridie", "Brielle", "Brigitte", "Brionna", "Brisa", "Britney", "Brittany", "Brock", "Broderick", "Brody", "Brook", "Brooke", "Brooklyn", "Brooks", "Brown", "Bruce", "Bryana", "Bryce", "Brycen", "Bryon", "Buck", "Bud", "Buddy", "Buford", "Bulah", "Burdette", "Burley", "Burnice", "Buster", "Cade", "Caden", "Caesar", "Caitlyn", "Cale", "Caleb", "Caleigh", "Cali", "Calista", "Callie", "Camden", "Cameron", "Camila", "Camilla", "Camille", "Camren", "Camron", "Camryn", "Camylle", "Candace", "Candelario", "Candice", "Candida", "Candido", "Cara", "Carey", "Carissa", "Carlee", "Carleton", "Carley", "Carli", "Carlie", "Carlo", "Carlos", "Carlotta", "Carmel", "Carmela", "Carmella", "Carmelo", "Carmen", "Carmine", "Carol", "Carolanne", "Carole", "Carolina", "Caroline", "Carolyn", "Carolyne", "Carrie", "Carroll", "Carson", "Carter", "Cary", "Casandra", "Casey", "Casimer", "Casimir", "Casper", "Cassandra", "Cassandre", "Cassidy", "Cassie", "Catalina", "Caterina", "Catharine", "Catherine", "Cathrine", "Cathryn", "Cathy", "Cayla", "Ceasar", "Cecelia", "Cecil", "Cecile", "Cecilia", "Cedrick", "Celestine", "Celestino", "Celia", "Celine", "Cesar", "Chad", "Chadd", "Chadrick", "Chaim", "Chance", "Chandler", "Chanel", "Chanelle", "Charity", "Charlene", "Charles", "Charley", "Charlie", "Charlotte", "Chase", "Chasity", "Chauncey", "Chaya", "Chaz", "Chelsea", "Chelsey", "Chelsie", "Chesley", "Chester", "Chet", "Cheyanne", "Cheyenne", "Chloe", "Chris", "Christ", "Christa", "Christelle", "Christian", "Christiana", "Christina", "Christine", "Christop", "Christophe", "Christopher", "Christy", "Chyna", "Ciara", "Cicero", "Cielo", "Cierra", "Cindy", "Citlalli", "Clair", "Claire", "Clara", "Clarabelle", "Clare", "Clarissa", "Clark", "Claud", "Claude", "Claudia", "Claudie", "Claudine", "Clay", "Clemens", "Clement", "Clementina", "Clementine", "Clemmie", "Cleo", "Cleora", "Cleta", "Cletus", "Cleve", "Cleveland", "Clifford", "Clifton", "Clint", "Clinton", "Clotilde", "Clovis", "Cloyd", "Clyde", "Coby", "Cody", "Colby", "Cole", "Coleman", "Colin", "Colleen", "Collin", "Colt", "Colten", "Colton", "Columbus", "Concepcion", "Conner", "Connie", "Connor", "Conor", "Conrad", "Constance", "Constantin", "Consuelo", "Cooper", "Cora", "Coralie", "Corbin", "Cordelia", "Cordell", "Cordia", "Cordie", "Corene", "Corine", "Cornelius", "Cornell", "Corrine", "Cortez", "Cortney", "Cory", "Coty", "Courtney", "Coy", "Craig", "Crawford", "Creola", "Cristal", "Cristian", "Cristina", "Cristobal", "Cristopher", "Cruz", "Crystal", "Crystel", "Cullen", "Curt", "Curtis", "Cydney", "Cynthia", "Cyril", "Cyrus", "Dagmar", "Dahlia", "Daija", "Daisha", "Daisy", "Dakota", "Dale", "Dallas", "Dallin", "Dalton", "Damaris", "Dameon", "Damian", "Damien", "Damion", "Damon", "Dan", "Dana", "Dandre", "Dane", "Dangelo", "Dangelo", "Danial", "Daniela", "Daniella", "Danielle", "Danika", "Dannie", "Danny", "Dante", "Danyka", "Daphne", "Daphnee", "Daphney", "Darby", "Daren", "Darian", "Dariana", "Darien", "Dario", "Darion", "Darius", "Darlene", "Daron", "Darrel", "Darrell", "Darren", "Darrick", "Darrin", "Darrion", "Darron", "Darryl", "Darwin", "Daryl", "Dashawn", "Dasia", "Dave", "David", "Davin", "Davion", "Davon", "Davonte", "Dawn", "Dawson", "Dax", "Dayana", "Dayna", "Dayne", "Dayton", "Dean", "Deangelo", "Deanna", "Deborah", "Declan", "Dedric", "Dedrick", "Dee", "Deion", "Deja", "Dejah", "Dejon", "Dejuan", "Delaney", "Delbert", "Delfina", "Delia", "Delilah", "Dell", "Della", "Delmer", "Delores", "Delpha", "Delphia", "Delphine", "Delta", "Demarco", "Demarcus", "Demario", "Demetris", "Demetrius", "Demond", "Dena", "Denis", "Dennis", "Deon", "Deondre", "Deontae", "Deonte", "Dereck", "Derek", "Derick", "Deron", "Derrick", "Deshaun", "Deshawn", "Desiree", "Desmond", "Dessie", "Destany", "Destin", "Destinee", "Destiney", "Destini", "Destiny", "Devan", "Devante", "Deven", "Devin", "Devon", "Devonte", "Devyn", "Dewayne", "Dewitt", "Dexter", "Diamond", "Diana", "Dianna", "Diego", "Dillan", "Dillon", "Dimitri", "Dina", "Dino", "Dion", "Dixie", "Dock", "Dolly", "Dolores", "Domenic", "Domenica", "Domenick", "Domenico", "Domingo", "Dominic", "Dominique", "Don", "Donald", "Donato", "Donavon", "Donna", "Donnell", "Donnie", "Donny", "Dora", "Dorcas", "Dorian", "Doris", "Dorothea", "Dorothy", "Dorris", "Dortha", "Dorthy", "Doug", "Douglas", "Dovie", "Doyle", "Drake", "Drew", "Duane", "Dudley", "Dulce", "Duncan", "Durward", "Dustin", "Dusty", "Dwight", "Dylan", "Earl", "Earlene", "Earline", "Earnest", "Earnestine", "Easter", "Easton", "Ebba", "Ebony", "Ed", "Eda", "Edd", "Eddie", "Eden", "Edgar", "Edgardo", "Edison", "Edmond", "Edmund", "Edna", "Eduardo", "Edward", "Edwardo", "Edwin", "Edwina", "Edyth", "Edythe", "Effie", "Efrain", "Efren", "Eileen", "Einar", "Eino", "Eladio", "Elaina", "Elbert", "Elda", "Eldon", "Eldora", "Eldred", "Eldridge", "Eleanora", "Eleanore", "Eleazar", "Electa", "Elena", "Elenor", "Elenora", "Eleonore", "Elfrieda", "Eli", "Elian", "Eliane", "Elias", "Eliezer", "Elijah", "Elinor", "Elinore", "Elisa", "Elisabeth", "Elise", "Eliseo", "Elisha", "Elissa", "Eliza", "Elizabeth", "Ella", "Ellen", "Ellie", "Elliot", "Elliott", "Ellis", "Ellsworth", "Elmer", "Elmira", "Elmo", "Elmore", "Elna", "Elnora", "Elody", "Eloisa", "Eloise", "Elouise", "Eloy", "Elroy", "Elsa", "Else", "Elsie", "Elta", "Elton", "Elva", "Elvera", "Elvie", "Elvis", "Elwin", "Elwyn", "Elyse", "Elyssa", "Elza", "Emanuel", "Emelia", "Emelie", "Emely", "Emerald", "Emerson", "Emery", "Emie", "Emil", "Emile", "Emilia", "Emiliano", "Emilie", "Emilio", "Emily", "Emma", "Emmalee", "Emmanuel", "Emmanuelle", "Emmet", "Emmett", "Emmie", "Emmitt", "Emmy", "Emory", "Ena", "Enid", "Enoch", "Enola", "Enos", "Enrico", "Enrique", "Ephraim", "Era", "Eriberto", "Eric", "Erica", "Erich", "Erick", "Ericka", "Erik", "Erika", "Erin", "Erling", "Erna", "Ernest", "Ernestina", "Ernestine", "Ernesto", "Ernie", "Ervin", "Erwin", "Eryn", "Esmeralda", "Esperanza", "Esta", "Esteban", "Estefania", "Estel", "Estell", "Estella", "Estelle", "Estevan", "Esther", "Estrella", "Etha", "Ethan", "Ethel", "Ethelyn", "Ethyl", "Ettie", "Eudora", "Eugene", "Eugenia", "Eula", "Eulah", "Eulalia", "Euna", "Eunice", "Eusebio", "Eva", "Evalyn", "Evan", "Evangeline", "Evans", "Eve", "Eveline", "Evelyn", "Everardo", "Everett", "Everette", "Evert", "Evie", "Ewald", "Ewell", "Ezekiel", "Ezequiel", "Ezra", "Fabian", "Fabiola", "Fae", "Fannie", "Fanny", "Fatima", "Faustino", "Fausto", "Favian", "Fay", "Faye", "Federico", "Felicia", "Felicita", "Felicity", "Felipa", "Felipe", "Felix", "Felton", "Fermin", "Fern", "Fernando", "Ferne", "Fidel", "Filiberto", "Filomena", "Finn", "Fiona", "Flavie", "Flavio", "Fleta", "Fletcher", "Flo", "Florence", "Florencio", "Florian", "Florida", "Florine", "Flossie", "Floy", "Floyd", "Ford", "Forest", "Forrest", "Foster", "Frances", "Francesca", "Francesco", "Francis", "Francisca", "Francisco", "Franco", "Frank", "Frankie", "Franz", "Fred", "Freda", "Freddie", "Freddy", "Frederic", "Frederick", "Frederik", "Frederique", "Fredrick", "Fredy", "Freeda", "Freeman", "Freida", "Frida", "Frieda", "Friedrich", "Fritz", "Furman", "Gabe", "Gabriel", "Gabriella", "Gabrielle", "Gaetano", "Gage", "Gail", "Gardner", "Garett", "Garfield", "Garland", "Garnet", "Garnett", "Garret", "Garrett", "Garrick", "Garrison", "Garry", "Garth", "Gaston", "Gavin", "Gay", "Gayle", "Gaylord", "Gene", "General", "Genesis", "Genevieve", "Gennaro", "Genoveva", "Geo", "Geoffrey", "George", "Georgette", "Georgiana", "Georgianna", "Geovanni", "Geovanny", "Geovany", "Gerald", "Geraldine", "Gerard", "Gerardo", "Gerda", "Gerhard", "Germaine", "German", "Gerry", "Gerson", "Gertrude", "Gia", "Gianni", "Gideon", "Gilbert", "Gilberto", "Gilda", "Giles", "Gillian", "Gina", "Gino", "Giovani", "Giovanna", "Giovanni", "Giovanny", "Gisselle", "Giuseppe", "Gladyce", "Gladys", "Glen", "Glenda", "Glenna", "Glennie", "Gloria", "Godfrey", "Golda", "Golden", "Gonzalo", "Gordon", "Grace", "Gracie", "Graciela", "Grady", "Graham", "Grant", "Granville", "Grayce", "Grayson", "Green", "Greg", "Gregg", "Gregoria", "Gregorio", "Gregory", "Greta", "Gretchen", "Greyson", "Griffin", "Grover", "Guadalupe", "Gudrun", "Guido", "Guillermo", "Guiseppe", "Gunnar", "Gunner", "Gus", "Gussie", "Gust", "Gustave", "Guy", "Gwen", "Gwendolyn", "Hadley", "Hailee", "Hailey", "Hailie", "Hal", "Haleigh", "Haley", "Halie", "Halle", "Hallie", "Hank", "Hanna", "Hannah", "Hans", "Hardy", "Harley", "Harmon", "Harmony", "Harold", "Harrison", "Harry", "Harvey", "Haskell", "Hassan", "Hassie", "Hattie", "Haven", "Hayden", "Haylee", "Hayley", "Haylie", "Hazel", "Hazle", "Heath", "Heather", "Heaven", "Heber", "Hector", "Heidi", "Helen", "Helena", "Helene", "Helga", "Hellen", "Helmer", "Heloise", "Henderson", "Henri", "Henriette", "Henry", "Herbert", "Herman", "Hermann", "Hermina", "Herminia", "Herminio", "Hershel", "Herta", "Hertha", "Hester", "Hettie", "Hilario", "Hilbert", "Hilda", "Hildegard", "Hillard", "Hillary", "Hilma", "Hilton", "Hipolito", "Hiram", "Hobart", "Holden", "Hollie", "Hollis", "Holly", "Hope", "Horace", "Horacio", "Hortense", "Hosea", "Houston", "Howard", "Howell", "Hoyt", "Hubert", "Hudson", "Hugh", "Hulda", "Humberto", "Hunter", "Hyman", "Ian", "Ibrahim", "Icie", "Ida", "Idell", "Idella", "Ignacio", "Ignatius", "Ike", "Ila", "Ilene", "Iliana", "Ima", "Imani", "Imelda", "Immanuel", "Imogene", "Ines", "Irma", "Irving", "Irwin", "Isaac", "Isabel", "Isabell", "Isabella", "Isabelle", "Isac", "Isadore", "Isai", "Isaiah", "Isaias", "Isidro", "Ismael", "Isobel", "Isom", "Israel", "Issac", "Itzel", "Iva", "Ivah", "Ivory", "Ivy", "Izabella", "Izaiah", "Jabari", "Jace", "Jacey", "Jacinthe", "Jacinto", "Jack", "Jackeline", "Jackie", "Jacklyn", "Jackson", "Jacky", "Jaclyn", "Jacquelyn", "Jacques", "Jacynthe", "Jada", "Jade", "Jaden", "Jadon", "Jadyn", "Jaeden", "Jaida", "Jaiden", "Jailyn", "Jaime", "Jairo", "Jakayla", "Jake", "Jakob", "Jaleel", "Jalen", "Jalon", "Jalyn", "Jamaal", "Jamal", "Jamar", "Jamarcus", "Jamel", "Jameson", "Jamey", "Jamie", "Jamil", "Jamir", "Jamison", "Jammie", "Jan", "Jana", "Janae", "Jane", "Janelle", "Janessa", "Janet", "Janice", "Janick", "Janie", "Janis", "Janiya", "Jannie", "Jany", "Jaquan", "Jaquelin", "Jaqueline", "Jared", "Jaren", "Jarod", "Jaron", "Jarred", "Jarrell", "Jarret", "Jarrett", "Jarrod", "Jarvis", "Jasen", "Jasmin", "Jason", "Jasper", "Jaunita", "Javier", "Javon", "Javonte", "Jay", "Jayce", "Jaycee", "Jayda", "Jayde", "Jayden", "Jaydon", "Jaylan", "Jaylen", "Jaylin", "Jaylon", "Jayme", "Jayne", "Jayson", "Jazlyn", "Jazmin", "Jazmyn", "Jazmyne", "Jean", "Jeanette", "Jeanie", "Jeanne", "Jed", "Jedediah", "Jedidiah", "Jeff", "Jefferey", "Jeffery", "Jeffrey", "Jeffry", "Jena", "Jenifer", "Jennie", "Jennifer", "Jennings", "Jennyfer", "Jensen", "Jerad", "Jerald", "Jeramie", "Jeramy", "Jerel", "Jeremie", "Jeremy", "Jermain", "Jermaine", "Jermey", "Jerod", "Jerome", "Jeromy", "Jerrell", "Jerrod", "Jerrold", "Jerry", "Jess", "Jesse", "Jessica", "Jessie", "Jessika", "Jessy", "Jessyca", "Jesus", "Jett", "Jettie", "Jevon", "Jewel", "Jewell", "Jillian", "Jimmie", "Jimmy", "Jo", "Joan", "Joana", "Joanie", "Joanne", "Joannie", "Joanny", "Joany", "Joaquin", "Jocelyn", "Jodie", "Jody", "Joe", "Joel", "Joelle", "Joesph", "Joey", "Johan", "Johann", "Johanna", "Johathan", "John", "Johnathan", "Johnathon", "Johnnie", "Johnny", "Johnpaul", "Johnson", "Jolie", "Jon", "Jonas", "Jonatan", "Jonathan", "Jonathon", "Jordan", "Jordane", "Jordi", "Jordon", "Jordy", "Jordyn", "Jorge", "Jose", "Josefa", "Josefina", "Joseph", "Josephine", "Josh", "Joshua", "Joshuah", "Josiah", "Josiane", "Josianne", "Josie", "Josue", "Jovan", "Jovani", "Jovanny", "Jovany", "Joy", "Joyce", "Juana", "Juanita", "Judah", "Judd", "Jude", "Judge", "Judson", "Judy", "Jules", "Julia", "Julian", "Juliana", "Julianne", "Julie", "Julien", "Juliet", "Julio", "Julius", "June", "Junior", "Junius", "Justen", "Justice", "Justina", "Justine", "Juston", "Justus", "Justyn", "Juvenal", "Juwan", "Kacey", "Kaci", "Kacie", "Kade", "Kaden", "Kadin", "Kaela", "Kaelyn", "Kaia", "Kailee", "Kailey", "Kailyn", "Kaitlin", "Kaitlyn", "Kale", "Kaleb", "Kaleigh", "Kaley", "Kali", "Kallie", "Kameron", "Kamille", "Kamren", "Kamron", "Kamryn", "Kane", "Kara", "Kareem", "Karelle", "Karen", "Kari", "Kariane", "Karianne", "Karina", "Karine", "Karl", "Karlee", "Karley", "Karli", "Karlie", "Karolann", "Karson", "Kasandra", "Kasey", "Kassandra", "Katarina", "Katelin", "Katelyn", "Katelynn", "Katharina", "Katherine", "Katheryn", "Kathleen", "Kathlyn", "Kathryn", "Kathryne", "Katlyn", "Katlynn", "Katrina", "Katrine", "Kattie", "Kavon", "Kay", "Kaya", "Kaycee", "Kayden", "Kayla", "Kaylah", "Kaylee", "Kayleigh", "Kayley", "Kayli", "Kaylie", "Kaylin", "Keagan", "Keanu", "Keara", "Keaton", "Keegan", "Keeley", "Keely", "Keenan", "Keira", "Keith", "Kellen", "Kelley", "Kelli", "Kellie", "Kelly", "Kelsi", "Kelsie", "Kelton", "Kelvin", "Ken", "Kendall", "Kendra", "Kendrick", "Kenna", "Kennedi", "Kennedy", "Kenneth", "Kennith", "Kenny", "Kenton", "Kenya", "Kenyatta", "Kenyon", "Keon", "Keshaun", "Keshawn", "Keven", "Kevin", "Kevon", "Keyon", "Keyshawn", "Khalid", "Khalil", "Kian", "Kiana", "Kianna", "Kiara", "Kiarra", "Kiel", "Kiera", "Kieran", "Kiley", "Kim", "Kimberly", "King", "Kip", "Kira", "Kirk", "Kirsten", "Kirstin", "Kitty", "Kobe", "Koby", "Kody", "Kolby", "Kole", "Korbin", "Korey", "Kory", "Kraig", "Kris", "Krista", "Kristian", "Kristin", "Kristina", "Kristofer", "Kristoffer", "Kristopher", "Kristy", "Krystal", "Krystel", "Krystina", "Kurt", "Kurtis", "Kyla", "Kyle", "Kylee", "Kyleigh", "Kyler", "Kylie", "Kyra", "Lacey", "Lacy", "Ladarius", "Lafayette", "Laila", "Laisha", "Lamar", "Lambert", "Lamont", "Lance", "Landen", "Lane", "Laney", "Larissa", "Laron", "Larry", "Larue", "Laura", "Laurel", "Lauren", "Laurence", "Lauretta", "Lauriane", "Laurianne", "Laurie", "Laurine", "Laury", "Lauryn", "Lavada", "Lavern", "Laverna", "Laverne", "Lavina", "Lavinia", "Lavon", "Lavonne", "Lawrence", "Lawson", "Layla", "Layne", "Lazaro", "Lea", "Leann", "Leanna", "Leanne", "Leatha", "Leda", "Lee", "Leif", "Leila", "Leilani", "Lela", "Lelah", "Leland", "Lelia", "Lempi", "Lemuel", "Lenna", "Lennie", "Lenny", "Lenora", "Lenore", "Leo", "Leola", "Leon", "Leonard", "Leonardo", "Leone", "Leonel", "Leonie", "Leonor", "Leonora", "Leopold", "Leopoldo", "Leora", "Lera", "Lesley", "Leslie", "Lesly", "Lessie", "Lester", "Leta", "Letha", "Letitia", "Levi", "Lew", "Lewis", "Lexi", "Lexie", "Lexus", "Lia", "Liam", "Liana", "Libbie", "Libby", "Lila", "Lilian", "Liliana", "Liliane", "Lilla", "Lillian", "Lilliana", "Lillie", "Lilly", "Lily", "Lilyan", "Lina", "Lincoln", "Linda", "Lindsay", "Lindsey", "Linnea", "Linnie", "Linwood", "Lionel", "Lisa", "Lisandro", "Lisette", "Litzy", "Liza", "Lizeth", "Lizzie", "Llewellyn", "Lloyd", "Logan", "Lois", "Lola", "Lolita", "Loma", "Lon", "London", "Lonie", "Lonnie", "Lonny", "Lonzo", "Lora", "Loraine", "Loren", "Lorena", "Lorenz", "Lorenza", "Lorenzo", "Lori", "Lorine", "Lorna", "Lottie", "Lou", "Louie", "Louisa", "Lourdes", "Louvenia", "Lowell", "Loy", "Loyal", "Loyce", "Lucas", "Luciano", "Lucie", "Lucienne", "Lucile", "Lucinda", "Lucio", "Lucious", "Lucius", "Lucy", "Ludie", "Ludwig", "Lue", "Luella", "Luigi", "Luis", "Luisa", "Lukas", "Lula", "Lulu", "Luna", "Lupe", "Lura", "Lurline", "Luther", "Luz", "Lyda", "Lydia", "Lyla", "Lynn", "Lyric", "Lysanne", "Mabel", "Mabelle", "Mable", "Mac", "Macey", "Maci", "Macie", "Mack", "Mackenzie", "Macy", "Madaline", "Madalyn", "Maddison", "Madeline", "Madelyn", "Madelynn", "Madge", "Madie", "Madilyn", "Madisen", "Madison", "Madisyn", "Madonna", "Madyson", "Mae", "Maegan", "Maeve", "Mafalda", "Magali", "Magdalen", "Magdalena", "Maggie", "Magnolia", "Magnus", "Maia", "Maida", "Maiya", "Major", "Makayla", "Makenna", "Makenzie", "Malachi", "Malcolm", "Malika", "Malinda", "Mallie", "Mallory", "Malvina", "Mandy", "Manley", "Manuel", "Manuela", "Mara", "Marc", "Marcel", "Marcelina", "Marcelino", "Marcella", "Marcelle", "Marcellus", "Marcelo", "Marcia", "Marco", "Marcos", "Marcus", "Margaret", "Margarete", "Margarett", "Margaretta", "Margarette", "Margarita", "Marge", "Margie", "Margot", "Margret", "Marguerite", "Maria", "Mariah", "Mariam", "Marian", "Mariana", "Mariane", "Marianna", "Marianne", "Mariano", "Maribel", "Marie", "Mariela", "Marielle", "Marietta", "Marilie", "Marilou", "Marilyne", "Marina", "Mario", "Marion", "Marisa", "Marisol", "Maritza", "Marjolaine", "Marjorie", "Marjory", "Mark", "Markus", "Marlee", "Marlen", "Marlene", "Marley", "Marlin", "Marlon", "Marques", "Marquis", "Marquise", "Marshall", "Marta", "Martin", "Martina", "Martine", "Marty", "Marvin", "Mary", "Maryam", "Maryjane", "Maryse", "Mason", "Mateo", "Mathew", "Mathias", "Mathilde", "Matilda", "Matilde", "Matt", "Matteo", "Mattie", "Maud", "Maude", "Maudie", "Maureen", "Maurice", "Mauricio", "Maurine", "Maverick", "Mavis", "Max", "Maxie", "Maxime", "Maximilian", "Maximillia", "Maximillian", "Maximo", "Maximus", "Maxine", "Maxwell", "May", "Maya", "Maybell", "Maybelle", "Maye", "Maymie", "Maynard", "Mayra", "Mazie", "Mckayla", "Mckenna", "Mckenzie", "Meagan", "Meaghan", "Meda", "Megane", "Meggie", "Meghan", "Mekhi", "Melany", "Melba", "Melisa", "Melissa", "Mellie", "Melody", "Melvin", "Melvina", "Melyna", "Melyssa", "Mercedes", "Meredith", "Merl", "Merle", "Merlin", "Merritt", "Mertie", "Mervin", "Meta", "Mia", "Micaela", "Micah", "Michael", "Michaela", "Michale", "Micheal", "Michel", "Michele", "Michelle", "Miguel", "Mikayla", "Mike", "Mikel", "Milan", "Miles", "Milford", "Miller", "Millie", "Milo", "Milton", "Mina", "Minerva", "Minnie", "Miracle", "Mireille", "Mireya", "Misael", "Missouri", "Misty", "Mitchel", "Mitchell", "Mittie", "Modesta", "Modesto", "Mohamed", "Mohammad", "Mohammed", "Moises", "Mollie", "Molly", "Mona", "Monica", "Monique", "Monroe", "Monserrat", "Monserrate", "Montana", "Monte", "Monty", "Morgan", "Moriah", "Morris", "Mortimer", "Morton", "Mose", "Moses", "Moshe", "Mossie", "Mozell", "Mozelle", "Muhammad", "Muriel", "Murl", "Murphy", "Murray", "Mustafa", "Mya", "Myah", "Mylene", "Myles", "Myra", "Myriam", "Myrl", "Myrna", "Myron", "Myrtice", "Myrtie", "Myrtis", "Myrtle", "Nadia", "Nakia", "Name", "Nannie", "Naomi", "Naomie", "Napoleon", "Narciso", "Nash", "Nasir", "Nat", "Natalia", "Natalie", "Natasha", "Nathan", "Nathanael", "Nathanial", "Nathaniel", "Nathen", "Nayeli", "Neal", "Ned", "Nedra", "Neha", "Neil", "Nelda", "Nella", "Nelle", "Nellie", "Nels", "Nelson", "Neoma", "Nestor", "Nettie", "Neva", "Newell", "Newton", "Nia", "Nicholas", "Nicholaus", "Nichole", "Nick", "Nicklaus", "Nickolas", "Nico", "Nicola", "Nicolas", "Nicole", "Nicolette", "Nigel", "Nikita", "Nikki", "Nikko", "Niko", "Nikolas", "Nils", "Nina", "Noah", "Noble", "Noe", "Noel", "Noelia", "Noemi", "Noemie", "Noemy", "Nola", "Nolan", "Nona", "Nora", "Norbert", "Norberto", "Norene", "Norma", "Norris", "Norval", "Norwood", "Nova", "Novella", "Nya", "Nyah", "Nyasia", "Obie", "Oceane", "Ocie", "Octavia", "Oda", "Odell", "Odessa", "Odie", "Ofelia", "Okey", "Ola", "Olaf", "Ole", "Olen", "Oleta", "Olga", "Olin", "Oliver", "Ollie", "Oma", "Omari", "Omer", "Ona", "Onie", "Opal", "Ophelia", "Ora", "Oral", "Oran", "Oren", "Orie", "Orin", "Orion", "Orland", "Orlando", "Orlo", "Orpha", "Orrin", "Orval", "Orville", "Osbaldo", "Osborne", "Oscar", "Osvaldo", "Oswald", "Oswaldo", "Otha", "Otho", "Otilia", "Otis", "Ottilie", "Ottis", "Otto", "Ova", "Owen", "Ozella", "Pablo", "Paige", "Palma", "Pamela", "Pansy", "Paolo", "Paris", "Parker", "Pascale", "Pasquale", "Pat", "Patience", "Patricia", "Patrick", "Patsy", "Pattie", "Paul", "Paula", "Pauline", "Paxton", "Payton", "Pearl", "Pearlie", "Pearline", "Pedro", "Peggie", "Penelope", "Percival", "Percy", "Perry", "Pete", "Peter", "Petra", "Peyton", "Philip", "Phoebe", "Phyllis", "Pierce", "Pierre", "Pietro", "Pink", "Pinkie", "Piper", "Polly", "Porter", "Precious", "Presley", "Preston", "Price", "Prince", "Princess", "Priscilla", "Providenci", "Prudence", "Queen", "Queenie", "Quentin", "Quincy", "Quinn", "Quinten", "Quinton", "Rachael", "Rachel", "Rachelle", "Rae", "Raegan", "Rafael", "Rafaela", "Raheem", "Rahsaan", "Rahul", "Raina", "Raleigh", "Ralph", "Ramiro", "Ramon", "Ramona", "Randal", "Randall", "Randi", "Randy", "Ransom", "Raoul", "Raphael", "Raphaelle", "Raquel", "Rashad", "Rashawn", "Rasheed", "Raul", "Raven", "Ray", "Raymond", "Raymundo", "Reagan", "Reanna", "Reba", "Rebeca", "Rebecca", "Rebeka", "Rebekah", "Reece", "Reed", "Reese", "Regan", "Reggie", "Reginald", "Reid", "Reilly", "Reina", "Reinhold", "Remington", "Rene", "Renee", "Ressie", "Reta", "Retha", "Retta", "Reuben", "Reva", "Rex", "Rey", "Reyes", "Reymundo", "Reyna", "Reynold", "Rhea", "Rhett", "Rhianna", "Rhiannon", "Rhoda", "Ricardo", "Richard", "Richie", "Richmond", "Rick", "Rickey", "Rickie", "Ricky", "Rico", "Rigoberto", "Riley", "Rita", "River", "Robb", "Robbie", "Robert", "Roberta", "Roberto", "Robin", "Robyn", "Rocio", "Rocky", "Rod", "Roderick", "Rodger", "Rodolfo", "Rodrick", "Rodrigo", "Roel", "Rogelio", "Roger", "Rogers", "Rolando", "Rollin", "Roma", "Romaine", "Roman", "Ron", "Ronaldo", "Ronny", "Roosevelt", "Rory", "Rosa", "Rosalee", "Rosalia", "Rosalind", "Rosalinda", "Rosalyn", "Rosamond", "Rosanna", "Rosario", "Roscoe", "Rose", "Rosella", "Roselyn", "Rosemarie", "Rosemary", "Rosendo", "Rosetta", "Rosie", "Rosina", "Roslyn", "Ross", "Rossie", "Rowan", "Rowena", "Rowland", "Roxane", "Roxanne", "Roy", "Royal", "Royce", "Rozella", "Ruben", "Rubie", "Ruby", "Rubye", "Rudolph", "Rudy", "Rupert", "Russ", "Russel", "Russell", "Rusty", "Ruth", "Ruthe", "Ruthie", "Ryan", "Ryann", "Ryder", "Rylan", "Rylee", "Ryleigh", "Ryley", "Sabina", "Sabrina", "Sabryna", "Sadie", "Sadye", "Sage", "Saige", "Sallie", "Sally", "Salma", "Salvador", "Salvatore", "Sam", "Samanta", "Samantha", "Samara", "Samir", "Sammie", "Sammy", "Samson", "Sandra", "Sandrine", "Sandy", "Sanford", "Santa", "Santiago", "Santina", "Santino", "Santos", "Sarah", "Sarai", "Sarina", "Sasha", "Saul", "Savanah", "Savanna", "Savannah", "Savion", "Scarlett", "Schuyler", "Scot", "Scottie", "Scotty", "Seamus", "Sean", "Sebastian", "Sedrick", "Selena", "Selina", "Selmer", "Serena", "Serenity", "Seth", "Shad", "Shaina", "Shakira", "Shana", "Shane", "Shanel", "Shanelle", "Shania", "Shanie", "Shaniya", "Shanna", "Shannon", "Shanny", "Shanon", "Shany", "Sharon", "Shaun", "Shawn", "Shawna", "Shaylee", "Shayna", "Shayne", "Shea", "Sheila", "Sheldon", "Shemar", "Sheridan", "Sherman", "Sherwood", "Shirley", "Shyann", "Shyanne", "Sibyl", "Sid", "Sidney", "Sienna", "Sierra", "Sigmund", "Sigrid", "Sigurd", "Silas", "Sim", "Simeon", "Simone", "Sincere", "Sister", "Skye", "Skyla", "Skylar", "Sofia", "Soledad", "Solon", "Sonia", "Sonny", "Sonya", "Sophia", "Sophie", "Spencer", "Stacey", "Stacy", "Stan", "Stanford", "Stanley", "Stanton", "Stefan", "Stefanie", "Stella", "Stephan", "Stephania", "Stephanie", "Stephany", "Stephen", "Stephon", "Sterling", "Steve", "Stevie", "Stewart", "Stone", "Stuart", "Summer", "Sunny", "Susan", "Susana", "Susanna", "Susie", "Suzanne", "Sven", "Syble", "Sydnee", "Sydney", "Sydni", "Sydnie", "Sylvan", "Sylvester", "Sylvia", "Tabitha", "Tad", "Talia", "Talon", "Tamara", "Tamia", "Tania", "Tanner", "Tanya", "Tara", "Taryn", "Tate", "Tatum", "Tatyana", "Taurean", "Tavares", "Taya", "Taylor", "Teagan", "Ted", "Telly", "Terence", "Teresa", "Terrance", "Terrell", "Terrence", "Terrill", "Terry", "Tess", "Tessie", "Tevin", "Thad", "Thaddeus", "Thalia", "Thea", "Thelma", "Theo", "Theodora", "Theodore", "Theresa", "Therese", "Theresia", "Theron", "Thomas", "Thora", "Thurman", "Tia", "Tiana", "Tianna", "Tiara", "Tierra", "Tiffany", "Tillman", "Timmothy", "Timmy", "Timothy", "Tina", "Tito", "Titus", "Tobin", "Toby", "Tod", "Tom", "Tomas", "Tomasa", "Tommie", "Toney", "Toni", "Tony", "Torey", "Torrance", "Torrey", "Toy", "Trace", "Tracey", "Tracy", "Travis", "Travon", "Tre", "Tremaine", "Tremayne", "Trent", "Trenton", "Tressa", "Tressie", "Treva", "Trever", "Trevion", "Trevor", "Trey", "Trinity", "Trisha", "Tristian", "Tristin", "Triston", "Troy", "Trudie", "Trycia", "Trystan", "Turner", "Twila", "Tyler", "Tyra", "Tyree", "Tyreek", "Tyrel", "Tyrell", "Tyrese", "Tyrique", "Tyshawn", "Tyson", "Ubaldo", "Ulices", "Ulises", "Una", "Unique", "Urban", "Uriah", "Uriel", "Ursula", "Vada", "Valentin", "Valentina", "Valentine", "Valerie", "Vallie", "Van", "Vance", "Vanessa", "Vaughn", "Veda", "Velda", "Vella", "Velma", "Velva", "Vena", "Verda", "Verdie", "Vergie", "Verla", "Verlie", "Vern", "Verna", "Verner", "Vernice", "Vernie", "Vernon", "Verona", "Veronica", "Vesta", "Vicenta", "Vicente", "Vickie", "Vicky", "Victor", "Victoria", "Vida", "Vidal", "Vilma", "Vince", "Vincent", "Vincenza", "Vincenzo", "Vinnie", "Viola", "Violet", "Violette", "Virgie", "Virgil", "Virginia", "Virginie", "Vita", "Vito", "Viva", "Vivian", "Viviane", "Vivianne", "Vivien", "Vivienne", "Vladimir", "Wade", "Waino", "Waldo", "Walker", "Wallace", "Walter", "Walton", "Wanda", "Ward", "Warren", "Watson", "Wava", "Waylon", "Wayne", "Webster", "Weldon", "Wellington", "Wendell", "Wendy", "Werner", "Westley", "Weston", "Whitney", "Wilber", "Wilbert", "Wilburn", "Wiley", "Wilford", "Wilfred", "Wilfredo", "Wilfrid", "Wilhelm", "Wilhelmine", "Will", "Willa", "Willard", "William", "Willie", "Willis", "Willow", "Willy", "Wilma", "Wilmer", "Wilson", "Wilton", "Winfield", "Winifred", "Winnifred", "Winona", "Winston", "Woodrow", "Wyatt", "Wyman", "Xander", "Xavier", "Xzavier", "Yadira", "Yasmeen", "Yasmin", "Yasmine", "Yazmin", "Yesenia", "Yessenia", "Yolanda", "Yoshiko", "Yvette", "Yvonne", "Zachariah", "Zachary", "Zachery", "Zack", "Zackary", "Zackery", "Zakary", "Zander", "Zane", "Zaria", "Zechariah", "Zelda", "Zella", "Zelma", "Zena", "Zetta", "Zion", "Zita", "Zoe", "Zoey", "Zoie", "Zoila", "Zola", "Zora", "Zula"},
+ "last": {"Abbott", "Abernathy", "Abshire", "Adams", "Altenwerth", "Anderson", "Ankunding", "Armstrong", "Auer", "Aufderhar", "Bahringer", "Bailey", "Balistreri", "Barrows", "Bartell", "Bartoletti", "Barton", "Bashirian", "Batz", "Bauch", "Baumbach", "Bayer", "Beahan", "Beatty", "Bechtelar", "Becker", "Bednar", "Beer", "Beier", "Berge", "Bergnaum", "Bergstrom", "Bernhard", "Bernier", "Bins", "Blanda", "Blick", "Block", "Bode", "Boehm", "Bogan", "Bogisich", "Borer", "Bosco", "Botsford", "Boyer", "Boyle", "Bradtke", "Brakus", "Braun", "Breitenberg", "Brekke", "Brown", "Bruen", "Buckridge", "Carroll", "Carter", "Cartwright", "Casper", "Cassin", "Champlin", "Christiansen", "Cole", "Collier", "Collins", "Conn", "Connelly", "Conroy", "Considine", "Corkery", "Cormier", "Corwin", "Cremin", "Crist", "Crona", "Cronin", "Crooks", "Cruickshank", "Cummerata", "Cummings", "Dach", "Damore", "Daniel", "Dare", "Daugherty", "Davis", "Deckow", "Denesik", "Dibbert", "Dickens", "Dicki", "Dickinson", "Dietrich", "Donnelly", "Dooley", "Douglas", "Doyle", "DuBuque", "Durgan", "Ebert", "Effertz", "Eichmann", "Emard", "Emmerich", "Erdman", "Ernser", "Fadel", "Fahey", "Farrell", "Fay", "Feeney", "Feest", "Feil", "Ferry", "Fisher", "Flatley", "Frami", "Franecki", "Friesen", "Fritsch", "Funk", "Gaylord", "Gerhold", "Gerlach", "Gibson", "Gislason", "Gleason", "Gleichner", "Glover", "Goldner", "Goodwin", "Gorczany", "Gottlieb", "Goyette", "Grady", "Graham", "Grant", "Green", "Greenfelder", "Greenholt", "Grimes", "Gulgowski", "Gusikowski", "Gutkowski", "Gutmann", "Haag", "Hackett", "Hagenes", "Hahn", "Haley", "Halvorson", "Hamill", "Hammes", "Hand", "Hane", "Hansen", "Harber", "Harris", "Hartmann", "Harvey", "Hauck", "Hayes", "Heaney", "Heathcote", "Hegmann", "Heidenreich", "Heller", "Herman", "Hermann", "Hermiston", "Herzog", "Hessel", "Hettinger", "Hickle", "Hilll", "Hills", "Hilpert", "Hintz", "Hirthe", "Hodkiewicz", "Hoeger", "Homenick", "Hoppe", "Howe", "Howell", "Hudson", "Huel", "Huels", "Hyatt", "Jacobi", "Jacobs", "Jacobson", "Jakubowski", "Jaskolski", "Jast", "Jenkins", "Jerde", "Jewess", "Johns", "Johnson", "Johnston", "Jones", "Kassulke", "Kautzer", "Keebler", "Keeling", "Kemmer", "Kerluke", "Kertzmann", "Kessler", "Kiehn", "Kihn", "Kilback", "King", "Kirlin", "Klein", "Kling", "Klocko", "Koch", "Koelpin", "Koepp", "Kohler", "Konopelski", "Koss", "Kovacek", "Kozey", "Krajcik", "Kreiger", "Kris", "Kshlerin", "Kub", "Kuhic", "Kuhlman", "Kuhn", "Kulas", "Kunde", "Kunze", "Kuphal", "Kutch", "Kuvalis", "Labadie", "Lakin", "Lang", "Langosh", "Langworth", "Larkin", "Larson", "Leannon", "Lebsack", "Ledner", "Leffler", "Legros", "Lehner", "Lemke", "Lesch", "Leuschke", "Lind", "Lindgren", "Littel", "Little", "Lockman", "Lowe", "Lubowitz", "Lueilwitz", "Luettgen", "Lynch", "Macejkovic", "Maggio", "Mann", "Mante", "Marks", "Marquardt", "Marvin", "Mayer", "Mayert", "McClure", "McCullough", "McDermott", "McGlynn", "McKenzie", "McLaughlin", "Medhurst", "Mertz", "Metz", "Miller", "Mills", "Mitchell", "Moen", "Mohr", "Monahan", "Moore", "Morar", "Morissette", "Mosciski", "Mraz", "Mueller", "Muller", "Murazik", "Murphy", "Murray", "Nader", "Nicolas", "Nienow", "Nikolaus", "Nitzsche", "Nolan", "Oberbrunner", "Okuneva", "Olson", "Ondricka", "OReilly", "Orn", "Ortiz", "Osinski", "Pacocha", "Padberg", "Pagac", "Parisian", "Parker", "Paucek", "Pfannerstill", "Pfeffer", "Pollich", "Pouros", "Powlowski", "Predovic", "Price", "Prohaska", "Prosacco", "Purdy", "Quigley", "Quitzon", "Rath", "Ratke", "Rau", "Raynor", "Reichel", "Reichert", "Reilly", "Reinger", "Rempel", "Renner", "Reynolds", "Rice", "Rippin", "Ritchie", "Robel", "Roberts", "Rodriguez", "Rogahn", "Rohan", "Rolfson", "Romaguera", "Roob", "Rosenbaum", "Rowe", "Ruecker", "Runolfsdottir", "Runolfsson", "Runte", "Russel", "Rutherford", "Ryan", "Sanford", "Satterfield", "Sauer", "Sawayn", "Schaden", "Schaefer", "Schamberger", "Schiller", "Schimmel", "Schinner", "Schmeler", "Schmidt", "Schmitt", "Schneider", "Schoen", "Schowalter", "Schroeder", "Schulist", "Schultz", "Schumm", "Schuppe", "Schuster", "Senger", "Shanahan", "Shields", "Simonis", "Sipes", "Skiles", "Smith", "Smitham", "Spencer", "Spinka", "Sporer", "Stamm", "Stanton", "Stark", "Stehr", "Steuber", "Stiedemann", "Stokes", "Stoltenberg", "Stracke", "Streich", "Stroman", "Strosin", "Swaniawski", "Swift", "Terry", "Thiel", "Thompson", "Tillman", "Torp", "Torphy", "Towne", "Toy", "Trantow", "Tremblay", "Treutel", "Tromp", "Turcotte", "Turner", "Ullrich", "Upton", "Vandervort", "Veum", "Volkman", "Von", "VonRueden", "Waelchi", "Walker", "Walsh", "Walter", "Ward", "Waters", "Watsica", "Weber", "Wehner", "Weimann", "Weissnat", "Welch", "West", "White", "Wiegand", "Wilderman", "Wilkinson", "Will", "Williamson", "Willms", "Windler", "Wintheiser", "Wisoky", "Wisozk", "Witting", "Wiza", "Wolf", "Wolff", "Wuckert", "Wunsch", "Wyman", "Yost", "Yundt", "Zboncak", "Zemlak", "Ziemann", "Zieme", "Zulauf"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/status_code.go b/vendor/github.com/brianvoe/gofakeit/data/status_code.go
new file mode 100644
index 000000000000..7d78fd995026
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/status_code.go
@@ -0,0 +1,7 @@
+package data
+
+// StatusCodes consists of commonly used HTTP status codes
+var StatusCodes = map[string][]int{
+ "simple": {200, 301, 302, 400, 404, 500},
+ "general": {100, 200, 201, 203, 204, 205, 301, 302, 304, 400, 401, 403, 404, 405, 406, 416, 500, 501, 502, 503, 504},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/data/vehicle.go b/vendor/github.com/brianvoe/gofakeit/data/vehicle.go
new file mode 100644
index 000000000000..3b96728bccad
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/data/vehicle.go
@@ -0,0 +1,10 @@
+package data
+
+// Vehicle Beer consists of various beer information
+var Vehicle = map[string][]string{
+ "vehicle_type": {"Passenger car mini", "Passenger car light", "Passenger car compact", "Passenger car medium", "Passenger car heavy", "Sport utility vehicle", "Pickup truck", "Van"},
+ "fuel_type": {"Gasoline", "Methanol", "Ethanol", "Diesel", "LPG", "CNG", "Electric"},
+ "transmission_type": {"Manual", "Automatic"},
+ "maker": {"Alfa Romeo", "Aston Martin", "Audi", "Bentley", "Benz", "BMW", "Bugatti", "Cadillac", "Chevrolet", "Chrysler", "Citroen", "Corvette", "DAF", "Dacia", "Daewoo", "Daihatsu", "Datsun", "De Lorean", "Dino", "Dodge", "Farboud", "Ferrari", "Fiat", "Ford", "Honda", "Hummer", "Hyundai", "Jaguar", "Jeep", "KIA", "Koenigsegg", "Lada", "Lamborghini", "Lancia", "Land Rover", "Lexus", "Ligier", "Lincoln", "Lotus", "Martini", "Maserati", "Maybach", "Mazda", "McLaren", "Mercedes", "Mercedes-Benz", "Mini", "Mitsubishi", "Nissan", "Noble", "Opel", "Peugeot", "Pontiac", "Porsche", "Renault", "Rolls-Royce", "Rover", "Saab", "Seat", "Skoda", "Smart", "Spyker", "Subaru", "Suzuki", "Toyota", "Tesla", "Vauxhall", "Volkswagen", "Volvo"},
+ "model": {"Db9 Coupe", "Db9 Coupe Manual", "Db9 Volante", "V12 Vanquish S", "V8 Vantage", "A3", "A4", "A4 Avant Quattro", "A4 Cabriolet", "A4 Cabriolet Quattro", "A4 Quattro", "A6", "A6 Avant Quattro", "A6 Quattro", "A8 L", "Gti", "Passat", "S4", "S4 Avant", "S4 Cabriolet", "Tt Coupe", "Tt Roadster", "Bentley Arnage", "Continental Flying Spur", "Continental Gt", " 325ci Convertible", " 325i", " 325xi", " 325xi Sport Wagon", " 330ci Convertible", " 330i", " 330xi", " 525i", " 525xi", " 530i", " 530xi", " 530xi Sport Wagon", " 550i", " 650ci", " 650ci Convertible", " 750li", " 760li", " M3", " M3 Convertible", " M5", " M6", " Mini Cooper", " Mini Cooper Convertible", " Mini Cooper S", " Mini Cooper S Convertible", " X3", " X5", " X5 4.8is", " Z4 3.0 Si Coupe", " Z4 3.0i", " Z4 3.0si", " Z4 M Roadster", "Veyron", "300c/srt-8", "Caravan 2wd", "Charger", "Commander 4wd", "Crossfire Roadster", "Dakota Pickup 2wd", "Dakota Pickup 4wd", "Durango 2wd", "Durango 4wd", "Grand Cherokee 2wd", "Grand Cherokee 4wd", "Liberty/cherokee 2wd", "Liberty/cherokee 4wd", "Pacifica 2wd", "Pacifica Awd", "Pt Cruiser", "Ram 1500 Pickup 2wd", "Ram 1500 Pickup 4wd", "Sebring 4-dr", "Stratus 4-dr", "Town & Country 2wd", "Viper Convertible", "Wrangler/tj 4wd", "F430", "Ferrari 612 Scaglietti", "Ferrari F141", "B4000 4wd", "Crown Victoria Police", "E150 Club Wagon", "E150 Econoline 2wd", "Escape 4wd", "Escape Fwd", "Escape Hybrid 4wd", "Escape Hybrid Fwd", "Expedition 2wd", "Explorer 2wd", "Explorer 4wd", "F150 Ffv 2wd", "F150 Ffv 4wd", "F150 Pickup 2wd", "F150 Pickup 4wd", "Five Hundred Awd", "Focus Fwd", "Focus Station Wag", "Freestar Wagon Fwd", "Freestyle Awd", "Freestyle Fwd", "Grand Marquis", "Gt 2wd", "Ls", "Mark Lt", "Milan", "Monterey Wagon Fwd", "Mountaineer 4wd", "Mustang", "Navigator 2wd", "Ranger Pickup 2wd", "Ranger Pickup 4wd", "Taurus", "Taurus Ethanol Ffv", "Thunderbird", "Town Car", "Zephyr", "B9 Tribeca Awd", "Baja Awd", "Forester Awd", "Impreza Awd", "Impreza Wgn/outback Spt Awd", "Legacy Awd", "Legacy Wagon Awd", "Outback Awd", "Outback Wagon Awd", "9-3 Convertible", "9-3 Sport Sedan", "9-5 Sedan", "C15 Silverado Hybrid 2wd", "C1500 Silverado 2wd", "C1500 Suburban 2wd", "C1500 Tahoe 2wd", "C1500 Yukon 2wd", "Cobalt", "Colorado 2wd", "Colorado 4wd", "Colorado Cab Chassis Inc 2wd", "Colorado Crew Cab 2wd", "Colorado Crew Cab 4wd", "Corvette", "Cts", "Dts", "Envoy 2wd", "Envoy Xl 4wd", "Equinox Awd", "Equinox Fwd", "Escalade 2wd", "Escalade Esv Awd", "G15/25chev Van 2wd Conv", "G1500/2500 Chevy Express 2wd", "G1500/2500 Chevy Van 2wd", "G6", "G6 Gt/gtp Convertible", "Grand Prix", "Gto", "H3 4wd", "Hhr Fwd", "I-280 2wd Ext Cab", "Impala", "K15 Silverado Hybrid 4wd", "K1500 Avalanche 4wd", "K1500 Silverado 4wd", "K1500 Tahoe 4wd", "Lacrosse/allure", "Limousine", "Malibu", "Montana Sv6 Awd", "Monte Carlo", "Rendezvous Awd", "Rendezvous Fwd", "Solstice", "Srx 2wd", "Srx Awd", "Ssr Pickup 2wd", "Sts", "Sts Awd", "Terraza Fwd", "Trailblazer 2wd", "Trailblazer 4wd", "Trailblazer Awd", "Trailblazer Ext 4wd", "Uplander Fwd", "Vue Awd", "Vue Fwd", "Xlr", "Aveo", "Forenza", "Forenza Wagon", "Verona", "Accord", "Accord Hybrid", "Civic", "Civic Hybrid", "Cr-v 4wd", "Element 2wd", "Element 4wd", "Insight", "Mdx 4wd", "Odyssey 2wd", "Pilot 2wd", "Pilot 4wd", "Ridgeline 4wd", "Rl", "Rsx", "S2000", "Tl", "Tsx", "Accent", "Azera", "Elantra", "Santafe 2wd", "Santafe 4wd", "Sonata", "Tiburon", "Tucson 2wd", "Tucson 4wd", "S-type 3.0 Litre", "S-type 4.2 Litre", "S-type R", "Vdp Lwb", "Xj8", "Xk8 Convertible", "Xkr Convertible", "X-type", "X-type Sport Brake", "Amanti", "Optima", "Optima(ms)", "Rio", "Sedona", "Sorento 2wd", "Sorento 4wd", "Spectra(ld)", "Sportage 2wd", "Sportage 4wd", "L-140/715 Gallardo", "L-147/148 Murcielago", "Lr3", "Range Rover", "Range Rover Sport", "Elise/exige", "Coupe Cambiocorsa/gt/g-sport", "Quattroporte", "Mazda 3", "Mazda 5", "Mazda 6", "Mazda 6 Sport Wagon", "Mazda Rx-8", "Mpv", "Mx-5", "C230", "C280", "C280 4matic", "C350", "C350 4matic", "C55 Amg", "Cl65 Amg", "Clk350", "Clk350 (cabriolet)", "Clk55 Amg (cabriolet)", "Cls500", "Cls55 Amg", "E320 Cdi", "E350", "E350 (wagon)", "E350 4matic", "E350 4matic (wagon)", "E500", "E55 Amg", "E55 Amg (wagon)", "Maybach 57s", "Maybach 62", "Ml350", "Ml500", "R350", "R500", "S350", "S430", "Sl500", "Sl600", "Sl65 Amg", "Slk280", "Slk350", "Slr", "Eclipse", "Endeavor 2wd", "Endeavor 4wd", "Galant", "Lancer", "Lancer Evolution", "Lancer Sportback", "Montero", "Outlander 2wd", "Outlander 4wd", "Vibe", "350z", "350z Roadster", "Altima", "Armada 2wd", "Armada 4wd", "Frontier 2wd", "Frontier V6-2wd", "Frontier V6-4wd", "Fx35 Awd", "Fx35 Rwd", "Fx45 Awd", "G35", "M35", "M35x", "M45", "Maxima", "Murano Awd", "Murano Fwd", "Pathfinder 2wd", "Pathfinder 4wd", "Q45", "Q45 Sport", "Quest", "Qx56 4wd", "Sentra", "Titan 2wd", "Titan 4wd", "Xterra 2wd", "Xterra 4wd", "Boxster", "Boxster S", "Carrera 2 Coupe", "Cayenne", "Cayenne S", "Cayenne Turbo", "Cayman S", "Phantom", "F150 Supercrew 4wd", "C8 Spyder", "Aerio", "Aerio Sx", "Aerio Sx Awd", "Grand Vitara Xl-7", "Grand Vitara Xl-7 4wd", "Grand Vitara Xv6", "Grand Vitara Xv6 Awd", "4runner 2wd", "4runner 4wd", "Avalon", "Camry", "Camry Solara", "Camry Solara Convertible", "Corolla", "Corolla Matrix", "Es 330", "Gs 300 4wd", "Gs 300/gs 430", "Gx 470", "Highlander 2wd", "Highlander 4wd", "Highlander Hybrid 2wd", "Highlander Hybrid 4wd", "Is 250", "Is 250 Awd", "Is 350", "Ls 430", "Lx 470", "Prius", "Rav4 2wd", "Rav4 4wd", "Rx 330 2wd", "Rx 330 4wd", "Rx 400h 4wd", "Sc 430", "Scion Tc", "Scion Xa", "Scion Xb", "Sequoia 2wd", "Sequoia 4wd", "Sienna 2wd", "Sienna 4wd", "Toyota Tacoma 2wd", "Toyota Tacoma 4wd", "Toyota Tundra 2wd", "Toyota Tundra 4wd", "Yaris", "A3 Quattro", "Golf", "Jetta", "New Beetle", "New Beetle Convertible", "Passat Wagon 4motion", "Phaeton", "Rabbit", "Touareg", "Tt Coupe Quattro", "Tt Roadster Quattro", "C70 Convertible", "S40 Awd", "S40 Fwd", "S60 Awd", "S60 Fwd", "S60 R Awd", "S80 Fwd", "V50 Awd", "V70 Fwd", "V70 R Awd", "Xc 70 Awd", "Xc 90 Awd", "Xc 90 Fwd"},
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/datetime.go b/vendor/github.com/brianvoe/gofakeit/datetime.go
new file mode 100644
index 000000000000..8c064473d3d8
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/datetime.go
@@ -0,0 +1,77 @@
+package gofakeit
+
+import (
+ "strconv"
+ "time"
+)
+
+// Date will generate a random time.Time struct
+func Date() time.Time {
+ return time.Date(Year(), time.Month(Number(0, 12)), Day(), Hour(), Minute(), Second(), NanoSecond(), time.UTC)
+}
+
+// DateRange will generate a random time.Time struct between a start and end date
+func DateRange(start, end time.Time) time.Time {
+ return time.Unix(0, int64(Number(int(start.UnixNano()), int(end.UnixNano())))).UTC()
+}
+
+// Month will generate a random month string
+func Month() string {
+ return time.Month(Number(1, 12)).String()
+}
+
+// Day will generate a random day between 1 - 31
+func Day() int {
+ return Number(1, 31)
+}
+
+// WeekDay will generate a random weekday string (Monday-Sunday)
+func WeekDay() string {
+ return time.Weekday(Number(0, 6)).String()
+}
+
+// Year will generate a random year between 1900 - current year
+func Year() int {
+ return Number(1900, time.Now().Year())
+}
+
+// Hour will generate a random hour - in military time
+func Hour() int {
+ return Number(0, 23)
+}
+
+// Minute will generate a random minute
+func Minute() int {
+ return Number(0, 59)
+}
+
+// Second will generate a random second
+func Second() int {
+ return Number(0, 59)
+}
+
+// NanoSecond will generate a random nano second
+func NanoSecond() int {
+ return Number(0, 999999999)
+}
+
+// TimeZone will select a random timezone string
+func TimeZone() string {
+ return getRandValue([]string{"timezone", "text"})
+}
+
+// TimeZoneFull will select a random full timezone string
+func TimeZoneFull() string {
+ return getRandValue([]string{"timezone", "full"})
+}
+
+// TimeZoneAbv will select a random timezone abbreviation string
+func TimeZoneAbv() string {
+ return getRandValue([]string{"timezone", "abr"})
+}
+
+// TimeZoneOffset will select a random timezone offset
+func TimeZoneOffset() float32 {
+ value, _ := strconv.ParseFloat(getRandValue([]string{"timezone", "offset"}), 32)
+ return float32(value)
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/doc.go b/vendor/github.com/brianvoe/gofakeit/doc.go
new file mode 100644
index 000000000000..c53335e634f1
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/doc.go
@@ -0,0 +1,10 @@
+/*
+Package gofakeit is a random data generator written in go
+
+Every function has an example and a benchmark
+
+See the full list here https://godoc.org/github.com/brianvoe/gofakeit
+
+80+ Functions!!!
+*/
+package gofakeit
diff --git a/vendor/github.com/brianvoe/gofakeit/faker.go b/vendor/github.com/brianvoe/gofakeit/faker.go
new file mode 100644
index 000000000000..38062d5cdf91
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/faker.go
@@ -0,0 +1,15 @@
+package gofakeit
+
+import (
+ "math/rand"
+ "time"
+)
+
+// Seed random. Setting seed to 0 will use time.Now().UnixNano()
+func Seed(seed int64) {
+ if seed == 0 {
+ rand.Seed(time.Now().UTC().UnixNano())
+ } else {
+ rand.Seed(seed)
+ }
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/file.go b/vendor/github.com/brianvoe/gofakeit/file.go
new file mode 100644
index 000000000000..6c1e8d56cba1
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/file.go
@@ -0,0 +1,11 @@
+package gofakeit
+
+// MimeType will generate a random mime file type
+func MimeType() string {
+ return getRandValue([]string{"file", "mime_type"})
+}
+
+// Extension will generate a random file extension
+func Extension() string {
+ return getRandValue([]string{"file", "extension"})
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/generate.go b/vendor/github.com/brianvoe/gofakeit/generate.go
new file mode 100644
index 000000000000..284eef8bb108
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/generate.go
@@ -0,0 +1,41 @@
+package gofakeit
+
+import (
+ "strings"
+)
+
+// Generate fake information from given string. String should contain {category.subcategory}
+//
+// Ex: {person.first} - random firstname
+//
+// Ex: {person.first}###{person.last}@{person.last}.{internet.domain_suffix} - billy834smith@smith.com
+//
+// Ex: ### - 481 - random numbers
+//
+// Ex: ??? - fda - random letters
+//
+// For a complete list possible categories use the Categories() function.
+func Generate(dataVal string) string {
+ // Identify items between brackets: {person.first}
+ for strings.Count(dataVal, "{") > 0 && strings.Count(dataVal, "}") > 0 {
+ catValue := ""
+ startIndex := strings.Index(dataVal, "{")
+ endIndex := strings.Index(dataVal, "}")
+ replace := dataVal[(startIndex + 1):endIndex]
+ categories := strings.Split(replace, ".")
+
+ if len(categories) >= 2 && dataCheck([]string{categories[0], categories[1]}) {
+ catValue = getRandValue([]string{categories[0], categories[1]})
+ }
+
+ dataVal = strings.Replace(dataVal, "{"+replace+"}", catValue, 1)
+ }
+
+ // Replace # with numbers
+ dataVal = replaceWithNumbers(dataVal)
+
+ // Replace ? with letters
+ dataVal = replaceWithLetters(dataVal)
+
+ return dataVal
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/hacker.go b/vendor/github.com/brianvoe/gofakeit/hacker.go
new file mode 100644
index 000000000000..0ac73b7109f3
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/hacker.go
@@ -0,0 +1,35 @@
+package gofakeit
+
+import "strings"
+
+// HackerPhrase will return a random hacker sentence
+func HackerPhrase() string {
+ words := strings.Split(Generate(getRandValue([]string{"hacker", "phrase"})), " ")
+ words[0] = strings.Title(words[0])
+ return strings.Join(words, " ")
+}
+
+// HackerAbbreviation will return a random hacker abbreviation
+func HackerAbbreviation() string {
+ return getRandValue([]string{"hacker", "abbreviation"})
+}
+
+// HackerAdjective will return a random hacker adjective
+func HackerAdjective() string {
+ return getRandValue([]string{"hacker", "adjective"})
+}
+
+// HackerNoun will return a random hacker noun
+func HackerNoun() string {
+ return getRandValue([]string{"hacker", "noun"})
+}
+
+// HackerVerb will return a random hacker verb
+func HackerVerb() string {
+ return getRandValue([]string{"hacker", "verb"})
+}
+
+// HackerIngverb will return a random hacker ingverb
+func HackerIngverb() string {
+ return getRandValue([]string{"hacker", "ingverb"})
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/hipster.go b/vendor/github.com/brianvoe/gofakeit/hipster.go
new file mode 100644
index 000000000000..3166a9966a13
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/hipster.go
@@ -0,0 +1,20 @@
+package gofakeit
+
+// HipsterWord will return a single hipster word
+func HipsterWord() string {
+ return getRandValue([]string{"hipster", "word"})
+}
+
+// HipsterSentence will generate a random sentence
+func HipsterSentence(wordCount int) string {
+ return sentence(wordCount, HipsterWord)
+}
+
+// HipsterParagraph will generate a random paragraphGenerator
+// Set Paragraph Count
+// Set Sentence Count
+// Set Word Count
+// Set Paragraph Separator
+func HipsterParagraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string {
+ return paragraphGenerator(paragrapOptions{paragraphCount, sentenceCount, wordCount, separator}, HipsterSentence)
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/image.go b/vendor/github.com/brianvoe/gofakeit/image.go
new file mode 100644
index 000000000000..de5a2e6d916c
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/image.go
@@ -0,0 +1,8 @@
+package gofakeit
+
+import "strconv"
+
+// ImageURL will generate a random Image Based Upon Height And Width. https://picsum.photos/
+func ImageURL(width int, height int) string {
+ return "https://picsum.photos/" + strconv.Itoa(width) + "/" + strconv.Itoa(height)
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/internet.go b/vendor/github.com/brianvoe/gofakeit/internet.go
new file mode 100644
index 000000000000..69dd700e5231
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/internet.go
@@ -0,0 +1,55 @@
+package gofakeit
+
+import (
+ "fmt"
+ "math/rand"
+ "strings"
+)
+
+// DomainName will generate a random url domain name
+func DomainName() string {
+ return strings.ToLower(JobDescriptor()+BS()) + "." + DomainSuffix()
+}
+
+// DomainSuffix will generate a random domain suffix
+func DomainSuffix() string {
+ return getRandValue([]string{"internet", "domain_suffix"})
+}
+
+// URL will generate a random url string
+func URL() string {
+ url := "http" + RandString([]string{"s", ""}) + "://www."
+ url += DomainName()
+
+ // Slugs
+ num := Number(1, 4)
+ slug := make([]string, num)
+ for i := 0; i < num; i++ {
+ slug[i] = BS()
+ }
+ url += "/" + strings.ToLower(strings.Join(slug, "/"))
+
+ return url
+}
+
+// HTTPMethod will generate a random http method
+func HTTPMethod() string {
+ return getRandValue([]string{"internet", "http_method"})
+}
+
+// IPv4Address will generate a random version 4 ip address
+func IPv4Address() string {
+ num := func() int { return 2 + rand.Intn(254) }
+ return fmt.Sprintf("%d.%d.%d.%d", num(), num(), num(), num())
+}
+
+// IPv6Address will generate a random version 6 ip address
+func IPv6Address() string {
+ num := 65536
+ return fmt.Sprintf("2001:cafe:%x:%x:%x:%x:%x:%x", rand.Intn(num), rand.Intn(num), rand.Intn(num), rand.Intn(num), rand.Intn(num), rand.Intn(num))
+}
+
+// Username will genrate a random username based upon picking a random lastname and random numbers at the end
+func Username() string {
+ return getRandValue([]string{"person", "last"}) + replaceWithNumbers("####")
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/job.go b/vendor/github.com/brianvoe/gofakeit/job.go
new file mode 100644
index 000000000000..c156bde77243
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/job.go
@@ -0,0 +1,34 @@
+package gofakeit
+
+// JobInfo is a struct of job information
+type JobInfo struct {
+ Company string
+ Title string
+ Descriptor string
+ Level string
+}
+
+// Job will generate a struct with random job information
+func Job() *JobInfo {
+ return &JobInfo{
+ Company: Company(),
+ Title: JobTitle(),
+ Descriptor: JobDescriptor(),
+ Level: JobLevel(),
+ }
+}
+
+// JobTitle will generate a random job title string
+func JobTitle() string {
+ return getRandValue([]string{"job", "title"})
+}
+
+// JobDescriptor will generate a random job descriptor string
+func JobDescriptor() string {
+ return getRandValue([]string{"job", "descriptor"})
+}
+
+// JobLevel will generate a random job level string
+func JobLevel() string {
+ return getRandValue([]string{"job", "level"})
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/log_level.go b/vendor/github.com/brianvoe/gofakeit/log_level.go
new file mode 100644
index 000000000000..bde9bf310588
--- /dev/null
+++ b/vendor/github.com/brianvoe/gofakeit/log_level.go
@@ -0,0 +1,15 @@
+package gofakeit
+
+import (
+ "github.com/brianvoe/gofakeit/data"
+)
+
+// LogLevel will generate a random log level
+// See data/LogLevels for list of available levels
+func LogLevel(logType string) string {
+ if _, ok := data.LogLevels[logType]; ok {
+ return getRandValue([]string{"log_level", logType})
+ }
+
+ return getRandValue([]string{"log_level", "general"})
+}
diff --git a/vendor/github.com/brianvoe/gofakeit/logo.png b/vendor/github.com/brianvoe/gofakeit/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..a97962030afda2145a035db6a01b2f3c256384fa
GIT binary patch
literal 36022
zcmYgXWl$VllpTB+Y;e~QoZ#*RcN;uNaCd_1;I6?5?yey?!2$$#2oOAY1efh^tF~&3
zH#JoM=e%2$5cMXT1Au4+7DE6lJ7zeAZ3_
zKLk)LE?#?7oH^=Uq*{$rZm9CDWU|tGO5@q%(a_S;_kZjzO-@c0;HXMgl9on9Kn$J9
z9Uu|tN^l}w?<8LR73iPq-Y3znz54vY*2m%O4GWyZ#!D%uikQqc8GI!wum1Jr@o+-l5Ol&nnOh)P_TXh66
zAXdgZ5~lRYf_}^284811k!+D%*_tWQX2AV~s~ey^(Bpl{d=dUq6&gl}DCje&8k9%6
zdVt78v3s+G{E4bx7etL{i?R)xsl(4v(-&?g|D5847=nRNi_Gbt*D?MD^aJ(MTE^i7
zVS83qHYyQ6P85^|3Lx))06#?kWq&1er!>brHSG^E@IEiU!3Z7~{{9cn*^0pEZEg
zkj$24)uIyr(t?;ku8bjS`3h=l*PMgm5+!d^g+2d<%CuHaAW+Q4(6M${ksC+it1}%n0f|<8IM*)dOhn2m
ziu;n-6njBK(OvoB=&Fqw^jsSE5+h*hX_<%!e~ZKLs=Him^fm&~j3eYVSpLTei7cld
zz9oE`Ry1t^-7H5O!?DPIh~eY+IJr;@{q3z!}Sq@$CriqeQo(}FG~J~)tr0NPBS
zdk6YqXK%lX1j@)5wsmw=KO{&3-G0*#ipCt0gd$&x;uIXxf_M7ZCPH@wT`9lFpZuVvz8FbnlrrAmWT3$dSVou@VQjIqrX^D>qO2y(1jN!H_A!H2k=s%TSuqtC
za=e;~`@Q_05XA9I)eQn&y8m}$emqBA+}-WSJd{k0D2Q`hccKMyEK3)kU0C={16n!r
z7R>)1`oNBQDJmyH=_LjWY2e*V3ztjyNQ
zDd8_^ZogY54{4-I1wk2HvXp?k%$8ARlSA=0+SyrtZ)3*$x7umSjI}9}zP{)=x$7F5
zn%>m3duXqGY=N(u(If#%#73eWM9dP@+$mr|Y8D}B1r}1iq4wv
zF&wNBZUcgDpTvDcseskFNmW^$Q@6I18eOQ2Pe@oc0XFZ&Gy~k7H*phj+Y459BOuUm
z%E8L|R#`btO@ByMR(91Coz_(Sr()>(F(NGiY6)Rj|0{x2=wE+H=7?cyq@l*}u@Gq3
z4->SF^Ay!TPP}9(X%(%=c-J;J6C@x*#@t8J&|NPBD5Pgp^Vs-!45cY!c@s{gxg;7u`X@E0*S^JI$NjUWiHRTk
z5GRfWGEa$?Qs4T*f_t#9FLO?=k=LM-X-zdkStuluHUX)RheW0G$tJk}t6jy=p<6}X
z_)ioB%%Sbo;AdP1(|eq?b&*KhC4-c-w79`RL;?bW5G#ZHypbX~qSxFN^D*b=&VR43
z2ai1mE+l9dhZBwJGGRhO*YXH?b!`?*Bya*qn3VxE6F~=}m`V#AGgQr*Y0(Y9DcWLV
zwRrigBEN0F012kl`<4SUtR{Xgs
zA3w&Wrs5G05=y}q1tp@2nf!35Y(Axr6nt5Aj9hgbfurO1b7B(6O4-;j3JVMGxC+WC
zW7N2R5%ZVTgwHaKU}eHVCLL(|i)~q4H*9(0FUAYsvrzoi#Pt)ytE(^lj0Ib5(0)m*
zL{m~bNxSHYU>3WX72erfSsA!kSWMg$3_VZ6sM4?CtG+s;wp1-{0q=T_}2`-N=R%@~Ni>iJhGtFxTZ>z$-s&;Lv~B
zgh+#>2WZqx8=(R((=USsxo90F#76?FoXMUDG>i?{{p;zyoynSs
zd7pRaySKk@{w;mHan*6iyZ2-!rD|llnGLXp>)x1Vr|}zwRNlSa{XOEJ%P+Sazah$z
zqANL*x&gbt|471oK}9=F<>dz)CuEuJ-v10jx{2oNElG*r1@c(u`-%$|$OmcKDTUgK
zx!1%qsL68)`l;kY`^pJ-B_zt_6~0to>R7?uP%(hHG&mN1FeXOCJgMZ#v!|
z9cau03Z4;40@;DbyFWmPX?MIM+1!kopuWDo{#pCFX4_3@?%D#q*e$t87CgDSMzlKf
zU<68+-9kn*0i)s$givcSMRybgfn(XiXJeE`1*58~^&8E^naX@#o^ZDRVe>!y2)MFS
z5f8*Bs_Uj#O`zqH{{_#W_y;1A+49+ZLo#LE-FHvg%1g0OrW2KjT>jJdkR3jKIP`E4
z%*o_=sQ&U0xOYJO{@~F=i;9)xZJ!VdxAKrI
z6^2^>}HigY!_Xpl=qJ=mk4D!&yV6e)nI!gDy
z4cL^sxQmPUjgG5YB$(10hg;7-S)hY(?eFyCp3MiJ8Sw%tter|l##2!%w98c#v&H3r
zT5U}*=uz-XBAfkoNV_H_*Hn;+H%u#by`dV3@$0A%KR>^Xlf39L!%j7fwIN^F
zs~Xs9&v;NZklLQ_4o81HbW?5onL07&HbOgdm>5T!XhKpv9e3>QOOw`D{`e*#b+H8W
zTo;#(P5BBfZ9JNgHFL-wD^15?p3Ar+fdBb&{^IM|%}C1&B587QB-AgMe`_6G&q24|
zIHu!=A4ZLC-3H<|=DTj778g6+3I<_eVSz%z1`!|qH=R54#mq>EwmSc^k;ES$kY(B<
zhCP>;7u0qa;*%{k+Al(C4gL`6)*1RD_sjfmHUCZ}m!HtO1-&q+3r%cO?6Py{1obUK
zFk*8t2QOdSeal^tT2^G+`(a4!yDvn*qaBeoisI(R5fd_Uk{;s%3q)DbDcM*`FJw)*
z4v!o*sk34EdC=CjHjB|j%ErTk-PuLzpV!kINT0B2*mU^$_yiLC2MycoNtbF3qF^<;
zR7e_SaT=(9rC}NwkuO>hvZ{@@%qSCA&?zF41nX#P4CGnx0F5+f7ESc~+a8**T@=?a
zP~z@DBO7>Vq{BKWWjZwebhl}574W7ZOcS;YRTo`89IJU?)h7H9)*u7VNL(H2F2j5Rb
z3>RRuuro?a%Uz-(`AEUdphFG52ypXV-g2L*7(`9?7|cRO2hXG!vKWZ*U%un{NxBD3
zSOi-HjiS^TEwh{};CC9FAZCJ)t=-7!-LU2T5MYfiVkFfNOBLe4&FL&BznYvJy>)OP
z_defvZvVV0kCYcsOa+SVS4N=gZo1m;pME-OIjEVt
z=DbFd;Ca2lf@(jZ2OavAhQJcTtZEkUZTF?W82ENDck3i_eHKYioiR@=-}Rs8;O;LJ
z{6+3p!K3(zRAR9}CtK1jni;fF#xwvVr8mOo=njk3gddRh4tGP-5ko=13L5}P7il<_
zq+jpf#AfL=(-$>ca}(8fA|KXuW5OlX4@GmPnTx$-c|v{idM*Y1x81G>&rU;2YAAas
z>yFMGBbLuVLycxa&*{8U=(H>CP9h+PCa)yy`0K$zW+a!}S&7#+A**hP`4}6hFb{%b
zph+;+j9GA~X=DTxebUI`QmyUnpPHH)^3vANB4=&kG+>}rDa$Oah6rYk`Vt#Q!t{)B
zTBfjJ>Q@1Sv!?5G@a=i0i`~Uw33$jfh9+;n8I(5my<*Pe!)HKOyy}h`s422<$S&N?LPKZ!;q!{DP=&)WeVevSkl2^isOc*ip>4PP!
zN$>$K#YT^qu4cJ}6nYqfTnQMc>NR=84(}A^6cH15N7CiWt67%n1O1spuyzX?f#`6@
zP1|Y7hO49Rp95D+V=xC?EKAtS{wuTEz#mZ=F41o}D&_Hs7gs))gBa5mQ#pJq-FL5$
z5eKBQVY@NP1Fx5Zuxjc%>rJT|2fqJn>|^~c{J6WqHIlE+`bZb5T_{t`rtsJsT?n-m
zyksu$y+xqd?DKcU3Pq;uX|2WlbMSq*+ZTh=<%g(GkICug7h?HDs)43RR6&YFt{rj*
zCnv14jnB$5!e!oSvV(?A?@5`gstqzJC^4QZfC^b9PY}1o`n${`9NqP=@b;D8pfzb(
zGClfp2(|M^45}nF@j@Jv1~jz_{Geb^kp>i@kNQ~>gwTgGpaIr_;a;LiN&y|_y>&m`
z>w>T%sHELomt4Fms(s63GT;f
zSVxHjVMifRC5ap)CnTxUxqbf~`pT2&->LY|xSm9NGhV#DMUKokE!S?4zX?Ln{mn-j
z<)YdzW&V9ZuLGRXqzB9%
zVO0zhsai>j108&&^M~HTFA?0C#)J^q4C47!cl4qjJLBuOJ-<*UgHvYUGo5Dpf9?Abh
zNl6dNfEf+{Q68M@;xmWIixvO+y7Sk_hT%Bk@WVSpkS}8g4$u*a9@#oXRI|{)k7mCs
zwvj-NHUj6+bfv+@*l2k@J;2L-_C)~_lN1z=@5hej?NrlHSFev@MvkzmSai)4yS6#A
zlEWtplaFuwFV+K===Dvd5+RU{EU6&x+01XEkmzE_$k}LtrI99SKEt7nY#1L~h}Gl;
zdz8)Hn1M|dOeFuu&-{kH@Yr40`uZOWD#fUomX|CV_%2JUD^lzJ4U-cZ(r8K~(M!J#
z|5~8447jGaJIl%0`4B-xaQ44o=Gopcxa&Qf*DTCUQ(N29e$~Uqyms|Pcx`6&2V~Y(
z#!XS^R&$|C2Nq$*31<}k$x#mOw?U^ZJX2R_!)INWIvyqPN!02p@s^ggfLVQh@Udti
z*sLh;MwZW6SEDeWD?0_dbp3DbzRk*23mc^hq1koD5nHP0j{fP_XESCJZ*a`Xgs1
zrvB9v|K!|xtoB+AAM$SqDx%;IR}vaY17AX!5<4Bs-p*3YgwaGAN>c=cSgA2tjAf}Fw
zScC*bg_TXji9e`3y;@IX_%iyidIekNs>?|w=zZmmVMu5psVae=Cuk$4M+
zlfDIKtArGWY%_xT8`?zO(RaG3TxO+~O!0X8Mv
zgD2q`Ih<|6RxrS90l+HasFJ)X5Fyg~oR|pagNXtBcwdH_F;|Wbn|$hB
zW+eRI>9t{N7te#pc!WfLM_Wtk;b%93bbqOB3GRF&
z>LmYl5*u}MP}yATUgDg7o`gav4bxi^PDfKBw9MlNHeuz-W;Q`-NpysvW>DGo{WvH7
zc;8~-#v)S;o@8da0(oR%cyVN5N^$ikrQ6ReDgAn|G7URHb$$I*Ix4zIkf&{07#t|G
z_>tIEnd-7YX{(~rN!*S^sE@C5ebS*?nA=7vSZw+WI(n0Wb2I6hO5|_u&|L$qiFvzv
zORh7^6+##lTXyXF64688UwB5{Il&Y@;h++!kb(Xo8ZZ=@iee(nV)YV?11XNQ3z-vt
z6>%M>QNbuOs?+zVHR}+YN;t|b=-P>^)*-JwlqxNiU_*>8m|=x((t%(9!+DHpor)A{}&wDU}6`guwBpRcEE&%vJ)9I1J@el`~
zVFOTUTGCXu2p7iYz39Iaua?#VDB$KAuy;s_yHSCgLcS=r4#MKUb_kJgi9=fCM(9g#
zy}QCoxrW-jvhNKNsn)0&k?m!6qs65Lp(H-o)-AY10Mw}oAD{^#)`xOx6d@{g6D2Ez
zFqb9oEyxRN^xz^X+9ndRfg6JnIx=|BI^-&j(Q_QFPeG!>y3>IkbZ9!$nLIbc*
zy7FBpT8{PhWdUXQ8ER&PEw9B|UJK{L>2PHMKZf+idyBJONEk>lldtt_nv2%8SjE7V
zv(l-=S39p_+YlzQ_smH5@>lpYnZAUdlIc-^d-ebDEte-EfZyPEHav(r6PW8CIiFU88=`W!jBdPB3gY>IT5{yjJdZMpowX@eyE}+kZM6pL*I>o`0-G
zw}XIUxAB&zP+d_FwP47{b#j#59t{*RVwnS#sj~BXJ=9XxaW<&eBr)gDt@O4_kSN4w;m6@Ja7;5;A5yA`we7
zUlOJ25jm;t$P8_nCgau1@Pjn33FCaDnhfWb;w+VK5Xp4)v}L^dYFd>dzQlQjnuW*v
z0X#wmwFvd^8x4GA-gK3CVe@C9L;I2IXW_M#+xpfrj6DjG-(B9skX2i{3mL{7b4T88LF
zepd>NM2r3w3X(`5sS;N!4K!C!na31If3y2z#+d`0u>WulN#@W}_&O`S;_U{Z-j0V}
z;2Yz3IxXKQ+F3-xon4hIPF#x7;ooUiziV~)*hFSY`zuxcIY9_yLnQ#f`~(1*;X|t!
z@-7?{Jz_^^XOR=fK&u`xx*xf`8@YTEV^v{>ssk`_icpZXTA1lhk+tNvW(bKO5z1$8
zg1uGT{7p+R6jSNw^juc2!(t`TH>W5zFS$_%GCiK`R-oCB6Iy$ZpIlw7K?sz-q_3^4
zn3&n+W%rTOGhv@POG!{7UUTM7nHgYHjE3$H-o?K!s~`Eq|9ht6(ghWcQNwT>!Wfe;KxO>
zO=wu6Jcr^h%TX7mhyXjXS}_e
z4e3Uuee)4RypXuLHpHBS
zBtZ^9p3RQutZx4`1S2(!A~h_B|0sC#_}FV&)Y9?_g*KtLY=9G4+nyzY^2^Nw&rYmV
z=i}h(eFQ~&VV_$cL$q-YHaWHPndWZ_sc?seJ362XGjB6A=QS{cVwwr@m(v~Y@6$3e
z%G;FjYP@?#7^lj@JSnT5WT61Zj8)0glY<3e#`Ekwvyb{mV%M+!9I
zZf5WXkCc?pj$WY%3Sb6)BI~nD3n2%Q;Yaok(jSa?B*FUi4IyvkxL-}&{&xDXbGcKp
za(mz6hVg@ymGx?pXZqiMrc%hphB1IIC#>3RY;H!JH1uJwceo_oZjq?6N1Bra{K@Uu
z@!xs~I$fX_lu1_84-fH
zN-;uFGP0CH-PCPQ0WJR0#%GTv$Gd9%jGX6cXC1HQahAXhzY~G0D2YcZwh3unokmB$
zlfl8y^RG#!h*S#w`OCkHV^{&n_mHsPwmoe3PN8XeGOl|`C5gkEMUmF$9kyVXh@0n@
z+Ua@|DVn!4fXJB5SBk!cK>)(zqT%T+g~8oZ&_>`*5^!FvR=HGlKWsg{6@9$ix;oi<
z!j<@Zv25#PH-OXYffgX{GH8SMx<%sYIBBKBg_pi^tAzJ!BPcDUIe-@%Oh8SQDP3
zk$^0y<%`pw{hAIA(%{iFfWb1H5v`+{wHauk;C^Smw=he%^!O$++vv_@*Yx{ewLBVH
ze7K+QWOo*kf5o8FB^;=od}#VVEr6|_CFz+emS9(~0DsRlCV+nAxby7)U0wO&U;`A*
z`64(t;hndeuFjFhIL4x?mjxRf@Wz(@6%+!ha@tT(|oXHoR`slTAq&YATQA2DTX`9(*TR6omz$
zVWZT+6hI3Oa^24;R9@?IFCF|wV{~=
z5NntTn-cSeS`@#z8(TWUa<}V_G{B`B{OPo^va7?qKXkeaJo+MElXz-HpX8Qc$3WS$!gymXR;7j>X$sVas{d
zG(Q{XNeob-T*1$XstByi%(DhIS%#U@hsr_-71a=hOjt3vl!h%_DrCbY&`wqsg@{f6
z6SK?k@UX>iq2~jEzS!KUhoPrFX$KD;eg3(1u%r(9&Ew^RU4x)G39A3iwH5C3xcWI|
zsg!ELMnxT2f*hM^Z@|jUdrokcP!IpetMs{-kH=hicuN-34MTA?0#v9oRBb|xI+?{?
z&AddaR4|T*nw->hDuZv$@!34;%Nlo|-H306=EkNu`h^bjFc6DBtGyxTs|}g1xa+Zn
zev$zz!(bbp^HVtoGy4A`o)0C|2hI|cwhvJd;=bo3Z9V-G@9w!L6uqA*Yruo18ZedRX1MWC`25#q2Z0kwhS9~+KuS}JN-b9yH+7?;N^^$&W1V5@
zESHdzs=|{f?PU28Q-(#y&-h1U&uhVLPv1{W5YZpKSq<%(ayn|2ioRO1ni@tTb{b4?
z|B6OLLu_(nusO<)-s#ph;*>Ol{*A-p?vrx5I
zyS6ON^g;|-7|jhMtJ2Gsa*cph-Vd6pG~2u1zCLdM%0GaC-#sp|e)Gn-0D7SvUAXHJAIdMeT1
zR8NoC-|4^nOosM!z-s!L>&%rHG1aq&7Pe3Yx}0P^kxy&{Z5O`yVsq35jjC3;`!&ar
zfeV+@xUW7AzgNsD7R)6oEr4Y4IkcB2Te(y<>@k?|{!84t7(HF}P*e2atfovv1=D6p
z1V4Y8E(xkte)F0B*u~;NAhdk-d$VF?1j};o?LqlSk%_;d8xlu-(4jU=lM%@kr~67K
zVblArAyKDyY2-b^u%DG|!+)?&{g{t}F+}vDGQviMS+-{l`6kaQ$
zS7c&hQdlub*E06mvEkjo{3w;$%4e37BkUw5!k#J;GcXz-UWEy}K_DRj6t|z&?~FWajb$XrOl2BYzLOg`
zC^W1PQ7Rk;mCB`!54Gysm!e517~e@|ExzXo;UCbWSm?lpJ9__}rH={%+B)Tb=PzfuN9X5DtB{b@_o}$ovPG2^(T%oQjV!FJysF|-P_>w!*ty3#AF7SQm+~ZKmFrE*!89Q;(bQ;^?CgA{L4jgR~Lk%`%-!>hh)^v
zXD8y&ZG`mC2%mY3T#5Bar0r~YQ<$wC90h~O*5pQC^;H@e&@zl>vKvL6+E^M8t+$`e
zU|s=y4f%QRgS;JOLt`V)r!)blnExKpd?WRcuZGg}
z0xdWSyoH6AmtqvA7bt#)5UCaG`{!WiXx1W!Cm3|OpsjA*O8?!G-!2C(_hWlZcrevg
zMAe2er1N^
z657*90{P_#dCll+bKJ<;B_(D`?F=uH$e3EMiRIJEY_;up!j0e;BwJ_aVSM#R@`}PB
zzfOKDPB1{gK_a&Q+-Vpa$A3A$7f+sRLJNnLVt0ghdKK#+`w27(IR32DLub*6wz
zpH30p&rgQt4ak53jkLh;w<4RqD=LF(0X*0Fo<+B=3)IkaZO^t5Y-S;L+5fcC?dzce
zGWFT_QV|oQSTr^eL;vTGy~8s>RJ3!HlD^CgMhv>2FSJGdn*WW&f1pz*w=pc$T5Q19
zd|jI~j+kTM=dkk6SR{GA85KryKR-WCuV1b4LAw~Yyrd3*2*+m0PQNgqF|u3x=K1o|+hLHMmmd)PZz&dB0u5Ih-j9
z7Yw+)?4+-qjm;7_Aj>Rf%K5%?m42J@wOS@h(T|CzBvg`SS8D3y%HCeTBj`nMp!QX5
zqibVMs5;Pn%A&|>;FZ=!b5N{C)igZ6&%^ooH`^nbN#6*ne;MG{%bqqKpFl1UuRPeS
zKUtxMCM#GgMUeJTDd&LoqbgQKTfXA^H)*-4QBKFfpF+B`zhCbi9z=N<6B53*dU-As
z;O8GHb43YJ7q0?9RYMp^5mF|GclM*l-xyLl?l@~5!es%CjB54CwFzuo0hm!0`A_y-!~KgPzA@L><*>l}Eo)TMobZ8IAJ4b(@xs(h<(%#lC$U~JrN
zBRRjCkdSa!t6u@~CuV}5u}#-qkWBSw1&6ytHkod-xH@wLjce$v@J%E5B+rAT|H?Qm
z^MHOrORzmXxsm1#QC4ZN#
zi^KdvV|;N_v-9}eL?*jQ>O1(qohVhP{&i>OjC>2e5-l?`>kV?r`bl7`aG&0Sh%+Aj_>Ao$O+H45tx5bRleOJ@dtEbqxUZmJWkx3wV(zFXUR
zuDIMNm)$5V%=->Ag+$5QcXKZMqVMH7x*(SnVMiEQbud+mIbwcaL3!w>FO|goY?53+
z`Qe0VuiXYDYhr&+$GX``mz{8vldZ%+PzSgd@zm5*&MT!pM1r);W%iz%7G*t49CPN_
z0Da|%igZBV`|jPQ885h|FL~2_LI4%kO^LnXEYSP>uOZ0@!9$p$s&(&lu2HLVQ805+
zWk!Kn)qqKA^ioBd{VZ!v(D1Iw=-P;w!z7OqzVBM|fxGsaExnitfW($JDuTHX(#GKx
zh0hJ;7JT>{Zln1*i!e2>zrucPs?yeu!|Ys`aR&znrHQ7}(d9|XqcZ^Fxbx4CuK+71
z!#qwLJCqRHX|45m4}ac7?EeJ`)A
zF91ShUKp$ZZ*x?9r_+Uv7&82RjzG#F6M)x*dGowt$aXsV0zn^v<}Z2C-yrkydi`Y7
zYp-ScB;o7eYky(-8ZgF8B6-{;q4&j)x?^MNgcx$l{*)m!zFiuj#Ber7zc+gy#XEQ%
zrO5Hj>()CFCO$3%Ql}{R<__1APT@mP2HYqN4O1KTmF1LzpZ}P;-fT($nv`tcF*k}I
z{ue{>dc}pY2cHO()bueYLO2c5IpRQw(`7(cQl&Z1OUnEqEYat5t3#cKFkyg(KA#tt
z?Xd#X`1kRTdTV5xK8Ut2x>q1KAtyz2zzhV3o<4Z0YkBL!_>L=m)}6KnpPwH7YXZlb
zYqQA!Y2W+v4d0cH3lM`^p5Qr3ZP{l74;f1#?`X9TYaP&RtAS=?6&^_LmFHNz6&@Dz
zlw9sqWMt$X;Rp=2I5nu*zcUn-HhrxPsaY*0ycO}fOYK@c*$VtHc1S%pqXZ^Dlv3uS
zLk%}aJ4&P!>31_8w6l>0Q(&XT#>L@N0r)C0NAE2jaP*#UMutnbni;y#e*17LV1qIZXeB-Z1ZZ-d$6Hp;?pRvF%XpTre@~!pB;tPbdamb3v{g;
z?Rfr`Org(m@+QbkA1e5WlPg9|3x
z9nc6TrtzDPCaM8n(N8qoi80DJjp8rS_{J^JK+TSdluKB&67NQUvofB_4YC=1h_xN$
z9es*;r)i0dh|t>FN+TiRCVuW|?Om(uu(l}X2@;W5E&3ckXi31=Mq5C_6GTYeM|S$GIA=!@So_Y;WT1Bn&yphY66Z
zZ!ab|`r>n)yM(GXj>wjnpi4#JIXELsBv5b_vta4@k{7waqm>R{t<_;ZTEj9L)OxG`
z!ZIQZidvO<1P((0YOz6}(@Z@tVTvu_a!0=UUY?L+m^PK!nJzYsadd7!8vK0E>ga33
zek!!%*mMvSH*Y7Htt;`0>%Da|8=xfwR#*_VuEZa(N#O5`%g+4UIm(;u4XZ+w(JMup
zkTZUfkdZxWHriwIRoxak#@zVPN@JKZ7$d3v@oPBn})vCSFqOM!h|
zSMLs=RNDwy$W4OXJOlJke)!{9e+eW)s0~MX8!_AOP<@$703r5x+#V`LlmXAO@+Z;c
z80GN&SHhZ^@^lp#%uH9EE&+j+@ZjCSD(IU-rH;
zOA~Zq|9qT3ZOLuE-d>mE{*f(RPX1t}`R-MtqdR5Pp}vAv&P1{P&P{eIYz?sP)q6IB
z)?}!(xMC$KGqSI*x0CnvgCuz1hnPR_$5^1Uf5$Bco|d=~jVC)Gq}>U)Xf6D>uXKo^o+)(!AwxJ4vDkDe2{y82v3tytRl%Y6Y;vJgGtnljBThW1tk)LwvPbg_pPpQg2uIKjf
zWIli^i+j;`LzUT}pQ4k1gBo68OgottYEuxT#DkhZi;Ox;z;hz{hB4?eLpfPbBUxWL
zHaTw7Sw6^_N~Q#yHjCV{N>KAd8gz?<@b~ZEZ69rItwA&Jz-aD|uV0xfb~dc8_LnDx
z?sl>nbR9}xYe+UZt@8sGrD0@btP%zA+08p~#L9-Pf(2?N=7>f6uv$y#AB8JDkSVDq
z7Hd0dbn+A#Bla{wxxyaf1UHyg|6ysZ6b_gWlXEeJ%KLOBv_!tufuEZVsf3TC7PpG!
zywgv}Cp=k$g!S8m8xZregnc1Ov9abI>nR^|F~X&y?_#Bkpn&{_BvMLB3c-3hFGlt-
zKk9WHaS06qIyYwcA=Mq_WV_FqXh$brvE~;tEl<9b0ki%tuM=L3yd&qxl!4wJe_p=x
zM+$OorF5yWk003$?8!6-*IMoHGZz>03+yEfbb0@{My1t9*m!gai!}K!*Oxy0k_sLg
zy$F8!GKOj94i-goFyR6ZZJ+5CL{1g(D1d8NSh)g!@#?edj-|K~_$N{_q;XZPGJ+As
z?{6&vI(>Qgdmi3=s?OrKf@HE8^4h|62~qx0JH|h-H8|3_pYb~lBybYZA}2{aXO&ms
zfsvaVW=2Sct}I4*V+vz*NT$29k9%I757DJ}f3&BDl>r264t)QQuM`+z`j5}qq8?vF
z?K_L6R=~NQg1WDNvF70Or)#aSTkmD4fgip+-$hAn6^ifjX5meO(V49`fh{aoEx?3n
z0%y7W=0~_KC!mGT3p~gMUy~=3L=KNE7bHCN@G(*kqX;p7)RFBwKR+Lorw*)S_U{tK
z!r|z-?JjSaF@w^K+mGxF!jmuF7-`u&Zhg6+A{{g|>1ZWGzSenAglHJ1R|uv0iUxka
zekbz5?qHF$^HdrJ1IAU}xzF|A^nTDI8`ft2{pXB{sUehZB7BM5ux<&C(0jle7xN!y
zvpQF{ed~HVSTE-PeviwQM0ep9vxUtiWWY=zdpS17_OIyl?oQN8{c**d;Yi6VK7L32
z3essCE^=DtZ^`bPV;m1%JZh2^A#IU0hv-<28-fWf5m*=fPOK!VwX$8Xwx0gdWZkPR`
ztvA8<66*H$U)U!4-*dgyib^3IW?M>xM|v5c+EdaiDl%I-cf7(Qq}JVk`8QUQ)9$9N
zt-bLe=6}8YZ`pJ{n;BtkZSC^+1V^|zm<
zNr%Nm=2XsugB2zz4lpQkdxdlHooRnAxZts)yl<6>?9&)`WJY$*Oa-u~@Z?}icQvI@
z^o-%0>hfZy%ZG>7S_>?!l~%X3^8WvtGQP`;pa&2(rD!G30%*f$_ah(~Ss@rysEVEY
z89)GA+CKacK$;}(sy-Nx1&0Q7x!|NDcr2#U4;{OgvHyibdHDn?S}*0#W9u`#FNDZP%CYHHX>DDGC^=6qjAu98EFbkS(!)t&N3>JO>=L5jfUAX3uSkV
zbmAl1RK7%T&;h)ywH|xoB-7gXy__yc6Uv+Ku~UmF)+A?PK%_2y1-JfW%S0fpuC6|5
zpbQ?sT7H*wvE}vgyB4nKzlF4wCJQu@R6il&X*W)==ntQL6wh{E5RD=lDPkBB8qTM-
zI(9V?2+G@|(^DnicYwRh_xQWH_T$k4tcGem!e}#+_OHlyXePmeIt&TEj%l{=DaY_(
zOQ{cf0sxnEk@J8SF6HUvmB4!Eb656}^TTysJS!w{*THnrI1b>90Bl^x>vs4j)6q94
zbYoOzbcd%%tWW9I#%;8vP=#j1G)0l;(Tf|}h3Rn?MLeq8v!@*2Y(IbhUBBYt_XBYp
zz3@N81yPYD@}pwrPnS^=pi#H&tOc|9s1J+&$QXi`MVrvnie67B*rAkB0A}@SI3!$fscfNE2Eg3r
zmnmRY3KB{Tf>i3rDnN>AzL1+V5I3{o%(bHgL^{
zh1OZ=Ji>->I{t4*pG9w*4-MrU%Bj*&S$x4aqJ}(};rI7`#sTBQit-D3ra`akk$}+)
zK?>|fim2;${>R-5oVO4~!9TSvBUlDc0LcV^VtQ}4Q5xyn#z>H{%U;V~-Xk7)`ALx!
zD;mO~oN$Eyv}LFySaRSzP)4T
zG_3VGh~QiC-)1tV;s~R)OEw_!Cgz0t5cTwYhi{E$Ly$RRiGX`09?q=K$F;Zf>(}W<
zqayu=K>C~507T9x@67$PS(~AHRk|GcQp4!S`Y%*Q?T!ev$bfJP7^`$k^pFPE_w7WM
zPCwr}Gz`zaiqa}@`27{45F7hRMXoeRkk;}cGJ*VqRjE0<&;$l
zii=1!f43UeHLjmE9`1druEuLQ{65g_`zIwA7()Dsr@D2b-#>tacKLGt7%UiYq%rA;
zgB)%~f|@}{==P+dJaj)Cc(ZdA{G{FDgAUp|SkZu)z>_g=!(YLUBKUiI%-_P~;BDQ`
zRT${MO|0G%K@gMbM6S!PE3)dCSXdJH%B!mdPx4x>78?SclEXQ1%{7%~NwWXpJ_wz^
zR+L_Zvjp7Ke`yT3`_1**XEcd&6acluoQyht9;LhjrgKpyns;uik@J-_0a2e_sb&!Y
z#(|2wLaJM3k%vr!keo>~GBD6_i71#kuGw#7;zDW7rKG2JHK0HYD175t#&;Zy;M+vYITFbcTk?(7|
z+BTOuswM%;|I-2t3yVi{-$&7%_R4UiNuK&2XB
z*dqSrKbMWzi8p<04iV=OhXtcKd@euJC=2^{Ttg1;TIB+iV7E`lu<`HHZ6)2UP+G;2
zrt$(V&F9)~dbHxusL_+56pJalcP#^EODen-uj6Jh%UW!vM~a8zT&KL22ORlrtkzE}
z8%AA%{y8{qb`cgcVY_}-(9q#4^3I5F%*oBY8m_6AHZuCvh?Bb*4`^jI2ngR$4@Ae}
z4U?up{&a}{J#ATo|NArXW=O8W0VSj((nCBHNhUPxP{?t!iK8x;nS~`{+%g8gVEv*S
ziAPO@{d%>oBS37Y)iIbo4vP2|k+GU>Vr*h!h`8Z1Sx3;HT!UGILf?D4?&n_)|KZj?
zkx9GU=Vt#y(pg4D^}TJF8akzh21Qyr1_bE_rMtVkySqWUo1q1yyOfabZjhGl_x#@f
zT6|(LoHH|L@BPGmUytFBLd(<7{nmLB-MQf;d=%RbBk@0(W^|J&-7$H|mrz5m;*uo(V#S6(AjLIOPM
zm{$rZ4Pea^GU>CG44gd|C#gIeS6CPOAwcR7fP66*vUyr%6n{wvLj@pIVq;voK7$tb$!aK?;|s=u@!-gJ
z+D0;!i_P)XWCMA+VNHK
zKjx?O;ZiEh;dJYsuaL}ZPrt9)bCqp}*~PkxF-r_Gg%RGYKTY32VC;jr|0SmuU;|Z?Ukn>^-jS3Il3-C`eeOQW;!aC!#0X
z*?L+^g_?p?G8(SYgaw!PQ;jL8;)<;Pw=U&SR8`PwU}6TD9IMdcg8lO0GqC^B$hyW5vPbt^3A{hOBRMd!(A2JXC^i#Yc`#+6}IzW;>!|O5=lK$!X
zla6UA4me5-IEfv%%)bPSM-v*q27!Q$j#P~VDCJ?*cHi>7z6{_Zhl+e)b^S^?DjXtm
zl;!-RYO}F|3mcsuEaFhrid2xmZ?*dlg9v;?)Vz!mN=aSCW%FcWFEM-ofd|YNA9DIx
zv|8>)CUhf#y})O2ixY?w0Dai-(|Kn&mwiWMX(O7(pK%s{@o8qcueH07?k=T
zD~=q;(^N2g^d}u3(ui_^0A;uY-;E+{v3`4
z&CCedJy-(bH-zs>AtLN?T=6vx0^rErcajiVe{svjhp+ULYsW&)*5(p-!pQK!z5G@M
zXi9Vl;;QuyTb>Z}E#Ck9R5GI@32A9a(Sz^EA@t&u;E>@{3441ME$x-bSr5{io6mK=
zE8LtsT*9nic4k?E8_gR@RnwAv`&BzaKoiP7;%d(wmCkM{Lal<}@E|GK9)Y}GF^DvYHIw6`JTO>wJ1ZC
zi}bXb4pl`Jq}soPUqnMV`a{$yyNqLAN(nm&PpGuA2@=OUZ-l-+S1^&dApwCjuPhm&BUP;vt
zOr1_u%x?@8A^N0{AM?XJKO`|>?>hqWQvXk;Z>Q~l9$p+jivOrF`JMV{X@2F~m%$%C
z!$cFF8wa|0n-C#2m9g9}C|Sf^GDj(BA7$O$e)byG7v|>!wSBJaW3xu-s}nbm_nd4)
zV7P+K=`Z$RI|XG3xJUtyDC#{3#VLus=T9~_l?@F7HHbVfh0`zxTAoSJyvMr;%Vrx|I!=|yk6%mvn_&^@mRUmQSEU#!73tCzO@iY~UI`++y
zQ9uu%Ed$LpE##iPzkqW2DEUA|k#j7A_hXQ{#Pa(U++XbdqAkM{S2LcHa6PoRyPzf}
z)uMD>U-q9qtshM1vavCfsC$oW5KFv%iRtMDuCwdnQ6U$eT^jV1D;8~HArv;XQf^>J
zMNCK-#)!R?HAfLi!#Yclt~ka{RLU4Z!*%w96hNJOukqo@x2eo!>}*cVUqjVbbxil;6wu(ZCeV_?l;BQ0+#t0YT#jdyo9kgrb9=
zW*F9M7;R(gA&7IlUUlaGoClLk~*44WpB!zg##
z4hQ5sKI%%xu+`*Ycumm-681&24~P#EWg^^3p%*{4v->Kt5$|$X$nmCLTkzpIn~@Tz
zehgL&%Xva9y;Y7d_@*{pu%t;9F>{)hgPJnjR~3GCh@S<419B*rXReS}FDv_@@=C}tK3c#+pdmXh%z
zbe^0&jxH8$kEPxeHF^h{wS7U1D#VOTDLQH5b`{cL^|p#Rm}0@9Tiz6qwYEN1#L#XQ
zyRnaM{4IkgPE?mwNgP<5KyH#7wZuVFRM^2O{*RM35pB$h%XsUgLeT&OV@uX`9i*Bd
zmG)Hs6;D-MDCu!-F+~YcQrktYpsGCG_ui0>o_UN*{&ZN2f$3L&5O2LfNjX@hF3FTA
zBq9AaNMxYGlI$=t6|a*^xj3}~O3W}KqoAr87FNA6FpD)I4TqbcB$vfct`^Np^SE-H
zJUO)|o%PcY?-=*n({wswecGXubn4TfZL7dLCK^Fhv?|334PDi^Dg2Vfe|t3{AZq7#
zXll;pU#BX=Z#867N(A-m7J}sk@x~otWfk$5#W*pk9Xvn6c_X0sNBB>%IzLidK*K)i
zpAQMOWQNadGcf$>-;JE>{Sr83jGutV$&1Mqi98qbS2jk2?pW|aRN5YG!kNECuS1Cw
zEVZ-%X(cGrLn)@*SPu5vtamu$B(-rXT$EG=$A*CbmH#
z2&8s|Wo)cN(vpIkG>Qq_;T_BI1jB^Yx}OCvko?L?!QofU9S^ch9u`$o=`SsXl^%R8
z{VFgkH1k+ISeJj4_P2Kr%Y)5pOal*}KqQH}@$*WNfELObAu>%txVFA5y4R6WdeI3p9I?f)(Lpnv)W1uOkL=uR50Tgj85XR=>L}u^Ud$XT
zIh6y7u#YVS;stk$bRq$%+&r8k3M$xHX73pphT@oq@BcDDR)F%kK5k20v%Lv2RRC1!
zg1@(=UX_mJQ&1t)3#T7)Rc-Z1J`P^Pk@SqJ$3cXz>$^*GWnVWlLutXqaLdByD3b@A
z)d{Ac@#<3x>+_&e%S}&AQc2cqk9mbEEonvAA@z8~VH|~#!X#~PndBlgGF|cupMNMD
z1pyPU=sAUSbd_|X&d#_VxssIFLWWxf@E0#w>N_i=>R`lFnxZ}NOi%s^5+^4FA?s`0e0swy7gev!#7r$dOrhAoknuVp2L)8?+x17;e;PjXYcy64rm>TP
z#LYPW>x0d0D>6LLqqQ1J8Q^*Y8&87!)2B?q+NzjwJDWETQzs6ED|;YQ^gisVZ6ad@
zdJt^jAoY8}jkxJ;k=-SxO^+{Qx_^;m&yfdUJf>$~XvhM@k*BudQ|S;z0=O3K3=svq
zQn6@cY0)^SVgX%!0n`Z(${-0+_3ea(Geu9T>Y+&3yhEatW{3L$pu_T^TC9}uWIs|g
zZ9?j%B6BT#uJ!9hCH)U2;gCtOk~Kr-Y$Wt@F&^9)tJIt!C3FZhzdd%4Vfos84iB$`
zcJ3YeLBuLoNM4v&Sx#U-r_&~wfbH#f3Nlp4aD1OXQ;o8xGV2nH|LeY5Z}+%Pl)M-g
zT+bnJma82WD+)MBZ3GX+bte
z1kyH@D|
z!#T@;V)F2{@q?8Wg9=5om0-wu2@C1+i(6m-_uCefbchkA7d?fv7FyD&W
z+=H-t-v1r&(aPiuE6p4frX>U5@y8>?|Nl1L1r(F$7G
zX-)y`vLcmdC^P~G**8v(1eJwYOmqsAlo)zZ@(kN7i&>GUB^8DAX@
z&@V>4CW*hnD8k5X|B9TLUTa4mkNfJ<{^E-Xo#Vt%%fcXKq1?Q>-GZ5N=#!@1^fu2W
zQd=<9#Dt##YCHXu^Ru(r7rZ`SC{Emf-mS=YmE8lv-UGK@lb^->+_xQYfU{WmaPW7&
zVp#B}91AxWstG$`Ra2Y+$X;aF9v>W~bwJhO&4MOD?cFaMl7_*Pujm>nHLwovs1N|Z
z5}*&)ykaMY=L8|swe1h$3K;2O2dT=lU5mw8K0hKbLH5P?
z9afs^BpP*Bx?YaU&(&2_#9nl?w4wl-6(r}9QIwO1he}XTaKt2w-5NGZ;(O5#FF3!e
z#Ze1u%Ra)&je+pmFGzZLxIt;aH7>PTjAzU#jOMUvhj;rAdK?{CxN9C@js<;40t2uL
zE#TNFA5`TEcxHb;3*sOXGJRiDoR-WB=xhRlHs)
z4C%xV<9d3KK>u#dUBpwYM+o{~z8=8TpxMIZO{6J}AIJr#m#Xg|d&PMr>)0P@8&rT`o
zs{JPQg*Tgic;8cnqawlqQe+%zmDU7(eR~)N8LRzr2dHllSH+%qlHX2}3=HMnB23E$_rQmwh^go$w
zuUCA?lO)gPXF`1pcXcGboa)6JQvu?6|EL^&?tlYvfVD`DDgFsmYU9sK&``~XI6?^Qm0YvWo|>z9_MPy+87pu^eK9GCM<7>
z_}`+$uct^M5*SwZ`4gg)=}(E#1yxl{X)o(Dt4er40S6FH2T%*A;)~t4U}0D})R*=l
z{J>8CrIapN1y_|ytq{k=D-AfETLnd54KL6(Fg^-_FGyrdUx+S6%
zpSY{i|pt#vdBbTmX#7~FG6t#1VA3}??fDWL4-y}(>uN3!GTCzHqU>%
z7kBE_d}zlDwZSt>aX{_U==u|_K7?h`al>3WJo$8XZ`jnFpQs!JOv03#pRd<@>-Uk7`W%550aP)c
z`)%dLc3(ydjnDIiLtW
zV8+EdVix|zxqS^lh_?2A%^wLHVqO}8?{p02pC~GCBu%kWFrmlHCl#An*S&b^NN>HM
zW8yp0uG=H*$<+^d?lVHB!A+MiZY=XyIHp(SN~T15W(A?~u@7BLiuw;p)8?RSrcO;y
zzXNGVn8_FWT=XK*F)>AK*3!jk+}f5$B_;hV=#EFvB~;K;jr|9;lFMnQL&oN(E4grS
z?KyVrJw6NDI2I3Y0OfT(>BrP$gg_l}uj|dxRc3?=LhrSA2?>fiJ^lOO`L3&epM|e>
z9qG2UwHbXO(bc?>*ENQJ2d9>R=@%8aM$#%2_AZSpMTj}q0$M03NgH8AS6wb*DgJ${
z32dxDU7x49Kg5jb;%zF&C+Q(dNv%_?EouvA#HclYEt
zB~CbGK_Ym6`RT$_;DzFw%*|{OrMD$uYUg61C}J2cc2R&q$b{~~SR~wuOhl2B4%_-Y
zy||;x2hiR9-fndkM%J_)SU<2i6V4Gou~}U`7Q@=zrwZ8|+IZn&u*_gXOsgcRRLcpS
zt5h#y+eSD!J40Pq9hsOI4upTYoY%JBF3qy*+x?A86*XmUd|B=!71)z+9~W{yme{zj
zwO{Ohba=?B<$X6GsDy3;tc9wBT`eYpewXNM^q3fO9+WbDKi{FiD}VdWkcC{&0f(#h
zH9NpB0>FksrrVII4i6{WWwooh)5q)A0o}{xCK`q5TKf4~7ydc_OY`bB3AG{~C?_YU
z-YkzYh8C{d9k4n?a#bORJbA}NV&lvLA=Al?mvh6^H%r^)EYIpx>}-#r*7VlRL-2So
zc2H0d91tK$^+F4%C>a6Bt)gNgl?MHfn5&u&UPi8rScg*G*{
z90-h}JzAXpUtsucuJ-^=pB}})@lD^(<*v%HAL?9V)gg6p1w+mKLym^qUj_u9E-Nwc)nz?#%^o1CFb%k*~}{C3CZYkX%ht
zR^0Q!R>BNsc#s8Kl3rXr?My3;nm){DT-wU0fiT}@tYs{iwj`a;95znd4>9xhR5*Ml
zFkk~-58I2A1j9>fC796H6R269nVmJM${1{a=vZ?0tFc_xmFNb{(i~MJdk$h{uNu|!
z*EV^{y}OpeK4s1KEjeNT*uaue1yDx~gJD61Mk#74
zhH~T7F*W2^(c?hYWcYJ22t&Bx+ZWFlPn(qsX}hT#MWAui*4|#^(2GnPu$W$neEL^N
z*I3lG@Ci&~^(nIfnRu5L77VARtxYmAGGe6Co4bAY)a$4tVP%bhq=?T;1EQNTgAN+O
zQz0muvkbII&j1^M7bSzK5I6=IA{;Ph6+D+qcxz$CsKGW=XS((lqnz$ET?zDN;MJ&5
zE6hvf+})q65aV|K9G>cham?;oh&1$X5$S5S#;|dJ$<-o6ty9MP$UH^G6xl!FneOp`6aX^Hj$JjDz%heAdO!V+!q2c(n^?X`|n>*1r52ieD#w
z-!g*8&ux>x8%s+*_Nnnp#Kb`~npz?RXS?a$7LeKtdbp2g>RHndf~qi@vJ@8<>%wKhUZu@crcp(QMe&_RQ
z-c!!;%;Pq9y|DI50Uoe))W&)E%#qv^A0EP1M)5&)GYeBX4^}4hyOPWbq7k-?j7;IO
zaIA}TBUp|Ajk)EBIf55SqI>wh^MJfRm*u!uq{PoD$)RI;Wd%uWlu-O~QQoyFuN0$B
zgx&r&JDgMg=~4(if)W{^!x2qI7Zw)6ndf-h)q-#Vx&tBJ`=%cO{ohoY?lScpxj?l@
zI$}k0^8`qGUR$31VylVFKwc3DE2Bgka(~>+S&FBdI}6q4wxZC9lt%*
z4@omhc%ZLk4f)NbsJ5>Z9AEmcrp5p
z-(he0=2#VGQKS63eeFLku~%om6V7*am0jcwJ$ZDYRD8N9KB>D8A4X89
zrQZP$4^VY@a&>qo!ad8yt;6AVqH%~C?IF_9B#&Hgzu1xJtJUqd3ByQDa?HcnK^YS#
zF84aU9QIg#B2^d*%ng8(4vzLs&=Wu)QN-mAW(4h^E2O$-LYBf-EN^Vp_9;-cvvS&;kYZnTMgFCSrx
z$?EpUv%;4j+X!+G>{0bY5`42r{^n^w-1QoXcso^aA2e?%1NQRqR^eZ(~>8t
zVepxIVd2I0b+r|cqz!MvsM*?n+MmdX2HvVSByI&0rtmjAJ`DH#oJ3Y)6G@%L`g_PS
z>7BIGBSBuNrG>S%D6ld5e9`Z2tWmJsh`B)QJzM6DB+yg~>LlkV^0m-(OSldn#h5Dv
zo)v&fX$^Vf5ilO?RO>Eryal#-#T9JX+=)C6Lf2Rf|4rm0xPYaGG`S2OdvflDxtoVv
zUxp{J(QO)&Ca*kRNCgSEeFRB)eSsT1G8MOe4^s0)&O8PnUmo5Owzg&jzRv`i$wo)Vw>(&Oa?QLa+f!bc+0}f_px|`UZBF!uIyhr^X{!u4+
z|3`z>y^Bgid^?0PYOauuz<#YQcW!?E_PFb1`4b6G$2j-gf~}y_ddC3ImI25m&9bZ#
z*9L|ly^zkr-X7RVLRL<{@q=Ko)Jd0kEO)2Hg*);YMzXio&dCCAR?aR2VgYX3r@f
zt=-B&&I9~sfZE`Agj8p6)J*x{3qRm{c&TG+YrFOMIkJaw*15jA8pE#Hdvc7E*ZdtK
zmppzFwRx;svh?IEyfOUNdsv`SA?@1@IB5T$7l@=q!2y#5S$#7m7~2Q-E`2y5Utix5
zjYP#BnXR6Y^WVas9_Ei)_Fl_?XrGsImxBr+v$Pj@$|E1eHdS?W(1$c2z0Rx*GUZUd
zLU|Dp7hH#Y?nMO-6j5I9g8XqH$PtCUM^FO7BnJp7VAxfB`G91^@&NZ*c1%q2XDZ+H
z+5`vD-0+?C^?*Vrmxm)DE;!JiydNa)xL9bXLRU~?p+bN{|0g?+DEyD34?xCq5Bh6t
zq6gumlllgS#1zF86{i9$g@Hd)p2I;=YeS(k-v~Ap>VF@S-`-B**DYvO9m@HZFn@6M
zKq)<)|J{5W8L4|WoMjWChK4`XPTqutLW9E21Y^qUkZprSILLsB8&(Y_~-MHczn+`k3J_8Z-~jG+VCIUg&9
zwUXe1f1CkjEx@9>GYKMy05PM)O|d29p$d)Fa)=+W#V`$}
zeUrd6z!AC6{mhm4Ssw@qV;P297oJH+Ct49I^*s@@<~s!GdcKbpMk6q{uaWQfJ{mE$
zw*jSbP2Q?rnVCl6QP4?k3n0JQZKKK`p;0tJ0ZLqhDCXJWC^O~&zok41T^L)~OC{F`
z-H4n2%D_@X1s^n60=fDJ(0YRyFMCJ78Nd8(?g@x#=8H#QZdgKZU&BoP
zo$}5h^w<0rQ&geQOqc}fJ3tujC19yd0fqJ*T-|&o`7oY^6ZPKs7H8OS>F6NeX%(S7
zs_?ESrp^WXs$+~liIYio;`gr7qzv60Wdx1S)9R?h5D;$r*9wHoQ*>jQU%?_5E>1ig
z>qqK{Ae(50QA=-nl6!grfg02Ayu*IHIxxJ6W}kxeC}6WLr0W54#B=M=$MewblSd4-
z7$O)cs8$VX^jz4(aBfWUpJ!NvDFmpc0tSDa%1jIs3?tJH%mU$`IKHFMQ)6prrH_+2~EqMFg3y1XzrK)H3Y9i4azy
zl*o>O8Q5^mD*`7yQA((qscb4EkSsa4mi&b_mMfg2j!2inV$n*Y7^CZ!NC(ozEcVkL
zaBwNKJeMC|GS`EcfLS{(V1fj(q{SZzvG2ymEr}-xCR+0YAqy2LLST5|^_q$THOd@r
z*>(_#ABGghrQ5tAP94ENL?_)hDZXwV@LDBKd
za`Fnr`O@63j{UDqcd@k^lV|mXj=b~A%wz4SvK61tQb$#Z{CFV~fY+KtQxU
z2T$qz)_#y1dDZWDcZ|_i(>OkbiK1<|L14`21CZ!bt6Of{o08CTSGh>Z+N+Hqv!Re{RfsaKi{9@A|v#XN&Wlx&wx9N
z3hCQ^flhFw5&PDvkoxk`WQ*#()rE2
zTR(~+x$GcWyH|`cHIlB?4$S}7w(U3))q}6%=HMk9`P15j`RqNY+(Tt(I_)!TjAaJ@NxubgSE6!;NHzKs`)(L^(3NT?nve~CEh~_Vc
z<_h?{a?YGpPmq%_Hf<(4I_AOpvh2vvQ8l4dxR-_E?Q?a9j>W5nM0kymp4FKz6Jpy{FP8=1!ZIXD(d1PNDo_bNX$O9!tsP*D7mOHk
z%@dlLrg$#@^)I1nkrhm4Gd-%F;SEw3K?aVE{v}KS;V3phQa?366?l8=ww9F&Gx@%@
z{ch7wZqq6ujqu3ExV$PipwhmZB8n;8xBz*0D^R;8V982!>9AhI?{*L@=t_g9iCVh_
zyH3v*SDD-R^$+z}2yfA^x&y-P&uMySaR3+UE`O60w7#5VtqXI`vj;PqUIq4Y30(#-9=vQnvh;afuY7@H)=Ui_&;VxyP@QX*e^D+k)93DpA2br_(5REDJvpxJ43i}H10!Lc
z#7D|dLz|@h_~OyIMbs4V}uyG(+CJ4~bSC?uzK>E5#V-
zB~z3a~ArURkrAlhsTGCh{QS*LmauH7WU-i#NNMkNe{}!9{TzF$?Y2$wTPG{Vhso^?`e+<2v^4+rF7
z%L{&3`a9iqr>EYI&p6@zb_DfLMd8~)1jgvZ&A&30Ppjukv&fQ0zES=?4oP-<1~y6BPZq>fzD&b&6Nr2zAHCMgtvr
z`Jp@>#Z!jvjScf26xRax=N)%N~rrM44!qN--}OAouI(a5p`X_})EBa%jN
zaTBl>I0(stGos@HH;#L0?K>kT)?ZmX6hnaVSmS1*K%xQ&!KZN0<%)$bu*JzsibfPP
zH3KsN)+{O2xjTC??Hk>qiRX4*<9y4
zs+z+>Cx5>Q;6ID+bx$nYrIaYnvSKm#}a@HB#kn|6Eab!f5l~`PNFXP*t%o_
zU|G4#w)oxyc%s~=yKcvY^Rp9Y$&5)4md{3rc5VA|V>$c;p=umriAW_()iftvscv#l
z^EY9o1$3DgwRb$G^8Dn+=#}~}JnJ536p9m-bXumsjIIx5h8Q2VK(J;FAf(%qfy_I-
z;nImWB=aj_kJYt=pzko-maxCQY5=}!AsrBjwVbbeUz9zaiHn;KK#|2H^e6T(~)OAc3&r{}~_!{^T{kw(yCMNm+owb=X_
z`(qAm41Ir;>DA?WCSUMF3eJV6VUxLdEI;;PvG=#Hao88U7}gQdlu@i%pB5W$jeRbb
z{V&Gny<8fb-eZs55sy*I$j}3ouZv0j*QksJvyU<7G9Rr3a#R(8e)MB;Ysft`{>vjW
z&)dmRiqV3I*hA=s0A{m}BO*=AC-{lWVDh*5J&b&--}qtHMt^XIHwoPZgKR43J70I2
zFURy>!K%v=GqahcG{arlVO$|0JDwU$YCdxs(eC;WJU{1bg+>EAx9~`cVqS#3x#L6u
zAXu0GdMo50+ZjMlxc4oHuE-1_kXk6n&t2mnkh`^$b$9My@;{w$NXLIF!P_ed^|*Hz
z?3;d2RxoH5QRE6RH0X(fRci-r^4{yLr{6Jss@uB%V7|R`vtsnx2#}2|pm4bpai1*f
z_Xks!s6%%9h{J+_)Fs8r<9zPm^0&9W!32+f-?5`PY5qId1;Fi%)I1E}cu2qY6T
zAL`|!Rz;OOXl3xuEZ1cMR*gS_J!9+6>*CS*+h#Nug~5Tkc3}Yh%GDd7HP*K8ilkRA
z+Xg^z!5b7(pYPEd)5mEd3L}pFy=;{X#OViDQwWm(-<;
zMZm?cjoW%W&V9SM^Y;&c|8%PHl(%8@XV&q8ks+!y7fr?z@9t)L<=El2IX