From f593bf75158a49be8b975645aa745c20a7293d04 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 9 May 2019 13:39:14 -0700 Subject: [PATCH 01/12] copy patch from ryantxu --- package.json | 1 + .../dashboard/services/ChangeTracker.ts | 9 ++++++ public/app/features/panel/panel_ctrl.ts | 21 ++++++++++++- public/app/features/panel/panel_directive.ts | 31 +++++++++++++++++-- .../features/panel/partials/general_tab.html | 18 +++++++++++ yarn.lock | 5 +++ 6 files changed, 82 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 69309e2e341b6..daaf7dc146c2c 100644 --- a/package.json +++ b/package.json @@ -198,6 +198,7 @@ "calculate-size": "1.1.1", "classnames": "2.2.6", "clipboard": "2.0.4", + "css-element-queries": "^1.1.1", "d3": "4.13.0", "d3-scale-chromatic": "1.3.3", "eventemitter3": "2.0.3", diff --git a/public/app/features/dashboard/services/ChangeTracker.ts b/public/app/features/dashboard/services/ChangeTracker.ts index 7fe365cd8e004..716b4f79565bc 100644 --- a/public/app/features/dashboard/services/ChangeTracker.ts +++ b/public/app/features/dashboard/services/ChangeTracker.ts @@ -110,6 +110,7 @@ export class ChangeTracker { // ignore iteration property delete dash.iteration; + let hasDynamicHeight = false; dash.panels = _.filter(dash.panels, panel => { if (panel.repeatPanelId) { return false; @@ -124,6 +125,14 @@ export class ChangeTracker { delete panel.legend.sortDesc; } + // ignore all h parameters after a dynamic height + if (panel.dynamicHeight) { + hasDynamicHeight = true; + } + if (hasDynamicHeight) { + delete panel.gridPos.h; + } + return true; }); diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index e81312aa0de25..d2ab4b32f40f6 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -14,7 +14,7 @@ import { calculateInnerPanelHeight, } from 'app/features/dashboard/utils/panel'; -import { GRID_COLUMN_COUNT } from 'app/core/constants'; +import { GRID_COLUMN_COUNT, GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; export class PanelCtrl { panel: any; @@ -63,6 +63,25 @@ export class PanelCtrl { profiler.renderingCompleted(this.panel.id); } + dynamicHeightChanged(height: number): boolean { + const min = this.panel.dynamicHeightMIN || 50; + if (height < min) { + height = min; + } + if (this.panel.dynamicHeightMAX && height > this.panel.dynamicHeightMAX) { + height = this.panel.dynamicHeightMAX; + } + const h = Math.ceil((height + 5) / (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN)) + 1; + if (h !== this.panel.gridPos.h) { + //console.log('Dynamic Height Changed', height, 'new:', h, 'old', this.panel.gridPos.h); + this.panel.gridPos.h = h; + this.events.emit('panel-size-changed'); + this.dashboard.events.emit('row-expanded'); // triggers grid re-layout + return true; + } + return false; + } + refresh() { this.panel.refresh(); } diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 8cfc627b424b6..8922ff16b6843 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -2,6 +2,7 @@ import angular from 'angular'; import $ from 'jquery'; import Drop from 'tether-drop'; import baron from 'baron'; +import ResizeSensor from 'css-element-queries/src/ResizeSensor.js'; const module = angular.module('grafana.directives'); @@ -17,7 +18,7 @@ const panelTemplate = ` - +
@@ -40,6 +41,8 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { const ctrl = scope.ctrl; let infoDrop; let panelScrollbar; + let panelInnerContent; + let panelInnerContentHeight = -1; // the reason for handling these classes this way is for performance // limit the watchers on panels etc @@ -58,6 +61,16 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { ctrl.dashboard.setPanelFocus(0); } + function checkInnerContentHeight() { + if (ctrl.panel.dynamicHeight && panelInnerContent) { + const v = panelInnerContent.outerHeight(true); + if (v !== panelInnerContentHeight) { + panelInnerContentHeight = v; + ctrl.dynamicHeightChanged(panelInnerContentHeight); + } + } + } + function resizeScrollableContent() { if (panelScrollbar) { panelScrollbar.update(); @@ -82,7 +95,21 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { `; const scrollRoot = panelContent; - const scroller = panelContent.find(':first').find(':first'); + let scroller = panelContent.find(':first').find(':first'); + + // Add a div under the scroller and watch for changes + if (ctrl.panel.dynamicHeight) { + $(scroller).wrap('
'); + scroller = panelContent.find(':first'); + + panelInnerContent = $(scroller).find(':first'); + panelInnerContent.removeClass('panel-height-helper'); + panelInnerContent.css('margin-right', '20px'); + panelInnerContent.css('border', '2px solid #F0F'); + + // tslint:disable-next-line + new ResizeSensor(panelInnerContent, checkInnerContentHeight); + } scrollRoot.addClass(scrollRootClass); $(scrollBarHTML).appendTo(scrollRoot); diff --git a/public/app/features/panel/partials/general_tab.html b/public/app/features/panel/partials/general_tab.html index ceae445f3ed88..2573129ff72b9 100644 --- a/public/app/features/panel/partials/general_tab.html +++ b/public/app/features/panel/partials/general_tab.html @@ -52,3 +52,21 @@
+ + +
+
Height
+
+ +
+ Minimum + +
+
+ Maximum + +
+
+
+ + diff --git a/yarn.lock b/yarn.lock index 624f08d556b85..ca2cc324d627b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5367,6 +5367,11 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" +css-element-queries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-element-queries/-/css-element-queries-1.1.1.tgz#129e104ecb277aa6337b7302cbb6d511c2d1633a" + integrity sha512-/PX6Bkk77ShgbOx/mpawHdEvS3PGgy1mmMktcztDPndWdMJxcorcQiivrs+nEljqtBpvNEhAmQky9tQR6FSm8Q== + css-loader@2.1.1, css-loader@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" From 5d29bd118776470c95a80dccf0a99d91dc5f5da1 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 9 May 2019 19:20:39 -0700 Subject: [PATCH 02/12] a commit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 075b1dfd7e2d9..5c393976c9555 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # [Grafana](https://grafana.com) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Go Report Card](https://goreportcard.com/badge/github.com/grafana/grafana)](https://goreportcard.com/report/github.com/grafana/grafana) [![codecov](https://codecov.io/gh/grafana/grafana/branch/master/graph/badge.svg)](https://codecov.io/gh/grafana/grafana) - +xxx [Website](https://grafana.com) | [Twitter](https://twitter.com/grafana) | [Community & Forum](https://community.grafana.com) From 1c739d6bf8153aa19c0cfeccd7a88d1411f6d113 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 9 May 2019 19:22:30 -0700 Subject: [PATCH 03/12] new PR --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c393976c9555..075b1dfd7e2d9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # [Grafana](https://grafana.com) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Go Report Card](https://goreportcard.com/badge/github.com/grafana/grafana)](https://goreportcard.com/report/github.com/grafana/grafana) [![codecov](https://codecov.io/gh/grafana/grafana/branch/master/graph/badge.svg)](https://codecov.io/gh/grafana/grafana) -xxx + [Website](https://grafana.com) | [Twitter](https://twitter.com/grafana) | [Community & Forum](https://community.grafana.com) From d24a1933aa1cd1b08e60364422a1a94505762b72 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 13 May 2019 17:58:14 -0700 Subject: [PATCH 04/12] add aria --- public/app/features/panel/panel_directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 8922ff16b6843..fd1f9f3536e99 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -18,7 +18,7 @@ const panelTemplate = ` - +
From 14212fe5c31ed3ad6ce25218936d10b530844f7b Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Thu, 16 May 2019 11:54:43 -0700 Subject: [PATCH 05/12] remove the border --- public/app/features/panel/panel_directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index fd1f9f3536e99..6fe45d666616c 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -105,7 +105,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { panelInnerContent = $(scroller).find(':first'); panelInnerContent.removeClass('panel-height-helper'); panelInnerContent.css('margin-right', '20px'); - panelInnerContent.css('border', '2px solid #F0F'); + // panelInnerContent.css('border', '2px solid #F0F'); // tslint:disable-next-line new ResizeSensor(panelInnerContent, checkInnerContentHeight); From b54747748ad09c24620155285859e78ed5f5a33c Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 16 May 2019 16:39:50 +0200 Subject: [PATCH 06/12] 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 7db50f75168fe..a62de9ca003ed 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 270342626dc2eeeed0c74450b3678d59159e702d Mon Sep 17 00:00:00 2001 From: Shavonn Brown Date: Thu, 16 May 2019 15:16:05 -0400 Subject: [PATCH 07/12] 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 c20623f8c0ed0..b60755311a020 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 dad49788eb091..315f9b35cd53d 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 aa022c686079b..ea340f38fed0d 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 4a071120222387228fcfedc83ce5971160178672 Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Fri, 17 May 2019 08:35:37 +0300 Subject: [PATCH 08/12] 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 0ecb0938d4f7c..a0f6480433997 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 2d4410ee54898..a40486c313332 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 eea0b9e62f7ab2afeec5e1745dc8288ef890934f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 17 May 2019 10:11:55 +0200 Subject: [PATCH 09/12] 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 54927b8cc91b8..5689f67ee13b9 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 db183a2240751655ce0b59addaf609187e952f47 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Fri, 17 May 2019 12:45:11 +0200 Subject: [PATCH 10/12] 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 7033473a33b3a..0fba2ae6ded47 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 83cce42c7d195..7356fb15c1786 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 78f190a05cb87..18ee70d8ee201 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 225a672ae2e51..ff7fdcb55dec6 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 310f310e6710d..c1157e01c6ae0 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 c5ee8dbb7798d..5af71c29f8df9 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 f0847360cbe36..743294f0e74cd 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 fff52651646c5..6925e706d4f45 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 a828cbf9d3ad5..6f70ecaa25bb3 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 d779920383276..26401522118b7 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 d189e56a668274387fffee2fbb000a25d0201d1e Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Fri, 17 May 2019 14:57:26 +0300 Subject: [PATCH 11/12] 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 106d8b726617f..619f5183a5c72 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 55223ecbc74e8..3c77812fbf7bd 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 6ee742a4d8e34..cbe9ec2b7b07b 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 56f614d92deac..51eda933c6e10 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 85ad3bc07dce7..658560b0ce1d4 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 abd861cbe6ddf..0b41e612a2ee3 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 3ea82d0a8ed6d..b93a4890bc312 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 d3d8d8e77d8aa..890fd5e4f24b6 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 98bacbeccf47f..4cb2de38c7f12 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 fbddca81d4013..4a1edc66ba5a9 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 ec7194a7fc66e..1b465e7a4e544 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 ece98e5e73b09..0000000000000 --- 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 d6b5b69d7525d..20953db97c31e 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 0000000000000..d995d0fd69921 --- /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 b8dd502667ea2..9da82cc6e9812 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 4da041ae1642a..266fe22a4fc71 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 0a0f66d9d7347..15f1583fea71f 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 98d169b9a1ad1..07fd9c6317c54 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 0000000000000..1b309c646e172 --- /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 44d0f545bfcc1..58bcc55785933 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 0000000000000..94762c811b08d --- /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 192f300021b82..194617f3bcb5b 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 0000000000000..ec6e6d7a37671 --- /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 0000000000000..99d12c90fecfb --- /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 0000000000000..5a4812c28ee86 --- /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 0000000000000..21984c9d5eaae --- /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 0000000000000..4e3723fd51171 --- /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 + +Buy Me A Coffee + +### 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 0000000000000..7a492842136b4 --- /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 0000000000000..82fc6b00e1911 --- /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 0000000000000..53297d5378090 --- /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 0000000000000..f63eeedd32418 --- /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 0000000000000..63a737e99a627 --- /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 0000000000000..abdb2aa698f1d --- /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 0000000000000..1eb0ae05303d9 --- /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 0000000000000..c25e4d62a7aa4 --- /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 0000000000000..671cdda91375f --- /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 0000000000000..1192907d5f296 --- /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 0000000000000..3aca817d69f3d --- /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 0000000000000..b2a3790c7c688 --- /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 0000000000000..b682c6f820ccc --- /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 0000000000000..88b957961dbbf --- /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 0000000000000..13b8019973cac --- /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 0000000000000..d751c99943563 --- /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 0000000000000..3347120a67e2b --- /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 0000000000000..363b840017f58 --- /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 0000000000000..4735f7d560af5 --- /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 0000000000000..f036f4639bc8d --- /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 0000000000000..1f16db95c7656 --- /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 0000000000000..905dd74ee0233 --- /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 0000000000000..01d98b63c6b62 --- /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 0000000000000..b0a8f8a1378f3 --- /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 0000000000000..e50903a72af67 --- /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 0000000000000..129b59ba6e3c8 --- /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 0000000000000..7d78fd9950263 --- /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 0000000000000..3b96728bccad5 --- /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 0000000000000..8c064473d3d87 --- /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 0000000000000..c53335e634f12 --- /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 0000000000000..38062d5cdf91c --- /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 0000000000000..6c1e8d56cba11 --- /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 0000000000000..284eef8bb1084 --- /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 0000000000000..0ac73b7109f37 --- /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 0000000000000..3166a9966a130 --- /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 0000000000000..de5a2e6d916c9 --- /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 0000000000000..69dd700e52310 --- /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 0000000000000..c156bde77243d --- /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 0000000000000..bde9bf3105885 --- /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 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 '9' { + return false + } + if i&1 == odd { + sum += t[c-'0'] + } else { + sum += int(c - '0') + } + } + return sum%10 == 0 +} diff --git a/vendor/github.com/brianvoe/gofakeit/person.go b/vendor/github.com/brianvoe/gofakeit/person.go new file mode 100644 index 0000000000000..5fd6cbe22a117 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/person.go @@ -0,0 +1,45 @@ +package gofakeit + +import "strconv" + +// SSN will generate a random Social Security Number +func SSN() string { + return strconv.Itoa(randIntRange(100000000, 999999999)) +} + +// Gender will generate a random gender string +func Gender() string { + if Bool() == true { + return "male" + } + + return "female" +} + +// PersonInfo is a struct of person information +type PersonInfo struct { + FirstName string + LastName string + Gender string + SSN string + Image string + Job *JobInfo + Address *AddressInfo + Contact *ContactInfo + CreditCard *CreditCardInfo +} + +// Person will generate a struct with person information +func Person() *PersonInfo { + return &PersonInfo{ + FirstName: FirstName(), + LastName: LastName(), + Gender: Gender(), + SSN: SSN(), + Image: ImageURL(300, 300) + "/people", + Job: Job(), + Address: Address(), + Contact: Contact(), + CreditCard: CreditCard(), + } +} diff --git a/vendor/github.com/brianvoe/gofakeit/status_code.go b/vendor/github.com/brianvoe/gofakeit/status_code.go new file mode 100644 index 0000000000000..1751c0fbe4011 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/status_code.go @@ -0,0 +1,11 @@ +package gofakeit + +// SimpleStatusCode will generate a random simple status code +func SimpleStatusCode() int { + return getRandIntValue([]string{"status_code", "simple"}) +} + +// StatusCode will generate a random status code +func StatusCode() int { + return getRandIntValue([]string{"status_code", "general"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/string.go b/vendor/github.com/brianvoe/gofakeit/string.go new file mode 100644 index 0000000000000..fc646cf38ac11 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/string.go @@ -0,0 +1,48 @@ +package gofakeit + +import ( + "math/rand" +) + +// Letter will generate a single random lower case ASCII letter +func Letter() string { + return string(randLetter()) +} + +// Digit will generate a single ASCII digit +func Digit() string { + return string(randDigit()) +} + +// Lexify will replace ? will random generated letters +func Lexify(str string) string { + return replaceWithLetters(str) +} + +// ShuffleStrings will randomize a slice of strings +func ShuffleStrings(a []string) { + swap := func(i, j int) { + a[i], a[j] = a[j], a[i] + } + //to avoid upgrading to 1.10 I copied the algorithm + n := len(a) + if n <= 1 { + return + } + + //if size is > int32 probably it will never finish, or ran out of entropy + i := n - 1 + for ; i > 0; i-- { + j := int(rand.Int31n(int32(i + 1))) + swap(i, j) + } +} + +// RandString will take in a slice of string and return a randomly selected value +func RandString(a []string) string { + size := len(a) + if size == 0 { + return "" + } + return a[rand.Intn(size)] +} diff --git a/vendor/github.com/brianvoe/gofakeit/struct.go b/vendor/github.com/brianvoe/gofakeit/struct.go new file mode 100644 index 0000000000000..2c68a9a3cb21f --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/struct.go @@ -0,0 +1,87 @@ +package gofakeit + +import ( + "reflect" +) + +// Struct fills in exported elements of a struct with random data +// based on the value of `fake` tag of exported elements. +// Use `fake:"skip"` to explicitly skip an element. +// All built-in types are supported, with templating support +// for string types. +func Struct(v interface{}) { + r(reflect.TypeOf(v), reflect.ValueOf(v), "") +} + +func r(t reflect.Type, v reflect.Value, template string) { + switch t.Kind() { + case reflect.Ptr: + rPointer(t, v, template) + case reflect.Struct: + rStruct(t, v) + case reflect.String: + rString(template, v) + case reflect.Uint8: + v.SetUint(uint64(Uint8())) + case reflect.Uint16: + v.SetUint(uint64(Uint16())) + case reflect.Uint32: + v.SetUint(uint64(Uint32())) + case reflect.Uint64: + //capped at [0, math.MaxInt64) + v.SetUint(uint64(Uint64())) + case reflect.Int: + v.SetInt(int64(Int64())) + case reflect.Int8: + v.SetInt(int64(Int8())) + case reflect.Int16: + v.SetInt(int64(Int16())) + case reflect.Int32: + v.SetInt(int64(Int32())) + case reflect.Int64: + v.SetInt(int64(Int64())) + case reflect.Float64: + v.SetFloat(Float64()) + case reflect.Float32: + v.SetFloat(float64(Float32())) + case reflect.Bool: + v.SetBool(Bool()) + } +} + +func rString(template string, v reflect.Value) { + if template != "" { + r := Generate(template) + v.SetString(r) + } else { + v.SetString(Generate("???????????????????")) + // we don't have a String(len int) string function!! + } +} + +func rStruct(t reflect.Type, v reflect.Value) { + n := t.NumField() + for i := 0; i < n; i++ { + elementT := t.Field(i) + elementV := v.Field(i) + fake := true + t, ok := elementT.Tag.Lookup("fake") + if ok && t == "skip" { + fake = false + } + if fake && elementV.CanSet() { + r(elementT.Type, elementV, t) + } + } +} + +func rPointer(t reflect.Type, v reflect.Value, template string) { + elemT := t.Elem() + if v.IsNil() { + nv := reflect.New(elemT) + r(elemT, nv.Elem(), template) + v.Set(nv) + } else { + r(elemT, v.Elem(), template) + } +} diff --git a/vendor/github.com/brianvoe/gofakeit/unique.go b/vendor/github.com/brianvoe/gofakeit/unique.go new file mode 100644 index 0000000000000..4b969a7e9b8fb --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/unique.go @@ -0,0 +1,34 @@ +package gofakeit + +import ( + "encoding/hex" + "math/rand" +) + +// UUID (version 4) will generate a random unique identifier based upon random nunbers +// Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +func UUID() string { + version := byte(4) + uuid := make([]byte, 16) + rand.Read(uuid) + + // Set version + uuid[6] = (uuid[6] & 0x0f) | (version << 4) + + // Set variant + uuid[8] = (uuid[8] & 0xbf) | 0x80 + + buf := make([]byte, 36) + var dash byte = '-' + hex.Encode(buf[0:8], uuid[0:4]) + buf[8] = dash + hex.Encode(buf[9:13], uuid[4:6]) + buf[13] = dash + hex.Encode(buf[14:18], uuid[6:8]) + buf[18] = dash + hex.Encode(buf[19:23], uuid[8:10]) + buf[23] = dash + hex.Encode(buf[24:], uuid[10:]) + + return string(buf) +} diff --git a/vendor/github.com/brianvoe/gofakeit/user_agent.go b/vendor/github.com/brianvoe/gofakeit/user_agent.go new file mode 100644 index 0000000000000..2ba3341214526 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/user_agent.go @@ -0,0 +1,92 @@ +package gofakeit + +import "strconv" + +// UserAgent will generate a random broswer user agent +func UserAgent() string { + randNum := randIntRange(0, 4) + switch randNum { + case 0: + return ChromeUserAgent() + case 1: + return FirefoxUserAgent() + case 2: + return SafariUserAgent() + case 3: + return OperaUserAgent() + default: + return ChromeUserAgent() + } +} + +// ChromeUserAgent will generate a random chrome browser user agent string +func ChromeUserAgent() string { + randNum1 := strconv.Itoa(randIntRange(531, 536)) + strconv.Itoa(randIntRange(0, 2)) + randNum2 := strconv.Itoa(randIntRange(36, 40)) + randNum3 := strconv.Itoa(randIntRange(800, 899)) + return "Mozilla/5.0 " + "(" + randomPlatform() + ") AppleWebKit/" + randNum1 + " (KHTML, like Gecko) Chrome/" + randNum2 + ".0." + randNum3 + ".0 Mobile Safari/" + randNum1 +} + +// FirefoxUserAgent will generate a random firefox broswer user agent string +func FirefoxUserAgent() string { + ver := "Gecko/" + Date().Format("2006-02-01") + " Firefox/" + strconv.Itoa(randIntRange(35, 37)) + ".0" + platforms := []string{ + "(" + windowsPlatformToken() + "; " + "en-US" + "; rv:1.9." + strconv.Itoa(randIntRange(0, 3)) + ".20) " + ver, + "(" + linuxPlatformToken() + "; rv:" + strconv.Itoa(randIntRange(5, 8)) + ".0) " + ver, + "(" + macPlatformToken() + " rv:" + strconv.Itoa(randIntRange(2, 7)) + ".0) " + ver, + } + + return "Mozilla/5.0 " + RandString(platforms) +} + +// SafariUserAgent will generate a random safari browser user agent string +func SafariUserAgent() string { + randNum := strconv.Itoa(randIntRange(531, 536)) + "." + strconv.Itoa(randIntRange(1, 51)) + "." + strconv.Itoa(randIntRange(1, 8)) + ver := strconv.Itoa(randIntRange(4, 6)) + "." + strconv.Itoa(randIntRange(0, 2)) + + mobileDevices := []string{ + "iPhone; CPU iPhone OS", + "iPad; CPU OS", + } + + platforms := []string{ + "(Windows; U; " + windowsPlatformToken() + ") AppleWebKit/" + randNum + " (KHTML, like Gecko) Version/" + ver + " Safari/" + randNum, + "(" + macPlatformToken() + " rv:" + strconv.Itoa(randIntRange(4, 7)) + ".0; en-US) AppleWebKit/" + randNum + " (KHTML, like Gecko) Version/" + ver + " Safari/" + randNum, + "(" + RandString(mobileDevices) + " " + strconv.Itoa(randIntRange(7, 9)) + "_" + strconv.Itoa(randIntRange(0, 3)) + "_" + strconv.Itoa(randIntRange(1, 3)) + " like Mac OS X; " + "en-US" + ") AppleWebKit/" + randNum + " (KHTML, like Gecko) Version/" + strconv.Itoa(randIntRange(3, 5)) + ".0.5 Mobile/8B" + strconv.Itoa(randIntRange(111, 120)) + " Safari/6" + randNum, + } + + return "Mozilla/5.0 " + RandString(platforms) +} + +// OperaUserAgent will generate a random opera browser user agent string +func OperaUserAgent() string { + platform := "(" + randomPlatform() + "; en-US) Presto/2." + strconv.Itoa(randIntRange(8, 13)) + "." + strconv.Itoa(randIntRange(160, 355)) + " Version/" + strconv.Itoa(randIntRange(10, 13)) + ".00" + + return "Opera/" + strconv.Itoa(randIntRange(8, 10)) + "." + strconv.Itoa(randIntRange(10, 99)) + " " + platform +} + +// linuxPlatformToken will generate a random linux platform +func linuxPlatformToken() string { + return "X11; Linux " + getRandValue([]string{"computer", "linux_processor"}) +} + +// macPlatformToken will generate a random mac platform +func macPlatformToken() string { + return "Macintosh; " + getRandValue([]string{"computer", "mac_processor"}) + " Mac OS X 10_" + strconv.Itoa(randIntRange(5, 9)) + "_" + strconv.Itoa(randIntRange(0, 10)) +} + +// windowsPlatformToken will generate a random windows platform +func windowsPlatformToken() string { + return getRandValue([]string{"computer", "windows_platform"}) +} + +// randomPlatform will generate a random platform +func randomPlatform() string { + platforms := []string{ + linuxPlatformToken(), + macPlatformToken(), + windowsPlatformToken(), + } + + return RandString(platforms) +} diff --git a/vendor/github.com/brianvoe/gofakeit/vehicle.go b/vendor/github.com/brianvoe/gofakeit/vehicle.go new file mode 100644 index 0000000000000..093fe3a1d84c7 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/vehicle.go @@ -0,0 +1,55 @@ +package gofakeit + +// VehicleInfo is a struct dataset of all vehicle information +type VehicleInfo struct { + // Vehicle type + VehicleType string + // Fuel type + Fuel string + // Transmission type + TransmissionGear string + // Brand name + Brand string + // Vehicle model + Model string + // Vehicle model year + Year int +} + +// Vehicle will generate a struct with vehicle information +func Vehicle() *VehicleInfo { + return &VehicleInfo{ + VehicleType: VehicleType(), + Fuel: FuelType(), + TransmissionGear: TransmissionGearType(), + Brand: CarMaker(), + Model: CarModel(), + Year: Year(), + } + +} + +// VehicleType will generate a random vehicle type string +func VehicleType() string { + return getRandValue([]string{"vehicle", "vehicle_type"}) +} + +// FuelType will return a random fuel type +func FuelType() string { + return getRandValue([]string{"vehicle", "fuel_type"}) +} + +// TransmissionGearType will return a random transmission gear type +func TransmissionGearType() string { + return getRandValue([]string{"vehicle", "transmission_type"}) +} + +// CarMaker will return a random car maker +func CarMaker() string { + return getRandValue([]string{"vehicle", "maker"}) +} + +// CarModel will return a random car model +func CarModel() string { + return getRandValue([]string{"vehicle", "model"}) +} diff --git a/vendor/github.com/brianvoe/gofakeit/words.go b/vendor/github.com/brianvoe/gofakeit/words.go new file mode 100644 index 0000000000000..631e45c7ddd71 --- /dev/null +++ b/vendor/github.com/brianvoe/gofakeit/words.go @@ -0,0 +1,100 @@ +package gofakeit + +import ( + "bytes" + "strings" + "unicode" +) + +type paragrapOptions struct { + paragraphCount int + sentenceCount int + wordCount int + separator string +} + +const bytesPerWordEstimation = 6 + +type sentenceGenerator func(wordCount int) string +type wordGenerator func() string + +// Word will generate a random word +func Word() string { + return getRandValue([]string{"lorem", "word"}) +} + +// Sentence will generate a random sentence +func Sentence(wordCount int) string { + return sentence(wordCount, Word) +} + +// Paragraph will generate a random paragraphGenerator +// Set Paragraph Count +// Set Sentence Count +// Set Word Count +// Set Paragraph Separator +func Paragraph(paragraphCount int, sentenceCount int, wordCount int, separator string) string { + return paragraphGenerator(paragrapOptions{paragraphCount, sentenceCount, wordCount, separator}, Sentence) +} + +func sentence(wordCount int, word wordGenerator) string { + if wordCount <= 0 { + return "" + } + + wordSeparator := ' ' + sentence := bytes.Buffer{} + sentence.Grow(wordCount * bytesPerWordEstimation) + + for i := 0; i < wordCount; i++ { + word := word() + if i == 0 { + runes := []rune(word) + runes[0] = unicode.ToTitle(runes[0]) + word = string(runes) + } + sentence.WriteString(word) + if i < wordCount-1 { + sentence.WriteRune(wordSeparator) + } + } + sentence.WriteRune('.') + return sentence.String() +} + +func paragraphGenerator(opts paragrapOptions, sentecer sentenceGenerator) string { + if opts.paragraphCount <= 0 || opts.sentenceCount <= 0 || opts.wordCount <= 0 { + return "" + } + + //to avoid making Go 1.10 dependency, we cannot use strings.Builder + paragraphs := bytes.Buffer{} + //we presume the length + paragraphs.Grow(opts.paragraphCount * opts.sentenceCount * opts.wordCount * bytesPerWordEstimation) + wordSeparator := ' ' + + for i := 0; i < opts.paragraphCount; i++ { + for e := 0; e < opts.sentenceCount; e++ { + paragraphs.WriteString(sentecer(opts.wordCount)) + if e < opts.sentenceCount-1 { + paragraphs.WriteRune(wordSeparator) + } + } + + if i < opts.paragraphCount-1 { + paragraphs.WriteString(opts.separator) + } + } + + return paragraphs.String() +} + +// Question will return a random question +func Question() string { + return strings.Replace(HipsterSentence(Number(3, 10)), ".", "?", 1) +} + +// Quote will return a random quote from a random person +func Quote() string { + return `"` + HipsterSentence(Number(3, 10)) + `" - ` + FirstName() + " " + LastName() +} diff --git a/vendor/github.com/robfig/cron/README.md b/vendor/github.com/robfig/cron/README.md index ec40c95fcb9d5..4e0ae1c25f391 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 d02ec2f3b563d..1ce84f7bf4623 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 diff --git a/vendor/modules.txt b/vendor/modules.txt index 19e66874848b7..1cf623aa57d4c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -56,6 +56,9 @@ github.com/benbjohnson/clock github.com/beorn7/perks/quantile # github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 github.com/bradfitz/gomemcache/memcache +# github.com/brianvoe/gofakeit v3.17.0+incompatible +github.com/brianvoe/gofakeit +github.com/brianvoe/gofakeit/data # github.com/codegangsta/cli v1.20.0 github.com/codegangsta/cli # github.com/davecgh/go-spew v1.1.1 From 5d43f44fc359e49f458bc61a127315e1fa0ff730 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 17 May 2019 11:26:27 -0700 Subject: [PATCH 12/12] y is the trigger --- public/app/features/dashboard/services/ChangeTracker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard/services/ChangeTracker.ts b/public/app/features/dashboard/services/ChangeTracker.ts index 716b4f79565bc..f5f125e6cf432 100644 --- a/public/app/features/dashboard/services/ChangeTracker.ts +++ b/public/app/features/dashboard/services/ChangeTracker.ts @@ -125,12 +125,13 @@ export class ChangeTracker { delete panel.legend.sortDesc; } - // ignore all h parameters after a dynamic height + // ignore all y parameters after a dynamic height if (panel.dynamicHeight) { hasDynamicHeight = true; + delete panel.gridPos.h; } if (hasDynamicHeight) { - delete panel.gridPos.h; + delete panel.gridPos.y; } return true;