From cf55d6889429df959b48d6f54ccb4bad5b690cbe Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Thu, 14 Mar 2019 17:20:33 +0100 Subject: [PATCH 001/101] using refId from panel model --- public/app/core/utils/explore.ts | 13 +++++------- public/app/core/utils/query.ts | 12 +++++++++++ .../features/dashboard/state/PanelModel.ts | 15 +++----------- public/app/features/explore/state/actions.ts | 20 ++++++++++--------- public/app/features/explore/state/reducers.ts | 8 +++++--- 5 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 public/app/core/utils/query.ts diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 31e5a392050..33adbfb5a73 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -23,6 +23,7 @@ import { ResultGetter, } from 'app/types/explore'; import { LogsDedupStrategy } from 'app/core/logs_model'; +import { getNextQueryLetter } from './query'; export const DEFAULT_RANGE = { from: 'now-6h', @@ -225,12 +226,8 @@ export function generateKey(index = 0): string { return `Q-${Date.now()}-${Math.random()}-${index}`; } -export function generateRefId(index = 0): string { - return `${index + 1}`; -} - -export function generateEmptyQuery(index = 0): { refId: string; key: string } { - return { refId: generateRefId(index), key: generateKey(index) }; +export function generateEmptyQuery(queries: DataQuery[], index = 0): { refId: string; key: string } { + return { refId: getNextQueryLetter(queries), key: generateKey(index) }; } /** @@ -238,9 +235,9 @@ export function generateEmptyQuery(index = 0): { refId: string; key: string } { */ export function ensureQueries(queries?: DataQuery[]): DataQuery[] { if (queries && typeof queries === 'object' && queries.length > 0) { - return queries.map((query, i) => ({ ...query, ...generateEmptyQuery(i) })); + return queries.map((query, i) => ({ ...query, ...generateEmptyQuery(queries, i) })); } - return [{ ...generateEmptyQuery() }]; + return [{ ...generateEmptyQuery(queries) }]; } /** diff --git a/public/app/core/utils/query.ts b/public/app/core/utils/query.ts new file mode 100644 index 00000000000..3a3fd64fbc8 --- /dev/null +++ b/public/app/core/utils/query.ts @@ -0,0 +1,12 @@ +import _ from 'lodash'; +import { DataQuery } from '@grafana/ui/'; + +export const getNextQueryLetter = (queries: DataQuery[]): string => { + const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + return _.find(letters, refId => { + return _.every(queries, other => { + return other.refId !== refId; + }); + }); +}; diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index 128bd8d0785..db9a9c04e14 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -5,6 +5,7 @@ import _ from 'lodash'; import { Emitter } from 'app/core/utils/emitter'; import { DataQuery, TimeSeries, Threshold, ScopedVars, PanelTypeChangedHook } from '@grafana/ui'; import { TableData } from '@grafana/ui/src'; +import { getNextQueryLetter } from '../../../core/utils/query'; export interface GridPos { x: number; @@ -128,7 +129,7 @@ export class PanelModel { if (this.targets) { for (const query of this.targets) { if (!query.refId) { - query.refId = this.getNextQueryLetter(); + query.refId = getNextQueryLetter(this.targets); } } } @@ -266,20 +267,10 @@ export class PanelModel { addQuery(query?: Partial) { query = query || { refId: 'A' }; - query.refId = this.getNextQueryLetter(); + query.refId = getNextQueryLetter(this.targets); this.targets.push(query as DataQuery); } - getNextQueryLetter(): string { - const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - - return _.find(letters, refId => { - return _.every(this.targets, other => { - return other.refId !== refId; - }); - }); - } - changeQuery(query: DataQuery, index: number) { // ensure refId is maintained query.refId = this.targets[index].refId; diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index e0b84320fa7..fda8fe5eef4 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -60,7 +60,6 @@ import { splitCloseAction, splitOpenAction, addQueryRowAction, - AddQueryRowPayload, toggleGraphAction, toggleLogsAction, toggleTableAction, @@ -87,9 +86,12 @@ const updateExploreUIState = (exploreId, uiStateFragment: Partial { - const query = generateEmptyQuery(index + 1); - return addQueryRowAction({ exploreId, index, query }); +export function addQueryRow(exploreId: ExploreId, index: number): ThunkResult { + return (dispatch, getState) => { + const query = generateEmptyQuery(getState().explore[exploreId].queries, index); + + dispatch(addQueryRowAction({ exploreId, index, query })); + }; } /** @@ -126,10 +128,10 @@ export function changeQuery( index: number, override: boolean ): ThunkResult { - return dispatch => { + return (dispatch, getState) => { // Null query means reset if (query === null) { - query = { ...generateEmptyQuery(index) }; + query = { ...generateEmptyQuery(getState().explore[exploreId].queries) }; } dispatch(changeQueryAction({ exploreId, query, index, override })); @@ -287,7 +289,7 @@ export function importQueries( const nextQueries = importedQueries.map((q, i) => ({ ...q, - ...generateEmptyQuery(i), + ...generateEmptyQuery(queries), })); dispatch(queriesImportedAction({ exploreId, queries: nextQueries })); @@ -629,9 +631,9 @@ export function scanStart(exploreId: ExploreId, scanner: RangeScanner): ThunkRes * Use this action for clicks on query examples. Triggers a query run. */ export function setQueries(exploreId: ExploreId, rawQueries: DataQuery[]): ThunkResult { - return dispatch => { + return (dispatch, getState) => { // Inject react keys into query objects - const queries = rawQueries.map(q => ({ ...q, ...generateEmptyQuery() })); + const queries = rawQueries.map(q => ({ ...q, ...generateEmptyQuery(getState().explore[exploreId].queries) })); dispatch(setQueriesAction({ exploreId, queries })); dispatch(runQueries(exploreId)); }; diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index a8815842c89..32bfe09a96b 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -127,7 +127,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta const { query, index } = action.payload; // Override path: queries are completely reset - const nextQuery: DataQuery = { ...query, ...generateEmptyQuery(index) }; + const nextQuery: DataQuery = { ...query, ...generateEmptyQuery(state.queries) }; const nextQueries = [...queries]; nextQueries[index] = nextQuery; @@ -267,7 +267,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta // Modify all queries nextQueries = queries.map((query, i) => ({ ...modifier({ ...query }, modification), - ...generateEmptyQuery(i), + ...generateEmptyQuery(state.queries), })); // Discard all ongoing transactions nextQueryTransactions = []; @@ -276,7 +276,9 @@ export const itemReducer = reducerFactory({} as ExploreItemSta nextQueries = queries.map((query, i) => { // Synchronize all queries with local query cache to ensure consistency // TODO still needed? - return i === index ? { ...modifier({ ...query }, modification), ...generateEmptyQuery(i) } : query; + return i === index + ? { ...modifier({ ...query }, modification), ...generateEmptyQuery(state.queries) } + : query; }); nextQueryTransactions = queryTransactions // Consume the hint corresponding to the action From 52dcb9bf002f4670e452d88f1d53255407dd1aab Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Thu, 14 Mar 2019 17:39:56 +0100 Subject: [PATCH 002/101] renaming function --- public/app/core/utils/explore.ts | 4 ++-- public/app/core/utils/query.ts | 2 +- public/app/features/dashboard/state/PanelModel.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 33adbfb5a73..06456fef0ba 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -23,7 +23,7 @@ import { ResultGetter, } from 'app/types/explore'; import { LogsDedupStrategy } from 'app/core/logs_model'; -import { getNextQueryLetter } from './query'; +import { getNextRefIdLetter } from './query'; export const DEFAULT_RANGE = { from: 'now-6h', @@ -227,7 +227,7 @@ export function generateKey(index = 0): string { } export function generateEmptyQuery(queries: DataQuery[], index = 0): { refId: string; key: string } { - return { refId: getNextQueryLetter(queries), key: generateKey(index) }; + return { refId: getNextRefIdLetter(queries), key: generateKey(index) }; } /** diff --git a/public/app/core/utils/query.ts b/public/app/core/utils/query.ts index 3a3fd64fbc8..304dcf1846f 100644 --- a/public/app/core/utils/query.ts +++ b/public/app/core/utils/query.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import { DataQuery } from '@grafana/ui/'; -export const getNextQueryLetter = (queries: DataQuery[]): string => { +export const getNextRefIdLetter = (queries: DataQuery[]): string => { const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; return _.find(letters, refId => { diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index db9a9c04e14..5aca2bad462 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import { Emitter } from 'app/core/utils/emitter'; import { DataQuery, TimeSeries, Threshold, ScopedVars, PanelTypeChangedHook } from '@grafana/ui'; import { TableData } from '@grafana/ui/src'; -import { getNextQueryLetter } from '../../../core/utils/query'; +import { getNextRefIdLetter } from '../../../core/utils/query'; export interface GridPos { x: number; @@ -129,7 +129,7 @@ export class PanelModel { if (this.targets) { for (const query of this.targets) { if (!query.refId) { - query.refId = getNextQueryLetter(this.targets); + query.refId = getNextRefIdLetter(this.targets); } } } @@ -267,7 +267,7 @@ export class PanelModel { addQuery(query?: Partial) { query = query || { refId: 'A' }; - query.refId = getNextQueryLetter(this.targets); + query.refId = getNextRefIdLetter(this.targets); this.targets.push(query as DataQuery); } From 09eddd1676d03b5a9038d497c455cce4df710dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 14 Mar 2019 18:48:12 +0100 Subject: [PATCH 003/101] Refactoring the bar gauge and the orientation modes --- .../components/BarGauge/BarGauge.story.tsx | 15 +- .../src/components/BarGauge/BarGauge.test.tsx | 1 + .../src/components/BarGauge/BarGauge.tsx | 214 ++++++----- .../__snapshots__/BarGauge.test.tsx.snap | 347 +----------------- .../plugins/panel/bargauge/BarGaugePanel.tsx | 1 + .../panel/bargauge/BarGaugePanelEditor.tsx | 13 +- public/app/plugins/panel/bargauge/types.ts | 4 + 7 files changed, 149 insertions(+), 446 deletions(-) diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx index c7a53af5ccf..6754d43a5cc 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx @@ -1,6 +1,7 @@ import { storiesOf } from '@storybook/react'; -import { number, text } from '@storybook/addon-knobs'; +import { number, text, boolean } from '@storybook/addon-knobs'; import { BarGauge } from './BarGauge'; +import { VizOrientation } from '../../types'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { renderComponentWithTheme } from '../../utils/storybook/withTheme'; @@ -15,6 +16,8 @@ const getKnobs = () => { threshold2Color: text('threshold2Color', 'red'), unit: text('unit', 'ms'), decimals: number('decimals', 1), + horizontal: boolean('horizontal', false), + lcd: boolean('lcd', false), }; }; @@ -22,7 +25,7 @@ const BarGaugeStories = storiesOf('UI/BarGauge/BarGauge', module); BarGaugeStories.addDecorator(withCenteredStory); -BarGaugeStories.add('Vertical, with basic thresholds', () => { +BarGaugeStories.add('Simple with basic thresholds', () => { const { value, minValue, @@ -33,11 +36,13 @@ BarGaugeStories.add('Vertical, with basic thresholds', () => { threshold2Value, unit, decimals, + horizontal, + lcd, } = getKnobs(); return renderComponentWithTheme(BarGauge, { - width: 200, - height: 400, + width: 700, + height: 700, value: value, minValue: minValue, maxValue: maxValue, @@ -45,6 +50,8 @@ BarGaugeStories.add('Vertical, with basic thresholds', () => { prefix: '', postfix: '', decimals: decimals, + orientation: horizontal ? VizOrientation.Horizontal : VizOrientation.Vertical, + displayMode: lcd ? 'lcd' : 'simple', thresholds: [ { index: 0, value: -Infinity, color: 'green' }, { index: 1, value: threshold1Value, color: threshold1Color }, diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx index 8fa0b2846a5..07a640ee7f7 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx @@ -15,6 +15,7 @@ const setup = (propOverrides?: object) => { minValue: 0, prefix: '', suffix: '', + displayMode: 'simple', thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }], unit: 'none', height: 300, diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx index 97cc4792785..0de46709c16 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx @@ -1,5 +1,5 @@ // Library -import React, { PureComponent, CSSProperties } from 'react'; +import React, { PureComponent, CSSProperties, ReactNode } from 'react'; import tinycolor from 'tinycolor2'; // Utils @@ -23,22 +23,37 @@ export interface Props extends Themeable { prefix?: string; suffix?: string; decimals?: number; + displayMode: 'simple' | 'lcd'; } -/* - * This visualization is still in POC state, needed more tests & better structure - */ export class BarGauge extends PureComponent { static defaultProps: Partial = { maxValue: 100, minValue: 0, value: 100, unit: 'none', + displayMode: 'simple', orientation: VizOrientation.Horizontal, thresholds: [], valueMappings: [], }; + render() { + const { maxValue, minValue, unit, decimals, displayMode } = this.props; + + const numericValue = this.getNumericValue(); + const valuePercent = Math.min(numericValue / (maxValue - minValue), 1); + + const formatFunc = getValueFormat(unit); + const valueFormatted = formatFunc(numericValue, decimals); + + if (displayMode === 'lcd') { + return this.renderLcdMode(valueFormatted, valuePercent); + } else { + return this.renderSimpleMode(valueFormatted, valuePercent); + } + } + getNumericValue(): number { if (Number.isFinite(this.props.value as number)) { return this.props.value as number; @@ -70,6 +85,70 @@ export class BarGauge extends PureComponent { }; } + getValueStyles(value: string, color: string, width: number): CSSProperties { + const guess = width / (value.length * 1.1); + const fontSize = Math.min(Math.max(guess, 14), 40); + + return { + color: color, + fontSize: fontSize + 'px', + }; + } + + /* + * Return width or height depending on viz orientation + * */ + get size() { + const { height, width, orientation } = this.props; + return orientation === VizOrientation.Horizontal ? width : height; + } + + renderSimpleMode(valueFormatted: string, valuePercent: number): ReactNode { + const { height, width, orientation } = this.props; + + const maxSize = this.size * BAR_SIZE_RATIO; + const barSize = Math.max(valuePercent * maxSize, 0); + const colors = this.getValueColors(); + const valueStyles = this.getValueStyles(valueFormatted, colors.value, this.size - maxSize); + + const containerStyles: CSSProperties = { + width: `${width}px`, + height: `${height}px`, + display: 'flex', + }; + + const barStyles: CSSProperties = { + backgroundColor: colors.bar, + }; + + // Custom styles for vertical orientation + if (orientation === VizOrientation.Vertical) { + containerStyles.flexDirection = 'column'; + containerStyles.justifyContent = 'flex-end'; + barStyles.height = `${barSize}px`; + barStyles.width = `${width}px`; + barStyles.borderTop = `1px solid ${colors.border}`; + } else { + // Custom styles for horizontal orientation + containerStyles.flexDirection = 'row-reverse'; + containerStyles.justifyContent = 'flex-end'; + containerStyles.alignItems = 'center'; + barStyles.height = `${height}px`; + barStyles.width = `${barSize}px`; + barStyles.marginRight = '10px'; + barStyles.borderRight = `1px solid ${colors.border}`; + } + + return ( +
+
+ {valueFormatted} +
+
+
+ ); + } + getCellColor(positionValue: TimeSeriesValue): string { const { thresholds, theme, value } = this.props; const activeThreshold = getThresholdForValue(thresholds, positionValue); @@ -92,117 +171,51 @@ export class BarGauge extends PureComponent { return 'gray'; } - getValueStyles(value: string, color: string, width: number): CSSProperties { - const guess = width / (value.length * 1.1); - const fontSize = Math.min(Math.max(guess, 14), 40); - - return { - color: color, - fontSize: fontSize + 'px', - }; - } - - renderVerticalBar(valueFormatted: string, valuePercent: number) { - const { height, width } = this.props; - - const maxHeight = height * BAR_SIZE_RATIO; - const barHeight = Math.max(valuePercent * maxHeight, 0); - const colors = this.getValueColors(); - const valueStyles = this.getValueStyles(valueFormatted, colors.value, width); - - const containerStyles: CSSProperties = { - width: `${width}px`, - height: `${height}px`, - display: 'flex', - flexDirection: 'column', - justifyContent: 'flex-end', - }; - - const barStyles: CSSProperties = { - height: `${barHeight}px`, - width: `${width}px`, - backgroundColor: colors.bar, - borderTop: `1px solid ${colors.border}`, - }; - - return ( -
-
- {valueFormatted} -
-
-
- ); - } - - renderHorizontalBar(valueFormatted: string, valuePercent: number) { - const { height, width } = this.props; - - const maxWidth = width * BAR_SIZE_RATIO; - const barWidth = Math.max(valuePercent * maxWidth, 0); - const colors = this.getValueColors(); - const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO)); - - valueStyles.marginLeft = '8px'; - - const containerStyles: CSSProperties = { - width: `${width}px`, - height: `${height}px`, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }; - - const barStyles = { - height: `${height}px`, - width: `${barWidth}px`, - backgroundColor: colors.bar, - borderRight: `1px solid ${colors.border}`, - }; - - return ( -
-
-
- {valueFormatted} -
-
- ); - } - - renderHorizontalLCD(valueFormatted: string, valuePercent: number) { - const { height, width, maxValue, minValue } = this.props; + renderLcdMode(valueFormatted: string, valuePercent: number): ReactNode { + const { height, width, maxValue, minValue, orientation } = this.props; const valueRange = maxValue - minValue; - const maxWidth = width * BAR_SIZE_RATIO; + const maxSize = this.size * BAR_SIZE_RATIO; const cellSpacing = 4; const cellCount = 30; - const cellWidth = (maxWidth - cellSpacing * cellCount) / cellCount; + const cellSize = (maxSize - cellSpacing * cellCount) / cellCount; const colors = this.getValueColors(); - const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO)); - valueStyles.marginLeft = '8px'; + const valueStyles = this.getValueStyles(valueFormatted, colors.value, this.size - maxSize); const containerStyles: CSSProperties = { width: `${width}px`, height: `${height}px`, display: 'flex', - flexDirection: 'row', - alignItems: 'center', }; + if (orientation === VizOrientation.Horizontal) { + containerStyles.flexDirection = 'row'; + containerStyles.alignItems = 'center'; + } else { + containerStyles.flexDirection = 'column-reverse'; + containerStyles.alignItems = 'center'; + } + const cells: JSX.Element[] = []; for (let i = 0; i < cellCount; i++) { const currentValue = (valueRange / cellCount) * i; const cellColor = this.getCellColor(currentValue); const cellStyles: CSSProperties = { - width: `${cellWidth}px`, backgroundColor: cellColor, - marginRight: '4px', - height: `${height}px`, borderRadius: '2px', }; + if (orientation === VizOrientation.Horizontal) { + cellStyles.width = `${cellSize}px`; + cellStyles.height = `${height}px`; + cellStyles.marginRight = '4px'; + } else { + cellStyles.height = `${cellSize}px`; + cellStyles.width = `${width}px`; + cellStyles.marginTop = '4px'; + } + cells.push(
); } @@ -215,21 +228,6 @@ export class BarGauge extends PureComponent {
); } - - render() { - const { maxValue, minValue, orientation, unit, decimals } = this.props; - - const numericValue = this.getNumericValue(); - const valuePercent = Math.min(numericValue / (maxValue - minValue), 1); - - const formatFunc = getValueFormat(unit); - const valueFormatted = formatFunc(numericValue, decimals); - const vertical = orientation === 'vertical'; - - return vertical - ? this.renderVerticalBar(valueFormatted, valuePercent) - : this.renderHorizontalLCD(valueFormatted, valuePercent); - } } interface BarColors { diff --git a/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap b/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap index 65c647bd90c..077389be418 100644 --- a/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap +++ b/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap @@ -6,353 +6,34 @@ exports[`Render BarGauge with basic options should render 1`] = ` Object { "alignItems": "center", "display": "flex", - "flexDirection": "row", + "flexDirection": "row-reverse", "height": "300px", + "justifyContent": "flex-end", "width": "300px", } } > -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
25
+
`; diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 9f60c3440eb..708b472ec2e 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -35,6 +35,7 @@ export class BarGaugePanel extends PureComponent { thresholds={options.thresholds} valueMappings={options.valueMappings} theme={config.theme} + displayMode={options.displayMode} /> ); } diff --git a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx index 4232155228b..87e5defd277 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx @@ -7,7 +7,7 @@ import { ThresholdsEditor, ValueMappingsEditor, PanelOptionsGrid, PanelOptionsGr // Types import { FormLabel, PanelEditorProps, Threshold, Select, ValueMapping } from '@grafana/ui'; -import { BarGaugeOptions, orientationOptions } from './types'; +import { BarGaugeOptions, orientationOptions, displayModes } from './types'; import { SingleStatValueOptions } from '../gauge/types'; export class BarGaugePanelEditor extends PureComponent> { @@ -32,6 +32,7 @@ export class BarGaugePanelEditor extends PureComponent this.props.onOptionsChange({ ...this.props.options, minValue: target.value }); onMaxValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, maxValue: target.value }); onOrientationChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, orientation: value }); + onDisplayModeChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, displayMode: value }); render() { const { options } = this.props; @@ -53,6 +54,16 @@ export class BarGaugePanelEditor extends PureComponent item.value === options.orientation)} />
+
+ Display Mode + + {error && !hideErrorMessage && {error}} +
+ ); + } +} diff --git a/packages/grafana-ui/src/types/forms.ts b/packages/grafana-ui/src/types/forms.ts new file mode 100644 index 00000000000..602ee434ee5 --- /dev/null +++ b/packages/grafana-ui/src/types/forms.ts @@ -0,0 +1,26 @@ +export enum InputStatus { + Invalid = 'invalid', + Valid = 'valid', +} + +export enum InputTypes { + Text = 'text', + Number = 'number', + Password = 'password', + Email = 'email', +} + +export enum EventsWithValidation { + onBlur = 'onBlur', + onFocus = 'onFocus', + onChange = 'onChange', +} + +export interface ValidationRule { + rule: (valueToValidate: string) => boolean; + errorMessage: string; +} + +export interface ValidationEvents { + [eventName: string]: ValidationRule[]; +} diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index b09d88bab4d..390f8a4db29 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -5,3 +5,4 @@ export * from './plugin'; export * from './datasource'; export * from './theme'; export * from './threshold'; +export * from './forms'; diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index a08b9ce1a89..00a6c20d4d1 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -7,3 +7,4 @@ export * from './thresholds'; export * from './string'; export * from './deprecationWarning'; export { getMappedValue } from './valueMappings'; +export * from './validate'; diff --git a/packages/grafana-ui/src/utils/validate.ts b/packages/grafana-ui/src/utils/validate.ts new file mode 100644 index 00000000000..20979ae33ff --- /dev/null +++ b/packages/grafana-ui/src/utils/validate.ts @@ -0,0 +1,15 @@ +import { EventsWithValidation, ValidationEvents, ValidationRule } from '../types'; + +export const validate = (value: string, validationRules: ValidationRule[]) => { + const errors = validationRules.reduce((acc, currentRule) => { + if (!currentRule.rule(value)) { + return acc.concat(currentRule.errorMessage); + } + return acc; + }, []); + return errors.length > 0 ? errors : null; +}; + +export const hasValidationEvent = (event: EventsWithValidation, validationEvents?: ValidationEvents) => { + return validationEvents && validationEvents[event]; +}; From 515fb5903ee772a1f43823d53c13c2ba2dea3e74 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 18 Mar 2019 10:44:00 +0100 Subject: [PATCH 015/101] sorting imports --- public/app/features/dashboard/state/PanelModel.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index 5aca2bad462..f49ed2c0785 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -1,11 +1,13 @@ // Libraries import _ from 'lodash'; -// Types +// Utils import { Emitter } from 'app/core/utils/emitter'; +import { getNextRefIdLetter } from 'app/core/utils/query'; + +// Types import { DataQuery, TimeSeries, Threshold, ScopedVars, PanelTypeChangedHook } from '@grafana/ui'; import { TableData } from '@grafana/ui/src'; -import { getNextRefIdLetter } from '../../../core/utils/query'; export interface GridPos { x: number; From 39728c885b8c9567ad5887895cb21bd88c877ab8 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 18 Mar 2019 11:17:58 +0100 Subject: [PATCH 016/101] rename to char --- public/app/core/utils/explore.ts | 4 ++-- public/app/core/utils/query.ts | 2 +- public/app/features/dashboard/state/PanelModel.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 45e26e79ebf..2e79610c3c6 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -23,7 +23,7 @@ import { ResultGetter, } from 'app/types/explore'; import { LogsDedupStrategy } from 'app/core/logs_model'; -import { getNextRefIdLetter } from './query'; +import { getNextRefIdChar } from './query'; export const DEFAULT_RANGE = { from: 'now-6h', @@ -227,7 +227,7 @@ export function generateKey(index = 0): string { } export function generateEmptyQuery(queries: DataQuery[], index = 0): DataQuery { - return { refId: getNextRefIdLetter(queries), key: generateKey(index) }; + return { refId: getNextRefIdChar(queries), key: generateKey(index) }; } /** diff --git a/public/app/core/utils/query.ts b/public/app/core/utils/query.ts index 304dcf1846f..933a73138a8 100644 --- a/public/app/core/utils/query.ts +++ b/public/app/core/utils/query.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import { DataQuery } from '@grafana/ui/'; -export const getNextRefIdLetter = (queries: DataQuery[]): string => { +export const getNextRefIdChar = (queries: DataQuery[]): string => { const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; return _.find(letters, refId => { diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index f49ed2c0785..8ffce0f1e3b 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; // Utils import { Emitter } from 'app/core/utils/emitter'; -import { getNextRefIdLetter } from 'app/core/utils/query'; +import { getNextRefIdChar } from 'app/core/utils/query'; // Types import { DataQuery, TimeSeries, Threshold, ScopedVars, PanelTypeChangedHook } from '@grafana/ui'; @@ -131,7 +131,7 @@ export class PanelModel { if (this.targets) { for (const query of this.targets) { if (!query.refId) { - query.refId = getNextRefIdLetter(this.targets); + query.refId = getNextRefIdChar(this.targets); } } } @@ -269,7 +269,7 @@ export class PanelModel { addQuery(query?: Partial) { query = query || { refId: 'A' }; - query.refId = getNextRefIdLetter(this.targets); + query.refId = getNextRefIdChar(this.targets); this.targets.push(query as DataQuery); } From cb9bda810fae9bb49ee25d2059dddbec53766ad7 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 18 Mar 2019 11:21:40 +0100 Subject: [PATCH 017/101] test --- public/app/core/utils/query.test.ts | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 public/app/core/utils/query.test.ts diff --git a/public/app/core/utils/query.test.ts b/public/app/core/utils/query.test.ts new file mode 100644 index 00000000000..a69162751a4 --- /dev/null +++ b/public/app/core/utils/query.test.ts @@ -0,0 +1,30 @@ +import { DataQuery } from '@grafana/ui'; +import { getNextRefIdChar } from './query'; + +const dataQueries: DataQuery[] = [ + { + refId: 'A', + }, + { + refId: 'B', + }, + { + refId: 'C', + }, + { + refId: 'D', + }, + { + refId: 'E', + }, +]; + +describe('Get next refId char', () => { + it('should return next char', () => { + expect(getNextRefIdChar(dataQueries)).toEqual('F'); + }); + + it('should get first char', () => { + expect(getNextRefIdChar([])).toEqual('A'); + }); +}); From be7a5dab69cefe7e2e6dac258fc31cbeb1ee99d8 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 18 Mar 2019 11:23:40 +0100 Subject: [PATCH 018/101] reorder imports --- public/app/core/utils/explore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 2e79610c3c6..fdc63b931f7 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -9,6 +9,7 @@ import store from 'app/core/store'; import { parse as parseDate } from 'app/core/utils/datemath'; import { colors } from '@grafana/ui'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; +import { getNextRefIdChar } from './query'; // Types import { RawTimeRange, IntervalValues, DataQuery, DataSourceApi } from '@grafana/ui'; @@ -23,7 +24,6 @@ import { ResultGetter, } from 'app/types/explore'; import { LogsDedupStrategy } from 'app/core/logs_model'; -import { getNextRefIdChar } from './query'; export const DEFAULT_RANGE = { from: 'now-6h', From 384e11fd6832d06c26af3873a12ef6337bcc7d3b Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 18 Mar 2019 15:41:46 +0100 Subject: [PATCH 019/101] Copied from new timepicker and unified component branch --- .../src/components/Input}/Input.test.tsx | 12 +-- .../grafana-ui/src/components/Input/Input.tsx | 40 +++----- .../Input}/__snapshots__/Input.test.tsx.snap | 0 packages/grafana-ui/src/components/index.ts | 1 + packages/grafana-ui/src/types/forms.ts | 26 ----- packages/grafana-ui/src/types/index.ts | 2 +- .../grafana-ui/src/types/input.ts | 0 packages/grafana-ui/src/utils/validate.ts | 25 +++-- public/app/core/components/Form/Input.tsx | 94 ------------------- public/app/core/components/Form/index.ts | 1 - public/app/core/utils/validate.ts | 16 ---- .../dashboard/panel_editor/QueryOptions.tsx | 9 +- public/app/types/index.ts | 1 - 13 files changed, 40 insertions(+), 187 deletions(-) rename {public/app/core/components/Form => packages/grafana-ui/src/components/Input}/Input.test.tsx (83%) rename {public/app/core/components/Form => packages/grafana-ui/src/components/Input}/__snapshots__/Input.test.tsx.snap (100%) delete mode 100644 packages/grafana-ui/src/types/forms.ts rename public/app/types/form.ts => packages/grafana-ui/src/types/input.ts (100%) delete mode 100644 public/app/core/components/Form/Input.tsx delete mode 100644 public/app/core/components/Form/index.ts delete mode 100644 public/app/core/utils/validate.ts diff --git a/public/app/core/components/Form/Input.test.tsx b/packages/grafana-ui/src/components/Input/Input.test.tsx similarity index 83% rename from public/app/core/components/Form/Input.test.tsx rename to packages/grafana-ui/src/components/Input/Input.test.tsx index 9e903208e80..1d39b594b1c 100644 --- a/public/app/core/components/Form/Input.test.tsx +++ b/packages/grafana-ui/src/components/Input/Input.test.tsx @@ -1,18 +1,16 @@ -import React from 'react'; +import React from 'react'; import renderer from 'react-test-renderer'; import { shallow } from 'enzyme'; -import { Input, EventsWithValidation } from './Input'; -import { ValidationEvents } from 'app/types'; +import { Input } from './Input'; +import { EventsWithValidation } from '../../utils'; +import { ValidationEvents } from '../../types'; const TEST_ERROR_MESSAGE = 'Value must be empty or less than 3 chars'; const testBlurValidation: ValidationEvents = { [EventsWithValidation.onBlur]: [ { rule: (value: string) => { - if (!value || value.length < 3) { - return true; - } - return false; + return !value || value.length < 3; }, errorMessage: TEST_ERROR_MESSAGE, }, diff --git a/packages/grafana-ui/src/components/Input/Input.tsx b/packages/grafana-ui/src/components/Input/Input.tsx index 57d6a753b17..f5f59e265c0 100644 --- a/packages/grafana-ui/src/components/Input/Input.tsx +++ b/packages/grafana-ui/src/components/Input/Input.tsx @@ -1,25 +1,13 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, ChangeEvent } from 'react'; import classNames from 'classnames'; -import { ValidationEvents, ValidationRule } from '../../types/forms'; +import { validate, EventsWithValidation, hasValidationEvent } from '../../utils'; +import { ValidationEvents, ValidationRule } from '../../types'; export enum InputStatus { Invalid = 'invalid', Valid = 'valid', } -export enum InputTypes { - Text = 'text', - Number = 'number', - Password = 'password', - Email = 'email', -} - -export enum EventsWithValidation { - onBlur = 'onBlur', - onFocus = 'onFocus', - onChange = 'onChange', -} - interface Props extends React.HTMLProps { validationEvents?: ValidationEvents; hideErrorMessage?: boolean; @@ -27,7 +15,7 @@ interface Props extends React.HTMLProps { // Override event props and append status as argument onBlur?: (event: React.FocusEvent, status?: InputStatus) => void; onFocus?: (event: React.FocusEvent, status?: InputStatus) => void; - onChange?: (event: React.FormEvent, status?: InputStatus) => void; + onChange?: (event: React.ChangeEvent, status?: InputStatus) => void; } export class Input extends PureComponent { @@ -48,24 +36,24 @@ export class Input extends PureComponent { } validatorAsync = (validationRules: ValidationRule[]) => { - return evt => { + return (evt: ChangeEvent) => { const errors = validate(evt.target.value, validationRules); this.setState(prevState => { - return { - ...prevState, - error: errors ? errors[0] : null, - }; + return { ...prevState, error: errors ? errors[0] : null }; }); }; }; - populateEventPropsWithStatus = (restProps, validationEvents: ValidationEvents) => { + populateEventPropsWithStatus = (restProps: any, validationEvents: ValidationEvents | undefined) => { const inputElementProps = { ...restProps }; - Object.keys(EventsWithValidation).forEach((eventName: EventsWithValidation) => { - if (hasValidationEvent(eventName, validationEvents) || restProps[eventName]) { - inputElementProps[eventName] = async evt => { + if (!validationEvents) { + return inputElementProps; + } + Object.keys(EventsWithValidation).forEach(eventName => { + if (hasValidationEvent(eventName as EventsWithValidation, validationEvents) || restProps[eventName]) { + inputElementProps[eventName] = async (evt: ChangeEvent) => { evt.persist(); // Needed for async. https://reactjs.org/docs/events.html#event-pooling - if (hasValidationEvent(eventName, validationEvents)) { + if (hasValidationEvent(eventName as EventsWithValidation, validationEvents)) { await this.validatorAsync(validationEvents[eventName]).apply(this, [evt]); } if (restProps[eventName]) { diff --git a/public/app/core/components/Form/__snapshots__/Input.test.tsx.snap b/packages/grafana-ui/src/components/Input/__snapshots__/Input.test.tsx.snap similarity index 100% rename from public/app/core/components/Form/__snapshots__/Input.test.tsx.snap rename to packages/grafana-ui/src/components/Input/__snapshots__/Input.test.tsx.snap diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index b8c8d66cead..e20a52f6485 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -25,6 +25,7 @@ export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor'; export { Switch } from './Switch/Switch'; export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult'; export { UnitPicker } from './UnitPicker/UnitPicker'; +export { Input, InputStatus } from './Input/Input'; // Visualizations export { Gauge } from './Gauge/Gauge'; diff --git a/packages/grafana-ui/src/types/forms.ts b/packages/grafana-ui/src/types/forms.ts deleted file mode 100644 index 602ee434ee5..00000000000 --- a/packages/grafana-ui/src/types/forms.ts +++ /dev/null @@ -1,26 +0,0 @@ -export enum InputStatus { - Invalid = 'invalid', - Valid = 'valid', -} - -export enum InputTypes { - Text = 'text', - Number = 'number', - Password = 'password', - Email = 'email', -} - -export enum EventsWithValidation { - onBlur = 'onBlur', - onFocus = 'onFocus', - onChange = 'onChange', -} - -export interface ValidationRule { - rule: (valueToValidate: string) => boolean; - errorMessage: string; -} - -export interface ValidationEvents { - [eventName: string]: ValidationRule[]; -} diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index 390f8a4db29..c0aede431d0 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -5,4 +5,4 @@ export * from './plugin'; export * from './datasource'; export * from './theme'; export * from './threshold'; -export * from './forms'; +export * from './input'; diff --git a/public/app/types/form.ts b/packages/grafana-ui/src/types/input.ts similarity index 100% rename from public/app/types/form.ts rename to packages/grafana-ui/src/types/input.ts diff --git a/packages/grafana-ui/src/utils/validate.ts b/packages/grafana-ui/src/utils/validate.ts index 20979ae33ff..286ec700577 100644 --- a/packages/grafana-ui/src/utils/validate.ts +++ b/packages/grafana-ui/src/utils/validate.ts @@ -1,15 +1,24 @@ -import { EventsWithValidation, ValidationEvents, ValidationRule } from '../types'; +import { ValidationRule, ValidationEvents } from '../types/input'; + +export enum EventsWithValidation { + onBlur = 'onBlur', + onFocus = 'onFocus', + onChange = 'onChange', +} export const validate = (value: string, validationRules: ValidationRule[]) => { - const errors = validationRules.reduce((acc, currentRule) => { - if (!currentRule.rule(value)) { - return acc.concat(currentRule.errorMessage); - } - return acc; - }, []); + const errors = validationRules.reduce( + (acc, currRule) => { + if (!currRule.rule(value)) { + return acc.concat(currRule.errorMessage); + } + return acc; + }, + [] as string[] + ); return errors.length > 0 ? errors : null; }; -export const hasValidationEvent = (event: EventsWithValidation, validationEvents?: ValidationEvents) => { +export const hasValidationEvent = (event: EventsWithValidation, validationEvents: ValidationEvents | undefined) => { return validationEvents && validationEvents[event]; }; diff --git a/public/app/core/components/Form/Input.tsx b/public/app/core/components/Form/Input.tsx deleted file mode 100644 index 7940f3b1104..00000000000 --- a/public/app/core/components/Form/Input.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { PureComponent } from 'react'; -import classNames from 'classnames'; -import { ValidationEvents, ValidationRule } from 'app/types'; -import { validate, hasValidationEvent } from 'app/core/utils/validate'; - -export enum InputStatus { - Invalid = 'invalid', - Valid = 'valid', -} - -export enum InputTypes { - Text = 'text', - Number = 'number', - Password = 'password', - Email = 'email', -} - -export enum EventsWithValidation { - onBlur = 'onBlur', - onFocus = 'onFocus', - onChange = 'onChange', -} - -interface Props extends React.HTMLProps { - validationEvents?: ValidationEvents; - hideErrorMessage?: boolean; - - // Override event props and append status as argument - onBlur?: (event: React.FocusEvent, status?: InputStatus) => void; - onFocus?: (event: React.FocusEvent, status?: InputStatus) => void; - onChange?: (event: React.FormEvent, status?: InputStatus) => void; -} - -export class Input extends PureComponent { - static defaultProps = { - className: '', - }; - - state = { - error: null, - }; - - get status() { - return this.state.error ? InputStatus.Invalid : InputStatus.Valid; - } - - get isInvalid() { - return this.status === InputStatus.Invalid; - } - - validatorAsync = (validationRules: ValidationRule[]) => { - return evt => { - const errors = validate(evt.target.value, validationRules); - this.setState(prevState => { - return { - ...prevState, - error: errors ? errors[0] : null, - }; - }); - }; - }; - - populateEventPropsWithStatus = (restProps, validationEvents: ValidationEvents) => { - const inputElementProps = { ...restProps }; - Object.keys(EventsWithValidation).forEach((eventName: EventsWithValidation) => { - if (hasValidationEvent(eventName, validationEvents) || restProps[eventName]) { - inputElementProps[eventName] = async evt => { - evt.persist(); // Needed for async. https://reactjs.org/docs/events.html#event-pooling - if (hasValidationEvent(eventName, validationEvents)) { - await this.validatorAsync(validationEvents[eventName]).apply(this, [evt]); - } - if (restProps[eventName]) { - restProps[eventName].apply(null, [evt, this.status]); - } - }; - } - }); - return inputElementProps; - }; - - render() { - const { validationEvents, className, hideErrorMessage, ...restProps } = this.props; - const { error } = this.state; - const inputClassName = classNames('gf-form-input', { invalid: this.isInvalid }, className); - const inputElementProps = this.populateEventPropsWithStatus(restProps, validationEvents); - - return ( -
- - {error && !hideErrorMessage && {error}} -
- ); - } -} diff --git a/public/app/core/components/Form/index.ts b/public/app/core/components/Form/index.ts deleted file mode 100644 index 6322cf3241a..00000000000 --- a/public/app/core/components/Form/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Input } from './Input'; diff --git a/public/app/core/utils/validate.ts b/public/app/core/utils/validate.ts deleted file mode 100644 index c6663882808..00000000000 --- a/public/app/core/utils/validate.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ValidationRule, ValidationEvents } from 'app/types'; -import { EventsWithValidation } from 'app/core/components/Form/Input'; - -export const validate = (value: string, validationRules: ValidationRule[]) => { - const errors = validationRules.reduce((acc, currRule) => { - if (!currRule.rule(value)) { - return acc.concat(currRule.errorMessage); - } - return acc; - }, []); - return errors.length > 0 ? errors : null; -}; - -export const hasValidationEvent = (event: EventsWithValidation, validationEvents: ValidationEvents) => { - return validationEvents && validationEvents[event]; -}; diff --git a/public/app/features/dashboard/panel_editor/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx index 0d031cb12ba..377582d7ce5 100644 --- a/public/app/features/dashboard/panel_editor/QueryOptions.tsx +++ b/public/app/features/dashboard/panel_editor/QueryOptions.tsx @@ -5,17 +5,12 @@ import React, { PureComponent, ChangeEvent, FocusEvent } from 'react'; import { isValidTimeSpan } from 'app/core/utils/rangeutil'; // Components -import { Switch } from '@grafana/ui'; -import { Input } from 'app/core/components/Form'; -import { EventsWithValidation } from 'app/core/components/Form/Input'; -import { InputStatus } from 'app/core/components/Form/Input'; +import { DataSourceSelectItem, EventsWithValidation, Input, InputStatus, Switch, ValidationEvents } from '@grafana/ui'; import { DataSourceOption } from './DataSourceOption'; import { FormLabel } from '@grafana/ui'; // Types -import { PanelModel } from '../state/PanelModel'; -import { DataSourceSelectItem } from '@grafana/ui/src/types'; -import { ValidationEvents } from 'app/types'; +import { PanelModel } from '../state'; const timeRangeValidationEvents: ValidationEvents = { [EventsWithValidation.onBlur]: [ diff --git a/public/app/types/index.ts b/public/app/types/index.ts index eefba746c61..3bf76aeb3c3 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -12,6 +12,5 @@ export * from './plugins'; export * from './organization'; export * from './appNotifications'; export * from './search'; -export * from './form'; export * from './explore'; export * from './store'; From 6673915f2ebe62334e418bb7c74e70f1ea394498 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 19 Mar 2019 08:16:19 +0100 Subject: [PATCH 020/101] Update style_guides/backend.md Co-Authored-By: bergquist --- style_guides/backend.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/style_guides/backend.md b/style_guides/backend.md index 8150530071b..1c6c86efc0b 100644 --- a/style_guides/backend.md +++ b/style_guides/backend.md @@ -17,7 +17,7 @@ The preferred solution, in this case, is to inject the `bus` into services or ta ### settings package In the `setting` packages there are many global variables which Grafana sets at startup. This is also something we want to move -away from and move as much configuration as possible to the `setting.Cfg` struct and pass the around just like the bus +away from and move as much configuration as possible to the `setting.Cfg` struct and pass it around, just like the bus. ## Linting and formatting We enforce strict `gofmt` formating and use some linters on our codebase. You can find the current list of linters at https://github.com/grafana/grafana/blob/master/scripts/gometalinter.sh#L23 @@ -27,4 +27,4 @@ We don't enforce `golint` but we encourage it and we will test so the number of ## Testing We use GoConvey for BDD/scenario based testing. Which we think is useful for testing certain chain or interactions. Ex https://github.com/grafana/grafana/blob/master/pkg/services/auth/auth_token_test.go -For smaller tests its preferred to use standard library testing. \ No newline at end of file +For smaller tests its preferred to use standard library testing. From 39e75d75b40a7a3cc570ac8fa36e762b9c8639d7 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 19 Mar 2019 10:48:41 +0100 Subject: [PATCH 021/101] build: crcmod speedups rsync to gcp for deploy. --- scripts/build/ci-deploy/Dockerfile | 2 +- scripts/build/ci-deploy/build-deploy.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/ci-deploy/Dockerfile b/scripts/build/ci-deploy/Dockerfile index dd4987b96c3..e608d9156e7 100644 --- a/scripts/build/ci-deploy/Dockerfile +++ b/scripts/build/ci-deploy/Dockerfile @@ -10,7 +10,7 @@ FROM circleci/python:2.7-stretch USER root -RUN pip install awscli && \ +RUN pip install -U awscli crcmod && \ curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-222.0.0-linux-x86_64.tar.gz | \ tar xvzf - -C /opt && \ apt update && \ diff --git a/scripts/build/ci-deploy/build-deploy.sh b/scripts/build/ci-deploy/build-deploy.sh index 8dedeead009..ed9c9e5459e 100755 --- a/scripts/build/ci-deploy/build-deploy.sh +++ b/scripts/build/ci-deploy/build-deploy.sh @@ -1,6 +1,6 @@ #!/bin/bash -_version="1.2.0" +_version="1.2.1" _tag="grafana/grafana-ci-deploy:${_version}" docker build -t $_tag . From 4152e5c16ca2059692cd18071128c1e6beac0b18 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 19 Mar 2019 10:57:17 +0100 Subject: [PATCH 022/101] build: updated deploy container with crcmod. --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index da0e0665285..49fb3776534 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -322,7 +322,7 @@ jobs: deploy-enterprise-master: docker: - - image: grafana/grafana-ci-deploy:1.2.0 + - image: grafana/grafana-ci-deploy:1.2.1 steps: - attach_workspace: at: . @@ -345,7 +345,7 @@ jobs: deploy-enterprise-release: docker: - - image: grafana/grafana-ci-deploy:1.2.0 + - image: grafana/grafana-ci-deploy:1.2.1 steps: - checkout - attach_workspace: @@ -378,7 +378,7 @@ jobs: deploy-master: docker: - - image: grafana/grafana-ci-deploy:1.2.0 + - image: grafana/grafana-ci-deploy:1.2.1 steps: - attach_workspace: at: . @@ -409,7 +409,7 @@ jobs: deploy-release: docker: - - image: grafana/grafana-ci-deploy:1.2.0 + - image: grafana/grafana-ci-deploy:1.2.1 steps: - checkout - attach_workspace: From 4dceb60d204cb9de6849037838f42d8e84f3fc36 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 19 Mar 2019 11:34:01 +0100 Subject: [PATCH 023/101] build: migrates the build container into the main repo. --- scripts/build/ci-build/Dockerfile | 114 +++++++++++++++++++++++++ scripts/build/ci-build/Makefile | 54 ++++++++++++ scripts/build/ci-build/README.md | 20 +++++ scripts/build/ci-build/bootstrap.sh | 5 ++ scripts/build/ci-build/build-deploy.sh | 7 ++ 5 files changed, 200 insertions(+) create mode 100644 scripts/build/ci-build/Dockerfile create mode 100644 scripts/build/ci-build/Makefile create mode 100644 scripts/build/ci-build/README.md create mode 100755 scripts/build/ci-build/bootstrap.sh create mode 100755 scripts/build/ci-build/build-deploy.sh diff --git a/scripts/build/ci-build/Dockerfile b/scripts/build/ci-build/Dockerfile new file mode 100644 index 00000000000..7c6ed58c0e4 --- /dev/null +++ b/scripts/build/ci-build/Dockerfile @@ -0,0 +1,114 @@ +FROM ubuntu:14.04 as toolchain + +ENV OSX_SDK_URL=https://s3.dockerproject.org/darwin/v2/ \ + OSX_SDK=MacOSX10.10.sdk \ + OSX_MIN=10.10 \ + CTNG=1.23.0 + +# FIRST PART +# build osx64 toolchain (stripped of man documentation) +# the toolchain produced is not self contained, it needs clang at runtime +# +# SECOND PART +# build gcc (no g++) centos6-x64 toolchain +# doc: https://crosstool-ng.github.io/docs/ +# apt-get should be all dep to build toolchain +# sed and 1st echo are for convenience to get the toolchain in /tmp/x86_64-centos6-linux-gnu +# other echo are to enable build by root (crosstool-NG refuse to do that by default) +# the last 2 rm are just to save some time and space writing docker layers +# +# THIRD PART +# build fpm and creates a set of deb from gem +# ruby2.0 depends on ruby1.9.3 which is install as default ruby +# rm/ln are here to change that +# created deb depends on rubygem-json but json gem is not build +# so do by hand + + +# might wanna make sure osx cross and the other tarball as well as the packages ends up somewhere other than tmp +# might also wanna put them as their own layer to not have to unpack them every time? + +RUN apt-get update && \ + apt-get install -y \ + clang-3.8 patch libxml2-dev \ + ca-certificates \ + curl \ + git \ + make \ + xz-utils && \ + git clone https://github.com/tpoechtrager/osxcross.git /tmp/osxcross && \ + curl -L ${OSX_SDK_URL}/${OSX_SDK}.tar.xz -o /tmp/osxcross/tarballs/${OSX_SDK}.tar.xz && \ + ln -s /usr/bin/clang-3.8 /usr/bin/clang && \ + ln -s /usr/bin/clang++-3.8 /usr/bin/clang++ && \ + ln -s /usr/bin/llvm-dsymutil-3.8 /usr/bin/dsymutil && \ + UNATTENDED=yes OSX_VERSION_MIN=${OSX_MIN} /tmp/osxcross/build.sh && \ + rm -rf /tmp/osxcross/target/SDK/${OSX_SDK}/usr/share && \ + cd /tmp && \ + tar cfJ osxcross.tar.xz osxcross/target && \ + rm -rf /tmp/osxcross && \ + apt-get install -y \ + bison curl flex gawk gcc g++ gperf help2man libncurses5-dev make patch python-dev texinfo xz-utils && \ + curl -L http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-${CTNG}.tar.xz \ + | tar -xJ -C /tmp/ && \ + cd /tmp/crosstool-ng-${CTNG} && \ + ./configure --enable-local && \ + make && \ + ./ct-ng x86_64-centos6-linux-gnu && \ + sed -i '/CT_PREFIX_DIR=/d' .config && \ + echo 'CT_PREFIX_DIR="/tmp/${CT_HOST:+HOST-${CT_HOST}/}${CT_TARGET}"' >> .config && \ + echo 'CT_EXPERIMENTAL=y' >> .config && \ + echo 'CT_ALLOW_BUILD_AS_ROOT=y' >> .config && \ + echo 'CT_ALLOW_BUILD_AS_ROOT_SURE=y' >> .config && \ + ./ct-ng build && \ + cd /tmp && \ + rm /tmp/x86_64-centos6-linux-gnu/build.log.bz2 && \ + tar cfJ x86_64-centos6-linux-gnu.tar.xz x86_64-centos6-linux-gnu/ && \ + rm -rf /tmp/x86_64-centos6-linux-gnu/ && \ + rm -rf /tmp/crosstool-ng-${CTNG} + +# base image to crossbuild grafana +FROM ubuntu:14.04 + +ENV GOVERSION=1.11.5 \ + PATH=/usr/local/go/bin:$PATH \ + GOPATH=/go \ + NODEVERSION=10.14.2 + +COPY --from=toolchain /tmp/x86_64-centos6-linux-gnu.tar.xz /tmp/ +COPY --from=toolchain /tmp/osxcross.tar.xz /tmp/ + +RUN apt-get update && \ + apt-get install -y \ + clang-3.8 gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf gcc-mingw-w64-x86-64 \ + apt-transport-https \ + ca-certificates \ + curl \ + libfontconfig1 \ + gcc \ + g++ \ + git \ + make \ + rpm \ + xz-utils \ + expect \ + gnupg2 \ + unzip && \ + ln -s /usr/bin/clang-3.8 /usr/bin/clang && \ + ln -s /usr/bin/clang++-3.8 /usr/bin/clang++ && \ + ln -s /usr/bin/llvm-dsymutil-3.8 /usr/bin/dsymutil && \ + curl -L https://nodejs.org/dist/v${NODEVERSION}/node-v${NODEVERSION}-linux-x64.tar.xz \ + | tar -xJ --strip-components=1 -C /usr/local && \ + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb [arch=amd64] https://dl.yarnpkg.com/debian/ stable main" \ + | tee /etc/apt/sources.list.d/yarn.list && \ + apt-get update && apt-get install --no-install-recommends yarn && \ + curl -L https://storage.googleapis.com/golang/go${GOVERSION}.linux-amd64.tar.gz \ + | tar -xz -C /usr/local + +RUN apt-get install -y \ + gcc libc-dev make && \ + gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB && \ + curl -sSL https://get.rvm.io | bash -s stable && \ + /bin/bash -l -c "rvm requirements && rvm install 2.2 && gem install -N fpm" + +ADD ./bootstrap.sh /tmp/bootstrap.sh \ No newline at end of file diff --git a/scripts/build/ci-build/Makefile b/scripts/build/ci-build/Makefile new file mode 100644 index 00000000000..64fa376d7cf --- /dev/null +++ b/scripts/build/ci-build/Makefile @@ -0,0 +1,54 @@ +VERSION="dev" +TAG="grafana/build-container" +USER_ID=$(shell id -u) +GROUP_ID=$(shell id -g) + +all: build deploy + +build: + docker build -t "${TAG}:${VERSION}" . + +deploy: + docker push "${TAG}:${VERSION}" + +run: + docker run -ti \ + -e "CIRCLE_BRANCH=local" \ + -e "CIRCLE_BUILD_NUM=472" \ + ${TAG}:${VERSION} \ + bash + +run-with-local-source-live: + docker run -d \ + -e "CIRCLE_BRANCH=local" \ + -e "CIRCLE_BUILD_NUM=472" \ + -w "/go/src/github.com/grafana/grafana" \ + --name grafana-build \ + -v "${GOPATH}/src/github.com/grafana/grafana:/go/src/github.com/grafana/grafana" \ + ${TAG}:${VERSION} \ + bash -c "/tmp/bootstrap.sh; mkdir /.cache; chown "${USER_ID}:${GROUP_ID}" /.cache; tail -f /dev/null" + docker exec -ti --user "${USER_ID}:${GROUP_ID}" grafana-build bash + +run-with-local-source-copy: + docker run -d \ + -e "CIRCLE_BRANCH=local" \ + -e "CIRCLE_BUILD_NUM=472" \ + -w "/go/src/github.com/grafana/grafana" \ + --name grafana-build \ + ${TAG}:${VERSION} \ + bash -c "/tmp/bootstrap.sh; tail -f /dev/null" + docker cp "${GOPATH}/src/github.com/grafana/grafana" grafana-build:/go/src/github.com/grafana/ + docker exec -ti grafana-build bash + +update-source: + docker cp "${GOPATH}/src/github.com/grafana/grafana" grafana-build:/go/src/github.com/grafana/ + +attach: + docker exec -ti grafana-build bash + +attach-live: + docker exec -ti --user "${USER_ID}:${GROUP_ID}" grafana-build bash + +stop: + docker kill grafana-build + docker rm grafana-build diff --git a/scripts/build/ci-build/README.md b/scripts/build/ci-build/README.md new file mode 100644 index 00000000000..e66ec1b3cf7 --- /dev/null +++ b/scripts/build/ci-build/README.md @@ -0,0 +1,20 @@ +# grafana-build-container +Grafana build container + +## Description + +This is a container for cross-platform builds of Grafana. You can run it locally using the Makefile. + +## Makefile targets + +* `make run-with-local-source-copy` + - Starts the container locally and copies your local sources into the container +* `make run-with-local-source-live` + - Starts the container (as your user) locally and maps your Grafana project dir into the container +* `make update-source` + - Updates the sources in the container from your local sources +* `make stop` + - Kills the container +* `make attach` + - Opens bash within the running container + diff --git a/scripts/build/ci-build/bootstrap.sh b/scripts/build/ci-build/bootstrap.sh new file mode 100755 index 00000000000..2eda345b5ab --- /dev/null +++ b/scripts/build/ci-build/bootstrap.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd /tmp +tar xfJ x86_64-centos6-linux-gnu.tar.xz +tar xfJ osxcross.tar.xz diff --git a/scripts/build/ci-build/build-deploy.sh b/scripts/build/ci-build/build-deploy.sh new file mode 100755 index 00000000000..c2a33e4a9e4 --- /dev/null +++ b/scripts/build/ci-build/build-deploy.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +_version="1.2.3" +_tag="grafana/build-container:${_version}" + +docker build -t $_tag . +docker push $_tag From d075af2b674176ecace3613a1e5d6fdc0d0e7671 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Tue, 19 Mar 2019 13:14:32 +0100 Subject: [PATCH 024/101] adding story and fixing tests --- .../ThresholdsEditor.story.tsx | 16 + .../ThresholdsEditor.test.tsx | 31 +- .../ThresholdsEditor/ThresholdsEditor.tsx | 4 +- .../ThresholdsEditor.test.tsx.snap | 446 +++++++++++++++++- 4 files changed, 477 insertions(+), 20 deletions(-) create mode 100644 packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx new file mode 100644 index 00000000000..8d6112130e7 --- /dev/null +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.story.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; + +import { ThresholdsEditor } from './ThresholdsEditor'; + +const ThresholdsEditorStories = storiesOf('UI/ThresholdsEditor', module); +const thresholds = [{ index: 0, value: -Infinity, color: 'green' }, { index: 1, value: 50, color: 'red' }]; + +ThresholdsEditorStories.add('default', () => { + return ; +}); + +ThresholdsEditorStories.add('with thresholds', () => { + return ; +}); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 38cd8e5c763..db494053d6e 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -1,6 +1,7 @@ import React, { ChangeEvent } from 'react'; import { mount } from 'enzyme'; import { ThresholdsEditor, Props } from './ThresholdsEditor'; +import { colors } from '../../utils'; const setup = (propOverrides?: Partial) => { const props: Props = { @@ -31,7 +32,7 @@ describe('Initialization', () => { it('should add a base threshold if missing', () => { const { instance } = setup(); - expect(instance.state.thresholds).toEqual([{ index: 0, value: -Infinity, color: '#7EB26D' }]); + expect(instance.state.thresholds).toEqual([{ index: 0, value: -Infinity, color: colors[0] }]); }); }); @@ -41,7 +42,7 @@ describe('Add threshold', () => { instance.onAddThreshold(0); - expect(instance.state.thresholds).toEqual([{ index: 0, value: -Infinity, color: '#7EB26D' }]); + expect(instance.state.thresholds).toEqual([{ index: 0, value: -Infinity, color: colors[0] }]); }); it('should add threshold', () => { @@ -50,41 +51,41 @@ describe('Add threshold', () => { instance.onAddThreshold(1); expect(instance.state.thresholds).toEqual([ - { index: 0, value: -Infinity, color: '#7EB26D' }, - { index: 1, value: 50, color: '#EAB839' }, + { index: 0, value: -Infinity, color: colors[0] }, + { index: 1, value: 50, color: colors[2] }, ]); }); it('should add another threshold above a first', () => { const { instance } = setup({ - thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }, { index: 1, value: 50, color: '#EAB839' }], + thresholds: [{ index: 0, value: -Infinity, color: colors[0] }, { index: 1, value: 50, color: colors[2] }], }); instance.onAddThreshold(2); expect(instance.state.thresholds).toEqual([ - { index: 0, value: -Infinity, color: '#7EB26D' }, - { index: 1, value: 50, color: '#EAB839' }, - { index: 2, value: 75, color: '#6ED0E0' }, + { index: 0, value: -Infinity, color: colors[0] }, + { index: 1, value: 50, color: colors[2] }, + { index: 2, value: 75, color: colors[3] }, ]); }); it('should add another threshold between first and second index', () => { const { instance } = setup({ thresholds: [ - { index: 0, value: -Infinity, color: '#7EB26D' }, - { index: 1, value: 50, color: '#EAB839' }, - { index: 2, value: 75, color: '#6ED0E0' }, + { index: 0, value: -Infinity, color: colors[0] }, + { index: 1, value: 50, color: colors[2] }, + { index: 2, value: 75, color: colors[3] }, ], }); instance.onAddThreshold(2); expect(instance.state.thresholds).toEqual([ - { index: 0, value: -Infinity, color: '#7EB26D' }, - { index: 1, value: 50, color: '#EAB839' }, - { index: 2, value: 62.5, color: '#EF843C' }, - { index: 3, value: 75, color: '#6ED0E0' }, + { index: 0, value: -Infinity, color: colors[0] }, + { index: 1, value: 50, color: colors[2] }, + { index: 2, value: 62.5, color: colors[4] }, + { index: 3, value: 75, color: colors[3] }, ]); }); }); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index 3361e1bee46..adacf393a09 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -3,8 +3,8 @@ import { Threshold } from '../../types'; import { ColorPicker } from '..'; import { PanelOptionsGroup } from '..'; import { colors } from '../../utils'; -import { ThemeContext } from '../../themes/ThemeContext'; -import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; +import { ThemeContext } from '../../themes'; +import { getColorFromHexRgbOrName } from '../../utils'; export interface Props { thresholds: Threshold[]; diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap index b0dc025090b..bd0ab03bf51 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap +++ b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap @@ -1,7 +1,447 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Render should render with base threshold 1`] = ` - - - + + +
+
+ + Thresholds + +
+
+
+
+
+ +
+
+
+
+ +
+
+ + + + } + hideAfter={300} + > + +
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+
+
+ + `; From 9f6b793563c9f6fee7a1fc74eda86309b25a2773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 19 Mar 2019 13:41:47 +0100 Subject: [PATCH 025/101] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f09ead1fe3..83d58bb81a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,16 @@ * **Datasource**: Empty user/password was not updated when updating datasources [#15608](https://github.com/grafana/grafana/pull/15608), thx [@Maddin-619](https://github.com/Maddin-619) * **Heatmap**: legend shows wrong colors for small values [#14019](https://github.com/grafana/grafana/issues/14019) +# 6.0.2 (unreleased) + +### Bug Fixes +* **Alerting**: Fixed issue with AlertList panel links resulting in panel not found errors. [#15975](https://github.com/grafana/grafana/pull/15975), [@torkelo](https://github.com/torkelo) +* **Dashboard**: Improved error handling when rendering dashboard panels. [#15970](https://github.com/grafana/grafana/pull/15970), [@torkelo](https://github.com/torkelo) +* **LDAP**: Fix allow anonymous server bind for ldap search. [#15872](https://github.com/grafana/grafana/pull/15872), [@marefr](https://github.com/marefr) +* **Discord**: Fix discord notifier so it doesn't crash when there are no image generated. [#15833](https://github.com/grafana/grafana/pull/15833), [@marefr](https://github.com/marefr) +* **Panel Edit**: Prevent search in VizPicker from stealing focus. [#15802](https://github.com/grafana/grafana/pull/15802), [@peterholmberg](https://github.com/peterholmberg) +* **Datasource admin**: Fixed url of back button in datasource edit page, when root_url configured. [#15759](https://github.com/grafana/grafana/pull/15759), [@dprokop](https://github.com/dprokop) + # 6.0.1 (2019-03-06) ### Bug Fixes From abbb7b81c760a07c0571823c1db7d55550ed9330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 19 Mar 2019 13:42:25 +0100 Subject: [PATCH 026/101] fix(ci): frontend tests was accidentially commented out --- scripts/circle-test-frontend.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/circle-test-frontend.sh b/scripts/circle-test-frontend.sh index 9d945a03b7f..423dee84954 100755 --- a/scripts/circle-test-frontend.sh +++ b/scripts/circle-test-frontend.sh @@ -14,7 +14,7 @@ function exit_if_fail { start=$(date +%s) exit_if_fail npm run prettier:check -# exit_if_fail npm run test +exit_if_fail npm run test end=$(date +%s) seconds=$((end - start)) From e294252e926232b93290d07c4b851622169942c1 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 5 Mar 2019 15:07:09 +0100 Subject: [PATCH 027/101] dashboards: user automatically becomes admin for created dashboards --- pkg/services/dashboards/dashboard_service.go | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pkg/services/dashboards/dashboard_service.go b/pkg/services/dashboards/dashboard_service.go index f8df6763994..424980c1a86 100644 --- a/pkg/services/dashboards/dashboard_service.go +++ b/pkg/services/dashboards/dashboard_service.go @@ -238,6 +238,49 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Da return nil, err } + // TODO: check if dashboard exists already. could have id set but not exist + if dto.Dashboard.Id == 0 && dto.Dashboard.Uid == "" { + rtEditor := models.ROLE_EDITOR + rtViewer := models.ROLE_VIEWER + + items := []*models.DashboardAcl{ + { + OrgId: dr.orgId, + DashboardId: cmd.Result.Id, + UserId: cmd.Result.CreatedBy, + Permission: models.PERMISSION_ADMIN, + Created: time.Now(), + Updated: time.Now(), + }, + { + OrgId: dr.orgId, + DashboardId: cmd.Result.Id, + Role: &rtEditor, + Permission: models.PERMISSION_EDIT, + Created: time.Now(), + Updated: time.Now(), + }, + { + OrgId: dr.orgId, + DashboardId: cmd.Result.Id, + Role: &rtViewer, + Permission: models.PERMISSION_VIEW, + Created: time.Now(), + Updated: time.Now(), + }, + } + + aclCmd := &models.UpdateDashboardAclCommand{ + DashboardId: cmd.Result.Id, + Items: items, + } + + if err = bus.Dispatch(aclCmd); err != nil { + return cmd.Result, err + } + + } + return cmd.Result, nil } From e174f7c20bd26fba79442a480528c54bf6060716 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 5 Mar 2019 15:25:02 +0100 Subject: [PATCH 028/101] folders: admin for created folders --- pkg/services/dashboards/folder_service.go | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pkg/services/dashboards/folder_service.go b/pkg/services/dashboards/folder_service.go index b521b0e5213..917852f4781 100644 --- a/pkg/services/dashboards/folder_service.go +++ b/pkg/services/dashboards/folder_service.go @@ -5,6 +5,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/search" + "time" ) // FolderService service for operating on folders @@ -114,6 +115,45 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er return toFolderError(err) } + rtEditor := models.ROLE_EDITOR + rtViewer := models.ROLE_VIEWER + + items := []*models.DashboardAcl{ + { + OrgId: dr.orgId, + DashboardId: saveDashboardCmd.Result.Id, + UserId: saveDashboardCmd.Result.CreatedBy, + Permission: models.PERMISSION_ADMIN, + Created: time.Now(), + Updated: time.Now(), + }, + { + OrgId: dr.orgId, + DashboardId: saveDashboardCmd.Result.Id, + Role: &rtEditor, + Permission: models.PERMISSION_EDIT, + Created: time.Now(), + Updated: time.Now(), + }, + { + OrgId: dr.orgId, + DashboardId: saveDashboardCmd.Result.Id, + Role: &rtViewer, + Permission: models.PERMISSION_VIEW, + Created: time.Now(), + Updated: time.Now(), + }, + } + + aclCmd := &models.UpdateDashboardAclCommand{ + DashboardId: saveDashboardCmd.Result.Id, + Items: items, + } + + if err = bus.Dispatch(aclCmd); err != nil { + return err + } + query := models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id} dashFolder, err = getFolder(query) if err != nil { From c8c004095cc5707761560bfc4163a056ddb5d96a Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 5 Mar 2019 16:44:41 +0100 Subject: [PATCH 029/101] permissions: broken out func for making creator admin. --- pkg/api/api.go | 2 +- pkg/api/dashboard.go | 11 +++- pkg/api/folder.go | 2 +- pkg/services/dashboards/acl_service.go | 62 ++++++++++++++++++++ pkg/services/dashboards/dashboard_service.go | 43 -------------- 5 files changed, 74 insertions(+), 46 deletions(-) create mode 100644 pkg/services/dashboards/acl_service.go diff --git a/pkg/api/api.go b/pkg/api/api.go index f3dc35b6b06..b5214f93d35 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -265,7 +265,7 @@ func (hs *HTTPServer) registerRoutes() { apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) { folderRoute.Get("/", Wrap(GetFolders)) folderRoute.Get("/id/:id", Wrap(GetFolderByID)) - folderRoute.Post("/", bind(m.CreateFolderCommand{}), Wrap(CreateFolder)) + folderRoute.Post("/", bind(m.CreateFolderCommand{}), Wrap(hs.CreateFolder)) folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) { folderUidRoute.Get("/", Wrap(GetFolderByUID)) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 07c4f75778d..016146a5c61 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -213,7 +213,8 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) dash := cmd.GetDashboardModel() - if dash.Id == 0 && dash.Uid == "" { + newDashboard := dash.Id == 0 && dash.Uid == "" + if newDashboard { limitReached, err := hs.QuotaService.QuotaReached(c, "dashboard") if err != nil { return Error(500, "failed to get quota", err) @@ -276,6 +277,14 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) return Error(500, "Failed to save dashboard", err) } + if hs.Cfg.EditorsCanOwn && newDashboard { + aclService := dashboards.NewAclService() + err := aclService.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id) + if err != nil { + hs.log.Error("Could not make user admin", "error", err) + } + } + c.TimeRequest(metrics.M_Api_Dashboard_Save) return JSON(200, util.DynMap{ "status": "success", diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 0e08343b556..4e106dc6452 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -54,7 +54,7 @@ func GetFolderByID(c *m.ReqContext) Response { return JSON(200, toFolderDto(g, folder)) } -func CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) Response { +func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) Response { s := dashboards.NewFolderService(c.OrgId, c.SignedInUser) err := s.CreateFolder(&cmd) if err != nil { diff --git a/pkg/services/dashboards/acl_service.go b/pkg/services/dashboards/acl_service.go new file mode 100644 index 00000000000..79b55470093 --- /dev/null +++ b/pkg/services/dashboards/acl_service.go @@ -0,0 +1,62 @@ +package dashboards + +import ( + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/models" + "time" +) + +// NewService factory for creating a new dashboard service +var NewAclService = func() *AclService { + return &AclService{ + log: log.New("dashboard-acl-service"), + } +} + +type AclService struct { + log log.Logger +} + +func (as *AclService) MakeUserAdmin(orgId int64, userId int64, dashboardId int64) error { + rtEditor := models.ROLE_EDITOR + rtViewer := models.ROLE_VIEWER + + items := []*models.DashboardAcl{ + { + OrgId: orgId, + DashboardId: dashboardId, + UserId: userId, + Permission: models.PERMISSION_ADMIN, + Created: time.Now(), + Updated: time.Now(), + }, + { + OrgId: orgId, + DashboardId: dashboardId, + Role: &rtEditor, + Permission: models.PERMISSION_EDIT, + Created: time.Now(), + Updated: time.Now(), + }, + { + OrgId: orgId, + DashboardId: dashboardId, + Role: &rtViewer, + Permission: models.PERMISSION_VIEW, + Created: time.Now(), + Updated: time.Now(), + }, + } + + aclCmd := &models.UpdateDashboardAclCommand{ + DashboardId: dashboardId, + Items: items, + } + + if err := bus.Dispatch(aclCmd); err != nil { + return err + } + + return nil +} diff --git a/pkg/services/dashboards/dashboard_service.go b/pkg/services/dashboards/dashboard_service.go index 424980c1a86..f8df6763994 100644 --- a/pkg/services/dashboards/dashboard_service.go +++ b/pkg/services/dashboards/dashboard_service.go @@ -238,49 +238,6 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Da return nil, err } - // TODO: check if dashboard exists already. could have id set but not exist - if dto.Dashboard.Id == 0 && dto.Dashboard.Uid == "" { - rtEditor := models.ROLE_EDITOR - rtViewer := models.ROLE_VIEWER - - items := []*models.DashboardAcl{ - { - OrgId: dr.orgId, - DashboardId: cmd.Result.Id, - UserId: cmd.Result.CreatedBy, - Permission: models.PERMISSION_ADMIN, - Created: time.Now(), - Updated: time.Now(), - }, - { - OrgId: dr.orgId, - DashboardId: cmd.Result.Id, - Role: &rtEditor, - Permission: models.PERMISSION_EDIT, - Created: time.Now(), - Updated: time.Now(), - }, - { - OrgId: dr.orgId, - DashboardId: cmd.Result.Id, - Role: &rtViewer, - Permission: models.PERMISSION_VIEW, - Created: time.Now(), - Updated: time.Now(), - }, - } - - aclCmd := &models.UpdateDashboardAclCommand{ - DashboardId: cmd.Result.Id, - Items: items, - } - - if err = bus.Dispatch(aclCmd); err != nil { - return cmd.Result, err - } - - } - return cmd.Result, nil } From da3dcd19184dacf4533b99d8532c6ea217378d25 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 5 Mar 2019 16:53:16 +0100 Subject: [PATCH 030/101] folder: uses service to make user admin of created folder. --- pkg/api/folder.go | 5 +++ pkg/services/dashboards/folder_service.go | 40 ----------------------- 2 files changed, 5 insertions(+), 40 deletions(-) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 4e106dc6452..4e66439219d 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -61,6 +61,11 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R return toFolderError(err) } + if hs.Cfg.EditorsCanOwn { + aclService := dashboards.NewAclService() + aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id) + } + g := guardian.New(cmd.Result.Id, c.OrgId, c.SignedInUser) return JSON(200, toFolderDto(g, cmd.Result)) } diff --git a/pkg/services/dashboards/folder_service.go b/pkg/services/dashboards/folder_service.go index 917852f4781..b521b0e5213 100644 --- a/pkg/services/dashboards/folder_service.go +++ b/pkg/services/dashboards/folder_service.go @@ -5,7 +5,6 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/search" - "time" ) // FolderService service for operating on folders @@ -115,45 +114,6 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er return toFolderError(err) } - rtEditor := models.ROLE_EDITOR - rtViewer := models.ROLE_VIEWER - - items := []*models.DashboardAcl{ - { - OrgId: dr.orgId, - DashboardId: saveDashboardCmd.Result.Id, - UserId: saveDashboardCmd.Result.CreatedBy, - Permission: models.PERMISSION_ADMIN, - Created: time.Now(), - Updated: time.Now(), - }, - { - OrgId: dr.orgId, - DashboardId: saveDashboardCmd.Result.Id, - Role: &rtEditor, - Permission: models.PERMISSION_EDIT, - Created: time.Now(), - Updated: time.Now(), - }, - { - OrgId: dr.orgId, - DashboardId: saveDashboardCmd.Result.Id, - Role: &rtViewer, - Permission: models.PERMISSION_VIEW, - Created: time.Now(), - Updated: time.Now(), - }, - } - - aclCmd := &models.UpdateDashboardAclCommand{ - DashboardId: saveDashboardCmd.Result.Id, - Items: items, - } - - if err = bus.Dispatch(aclCmd); err != nil { - return err - } - query := models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id} dashFolder, err = getFolder(query) if err != nil { From 124fb743e8d9edfdaf58559e1925dc4a6dd13489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 6 Mar 2019 08:09:34 +0100 Subject: [PATCH 031/101] teams: make test cases pass again --- pkg/api/dashboard_test.go | 4 ++++ pkg/api/folder_test.go | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 923bf57ce8a..d58e2246eec 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -972,8 +972,12 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d Convey(desc+" "+url, func() { defer bus.ClearBusHandlers() + cfg := setting.NewCfg() + cfg.EditorsCanOwn = false + hs := HTTPServer{ Bus: bus.GetBus(), + Cfg: cfg, } sc := setupScenarioContext(url) diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index 880de338c8f..914acf5797e 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -9,6 +9,7 @@ import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" ) @@ -141,12 +142,20 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa Convey(desc+" "+url, func() { defer bus.ClearBusHandlers() + cfg := setting.NewCfg() + cfg.EditorsCanOwn = false + + hs := HTTPServer{ + Bus: bus.GetBus(), + Cfg: cfg, + } + sc := setupScenarioContext(url) sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID} - return CreateFolder(c, cmd) + return hs.CreateFolder(c, cmd) }) origNewFolderService := dashboards.NewFolderService From efbd93f824ff9c83bed73dd30150dd15c95f3546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 6 Mar 2019 08:40:42 +0100 Subject: [PATCH 032/101] teams: show teams and plugins for editors that can own --- pkg/api/folder_test.go | 2 +- pkg/api/index.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index 914acf5797e..d5e4ee418cd 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -143,7 +143,7 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa defer bus.ClearBusHandlers() cfg := setting.NewCfg() - cfg.EditorsCanOwn = false + cfg.EditorsCanOwn = true hs := HTTPServer{ Bus: bus.GetBus(), diff --git a/pkg/api/index.go b/pkg/api/index.go index 904a885b171..88c4b7e929d 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -327,6 +327,34 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er }) } + if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanOwn { + cfgNode := &dtos.NavLink{ + Id: "cfg", + Text: "Configuration", + SubTitle: "Organization: " + c.OrgName, + Icon: "gicon gicon-cog", + Url: setting.AppSubUrl + "/org/teams", + Children: []*dtos.NavLink{ + { + Text: "Teams", + Id: "teams", + Description: "Manage org groups", + Icon: "gicon gicon-team", + Url: setting.AppSubUrl + "/org/teams", + }, + { + Text: "Plugins", + Id: "plugins", + Description: "View and configure plugins", + Icon: "gicon gicon-plugins", + Url: setting.AppSubUrl + "/plugins", + }, + }, + } + + data.NavTree = append(data.NavTree, cfgNode) + } + data.NavTree = append(data.NavTree, &dtos.NavLink{ Text: "Help", SubTitle: fmt.Sprintf(`%s v%s (%s)`, setting.ApplicationName, setting.BuildVersion, setting.BuildCommit), From 22e098b83019bb048212a704a406e84316f499c0 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 6 Mar 2019 10:27:38 +0100 Subject: [PATCH 033/101] teams: editors can work with teams. --- pkg/api/api.go | 7 ++++--- pkg/middleware/auth.go | 17 +++++++++++++++++ public/app/routes/routes.ts | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index b5214f93d35..50700108394 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -14,6 +14,7 @@ func (hs *HTTPServer) registerRoutes() { reqGrafanaAdmin := middleware.ReqGrafanaAdmin reqEditorRole := middleware.ReqEditorRole reqOrgAdmin := middleware.ReqOrgAdmin + reqAdminOrEditorCanAdmin := middleware.EditorCanAdmin(hs.Cfg.EditorsCanOwn) redirectFromLegacyDashboardURL := middleware.RedirectFromLegacyDashboardURL() redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL() quota := middleware.Quota(hs.QuotaService) @@ -41,8 +42,8 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/org/users", reqOrgAdmin, hs.Index) r.Get("/org/users/new", reqOrgAdmin, hs.Index) r.Get("/org/users/invite", reqOrgAdmin, hs.Index) - r.Get("/org/teams", reqOrgAdmin, hs.Index) - r.Get("/org/teams/*", reqOrgAdmin, hs.Index) + r.Get("/org/teams", reqAdminOrEditorCanAdmin, hs.Index) + r.Get("/org/teams/*", reqAdminOrEditorCanAdmin, hs.Index) r.Get("/org/apikeys/", reqOrgAdmin, hs.Index) r.Get("/dashboard/import/", reqSignedIn, hs.Index) r.Get("/configuration", reqGrafanaAdmin, hs.Index) @@ -161,7 +162,7 @@ func (hs *HTTPServer) registerRoutes() { teamsRoute.Delete("/:teamId/members/:userId", Wrap(RemoveTeamMember)) teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences)) teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences)) - }, reqOrgAdmin) + }, reqAdminOrEditorCanAdmin) // team without requirement of user to be org admin apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index e06409211eb..6bf37e7fd50 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -86,3 +86,20 @@ func Auth(options *AuthOptions) macaron.Handler { } } } + +func EditorCanAdmin(enabled bool) macaron.Handler { + return func(c *m.ReqContext) { + ok := false + if c.OrgRole == m.ROLE_ADMIN { + ok = true + } + + if c.OrgRole == m.ROLE_EDITOR && enabled { + ok = true + } + + if !ok { + accessForbidden(c) + } + } +} diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index 442fb5acb0c..06af66d7d5d 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -207,7 +207,7 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { .when('/org/teams/edit/:id/:page?', { template: '', resolve: { - roles: () => ['Admin'], + roles: () => (config.editorsCanOwn ? ['Editor', 'Admin'] : ['Admin']), component: () => TeamPages, }, }) From af4994ba1623482d42b2b79af80b6d9ed01b46b5 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 6 Mar 2019 11:47:18 +0100 Subject: [PATCH 034/101] teams: editor added as admin for created teams. --- pkg/api/api.go | 2 +- pkg/api/team.go | 13 ++++++++++++- pkg/models/team_member.go | 20 +++++++++++--------- pkg/services/sqlstore/migrations/team_mig.go | 6 ++++++ pkg/services/sqlstore/team.go | 13 +++++++------ 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 50700108394..3cbcc8029a3 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -154,7 +154,7 @@ func (hs *HTTPServer) registerRoutes() { // team (admin permission required) apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { - teamsRoute.Post("/", bind(m.CreateTeamCommand{}), Wrap(CreateTeam)) + teamsRoute.Post("/", bind(m.CreateTeamCommand{}), Wrap(hs.CreateTeam)) teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(UpdateTeam)) teamsRoute.Delete("/:teamId", Wrap(DeleteTeamByID)) teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) diff --git a/pkg/api/team.go b/pkg/api/team.go index 32265e5d018..5c58a0df71c 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -8,7 +8,7 @@ import ( ) // POST /api/teams -func CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Response { +func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Response { cmd.OrgId = c.OrgId if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNameTaken { @@ -17,6 +17,17 @@ func CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Response { return Error(500, "Failed to create Team", err) } + if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanOwn { + addMemberCmd := m.AddTeamMemberCommand{ + UserId: c.SignedInUser.UserId, + OrgId: cmd.OrgId, + TeamId: cmd.Result.Id, + Permission: int64(m.PERMISSION_ADMIN), + } + err := bus.Dispatch(&addMemberCmd) + c.Logger.Error("Could not add creator to team.", "error", err) + } + return JSON(200, &util.DynMap{ "teamId": cmd.Result.Id, "message": "Team created", diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index dd64787f465..01659cb0347 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -12,11 +12,12 @@ var ( // TeamMember model type TeamMember struct { - Id int64 - OrgId int64 - TeamId int64 - UserId int64 - External bool + Id int64 + OrgId int64 + TeamId int64 + UserId int64 + External bool + Permission int64 Created time.Time Updated time.Time @@ -26,10 +27,11 @@ type TeamMember struct { // COMMANDS type AddTeamMemberCommand struct { - UserId int64 `json:"userId" binding:"Required"` - OrgId int64 `json:"-"` - TeamId int64 `json:"-"` - External bool `json:"-"` + UserId int64 `json:"userId" binding:"Required"` + OrgId int64 `json:"-"` + TeamId int64 `json:"-"` + External bool `json:"-"` + Permission int64 `json:"-"` } type RemoveTeamMemberCommand struct { diff --git a/pkg/services/sqlstore/migrations/team_mig.go b/pkg/services/sqlstore/migrations/team_mig.go index 34c46ad13cf..1ec27ee926d 100644 --- a/pkg/services/sqlstore/migrations/team_mig.go +++ b/pkg/services/sqlstore/migrations/team_mig.go @@ -54,4 +54,10 @@ func addTeamMigrations(mg *Migrator) { mg.AddMigration("Add column external to team_member table", NewAddColumnMigration(teamMemberV1, &Column{ Name: "external", Type: DB_Bool, Nullable: true, })) + + mg.AddMigration("Add column permission to team_member table", NewAddColumnMigration(teamMemberV1, &Column{ + Name: "permission", + Type: DB_BigInt, + Nullable: true, + })) } diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 83593e6f2d7..c11a2d077ed 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -240,12 +240,13 @@ func AddTeamMember(cmd *m.AddTeamMemberCommand) error { } entity := m.TeamMember{ - OrgId: cmd.OrgId, - TeamId: cmd.TeamId, - UserId: cmd.UserId, - External: cmd.External, - Created: time.Now(), - Updated: time.Now(), + OrgId: cmd.OrgId, + TeamId: cmd.TeamId, + UserId: cmd.UserId, + External: cmd.External, + Created: time.Now(), + Updated: time.Now(), + Permission: cmd.Permission, } _, err := sess.Insert(&entity) From 7888457aaee0aa233bd2696b75436d6fc686ff06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 6 Mar 2019 12:27:18 +0100 Subject: [PATCH 035/101] teams: basic ui for permission in team members view --- pkg/models/team_member.go | 17 +- pkg/services/sqlstore/team.go | 2 +- public/app/features/teams/TeamMembers.tsx | 18 +- .../app/features/teams/__mocks__/teamMocks.ts | 2 + .../__snapshots__/TeamMembers.test.tsx.snap | 429 ++++++++++++++++++ public/app/types/acl.ts | 20 + public/app/types/teams.ts | 1 + 7 files changed, 478 insertions(+), 11 deletions(-) diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index 01659cb0347..813455d3d2b 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -55,12 +55,13 @@ type GetTeamMembersQuery struct { // Projections and DTOs type TeamMemberDTO struct { - OrgId int64 `json:"orgId"` - TeamId int64 `json:"teamId"` - UserId int64 `json:"userId"` - External bool `json:"-"` - Email string `json:"email"` - Login string `json:"login"` - AvatarUrl string `json:"avatarUrl"` - Labels []string `json:"labels"` + OrgId int64 `json:"orgId"` + TeamId int64 `json:"teamId"` + UserId int64 `json:"userId"` + External bool `json:"-"` + Email string `json:"email"` + Login string `json:"login"` + AvatarUrl string `json:"avatarUrl"` + Labels []string `json:"labels"` + Permission int64 `json:"permission"` } diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index c11a2d077ed..546e0231706 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -294,7 +294,7 @@ func GetTeamMembers(query *m.GetTeamMembersQuery) error { if query.External { sess.Where("team_member.external=?", dialect.BooleanStr(true)) } - sess.Cols("team_member.org_id", "team_member.team_id", "team_member.user_id", "user.email", "user.login", "team_member.external") + sess.Cols("team_member.org_id", "team_member.team_id", "team_member.user_id", "user.email", "user.login", "team_member.external", "team_member.permission") sess.Asc("user.login", "user.email") err := sess.Find(&query.Result) diff --git a/public/app/features/teams/TeamMembers.tsx b/public/app/features/teams/TeamMembers.tsx index e5c3aaafef0..341d9311b53 100644 --- a/public/app/features/teams/TeamMembers.tsx +++ b/public/app/features/teams/TeamMembers.tsx @@ -2,9 +2,9 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import SlideDown from 'app/core/components/Animations/SlideDown'; import { UserPicker } from 'app/core/components/Select/UserPicker'; -import { DeleteButton } from '@grafana/ui'; +import { DeleteButton, Select } from '@grafana/ui'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; -import { TeamMember, User } from 'app/types'; +import { TeamMember, User, teamsPermissionLevels } from 'app/types'; import { loadTeamMembers, addTeamMember, removeTeamMember, setSearchMemberQuery } from './state/actions'; import { getSearchMemberQuery, getTeamMembers } from './state/selectors'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; @@ -70,6 +70,7 @@ export class TeamMembers extends PureComponent { } renderMember(member: TeamMember, syncEnabled: boolean) { + const currentPermissionLevel = teamsPermissionLevels.find(dp => dp.value === member.permission); return ( @@ -77,6 +78,18 @@ export class TeamMembers extends PureComponent { {member.login} {member.email} + +
+ +
+ + @@ -205,6 +253,48 @@ exports[`Render should render team members 1`] = ` test@test.com + +
+ +
+ + @@ -255,6 +387,48 @@ exports[`Render should render team members 1`] = ` test@test.com + +
+ +
+ + @@ -363,6 +579,9 @@ exports[`Render should render team members when sync enabled 1`] = ` Email + + Permission + test@test.com + +
+ +
+ + test@test.com + +
+ +
+ + test@test.com + +
+ {}} - className="gf-form-select-box__control--menu-right" - value={currentPermissionLevel} - isDisabled={true} - /> -
- {' '} + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ {}} + onChange={item => this.onPermissionChange(item, member)} className="gf-form-select-box__control--menu-right" - value={currentPermissionLevel} - isDisabled={true} + value={teamsPermissionLevels.find(dp => dp.value === member.permission)} />
@@ -176,6 +188,7 @@ const mapDispatchToProps = { addTeamMember, removeTeamMember, setSearchMemberQuery, + updateTeamMember, }; export default connect( diff --git a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap index fc168457334..01d7b40ec61 100644 --- a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap @@ -206,7 +206,7 @@ exports[`Render should render team members 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -276,7 +276,7 @@ exports[`Render should render team members 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -346,7 +346,7 @@ exports[`Render should render team members 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -416,7 +416,7 @@ exports[`Render should render team members 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -486,7 +486,7 @@ exports[`Render should render team members 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -649,7 +649,7 @@ exports[`Render should render team members when sync enabled 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -735,7 +735,7 @@ exports[`Render should render team members when sync enabled 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -821,7 +821,7 @@ exports[`Render should render team members when sync enabled 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -907,7 +907,7 @@ exports[`Render should render team members when sync enabled 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} @@ -993,7 +993,7 @@ exports[`Render should render team members when sync enabled 1`] = ` backspaceRemovesValue={true} className="gf-form-select-box__control--menu-right" isClearable={false} - isDisabled={true} + isDisabled={false} isLoading={false} isMulti={false} isSearchable={false} diff --git a/public/app/features/teams/state/actions.ts b/public/app/features/teams/state/actions.ts index d948dc1c5a3..e2582839233 100644 --- a/public/app/features/teams/state/actions.ts +++ b/public/app/features/teams/state/actions.ts @@ -160,3 +160,12 @@ export function deleteTeam(id: number): ThunkResult { dispatch(loadTeams()); }; } + +export function updateTeamMember(member: TeamMember): ThunkResult { + return async dispatch => { + await getBackendSrv().put(`/api/teams/${member.teamId}/members/${member.userId}`, { + permission: member.permission, + }); + dispatch(loadTeamMembers()); + }; +} From 074ebf0f482e9f1a5e445c19fe947d6e8e2bd989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 8 Mar 2019 11:56:48 +0100 Subject: [PATCH 040/101] teams: only write error message if error --- pkg/api/team.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/api/team.go b/pkg/api/team.go index 5c58a0df71c..da72bda472b 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -24,8 +24,10 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo TeamId: cmd.Result.Id, Permission: int64(m.PERMISSION_ADMIN), } - err := bus.Dispatch(&addMemberCmd) - c.Logger.Error("Could not add creator to team.", "error", err) + + if err := bus.Dispatch(&addMemberCmd); err != nil { + c.Logger.Error("Could not add creator to team.", "error", err) + } } return JSON(200, &util.DynMap{ From 3c74ac304490aa3ef46f045fd7d61d718253c5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 8 Mar 2019 12:25:10 +0100 Subject: [PATCH 041/101] teams: update only the selected user --- pkg/services/sqlstore/team.go | 2 +- public/app/types/acl.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 4822af7009c..0a5383d993f 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -272,7 +272,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { } member.Permission = cmd.Permission - _, err = sess.Update(member) + _, err = sess.Where("org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId).Update(member) return err }) diff --git a/public/app/types/acl.ts b/public/app/types/acl.ts index 8134ddb1749..12016732222 100644 --- a/public/app/types/acl.ts +++ b/public/app/types/acl.ts @@ -100,7 +100,7 @@ export const dashboardPermissionLevels: DashboardPermissionInfo[] = [ ]; export enum TeamPermissionLevel { - Member = 0, + Member = 1, Admin = 4, } From 1315a67022c5e117e36cfb14cd59cd0586c4c079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 8 Mar 2019 13:10:03 +0100 Subject: [PATCH 042/101] teams: make sure we use TeamPermissionLevel enum --- .../app/features/teams/__mocks__/teamMocks.ts | 6 +-- .../__snapshots__/TeamMembers.test.tsx.snap | 40 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/public/app/features/teams/__mocks__/teamMocks.ts b/public/app/features/teams/__mocks__/teamMocks.ts index 6d4b5ea3aad..3f0830eda16 100644 --- a/public/app/features/teams/__mocks__/teamMocks.ts +++ b/public/app/features/teams/__mocks__/teamMocks.ts @@ -1,4 +1,4 @@ -import { Team, TeamGroup, TeamMember } from 'app/types'; +import { Team, TeamGroup, TeamMember, TeamPermissionLevel } from 'app/types'; export const getMultipleMockTeams = (numberOfTeams: number): Team[] => { const teams: Team[] = []; @@ -36,7 +36,7 @@ export const getMockTeamMembers = (amount: number): TeamMember[] => { email: 'test@test.com', login: `testUser-${i}`, labels: ['label 1', 'label 2'], - permission: 0, + permission: TeamPermissionLevel.Member, }); } @@ -51,7 +51,7 @@ export const getMockTeamMember = (): TeamMember => { email: 'test@test.com', login: 'testUser', labels: [], - permission: 0, + permission: TeamPermissionLevel.Member, }; }; diff --git a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap index 01d7b40ec61..c356727ebeb 100644 --- a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap @@ -218,7 +218,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -231,7 +231,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -288,7 +288,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -301,7 +301,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -358,7 +358,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -371,7 +371,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -428,7 +428,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -441,7 +441,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -498,7 +498,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -511,7 +511,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -661,7 +661,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -674,7 +674,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -747,7 +747,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -760,7 +760,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -833,7 +833,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -846,7 +846,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -919,7 +919,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -932,7 +932,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} @@ -1005,7 +1005,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, }, Object { "description": "Can add/remove permissions and delete team.", @@ -1018,7 +1018,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 0, + "value": 1, } } width={null} From 3c46b786d2a58f23ebe9ae6fd8b846ffbcc21cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 8 Mar 2019 13:53:42 +0100 Subject: [PATCH 043/101] teams: change back to permissionlevel for Member to 0 --- pkg/services/sqlstore/team.go | 2 +- .../__snapshots__/TeamMembers.test.tsx.snap | 40 +++++++++---------- public/app/types/acl.ts | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 0a5383d993f..f7f7d7fc2cb 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -272,7 +272,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { } member.Permission = cmd.Permission - _, err = sess.Where("org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId).Update(member) + _, err = sess.Cols("permission").Where("org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId).Update(member) return err }) diff --git a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap index c356727ebeb..01d7b40ec61 100644 --- a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap @@ -218,7 +218,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -231,7 +231,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -288,7 +288,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -301,7 +301,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -358,7 +358,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -371,7 +371,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -428,7 +428,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -441,7 +441,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -498,7 +498,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -511,7 +511,7 @@ exports[`Render should render team members 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -661,7 +661,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -674,7 +674,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -747,7 +747,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -760,7 +760,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -833,7 +833,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -846,7 +846,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -919,7 +919,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -932,7 +932,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} @@ -1005,7 +1005,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, }, Object { "description": "Can add/remove permissions and delete team.", @@ -1018,7 +1018,7 @@ exports[`Render should render team members when sync enabled 1`] = ` Object { "description": "Is team member", "label": "Member", - "value": 1, + "value": 0, } } width={null} diff --git a/public/app/types/acl.ts b/public/app/types/acl.ts index 12016732222..8134ddb1749 100644 --- a/public/app/types/acl.ts +++ b/public/app/types/acl.ts @@ -100,7 +100,7 @@ export const dashboardPermissionLevels: DashboardPermissionInfo[] = [ ]; export enum TeamPermissionLevel { - Member = 1, + Member = 0, Admin = 4, } From 5adde259d307ac36277b419475712503cf25d63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 8 Mar 2019 14:37:21 +0100 Subject: [PATCH 044/101] teams: team update test --- pkg/api/team.go | 3 ++- pkg/services/teams/team.go | 10 ++++++++ pkg/services/teams/teams_test.go | 42 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 pkg/services/teams/team.go create mode 100644 pkg/services/teams/teams_test.go diff --git a/pkg/api/team.go b/pkg/api/team.go index da72bda472b..3d357fa9763 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -4,6 +4,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/teams" "github.com/grafana/grafana/pkg/util" ) @@ -40,7 +41,7 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") - if err := bus.Dispatch(&cmd); err != nil { + if err := teams.UpdateTeam(c.SignedInUser, &cmd); err != nil { if err == m.ErrTeamNameTaken { return Error(400, "Team name taken", err) } diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go new file mode 100644 index 00000000000..4bd4b78d587 --- /dev/null +++ b/pkg/services/teams/team.go @@ -0,0 +1,10 @@ +package teams + +import ( + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func UpdateTeam(user m.SignedInUser, cmd *m.UpdateTeamCommand) error { + return bus.Dispatch(cmd) +} diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go new file mode 100644 index 00000000000..aaa19440bb4 --- /dev/null +++ b/pkg/services/teams/teams_test.go @@ -0,0 +1,42 @@ +package teams + +import ( + . "github.com/smartystreets/goconvey/convey" + m "github.com/grafana/grafana/pkg/models" +) + + +func TestUpdateTeam(t *testing.T) { + Convey("Updating a team as an editor", t, func() { + Convey("Given an editor and a team he isn't a member of", func() { + + UpdateTeam(editor, m.UpdateTeamCommand{ + Id: 0, + Name: "", + Email: "", + OrgId: 0, + }) + }) + + // the editor should not be able to update the team if they aren't members of it + + fakeDash := m.NewDashboard("Child dash") + fakeDash.Id = 1 + fakeDash.FolderId = 1 + fakeDash.HasAcl = false + + bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { + dashboards := []*m.Dashboard{fakeDash} + query.Result = dashboards + return nil + }) + + var getDashboardQueries []*m.GetDashboardQuery + + bus.AddHandler("test", func(query *m.GetDashboardQuery) error { + query.Result = fakeDash + getDashboardQueries = append(getDashboardQueries, query) + return nil + }) + + bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error { From 90e9fda90c9904e273c30ed6563eb4e15de915e6 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 8 Mar 2019 15:46:15 +0100 Subject: [PATCH 045/101] teams: start of team update guardian for editors --- pkg/models/team.go | 8 +- pkg/services/teams/team.go | 35 ++++++ pkg/services/teams/teams_test.go | 176 +++++++++++++++++++++++++------ 3 files changed, 183 insertions(+), 36 deletions(-) diff --git a/pkg/models/team.go b/pkg/models/team.go index 61285db3a5f..bd0d803d9d3 100644 --- a/pkg/models/team.go +++ b/pkg/models/team.go @@ -7,9 +7,11 @@ import ( // Typed errors var ( - ErrTeamNotFound = errors.New("Team not found") - ErrTeamNameTaken = errors.New("Team name is taken") - ErrTeamMemberNotFound = errors.New("Team member not found") + ErrTeamNotFound = errors.New("Team not found") + ErrTeamNameTaken = errors.New("Team name is taken") + ErrTeamMemberNotFound = errors.New("Team member not found") + ErrNotAllowedToUpdateTeam = errors.New("User not allowed to update team") + ErrNotAllowedToUpdateTeamInDifferentOrg = errors.New("User not allowed to update team in another org") ) // Team model diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index 4bd4b78d587..7ff18820b62 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -5,6 +5,41 @@ import ( m "github.com/grafana/grafana/pkg/models" ) +func canUpdateTeam(orgId int64, teamId int64, user m.SignedInUser) error { + if user.OrgRole == m.ROLE_ADMIN { + return nil + } + + if user.OrgId != orgId { + return m.ErrNotAllowedToUpdateTeamInDifferentOrg + } + + cmd := m.GetTeamMembersQuery{ + OrgId: orgId, + TeamId: teamId, + UserId: user.UserId, + // TODO: do we need to do something special about external users + // External: false, + } + + if err := bus.Dispatch(&cmd); err != nil { + // TODO: look into how we want to do logging + return err + } + + for _, member := range cmd.Result { + if member.UserId == user.UserId && member.Permission == int64(m.PERMISSION_ADMIN) { + return nil + } + } + + return m.ErrNotAllowedToUpdateTeam +} + func UpdateTeam(user m.SignedInUser, cmd *m.UpdateTeamCommand) error { + if err := canUpdateTeam(cmd.OrgId, cmd.Id, user); err != nil { + return err + } + return bus.Dispatch(cmd) } diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go index aaa19440bb4..9dd42e1add5 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teams/teams_test.go @@ -1,42 +1,152 @@ package teams import ( - . "github.com/smartystreets/goconvey/convey" + "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "github.com/pkg/errors" + . "github.com/smartystreets/goconvey/convey" + "testing" ) - func TestUpdateTeam(t *testing.T) { - Convey("Updating a team as an editor", t, func() { + Convey("Updating a team", t, func() { + bus.ClearBusHandlers() Convey("Given an editor and a team he isn't a member of", func() { - - UpdateTeam(editor, m.UpdateTeamCommand{ - Id: 0, - Name: "", - Email: "", - OrgId: 0, + editor := m.SignedInUser{ + UserId: 1, + OrgId: 1, + OrgRole: m.ROLE_EDITOR, + } + + Convey("Should not be able to update the team", func() { + cmd := m.UpdateTeamCommand{ + Id: 1, + OrgId: editor.OrgId, + } + + bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { + return errors.New("Editor not allowed to update team.") + }) + bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { + cmd.Result = []*m.TeamMemberDTO{} + return nil + }) + + err := UpdateTeam(editor, &cmd) + + So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) + }) + }) + + Convey("Given an editor and a team he is a member of", func() { + editor := m.SignedInUser{ + UserId: 1, + OrgId: 1, + OrgRole: m.ROLE_EDITOR, + } + + testTeam := m.Team{ + Id: 1, + OrgId: 1, + } + + Convey("Should be able to update the team", func() { + cmd := m.UpdateTeamCommand{ + Id: testTeam.Id, + OrgId: testTeam.OrgId, + } + + teamUpdated := false + + bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { + teamUpdated = true + return nil + }) + + bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { + cmd.Result = []*m.TeamMemberDTO{{ + OrgId: testTeam.OrgId, + TeamId: testTeam.Id, + UserId: editor.UserId, + Permission: int64(m.PERMISSION_ADMIN), + }} + return nil + }) + + err := UpdateTeam(editor, &cmd) + + So(teamUpdated, ShouldBeTrue) + So(err, ShouldBeNil) + }) + }) + + Convey("Given an editor and a team in another org", func() { + editor := m.SignedInUser{ + UserId: 1, + OrgId: 1, + OrgRole: m.ROLE_EDITOR, + } + + testTeam := m.Team{ + Id: 1, + OrgId: 2, + } + + Convey("Shouldn't be able to update the team", func() { + cmd := m.UpdateTeamCommand{ + Id: testTeam.Id, + OrgId: testTeam.OrgId, + } + + bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { + return errors.New("Can't update a team in a different org.") + }) + bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { + cmd.Result = []*m.TeamMemberDTO{{ + OrgId: testTeam.OrgId, + TeamId: testTeam.Id, + UserId: editor.UserId, + Permission: int64(m.PERMISSION_ADMIN), + }} + return nil + }) + + err := UpdateTeam(editor, &cmd) + + So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) + }) + }) + + Convey("Given an org admin and a team", func() { + editor := m.SignedInUser{ + UserId: 1, + OrgId: 1, + OrgRole: m.ROLE_ADMIN, + } + + testTeam := m.Team{ + Id: 1, + OrgId: 1, + } + + Convey("Should be able to update the team", func() { + cmd := m.UpdateTeamCommand{ + Id: testTeam.Id, + OrgId: testTeam.OrgId, + } + + teamUpdated := false + + bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { + teamUpdated = true + return nil + }) + + err := UpdateTeam(editor, &cmd) + + So(teamUpdated, ShouldBeTrue) + So(err, ShouldBeNil) + }) + }) }) - }) - - // the editor should not be able to update the team if they aren't members of it - - fakeDash := m.NewDashboard("Child dash") - fakeDash.Id = 1 - fakeDash.FolderId = 1 - fakeDash.HasAcl = false - - bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { - dashboards := []*m.Dashboard{fakeDash} - query.Result = dashboards - return nil - }) - - var getDashboardQueries []*m.GetDashboardQuery - - bus.AddHandler("test", func(query *m.GetDashboardQuery) error { - query.Result = fakeDash - getDashboardQueries = append(getDashboardQueries, query) - return nil - }) - - bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error { +} From 319879cfa8a069f9905031d9fc9dcc8ae5d5f483 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 8 Mar 2019 15:58:32 +0100 Subject: [PATCH 046/101] teams: bugfix, user pointer. --- pkg/services/teams/team.go | 4 ++-- pkg/services/teams/teams_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index 7ff18820b62..6adf03b8b21 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -5,7 +5,7 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func canUpdateTeam(orgId int64, teamId int64, user m.SignedInUser) error { +func canUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { if user.OrgRole == m.ROLE_ADMIN { return nil } @@ -36,7 +36,7 @@ func canUpdateTeam(orgId int64, teamId int64, user m.SignedInUser) error { return m.ErrNotAllowedToUpdateTeam } -func UpdateTeam(user m.SignedInUser, cmd *m.UpdateTeamCommand) error { +func UpdateTeam(user *m.SignedInUser, cmd *m.UpdateTeamCommand) error { if err := canUpdateTeam(cmd.OrgId, cmd.Id, user); err != nil { return err } diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go index 9dd42e1add5..12b773568c7 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teams/teams_test.go @@ -32,7 +32,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(editor, &cmd) + err := UpdateTeam(&editor, &cmd) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) @@ -73,7 +73,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(editor, &cmd) + err := UpdateTeam(&editor, &cmd) So(teamUpdated, ShouldBeTrue) So(err, ShouldBeNil) @@ -111,7 +111,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(editor, &cmd) + err := UpdateTeam(&editor, &cmd) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) @@ -142,7 +142,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(editor, &cmd) + err := UpdateTeam(&editor, &cmd) So(teamUpdated, ShouldBeTrue) So(err, ShouldBeNil) From 3be1d71f1ff1e1ed87e1c9a3896253fa4cdd6df3 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 11:12:52 +0100 Subject: [PATCH 047/101] teams: test refactorings. --- pkg/services/teams/team.go | 1 - pkg/services/teams/teams_test.go | 133 ++++++++++++------------------- 2 files changed, 52 insertions(+), 82 deletions(-) diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index 6adf03b8b21..ae9327699be 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -23,7 +23,6 @@ func canUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { } if err := bus.Dispatch(&cmd); err != nil { - // TODO: look into how we want to do logging return err } diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go index 12b773568c7..1282eefc611 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teams/teams_test.go @@ -11,57 +11,43 @@ import ( func TestUpdateTeam(t *testing.T) { Convey("Updating a team", t, func() { bus.ClearBusHandlers() + + admin := m.SignedInUser{ + UserId: 1, + OrgId: 1, + OrgRole: m.ROLE_ADMIN, + } + editor := m.SignedInUser{ + UserId: 2, + OrgId: 1, + OrgRole: m.ROLE_EDITOR, + } + testTeam := m.Team{ + Id: 1, + OrgId: 1, + } + + updateTeamCmd := m.UpdateTeamCommand{ + Id: testTeam.Id, + OrgId: testTeam.OrgId, + } + Convey("Given an editor and a team he isn't a member of", func() { - editor := m.SignedInUser{ - UserId: 1, - OrgId: 1, - OrgRole: m.ROLE_EDITOR, - } - Convey("Should not be able to update the team", func() { - cmd := m.UpdateTeamCommand{ - Id: 1, - OrgId: editor.OrgId, - } - - bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { - return errors.New("Editor not allowed to update team.") - }) + shouldNotUpdateTeam() bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { cmd.Result = []*m.TeamMemberDTO{} return nil }) - err := UpdateTeam(&editor, &cmd) - + err := UpdateTeam(&editor, &updateTeamCmd) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) Convey("Given an editor and a team he is a member of", func() { - editor := m.SignedInUser{ - UserId: 1, - OrgId: 1, - OrgRole: m.ROLE_EDITOR, - } - - testTeam := m.Team{ - Id: 1, - OrgId: 1, - } - Convey("Should be able to update the team", func() { - cmd := m.UpdateTeamCommand{ - Id: testTeam.Id, - OrgId: testTeam.OrgId, - } - - teamUpdated := false - - bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { - teamUpdated = true - return nil - }) + teamUpdatedCallback := updateTeamCalled() bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { cmd.Result = []*m.TeamMemberDTO{{ @@ -73,38 +59,29 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(&editor, &cmd) - - So(teamUpdated, ShouldBeTrue) + err := UpdateTeam(&editor, &updateTeamCmd) + So(teamUpdatedCallback(), ShouldBeTrue) So(err, ShouldBeNil) }) }) Convey("Given an editor and a team in another org", func() { - editor := m.SignedInUser{ - UserId: 1, - OrgId: 1, - OrgRole: m.ROLE_EDITOR, - } - - testTeam := m.Team{ + testTeamOtherOrg := m.Team{ Id: 1, OrgId: 2, } Convey("Shouldn't be able to update the team", func() { cmd := m.UpdateTeamCommand{ - Id: testTeam.Id, - OrgId: testTeam.OrgId, + Id: testTeamOtherOrg.Id, + OrgId: testTeamOtherOrg.OrgId, } - bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { - return errors.New("Can't update a team in a different org.") - }) + shouldNotUpdateTeam() bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { cmd.Result = []*m.TeamMemberDTO{{ - OrgId: testTeam.OrgId, - TeamId: testTeam.Id, + OrgId: testTeamOtherOrg.OrgId, + TeamId: testTeamOtherOrg.Id, UserId: editor.UserId, Permission: int64(m.PERMISSION_ADMIN), }} @@ -112,41 +89,35 @@ func TestUpdateTeam(t *testing.T) { }) err := UpdateTeam(&editor, &cmd) - So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) }) Convey("Given an org admin and a team", func() { - editor := m.SignedInUser{ - UserId: 1, - OrgId: 1, - OrgRole: m.ROLE_ADMIN, - } - - testTeam := m.Team{ - Id: 1, - OrgId: 1, - } - Convey("Should be able to update the team", func() { - cmd := m.UpdateTeamCommand{ - Id: testTeam.Id, - OrgId: testTeam.OrgId, - } + teamUpdatedCallback := updateTeamCalled() + err := UpdateTeam(&admin, &updateTeamCmd) - teamUpdated := false - - bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { - teamUpdated = true - return nil - }) - - err := UpdateTeam(&editor, &cmd) - - So(teamUpdated, ShouldBeTrue) + So(teamUpdatedCallback(), ShouldBeTrue) So(err, ShouldBeNil) }) }) }) } + +func updateTeamCalled() func() bool { + wasCalled := false + bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { + wasCalled = true + return nil + }) + + return func() bool { return wasCalled } +} + +func shouldNotUpdateTeam() { + bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { + return errors.New("UpdateTeamCommand not expected.") + }) + +} From 0d61f895773fd91f338769700ed70f3968fe528c Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 11:26:01 +0100 Subject: [PATCH 048/101] teams: cleanup. --- pkg/api/team.go | 7 ++++++- pkg/services/teams/team.go | 10 +--------- pkg/services/teams/teams_test.go | 16 +++++++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/pkg/api/team.go b/pkg/api/team.go index 3d357fa9763..6e62b186f83 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -41,7 +41,12 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") - if err := teams.UpdateTeam(c.SignedInUser, &cmd); err != nil { + + if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { + return Error(403, "User not allowed to update team", err) + } + + if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNameTaken { return Error(400, "Team name taken", err) } diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index ae9327699be..9419d649204 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -5,7 +5,7 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func canUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { +func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { if user.OrgRole == m.ROLE_ADMIN { return nil } @@ -34,11 +34,3 @@ func canUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { return m.ErrNotAllowedToUpdateTeam } - -func UpdateTeam(user *m.SignedInUser, cmd *m.UpdateTeamCommand) error { - if err := canUpdateTeam(cmd.OrgId, cmd.Id, user); err != nil { - return err - } - - return bus.Dispatch(cmd) -} diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go index 1282eefc611..7fac1be6880 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teams/teams_test.go @@ -40,12 +40,12 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(&editor, &updateTeamCmd) + err := CanUpdateTeam(&editor, &updateTeamCmd) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) - Convey("Given an editor and a team he is a member of", func() { + Convey("Given an editor and a team he is an admin in", func() { Convey("Should be able to update the team", func() { teamUpdatedCallback := updateTeamCalled() @@ -59,7 +59,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(&editor, &updateTeamCmd) + err := CanUpdateTeam(&editor, &updateTeamCmd) So(teamUpdatedCallback(), ShouldBeTrue) So(err, ShouldBeNil) }) @@ -88,7 +88,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := UpdateTeam(&editor, &cmd) + err := CanUpdateTeam(&editor, &cmd) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) }) @@ -96,12 +96,18 @@ func TestUpdateTeam(t *testing.T) { Convey("Given an org admin and a team", func() { Convey("Should be able to update the team", func() { teamUpdatedCallback := updateTeamCalled() - err := UpdateTeam(&admin, &updateTeamCmd) + err := CanUpdateTeam(&admin, &updateTeamCmd) So(teamUpdatedCallback(), ShouldBeTrue) So(err, ShouldBeNil) }) }) + Convey("Given that the editorsCanOwn feature toggle is disabled", func() { + + Convey("Given an editor and a team he is an admin", func() { + + }) + }) }) } From d668550aa2127254d83166c7da90053b9d85728a Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 11:45:06 +0100 Subject: [PATCH 049/101] teams: added feature toggle and refactor tests --- pkg/api/team.go | 4 +-- pkg/services/teams/team.go | 6 +++- pkg/services/teams/teams_test.go | 50 +++++--------------------------- 3 files changed, 15 insertions(+), 45 deletions(-) diff --git a/pkg/api/team.go b/pkg/api/team.go index 6e62b186f83..e9239acffa3 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -38,11 +38,11 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo } // PUT /api/teams/:teamId -func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { +func (hs *HTTPServer) UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") - if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { + if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser, hs.Cfg.EditorsCanOwn); err != nil { return Error(403, "User not allowed to update team", err) } diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index 9419d649204..3818b22bca3 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -5,11 +5,15 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { +func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser, editorCanOwn bool) error { if user.OrgRole == m.ROLE_ADMIN { return nil } + if !editorCanOwn { + return m.ErrNotAllowedToUpdateTeam + } + if user.OrgId != orgId { return m.ErrNotAllowedToUpdateTeamInDifferentOrg } diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go index 7fac1be6880..50237af2945 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teams/teams_test.go @@ -3,7 +3,6 @@ package teams import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "github.com/pkg/errors" . "github.com/smartystreets/goconvey/convey" "testing" ) @@ -27,28 +26,20 @@ func TestUpdateTeam(t *testing.T) { OrgId: 1, } - updateTeamCmd := m.UpdateTeamCommand{ - Id: testTeam.Id, - OrgId: testTeam.OrgId, - } - Convey("Given an editor and a team he isn't a member of", func() { Convey("Should not be able to update the team", func() { - shouldNotUpdateTeam() bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { cmd.Result = []*m.TeamMemberDTO{} return nil }) - err := CanUpdateTeam(&editor, &updateTeamCmd) + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor, true) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) Convey("Given an editor and a team he is an admin in", func() { Convey("Should be able to update the team", func() { - teamUpdatedCallback := updateTeamCalled() - bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { cmd.Result = []*m.TeamMemberDTO{{ OrgId: testTeam.OrgId, @@ -59,8 +50,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(&editor, &updateTeamCmd) - So(teamUpdatedCallback(), ShouldBeTrue) + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor, true) So(err, ShouldBeNil) }) }) @@ -72,12 +62,6 @@ func TestUpdateTeam(t *testing.T) { } Convey("Shouldn't be able to update the team", func() { - cmd := m.UpdateTeamCommand{ - Id: testTeamOtherOrg.Id, - OrgId: testTeamOtherOrg.OrgId, - } - - shouldNotUpdateTeam() bus.AddHandler("test", func(cmd *m.GetTeamMembersQuery) error { cmd.Result = []*m.TeamMemberDTO{{ OrgId: testTeamOtherOrg.OrgId, @@ -88,42 +72,24 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(&editor, &cmd) + err := CanUpdateTeam(testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor, true) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) }) Convey("Given an org admin and a team", func() { Convey("Should be able to update the team", func() { - teamUpdatedCallback := updateTeamCalled() - err := CanUpdateTeam(&admin, &updateTeamCmd) - - So(teamUpdatedCallback(), ShouldBeTrue) + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &admin, true) So(err, ShouldBeNil) }) }) + Convey("Given that the editorsCanOwn feature toggle is disabled", func() { + Convey("Editors should not be able to update teams", func() { + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor, false) - Convey("Given an editor and a team he is an admin", func() { - + So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) }) } - -func updateTeamCalled() func() bool { - wasCalled := false - bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { - wasCalled = true - return nil - }) - - return func() bool { return wasCalled } -} - -func shouldNotUpdateTeam() { - bus.AddHandler("test", func(cmd *m.UpdateTeamCommand) error { - return errors.New("UpdateTeamCommand not expected.") - }) - -} From 8e7a8282c1f52141c0c9d351a74f54a45c74baef Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 11:51:06 +0100 Subject: [PATCH 050/101] teams: removed feature toggle as it is already in middleware --- pkg/api/api.go | 2 +- pkg/api/team.go | 2 +- pkg/services/teams/team.go | 6 +----- pkg/services/teams/teams_test.go | 16 ++++------------ 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index e5d725342fe..c004d600b1b 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -155,7 +155,7 @@ func (hs *HTTPServer) registerRoutes() { // team (admin permission required) apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { teamsRoute.Post("/", bind(m.CreateTeamCommand{}), Wrap(hs.CreateTeam)) - teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(UpdateTeam)) + teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(hs.UpdateTeam)) teamsRoute.Delete("/:teamId", Wrap(DeleteTeamByID)) teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember)) diff --git a/pkg/api/team.go b/pkg/api/team.go index e9239acffa3..6d74b11e588 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -42,7 +42,7 @@ func (hs *HTTPServer) UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Respo cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") - if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser, hs.Cfg.EditorsCanOwn); err != nil { + if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { return Error(403, "User not allowed to update team", err) } diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index 3818b22bca3..9419d649204 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -5,15 +5,11 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser, editorCanOwn bool) error { +func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { if user.OrgRole == m.ROLE_ADMIN { return nil } - if !editorCanOwn { - return m.ErrNotAllowedToUpdateTeam - } - if user.OrgId != orgId { return m.ErrNotAllowedToUpdateTeamInDifferentOrg } diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teams/teams_test.go index 50237af2945..85bbddf014f 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teams/teams_test.go @@ -33,7 +33,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor, true) + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) @@ -50,7 +50,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor, true) + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor) So(err, ShouldBeNil) }) }) @@ -72,24 +72,16 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor, true) + err := CanUpdateTeam(testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) }) Convey("Given an org admin and a team", func() { Convey("Should be able to update the team", func() { - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &admin, true) + err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &admin) So(err, ShouldBeNil) }) }) - - Convey("Given that the editorsCanOwn feature toggle is disabled", func() { - Convey("Editors should not be able to update teams", func() { - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor, false) - - So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) - }) - }) }) } From 23231e6d510b60f5609ee76343f808a5dd5becf6 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 12:03:15 +0100 Subject: [PATCH 051/101] teams: added delete team guard --- pkg/api/api.go | 2 +- pkg/api/team.go | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index c004d600b1b..e5d725342fe 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -155,7 +155,7 @@ func (hs *HTTPServer) registerRoutes() { // team (admin permission required) apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { teamsRoute.Post("/", bind(m.CreateTeamCommand{}), Wrap(hs.CreateTeam)) - teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(hs.UpdateTeam)) + teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(UpdateTeam)) teamsRoute.Delete("/:teamId", Wrap(DeleteTeamByID)) teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember)) diff --git a/pkg/api/team.go b/pkg/api/team.go index 6d74b11e588..61d966c2a8b 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -38,12 +38,12 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo } // PUT /api/teams/:teamId -func (hs *HTTPServer) UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { +func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { - return Error(403, "User not allowed to update team", err) + return Error(403, "Not allowed to update team", err) } if err := bus.Dispatch(&cmd); err != nil { @@ -58,11 +58,19 @@ func (hs *HTTPServer) UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Respo // DELETE /api/teams/:teamId func DeleteTeamByID(c *m.ReqContext) Response { - if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}); err != nil { + orgId := c.OrgId + teamId := c.ParamsInt64(":teamId") + user := c.SignedInUser + + if err := teams.CanUpdateTeam(orgId, teamId, user); err != nil { + return Error(403, "Not allowed to delete team", err) + } + + if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: orgId, Id: teamId}); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Failed to delete Team. ID not found", nil) } - return Error(500, "Failed to update Team", err) + return Error(500, "Failed to delete Team", err) } return Success("Team deleted") } From 1f949e58e1f9e15d43cb88daa0f753ca927bd8cd Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 13:14:06 +0100 Subject: [PATCH 052/101] teams: teams guard on all teams update methods. --- pkg/api/team.go | 9 ++++++++- pkg/api/team_members.go | 33 ++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/pkg/api/team.go b/pkg/api/team.go index 61d966c2a8b..223ad404793 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -131,5 +131,12 @@ func GetTeamPreferences(c *m.ReqContext) Response { // PUT /api/teams/:teamId/preferences func UpdateTeamPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response { - return updatePreferencesFor(c.OrgId, 0, c.ParamsInt64(":teamId"), &dtoCmd) + teamId := c.ParamsInt64(":teamId") + orgId := c.OrgId + + if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + return Error(403, "Not allowed to update team preferences.", err) + } + + return updatePreferencesFor(orgId, 0, teamId, &dtoCmd) } diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index e0919262111..b2bb1781020 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -4,6 +4,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/teams" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -30,8 +31,15 @@ func GetTeamMembers(c *m.ReqContext) Response { // POST /api/teams/:teamId/members func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { - cmd.TeamId = c.ParamsInt64(":teamId") - cmd.OrgId = c.OrgId + teamId := c.ParamsInt64(":teamId") + orgId := c.OrgId + + if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + return Error(403, "Not allowed to add team member", err) + } + + cmd.TeamId = teamId + cmd.OrgId = orgId if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNotFound { @@ -52,9 +60,16 @@ func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { // PUT /:teamId/members/:userId func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { - cmd.TeamId = c.ParamsInt64(":teamId") + teamId := c.ParamsInt64(":teamId") + orgId := c.OrgId + + if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + return Error(403, "Not allowed to update team member", err) + } + + cmd.TeamId = teamId cmd.UserId = c.ParamsInt64(":userId") - cmd.OrgId = c.OrgId + cmd.OrgId = orgId if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamMemberNotFound { @@ -67,7 +82,15 @@ func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { // DELETE /api/teams/:teamId/members/:userId func RemoveTeamMember(c *m.ReqContext) Response { - if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId"), UserId: c.ParamsInt64(":userId")}); err != nil { + orgId := c.OrgId + teamId := c.ParamsInt64(":teamId") + userId := c.ParamsInt64(":userId") + + if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + return Error(403, "Not allowed to remove team member", err) + } + + if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId}); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Team not found", nil) } From 89d4db8eb6d02cd585cf53cbfeda01dd5a6f1e9a Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 14:40:57 +0100 Subject: [PATCH 053/101] teams: team listing shows only your teams (editors). --- pkg/api/team.go | 16 +++++++++++----- pkg/models/team.go | 11 ++++++----- pkg/services/sqlstore/team.go | 4 ++++ public/app/features/teams/state/actions.ts | 6 ++++-- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/pkg/api/team.go b/pkg/api/team.go index 223ad404793..e4adb0bd430 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -86,12 +86,18 @@ func SearchTeams(c *m.ReqContext) Response { page = 1 } + var userIdFilter int64 + if c.QueryBool("showMine") { + userIdFilter = c.SignedInUser.UserId + } + query := m.SearchTeamsQuery{ - OrgId: c.OrgId, - Query: c.Query("query"), - Name: c.Query("name"), - Page: page, - Limit: perPage, + OrgId: c.OrgId, + Query: c.Query("query"), + Name: c.Query("name"), + UserIdFilter: userIdFilter, + Page: page, + Limit: perPage, } if err := bus.Dispatch(&query); err != nil { diff --git a/pkg/models/team.go b/pkg/models/team.go index bd0d803d9d3..bb9289ee5e5 100644 --- a/pkg/models/team.go +++ b/pkg/models/team.go @@ -61,11 +61,12 @@ type GetTeamsByUserQuery struct { } type SearchTeamsQuery struct { - Query string - Name string - Limit int - Page int - OrgId int64 + Query string + Name string + Limit int + Page int + OrgId int64 + UserIdFilter int64 Result SearchTeamQueryResult } diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index f7f7d7fc2cb..a9ee6979406 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -149,6 +149,10 @@ func SearchTeams(query *m.SearchTeamsQuery) error { params := make([]interface{}, 0) sql.WriteString(getTeamSelectSqlBase()) + if query.UserIdFilter > 0 { + sql.WriteString(`INNER JOIN team_member on team.id = team_member.team_id AND team_member.user_id = ?`) + params = append(params, query.UserIdFilter) + } sql.WriteString(` WHERE team.org_id = ?`) params = append(params, query.OrgId) diff --git a/public/app/features/teams/state/actions.ts b/public/app/features/teams/state/actions.ts index e2582839233..bfccddeefc5 100644 --- a/public/app/features/teams/state/actions.ts +++ b/public/app/features/teams/state/actions.ts @@ -1,8 +1,9 @@ import { ThunkAction } from 'redux-thunk'; import { getBackendSrv } from 'app/core/services/backend_srv'; -import { StoreState, Team, TeamGroup, TeamMember } from 'app/types'; +import { OrgRole, StoreState, Team, TeamGroup, TeamMember } from 'app/types'; import { updateNavIndex, UpdateNavIndexAction } from 'app/core/actions'; import { buildNavModel } from './navModel'; +import { contextSrv } from '../../../core/services/context_srv'; export enum ActionTypes { LoadTeams = 'LOAD_TEAMS', @@ -85,7 +86,8 @@ export const setSearchQuery = (searchQuery: string): SetSearchQueryAction => ({ export function loadTeams(): ThunkResult { return async dispatch => { - const response = await getBackendSrv().get('/api/teams/search', { perpage: 1000, page: 1 }); + const showMine = contextSrv.user.orgRole === OrgRole.Editor; + const response = await getBackendSrv().get('/api/teams/search', { perpage: 1000, page: 1, showMine }); dispatch(teamsLoaded(response.teams)); }; } From d593ffe3c1a8e0e0fefd343c769355a02cdecdeb Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 15:05:28 +0100 Subject: [PATCH 054/101] dashboards: better error handling --- pkg/api/dashboard.go | 3 ++- pkg/api/folder.go | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 016146a5c61..deecdf2c1c8 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -281,7 +281,8 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) aclService := dashboards.NewAclService() err := aclService.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id) if err != nil { - hs.log.Error("Could not make user admin", "error", err) + hs.log.Error("Could not make user admin", "dashboard", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) + return Error(500, "Failed to make user admin of dashboard", err) } } diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 4e66439219d..a2d6a765b16 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -63,7 +63,10 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R if hs.Cfg.EditorsCanOwn { aclService := dashboards.NewAclService() - aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id) + if err := aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id); err != nil { + hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) + return Error(500, "Failed to make user admin of folder", err) + } } g := guardian.New(cmd.Result.Id, c.OrgId, c.SignedInUser) From 0b209de5d1addb9a31b86aa720778ebf551ac18a Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 15:34:32 +0100 Subject: [PATCH 055/101] dashboard: only admin permission added to dashboard in folder. --- pkg/api/dashboard.go | 3 +- pkg/api/folder.go | 2 +- pkg/services/dashboards/acl_service.go | 39 +++++++++++++++----------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index deecdf2c1c8..b7b2383d1f8 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -279,7 +279,8 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) if hs.Cfg.EditorsCanOwn && newDashboard { aclService := dashboards.NewAclService() - err := aclService.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id) + inFolder := cmd.FolderId > 0 + err := aclService.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) if err != nil { hs.log.Error("Could not make user admin", "dashboard", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) return Error(500, "Failed to make user admin of dashboard", err) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index a2d6a765b16..fd10897fc9a 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -63,7 +63,7 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R if hs.Cfg.EditorsCanOwn { aclService := dashboards.NewAclService() - if err := aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id); err != nil { + if err := aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) return Error(500, "Failed to make user admin of folder", err) } diff --git a/pkg/services/dashboards/acl_service.go b/pkg/services/dashboards/acl_service.go index 79b55470093..dae3ec1372b 100644 --- a/pkg/services/dashboards/acl_service.go +++ b/pkg/services/dashboards/acl_service.go @@ -18,7 +18,7 @@ type AclService struct { log log.Logger } -func (as *AclService) MakeUserAdmin(orgId int64, userId int64, dashboardId int64) error { +func (as *AclService) MakeUserAdmin(orgId int64, userId int64, dashboardId int64, setViewAndEditPermissions bool) error { rtEditor := models.ROLE_EDITOR rtViewer := models.ROLE_VIEWER @@ -31,22 +31,27 @@ func (as *AclService) MakeUserAdmin(orgId int64, userId int64, dashboardId int64 Created: time.Now(), Updated: time.Now(), }, - { - OrgId: orgId, - DashboardId: dashboardId, - Role: &rtEditor, - Permission: models.PERMISSION_EDIT, - Created: time.Now(), - Updated: time.Now(), - }, - { - OrgId: orgId, - DashboardId: dashboardId, - Role: &rtViewer, - Permission: models.PERMISSION_VIEW, - Created: time.Now(), - Updated: time.Now(), - }, + } + + if setViewAndEditPermissions { + items = append(items, + &models.DashboardAcl{ + OrgId: orgId, + DashboardId: dashboardId, + Role: &rtEditor, + Permission: models.PERMISSION_EDIT, + Created: time.Now(), + Updated: time.Now(), + }, + &models.DashboardAcl{ + OrgId: orgId, + DashboardId: dashboardId, + Role: &rtViewer, + Permission: models.PERMISSION_VIEW, + Created: time.Now(), + Updated: time.Now(), + }, + ) } aclCmd := &models.UpdateDashboardAclCommand{ From a6a3d698da2f09802ad25363917371fe6bda5237 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 11 Mar 2019 15:48:05 +0100 Subject: [PATCH 056/101] teams: cleanup. --- pkg/services/sqlstore/team.go | 14 ++++---------- pkg/services/teams/team.go | 2 -- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index a9ee6979406..7c5a5f88983 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -92,10 +92,8 @@ func UpdateTeam(cmd *m.UpdateTeamCommand) error { // DeleteTeam will delete a team, its member and any permissions connected to the team func DeleteTeam(cmd *m.DeleteTeamCommand) error { return inTransaction(func(sess *DBSession) error { - if teamExists, err := teamExists(cmd.OrgId, cmd.Id, sess); err != nil { + if _, err := teamExists(cmd.OrgId, cmd.Id, sess); err != nil { return err - } else if !teamExists { - return m.ErrTeamNotFound } deletes := []string{ @@ -118,7 +116,7 @@ func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) { if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgId, teamId); err != nil { return false, err } else if len(res) != 1 { - return false, nil + return false, m.ErrTeamNotFound } return true, nil @@ -238,10 +236,8 @@ func AddTeamMember(cmd *m.AddTeamMemberCommand) error { return m.ErrTeamMemberAlreadyAdded } - if teamExists, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil { + if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil { return err - } else if !teamExists { - return m.ErrTeamNotFound } entity := m.TeamMember{ @@ -285,10 +281,8 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { // RemoveTeamMember removes a member from a team func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { return inTransaction(func(sess *DBSession) error { - if teamExists, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil { + if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil { return err - } else if !teamExists { - return m.ErrTeamNotFound } var rawSql = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?" diff --git a/pkg/services/teams/team.go b/pkg/services/teams/team.go index 9419d649204..080fe961ab6 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teams/team.go @@ -18,8 +18,6 @@ func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { OrgId: orgId, TeamId: teamId, UserId: user.UserId, - // TODO: do we need to do something special about external users - // External: false, } if err := bus.Dispatch(&cmd); err != nil { From a90b3e331ecc2c9912802d693d5e8cdcf6ac1657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 12 Mar 2019 07:32:47 +0100 Subject: [PATCH 057/101] config: updated feature toggle name --- conf/defaults.ini | 2 +- conf/sample.ini | 2 +- pkg/api/api.go | 2 +- pkg/api/dashboard.go | 2 +- pkg/api/dashboard_test.go | 2 +- pkg/api/folder.go | 2 +- pkg/api/folder_test.go | 2 +- pkg/api/frontendsettings.go | 2 +- pkg/api/index.go | 2 +- pkg/api/team.go | 2 +- pkg/setting/setting.go | 7 +++---- public/app/core/config.ts | 4 ++-- public/app/features/teams/TeamMembers.tsx | 4 ++-- public/app/routes/routes.ts | 2 +- 14 files changed, 18 insertions(+), 19 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 492525e6b5f..bb415721391 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -259,7 +259,7 @@ external_manage_info = viewers_can_edit = false # Editors can administrate dashboard, folders and teams they create -editors_can_own = false +editors_can_admin = false [auth] # Login cookie name diff --git a/conf/sample.ini b/conf/sample.ini index fd414c2af47..321c1120693 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -239,7 +239,7 @@ log_queries = ;viewers_can_edit = false # Editors can administrate dashboard, folders and teams they create -;editors_can_own = false +;editors_can_admin = false [auth] # Login cookie name diff --git a/pkg/api/api.go b/pkg/api/api.go index e5d725342fe..9ffb0278935 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -14,7 +14,7 @@ func (hs *HTTPServer) registerRoutes() { reqGrafanaAdmin := middleware.ReqGrafanaAdmin reqEditorRole := middleware.ReqEditorRole reqOrgAdmin := middleware.ReqOrgAdmin - reqAdminOrEditorCanAdmin := middleware.EditorCanAdmin(hs.Cfg.EditorsCanOwn) + reqAdminOrEditorCanAdmin := middleware.EditorCanAdmin(hs.Cfg.EditorsCanAdmin) redirectFromLegacyDashboardURL := middleware.RedirectFromLegacyDashboardURL() redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL() quota := middleware.Quota(hs.QuotaService) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index b7b2383d1f8..14a1e3baf32 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -277,7 +277,7 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) return Error(500, "Failed to save dashboard", err) } - if hs.Cfg.EditorsCanOwn && newDashboard { + if hs.Cfg.EditorsCanAdmin && newDashboard { aclService := dashboards.NewAclService() inFolder := cmd.FolderId > 0 err := aclService.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index d58e2246eec..5411643af96 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -973,7 +973,7 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d defer bus.ClearBusHandlers() cfg := setting.NewCfg() - cfg.EditorsCanOwn = false + cfg.EditorsCanAdmin = false hs := HTTPServer{ Bus: bus.GetBus(), diff --git a/pkg/api/folder.go b/pkg/api/folder.go index fd10897fc9a..66c640f96e8 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -61,7 +61,7 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R return toFolderError(err) } - if hs.Cfg.EditorsCanOwn { + if hs.Cfg.EditorsCanAdmin { aclService := dashboards.NewAclService() if err := aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index d5e4ee418cd..15c51e476b5 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -143,7 +143,7 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa defer bus.ClearBusHandlers() cfg := setting.NewCfg() - cfg.EditorsCanOwn = true + cfg.EditorsCanAdmin = true hs := HTTPServer{ Bus: bus.GetBus(), diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 67a511b8b4d..cd61c2f3ebc 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -167,7 +167,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, "externalUserMngLinkName": setting.ExternalUserMngLinkName, "viewersCanEdit": setting.ViewersCanEdit, - "editorsCanOwn": hs.Cfg.EditorsCanOwn, + "editorsCanAdmin": hs.Cfg.EditorsCanAdmin, "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, "buildInfo": map[string]interface{}{ "version": setting.BuildVersion, diff --git a/pkg/api/index.go b/pkg/api/index.go index 88c4b7e929d..3f60290ea55 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -327,7 +327,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er }) } - if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanOwn { + if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanAdmin { cfgNode := &dtos.NavLink{ Id: "cfg", Text: "Configuration", diff --git a/pkg/api/team.go b/pkg/api/team.go index e4adb0bd430..619d24ea0b1 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -18,7 +18,7 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo return Error(500, "Failed to create Team", err) } - if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanOwn { + if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanAdmin { addMemberCmd := m.AddTeamMemberCommand{ UserId: c.SignedInUser.UserId, OrgId: cmd.OrgId, diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index bc57291b5f9..8c6d8c54f11 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -239,14 +239,13 @@ type Cfg struct { LoginMaxLifetimeDays int TokenRotationIntervalMinutes int - // User - EditorsCanOwn bool - // Dataproxy SendUserHeader bool // DistributedCache RemoteCacheOptions *RemoteCacheOptions + + EditorsCanAdmin bool } type CommandLineArgs struct { @@ -670,7 +669,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { ExternalUserMngLinkName = users.Key("external_manage_link_name").String() ExternalUserMngInfo = users.Key("external_manage_info").String() ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false) - cfg.EditorsCanOwn = users.Key("editors_can_own").MustBool(false) + cfg.EditorsCanAdmin = users.Key("editors_can_admin").MustBool(false) // auth auth := iniFile.Section("auth") diff --git a/public/app/core/config.ts b/public/app/core/config.ts index 9789888e60f..fe9005973b8 100644 --- a/public/app/core/config.ts +++ b/public/app/core/config.ts @@ -37,7 +37,7 @@ export class Settings { passwordHint: any; loginError: any; viewersCanEdit: boolean; - editorsCanOwn: boolean; + editorsCanAdmin: boolean; disableSanitizeHtml: boolean; theme: GrafanaTheme; @@ -59,7 +59,7 @@ export class Settings { isEnterprise: false, }, viewersCanEdit: false, - editorsCanOwn: false, + editorsCanAdmin: false, disableSanitizeHtml: false, }; diff --git a/public/app/features/teams/TeamMembers.tsx b/public/app/features/teams/TeamMembers.tsx index 29f1e2e4947..bcb2f3fab4b 100644 --- a/public/app/features/teams/TeamMembers.tsx +++ b/public/app/features/teams/TeamMembers.tsx @@ -93,7 +93,7 @@ export class TeamMembers extends PureComponent { {member.login} {member.email} - +
this.onPermissionChange(item, member)} + className="gf-form-select-box__control--menu-right" + value={value} + /> + )} + {!isUserTeamAdmin && {value.label}} +
+ +
+ ); + } + renderMember(member: TeamMember, syncEnabled: boolean) { return ( @@ -93,19 +125,7 @@ export class TeamMembers extends PureComponent { {member.login} {member.email} - - -
- + + Member +
@@ -271,41 +239,9 @@ exports[`Render should render team members 1`] = `
- + + Member +
@@ -411,41 +315,9 @@ exports[`Render should render team members 1`] = `
- + + Member +
@@ -644,41 +484,9 @@ exports[`Render should render team members when sync enabled 1`] = `
- + + Member +
@@ -816,41 +592,9 @@ exports[`Render should render team members when sync enabled 1`] = `
- + + Member +
@@ -983,6 +695,152 @@ exports[`Render should render team members when sync enabled 1`] = ` + +
+ + Member + +
+ +
+ + + + + + + + + + +
+
+`; + +exports[`Render when feature toggle editorsCanAdmin is turned on should render permissions select if user is Grafana Admin 1`] = ` +
+
+
+ +
+
+ +
+ +
+ +
+ Add team member +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + Email + + Permission + +
+ + + testUser-1 + + test@test.com +
- - +
+ + + testUser-2 + + test@test.com + +
+
+ +
+ + + testUser-3 + + test@test.com + +
+
+ +
+ + + testUser-4 + + test@test.com + +
+
+ +
+ + + testUser-5 + + test@test.com + +
+
+ +
+
+
+`; + +exports[`Render when feature toggle editorsCanAdmin is turned on should render permissions select if user is Org Admin 1`] = ` +
+
+
+ +
+
+ +
+ +
+ +
+ Add team member +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Name + + Email + + Permission + +
+ + + testUser-1 + + test@test.com + +
+
+ +
+ + + testUser-2 + + test@test.com + +
+
+ +
+ + + testUser-3 + + test@test.com + +
+
+ +
+ + + testUser-4 + + test@test.com + +
+
+ +
+ + + testUser-5 + + test@test.com + +
+
+ +
+
+
+`; + +exports[`Render when feature toggle editorsCanAdmin is turned on should render permissions select if user is team admin 1`] = ` +
+
+
+ +
+
+ +
+ +
+ +
+ Add team member +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - {this.renderPermissionsSelect(member)} + {this.renderPermissions(member)} {syncEnabled && this.renderLabels(member.labels)} ); @@ -152,7 +158,11 @@ export class TeamMembers extends PureComponent {
-
diff --git a/public/app/features/teams/__mocks__/teamMocks.ts b/public/app/features/teams/__mocks__/teamMocks.ts index 3f0830eda16..f38f8f2b144 100644 --- a/public/app/features/teams/__mocks__/teamMocks.ts +++ b/public/app/features/teams/__mocks__/teamMocks.ts @@ -25,7 +25,7 @@ export const getMockTeam = (): Team => { }; }; -export const getMockTeamMembers = (amount: number): TeamMember[] => { +export const getMockTeamMembers = (amount: number, teamAdminId: number): TeamMember[] => { const teamMembers: TeamMember[] = []; for (let i = 1; i <= amount; i++) { @@ -36,7 +36,7 @@ export const getMockTeamMembers = (amount: number): TeamMember[] => { email: 'test@test.com', login: `testUser-${i}`, labels: ['label 1', 'label 2'], - permission: TeamPermissionLevel.Member, + permission: i === teamAdminId ? TeamPermissionLevel.Admin : TeamPermissionLevel.Member, }); } diff --git a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap index 77b50436590..da89d26d191 100644 --- a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap @@ -201,9 +201,41 @@ exports[`Render should render team members 1`] = `
- - Member - +
@@ -249,6 +314,7 @@ exports[`Render should render team members 1`] = ` className="text-right" > @@ -277,9 +343,41 @@ exports[`Render should render team members 1`] = `
- - Member - +
@@ -325,6 +456,7 @@ exports[`Render should render team members 1`] = ` className="text-right" > @@ -353,9 +485,41 @@ exports[`Render should render team members 1`] = `
- - Member - +
@@ -510,6 +707,7 @@ exports[`Render should render team members when sync enabled 1`] = ` className="text-right" > @@ -538,9 +736,41 @@ exports[`Render should render team members when sync enabled 1`] = `
- - Member - +
@@ -618,6 +881,7 @@ exports[`Render should render team members when sync enabled 1`] = ` className="text-right" > @@ -646,9 +910,41 @@ exports[`Render should render team members when sync enabled 1`] = `
- - Member - +
@@ -726,6 +1055,7 @@ exports[`Render should render team members when sync enabled 1`] = ` className="text-right" > @@ -888,6 +1218,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -958,6 +1289,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1028,6 +1360,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1098,6 +1431,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1168,6 +1502,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1330,6 +1665,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1400,6 +1736,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1470,6 +1807,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1540,6 +1878,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1610,6 +1949,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p className="text-right" > @@ -1641,7 +1981,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p />
+
+ + Name + + Email + + Permission + +
+ + + testUser-1 + + test@test.com + +
+
+ +
+ + + testUser-2 + + test@test.com + +
+
+ +
+ + + testUser-3 + + test@test.com + +
+
+ +
+ + + testUser-4 + + test@test.com + +
+
+ +
+ + + testUser-5 + + test@test.com + +
+
From b783fa7039daff42e0406262c80fdf19c852dc53 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 12 Mar 2019 13:59:53 +0100 Subject: [PATCH 060/101] team: renames teams.CanUpdate teamguardian.CanAdmin --- pkg/api/dashboard_test.go | 1 + pkg/api/folder_test.go | 5 +---- pkg/api/team.go | 17 ++++++++++++----- pkg/api/team_members.go | 8 ++++---- pkg/services/{teams => teamguardian}/team.go | 4 ++-- .../{teams => teamguardian}/teams_test.go | 10 +++++----- public/app/types/acl.ts | 2 +- 7 files changed, 26 insertions(+), 21 deletions(-) rename pkg/services/{teams => teamguardian}/team.go (85%) rename pkg/services/{teams => teamguardian}/teams_test.go (87%) diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 5411643af96..c54647d9847 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -1028,6 +1028,7 @@ func restoreDashboardVersionScenario(desc string, url string, routePattern strin defer bus.ClearBusHandlers() hs := HTTPServer{ + Cfg: setting.NewCfg(), Bus: bus.GetBus(), } diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index 15c51e476b5..5e7184ae0c9 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -142,12 +142,9 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa Convey(desc+" "+url, func() { defer bus.ClearBusHandlers() - cfg := setting.NewCfg() - cfg.EditorsCanAdmin = true - hs := HTTPServer{ Bus: bus.GetBus(), - Cfg: cfg, + Cfg: setting.NewCfg(), } sc := setupScenarioContext(url) diff --git a/pkg/api/team.go b/pkg/api/team.go index 619d24ea0b1..6d5753fdc90 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -4,7 +4,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/teams" + "github.com/grafana/grafana/pkg/services/teamguardian" "github.com/grafana/grafana/pkg/util" ) @@ -42,7 +42,7 @@ func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") - if err := teams.CanUpdateTeam(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { return Error(403, "Not allowed to update team", err) } @@ -62,7 +62,7 @@ func DeleteTeamByID(c *m.ReqContext) Response { teamId := c.ParamsInt64(":teamId") user := c.SignedInUser - if err := teams.CanUpdateTeam(orgId, teamId, user); err != nil { + if err := teamguardian.CanAdmin(orgId, teamId, user); err != nil { return Error(403, "Not allowed to delete team", err) } @@ -132,7 +132,14 @@ func GetTeamByID(c *m.ReqContext) Response { // GET /api/teams/:teamId/preferences func GetTeamPreferences(c *m.ReqContext) Response { - return getPreferencesFor(c.OrgId, 0, c.ParamsInt64(":teamId")) + teamId := c.ParamsInt64(":teamId") + orgId := c.OrgId + + if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { + return Error(403, "Not allowed to view team preferences.", err) + } + + return getPreferencesFor(orgId, 0, teamId) } // PUT /api/teams/:teamId/preferences @@ -140,7 +147,7 @@ func UpdateTeamPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response teamId := c.ParamsInt64(":teamId") orgId := c.OrgId - if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to update team preferences.", err) } diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index b2bb1781020..669326ded18 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -4,7 +4,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/teams" + "github.com/grafana/grafana/pkg/services/teamguardian" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -34,7 +34,7 @@ func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { teamId := c.ParamsInt64(":teamId") orgId := c.OrgId - if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to add team member", err) } @@ -63,7 +63,7 @@ func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { teamId := c.ParamsInt64(":teamId") orgId := c.OrgId - if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to update team member", err) } @@ -86,7 +86,7 @@ func RemoveTeamMember(c *m.ReqContext) Response { teamId := c.ParamsInt64(":teamId") userId := c.ParamsInt64(":userId") - if err := teams.CanUpdateTeam(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to remove team member", err) } diff --git a/pkg/services/teams/team.go b/pkg/services/teamguardian/team.go similarity index 85% rename from pkg/services/teams/team.go rename to pkg/services/teamguardian/team.go index 080fe961ab6..9946ae7c734 100644 --- a/pkg/services/teams/team.go +++ b/pkg/services/teamguardian/team.go @@ -1,11 +1,11 @@ -package teams +package teamguardian import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" ) -func CanUpdateTeam(orgId int64, teamId int64, user *m.SignedInUser) error { +func CanAdmin(orgId int64, teamId int64, user *m.SignedInUser) error { if user.OrgRole == m.ROLE_ADMIN { return nil } diff --git a/pkg/services/teams/teams_test.go b/pkg/services/teamguardian/teams_test.go similarity index 87% rename from pkg/services/teams/teams_test.go rename to pkg/services/teamguardian/teams_test.go index 85bbddf014f..9b1ba7ee4cb 100644 --- a/pkg/services/teams/teams_test.go +++ b/pkg/services/teamguardian/teams_test.go @@ -1,4 +1,4 @@ -package teams +package teamguardian import ( "github.com/grafana/grafana/pkg/bus" @@ -33,7 +33,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor) + err := CanAdmin(testTeam.OrgId, testTeam.Id, &editor) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) @@ -50,7 +50,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &editor) + err := CanAdmin(testTeam.OrgId, testTeam.Id, &editor) So(err, ShouldBeNil) }) }) @@ -72,14 +72,14 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanUpdateTeam(testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor) + err := CanAdmin(testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) }) Convey("Given an org admin and a team", func() { Convey("Should be able to update the team", func() { - err := CanUpdateTeam(testTeam.OrgId, testTeam.Id, &admin) + err := CanAdmin(testTeam.OrgId, testTeam.Id, &admin) So(err, ShouldBeNil) }) }) diff --git a/public/app/types/acl.ts b/public/app/types/acl.ts index 8134ddb1749..55e9bff620b 100644 --- a/public/app/types/acl.ts +++ b/public/app/types/acl.ts @@ -115,6 +115,6 @@ export const teamsPermissionLevels: TeamPermissionInfo[] = [ { value: TeamPermissionLevel.Admin, label: 'Admin', - description: 'Can add/remove permissions and delete team.', + description: 'Can add/remove permissions, members and delete team.', }, ]; From 8593668ab23acf72a6f88abaa6c01ce52c9284cf Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 12 Mar 2019 14:19:12 +0100 Subject: [PATCH 061/101] teams: tests use the new message for modifying team members. --- .../__snapshots__/TeamMembers.test.tsx.snap | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap index d8540ed0615..77b50436590 100644 --- a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap @@ -866,7 +866,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -936,7 +936,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1006,7 +1006,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1076,7 +1076,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1146,7 +1146,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1154,7 +1154,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p } value={ Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, } @@ -1308,7 +1308,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1378,7 +1378,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1448,7 +1448,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1518,7 +1518,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1588,7 +1588,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1596,7 +1596,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p } value={ Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, } @@ -1750,7 +1750,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1758,7 +1758,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p } value={ Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, } @@ -1820,7 +1820,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1890,7 +1890,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -1960,7 +1960,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, @@ -2030,7 +2030,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p "value": 0, }, Object { - "description": "Can add/remove permissions and delete team.", + "description": "Can add/remove permissions, members and delete team.", "label": "Admin", "value": 4, }, From 21d3d274523be3817caed94d2e85f6a77f1dc877 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 12 Mar 2019 16:59:39 +0100 Subject: [PATCH 062/101] teams: editors can't remove the last admin from a team. --- pkg/api/api.go | 2 +- pkg/api/team_members.go | 9 ++++++-- pkg/models/team.go | 1 + pkg/models/team_member.go | 7 +++--- pkg/services/sqlstore/team.go | 35 ++++++++++++++++++++++++++++++ pkg/services/sqlstore/team_test.go | 17 +++++++++++++++ 6 files changed, 65 insertions(+), 6 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 9ffb0278935..9acd9485312 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -160,7 +160,7 @@ func (hs *HTTPServer) registerRoutes() { teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember)) teamsRoute.Put("/:teamId/members/:userId", bind(m.UpdateTeamMemberCommand{}), Wrap(UpdateTeamMember)) - teamsRoute.Delete("/:teamId/members/:userId", Wrap(RemoveTeamMember)) + teamsRoute.Delete("/:teamId/members/:userId", Wrap(hs.RemoveTeamMember)) teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences)) teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences)) }, reqAdminOrEditorCanAdmin) diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index 669326ded18..72aded688ec 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -81,7 +81,7 @@ func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { } // DELETE /api/teams/:teamId/members/:userId -func RemoveTeamMember(c *m.ReqContext) Response { +func (hs *HTTPServer) RemoveTeamMember(c *m.ReqContext) Response { orgId := c.OrgId teamId := c.ParamsInt64(":teamId") userId := c.ParamsInt64(":userId") @@ -90,7 +90,12 @@ func RemoveTeamMember(c *m.ReqContext) Response { return Error(403, "Not allowed to remove team member", err) } - if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId}); err != nil { + protectLastAdmin := false + if c.OrgRole == m.ROLE_EDITOR { + protectLastAdmin = true + } + + if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId, ProtectLastAdmin: protectLastAdmin}); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Team not found", nil) } diff --git a/pkg/models/team.go b/pkg/models/team.go index bb9289ee5e5..5b659331601 100644 --- a/pkg/models/team.go +++ b/pkg/models/team.go @@ -10,6 +10,7 @@ var ( ErrTeamNotFound = errors.New("Team not found") ErrTeamNameTaken = errors.New("Team name is taken") ErrTeamMemberNotFound = errors.New("Team member not found") + ErrLastTeamAdmin = errors.New("Not allowed to remove last admin") ErrNotAllowedToUpdateTeam = errors.New("User not allowed to update team") ErrNotAllowedToUpdateTeamInDifferentOrg = errors.New("User not allowed to update team in another org") ) diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index 1140e39b095..0cc39b0f605 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -42,9 +42,10 @@ type UpdateTeamMemberCommand struct { } type RemoveTeamMemberCommand struct { - OrgId int64 `json:"-"` - UserId int64 - TeamId int64 + OrgId int64 `json:"-"` + UserId int64 + TeamId int64 + ProtectLastAdmin bool `json:"-"` } // ---------------------- diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 7c5a5f88983..3848adcc7dc 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -285,6 +285,18 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { return err } + if cmd.ProtectLastAdmin { + lastAdmin, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) + if err != nil { + return err + } + + if lastAdmin { + return m.ErrLastTeamAdmin + } + + } + var rawSql = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?" res, err := sess.Exec(rawSql, cmd.OrgId, cmd.TeamId, cmd.UserId) if err != nil { @@ -299,6 +311,29 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { }) } +func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool, error) { + rawSql := "SELECT user_id FROM team_member WHERE org_id=? and team_id=? and permission=?" + userIds := []*int64{} + err := sess.SQL(rawSql, orgId, teamId, m.PERMISSION_ADMIN).Find(&userIds) + if err != nil { + return false, err + } + + isAdmin := false + for _, adminId := range userIds { + if userId == *adminId { + isAdmin = true + break + } + } + + if isAdmin && len(userIds) == 1 { + return true, nil + } + + return false, err +} + // GetTeamMembers return a list of members for the specified team func GetTeamMembers(query *m.GetTeamMembersQuery) error { query.Result = make([]*m.TeamMemberDTO, 0) diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index 1c5f2024a79..ca5379bae65 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -152,6 +152,23 @@ func TestTeamCommandsAndQueries(t *testing.T) { So(len(q2.Result), ShouldEqual, 0) }) + Convey("When ProtectLastAdmin is set to true", func() { + err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: int64(m.PERMISSION_ADMIN)}) + So(err, ShouldBeNil) + + Convey("A user should not be able to remove the last admin", func() { + err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true}) + So(err, ShouldEqual, m.ErrLastTeamAdmin) + }) + + Convey("A user should be able to remove an admin if there are other admins", func() { + err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: int64(m.PERMISSION_ADMIN)}) + err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true}) + So(err, ShouldEqual, nil) + }) + + }) + Convey("Should be able to remove a group with users and permissions", func() { groupId := group2.Result.Id err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]}) From c823ad5de7f1ce2da450b2a65902bcf236c2d113 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 12 Mar 2019 17:24:18 +0100 Subject: [PATCH 063/101] team: uses PermissionType instead of int64 for permissions. --- pkg/api/team.go | 2 +- pkg/models/team_member.go | 20 ++++++++++---------- pkg/services/sqlstore/team.go | 1 + pkg/services/sqlstore/team_test.go | 8 ++++---- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/api/team.go b/pkg/api/team.go index 6d5753fdc90..ab853888f76 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -23,7 +23,7 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo UserId: c.SignedInUser.UserId, OrgId: cmd.OrgId, TeamId: cmd.Result.Id, - Permission: int64(m.PERMISSION_ADMIN), + Permission: m.PERMISSION_ADMIN, } if err := bus.Dispatch(&addMemberCmd); err != nil { diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index 0cc39b0f605..9b7c2aeb0a4 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -17,7 +17,7 @@ type TeamMember struct { TeamId int64 UserId int64 External bool - Permission int64 + Permission PermissionType Created time.Time Updated time.Time @@ -27,18 +27,18 @@ type TeamMember struct { // COMMANDS type AddTeamMemberCommand struct { - UserId int64 `json:"userId" binding:"Required"` - OrgId int64 `json:"-"` - TeamId int64 `json:"-"` - External bool `json:"-"` - Permission int64 `json:"-"` + UserId int64 `json:"userId" binding:"Required"` + OrgId int64 `json:"-"` + TeamId int64 `json:"-"` + External bool `json:"-"` + Permission PermissionType `json:"-"` } type UpdateTeamMemberCommand struct { - UserId int64 `json:"-"` - OrgId int64 `json:"-"` - TeamId int64 `json:"-"` - Permission int64 `json:"permission"` + UserId int64 `json:"-"` + OrgId int64 `json:"-"` + TeamId int64 `json:"-"` + Permission PermissionType `json:"permission"` } type RemoveTeamMemberCommand struct { diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 3848adcc7dc..bf993a930f2 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -271,6 +271,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { return m.ErrTeamMemberNotFound } + // TODO: check to make sure that permission is a legal value member.Permission = cmd.Permission _, err = sess.Cols("permission").Where("org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId).Update(member) diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index ca5379bae65..ac357c57a53 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -91,7 +91,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { UserId: userId, OrgId: testOrgId, TeamId: team.Id, - Permission: int64(m.PERMISSION_ADMIN), + Permission: m.PERMISSION_ADMIN, }) So(err, ShouldBeNil) @@ -107,7 +107,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { UserId: 1, OrgId: testOrgId, TeamId: group1.Result.Id, - Permission: int64(m.PERMISSION_ADMIN), + Permission: m.PERMISSION_ADMIN, }) So(err, ShouldEqual, m.ErrTeamMemberNotFound) @@ -153,7 +153,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { }) Convey("When ProtectLastAdmin is set to true", func() { - err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: int64(m.PERMISSION_ADMIN)}) + err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: m.PERMISSION_ADMIN}) So(err, ShouldBeNil) Convey("A user should not be able to remove the last admin", func() { @@ -162,7 +162,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { }) Convey("A user should be able to remove an admin if there are other admins", func() { - err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: int64(m.PERMISSION_ADMIN)}) + err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: m.PERMISSION_ADMIN}) err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true}) So(err, ShouldEqual, nil) }) From c826f39a8bef2bbfb76c300df655bf4b17536644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 13 Mar 2019 07:18:57 +0100 Subject: [PATCH 064/101] teams: defaulting invalid permission level to member permission level --- pkg/services/sqlstore/team.go | 5 ++++- pkg/services/sqlstore/team_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index bf993a930f2..c36e45ac503 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -271,7 +271,10 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { return m.ErrTeamMemberNotFound } - // TODO: check to make sure that permission is a legal value + if cmd.Permission != int64(m.PERMISSION_ADMIN) { + cmd.Permission = 0 + } + member.Permission = cmd.Permission _, err = sess.Cols("permission").Where("org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId).Update(member) diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index ac357c57a53..5580f5f9fab 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -102,6 +102,34 @@ func TestTeamCommandsAndQueries(t *testing.T) { So(qAfterUpdate.Result[0].Permission, ShouldEqual, m.PERMISSION_ADMIN) }) + Convey("Should default to member permission level when updating a user with invalid permission level", func() { + userID := userIds[0] + team := group1.Result + addMemberCmd := m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userID} + err = AddTeamMember(&addMemberCmd) + So(err, ShouldBeNil) + + qBeforeUpdate := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id} + err = GetTeamMembers(qBeforeUpdate) + So(err, ShouldBeNil) + So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0) + + invalidPermissionLevel := 1337 + err = UpdateTeamMember(&m.UpdateTeamMemberCommand{ + UserId: userID, + OrgId: testOrgId, + TeamId: team.Id, + Permission: int64(invalidPermissionLevel), + }) + + So(err, ShouldBeNil) + + qAfterUpdate := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id} + err = GetTeamMembers(qAfterUpdate) + So(err, ShouldBeNil) + So(qAfterUpdate.Result[0].Permission, ShouldEqual, 0) + }) + Convey("Shouldn't be able to update a user not in the team.", func() { err = UpdateTeamMember(&m.UpdateTeamMemberCommand{ UserId: 1, From 246e1280489432ae0998ec5a8dbb3066ea9e95b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 13 Mar 2019 07:27:32 +0100 Subject: [PATCH 065/101] teams: changed permission to permission type instead of int --- pkg/services/sqlstore/team.go | 2 +- pkg/services/sqlstore/team_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index c36e45ac503..f7cb7b1ce45 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -271,7 +271,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { return m.ErrTeamMemberNotFound } - if cmd.Permission != int64(m.PERMISSION_ADMIN) { + if cmd.Permission != m.PERMISSION_ADMIN { cmd.Permission = 0 } diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index 5580f5f9fab..c63b28625b7 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -114,12 +114,12 @@ func TestTeamCommandsAndQueries(t *testing.T) { So(err, ShouldBeNil) So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0) - invalidPermissionLevel := 1337 + invalidPermissionLevel := m.PERMISSION_EDIT err = UpdateTeamMember(&m.UpdateTeamMemberCommand{ UserId: userID, OrgId: testOrgId, TeamId: team.Id, - Permission: int64(invalidPermissionLevel), + Permission: invalidPermissionLevel, }) So(err, ShouldBeNil) From c420af16b14e586b96d190e52f13805e0491e16a Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 13 Mar 2019 10:11:53 +0100 Subject: [PATCH 066/101] teams: editor/viewer team admin cant remove the last admin. --- pkg/api/team_members.go | 6 +++++- pkg/models/team_member.go | 9 +++++---- pkg/services/sqlstore/team.go | 12 ++++++++++++ pkg/services/sqlstore/team_test.go | 12 +++++++++++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index 72aded688ec..4e2dd86a959 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -67,6 +67,10 @@ func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { return Error(403, "Not allowed to update team member", err) } + if c.OrgRole != m.ROLE_ADMIN { + cmd.ProtectLastAdmin = true + } + cmd.TeamId = teamId cmd.UserId = c.ParamsInt64(":userId") cmd.OrgId = orgId @@ -91,7 +95,7 @@ func (hs *HTTPServer) RemoveTeamMember(c *m.ReqContext) Response { } protectLastAdmin := false - if c.OrgRole == m.ROLE_EDITOR { + if c.OrgRole != m.ROLE_ADMIN { protectLastAdmin = true } diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index 9b7c2aeb0a4..6d0ae7793b3 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -35,10 +35,11 @@ type AddTeamMemberCommand struct { } type UpdateTeamMemberCommand struct { - UserId int64 `json:"-"` - OrgId int64 `json:"-"` - TeamId int64 `json:"-"` - Permission PermissionType `json:"permission"` + UserId int64 `json:"-"` + OrgId int64 `json:"-"` + TeamId int64 `json:"-"` + Permission PermissionType `json:"permission"` + ProtectLastAdmin bool `json:"-"` } type RemoveTeamMemberCommand struct { diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index f7cb7b1ce45..85801f42832 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -271,6 +271,18 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { return m.ErrTeamMemberNotFound } + if cmd.ProtectLastAdmin { + lastAdmin, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) + if err != nil { + return err + } + + if lastAdmin { + return m.ErrLastTeamAdmin + } + + } + if cmd.Permission != m.PERMISSION_ADMIN { cmd.Permission = 0 } diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index c63b28625b7..7ac78733af7 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -190,11 +190,21 @@ func TestTeamCommandsAndQueries(t *testing.T) { }) Convey("A user should be able to remove an admin if there are other admins", func() { - err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: m.PERMISSION_ADMIN}) + AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: m.PERMISSION_ADMIN}) err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true}) So(err, ShouldEqual, nil) }) + Convey("A user should not be able to remove the admin permission for the last admin", func() { + err = UpdateTeamMember(&m.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true}) + So(err, ShouldEqual, m.ErrLastTeamAdmin) + }) + + Convey("A user should be able to remove the admin permission if there are other admins", func() { + AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: m.PERMISSION_ADMIN}) + err = UpdateTeamMember(&m.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true}) + So(err, ShouldEqual, nil) + }) }) Convey("Should be able to remove a group with users and permissions", func() { From 782b5b6a3ab5b1965f1cf1a17d03867cfc376cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 13 Mar 2019 10:38:09 +0100 Subject: [PATCH 067/101] teams: viewers and editors can view teams --- pkg/api/api.go | 8 ++++---- pkg/api/index.go | 9 +-------- pkg/api/team.go | 5 +++++ pkg/middleware/auth.go | 11 +++-------- public/app/routes/routes.ts | 4 ++-- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 9acd9485312..24183e63782 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -14,7 +14,7 @@ func (hs *HTTPServer) registerRoutes() { reqGrafanaAdmin := middleware.ReqGrafanaAdmin reqEditorRole := middleware.ReqEditorRole reqOrgAdmin := middleware.ReqOrgAdmin - reqAdminOrEditorCanAdmin := middleware.EditorCanAdmin(hs.Cfg.EditorsCanAdmin) + reqAdminOrCanAdmin := middleware.AdminOrCanAdmin(hs.Cfg.EditorsCanAdmin) redirectFromLegacyDashboardURL := middleware.RedirectFromLegacyDashboardURL() redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL() quota := middleware.Quota(hs.QuotaService) @@ -42,8 +42,8 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/org/users", reqOrgAdmin, hs.Index) r.Get("/org/users/new", reqOrgAdmin, hs.Index) r.Get("/org/users/invite", reqOrgAdmin, hs.Index) - r.Get("/org/teams", reqAdminOrEditorCanAdmin, hs.Index) - r.Get("/org/teams/*", reqAdminOrEditorCanAdmin, hs.Index) + r.Get("/org/teams", reqAdminOrCanAdmin, hs.Index) + r.Get("/org/teams/*", reqAdminOrCanAdmin, hs.Index) r.Get("/org/apikeys/", reqOrgAdmin, hs.Index) r.Get("/dashboard/import/", reqSignedIn, hs.Index) r.Get("/configuration", reqGrafanaAdmin, hs.Index) @@ -163,7 +163,7 @@ func (hs *HTTPServer) registerRoutes() { teamsRoute.Delete("/:teamId/members/:userId", Wrap(hs.RemoveTeamMember)) teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences)) teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences)) - }, reqAdminOrEditorCanAdmin) + }, reqAdminOrCanAdmin) // team without requirement of user to be org admin apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { diff --git a/pkg/api/index.go b/pkg/api/index.go index 3f60290ea55..e7555e14621 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -327,7 +327,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er }) } - if c.OrgRole == m.ROLE_EDITOR && hs.Cfg.EditorsCanAdmin { + if (c.OrgRole == m.ROLE_EDITOR || c.OrgRole == m.ROLE_VIEWER) && hs.Cfg.EditorsCanAdmin { cfgNode := &dtos.NavLink{ Id: "cfg", Text: "Configuration", @@ -342,13 +342,6 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er Icon: "gicon gicon-team", Url: setting.AppSubUrl + "/org/teams", }, - { - Text: "Plugins", - Id: "plugins", - Description: "View and configure plugins", - Icon: "gicon gicon-plugins", - Url: setting.AppSubUrl + "/plugins", - }, }, } diff --git a/pkg/api/team.go b/pkg/api/team.go index ab853888f76..eb7b1df37be 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -11,6 +11,11 @@ import ( // POST /api/teams func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Response { cmd.OrgId = c.OrgId + + if c.OrgRole == m.ROLE_VIEWER { + return Error(403, "Not allowed to create team.", nil) + } + if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNameTaken { return Error(409, "Team name taken", err) diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 6bf37e7fd50..8c1e5e04ae7 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -87,18 +87,13 @@ func Auth(options *AuthOptions) macaron.Handler { } } -func EditorCanAdmin(enabled bool) macaron.Handler { +func AdminOrCanAdmin(enabled bool) macaron.Handler { return func(c *m.ReqContext) { - ok := false if c.OrgRole == m.ROLE_ADMIN { - ok = true + return } - if c.OrgRole == m.ROLE_EDITOR && enabled { - ok = true - } - - if !ok { + if !enabled { accessForbidden(c) } } diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index 6fe0483c100..19bb96be603 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -195,7 +195,7 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { .when('/org/teams', { template: '', resolve: { - roles: () => ['Editor', 'Admin'], + roles: () => (config.editorsCanAdmin ? [] : ['Editor', 'Admin']), component: () => TeamList, }, }) @@ -207,7 +207,7 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { .when('/org/teams/edit/:id/:page?', { template: '', resolve: { - roles: () => (config.editorsCanAdmin ? ['Editor', 'Admin'] : ['Admin']), + roles: () => (config.editorsCanAdmin ? [] : ['Admin']), component: () => TeamPages, }, }) From b60e71c28b0c62c23717a88ba48bde97a97fdeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 13 Mar 2019 14:05:08 +0100 Subject: [PATCH 068/101] teams: moved logic for searchteams to backend --- pkg/api/api.go | 2 +- pkg/api/team.go | 4 ++-- pkg/api/team_test.go | 10 ++++++++-- public/app/features/teams/state/actions.ts | 6 ++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 24183e63782..9d1151a757e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -168,7 +168,7 @@ func (hs *HTTPServer) registerRoutes() { // team without requirement of user to be org admin apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { teamsRoute.Get("/:teamId", Wrap(GetTeamByID)) - teamsRoute.Get("/search", Wrap(SearchTeams)) + teamsRoute.Get("/search", Wrap(hs.SearchTeams)) }) // org information available to all users. diff --git a/pkg/api/team.go b/pkg/api/team.go index eb7b1df37be..fd34c0ab720 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -81,7 +81,7 @@ func DeleteTeamByID(c *m.ReqContext) Response { } // GET /api/teams/search -func SearchTeams(c *m.ReqContext) Response { +func (hs *HTTPServer) SearchTeams(c *m.ReqContext) Response { perPage := c.QueryInt("perpage") if perPage <= 0 { perPage = 1000 @@ -92,7 +92,7 @@ func SearchTeams(c *m.ReqContext) Response { } var userIdFilter int64 - if c.QueryBool("showMine") { + if hs.Cfg.EditorsCanAdmin && c.OrgRole != m.ROLE_ADMIN { userIdFilter = c.SignedInUser.UserId } diff --git a/pkg/api/team_test.go b/pkg/api/team_test.go index a1984288870..cab59cc5f98 100644 --- a/pkg/api/team_test.go +++ b/pkg/api/team_test.go @@ -3,6 +3,8 @@ package api import ( "testing" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" @@ -20,6 +22,10 @@ func TestTeamApiEndpoint(t *testing.T) { TotalCount: 2, } + hs := &HTTPServer{ + Cfg: setting.NewCfg(), + } + Convey("When searching with no parameters", func() { loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) { var sentLimit int @@ -33,7 +39,7 @@ func TestTeamApiEndpoint(t *testing.T) { return nil }) - sc.handlerFunc = SearchTeams + sc.handlerFunc = hs.SearchTeams sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() So(sentLimit, ShouldEqual, 1000) @@ -60,7 +66,7 @@ func TestTeamApiEndpoint(t *testing.T) { return nil }) - sc.handlerFunc = SearchTeams + sc.handlerFunc = hs.SearchTeams sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec() So(sentLimit, ShouldEqual, 10) diff --git a/public/app/features/teams/state/actions.ts b/public/app/features/teams/state/actions.ts index bfccddeefc5..e2582839233 100644 --- a/public/app/features/teams/state/actions.ts +++ b/public/app/features/teams/state/actions.ts @@ -1,9 +1,8 @@ import { ThunkAction } from 'redux-thunk'; import { getBackendSrv } from 'app/core/services/backend_srv'; -import { OrgRole, StoreState, Team, TeamGroup, TeamMember } from 'app/types'; +import { StoreState, Team, TeamGroup, TeamMember } from 'app/types'; import { updateNavIndex, UpdateNavIndexAction } from 'app/core/actions'; import { buildNavModel } from './navModel'; -import { contextSrv } from '../../../core/services/context_srv'; export enum ActionTypes { LoadTeams = 'LOAD_TEAMS', @@ -86,8 +85,7 @@ export const setSearchQuery = (searchQuery: string): SetSearchQueryAction => ({ export function loadTeams(): ThunkResult { return async dispatch => { - const showMine = contextSrv.user.orgRole === OrgRole.Editor; - const response = await getBackendSrv().get('/api/teams/search', { perpage: 1000, page: 1, showMine }); + const response = await getBackendSrv().get('/api/teams/search', { perpage: 1000, page: 1 }); dispatch(teamsLoaded(response.teams)); }; } From b82b94a2470e6d6f5889076b20dd4155ec4d473e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 13 Mar 2019 15:34:38 +0100 Subject: [PATCH 069/101] teams: disable buttons for team members --- .../components/DeleteButton/DeleteButton.tsx | 24 +- .../app/features/teams/TeamMembers.test.tsx | 34 +- public/app/features/teams/TeamMembers.tsx | 28 +- .../app/features/teams/__mocks__/teamMocks.ts | 4 +- .../__snapshots__/TeamMembers.test.tsx.snap | 884 ++++++++++++++---- .../features/teams/state/selectors.test.ts | 2 +- 6 files changed, 734 insertions(+), 242 deletions(-) diff --git a/packages/grafana-ui/src/components/DeleteButton/DeleteButton.tsx b/packages/grafana-ui/src/components/DeleteButton/DeleteButton.tsx index df65d156ab3..d262c821968 100644 --- a/packages/grafana-ui/src/components/DeleteButton/DeleteButton.tsx +++ b/packages/grafana-ui/src/components/DeleteButton/DeleteButton.tsx @@ -2,6 +2,7 @@ import React, { PureComponent, SyntheticEvent } from 'react'; interface Props { onConfirm(): void; + disabled?: boolean; } interface State { @@ -33,25 +34,22 @@ export class DeleteButton extends PureComponent { }; render() { - const { onConfirm } = this.props; - let showConfirm; - let showDeleteButton; - - if (this.state.showConfirm) { - showConfirm = 'show'; - showDeleteButton = 'hide'; - } else { - showConfirm = 'hide'; - showDeleteButton = 'show'; - } + const { onConfirm, disabled } = this.props; + const showConfirmClass = this.state.showConfirm ? 'show' : 'hide'; + const showDeleteButtonClass = this.state.showConfirm ? 'hide' : 'show'; + const disabledClass = disabled ? 'disabled btn-inverse' : ''; + const onClick = disabled ? () => {} : this.onClickDelete; return ( - + - + Cancel diff --git a/public/app/features/teams/TeamMembers.test.tsx b/public/app/features/teams/TeamMembers.test.tsx index 64609f1fd79..f6f0b4a5e49 100644 --- a/public/app/features/teams/TeamMembers.test.tsx +++ b/public/app/features/teams/TeamMembers.test.tsx @@ -6,16 +6,17 @@ import { getMockTeamMember, getMockTeamMembers } from './__mocks__/teamMocks'; import { SelectOptionItem } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; +const signedInUserId = 1; +const originalContextSrv = contextSrv; + jest.mock('app/core/services/context_srv', () => ({ contextSrv: { isGrafanaAdmin: false, hasRole: role => false, - user: { id: 1 }, + user: { id: signedInUserId }, }, })); -const originalContextSrv = contextSrv; - interface SetupProps { propOverrides?: object; isGrafanaAdmin?: boolean; @@ -64,7 +65,7 @@ describe('Render', () => { it('should render team members', () => { const { wrapper } = setup({ propOverrides: { - members: getMockTeamMembers(5), + members: getMockTeamMembers(5, 5), }, }); @@ -74,7 +75,7 @@ describe('Render', () => { it('should render team members when sync enabled', () => { const { wrapper } = setup({ propOverrides: { - members: getMockTeamMembers(5), + members: getMockTeamMembers(5, 5), syncEnabled: true, }, }); @@ -84,8 +85,7 @@ describe('Render', () => { describe('when feature toggle editorsCanAdmin is turned on', () => { it('should render permissions select if user is Grafana Admin', () => { - const members = getMockTeamMembers(5); - members[4].permission = TeamPermissionLevel.Admin; + const members = getMockTeamMembers(5, 5); const { wrapper } = setup({ propOverrides: { members, editorsCanAdmin: true }, isGrafanaAdmin: true, @@ -96,8 +96,7 @@ describe('Render', () => { }); it('should render permissions select if user is Org Admin', () => { - const members = getMockTeamMembers(5); - members[4].permission = TeamPermissionLevel.Admin; + const members = getMockTeamMembers(5, 5); const { wrapper } = setup({ propOverrides: { members, editorsCanAdmin: true }, isGrafanaAdmin: false, @@ -108,8 +107,7 @@ describe('Render', () => { }); it('should render permissions select if user is team admin', () => { - const members = getMockTeamMembers(5); - members[0].permission = TeamPermissionLevel.Admin; + const members = getMockTeamMembers(5, signedInUserId); const { wrapper } = setup({ propOverrides: { members, editorsCanAdmin: true }, isGrafanaAdmin: false, @@ -118,6 +116,20 @@ describe('Render', () => { expect(wrapper).toMatchSnapshot(); }); + + it('should render span and disable buttons if user is team member', () => { + const members = getMockTeamMembers(5, 5); + const { wrapper } = setup({ + propOverrides: { + members, + editorsCanAdmin: true, + }, + isGrafanaAdmin: false, + isOrgAdmin: false, + }); + + expect(wrapper).toMatchSnapshot(); + }); }); }); diff --git a/public/app/features/teams/TeamMembers.tsx b/public/app/features/teams/TeamMembers.tsx index 89ad24f9c58..fc5706ec68f 100644 --- a/public/app/features/teams/TeamMembers.tsx +++ b/public/app/features/teams/TeamMembers.tsx @@ -39,7 +39,7 @@ export class TeamMembers extends PureComponent { constructor(props) { super(props); this.state = { isAdding: false, newTeamMember: null }; - this.renderPermissionsSelect = this.renderPermissionsSelect.bind(this); + this.renderPermissions = this.renderPermissions.bind(this); } componentDidMount() { @@ -88,13 +88,19 @@ export class TeamMembers extends PureComponent { this.props.updateTeamMember(updatedTeamMember); }; - renderPermissionsSelect(member: TeamMember) { + private isSignedInUserTeamAdmin = () => { const { members, editorsCanAdmin } = this.props; const userInMembers = members.find(m => m.userId === contextSrv.user.id); - const isUserTeamAdmin = - contextSrv.isGrafanaAdmin || contextSrv.hasRole(OrgRole.Admin) - ? true - : userInMembers && userInMembers.permission === TeamPermissionLevel.Admin; + const isAdmin = contextSrv.isGrafanaAdmin || contextSrv.hasRole(OrgRole.Admin); + const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin; + const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin; + + return isSignedInUserTeamAdmin || !editorsCanAdmin; + }; + + renderPermissions(member: TeamMember) { + const { editorsCanAdmin } = this.props; + const isUserTeamAdmin = this.isSignedInUserTeamAdmin(); const value = teamsPermissionLevels.find(dp => dp.value === member.permission); return ( @@ -125,10 +131,10 @@ export class TeamMembers extends PureComponent { {member.login} {member.email} - this.onRemoveMember(member)} /> + this.onRemoveMember(member)} disabled={!this.isSignedInUserTeamAdmin()} />
+
+
+`; + +exports[`Render when feature toggle editorsCanAdmin is turned on should render span and disable buttons if user is team member 1`] = ` +
+
+
+ +
+
+ +
+ +
+ +
+ Add team member +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/app/features/teams/state/selectors.test.ts b/public/app/features/teams/state/selectors.test.ts index e88fbdfd4b1..1721437c929 100644 --- a/public/app/features/teams/state/selectors.test.ts +++ b/public/app/features/teams/state/selectors.test.ts @@ -40,7 +40,7 @@ describe('Team selectors', () => { }); describe('Get members', () => { - const mockTeamMembers = getMockTeamMembers(5); + const mockTeamMembers = getMockTeamMembers(5, 5); it('should return team members', () => { const mockState: TeamState = { From fc0461134f0e4ff014f0b2b33719abf4eb842987 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 13 Mar 2019 15:47:47 +0100 Subject: [PATCH 070/101] dashboards: simplified code. --- pkg/api/dashboard_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index c54647d9847..ea69c049115 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -972,12 +972,9 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d Convey(desc+" "+url, func() { defer bus.ClearBusHandlers() - cfg := setting.NewCfg() - cfg.EditorsCanAdmin = false - hs := HTTPServer{ Bus: bus.GetBus(), - Cfg: cfg, + Cfg: setting.NewCfg(), } sc := setupScenarioContext(url) From ccfd6789ca1f9ae0ea6b9927cc45813d795c5ad9 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 13 Mar 2019 16:32:59 +0100 Subject: [PATCH 071/101] teams: cleanup. --- pkg/models/team_member.go | 18 +++++++++--------- pkg/services/teamguardian/team.go | 2 +- pkg/services/teamguardian/teams_test.go | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index 6d0ae7793b3..c9afd4cd75f 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -64,13 +64,13 @@ type GetTeamMembersQuery struct { // Projections and DTOs type TeamMemberDTO struct { - OrgId int64 `json:"orgId"` - TeamId int64 `json:"teamId"` - UserId int64 `json:"userId"` - External bool `json:"-"` - Email string `json:"email"` - Login string `json:"login"` - AvatarUrl string `json:"avatarUrl"` - Labels []string `json:"labels"` - Permission int64 `json:"permission"` + OrgId int64 `json:"orgId"` + TeamId int64 `json:"teamId"` + UserId int64 `json:"userId"` + External bool `json:"-"` + Email string `json:"email"` + Login string `json:"login"` + AvatarUrl string `json:"avatarUrl"` + Labels []string `json:"labels"` + Permission PermissionType `json:"permission"` } diff --git a/pkg/services/teamguardian/team.go b/pkg/services/teamguardian/team.go index 9946ae7c734..6fddc318f5e 100644 --- a/pkg/services/teamguardian/team.go +++ b/pkg/services/teamguardian/team.go @@ -25,7 +25,7 @@ func CanAdmin(orgId int64, teamId int64, user *m.SignedInUser) error { } for _, member := range cmd.Result { - if member.UserId == user.UserId && member.Permission == int64(m.PERMISSION_ADMIN) { + if member.UserId == user.UserId && member.Permission == m.PERMISSION_ADMIN { return nil } } diff --git a/pkg/services/teamguardian/teams_test.go b/pkg/services/teamguardian/teams_test.go index 9b1ba7ee4cb..2ec86769a29 100644 --- a/pkg/services/teamguardian/teams_test.go +++ b/pkg/services/teamguardian/teams_test.go @@ -45,7 +45,7 @@ func TestUpdateTeam(t *testing.T) { OrgId: testTeam.OrgId, TeamId: testTeam.Id, UserId: editor.UserId, - Permission: int64(m.PERMISSION_ADMIN), + Permission: m.PERMISSION_ADMIN, }} return nil }) @@ -67,7 +67,7 @@ func TestUpdateTeam(t *testing.T) { OrgId: testTeamOtherOrg.OrgId, TeamId: testTeamOtherOrg.Id, UserId: editor.UserId, - Permission: int64(m.PERMISSION_ADMIN), + Permission: m.PERMISSION_ADMIN, }} return nil }) From 3f57a81c4722cbd77328e370272e749fdcdcbc7a Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Wed, 13 Mar 2019 16:46:35 +0100 Subject: [PATCH 072/101] teams: cleanup. --- pkg/api/team_members.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index 4e2dd86a959..1674cc120ce 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -31,16 +31,13 @@ func GetTeamMembers(c *m.ReqContext) Response { // POST /api/teams/:teamId/members func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { - teamId := c.ParamsInt64(":teamId") - orgId := c.OrgId + cmd.OrgId = c.OrgId + cmd.TeamId = c.ParamsInt64(":teamId") - if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(cmd.OrgId, cmd.TeamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to add team member", err) } - cmd.TeamId = teamId - cmd.OrgId = orgId - if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Team not found", nil) From 6a63725df04fa5a2c717f7af63b485a171cb66fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Wed, 13 Mar 2019 16:48:15 +0100 Subject: [PATCH 073/101] teams: comment explaining input validation Co-Authored-By: xlson --- pkg/services/sqlstore/team.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 85801f42832..d76d8401499 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -283,7 +283,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { } - if cmd.Permission != m.PERMISSION_ADMIN { + if cmd.Permission != m.PERMISSION_ADMIN { // make sure we don't get invalid permission levels in store cmd.Permission = 0 } From 178d637b4e5fa21fb89ed102de6e6a6c022fe065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 14 Mar 2019 07:53:31 +0100 Subject: [PATCH 074/101] refactor: splitted TeamMembers to TeamMemberRow --- .../app/features/teams/TeamMemberRow.test.tsx | 82 + public/app/features/teams/TeamMemberRow.tsx | 106 + .../app/features/teams/TeamMembers.test.tsx | 206 +- public/app/features/teams/TeamMembers.tsx | 95 +- .../__snapshots__/TeamMemberRow.test.tsx.snap | 191 ++ .../__snapshots__/TeamMembers.test.tsx.snap | 2430 ++--------------- 6 files changed, 677 insertions(+), 2433 deletions(-) create mode 100644 public/app/features/teams/TeamMemberRow.test.tsx create mode 100644 public/app/features/teams/TeamMemberRow.tsx create mode 100644 public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap diff --git a/public/app/features/teams/TeamMemberRow.test.tsx b/public/app/features/teams/TeamMemberRow.test.tsx new file mode 100644 index 00000000000..87f771cc833 --- /dev/null +++ b/public/app/features/teams/TeamMemberRow.test.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { TeamMember, TeamPermissionLevel } from '../../types'; +import { getMockTeamMember } from './__mocks__/teamMocks'; +import { TeamMemberRow, Props } from './TeamMemberRow'; +import { SelectOptionItem } from '@grafana/ui'; + +const setup = (propOverrides?: object) => { + const props: Props = { + member: getMockTeamMember(), + syncEnabled: false, + editorsCanAdmin: false, + signedInUserIsTeamAdmin: false, + updateTeamMember: jest.fn(), + removeTeamMember: jest.fn(), + }; + + Object.assign(props, propOverrides); + + const wrapper = shallow(); + const instance = wrapper.instance() as TeamMemberRow; + + return { + wrapper, + instance, + }; +}; + +describe('Render', () => { + describe('when feature toggle editorsCanAdmin is turned on', () => { + it('should render permissions select if user is team admin', () => { + const { wrapper } = setup({ editorsCanAdmin: true, signedInUserIsTeamAdmin: true }); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should render span and disable buttons if user is team member', () => { + const { wrapper } = setup({ editorsCanAdmin: true, signedInUserIsTeamAdmin: false }); + + expect(wrapper).toMatchSnapshot(); + }); + }); + + describe('when feature toggle editorsCanAdmin is turned off', () => { + it('should not render permissions', () => { + const { wrapper } = setup({ editorsCanAdmin: false, signedInUserIsTeamAdmin: true }); + + expect(wrapper).toMatchSnapshot(); + }); + }); +}); + +describe('Functions', () => { + describe('on remove member', () => { + const member = getMockTeamMember(); + const { instance } = setup({ member }); + + instance.onRemoveMember(member); + + expect(instance.props.removeTeamMember).toHaveBeenCalledWith(1); + }); + + describe('on update permision for user in team', () => { + const member: TeamMember = { + userId: 3, + teamId: 2, + avatarUrl: '', + email: 'user@user.org', + labels: [], + login: 'member', + permission: TeamPermissionLevel.Member, + }; + const { instance } = setup({ member }); + const permission = TeamPermissionLevel.Admin; + const item: SelectOptionItem = { value: permission }; + const expectedTeamMemeber = { ...member, permission }; + + instance.onPermissionChange(item, member); + + expect(instance.props.updateTeamMember).toHaveBeenCalledWith(expectedTeamMemeber); + }); +}); diff --git a/public/app/features/teams/TeamMemberRow.tsx b/public/app/features/teams/TeamMemberRow.tsx new file mode 100644 index 00000000000..e0bd26f4fd7 --- /dev/null +++ b/public/app/features/teams/TeamMemberRow.tsx @@ -0,0 +1,106 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; +import { DeleteButton, Select, SelectOptionItem } from '@grafana/ui'; + +import { TeamMember, teamsPermissionLevels } from 'app/types'; +import { WithFeatureToggle } from 'app/core/components/WithFeatureToggle'; +import { updateTeamMember, removeTeamMember } from './state/actions'; +import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; + +export interface Props { + member: TeamMember; + syncEnabled: boolean; + editorsCanAdmin: boolean; + signedInUserIsTeamAdmin: boolean; + removeTeamMember?: typeof removeTeamMember; + updateTeamMember?: typeof updateTeamMember; +} + +export class TeamMemberRow extends PureComponent { + constructor(props: Props) { + super(props); + this.renderLabels = this.renderLabels.bind(this); + this.renderPermissions = this.renderPermissions.bind(this); + } + + onRemoveMember(member: TeamMember) { + this.props.removeTeamMember(member.userId); + } + + onPermissionChange = (item: SelectOptionItem, member: TeamMember) => { + const permission = item.value; + const updatedTeamMember = { ...member, permission }; + + this.props.updateTeamMember(updatedTeamMember); + }; + + renderPermissions(member: TeamMember) { + const { editorsCanAdmin, signedInUserIsTeamAdmin } = this.props; + const value = teamsPermissionLevels.find(dp => dp.value === member.permission); + + return ( + + + + ); + } + + renderLabels(labels: string[]) { + if (!labels) { + return + ); + } + + render() { + const { member, syncEnabled, signedInUserIsTeamAdmin } = this.props; + return ( + + + + + {this.renderPermissions(member)} + {syncEnabled && this.renderLabels(member.labels)} + + + ); + } +} + +function mapStateToProps(state) { + return {}; +} + +const mapDispatchToProps = { + removeTeamMember, + updateTeamMember, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(TeamMemberRow); diff --git a/public/app/features/teams/TeamMembers.test.tsx b/public/app/features/teams/TeamMembers.test.tsx index f6f0b4a5e49..fc65b0532e1 100644 --- a/public/app/features/teams/TeamMembers.test.tsx +++ b/public/app/features/teams/TeamMembers.test.tsx @@ -1,45 +1,29 @@ import React from 'react'; import { shallow } from 'enzyme'; import { TeamMembers, Props, State } from './TeamMembers'; -import { TeamMember, TeamPermissionLevel } from '../../types'; -import { getMockTeamMember, getMockTeamMembers } from './__mocks__/teamMocks'; -import { SelectOptionItem } from '@grafana/ui'; -import { contextSrv } from 'app/core/services/context_srv'; +import { TeamMember, OrgRole } from '../../types'; +import { getMockTeamMembers } from './__mocks__/teamMocks'; +import { User } from 'app/core/services/context_srv'; const signedInUserId = 1; -const originalContextSrv = contextSrv; -jest.mock('app/core/services/context_srv', () => ({ - contextSrv: { - isGrafanaAdmin: false, - hasRole: role => false, - user: { id: signedInUserId }, - }, -})); - -interface SetupProps { - propOverrides?: object; - isGrafanaAdmin?: boolean; - isOrgAdmin?: boolean; -} - -const setup = (setupProps: SetupProps) => { +const setup = (propOverrides?: object) => { const props: Props = { members: [] as TeamMember[], searchMemberQuery: '', setSearchMemberQuery: jest.fn(), loadTeamMembers: jest.fn(), addTeamMember: jest.fn(), - removeTeamMember: jest.fn(), - updateTeamMember: jest.fn(), syncEnabled: false, editorsCanAdmin: false, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + } as User, }; - contextSrv.isGrafanaAdmin = setupProps.isGrafanaAdmin || false; - contextSrv.hasRole = role => setupProps.isOrgAdmin || false; - - Object.assign(props, setupProps.propOverrides); + Object.assign(props, propOverrides); const wrapper = shallow(); const instance = wrapper.instance() as TeamMembers; @@ -51,11 +35,6 @@ const setup = (setupProps: SetupProps) => { }; describe('Render', () => { - beforeEach(() => { - contextSrv.isGrafanaAdmin = originalContextSrv.isGrafanaAdmin; - contextSrv.hasRole = originalContextSrv.hasRole; - }); - it('should render component', () => { const { wrapper } = setup({}); @@ -63,74 +42,16 @@ describe('Render', () => { }); it('should render team members', () => { - const { wrapper } = setup({ - propOverrides: { - members: getMockTeamMembers(5, 5), - }, - }); + const { wrapper } = setup({ members: getMockTeamMembers(5, 5) }); expect(wrapper).toMatchSnapshot(); }); it('should render team members when sync enabled', () => { - const { wrapper } = setup({ - propOverrides: { - members: getMockTeamMembers(5, 5), - syncEnabled: true, - }, - }); + const { wrapper } = setup({ members: getMockTeamMembers(5, 5), syncEnabled: true }); expect(wrapper).toMatchSnapshot(); }); - - describe('when feature toggle editorsCanAdmin is turned on', () => { - it('should render permissions select if user is Grafana Admin', () => { - const members = getMockTeamMembers(5, 5); - const { wrapper } = setup({ - propOverrides: { members, editorsCanAdmin: true }, - isGrafanaAdmin: true, - isOrgAdmin: false, - }); - - expect(wrapper).toMatchSnapshot(); - }); - - it('should render permissions select if user is Org Admin', () => { - const members = getMockTeamMembers(5, 5); - const { wrapper } = setup({ - propOverrides: { members, editorsCanAdmin: true }, - isGrafanaAdmin: false, - isOrgAdmin: true, - }); - - expect(wrapper).toMatchSnapshot(); - }); - - it('should render permissions select if user is team admin', () => { - const members = getMockTeamMembers(5, signedInUserId); - const { wrapper } = setup({ - propOverrides: { members, editorsCanAdmin: true }, - isGrafanaAdmin: false, - isOrgAdmin: false, - }); - - expect(wrapper).toMatchSnapshot(); - }); - - it('should render span and disable buttons if user is team member', () => { - const members = getMockTeamMembers(5, 5); - const { wrapper } = setup({ - propOverrides: { - members, - editorsCanAdmin: true, - }, - isGrafanaAdmin: false, - isOrgAdmin: false, - }); - - expect(wrapper).toMatchSnapshot(); - }); - }); }); describe('Functions', () => { @@ -144,15 +65,6 @@ describe('Functions', () => { }); }); - describe('on remove member', () => { - const { instance } = setup({}); - const mockTeamMember = getMockTeamMember(); - - instance.onRemoveMember(mockTeamMember); - - expect(instance.props.removeTeamMember).toHaveBeenCalledWith(1); - }); - describe('on add user to team', () => { const { wrapper, instance } = setup({}); const state = wrapper.state() as State; @@ -169,23 +81,85 @@ describe('Functions', () => { expect(instance.props.addTeamMember).toHaveBeenCalledWith(1); }); - describe('on update permision for user in team', () => { - const { instance } = setup({}); - const permission = TeamPermissionLevel.Admin; - const item: SelectOptionItem = { value: permission }; - const member: TeamMember = { - userId: 3, - teamId: 2, - avatarUrl: '', - email: 'user@user.org', - labels: [], - login: 'member', - permission: TeamPermissionLevel.Member, - }; - const expectedTeamMemeber = { ...member, permission }; + describe('isSignedInUserTeamAdmin', () => { + describe('when feature toggle editorsCanAdmin is turned off', () => { + it('should return true', () => { + const { instance } = setup({ editorsCanAdmin: false }); - instance.onPermissionChange(item, member); + const result = instance.isSignedInUserTeamAdmin(); - expect(instance.props.updateTeamMember).toHaveBeenCalledWith(expectedTeamMemeber); + expect(result).toBe(true); + }); + }); + + describe('when feature toggle editorsCanAdmin is turned on', () => { + it('should return true if signed in user is grafanaAdmin', () => { + const members = getMockTeamMembers(5, 5); + const { instance } = setup({ + members, + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: true, + orgRole: OrgRole.Viewer, + }, + }); + + const result = instance.isSignedInUserTeamAdmin(); + + expect(result).toBe(true); + }); + + it('should return true if signed in user is org admin', () => { + const members = getMockTeamMembers(5, 5); + const { instance } = setup({ + members, + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Admin, + }, + }); + + const result = instance.isSignedInUserTeamAdmin(); + + expect(result).toBe(true); + }); + + it('should return true if signed in user is team admin', () => { + const members = getMockTeamMembers(5, signedInUserId); + const { instance } = setup({ + members, + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + }, + }); + + const result = instance.isSignedInUserTeamAdmin(); + + expect(result).toBe(true); + }); + + it('should return false if signed in user is not grafanaAdmin, org admin or team admin', () => { + const members = getMockTeamMembers(5, 5); + const { instance } = setup({ + members, + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + }, + }); + + const result = instance.isSignedInUserTeamAdmin(); + + expect(result).toBe(false); + }); + }); }); }); diff --git a/public/app/features/teams/TeamMembers.tsx b/public/app/features/teams/TeamMembers.tsx index fc5706ec68f..db2b9024a1f 100644 --- a/public/app/features/teams/TeamMembers.tsx +++ b/public/app/features/teams/TeamMembers.tsx @@ -2,32 +2,25 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import SlideDown from 'app/core/components/Animations/SlideDown'; import { UserPicker } from 'app/core/components/Select/UserPicker'; -import { DeleteButton, Select, SelectOptionItem } from '@grafana/ui'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; -import { TeamMember, User, teamsPermissionLevels, TeamPermissionLevel, OrgRole } from 'app/types'; -import { - loadTeamMembers, - addTeamMember, - removeTeamMember, - setSearchMemberQuery, - updateTeamMember, -} from './state/actions'; +import { TeamMember, User, TeamPermissionLevel, OrgRole } from 'app/types'; +import { loadTeamMembers, addTeamMember, setSearchMemberQuery } from './state/actions'; import { getSearchMemberQuery, getTeamMembers } from './state/selectors'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { WithFeatureToggle } from 'app/core/components/WithFeatureToggle'; import { config } from 'app/core/config'; -import { contextSrv } from 'app/core/services/context_srv'; +import { contextSrv, User as SignedInUser } from 'app/core/services/context_srv'; +import TeamMemberRow from './TeamMemberRow'; export interface Props { members: TeamMember[]; searchMemberQuery: string; loadTeamMembers: typeof loadTeamMembers; addTeamMember: typeof addTeamMember; - removeTeamMember: typeof removeTeamMember; setSearchMemberQuery: typeof setSearchMemberQuery; - updateTeamMember: typeof updateTeamMember; syncEnabled: boolean; editorsCanAdmin?: boolean; + signedInUser?: SignedInUser; } export interface State { @@ -39,7 +32,6 @@ export class TeamMembers extends PureComponent { constructor(props) { super(props); this.state = { isAdding: false, newTeamMember: null }; - this.renderPermissions = this.renderPermissions.bind(this); } componentDidMount() { @@ -50,10 +42,6 @@ export class TeamMembers extends PureComponent { this.props.setSearchMemberQuery(value); }; - onRemoveMember(member: TeamMember) { - this.props.removeTeamMember(member.userId); - } - onToggleAdding = () => { this.setState({ isAdding: !this.state.isAdding }); }; @@ -81,65 +69,16 @@ export class TeamMembers extends PureComponent { ); } - onPermissionChange = (item: SelectOptionItem, member: TeamMember) => { - const permission = item.value; - const updatedTeamMember = { ...member, permission }; - - this.props.updateTeamMember(updatedTeamMember); - }; - - private isSignedInUserTeamAdmin = () => { - const { members, editorsCanAdmin } = this.props; - const userInMembers = members.find(m => m.userId === contextSrv.user.id); - const isAdmin = contextSrv.isGrafanaAdmin || contextSrv.hasRole(OrgRole.Admin); + isSignedInUserTeamAdmin = (): boolean => { + const { members, editorsCanAdmin, signedInUser } = this.props; + const userInMembers = members.find(m => m.userId === signedInUser.id); + const isAdmin = signedInUser.isGrafanaAdmin || signedInUser.orgRole === OrgRole.Admin; const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin; const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin; return isSignedInUserTeamAdmin || !editorsCanAdmin; }; - renderPermissions(member: TeamMember) { - const { editorsCanAdmin } = this.props; - const isUserTeamAdmin = this.isSignedInUserTeamAdmin(); - const value = teamsPermissionLevels.find(dp => dp.value === member.permission); - - return ( - - - - ); - } - - renderMember(member: TeamMember, syncEnabled: boolean) { - return ( - - - - - {this.renderPermissions(member)} - {syncEnabled && this.renderLabels(member.labels)} - - - ); - } - render() { const { isAdding } = this.state; const { searchMemberQuery, members, syncEnabled, editorsCanAdmin } = this.props; @@ -198,7 +137,18 @@ export class TeamMembers extends PureComponent { - {members && members.map(member => this.renderMember(member, syncEnabled))} + + {members && + members.map(member => ( + + ))} +
+ + Name + + Email + + Permission + +
+ + + testUser-1 + + test@test.com + +
+ + Member + +
+
+ +
+ + + testUser-2 + + test@test.com + +
+ + Member + +
+
+ +
+ + + testUser-3 + + test@test.com + +
+ + Member + +
+
+ +
+ + + testUser-4 + + test@test.com + +
+ + Member + +
+
+ +
+ + + testUser-5 + + test@test.com + +
+ + Admin + +
+
+ +
+ {signedInUserIsTeamAdmin && ( +
; + } + + return ( + + {labels.map(label => ( + {}} /> + ))} +
+ + {member.login}{member.email} + this.onRemoveMember(member)} disabled={!signedInUserIsTeamAdmin} /> +
-
- {isUserTeamAdmin && ( -
- - {member.login}{member.email} - this.onRemoveMember(member)} disabled={!this.isSignedInUserTeamAdmin()} /> -
@@ -211,15 +161,14 @@ function mapStateToProps(state) { members: getTeamMembers(state.team), searchMemberQuery: getSearchMemberQuery(state.team), editorsCanAdmin: config.editorsCanAdmin, // this makes the feature toggle mockable/controllable from tests, + signedInUser: contextSrv.user, // this makes the feature toggle mockable/controllable from tests, }; } const mapDispatchToProps = { loadTeamMembers, addTeamMember, - removeTeamMember, setSearchMemberQuery, - updateTeamMember, }; export default connect( diff --git a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap new file mode 100644 index 00000000000..3e7630d0618 --- /dev/null +++ b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap @@ -0,0 +1,191 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render when feature toggle editorsCanAdmin is turned off should not render permissions 1`] = ` + + + + + + testUser + + + test@test.com + + + +
+ +
+ +
+ + + + +`; + +exports[`Render when feature toggle editorsCanAdmin is turned on should render span and disable buttons if user is team member 1`] = ` + + + + + + testUser + + + test@test.com + + + +
+ + Member + +
+ +
+ + + + +`; diff --git a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap index da89d26d191..4d35a4a772b 100644 --- a/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMembers.test.tsx.snap @@ -177,361 +177,106 @@ exports[`Render should render team members 1`] = ` - - - - - - testUser-1 - - - test@test.com - - - -
- -
- -
- - - - - + - - - - - testUser-3 - - - test@test.com - - - -
- -
- -
- - - - - + - - - - - testUser-5 - - - test@test.com - - - -
- -
- -
- - - - - - - - - - - - - - testUser-2 - - - test@test.com - - - -
- -
- -
- - - - - - - - - - - - - - testUser-4 - - - test@test.com - - - -
- -
- -
- - - - - - - - - - -
-
-`; - -exports[`Render when feature toggle editorsCanAdmin is turned on should render permissions select if user is Grafana Admin 1`] = ` -
-
-
- -
-
- -
- -
- -
- Add team member -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Name - - Email - - Permission - -
- - - testUser-1 - - test@test.com - -
-
- -
- - - testUser-2 - - test@test.com - -
-
- -
- - - testUser-3 - - test@test.com - -
-
- -
- - - testUser-4 - - test@test.com - -
-
- -
- - - testUser-5 - - test@test.com - -
-
- -
-
-
-`; - -exports[`Render when feature toggle editorsCanAdmin is turned on should render permissions select if user is Org Admin 1`] = ` -
-
-
- -
-
- -
- -
- -
- Add team member -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Name - - Email - - Permission - -
- - - testUser-1 - - test@test.com - -
-
- -
- - - testUser-2 - - test@test.com - -
-
- -
- - - testUser-3 - - test@test.com - -
-
- -
- - - testUser-4 - - test@test.com - -
-
- -
- - - testUser-5 - - test@test.com - -
-
- -
-
-
-`; - -exports[`Render when feature toggle editorsCanAdmin is turned on should render permissions select if user is team admin 1`] = ` -
-
-
- -
-
- -
- -
- -
- Add team member -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - -
- - Name - - Email - - Permission - -
- - - testUser-1 - - test@test.com - -
- - Admin - -
-
- -
- - - testUser-2 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-3 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-4 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-5 - - test@test.com - -
- - Member - -
-
- -
-
-
-`; - -exports[`Render when feature toggle editorsCanAdmin is turned on should render span and disable buttons if user is team member 1`] = ` -
-
-
- -
-
- -
- -
- -
- Add team member -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - + member={ + Object { + "avatarUrl": "some/url/", + "email": "test@test.com", + "labels": Array [ + "label 1", + "label 2", + ], + "login": "testUser-5", + "permission": 4, + "teamId": 1, + "userId": 5, + } + } + signedInUserIsTeamAdmin={true} + syncEnabled={true} + />
- - Name - - Email - - Permission - -
- - - testUser-1 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-2 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-3 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-4 - - test@test.com - -
- - Member - -
-
- -
- - - testUser-5 - - test@test.com - -
- - Admin - -
-
- -
From e3fc61b326e8bdfd4f01cdbf25b6927454477e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 14 Mar 2019 08:02:24 +0100 Subject: [PATCH 075/101] refactor: moved test from TeamMembers to TeamMemberRow --- .../app/features/teams/TeamMemberRow.test.tsx | 8 + .../app/features/teams/TeamMembers.test.tsx | 6 - .../__snapshots__/TeamMemberRow.test.tsx.snap | 51 +++++ .../__snapshots__/TeamMembers.test.tsx.snap | 203 +----------------- 4 files changed, 64 insertions(+), 204 deletions(-) diff --git a/public/app/features/teams/TeamMemberRow.test.tsx b/public/app/features/teams/TeamMemberRow.test.tsx index 87f771cc833..0607825bff3 100644 --- a/public/app/features/teams/TeamMemberRow.test.tsx +++ b/public/app/features/teams/TeamMemberRow.test.tsx @@ -27,6 +27,14 @@ const setup = (propOverrides?: object) => { }; describe('Render', () => { + it('should render team members when sync enabled', () => { + const member = getMockTeamMember(); + member.labels = ['LDAP']; + const { wrapper } = setup({ member, syncEnabled: true }); + + expect(wrapper).toMatchSnapshot(); + }); + describe('when feature toggle editorsCanAdmin is turned on', () => { it('should render permissions select if user is team admin', () => { const { wrapper } = setup({ editorsCanAdmin: true, signedInUserIsTeamAdmin: true }); diff --git a/public/app/features/teams/TeamMembers.test.tsx b/public/app/features/teams/TeamMembers.test.tsx index fc65b0532e1..02bfc4149f0 100644 --- a/public/app/features/teams/TeamMembers.test.tsx +++ b/public/app/features/teams/TeamMembers.test.tsx @@ -46,12 +46,6 @@ describe('Render', () => { expect(wrapper).toMatchSnapshot(); }); - - it('should render team members when sync enabled', () => { - const { wrapper } = setup({ members: getMockTeamMembers(5, 5), syncEnabled: true }); - - expect(wrapper).toMatchSnapshot(); - }); }); describe('Functions', () => { diff --git a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap index 3e7630d0618..3dff08ddc1e 100644 --- a/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamMemberRow.test.tsx.snap @@ -1,5 +1,56 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Render should render team members when sync enabled 1`] = ` + + + + + + testUser + + + test@test.com + + + +
+ + Member + +
+ +
+ + + + + + + +`; + exports[`Render when feature toggle editorsCanAdmin is turned off should not render permissions 1`] = ` - - - - -
`; - -exports[`Render should render team members when sync enabled 1`] = ` -
-
-
- -
-
- -
- -
- -
- Add team member -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - -
- - Name - - Email - - Permission - - -
-
-
-`; From 8c34f595f0683d2277e5f907039689348de9e2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 14 Mar 2019 08:21:53 +0100 Subject: [PATCH 076/101] teams: disable new team button if user is viewer --- public/app/features/teams/TeamList.test.tsx | 44 ++- public/app/features/teams/TeamList.tsx | 13 +- .../__snapshots__/TeamList.test.tsx.snap | 250 ++++++++++++++++++ 3 files changed, 303 insertions(+), 4 deletions(-) diff --git a/public/app/features/teams/TeamList.test.tsx b/public/app/features/teams/TeamList.test.tsx index 369771bb340..da5afb58796 100644 --- a/public/app/features/teams/TeamList.test.tsx +++ b/public/app/features/teams/TeamList.test.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Props, TeamList } from './TeamList'; -import { NavModel, Team } from '../../types'; +import { NavModel, Team, OrgRole } from '../../types'; import { getMockTeam, getMultipleMockTeams } from './__mocks__/teamMocks'; +import { User } from 'app/core/services/context_srv'; const setup = (propOverrides?: object) => { const props: Props = { @@ -21,6 +22,11 @@ const setup = (propOverrides?: object) => { searchQuery: '', teamsCount: 0, hasFetched: false, + editorsCanAdmin: false, + signedInUser: { + id: 1, + orgRole: OrgRole.Viewer, + } as User, }; Object.assign(props, propOverrides); @@ -49,6 +55,42 @@ describe('Render', () => { expect(wrapper).toMatchSnapshot(); }); + + describe('when feature toggle editorsCanAdmin is turned on', () => { + describe('and signedin user is not viewer', () => { + it('should enable the new team button', () => { + const { wrapper } = setup({ + teams: getMultipleMockTeams(1), + teamsCount: 1, + hasFetched: true, + editorsCanAdmin: true, + signedInUser: { + id: 1, + orgRole: OrgRole.Editor, + } as User, + }); + + expect(wrapper).toMatchSnapshot(); + }); + }); + + describe('and signedin user is a viewer', () => { + it('should disable the new team button', () => { + const { wrapper } = setup({ + teams: getMultipleMockTeams(1), + teamsCount: 1, + hasFetched: true, + editorsCanAdmin: true, + signedInUser: { + id: 1, + orgRole: OrgRole.Viewer, + } as User, + }); + + expect(wrapper).toMatchSnapshot(); + }); + }); + }); }); describe('Life cycle', () => { diff --git a/public/app/features/teams/TeamList.tsx b/public/app/features/teams/TeamList.tsx index 60921a3378b..f603994b578 100644 --- a/public/app/features/teams/TeamList.tsx +++ b/public/app/features/teams/TeamList.tsx @@ -4,11 +4,13 @@ import { hot } from 'react-hot-loader'; import Page from 'app/core/components/Page/Page'; import { DeleteButton } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; -import { NavModel, Team } from 'app/types'; +import { NavModel, Team, OrgRole } from 'app/types'; import { loadTeams, deleteTeam, setSearchQuery } from './state/actions'; import { getSearchQuery, getTeams, getTeamsCount } from './state/selectors'; import { getNavModel } from 'app/core/selectors/navModel'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; +import { config } from 'app/core/config'; +import { contextSrv, User } from 'app/core/services/context_srv'; export interface Props { navModel: NavModel; @@ -19,6 +21,8 @@ export interface Props { loadTeams: typeof loadTeams; deleteTeam: typeof deleteTeam; setSearchQuery: typeof setSearchQuery; + editorsCanAdmin?: boolean; + signedInUser?: User; } export class TeamList extends PureComponent { @@ -84,7 +88,8 @@ export class TeamList extends PureComponent { } renderTeamList() { - const { teams, searchQuery } = this.props; + const { teams, searchQuery, editorsCanAdmin, signedInUser } = this.props; + const disabledClass = editorsCanAdmin && signedInUser.orgRole === OrgRole.Viewer ? ' disabled' : ''; return ( <> @@ -101,7 +106,7 @@ export class TeamList extends PureComponent { @@ -152,6 +157,8 @@ function mapStateToProps(state) { searchQuery: getSearchQuery(state.teams), teamsCount: getTeamsCount(state.teams), hasFetched: state.teams.hasFetched, + editorsCanAdmin: config.editorsCanAdmin, // this makes the feature toggle mockable/controllable from tests, + signedInUser: contextSrv.user, // this makes the feature toggle mockable/controllable from tests, }; } diff --git a/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap index 76d13e6a3d6..d4dd2170bae 100644 --- a/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap @@ -343,3 +343,253 @@ exports[`Render should render teams table 1`] = ` `; + +exports[`Render when feature toggle editorsCanAdmin is turned on and signedin user is a viewer should disable the new team button 1`] = ` + + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + +
+ + Name + + Email + + Members + +
+ + + + + + test-1 + + + + test-1@test.com + + + + 1 + + + +
+
+ + +`; + +exports[`Render when feature toggle editorsCanAdmin is turned on and signedin user is not viewer should enable the new team button 1`] = ` + + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + +
+ + Name + + Email + + Members + +
+ + + + + + test-1 + + + + test-1@test.com + + + + 1 + + + +
+
+ + +`; From d1481cac50fff0114c833c43be8905f5b505ee4f Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 09:08:48 +0100 Subject: [PATCH 077/101] teams: refactored db code. --- pkg/services/sqlstore/migrations/team_mig.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/services/sqlstore/migrations/team_mig.go b/pkg/services/sqlstore/migrations/team_mig.go index 1ec27ee926d..981a4865caa 100644 --- a/pkg/services/sqlstore/migrations/team_mig.go +++ b/pkg/services/sqlstore/migrations/team_mig.go @@ -56,8 +56,6 @@ func addTeamMigrations(mg *Migrator) { })) mg.AddMigration("Add column permission to team_member table", NewAddColumnMigration(teamMemberV1, &Column{ - Name: "permission", - Type: DB_BigInt, - Nullable: true, + Name: "permission", Type: DB_SmallInt, Nullable: true, })) } From 13ed10495a96927bb31e26fe72d7aec579c4ce66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 14 Mar 2019 09:49:35 +0100 Subject: [PATCH 078/101] teams: hide tabs settings and groupsync for non team admins --- .../app/features/teams/TeamMembers.test.tsx | 82 ---------------- public/app/features/teams/TeamMembers.tsx | 22 ++--- public/app/features/teams/TeamPages.test.tsx | 52 +++++++++- public/app/features/teams/TeamPages.tsx | 32 +++++-- .../__snapshots__/TeamPages.test.tsx.snap | 22 +++++ .../features/teams/state/selectors.test.ts | 96 ++++++++++++++++++- public/app/features/teams/state/selectors.ts | 18 +++- 7 files changed, 215 insertions(+), 109 deletions(-) diff --git a/public/app/features/teams/TeamMembers.test.tsx b/public/app/features/teams/TeamMembers.test.tsx index 02bfc4149f0..64e679f92ef 100644 --- a/public/app/features/teams/TeamMembers.test.tsx +++ b/public/app/features/teams/TeamMembers.test.tsx @@ -74,86 +74,4 @@ describe('Functions', () => { expect(instance.props.addTeamMember).toHaveBeenCalledWith(1); }); - - describe('isSignedInUserTeamAdmin', () => { - describe('when feature toggle editorsCanAdmin is turned off', () => { - it('should return true', () => { - const { instance } = setup({ editorsCanAdmin: false }); - - const result = instance.isSignedInUserTeamAdmin(); - - expect(result).toBe(true); - }); - }); - - describe('when feature toggle editorsCanAdmin is turned on', () => { - it('should return true if signed in user is grafanaAdmin', () => { - const members = getMockTeamMembers(5, 5); - const { instance } = setup({ - members, - editorsCanAdmin: true, - signedInUser: { - id: signedInUserId, - isGrafanaAdmin: true, - orgRole: OrgRole.Viewer, - }, - }); - - const result = instance.isSignedInUserTeamAdmin(); - - expect(result).toBe(true); - }); - - it('should return true if signed in user is org admin', () => { - const members = getMockTeamMembers(5, 5); - const { instance } = setup({ - members, - editorsCanAdmin: true, - signedInUser: { - id: signedInUserId, - isGrafanaAdmin: false, - orgRole: OrgRole.Admin, - }, - }); - - const result = instance.isSignedInUserTeamAdmin(); - - expect(result).toBe(true); - }); - - it('should return true if signed in user is team admin', () => { - const members = getMockTeamMembers(5, signedInUserId); - const { instance } = setup({ - members, - editorsCanAdmin: true, - signedInUser: { - id: signedInUserId, - isGrafanaAdmin: false, - orgRole: OrgRole.Viewer, - }, - }); - - const result = instance.isSignedInUserTeamAdmin(); - - expect(result).toBe(true); - }); - - it('should return false if signed in user is not grafanaAdmin, org admin or team admin', () => { - const members = getMockTeamMembers(5, 5); - const { instance } = setup({ - members, - editorsCanAdmin: true, - signedInUser: { - id: signedInUserId, - isGrafanaAdmin: false, - orgRole: OrgRole.Viewer, - }, - }); - - const result = instance.isSignedInUserTeamAdmin(); - - expect(result).toBe(false); - }); - }); - }); }); diff --git a/public/app/features/teams/TeamMembers.tsx b/public/app/features/teams/TeamMembers.tsx index db2b9024a1f..76fbdc87f1c 100644 --- a/public/app/features/teams/TeamMembers.tsx +++ b/public/app/features/teams/TeamMembers.tsx @@ -3,9 +3,9 @@ import { connect } from 'react-redux'; import SlideDown from 'app/core/components/Animations/SlideDown'; import { UserPicker } from 'app/core/components/Select/UserPicker'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; -import { TeamMember, User, TeamPermissionLevel, OrgRole } from 'app/types'; +import { TeamMember, User } from 'app/types'; import { loadTeamMembers, addTeamMember, setSearchMemberQuery } from './state/actions'; -import { getSearchMemberQuery, getTeamMembers } from './state/selectors'; +import { getSearchMemberQuery, getTeamMembers, isSignedInUserTeamAdmin } from './state/selectors'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { WithFeatureToggle } from 'app/core/components/WithFeatureToggle'; import { config } from 'app/core/config'; @@ -69,19 +69,11 @@ export class TeamMembers extends PureComponent { ); } - isSignedInUserTeamAdmin = (): boolean => { - const { members, editorsCanAdmin, signedInUser } = this.props; - const userInMembers = members.find(m => m.userId === signedInUser.id); - const isAdmin = signedInUser.isGrafanaAdmin || signedInUser.orgRole === OrgRole.Admin; - const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin; - const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin; - - return isSignedInUserTeamAdmin || !editorsCanAdmin; - }; - render() { const { isAdding } = this.state; - const { searchMemberQuery, members, syncEnabled, editorsCanAdmin } = this.props; + const { searchMemberQuery, members, syncEnabled, editorsCanAdmin, signedInUser } = this.props; + const isTeamAdmin = isSignedInUserTeamAdmin({ members, editorsCanAdmin, signedInUser }); + return (
@@ -100,7 +92,7 @@ export class TeamMembers extends PureComponent { @@ -145,7 +137,7 @@ export class TeamMembers extends PureComponent { member={member} syncEnabled={syncEnabled} editorsCanAdmin={editorsCanAdmin} - signedInUserIsTeamAdmin={this.isSignedInUserTeamAdmin()} + signedInUserIsTeamAdmin={isTeamAdmin} /> ))} diff --git a/public/app/features/teams/TeamPages.test.tsx b/public/app/features/teams/TeamPages.test.tsx index 5d751f46989..74e9dfa24f2 100644 --- a/public/app/features/teams/TeamPages.test.tsx +++ b/public/app/features/teams/TeamPages.test.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { shallow } from 'enzyme'; import { TeamPages, Props } from './TeamPages'; -import { NavModel, Team } from '../../types'; +import { NavModel, Team, TeamMember, OrgRole } from '../../types'; import { getMockTeam } from './__mocks__/teamMocks'; +import { User } from 'app/core/services/context_srv'; jest.mock('app/core/config', () => ({ buildInfo: { isEnterprise: true }, @@ -15,6 +16,13 @@ const setup = (propOverrides?: object) => { loadTeam: jest.fn(), pageName: 'members', team: {} as Team, + members: [] as TeamMember[], + editorsCanAdmin: false, + signedInUser: { + id: 1, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + } as User, }; Object.assign(props, propOverrides); @@ -65,4 +73,46 @@ describe('Render', () => { expect(wrapper).toMatchSnapshot(); }); + + describe('when feature toggle editorsCanAdmin is turned on', () => { + it('should render settings page if user is team admin', () => { + const { wrapper } = setup({ + team: getMockTeam(), + pageName: 'settings', + preferences: { + homeDashboardId: 1, + theme: 'Default', + timezone: 'Default', + }, + editorsCanAdmin: true, + signedInUser: { + id: 1, + isGrafanaAdmin: false, + orgRole: OrgRole.Admin, + } as User, + }); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should not render settings page if user is team member', () => { + const { wrapper } = setup({ + team: getMockTeam(), + pageName: 'settings', + preferences: { + homeDashboardId: 1, + theme: 'Default', + timezone: 'Default', + }, + editorsCanAdmin: true, + signedInUser: { + id: 1, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + } as User, + }); + + expect(wrapper).toMatchSnapshot(); + }); + }); }); diff --git a/public/app/features/teams/TeamPages.tsx b/public/app/features/teams/TeamPages.tsx index 15adc8b3856..a64fe61612d 100644 --- a/public/app/features/teams/TeamPages.tsx +++ b/public/app/features/teams/TeamPages.tsx @@ -7,12 +7,13 @@ import Page from 'app/core/components/Page/Page'; import TeamMembers from './TeamMembers'; import TeamSettings from './TeamSettings'; import TeamGroupSync from './TeamGroupSync'; -import { NavModel, Team } from 'app/types'; +import { NavModel, Team, TeamMember } from 'app/types'; import { loadTeam } from './state/actions'; -import { getTeam } from './state/selectors'; +import { getTeam, getTeamMembers, isSignedInUserTeamAdmin } from './state/selectors'; import { getTeamLoadingNav } from './state/navModel'; import { getNavModel } from 'app/core/selectors/navModel'; import { getRouteParamsId, getRouteParamsPage } from '../../core/selectors/location'; +import { contextSrv, User } from 'app/core/services/context_srv'; export interface Props { team: Team; @@ -20,6 +21,9 @@ export interface Props { teamId: number; pageName: string; navModel: NavModel; + members?: TeamMember[]; + editorsCanAdmin?: boolean; + signedInUser?: User; } interface State { @@ -61,7 +65,15 @@ export class TeamPages extends PureComponent { return _.includes(pages, currentPage) ? currentPage : pages[0]; } - renderPage() { + hideTabsFromNonTeamAdmin = (navModel: NavModel, isSignedInUserTeamAdmin: boolean) => { + if (!isSignedInUserTeamAdmin && navModel.main && navModel.main.children) { + navModel.main.children = navModel.main.children.filter(navItem => navItem.text === 'Members'); + } + + return navModel; + }; + + renderPage(isSignedInUserTeamAdmin: boolean) { const { isSyncEnabled } = this.state; const currentPage = this.getCurrentPage(); @@ -70,21 +82,22 @@ export class TeamPages extends PureComponent { return ; case PageTypes.Settings: - return ; + return isSignedInUserTeamAdmin && ; case PageTypes.GroupSync: - return isSyncEnabled && ; + return isSignedInUserTeamAdmin && isSyncEnabled && ; } return null; } render() { - const { team, navModel } = this.props; + const { team, navModel, members, editorsCanAdmin, signedInUser } = this.props; + const isTeamAdmin = isSignedInUserTeamAdmin({ members, editorsCanAdmin, signedInUser }); return ( - + - {team && Object.keys(team).length !== 0 && this.renderPage()} + {team && Object.keys(team).length !== 0 && this.renderPage(isTeamAdmin)} ); @@ -101,6 +114,9 @@ function mapStateToProps(state) { teamId: teamId, pageName: pageName, team: getTeam(state.team, teamId), + members: getTeamMembers(state.team), + editorsCanAdmin: config.editorsCanAdmin, // this makes the feature toggle mockable/controllable from tests, + signedInUser: contextSrv.user, // this makes the feature toggle mockable/controllable from tests, }; } diff --git a/public/app/features/teams/__snapshots__/TeamPages.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamPages.test.tsx.snap index 70f37cea4c5..6fdf7d063ba 100644 --- a/public/app/features/teams/__snapshots__/TeamPages.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamPages.test.tsx.snap @@ -47,3 +47,25 @@ exports[`Render should render settings and preferences page 1`] = ` `; + +exports[`Render when feature toggle editorsCanAdmin is turned on should not render settings page if user is team member 1`] = ` + + + +`; + +exports[`Render when feature toggle editorsCanAdmin is turned on should render settings page if user is team admin 1`] = ` + + + + + +`; diff --git a/public/app/features/teams/state/selectors.test.ts b/public/app/features/teams/state/selectors.test.ts index 1721437c929..5d9981e0403 100644 --- a/public/app/features/teams/state/selectors.test.ts +++ b/public/app/features/teams/state/selectors.test.ts @@ -1,6 +1,7 @@ -import { getTeam, getTeamMembers, getTeams } from './selectors'; +import { getTeam, getTeamMembers, getTeams, isSignedInUserTeamAdmin, Config } from './selectors'; import { getMockTeam, getMockTeamMembers, getMultipleMockTeams } from '../__mocks__/teamMocks'; -import { Team, TeamGroup, TeamsState, TeamState } from '../../../types'; +import { Team, TeamGroup, TeamsState, TeamState, OrgRole } from '../../../types'; +import { User } from 'app/core/services/context_srv'; describe('Teams selectors', () => { describe('Get teams', () => { @@ -55,3 +56,94 @@ describe('Team selectors', () => { }); }); }); + +const signedInUserId = 1; + +const setup = (configOverrides?: Partial) => { + const defaultConfig: Config = { + editorsCanAdmin: false, + members: getMockTeamMembers(5, 5), + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + } as User, + }; + + return { ...defaultConfig, ...configOverrides }; +}; + +describe('isSignedInUserTeamAdmin', () => { + describe('when feature toggle editorsCanAdmin is turned off', () => { + it('should return true', () => { + const config = setup(); + + const result = isSignedInUserTeamAdmin(config); + + expect(result).toBe(true); + }); + }); + + describe('when feature toggle editorsCanAdmin is turned on', () => { + it('should return true if signed in user is grafanaAdmin', () => { + const config = setup({ + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: true, + orgRole: OrgRole.Viewer, + } as User, + }); + + const result = isSignedInUserTeamAdmin(config); + + expect(result).toBe(true); + }); + + it('should return true if signed in user is org admin', () => { + const config = setup({ + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Admin, + } as User, + }); + + const result = isSignedInUserTeamAdmin(config); + + expect(result).toBe(true); + }); + + it('should return true if signed in user is team admin', () => { + const config = setup({ + members: getMockTeamMembers(5, signedInUserId), + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + } as User, + }); + + const result = isSignedInUserTeamAdmin(config); + + expect(result).toBe(true); + }); + + it('should return false if signed in user is not grafanaAdmin, org admin or team admin', () => { + const config = setup({ + editorsCanAdmin: true, + signedInUser: { + id: signedInUserId, + isGrafanaAdmin: false, + orgRole: OrgRole.Viewer, + } as User, + }); + + const result = isSignedInUserTeamAdmin(config); + + expect(result).toBe(false); + }); + }); +}); diff --git a/public/app/features/teams/state/selectors.ts b/public/app/features/teams/state/selectors.ts index 9201993bf0d..d8b8220bb44 100644 --- a/public/app/features/teams/state/selectors.ts +++ b/public/app/features/teams/state/selectors.ts @@ -1,4 +1,5 @@ -import { Team, TeamsState, TeamState } from 'app/types'; +import { Team, TeamsState, TeamState, TeamMember, OrgRole, TeamPermissionLevel } from 'app/types'; +import { User } from 'app/core/services/context_srv'; export const getSearchQuery = (state: TeamsState) => state.searchQuery; export const getSearchMemberQuery = (state: TeamState) => state.searchMemberQuery; @@ -28,3 +29,18 @@ export const getTeamMembers = (state: TeamState) => { return regex.test(member.login) || regex.test(member.email); }); }; + +export interface Config { + members: TeamMember[]; + editorsCanAdmin: boolean; + signedInUser: User; +} + +export const isSignedInUserTeamAdmin = (config: Config): boolean => { + const userInMembers = config.members.find(m => m.userId === config.signedInUser.id); + const isAdmin = config.signedInUser.isGrafanaAdmin || config.signedInUser.orgRole === OrgRole.Admin; + const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin; + const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin; + + return isSignedInUserTeamAdmin || !config.editorsCanAdmin; +}; From b796027bc6854d663f0789c34324d99146f3d390 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 09:14:35 +0100 Subject: [PATCH 079/101] teams: refactor. --- pkg/services/sqlstore/team.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index d76d8401499..e52983b5918 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -259,28 +259,21 @@ func AddTeamMember(cmd *m.AddTeamMemberCommand) error { func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { return inTransaction(func(sess *DBSession) error { rawSql := `SELECT * FROM team_member WHERE org_id=? and team_id=? and user_id=?` - var member m.TeamMember exists, err := sess.SQL(rawSql, cmd.OrgId, cmd.TeamId, cmd.UserId).Get(&member) if err != nil { return err } - if !exists { return m.ErrTeamMemberNotFound } if cmd.ProtectLastAdmin { - lastAdmin, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) + _, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) if err != nil { return err } - - if lastAdmin { - return m.ErrLastTeamAdmin - } - } if cmd.Permission != m.PERMISSION_ADMIN { // make sure we don't get invalid permission levels in store @@ -302,15 +295,10 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { } if cmd.ProtectLastAdmin { - lastAdmin, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) + _, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) if err != nil { return err } - - if lastAdmin { - return m.ErrLastTeamAdmin - } - } var rawSql = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?" @@ -344,7 +332,7 @@ func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool } if isAdmin && len(userIds) == 1 { - return true, nil + return true, m.ErrLastTeamAdmin } return false, err From 9f8e43916dbdc49c69b6a2bdcfe1232d827ce522 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 09:18:06 +0100 Subject: [PATCH 080/101] permissions: refactor. --- pkg/api/dashboard.go | 3 +-- pkg/api/folder.go | 3 +-- pkg/services/dashboards/acl_service.go | 14 +------------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 14a1e3baf32..e7fcf5d4355 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -278,9 +278,8 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) } if hs.Cfg.EditorsCanAdmin && newDashboard { - aclService := dashboards.NewAclService() inFolder := cmd.FolderId > 0 - err := aclService.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) + err := dashboards.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) if err != nil { hs.log.Error("Could not make user admin", "dashboard", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) return Error(500, "Failed to make user admin of dashboard", err) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 66c640f96e8..4b64fc1139f 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -62,8 +62,7 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R } if hs.Cfg.EditorsCanAdmin { - aclService := dashboards.NewAclService() - if err := aclService.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { + if err := dashboards.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) return Error(500, "Failed to make user admin of folder", err) } diff --git a/pkg/services/dashboards/acl_service.go b/pkg/services/dashboards/acl_service.go index dae3ec1372b..6158b190d68 100644 --- a/pkg/services/dashboards/acl_service.go +++ b/pkg/services/dashboards/acl_service.go @@ -2,23 +2,11 @@ package dashboards import ( "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/models" "time" ) -// NewService factory for creating a new dashboard service -var NewAclService = func() *AclService { - return &AclService{ - log: log.New("dashboard-acl-service"), - } -} - -type AclService struct { - log log.Logger -} - -func (as *AclService) MakeUserAdmin(orgId int64, userId int64, dashboardId int64, setViewAndEditPermissions bool) error { +func MakeUserAdmin(orgId int64, userId int64, dashboardId int64, setViewAndEditPermissions bool) error { rtEditor := models.ROLE_EDITOR rtViewer := models.ROLE_VIEWER From 9f33f0034342790bce1d289a3d4e9a3965d26587 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 09:37:56 +0100 Subject: [PATCH 081/101] teams: refactor. --- pkg/services/sqlstore/team.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index e52983b5918..b561f2e00f6 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -255,19 +255,28 @@ func AddTeamMember(cmd *m.AddTeamMemberCommand) error { }) } +func getTeamMember(sess *DBSession, orgId int64, teamId int64, userId int64) (m.TeamMember, error) { + rawSql := `SELECT * FROM team_member WHERE org_id=? and team_id=? and user_id=?` + var member m.TeamMember + exists, err := sess.SQL(rawSql, orgId, teamId, userId).Get(&member) + + if err != nil { + return member, err + } + if !exists { + return member, m.ErrTeamMemberNotFound + } + + return member, nil +} + // UpdateTeamMember updates a team member func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { return inTransaction(func(sess *DBSession) error { - rawSql := `SELECT * FROM team_member WHERE org_id=? and team_id=? and user_id=?` - var member m.TeamMember - exists, err := sess.SQL(rawSql, cmd.OrgId, cmd.TeamId, cmd.UserId).Get(&member) - + member, err := getTeamMember(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) if err != nil { return err } - if !exists { - return m.ErrTeamMemberNotFound - } if cmd.ProtectLastAdmin { _, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) From 6589a4e55f9ed120ec8d36f461cc7ea07a561074 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 10:02:46 +0100 Subject: [PATCH 082/101] teams: better names for api permissions. --- pkg/api/api.go | 8 ++++---- pkg/middleware/auth.go | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 9d1151a757e..32213e3a58a 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -14,7 +14,7 @@ func (hs *HTTPServer) registerRoutes() { reqGrafanaAdmin := middleware.ReqGrafanaAdmin reqEditorRole := middleware.ReqEditorRole reqOrgAdmin := middleware.ReqOrgAdmin - reqAdminOrCanAdmin := middleware.AdminOrCanAdmin(hs.Cfg.EditorsCanAdmin) + reqCanAccessTeams := middleware.AdminOrFeatureEnabled(hs.Cfg.EditorsCanAdmin) redirectFromLegacyDashboardURL := middleware.RedirectFromLegacyDashboardURL() redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL() quota := middleware.Quota(hs.QuotaService) @@ -42,8 +42,8 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/org/users", reqOrgAdmin, hs.Index) r.Get("/org/users/new", reqOrgAdmin, hs.Index) r.Get("/org/users/invite", reqOrgAdmin, hs.Index) - r.Get("/org/teams", reqAdminOrCanAdmin, hs.Index) - r.Get("/org/teams/*", reqAdminOrCanAdmin, hs.Index) + r.Get("/org/teams", reqCanAccessTeams, hs.Index) + r.Get("/org/teams/*", reqCanAccessTeams, hs.Index) r.Get("/org/apikeys/", reqOrgAdmin, hs.Index) r.Get("/dashboard/import/", reqSignedIn, hs.Index) r.Get("/configuration", reqGrafanaAdmin, hs.Index) @@ -163,7 +163,7 @@ func (hs *HTTPServer) registerRoutes() { teamsRoute.Delete("/:teamId/members/:userId", Wrap(hs.RemoveTeamMember)) teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences)) teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences)) - }, reqAdminOrCanAdmin) + }, reqCanAccessTeams) // team without requirement of user to be org admin apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 8c1e5e04ae7..c00241ea34c 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -87,7 +87,12 @@ func Auth(options *AuthOptions) macaron.Handler { } } -func AdminOrCanAdmin(enabled bool) macaron.Handler { +// AdminOrFeatureEnabled creates a middleware that allows access +// if the signed in user is either an Org Admin or if the +// feature flag is enabled. +// Intended for when feature flags open up access to APIs that +// are otherwise only available to admins. +func AdminOrFeatureEnabled(enabled bool) macaron.Handler { return func(c *m.ReqContext) { if c.OrgRole == m.ROLE_ADMIN { return From adf0390b2c8f1beaaf0d904b884f59ff160310ee Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 12:10:10 +0100 Subject: [PATCH 083/101] teams: local access to bus, moving away from dep on global. --- pkg/api/api.go | 12 ++++++------ pkg/api/team.go | 20 ++++++++++---------- pkg/api/team_members.go | 16 ++++++++-------- pkg/services/teamguardian/team.go | 2 +- pkg/services/teamguardian/teams_test.go | 8 ++++---- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 32213e3a58a..86bc83f558b 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -155,14 +155,14 @@ func (hs *HTTPServer) registerRoutes() { // team (admin permission required) apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { teamsRoute.Post("/", bind(m.CreateTeamCommand{}), Wrap(hs.CreateTeam)) - teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(UpdateTeam)) - teamsRoute.Delete("/:teamId", Wrap(DeleteTeamByID)) + teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(hs.UpdateTeam)) + teamsRoute.Delete("/:teamId", Wrap(hs.DeleteTeamByID)) teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) - teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember)) - teamsRoute.Put("/:teamId/members/:userId", bind(m.UpdateTeamMemberCommand{}), Wrap(UpdateTeamMember)) + teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(hs.AddTeamMember)) + teamsRoute.Put("/:teamId/members/:userId", bind(m.UpdateTeamMemberCommand{}), Wrap(hs.UpdateTeamMember)) teamsRoute.Delete("/:teamId/members/:userId", Wrap(hs.RemoveTeamMember)) - teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences)) - teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences)) + teamsRoute.Get("/:teamId/preferences", Wrap(hs.GetTeamPreferences)) + teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(hs.UpdateTeamPreferences)) }, reqCanAccessTeams) // team without requirement of user to be org admin diff --git a/pkg/api/team.go b/pkg/api/team.go index fd34c0ab720..cb0ff2f25a0 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -43,15 +43,15 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo } // PUT /api/teams/:teamId -func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { +func (hs *HTTPServer) UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { cmd.OrgId = c.OrgId cmd.Id = c.ParamsInt64(":teamId") - if err := teamguardian.CanAdmin(cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, cmd.OrgId, cmd.Id, c.SignedInUser); err != nil { return Error(403, "Not allowed to update team", err) } - if err := bus.Dispatch(&cmd); err != nil { + if err := hs.Bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNameTaken { return Error(400, "Team name taken", err) } @@ -62,16 +62,16 @@ func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response { } // DELETE /api/teams/:teamId -func DeleteTeamByID(c *m.ReqContext) Response { +func (hs *HTTPServer) DeleteTeamByID(c *m.ReqContext) Response { orgId := c.OrgId teamId := c.ParamsInt64(":teamId") user := c.SignedInUser - if err := teamguardian.CanAdmin(orgId, teamId, user); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, user); err != nil { return Error(403, "Not allowed to delete team", err) } - if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: orgId, Id: teamId}); err != nil { + if err := hs.Bus.Dispatch(&m.DeleteTeamCommand{OrgId: orgId, Id: teamId}); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Failed to delete Team. ID not found", nil) } @@ -136,11 +136,11 @@ func GetTeamByID(c *m.ReqContext) Response { } // GET /api/teams/:teamId/preferences -func GetTeamPreferences(c *m.ReqContext) Response { +func (hs *HTTPServer) GetTeamPreferences(c *m.ReqContext) Response { teamId := c.ParamsInt64(":teamId") orgId := c.OrgId - if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to view team preferences.", err) } @@ -148,11 +148,11 @@ func GetTeamPreferences(c *m.ReqContext) Response { } // PUT /api/teams/:teamId/preferences -func UpdateTeamPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response { +func (hs *HTTPServer) UpdateTeamPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response { teamId := c.ParamsInt64(":teamId") orgId := c.OrgId - if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to update team preferences.", err) } diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index 1674cc120ce..54a4d8220e5 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -30,15 +30,15 @@ func GetTeamMembers(c *m.ReqContext) Response { } // POST /api/teams/:teamId/members -func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { +func (hs *HTTPServer) AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { cmd.OrgId = c.OrgId cmd.TeamId = c.ParamsInt64(":teamId") - if err := teamguardian.CanAdmin(cmd.OrgId, cmd.TeamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, cmd.OrgId, cmd.TeamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to add team member", err) } - if err := bus.Dispatch(&cmd); err != nil { + if err := hs.Bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Team not found", nil) } @@ -56,11 +56,11 @@ func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response { } // PUT /:teamId/members/:userId -func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { +func (hs *HTTPServer) UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { teamId := c.ParamsInt64(":teamId") orgId := c.OrgId - if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to update team member", err) } @@ -72,7 +72,7 @@ func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response { cmd.UserId = c.ParamsInt64(":userId") cmd.OrgId = orgId - if err := bus.Dispatch(&cmd); err != nil { + if err := hs.Bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamMemberNotFound { return Error(404, "Team member not found.", nil) } @@ -87,7 +87,7 @@ func (hs *HTTPServer) RemoveTeamMember(c *m.ReqContext) Response { teamId := c.ParamsInt64(":teamId") userId := c.ParamsInt64(":userId") - if err := teamguardian.CanAdmin(orgId, teamId, c.SignedInUser); err != nil { + if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil { return Error(403, "Not allowed to remove team member", err) } @@ -96,7 +96,7 @@ func (hs *HTTPServer) RemoveTeamMember(c *m.ReqContext) Response { protectLastAdmin = true } - if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId, ProtectLastAdmin: protectLastAdmin}); err != nil { + if err := hs.Bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId, ProtectLastAdmin: protectLastAdmin}); err != nil { if err == m.ErrTeamNotFound { return Error(404, "Team not found", nil) } diff --git a/pkg/services/teamguardian/team.go b/pkg/services/teamguardian/team.go index 6fddc318f5e..70053d12da1 100644 --- a/pkg/services/teamguardian/team.go +++ b/pkg/services/teamguardian/team.go @@ -5,7 +5,7 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func CanAdmin(orgId int64, teamId int64, user *m.SignedInUser) error { +func CanAdmin(bus bus.Bus, orgId int64, teamId int64, user *m.SignedInUser) error { if user.OrgRole == m.ROLE_ADMIN { return nil } diff --git a/pkg/services/teamguardian/teams_test.go b/pkg/services/teamguardian/teams_test.go index 2ec86769a29..8af69569620 100644 --- a/pkg/services/teamguardian/teams_test.go +++ b/pkg/services/teamguardian/teams_test.go @@ -33,7 +33,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanAdmin(testTeam.OrgId, testTeam.Id, &editor) + err := CanAdmin(bus.GetBus(), testTeam.OrgId, testTeam.Id, &editor) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeam) }) }) @@ -50,7 +50,7 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanAdmin(testTeam.OrgId, testTeam.Id, &editor) + err := CanAdmin(bus.GetBus(), testTeam.OrgId, testTeam.Id, &editor) So(err, ShouldBeNil) }) }) @@ -72,14 +72,14 @@ func TestUpdateTeam(t *testing.T) { return nil }) - err := CanAdmin(testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor) + err := CanAdmin(bus.GetBus(), testTeamOtherOrg.OrgId, testTeamOtherOrg.Id, &editor) So(err, ShouldEqual, m.ErrNotAllowedToUpdateTeamInDifferentOrg) }) }) Convey("Given an org admin and a team", func() { Convey("Should be able to update the team", func() { - err := CanAdmin(testTeam.OrgId, testTeam.Id, &admin) + err := CanAdmin(bus.GetBus(), testTeam.OrgId, testTeam.Id, &admin) So(err, ShouldBeNil) }) }) From a615b78f8a17b193e802628f58a6ff92f6fc63b1 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Thu, 14 Mar 2019 12:18:07 +0100 Subject: [PATCH 084/101] permissions: removes global access to bus from MakeUserAdmin. --- pkg/api/dashboard.go | 2 +- pkg/api/folder.go | 2 +- pkg/api/team.go | 4 ++-- pkg/services/dashboards/acl_service.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index e7fcf5d4355..c47e8f31ccc 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -279,7 +279,7 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) if hs.Cfg.EditorsCanAdmin && newDashboard { inFolder := cmd.FolderId > 0 - err := dashboards.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) + err := dashboards.MakeUserAdmin(hs.Bus, cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) if err != nil { hs.log.Error("Could not make user admin", "dashboard", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) return Error(500, "Failed to make user admin of dashboard", err) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 4b64fc1139f..0a9a2671071 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -62,7 +62,7 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R } if hs.Cfg.EditorsCanAdmin { - if err := dashboards.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { + if err := dashboards.MakeUserAdmin(hs.Bus, c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) return Error(500, "Failed to make user admin of folder", err) } diff --git a/pkg/api/team.go b/pkg/api/team.go index cb0ff2f25a0..ecfd8028c1b 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -16,7 +16,7 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo return Error(403, "Not allowed to create team.", nil) } - if err := bus.Dispatch(&cmd); err != nil { + if err := hs.Bus.Dispatch(&cmd); err != nil { if err == m.ErrTeamNameTaken { return Error(409, "Team name taken", err) } @@ -31,7 +31,7 @@ func (hs *HTTPServer) CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Respo Permission: m.PERMISSION_ADMIN, } - if err := bus.Dispatch(&addMemberCmd); err != nil { + if err := hs.Bus.Dispatch(&addMemberCmd); err != nil { c.Logger.Error("Could not add creator to team.", "error", err) } } diff --git a/pkg/services/dashboards/acl_service.go b/pkg/services/dashboards/acl_service.go index 6158b190d68..864fbb80a6b 100644 --- a/pkg/services/dashboards/acl_service.go +++ b/pkg/services/dashboards/acl_service.go @@ -6,7 +6,7 @@ import ( "time" ) -func MakeUserAdmin(orgId int64, userId int64, dashboardId int64, setViewAndEditPermissions bool) error { +func MakeUserAdmin(bus bus.Bus, orgId int64, userId int64, dashboardId int64, setViewAndEditPermissions bool) error { rtEditor := models.ROLE_EDITOR rtViewer := models.ROLE_VIEWER From 53c74fa2f56a4b1b029be8a71a3ec882d88b86e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 14 Mar 2019 14:24:13 +0100 Subject: [PATCH 085/101] teams: refactor so that you can only delete teams if you are team admin --- pkg/models/team.go | 13 ++++++----- pkg/services/sqlstore/team.go | 19 +++++++++++++--- public/app/features/teams/TeamList.tsx | 7 ++++-- .../app/features/teams/__mocks__/teamMocks.ts | 2 ++ .../__snapshots__/TeamList.test.tsx.snap | 7 ++++++ public/app/features/teams/state/navModel.ts | 3 ++- public/app/features/teams/state/selectors.ts | 22 +++++++++++++++---- public/app/types/teams.ts | 3 +++ 8 files changed, 60 insertions(+), 16 deletions(-) diff --git a/pkg/models/team.go b/pkg/models/team.go index 5b659331601..bc8cbba8100 100644 --- a/pkg/models/team.go +++ b/pkg/models/team.go @@ -73,12 +73,13 @@ type SearchTeamsQuery struct { } type TeamDTO struct { - Id int64 `json:"id"` - OrgId int64 `json:"orgId"` - Name string `json:"name"` - Email string `json:"email"` - AvatarUrl string `json:"avatarUrl"` - MemberCount int64 `json:"memberCount"` + Id int64 `json:"id"` + OrgId int64 `json:"orgId"` + Name string `json:"name"` + Email string `json:"email"` + AvatarUrl string `json:"avatarUrl"` + MemberCount int64 `json:"memberCount"` + Permission PermissionType `json:"permission"` } type SearchTeamQueryResult struct { diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index b561f2e00f6..03fd2df78fc 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -23,13 +23,25 @@ func init() { bus.AddHandler("sql", GetTeamMembers) } +func getTeamSearchSqlBase() string { + return `SELECT + team.id as id, + team.org_id, + team.name as name, + team.email as email, + (SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count, + team_member.permission + FROM team as team + INNER JOIN team_member on team.id = team_member.team_id AND team_member.user_id = ? ` +} + func getTeamSelectSqlBase() string { return `SELECT team.id as id, team.org_id, team.name as name, team.email as email, - (SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count + (SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count FROM team as team ` } @@ -146,10 +158,11 @@ func SearchTeams(query *m.SearchTeamsQuery) error { var sql bytes.Buffer params := make([]interface{}, 0) - sql.WriteString(getTeamSelectSqlBase()) if query.UserIdFilter > 0 { - sql.WriteString(`INNER JOIN team_member on team.id = team_member.team_id AND team_member.user_id = ?`) + sql.WriteString(getTeamSearchSqlBase()) params = append(params, query.UserIdFilter) + } else { + sql.WriteString(getTeamSelectSqlBase()) } sql.WriteString(` WHERE team.org_id = ?`) diff --git a/public/app/features/teams/TeamList.tsx b/public/app/features/teams/TeamList.tsx index f603994b578..e9d51785d72 100644 --- a/public/app/features/teams/TeamList.tsx +++ b/public/app/features/teams/TeamList.tsx @@ -6,7 +6,7 @@ import { DeleteButton } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { NavModel, Team, OrgRole } from 'app/types'; import { loadTeams, deleteTeam, setSearchQuery } from './state/actions'; -import { getSearchQuery, getTeams, getTeamsCount } from './state/selectors'; +import { getSearchQuery, getTeams, getTeamsCount, isPermissionTeamAdmin } from './state/selectors'; import { getNavModel } from 'app/core/selectors/navModel'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { config } from 'app/core/config'; @@ -43,7 +43,10 @@ export class TeamList extends PureComponent { }; renderTeam(team: Team) { + const { editorsCanAdmin, signedInUser } = this.props; + const permission = team.permission; const teamUrl = `org/teams/edit/${team.id}`; + const canDelete = isPermissionTeamAdmin({ permission, editorsCanAdmin, signedInUser }); return ( @@ -62,7 +65,7 @@ export class TeamList extends PureComponent { {team.memberCount} - this.deleteTeam(team)} /> + this.deleteTeam(team)} disabled={!canDelete} /> ); diff --git a/public/app/features/teams/__mocks__/teamMocks.ts b/public/app/features/teams/__mocks__/teamMocks.ts index f38f8f2b144..abaa5ef555f 100644 --- a/public/app/features/teams/__mocks__/teamMocks.ts +++ b/public/app/features/teams/__mocks__/teamMocks.ts @@ -9,6 +9,7 @@ export const getMultipleMockTeams = (numberOfTeams: number): Team[] => { avatarUrl: 'some/url/', email: `test-${i}@test.com`, memberCount: i, + permission: TeamPermissionLevel.Member, }); } @@ -22,6 +23,7 @@ export const getMockTeam = (): Team => { avatarUrl: 'some/url/', email: 'test@test.com', memberCount: 1, + permission: TeamPermissionLevel.Member, }; }; diff --git a/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap index d4dd2170bae..430466559c5 100644 --- a/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamList.test.tsx.snap @@ -133,6 +133,7 @@ exports[`Render should render teams table 1`] = ` className="text-right" > @@ -183,6 +184,7 @@ exports[`Render should render teams table 1`] = ` className="text-right" > @@ -233,6 +235,7 @@ exports[`Render should render teams table 1`] = ` className="text-right" > @@ -283,6 +286,7 @@ exports[`Render should render teams table 1`] = ` className="text-right" > @@ -333,6 +337,7 @@ exports[`Render should render teams table 1`] = ` className="text-right" > @@ -458,6 +463,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on and signedin us className="text-right" > @@ -583,6 +589,7 @@ exports[`Render when feature toggle editorsCanAdmin is turned on and signedin us className="text-right" > diff --git a/public/app/features/teams/state/navModel.ts b/public/app/features/teams/state/navModel.ts index 2fd5a68e680..aeb6b85f91e 100644 --- a/public/app/features/teams/state/navModel.ts +++ b/public/app/features/teams/state/navModel.ts @@ -1,4 +1,4 @@ -import { Team, NavModelItem, NavModel } from 'app/types'; +import { Team, NavModelItem, NavModel, TeamPermissionLevel } from 'app/types'; import config from 'app/core/config'; export function buildNavModel(team: Team): NavModelItem { @@ -47,6 +47,7 @@ export function getTeamLoadingNav(pageName: string): NavModel { name: 'Loading', email: 'loading', memberCount: 0, + permission: TeamPermissionLevel.Member, }); let node: NavModelItem; diff --git a/public/app/features/teams/state/selectors.ts b/public/app/features/teams/state/selectors.ts index d8b8220bb44..e770abfc093 100644 --- a/public/app/features/teams/state/selectors.ts +++ b/public/app/features/teams/state/selectors.ts @@ -37,10 +37,24 @@ export interface Config { } export const isSignedInUserTeamAdmin = (config: Config): boolean => { - const userInMembers = config.members.find(m => m.userId === config.signedInUser.id); - const isAdmin = config.signedInUser.isGrafanaAdmin || config.signedInUser.orgRole === OrgRole.Admin; - const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin; + const { members, signedInUser, editorsCanAdmin } = config; + const userInMembers = members.find(m => m.userId === signedInUser.id); + const permission = userInMembers ? userInMembers.permission : TeamPermissionLevel.Member; + + return isPermissionTeamAdmin({ permission, signedInUser, editorsCanAdmin }); +}; + +export interface PermissionConfig { + permission: TeamPermissionLevel; + editorsCanAdmin: boolean; + signedInUser: User; +} + +export const isPermissionTeamAdmin = (config: PermissionConfig): boolean => { + const { permission, signedInUser, editorsCanAdmin } = config; + const isAdmin = signedInUser.isGrafanaAdmin || signedInUser.orgRole === OrgRole.Admin; + const userIsTeamAdmin = permission === TeamPermissionLevel.Admin; const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin; - return isSignedInUserTeamAdmin || !config.editorsCanAdmin; + return isSignedInUserTeamAdmin || !editorsCanAdmin; }; diff --git a/public/app/types/teams.ts b/public/app/types/teams.ts index ef804e437d4..707ff97b738 100644 --- a/public/app/types/teams.ts +++ b/public/app/types/teams.ts @@ -1,9 +1,12 @@ +import { TeamPermissionLevel } from './acl'; + export interface Team { id: number; name: string; avatarUrl: string; email: string; memberCount: number; + permission: TeamPermissionLevel; } export interface TeamMember { From b71c9803a9e89c0c9f5b2e55935108ba5949b3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Mon, 18 Mar 2019 13:24:47 +0100 Subject: [PATCH 086/101] fix: new team link goes nowhere for viewers --- public/app/features/teams/TeamList.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/app/features/teams/TeamList.tsx b/public/app/features/teams/TeamList.tsx index e9d51785d72..5d3ef005c9e 100644 --- a/public/app/features/teams/TeamList.tsx +++ b/public/app/features/teams/TeamList.tsx @@ -92,7 +92,9 @@ export class TeamList extends PureComponent { renderTeamList() { const { teams, searchQuery, editorsCanAdmin, signedInUser } = this.props; - const disabledClass = editorsCanAdmin && signedInUser.orgRole === OrgRole.Viewer ? ' disabled' : ''; + const isCanAdminAndViewer = editorsCanAdmin && signedInUser.orgRole === OrgRole.Viewer; + const disabledClass = isCanAdminAndViewer ? ' disabled' : ''; + const newTeamHref = isCanAdminAndViewer ? '#' : 'org/teams/new'; return ( <> @@ -109,7 +111,7 @@ export class TeamList extends PureComponent { From e23e6a8bdc1c5c4addbdf4bf76334fcb17b901a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Mon, 18 Mar 2019 14:09:15 +0100 Subject: [PATCH 087/101] fix: fixed snapshots and permission select not beeing able to click --- public/app/features/teams/TeamMemberRow.tsx | 2 +- .../teams/__snapshots__/TeamList.test.tsx.snap | 2 +- .../__snapshots__/TeamMemberRow.test.tsx.snap | 16 ++++++++++++---- public/sass/pages/_admin.scss | 6 ++++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/public/app/features/teams/TeamMemberRow.tsx b/public/app/features/teams/TeamMemberRow.tsx index e0bd26f4fd7..0111d1efd8e 100644 --- a/public/app/features/teams/TeamMemberRow.tsx +++ b/public/app/features/teams/TeamMemberRow.tsx @@ -40,7 +40,7 @@ export class TeamMemberRow extends PureComponent { return ( - +
{signedInUserIsTeamAdmin && ( + ng-options="f for f in ['custom', 'critical', 'warning', 'ok']" ng-change="ctrl.onThresholdTypeChange($index)" ng-disabled="ctrl.disabled">
@@ -73,4 +73,4 @@
-
\ No newline at end of file +
diff --git a/public/app/plugins/panel/graph/thresholds_form.ts b/public/app/plugins/panel/graph/thresholds_form.ts index 4f480873d5b..c67a030188d 100644 --- a/public/app/plugins/panel/graph/thresholds_form.ts +++ b/public/app/plugins/panel/graph/thresholds_form.ts @@ -1,5 +1,6 @@ import coreModule from 'app/core/core_module'; - +import config from 'app/core/config'; +import tinycolor from 'tinycolor2'; export class ThresholdFormCtrl { panelCtrl: any; panel: any; @@ -30,6 +31,8 @@ export class ThresholdFormCtrl { fill: true, line: true, yaxis: 'left', + fillColor: 'rgba(234,112, 112, 0.12)', + lineColor: 'rgba(237, 46, 24, 0.60)', }); this.panelCtrl.render(); } @@ -56,6 +59,19 @@ export class ThresholdFormCtrl { this.render(); }; } + + onThresholdTypeChange(index) { + // Because of the ng-model binding, threshold's color mode is already set here + if (this.panel.thresholds[index].colorMode === 'custom') { + this.panel.thresholds[index].fillColor = tinycolor(config.theme.colors.blueBase) + .setAlpha(0.2) + .toRgbString(); + this.panel.thresholds[index].lineColor = tinycolor(config.theme.colors.blueShade) + .setAlpha(0.6) + .toRgbString(); + } + this.panelCtrl.render(); + } } coreModule.directive('graphThresholdForm', () => { From c1d585b156c25a76919122400d5f4c2cab9b77f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 19 Mar 2019 14:48:35 +0100 Subject: [PATCH 093/101] chore: cleaning up noimplicit anys in search_srv and tests progress: #14714 --- .../manage_dashboards/manage_dashboards.ts | 3 +- public/app/core/services/backend_srv.ts | 24 +++++++++++++- public/app/core/services/search_srv.ts | 32 ++++++++++++------- .../app/core/specs/manage_dashboards.test.ts | 1 + public/app/core/specs/search_srv.test.ts | 30 +++++++++-------- 5 files changed, 64 insertions(+), 26 deletions(-) diff --git a/public/app/core/components/manage_dashboards/manage_dashboards.ts b/public/app/core/components/manage_dashboards/manage_dashboards.ts index 3f6dacd311d..5b5e299b1af 100644 --- a/public/app/core/components/manage_dashboards/manage_dashboards.ts +++ b/public/app/core/components/manage_dashboards/manage_dashboards.ts @@ -11,7 +11,8 @@ export interface Section { id: number; uid: string; title: string; - expanded: false; + expanded: boolean; + removable: boolean; items: any[]; url: string; icon: string; diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index c73cc7661f5..53ab3ab6ce7 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -4,6 +4,28 @@ import appEvents from 'app/core/app_events'; import config from 'app/core/config'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; +export enum HitType { + DashHitDB = 'dash-db', + DashHitHome = 'dash-home', + DashHitFolder = 'dash-folder', +} + +export interface Hit { + id: number; + uid: string; + title: string; + uri: string; + url: string; + slug: string; + type: HitType; + tags: string[]; + isStarred: boolean; + folderId: number; + folderUid: string; + folderTitle: string; + folderUrl: string; +} + export class BackendSrv { private inFlightRequests = {}; private HTTP_REQUEST_CANCELED = -1; @@ -237,7 +259,7 @@ export class BackendSrv { return this.request({ url: '/api/login/ping', method: 'GET', retry: 1 }); } - search(query) { + search(query): Promise { return this.get('/api/search', query); } diff --git a/public/app/core/services/search_srv.ts b/public/app/core/services/search_srv.ts index 22d33921ebd..4a605d3fc50 100644 --- a/public/app/core/services/search_srv.ts +++ b/public/app/core/services/search_srv.ts @@ -1,21 +1,31 @@ +// @ts-ignore import _ from 'lodash'; +// @ts-ignore +import { IQService } from 'angular'; + import coreModule from 'app/core/core_module'; import impressionSrv from 'app/core/services/impression_srv'; import store from 'app/core/store'; import { contextSrv } from 'app/core/services/context_srv'; +import { BackendSrv, Hit } from './backend_srv'; +import { Section } from '../components/manage_dashboards/manage_dashboards'; + +interface Sections { + [key: string]: Partial
; +} export class SearchSrv { recentIsOpen: boolean; starredIsOpen: boolean; /** @ngInject */ - constructor(private backendSrv, private $q) { + constructor(private backendSrv: BackendSrv, private $q: IQService) { this.recentIsOpen = store.getBool('search.sections.recent', true); this.starredIsOpen = store.getBool('search.sections.starred', true); } - private getRecentDashboards(sections) { - return this.queryForRecentDashboards().then(result => { + private getRecentDashboards(sections: Sections) { + return this.queryForRecentDashboards().then((result: any[]) => { if (result.length > 0) { sections['recent'] = { title: 'Recent', @@ -30,8 +40,8 @@ export class SearchSrv { }); } - private queryForRecentDashboards() { - const dashIds = _.take(impressionSrv.getDashboardOpened(), 30); + private queryForRecentDashboards(): Promise { + const dashIds: number[] = _.take(impressionSrv.getDashboardOpened(), 30); if (dashIds.length === 0) { return Promise.resolve([]); } @@ -45,7 +55,7 @@ export class SearchSrv { }); } - private toggleRecent(section) { + private toggleRecent(section: Section) { this.recentIsOpen = section.expanded = !section.expanded; store.set('search.sections.recent', this.recentIsOpen); @@ -59,13 +69,13 @@ export class SearchSrv { }); } - private toggleStarred(section) { + private toggleStarred(section: Section) { this.starredIsOpen = section.expanded = !section.expanded; store.set('search.sections.starred', this.starredIsOpen); return Promise.resolve(section); } - private getStarred(sections) { + private getStarred(sections: Sections) { if (!contextSrv.isSignedIn) { return Promise.resolve(); } @@ -84,7 +94,7 @@ export class SearchSrv { }); } - search(options) { + search(options: any) { const sections: any = {}; const promises = []; const query = _.clone(options); @@ -118,7 +128,7 @@ export class SearchSrv { }); } - private handleSearchResult(sections, results) { + private handleSearchResult(sections: Sections, results: Hit[]): any { if (results.length === 0) { return sections; } @@ -177,7 +187,7 @@ export class SearchSrv { } } - private toggleFolder(section) { + private toggleFolder(section: Section) { section.expanded = !section.expanded; section.icon = section.expanded ? 'fa fa-folder-open' : 'fa fa-folder'; diff --git a/public/app/core/specs/manage_dashboards.test.ts b/public/app/core/specs/manage_dashboards.test.ts index ef5e240fd36..5e94d26ea89 100644 --- a/public/app/core/specs/manage_dashboards.test.ts +++ b/public/app/core/specs/manage_dashboards.test.ts @@ -16,6 +16,7 @@ const mockSection = (overides?: object): Section => { items: [], checked: false, expanded: false, + removable: false, hideHeader: false, icon: '', score: 0, diff --git a/public/app/core/specs/search_srv.test.ts b/public/app/core/specs/search_srv.test.ts index 550d11bbf9e..6566224c613 100644 --- a/public/app/core/specs/search_srv.test.ts +++ b/public/app/core/specs/search_srv.test.ts @@ -1,8 +1,12 @@ +// @ts-ignore +import { IQService } from 'angular'; + import { SearchSrv } from 'app/core/services/search_srv'; import { BackendSrvMock } from 'test/mocks/backend_srv'; import impressionSrv from 'app/core/services/impression_srv'; import { contextSrv } from 'app/core/services/context_srv'; import { beforeEach } from 'test/lib/common'; +import { BackendSrv } from '../services/backend_srv'; jest.mock('app/core/store', () => { return { @@ -18,18 +22,18 @@ jest.mock('app/core/services/impression_srv', () => { }); describe('SearchSrv', () => { - let searchSrv, backendSrvMock; + let searchSrv: SearchSrv, backendSrvMock: BackendSrvMock; beforeEach(() => { backendSrvMock = new BackendSrvMock(); - searchSrv = new SearchSrv(backendSrvMock, Promise); + searchSrv = new SearchSrv(backendSrvMock as BackendSrv, (Promise as any) as IQService); contextSrv.isSignedIn = true; impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]); }); describe('With recent dashboards', () => { - let results; + let results: any; beforeEach(() => { backendSrvMock.search = jest @@ -56,7 +60,7 @@ describe('SearchSrv', () => { }); describe('and 3 recent dashboards removed in backend', () => { - let results; + let results: any; beforeEach(() => { backendSrvMock.search = jest @@ -80,7 +84,7 @@ describe('SearchSrv', () => { }); describe('With starred dashboards', () => { - let results; + let results: any; beforeEach(() => { backendSrvMock.search = jest.fn().mockReturnValue(Promise.resolve([{ id: 1, title: 'starred' }])); @@ -97,7 +101,7 @@ describe('SearchSrv', () => { }); describe('With starred dashboards and recent', () => { - let results; + let results: any; beforeEach(() => { backendSrvMock.search = jest @@ -125,7 +129,7 @@ describe('SearchSrv', () => { }); describe('with no query string and dashboards with folders returned', () => { - let results; + let results: any; beforeEach(() => { backendSrvMock.search = jest @@ -173,12 +177,10 @@ describe('SearchSrv', () => { }); describe('with query string and dashboards with folders returned', () => { - let results; + let results: any; beforeEach(() => { - backendSrvMock.search = jest.fn(); - - backendSrvMock.search.mockReturnValue( + backendSrvMock.search = jest.fn().mockReturnValue( Promise.resolve([ { id: 2, @@ -249,8 +251,9 @@ describe('SearchSrv', () => { backendSrvMock.search = jest.fn(); backendSrvMock.search.mockReturnValue(Promise.resolve([])); - searchSrv.getRecentDashboards = () => { + searchSrv['getRecentDashboards'] = () => { getRecentDashboardsCalled = true; + return Promise.resolve(); }; return searchSrv.search({ skipRecent: true }).then(() => {}); @@ -269,8 +272,9 @@ describe('SearchSrv', () => { backendSrvMock.search.mockReturnValue(Promise.resolve([])); impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]); - searchSrv.getStarred = () => { + searchSrv['getStarred'] = () => { getStarredCalled = true; + return Promise.resolve(); }; return searchSrv.search({ skipStarred: true }).then(() => {}); From 40978ee08a088e14a913df3427c7317d1da8cc5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 19 Mar 2019 15:10:00 +0100 Subject: [PATCH 094/101] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d58bb81a2..5468a2b41f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ * **Datasource**: Empty user/password was not updated when updating datasources [#15608](https://github.com/grafana/grafana/pull/15608), thx [@Maddin-619](https://github.com/Maddin-619) * **Heatmap**: legend shows wrong colors for small values [#14019](https://github.com/grafana/grafana/issues/14019) -# 6.0.2 (unreleased) +# 6.0.2 (2019-03-19) ### Bug Fixes * **Alerting**: Fixed issue with AlertList panel links resulting in panel not found errors. [#15975](https://github.com/grafana/grafana/pull/15975), [@torkelo](https://github.com/torkelo) From 9a4a8b0f857f502757641bba069718eecd063924 Mon Sep 17 00:00:00 2001 From: yalhyane Date: Tue, 19 Mar 2019 14:39:26 +0000 Subject: [PATCH 095/101] Update templating.md fix a typo in the example of `percentencode` option --- docs/sources/reference/templating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index b00e44943ef..2be2b6863a9 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -110,7 +110,7 @@ Formats single & multi valued variables for use in URL parameters. ```bash servers = ['foo()bar BAZ', 'test2'] -String to interpolate: '${servers:lucene}' +String to interpolate: '${servers:percentencode}' Interpolation result: 'foo%28%29bar%20BAZ%2Ctest2' ``` From 124bedbf24160de9b8baae4b707cddf9f43f8f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 19 Mar 2019 15:44:10 +0100 Subject: [PATCH 096/101] Update latest.json --- latest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/latest.json b/latest.json index e19a0f8550d..bc1ac9d90cc 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "stable": "6.0.1", - "testing": "6.0.1" + "stable": "6.0.2", + "testing": "6.0.2" } From 512f2dd02435083fc84d0b89dbc81f0435d5faad Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 19 Mar 2019 16:11:47 +0100 Subject: [PATCH 097/101] chore: Bump react and react-dom to 16.8.4 --- package.json | 10 +- packages/grafana-ui/package.json | 6 +- yarn.lock | 1986 +++++++++++++++--------------- 3 files changed, 1000 insertions(+), 1002 deletions(-) diff --git a/package.json b/package.json index c951e5241c1..afd5307035c 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "@types/jest": "^23.3.2", "@types/jquery": "^1.10.35", "@types/node": "^8.0.31", - "@types/react": "^16.7.6", - "@types/react-dom": "^16.0.9", + "@types/react": "^16.8.8", + "@types/react-dom": "^16.8.2", "@types/react-grid-layout": "^0.16.6", "@types/react-select": "^2.0.4", "@types/react-transition-group": "^2.0.15", @@ -191,8 +191,8 @@ "prismjs": "^1.6.0", "prop-types": "^15.6.2", "rc-cascader": "^0.14.0", - "react": "^16.6.3", - "react-dom": "^16.6.3", + "react": "^16.8.4", + "react-dom": "^16.8.4", "react-grid-layout": "0.16.6", "react-highlight-words": "0.11.0", "react-popper": "^1.3.0", @@ -219,7 +219,7 @@ }, "resolutions": { "caniuse-db": "1.0.30000772", - "**/@types/react": "16.7.6" + "**/@types/react": "16.8.8" }, "workspaces": { "packages": [ diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index aec32fd9282..bfe5d48cbf6 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -25,10 +25,10 @@ "lodash": "^4.17.10", "moment": "^2.22.2", "papaparse": "^4.6.3", - "react": "^16.6.3", + "react": "^16.8.4", "react-color": "^2.17.0", "react-custom-scrollbars": "^4.2.1", - "react-dom": "^16.6.3", + "react-dom": "^16.8.4", "react-highlight-words": "0.11.0", "react-popper": "^1.3.0", "react-transition-group": "^2.2.1", @@ -48,7 +48,7 @@ "@types/lodash": "^4.14.119", "@types/node": "^10.12.18", "@types/papaparse": "^4.5.9", - "@types/react": "^16.7.6", + "@types/react": "^16.8.8", "@types/react-custom-scrollbars": "^4.0.5", "@types/react-test-renderer": "^16.0.3", "@types/react-transition-group": "^2.0.15", diff --git a/yarn.lock b/yarn.lock index c75b8e5a57b..700d2df7544 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,33 +30,33 @@ source-map "^0.5.0" "@babel/core@^7.1.2", "@babel/core@^7.1.6": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.2.tgz#07adba6dde27bb5ad8d8672f15fde3e08184a687" - integrity sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b" + integrity sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.2.2" + "@babel/generator" "^7.3.4" "@babel/helpers" "^7.2.0" - "@babel/parser" "^7.2.2" + "@babel/parser" "^7.3.4" "@babel/template" "^7.2.2" - "@babel/traverse" "^7.2.2" - "@babel/types" "^7.2.2" + "@babel/traverse" "^7.3.4" + "@babel/types" "^7.3.4" convert-source-map "^1.1.0" debug "^4.1.0" json5 "^2.1.0" - lodash "^4.17.10" + lodash "^4.17.11" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.0.0", "@babel/generator@^7.2.2": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.2.tgz#fff31a7b2f2f3dad23ef8e01be45b0d5c2fc0132" - integrity sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ== +"@babel/generator@^7.0.0", "@babel/generator@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.4.tgz#9aa48c1989257877a9d971296e5b73bfe72e446e" + integrity sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg== dependencies: - "@babel/types" "^7.3.2" + "@babel/types" "^7.3.4" jsesc "^2.5.1" - lodash "^4.17.10" + lodash "^4.17.11" source-map "^0.5.0" trim-right "^1.0.1" @@ -92,16 +92,17 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-create-class-features-plugin@^7.3.0": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.2.tgz#ba1685603eb1c9f2f51c9106d5180135c163fe73" - integrity sha512-tdW8+V8ceh2US4GsYdNVNoohq5uVwOf9k6krjwW4E1lINcHgttnWcNqgdoessn12dAy8QkbezlbQh2nXISNY+A== +"@babel/helper-create-class-features-plugin@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.4.tgz#092711a7a3ad8ea34de3e541644c2ce6af1f6f0c" + integrity sha512-uFpzw6L2omjibjxa8VGZsJUPL5wJH0zzGKpoz0ccBkzIa6C8kWNUbiBmQ0rgOKWlHJ6qzmfa6lTiGchiV8SC+g== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/helper-member-expression-to-functions" "^7.0.0" "@babel/helper-optimise-call-expression" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.2.3" + "@babel/helper-replace-supers" "^7.3.4" + "@babel/helper-split-export-declaration" "^7.0.0" "@babel/helper-define-map@^7.1.0": version "7.1.0" @@ -199,15 +200,15 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-replace-supers@^7.0.0", "@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz#19970020cf22677d62b3a689561dbd9644d8c5e5" - integrity sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA== +"@babel/helper-replace-supers@^7.0.0", "@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.3.4.tgz#a795208e9b911a6eeb08e5891faacf06e7013e13" + integrity sha512-pvObL9WVf2ADs+ePg0jrqlhHoxRXlOa+SHRHzAXIz2xkYuOHfGl+fKxPMaS4Fq+uje8JQPobnertBBvyrWnQ1A== dependencies: "@babel/helper-member-expression-to-functions" "^7.0.0" "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.2.3" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.3.4" + "@babel/types" "^7.3.4" "@babel/helper-simple-access@^7.1.0": version "7.1.0" @@ -252,10 +253,10 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.2.tgz#95cdeddfc3992a6ca2a1315191c1679ca32c55cd" - integrity sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.2.2", "@babel/parser@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c" + integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ== "@babel/plugin-proposal-async-generator-functions@^7.1.0", "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" @@ -279,11 +280,11 @@ "@babel/plugin-syntax-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties@^7.2.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.0.tgz#272636bc0fa19a0bc46e601ec78136a173ea36cd" - integrity sha512-wNHxLkEKTQ2ay0tnsam2z7fGZUi+05ziDJflEt3AZTP3oXLKHJp9HqhfroB/vdMvt3sda9fAbq7FsG8QPDrZBg== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.4.tgz#410f5173b3dc45939f9ab30ca26684d72901405e" + integrity sha512-lUf8D3HLs4yYlAo8zjuneLvfxN7qfKv1Yzbj5vjqaqMJxgJA3Ipwp4VUJ+OrOdz53Wbww6ahwB8UhB2HQyLotA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.3.0" + "@babel/helper-create-class-features-plugin" "^7.3.4" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-decorators@7.1.2": @@ -312,10 +313,10 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.0.0" -"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.3.1": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.2.tgz#6d1859882d4d778578e41f82cc5d7bf3d5daf6c1" - integrity sha512-DjeMS+J2+lpANkYLLO+m6GjoTMygYglKmRe6cDTbFv3L9i6mmiE8fe6B8MtCSLZpVXscD5kn7s6SgtHrDoBWoA== +"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.4.tgz#47f73cf7f2a721aad5c0261205405c642e424654" + integrity sha512-j7VQmbbkA+qrzNqbKHrBsW3ddFnOeva6wzSe/zB7T+xaxGc+RCpwo44wCmRixAIGRoIpmVgvzFzNJqQcO3/9RA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" @@ -408,9 +409,9 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-typescript@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.2.0.tgz#55d240536bd314dcbbec70fd949c5cabaed1de29" - integrity sha512-WhKr6yu6yGpGcNMVgIBuI9MkredpVc7Y3YR4UzEZmDztHoL6wV56YBHLhWnjO1EvId1B32HrD3DRFc+zSoKI1g== + version "7.3.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991" + integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -421,10 +422,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-async-to-generator@^7.1.0", "@babel/plugin-transform-async-to-generator@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.2.0.tgz#68b8a438663e88519e65b776f8938f3445b1a2ff" - integrity sha512-CEHzg4g5UraReozI9D4fblBYABs7IM6UerAVG7EJVrTLC5keh00aEuLUT+O40+mJCEzaXkYfTCUKIyeDfMOFFQ== +"@babel/plugin-transform-async-to-generator@^7.1.0", "@babel/plugin-transform-async-to-generator@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.3.4.tgz#4e45408d3c3da231c0e7b823f407a53a7eb3048c" + integrity sha512-Y7nCzv2fw/jEZ9f678MuKdMo99MFDJMT/PvD9LisrR5JDFcJH6vYeH6RnjVt3p5tceyGRvTtEN0VOlU+rgHZjA== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -437,13 +438,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.2.0.tgz#f17c49d91eedbcdf5dd50597d16f5f2f770132d4" - integrity sha512-vDTgf19ZEV6mx35yiPJe4fS02mPQUUcBNwWQSZFXSzTSbsJFQvHt7DqyS3LK8oOWALFOsJ+8bbqBgkirZteD5Q== +"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.3.4.tgz#5c22c339de234076eee96c8783b2fed61202c5c4" + integrity sha512-blRr2O8IOZLAOJklXLV4WhcEzpYafYQKSGT3+R26lWG41u/FODJuBggehtOwilVAcFu393v3OFj+HmaE6tVjhA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.10" + lodash "^4.17.11" "@babel/plugin-transform-classes@7.1.0": version "7.1.0" @@ -459,17 +460,17 @@ "@babel/helper-split-export-declaration" "^7.0.0" globals "^11.1.0" -"@babel/plugin-transform-classes@^7.1.0", "@babel/plugin-transform-classes@^7.2.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.2.2.tgz#6c90542f210ee975aa2aa8c8b5af7fa73a126953" - integrity sha512-gEZvgTy1VtcDOaQty1l10T3jQmJKlNVxLDCs+3rCVPr6nMkODLELxViq5X9l+rfxbie3XrfrMCYYY6eX3aOcOQ== +"@babel/plugin-transform-classes@^7.1.0", "@babel/plugin-transform-classes@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.3.4.tgz#dc173cb999c6c5297e0b5f2277fdaaec3739d0cc" + integrity sha512-J9fAvCFBkXEvBimgYxCjvaVDzL6thk0j0dBvCeZmIUDBwyt+nv6HfbImsSrWsYXfDNDivyANgJlFXDUWRTZBuA== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-define-map" "^7.1.0" "@babel/helper-function-name" "^7.1.0" "@babel/helper-optimise-call-expression" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.1.0" + "@babel/helper-replace-supers" "^7.3.4" "@babel/helper-split-export-declaration" "^7.0.0" globals "^11.1.0" @@ -527,9 +528,9 @@ "@babel/plugin-syntax-flow" "^7.0.0" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.2.3.tgz#e3ac2a594948454e7431c7db33e1d02d51b5cd69" - integrity sha512-xnt7UIk9GYZRitqCnsVMjQK1O2eKZwFB3CvvHjf5SGx6K6vr/MScCKQDnf1DxRaj501e3pXjti+inbSXX2ZUoQ== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.3.4.tgz#00156236defb7dedddc2d3c9477dcc01a4494327" + integrity sha512-PmQC9R7DwpBFA+7ATKMyzViz3zCaMNouzZMPZN2K5PnbBbtL3AXFYTkDk+Hey5crQq2A90UG5Uthz0mel+XZrA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-flow" "^7.2.0" @@ -573,10 +574,10 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-simple-access" "^7.1.0" -"@babel/plugin-transform-modules-systemjs@^7.0.0", "@babel/plugin-transform-modules-systemjs@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.2.0.tgz#912bfe9e5ff982924c81d0937c92d24994bb9068" - integrity sha512-aYJwpAhoK9a+1+O625WIjvMY11wkB/ok0WClVwmeo3mCjcNRjt+/8gHWrB5i+00mUju0gWsBkQnPpdvQ7PImmQ== +"@babel/plugin-transform-modules-systemjs@^7.0.0", "@babel/plugin-transform-modules-systemjs@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.3.4.tgz#813b34cd9acb6ba70a84939f3680be0eb2e58861" + integrity sha512-VZ4+jlGOF36S7TjKs8g4ojp4MEI+ebCQZdswWb/T9I4X84j8OtFAyjXjt/M16iIm5RIZn0UMQgg/VgIwo/87vw== dependencies: "@babel/helper-hoist-variables" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -612,9 +613,9 @@ "@babel/helper-replace-supers" "^7.1.0" "@babel/plugin-transform-parameters@^7.1.0", "@babel/plugin-transform-parameters@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.2.0.tgz#0d5ad15dc805e2ea866df4dd6682bfe76d1408c2" - integrity sha512-kB9+hhUidIgUoBQ0MsxMewhzr8i60nMa2KgeJKQWYrqQpqcBYtnpR+JgkadZVZoaEZ/eKu9mclFaVwhRpLNSzA== + version "7.3.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.3.3.tgz#3a873e07114e1a5bee17d04815662c8317f10e30" + integrity sha512-IrIP25VvXWu/VlBWTpsjGptpomtIkYrN/3aDp4UKm7xK6UxZY88kcJ1UwETbzHAlwN21MnNfwlar0u8y3KpiXw== dependencies: "@babel/helper-call-delegate" "^7.1.0" "@babel/helper-get-function-arity" "^7.0.0" @@ -675,12 +676,12 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-jsx" "^7.2.0" -"@babel/plugin-transform-regenerator@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz#5b41686b4ed40bef874d7ed6a84bdd849c13e0c1" - integrity sha512-sj2qzsEx8KDVv1QuJc/dEfilkg3RRPvPYx/VnKLtItVQRWt1Wqf5eVCOLZm29CiGFfYYsA3VPjfizTCV0S0Dlw== +"@babel/plugin-transform-regenerator@^7.0.0", "@babel/plugin-transform-regenerator@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.3.4.tgz#1601655c362f5b38eead6a52631f5106b29fa46a" + integrity sha512-hvJg8EReQvXT6G9H2MvNPXkv9zK36Vxa1+csAVTpE1J3j0zlHplw76uudEbJxgvqZzAq9Yh45FLD4pk5mKRFQA== dependencies: - regenerator-transform "^0.13.3" + regenerator-transform "^0.13.4" "@babel/plugin-transform-runtime@7.1.0": version "7.1.0" @@ -729,7 +730,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-typescript@^7.1.0": +"@babel/plugin-transform-typescript@^7.1.0", "@babel/plugin-transform-typescript@^7.3.2": version "7.3.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.3.2.tgz#59a7227163e55738842f043d9e5bd7c040447d96" integrity sha512-Pvco0x0ZSCnexJnshMfaibQ5hnK8aUHSvjCQhC1JR8eeg+iBwt0AtCO7gWxJ358zZevuf9wPSO5rv+WJcbHPXQ== @@ -802,15 +803,15 @@ semver "^5.3.0" "@babel/preset-env@^7.1.0", "@babel/preset-env@^7.1.6", "@babel/preset-env@^7.2.0": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.1.tgz#389e8ca6b17ae67aaf9a2111665030be923515db" - integrity sha512-FHKrD6Dxf30e8xgHQO0zJZpUPfVZg+Xwgz5/RdSWCbza9QLNk4Qbp40ctRoqDxml3O8RMzB1DU55SXeDG6PqHQ== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1" + integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-async-generator-functions" "^7.2.0" "@babel/plugin-proposal-json-strings" "^7.2.0" - "@babel/plugin-proposal-object-rest-spread" "^7.3.1" + "@babel/plugin-proposal-object-rest-spread" "^7.3.4" "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" "@babel/plugin-syntax-async-generators" "^7.2.0" @@ -818,10 +819,10 @@ "@babel/plugin-syntax-object-rest-spread" "^7.2.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" "@babel/plugin-transform-arrow-functions" "^7.2.0" - "@babel/plugin-transform-async-to-generator" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.3.4" "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.2.0" - "@babel/plugin-transform-classes" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.3.4" + "@babel/plugin-transform-classes" "^7.3.4" "@babel/plugin-transform-computed-properties" "^7.2.0" "@babel/plugin-transform-destructuring" "^7.2.0" "@babel/plugin-transform-dotall-regex" "^7.2.0" @@ -832,13 +833,13 @@ "@babel/plugin-transform-literals" "^7.2.0" "@babel/plugin-transform-modules-amd" "^7.2.0" "@babel/plugin-transform-modules-commonjs" "^7.2.0" - "@babel/plugin-transform-modules-systemjs" "^7.2.0" + "@babel/plugin-transform-modules-systemjs" "^7.3.4" "@babel/plugin-transform-modules-umd" "^7.2.0" "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0" "@babel/plugin-transform-new-target" "^7.0.0" "@babel/plugin-transform-object-super" "^7.2.0" "@babel/plugin-transform-parameters" "^7.2.0" - "@babel/plugin-transform-regenerator" "^7.0.0" + "@babel/plugin-transform-regenerator" "^7.3.4" "@babel/plugin-transform-shorthand-properties" "^7.2.0" "@babel/plugin-transform-spread" "^7.2.0" "@babel/plugin-transform-sticky-regex" "^7.2.0" @@ -869,7 +870,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/preset-typescript@7.1.0", "@babel/preset-typescript@^7.1.0": +"@babel/preset-typescript@7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.1.0.tgz#49ad6e2084ff0bfb5f1f7fb3b5e76c434d442c7f" integrity sha512-LYveByuF9AOM8WrsNne5+N79k1YxjNB6gmpCQsnuSBAcV8QUeB+ZUxQzL7Rz7HksPbahymKkq2qBR+o36ggFZA== @@ -877,6 +878,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-typescript" "^7.1.0" +"@babel/preset-typescript@^7.1.0": + version "7.3.3" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a" + integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.3.2" + "@babel/runtime@7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c" @@ -885,9 +894,9 @@ regenerator-runtime "^0.12.0" "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a" - integrity sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" + integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g== dependencies: regenerator-runtime "^0.12.0" @@ -900,28 +909,28 @@ "@babel/parser" "^7.2.2" "@babel/types" "^7.2.2" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" - integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06" + integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.2.2" + "@babel/generator" "^7.3.4" "@babel/helper-function-name" "^7.1.0" "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.2.3" - "@babel/types" "^7.2.2" + "@babel/parser" "^7.3.4" + "@babel/types" "^7.3.4" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.10" + lodash "^4.17.11" -"@babel/types@^7.0.0", "@babel/types@^7.1.6", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.2": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.2.tgz#424f5be4be633fff33fb83ab8d67e4a8290f5a2f" - integrity sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ== +"@babel/types@^7.0.0", "@babel/types@^7.1.6", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed" + integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ== dependencies: esutils "^2.0.2" - lodash "^4.17.10" + lodash "^4.17.11" to-fast-properties "^2.0.0" "@emotion/babel-utils@^0.6.4": @@ -1040,20 +1049,6 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.1.3.tgz#b700d97385fa91affed60c71dfd51c67e9dad762" integrity sha512-QsYGKdhhuDFNq7bjm2r44y0mp5xW3uO3csuTPDWZc0OIiMQv+AIY5Cqwd4mJiC5N8estVl7qlvOx1hbtOuUWbw== -"@iamstarkov/listr-update-renderer@0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz#d7c48092a2dcf90fd672b6c8b458649cb350c77e" - integrity sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA== - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^2.3.0" - strip-ansi "^3.0.1" - "@icons/material@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" @@ -1097,16 +1092,16 @@ integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== "@storybook/addon-actions@^4.1.7": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-4.1.11.tgz#8946ea78f050ae2d06a2f2231ec56d1831942e15" - integrity sha512-iVsxEPmOCuPMAaJhHbpxQhzEPzKnZad4GELNfKrwmmvv3mY+3UN/z208HguW4NHjhMJZVYSS3H/qic8CQS+pHw== + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-4.1.14.tgz#4bd962da767aa6a99867114894229c739f87d780" + integrity sha512-zq8MSSLXv+D+e/m3LdRrAMVrJP5xcETO4OtutJCdJeAkIjMvstnWncUEUHMltv/+bTBQgb77uoTFKVN4zrJL2g== dependencies: "@emotion/core" "^0.13.1" "@emotion/provider" "^0.11.2" "@emotion/styled" "^0.10.6" - "@storybook/addons" "4.1.11" - "@storybook/components" "4.1.11" - "@storybook/core-events" "4.1.11" + "@storybook/addons" "4.1.14" + "@storybook/components" "4.1.14" + "@storybook/core-events" "4.1.14" core-js "^2.5.7" deep-equal "^1.0.1" global "^4.3.2" @@ -1117,13 +1112,13 @@ uuid "^3.3.2" "@storybook/addon-info@^4.1.6": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/addon-info/-/addon-info-4.1.11.tgz#b2ea3a4fb4cad208f9d6075737b5bfe8636e28f9" - integrity sha512-eROXuXS5YgLeXsnkqjXqbZ8UFgNIwORDkn4UfD+Aej1//SWpGeNihOxQvx+pvs0NnsTR+/w4c1gbqa/Gr3f78w== + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-info/-/addon-info-4.1.14.tgz#d0e155413881403e9dd0bcba04a05199141239a2" + integrity sha512-PcJfcZ0WnC+mr+AIn1LfacjruG6cLnCC98BdxpdTOMK+nncR/JffJ7ySqykFEnwRfLuSHGglIg+LwjUViQ4mwA== dependencies: - "@storybook/addons" "4.1.11" - "@storybook/client-logger" "4.1.11" - "@storybook/components" "4.1.11" + "@storybook/addons" "4.1.14" + "@storybook/client-logger" "4.1.14" + "@storybook/components" "4.1.14" core-js "^2.5.7" global "^4.3.2" marksy "^6.1.0" @@ -1134,14 +1129,14 @@ util-deprecate "^1.0.2" "@storybook/addon-knobs@^4.1.7": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-4.1.11.tgz#fd6c90d62a5bf5f94899746a95b02f1ef127cd81" - integrity sha512-UQzYZoo0WKHKHSayaEBLvyZNqlqCOKahXzT2r+hS3t6wRnHJSfPtEHD0xTYMJSkA5t+bhlIOFJy0tribd0sdPQ== + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-4.1.14.tgz#48bc37f58707cb00960a501f24f702c993f17ad8" + integrity sha512-ScMi3iZxdOYMzVv7jMUqfgyPBWYwkSW4OLrVFIr5/EPP9mAOwnlYz+tI/8zM6w7/cvdEPY2pCilKt2zhzEMQ9w== dependencies: "@emotion/styled" "^0.10.6" - "@storybook/addons" "4.1.11" - "@storybook/components" "4.1.11" - "@storybook/core-events" "4.1.11" + "@storybook/addons" "4.1.14" + "@storybook/components" "4.1.14" + "@storybook/core-events" "4.1.14" copy-to-clipboard "^3.0.8" core-js "^2.5.7" escape-html "^1.0.3" @@ -1153,39 +1148,39 @@ react-lifecycles-compat "^3.0.4" util-deprecate "^1.0.2" -"@storybook/addons@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-4.1.11.tgz#a0d537bd10d123ecee6cb1f5f149b148ce250e57" - integrity sha512-n9oDs7GgJbiN5NYPkR3B3e5W0Tr6bIZvFfcJzgyP4dn50AUvS1IE1CEthezfn1L/nc2suw/8Oe30bOXOyTl/SQ== +"@storybook/addons@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-4.1.14.tgz#e976ac21b82043efb8fb2289063b370d94c29eae" + integrity sha512-nAieZLLeXzuUY8TIHshzVX8VUtUowsyohIKilZSzLIBFMpCEYlyPBilrE9ULqUEtVQWi5peAiKRZ1xJQxx/TIA== dependencies: - "@storybook/channels" "4.1.11" - "@storybook/components" "4.1.11" + "@storybook/channels" "4.1.14" + "@storybook/components" "4.1.14" global "^4.3.2" util-deprecate "^1.0.2" -"@storybook/channel-postmessage@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-4.1.11.tgz#3320a5f3e05652466eff1c53843205c262a92dfb" - integrity sha512-/9p4I5CZWVl6mszY5AR5XPRdQ88LUaAt4iyhdxMIaqNRiVo3Rq4ptMXiw35eCr+sLQuG2KO2SiPIwaA4/FgQuw== +"@storybook/channel-postmessage@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-4.1.14.tgz#003dfbf8dd62f4f3f9d9b7766e72f57377a8589e" + integrity sha512-Kz1oOoJXoqZYWh7V4yykEAKsZwegY/VQzEIFQjlyCAvNyfCvN35AgAdrgLCU2Uwss5lds18SkdwxCOxmH2lnEA== dependencies: - "@storybook/channels" "4.1.11" + "@storybook/channels" "4.1.14" global "^4.3.2" json-stringify-safe "^5.0.1" -"@storybook/channels@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-4.1.11.tgz#d161497fa3cd848cc9d518aa1c37052857e22e3c" - integrity sha512-zYusY8cno4keMozn2lDpBgyNSOueFh+hrPETioSB5Z8Kd3F5OjM7681vJC8QA67yOBEie2hHk0CVxRpuxziMwA== +"@storybook/channels@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-4.1.14.tgz#8af55d028d7f86f5a9abb9329df7e9d1121f404b" + integrity sha512-2BpKF7MXWfJeY9lRHrUseJ6JoZXshmo9B4np7TAex3NXOeRLsptQMCfQXgMgUMDQhABc0o9XBAruQOb9zMyd7w== -"@storybook/client-logger@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-4.1.11.tgz#2b1e34e892045199592fdb01656e5dcdcd1999d7" - integrity sha512-Xxy6sY7Zd405o28wUAhlpqY2FbSZsTrsN3g/uo4Mqo4XD2f0Z4wIv1GOuM5DI2KlHpHGI+36YPO2VFx5Bq+yiQ== +"@storybook/client-logger@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-4.1.14.tgz#73a408280af594f66259f750bd058534f7a3a95f" + integrity sha512-8Vb4DaGvUsc/voPxOW2LAJZBK5ac8btvesGZZJO43HVWpmROLPrCcoKIUFLWgPmmXyHTkLuheTn9tcbok1BowQ== -"@storybook/components@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-4.1.11.tgz#25458a4a4f2edd836b1e4b944cfcfcb4a3567036" - integrity sha512-KJA8Nr8MbXiibDLcndx1GRVmVDyBBL2Tbb1kfQfr58vDwz6qhYxempejY6W+voaEqohnFxrOtnnbqlCyf8peUQ== +"@storybook/components@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-4.1.14.tgz#54e33ab9bf09ce75f1b97e9212672921444a9fdc" + integrity sha512-UdSwxZutRUW8umaaPnris5M9pGz2220KXs/5g2URaw3rEL8eUaKLKJXSpFSOeXPhlCnV7B9R/qanG9GXGQKXmg== dependencies: "@emotion/core" "^0.13.1" "@emotion/provider" "^0.11.2" @@ -1198,27 +1193,27 @@ react-textarea-autosize "^7.0.4" render-fragment "^0.1.1" -"@storybook/core-events@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-4.1.11.tgz#78cfb2b4014ca27909421cdebfa9c96533929a5a" - integrity sha512-rVb76xFLJkTFcBHL1oTdJW8O2N7q+Cc6Mo7v9u3TnM4WuRk08/GyzzO7sRvEg3Mvo59AOLu1uqYovRMo4tZEnQ== +"@storybook/core-events@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-4.1.14.tgz#919a577bab7d4c45ea6cb83f6cf8b35782a8f671" + integrity sha512-UBVTWZonTD5hHR7huWs15mYMKJONjQC6GBQy5AvKDpAIXtAlHe75mv5aZM5gRTP/ThtNE9V1HO4IF3C54dvUjg== -"@storybook/core@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-4.1.11.tgz#f91cf77d4750edeb92717f6b2a2a4258b0a06c64" - integrity sha512-iUrtFCav7xJicCLhp4zdqbhaOXRWXrx4wMPSs0keBD2G7NQtSg/TQMUdx2VYFBl5thIFT1jt5dAm66y0Q2OCTQ== +"@storybook/core@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-4.1.14.tgz#488e6f8b4cbb9c1346e8c3755437e0269f790b74" + integrity sha512-864gvxZNaL3vjskyPkXqx88y+C51KenVY4g5AdX1MrerHHSzPzfSS9PPO+XT8rrJO6FQ5vRHyG9sxa0MkG6O3A== dependencies: "@babel/plugin-proposal-class-properties" "^7.2.0" "@babel/preset-env" "^7.2.0" "@emotion/core" "^0.13.1" "@emotion/provider" "^0.11.2" "@emotion/styled" "^0.10.6" - "@storybook/addons" "4.1.11" - "@storybook/channel-postmessage" "4.1.11" - "@storybook/client-logger" "4.1.11" - "@storybook/core-events" "4.1.11" - "@storybook/node-logger" "4.1.11" - "@storybook/ui" "4.1.11" + "@storybook/addons" "4.1.14" + "@storybook/channel-postmessage" "4.1.14" + "@storybook/client-logger" "4.1.14" + "@storybook/core-events" "4.1.14" + "@storybook/node-logger" "4.1.14" + "@storybook/ui" "4.1.14" airbnb-js-shims "^1 || ^2" autoprefixer "^9.3.1" babel-plugin-macros "^2.4.2" @@ -1282,10 +1277,10 @@ "@storybook/react-simple-di" "^1.2.1" babel-runtime "6.x.x" -"@storybook/node-logger@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-4.1.11.tgz#8ea9779eb6260a02bf06c02eafbff5925b883f9f" - integrity sha512-rCXk1PUcakkV72oyTR+nOVDUGnkk1On8/sm9u3NtBEUuwsCtm4p+jh42Pp4jsTtWpG36AVABDtiN65VgCfS+9w== +"@storybook/node-logger@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-4.1.14.tgz#d97ba3f8bd727dc0a0266aaabc07e3111a58f0ca" + integrity sha512-eUWa+PnZ2t/j74PghtcjMINkPRLRWi8ekSbrbYsiwfErG6B6r/m89Ca6gvg/51o1e12rPz5B4cbCv6gtZEa7UA== dependencies: chalk "^2.4.1" core-js "^2.5.7" @@ -1330,16 +1325,16 @@ babel-runtime "^6.5.0" "@storybook/react@^4.1.4": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-4.1.11.tgz#fb3cda82fd3334a6653325ec281a2da284ac6895" - integrity sha512-NPNcfOlWmFBevza/+GXIK23h46HqdWKFmId19E0PtoWGoR5xOR56rnwagr2+yHngUb4AATUCzhzTwxfWGxSvBg== + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-4.1.14.tgz#fb1e68ffabaed258fa90cf328500803715b45bf6" + integrity sha512-sza+wvWymLv2e6aGEcVO4ka5J3gde298jRw3Qjf7DBHfKx0dBx1QCwNTCUn3Zw6NZvY3Li2gFHN1MFAwshsZtA== dependencies: "@babel/plugin-transform-react-constant-elements" "^7.2.0" "@babel/preset-flow" "^7.0.0" "@babel/preset-react" "^7.0.0" "@emotion/styled" "^0.10.6" - "@storybook/core" "4.1.11" - "@storybook/node-logger" "4.1.11" + "@storybook/core" "4.1.14" + "@storybook/node-logger" "4.1.14" "@svgr/webpack" "^4.0.3" babel-plugin-named-asset-import "^0.2.3" babel-plugin-react-docgen "^2.0.0" @@ -1355,16 +1350,16 @@ semver "^5.6.0" webpack "^4.23.1" -"@storybook/ui@4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-4.1.11.tgz#0c6fc34a8096028ef236a5196e7b91831702f2fb" - integrity sha512-bgIagh2Z4flGA7jv4JN++ThLwGq8CI8Wq+1/vhCiTxjTE3H1j9VNPdJfhNgxrQypcLRVHl5AKhf0mlSMyz0S1A== +"@storybook/ui@4.1.14": + version "4.1.14" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-4.1.14.tgz#5dd50490fcede29d552ffe4e0870648a35f0c997" + integrity sha512-GV7MKjcFkfBmr7odrb/oJJ2VlDE1kKXjOo0Fk+3jYhdOd8JHv60Xj5z7Q91xqxPLwThNUZkCMUeWdbvyYRykFw== dependencies: "@emotion/core" "^0.13.1" "@emotion/provider" "^0.11.2" "@emotion/styled" "^0.10.6" - "@storybook/components" "4.1.11" - "@storybook/core-events" "4.1.11" + "@storybook/components" "4.1.14" + "@storybook/core-events" "4.1.14" "@storybook/mantra-core" "^1.7.2" "@storybook/podda" "^1.2.3" "@storybook/react-komposer" "^2.0.5" @@ -1527,9 +1522,11 @@ chalk "*" "@types/cheerio@*": - version "0.22.10" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.10.tgz#780d552467824be4a241b29510a7873a7432c4a6" - integrity sha512-fOM/Jhv51iyugY7KOBZz2ThfT1gwvsGCfWxpLpZDgkGjpEO4Le9cld07OdskikLjDUQJ43dzDaVRSFwQlpdqVg== + version "0.22.11" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.11.tgz#61c0facf9636d14ba5f77fc65ed8913aa845d717" + integrity sha512-x0X3kPbholdJZng9wDMhb2swvUi3UYRNAuWAmIPIWlfgAJZp//cql/qblE7181Mg7SjWVwq6ldCPCLn5AY/e7w== + dependencies: + "@types/node" "*" "@types/classnames@^2.2.6": version "2.2.7" @@ -1544,119 +1541,119 @@ commander "*" "@types/d3-array@*": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.5.tgz#7f87eccfe53396d48700294a518c379be14c8254" - integrity sha512-kELkPCl/pCcelr5cXDoQyy3WOkLn8dVdYA+qmtQcuxX9gLoD4s12/CJf6Yxx4UvvuMKJHA8kUbdcH/3DY8SzNg== + version "1.2.6" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.6.tgz#bbc5d7e6e837353f5849c0ffad9bf6fbf9c775bb" + integrity sha512-/EcY/15X5tnwkMT2txpjiLUNJj5xHA2vGHOXI8NTYGhETK914RRLQLjNm6EpAI1D2IY5vh3CzuLODnqBAwKjPA== "@types/d3-axis@*": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-1.0.11.tgz#efd975f9fec14c2afd03828f3acec0ef97d37c3b" - integrity sha512-cuigApCyCwYJxaQPghj+BqaxzbdRdT/lpZBMtF7EuEIJ61NMQ8yvGnqFvHCIgJEmUu2Wb2wiZqy9kiHi3Ddftg== + version "1.0.12" + resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-1.0.12.tgz#8c124edfcc02f3b3a9cdaa2a28b8a20341401799" + integrity sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg== dependencies: "@types/d3-selection" "*" "@types/d3-brush@*": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-1.0.9.tgz#c71070845946eeee4cf330e04123a3997e6476bf" - integrity sha512-mAx8IVc0luUHfk51pl0UN1vzybnAzLMUsvIwLt3fbsqqPkSXr+Pu1AxOPPeyNc27LhHJnfH/LCV7Jlv+Yzqu1A== + version "1.0.10" + resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-1.0.10.tgz#aa9b5545d816c29d19cff20118f236713af8e9fb" + integrity sha512-J8jREATIrfJaAfhJivqaEKPnJsRlwwrOPje+ABqZFgamADjll+q9zaDXnYyjiGPPsiJEU+Qq9jQi5rECxIOfhg== dependencies: "@types/d3-selection" "*" "@types/d3-chord@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-1.0.8.tgz#08c0fbb10281be0a5b3fdf48c9c081af02f79fb6" - integrity sha512-F0ftYOo7FenAIxsRjXLt8vbij0NLDuVcL+xaGY7R9jUmF2Mrpj1T5XukBI9Cad+Ei7YSxEWREIO+CYcaKCl2qQ== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-1.0.9.tgz#ccc5de03ff079025491b7aa6b750670a140b45ae" + integrity sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw== "@types/d3-collection@*": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.7.tgz#829e1db477d6bbbcdc038cbc489f22798752d707" - integrity sha512-vR3BT0GwHc5y93Jv6bxn3zoxP/vGu+GdXu/r1ApjbP9dLk9I2g6NiV7iP/QMQSuFZd0It0n/qWrfXHxCWwHIkg== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.8.tgz#aa9552c570a96e33c132e0fd20e331f64baa9dd5" + integrity sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ== "@types/d3-color@*": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.2.1.tgz#26141c3c554e320edd40726b793570a3ae57397e" - integrity sha512-xwb1tqvYNWllbHuhMFhiXk63Imf+QNq/dJdmbXmr2wQVnwGenCuj3/0IWJ9hdIFQIqzvhT7T37cvx93jtAsDbQ== + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.2.2.tgz#80cf7cfff7401587b8f89307ba36fe4a576bc7cf" + integrity sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw== "@types/d3-dispatch@*": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-1.0.6.tgz#19b173f669cd2ab7dd3d862e8037aae1a98c7508" - integrity sha512-xyWJQMr832vqhu6fD/YqX+MSFBWnkxasNhcStvlhqygXxj0cKqPft0wuGoH5TIq5ADXgP83qeNVa4R7bEYN3uA== + version "1.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-1.0.7.tgz#6721aefbb9862ce78c20a87a1490c21f57c3ed7f" + integrity sha512-M+z84G7UKwK6hEPnGCSccOg8zJ3Nk2hgDQ9sCstHXgsFU0sMxlIZVKqKB5oxUDbALqQG6ucg0G9e8cmOSlishg== "@types/d3-drag@*": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-1.2.2.tgz#1cdd7716212a8cdef0a24c782c9d86c6aeb4a451" - integrity sha512-+UKFeaMVTfSQvMO0PTzOyLXSr7OZbF2Rx1iNVwo2XsyiOsd4MSuLyJKUwRmGn67044QpbNzr+VD6/8iBBLExWw== + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-1.2.3.tgz#d8ddccca28e939e9c689bea6f40a937e48c39051" + integrity sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ== dependencies: "@types/d3-selection" "*" "@types/d3-dsv@*": - version "1.0.35" - resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-1.0.35.tgz#eeb884bfbaa6775daa41ebd2dc8c07a24505f311" - integrity sha512-QeH7cN9phcm68TDwpSGmzE71/JtGoKZ2rZJABNUMQ7nYIhHkm2UldqI1Cp2pjEo8ycSeutudjzq+Lfim/ZCadQ== + version "1.0.36" + resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-1.0.36.tgz#e91129d7c02b1b814838d001e921e8b9a67153d0" + integrity sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA== "@types/d3-ease@*": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-1.0.7.tgz#93a301868be9e15061f3d44343b1ab3f8acb6f09" - integrity sha1-k6MBhovp4VBh89RDQ7GrP4rLbwk= + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-1.0.8.tgz#b440761fb85985d76259ec9c5bf01c4c56778ac2" + integrity sha512-VRf8czVWHSJPoUWxMunzpePK02//wHDAswknU8QWzcyrQn6pqe46bHRYi2smSpw5VjsT2CG8k/QeWIdWPS3Bmg== "@types/d3-force@*": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-1.2.0.tgz#430d572eb3922bc463726dd06580829ef36b6434" - integrity sha512-rfNJogFDPEO16RBqA4anZtiscYeMxreNg8zUKGnBi/1DnrZ42rG5RvOS/qXqBqLZJ3HY0ouAx8AZqgT3/hHbFA== + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-1.2.1.tgz#c28803ea36fe29788db69efa0ad6c2dc09544e83" + integrity sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA== "@types/d3-format@*": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.3.0.tgz#c5e115fac8e6861ce656fe9861892b22f6b0cfcb" - integrity sha512-ZiY4j3iJvAdOwzwW24WjlZbUNvqOsnPAMfPBmdXqxj3uKJbrzBlRrdGl5uC89pZpFs9Dc92E81KcwG2uEgkIZA== + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.3.1.tgz#35bf88264bd6bcda39251165bb827f67879c4384" + integrity sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A== "@types/d3-geo@*": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.11.0.tgz#f7921c5c50d1a43df928846d8a7e47140455687f" - integrity sha512-/IbMHRG9cur+6hkWvBrRg3DnnUWtaSW8Bl6nu1OO1J8K25BxRYyLslyjIBbwlK0kV0haztlAR2LCIRuDc/U2LA== + version "1.11.1" + resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.11.1.tgz#e96ec91f16221d87507fec66b2cc889f52d2493e" + integrity sha512-Ox8WWOG3igDRoep/dNsGbOiSJYdUG3ew/6z0ETvHyAtXZVBjOE0S96zSSmzgl0gqQ3RdZjn2eeJOj9oRcMZPkQ== dependencies: "@types/geojson" "*" "@types/d3-hierarchy@*": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#b9a941bbfbd8f83a163a1667aae8a52f41a9edab" - integrity sha512-DKhqURrURt2c7MsF9sHiF2wrWf2+yZR4Q9oIG026t/ZY4VWoM0Yd7UonaR+rygyReWcFSEjKC/+5A27TgD8R8g== + version "1.1.6" + resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz#4c017521900813ea524c9ecb8d7985ec26a9ad9a" + integrity sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg== "@types/d3-interpolate@*": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.3.0.tgz#65b9627900bfdd82474875d9b23d574a4388af7c" - integrity sha512-Ng4ds7kPSvP/c3W3J5PPUQlgewif1tGBqCeh5lgY+UG82Y7H9zQ8c2gILsEFDLg7wRGOwnuKZ940Q/LSN14w9w== + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz#1c280511f622de9b0b47d463fa55f9a4fd6f5fc8" + integrity sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ== dependencies: "@types/d3-color" "*" "@types/d3-path@*": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.7.tgz#a0736fceed688a695f48265a82ff7a3369414b81" - integrity sha512-U8dFRG+8WhkLJr2sxZ9Cw/5WeRgBnNqMxGdA1+Z0+ZG6tK0s75OQ4OXnxeyfKuh6E4wQPY8OAKr1+iNDx01BEQ== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" + integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== "@types/d3-polygon@*": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-1.0.6.tgz#db25c630a2afb9191fe51ba61dd37baee9dd44c7" - integrity sha512-E6Kyodn9JThgLq20nxSbEce9ow5/ePgm9PX2EO6W1INIL4DayM7cFaiG10DStuamjYAd0X4rntW2q+GRjiIktw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-1.0.7.tgz#7b3947aa2d48287ff535230d3d396668ab17bfdf" + integrity sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q== "@types/d3-quadtree@*": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-1.0.6.tgz#45da9e603688ba90eedd3d40f6e504764e06e493" - integrity sha512-sphVuDdiSIaxLt9kQgebJW98pTktQ/xuN7Ysd8X68Rnjeg/q8+c36/ShlqU52qoKg9nob/JEHH1uQMdxURZidQ== + version "1.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz#8e29464ff5b326f6612c1428d9362b4b35de2b70" + integrity sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q== "@types/d3-queue@*": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-queue/-/d3-queue-3.0.7.tgz#94dc7af693281ab78ccdf381a8c1f71ef16659c1" - integrity sha512-nBbDO69wu1TUWqtGYAePw40jSPcQSt5VwAf7403vYopVCs3Rtbt5f47j2wbuMY4Z2x543VbTIlDo5gwdpV5O+Q== + version "3.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-queue/-/d3-queue-3.0.8.tgz#fad6212f14f34a549fc67144e354f032fb25a447" + integrity sha512-1FWOiI/MYwS5Z1Sa9EvS1Xet3isiVIIX5ozD6iGnwHonGcqL+RcC1eThXN5VfDmAiYt9Me9EWNEv/9J9k9RIKQ== "@types/d3-random@*": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-1.1.1.tgz#38647ce2ff4ce7d0d56974334c1c4092513c8b9f" - integrity sha512-jUPeBq1XKK9/5XasTvy5QAUwFeMsjma2yt/nP02yC2Tijovx7i/W5776U/HZugxc5SSmtpx4Z3g9KFVon0QrjQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-1.1.2.tgz#6f77e8b7bb64ac393f92d33fe8f71038bc4f3cde" + integrity sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA== "@types/d3-request@*": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/d3-request/-/d3-request-1.0.4.tgz#b7bde4c63794ee6a47dc3d71d64ca91b9ac87349" - integrity sha512-6ZVaWdXNjXEp3A+PB/vMTIZDfmEiSay3oDyy7HpsTmnSAWSsqfXYTE9RxMmZs8MY0QMFbtous0LiUSrv5uOGXA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/d3-request/-/d3-request-1.0.5.tgz#a2717ab95cd1e504662f52802aff1476af38cce4" + integrity sha512-X+/c/qXp92o056C5Qbcp7jL27YRHpmIqOchHb/WB7NwFFqkBtAircqO7oKWv2GTtX4LyEqiDF9gqXsV+ldOlIg== dependencies: "@types/d3-dsv" "*" @@ -1668,48 +1665,48 @@ "@types/d3-time" "*" "@types/d3-selection@*": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-1.3.4.tgz#2f9b71e25fb73bc95c3842dd52b7e6d523292896" - integrity sha512-WQ6Ivy7VuUlZ/Grqc8493ZxC+y/fpvZLy5+8ELvmCr2hll8eJPUqC05l6fgRRA7kjqlpbH7lbmvY6pRKf6yzxw== + version "1.4.1" + resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-1.4.1.tgz#fa1f8710a6b5d7cfe5c6caa61d161be7cae4a022" + integrity sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA== "@types/d3-shape@*": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.0.tgz#ea195c543a1f6d3e250ceb07a7d208d80ee37b01" - integrity sha512-ERWJ8bNZjkzfWfPAlkN3XCqYOOsWTnqTX0jX2Bx+WLd2AfEl97WXr2igwssFc91MadrZLw7HNS/JTUZPQL5sZQ== + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.1.tgz#1b4f92b7efd7306fe2474dc6ee94c0f0ed2e6ab6" + integrity sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg== dependencies: "@types/d3-path" "*" "@types/d3-time-format@*": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.1.0.tgz#011e0fb7937be34a9a8f580ae1e2f2f1336a8a22" - integrity sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.1.1.tgz#dd2c79ec4575f1355484ab6b10407824668eba42" + integrity sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g== "@types/d3-time@*": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.9.tgz#c2cf05a3cd51f810b8d8a9bbca0c74030d4e535e" - integrity sha512-m+D4NbQdDlTVaO7QgXAnatR3IDxQYDMBtRhgSCi5rs9R1LPq1y7/2aqa1FJ2IWjFm1mOV63swDxonnCDlHgHMA== + version "1.0.10" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.10.tgz#d338c7feac93a98a32aac875d1100f92c7b61f4f" + integrity sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw== "@types/d3-timer@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-1.0.8.tgz#a3441d9605367059e14ad8c3494132143cbc8d58" - integrity sha512-AKUgQ/nljUFcUO2P3gK24weVI5XwUTdJvjoh8gJ0yxT4aJ+d7t2Or3TB+k9dEYl14BAjoj32D0ky+YzQSVszfg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-1.0.9.tgz#aed1bde0cf18920d33f5d44839d73de393633fd3" + integrity sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ== "@types/d3-transition@*": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-1.1.3.tgz#efcf4941dae22135d595514ba488f4f370d396b0" - integrity sha512-1EukXNuVu/z2G1GZpZagzFJnie9C5zze17ox/vhTgGXNy46rYAm4UkhLLlUeeZ1ndq88k95SOeC8898RpKMLOQ== + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-1.1.4.tgz#3c7a35ae9acfc59dfef1eb7308ebabf0fc0680de" + integrity sha512-/vsmKVUIXEyCcIXYAlw7bnYkIs9/J/nZbptRJFKUN3FdXq/dF6j9z9xXzerkyU6TDHLrMrwx9eGwdKyTIy/j9w== dependencies: "@types/d3-selection" "*" "@types/d3-voronoi@*": - version "1.1.8" - resolved "https://registry.yarnpkg.com/@types/d3-voronoi/-/d3-voronoi-1.1.8.tgz#a039cb8368bce4efc1a70aebe744d210851cf1a7" - integrity sha512-zqNhW7QsYQGlfOdrwPNPG3Wk64zUa4epKRurkJ/dVc6oeXrB+iTDt8sRZ0KZKOOXvvfa1dcdB0e45TZeLBiodQ== + version "1.1.9" + resolved "https://registry.yarnpkg.com/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz#7bbc210818a3a5c5e0bafb051420df206617c9e5" + integrity sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ== "@types/d3-zoom@*": - version "1.7.3" - resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-1.7.3.tgz#ed9421551328157f70edabc401d8c91c38d360d9" - integrity sha512-Tz7+z4+Id0MxERw/ozinC5QHJmGLARs9Mpi/7VVfiR+9AHcFGe9q+fjQa30/oPNY8WPuCh5p5uuXmBYAJ3y91Q== + version "1.7.4" + resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-1.7.4.tgz#9226ffd2bd3846ec0e4a4e2bff211612d3aafad5" + integrity sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA== dependencies: "@types/d3-interpolate" "*" "@types/d3-selection" "*" @@ -1751,9 +1748,9 @@ "@types/d3-zoom" "*" "@types/enzyme@^3.1.13": - version "3.1.17" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.17.tgz#41f553bfdbaa00880488feabb3ade47d5489db42" - integrity sha512-pZ+Blk1hODkprPZ9cxXd8njxdBnbLGWOKAmKk0QhpJvWzI4q4F20FHHUnkZXPXJt5WnK6SbbY5lfTKoz1M/CTw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.9.0.tgz#a81c91e2dfd2d70e67f013f2c0e5efed6df05489" + integrity sha512-o0C7ooyBtj9NKyMzn2BWN53W4J21KPhO+/v+qqQX28Pcz0Z1B3DjL9bq2ZR4TN70PVw8O7gkhuFtC7VN3tausg== dependencies: "@types/cheerio" "*" "@types/react" "*" @@ -1764,9 +1761,9 @@ integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== "@types/geojson@*": - version "7946.0.5" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.5.tgz#9aea839ea5af4b1bc079f1d9fa977d48665e02b0" - integrity sha512-rLlMXpd3rdlrp0+xsrda/hFfOpIxgqFcRpk005UKbHtcdFK+QXAjhBAPnvO58qF4O1LdDXrcaiJxMgstCIlcaw== + version "7946.0.6" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.6.tgz#416f388a06b227784a2d91a88a53f14de05cd54b" + integrity sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA== "@types/inquirer@^0.0.43": version "0.0.43" @@ -1787,24 +1784,24 @@ integrity sha512-SVtqEcudm7yjkTwoRA1gC6CNMhGDdMx4Pg8BPdiqI7bXXdCn1BPmtxgeWYQOgDxrq53/5YTlhq5ULxBEAlWIBg== "@types/lodash@^4.14.119": - version "4.14.119" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" - integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== + version "4.14.123" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d" + integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q== -"@types/node@*": - version "11.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.0.tgz#35fea17653490dab82e1d5e69731abfdbf13160d" - integrity sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg== +"@types/node@*", "@types/node@^11.9.5": + version "11.11.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.3.tgz#7c6b0f8eaf16ae530795de2ad1b85d34bf2f5c58" + integrity sha512-wp6IOGu1lxsfnrD+5mX6qwSwWuqsdkKKxTN4aQc4wByHAKZJf9/D4KXPQ1POUjEbnCP5LMggB0OEFNY9OTsMqg== "@types/node@^10.12.18": - version "10.12.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.25.tgz#0d01a7dd6127de60d861ece4a650963042abb538" - integrity sha512-IcvnGLGSQFDvC07Bz2I8SX+QKErDZbUdiQq7S2u3XyzTyJfUmT0sWJMbeQkMzpTAkO7/N7sZpW/arUM2jfKsbQ== + version "10.14.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.1.tgz#8701cd760acc20beba5ffe0b7a1b879f39cb8c41" + integrity sha512-Rymt08vh1GaW4vYB6QP61/5m/CFLGnFZP++bJpWbiNxceNa6RBipDmb413jvtSf/R1gg5a/jQVl2jY4XVRscEA== "@types/node@^8.0.31": - version "8.10.40" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.40.tgz#4314888d5cd537945d73e9ce165c04cc550144a4" - integrity sha512-RRSjdwz63kS4u7edIwJUn8NqKLLQ6LyqF/X4+4jp38MBT3Vwetewi2N4dgJEshLbDwNgOJXNYoOwzVZUSSLhkQ== + version "8.10.44" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.44.tgz#b00cf3595c6a3d75740af9768739a8125053a5a9" + integrity sha512-HY3SK7egERHGUfY8p6ztXIEQWcIPHouYhCGcLAPQin7gE2G/fALFz+epnMwcxKUS6aKqTVoAFdi+t1llQd3xcw== "@types/papaparse@^4.5.9": version "4.5.9" @@ -1814,19 +1811,19 @@ "@types/node" "*" "@types/prop-types@*": - version "15.5.8" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce" - integrity sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw== + version "15.7.0" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.0.tgz#4c48fed958d6dcf9487195a0ef6456d5f6e0163a" + integrity sha512-eItQyV43bj4rR3JPV0Skpl1SncRCdziTEK9/v8VwXmV6d/qOUO8/EuWeHBbCZcsfSHfzI5UyMJLCSXtxxznyZg== "@types/q@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" - integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== "@types/react-color@^2.14.0": - version "2.14.0" - resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-2.14.0.tgz#e01f6069902819fe9f6544375829a485f894206f" - integrity sha512-5UfGjUsu7bXop7K064nNrIGgS7wPjGEY7Or9tAE6BLslDtfhRnAlqWo9N2BIntEZ/3KpXlRnt0MBuc+hZJTevw== + version "2.17.0" + resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-2.17.0.tgz#7f3c958bb43ebeedc7e04309576a235d5233ce9d" + integrity sha512-NQCLW437DXzaV/XvtoH3cBW75f0KQ9ZtFvvXnn7QEudLTR5zGxLdsEhPffrateSizsG2CTml4X+2/2TyEisotQ== dependencies: "@types/react" "*" @@ -1837,10 +1834,10 @@ dependencies: "@types/react" "*" -"@types/react-dom@*", "@types/react-dom@^16.0.9": - version "16.8.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.0.tgz#c565f43f9d2ec911f9e0b8f3b74e25e67879aa3f" - integrity sha512-Jp4ufcEEjVJEB0OHq2MCZcE1u3KYUKO6WnSuiU/tZeYeiZxUoQavfa/TZeiIT+1XoN6l0lQVNM30VINZFDeolQ== +"@types/react-dom@*", "@types/react-dom@^16.8.2": + version "16.8.2" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.2.tgz#9bd7d33f908b243ff0692846ef36c81d4941ad12" + integrity sha512-MX7n1wq3G/De15RGAAqnmidzhr2Y9O/ClxPxyqaNg96pGyeXUYPSvujgzEVpLo9oIP4Wn1UETl+rxTN02KEpBw== dependencies: "@types/react" "*" @@ -1852,25 +1849,25 @@ "@types/react" "*" "@types/react-select@^2.0.4": - version "2.0.11" - resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-2.0.11.tgz#9b2b1fdb12b67a5a617c5f572e15617636cc65af" - integrity sha512-kITn4R50eUJCi2YT3JFZS4z5M2SJJqqYiVUX1HyLSFWbHbF6J25ZPKCCXANQrsnQzSrac2XiNpR5oYBif6l93g== + version "2.0.15" + resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-2.0.15.tgz#51d607667f59a12e980abcc5bbf9636307293e44" + integrity sha512-lbtGCfZ82lKAU0KPoO6M81ZqoT3cOOLWTNkwgmPlBekNBt95ccWItAIKiGZnoO7+gzk413biIxetRSM2CoLL8w== dependencies: "@types/react" "*" "@types/react-dom" "*" "@types/react-transition-group" "*" "@types/react-test-renderer@^16.0.3": - version "16.8.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.8.0.tgz#dbed6549f97a7f114b6920bf553a5db7e61bf83c" - integrity sha512-m563EQSTVB2g6h+FDUH2cgfiRdjL1KHVyi643EQQSFIblMPrWwJh/adqTcMS/FhJHvhEboR4pmhrhEXyHDDsmQ== + version "16.8.1" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.8.1.tgz#96f3ce45a3a41c94eca532a99103dd3042c9d055" + integrity sha512-8gU69ELfJGxzVWVYj4MTtuHxz9nO+d175XeQ1XrXXxesUBsB4KK6OCfzVhEX6leZWWBDVtMJXp/rUjhClzL7gw== dependencies: "@types/react" "*" "@types/react-transition-group@*", "@types/react-transition-group@^2.0.15": - version "2.0.15" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.15.tgz#e5ee3fe558832e141cc6041bdd54caea7b787af8" - integrity sha512-S0QnNzbHoWXDbKBl/xk5dxA4FT+BNlBcI3hku991cl8Cz3ytOkUMcCRtzdX11eb86E131bSsQqy5WrPCdJYblw== + version "2.0.16" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.16.tgz#2dcb9e396ab385ee19c4af1c9caa469a14cd042f" + integrity sha512-FUJEx2BGJPU1qVQoWd9v7wpOwnCPTWhcE4iTaU5prry9SvwiI11lCXOci8Nz9cM/Fuf650l7Skg6nlVeCYjPFA== dependencies: "@types/react" "*" @@ -1882,10 +1879,10 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react@*", "@types/react@16.7.6", "@types/react@^16.7.6": - version "16.7.6" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040" - integrity sha512-QBUfzftr/8eg/q3ZRgf/GaDP6rTYc7ZNem+g4oZM38C9vXyV8AWRWaTQuW5yCoZTsfHrN7b3DeEiUnqH9SrnpA== +"@types/react@*", "@types/react@16.8.8", "@types/react@^16.7.6", "@types/react@^16.8.8": + version "16.8.8" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.8.tgz#4b60a469fd2469f7aa6eaa0f8cfbc51f6d76e662" + integrity sha512-xwEvyet96u7WnB96kqY0yY7qxx/pEpU51QeACkKFtrgjjXITQn0oO1iwPEraXVgh10ZFPix7gs1R4OJXF7P5sg== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -1999,30 +1996,30 @@ "@types/rx-lite-virtualtime" "*" "@types/storybook__addon-actions@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@types/storybook__addon-actions/-/storybook__addon-actions-3.4.1.tgz#8f90d76b023b58ee794170f2fe774a3fddda2c1d" - integrity sha512-An8pNb1/7QhkdOT8Ht5WjJsSxAh2mWti/J4eILwUHpXVZ1j3xlVaOzwTbg8twN4DjgOAggjEDOj6Bx8YOWh9Pg== + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/storybook__addon-actions/-/storybook__addon-actions-3.4.2.tgz#1d08689cc3259269ddb3479a2307c9d16944309e" + integrity sha512-CWxGz2pXav9PHcwrtXmkuH+xJL7sAu2AmIGEbkdT3Xs5jzBPZUEDEN//ZF7o6IOPP/tdXU37K1hrVMt9TDO0Bw== "@types/storybook__addon-info@^3.4.2": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@types/storybook__addon-info/-/storybook__addon-info-3.4.3.tgz#c952150737830d665b8c95a9b652b18226dd8afa" - integrity sha512-j9lhGbdSV6ydZGJ24CShlMEnvYTiGYBM+sfT88XnLm3j/mr/VkBzU9fhGlDPdWk7/rDgqkrztDk/0E3AsxBNRg== + version "3.4.4" + resolved "https://registry.yarnpkg.com/@types/storybook__addon-info/-/storybook__addon-info-3.4.4.tgz#6ded3159f6e279746790566544333f83d06a2703" + integrity sha512-x3AIgYfaojkjxq9WxtQE/ZdQzptisiSzL4J5cwKvO8vIMyHPdzD4lMAOEs+ucP43TxmZldrdutqzzq20jbbZKQ== dependencies: "@types/react" "*" "@types/storybook__react" "*" "@types/storybook__addon-knobs@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/storybook__addon-knobs/-/storybook__addon-knobs-4.0.0.tgz#b218f0d84888833cc8b8d7a7b524175e8bb3030f" - integrity sha512-x3GNz8f0fQv7USvDuVXdZ4p/7nofFHyH6iB/qwR84Yp97xZxOzlQ0SY+6K14tVbdi9P7Qm5DZ2kZr0a+Io8qEQ== + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/storybook__addon-knobs/-/storybook__addon-knobs-4.0.4.tgz#66bd835a086a8d881013a09386da65270713c3a0" + integrity sha512-dN7sRS7pjLHVRY+Cnk7G94kfc7LZQAzEGnqOY0XG9ZdKse25tAKlAyidI7rjxwJ54v1CWswP8p+X4lBznV3cRw== dependencies: "@types/react" "*" "@types/storybook__react" "*" "@types/storybook__react@*", "@types/storybook__react@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/storybook__react/-/storybook__react-4.0.0.tgz#52cc452fbab599568d595075a90142ef4a1233f6" - integrity sha512-Iq3RX953fqZRwWN3jywm8pUx1/Atev+x/9tF7/2CNA+Ii55sGSJJRWMRthUKQXTa3zOexcvfksfVYdUaIZY91w== + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/storybook__react/-/storybook__react-4.0.1.tgz#b6320c9d027b8ee7ef1445fef8b4cba196d48ace" + integrity sha512-knkZErqv8Iy2QbebqBa5tsy2itIMKdO6bcQ7C19nmgTc+j1pnQhXCGcVyARzAQ1/NAuSYudSWQAKG+plgK7hyQ== dependencies: "@types/react" "*" "@types/webpack-env" "*" @@ -2064,9 +2061,9 @@ source-map "^0.6.1" "@types/unist@*", "@types/unist@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.2.tgz#5dc0a7f76809b7518c0df58689cd16a19bd751c6" - integrity sha512-iHI60IbyfQilNubmxsq4zqSjdynlmc2Q/QvH9kjzg9+CCYVVzq1O6tc7VBzSygIwnmOt07w80IG6HDQvjv3Liw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" + integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== "@types/vfile-message@*": version "1.0.1" @@ -2086,9 +2083,9 @@ "@types/vfile-message" "*" "@types/webpack-env@*": - version "1.13.7" - resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.7.tgz#137a4e57aa31ab57b1baf66f5dc3b6bf085e9944" - integrity sha512-rzi6fw7hhxPcCoNVsgysHFlKnhYYvVj7AJwdAO0HQNP5vg9sY0DoRRC1pfuCQm94cOa1sab82HGUtdFlWHIhBg== + version "1.13.9" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.9.tgz#a67287861c928ebf4159a908d1fb1a2a34d4097a" + integrity sha512-p8zp5xqkly3g4cCmo2mKOHI9+Z/kObmDj0BmjbDDJQlgDTiEGTbm17MEwTAusV6XceCy+bNw9q/ZHXHyKo3zkg== "@types/webpack@^3.0.5": version "3.8.17" @@ -2100,15 +2097,6 @@ "@types/uglify-js" "*" source-map "^0.6.0" -"@webassemblyjs/ast@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" - integrity sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA== - dependencies: - "@webassemblyjs/helper-module-context" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/wast-parser" "1.7.11" - "@webassemblyjs/ast@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.6.tgz#3ef8c45b3e5e943a153a05281317474fef63e21e" @@ -2119,42 +2107,44 @@ "@webassemblyjs/wast-parser" "1.7.6" mamacro "^0.0.3" -"@webassemblyjs/floating-point-hex-parser@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313" - integrity sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg== +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" "@webassemblyjs/floating-point-hex-parser@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.6.tgz#7cb37d51a05c3fe09b464ae7e711d1ab3837801f" integrity sha512-VBOZvaOyBSkPZdIt5VBMg3vPWxouuM13dPXGWI1cBh3oFLNcFJ8s9YA7S9l4mPI7+Q950QqOmqj06oa83hNWBA== -"@webassemblyjs/helper-api-error@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a" - integrity sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg== +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== "@webassemblyjs/helper-api-error@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.6.tgz#99b7e30e66f550a2638299a109dda84a622070ef" integrity sha512-SCzhcQWHXfrfMSKcj8zHg1/kL9kb3aa5TN4plc/EREOs5Xop0ci5bdVBApbk2yfVi8aL+Ly4Qpp3/TRAUInjrg== -"@webassemblyjs/helper-buffer@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b" - integrity sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w== +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== "@webassemblyjs/helper-buffer@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.6.tgz#ba0648be12bbe560c25c997e175c2018df39ca3e" integrity sha512-1/gW5NaGsEOZ02fjnFiU8/OEEXU1uVbv2um0pQ9YVL3IHSkyk6xOwokzyqqO1qDZQUAllb+V8irtClPWntbVqw== -"@webassemblyjs/helper-code-frame@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b" - integrity sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw== - dependencies: - "@webassemblyjs/wast-printer" "1.7.11" +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== "@webassemblyjs/helper-code-frame@1.7.6": version "1.7.6" @@ -2163,20 +2153,22 @@ dependencies: "@webassemblyjs/wast-printer" "1.7.6" -"@webassemblyjs/helper-fsm@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181" - integrity sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A== +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" "@webassemblyjs/helper-fsm@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.6.tgz#ae1741c6f6121213c7a0b587fb964fac492d3e49" integrity sha512-HCS6KN3wgxUihGBW7WFzEC/o8Eyvk0d56uazusnxXthDPnkWiMv+kGi9xXswL2cvfYfeK5yiM17z2K5BVlwypw== -"@webassemblyjs/helper-module-context@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209" - integrity sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg== +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== "@webassemblyjs/helper-module-context@1.7.6": version "1.7.6" @@ -2185,25 +2177,23 @@ dependencies: mamacro "^0.0.3" -"@webassemblyjs/helper-wasm-bytecode@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06" - integrity sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ== +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" "@webassemblyjs/helper-wasm-bytecode@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.6.tgz#98e515eaee611aa6834eb5f6a7f8f5b29fefb6f1" integrity sha512-PzYFCb7RjjSdAOljyvLWVqd6adAOabJW+8yRT+NWhXuf1nNZWH+igFZCUK9k7Cx7CsBbzIfXjJc7u56zZgFj9Q== -"@webassemblyjs/helper-wasm-section@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a" - integrity sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-buffer" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/wasm-gen" "1.7.11" +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== "@webassemblyjs/helper-wasm-section@1.7.6": version "1.7.6" @@ -2215,12 +2205,15 @@ "@webassemblyjs/helper-wasm-bytecode" "1.7.6" "@webassemblyjs/wasm-gen" "1.7.6" -"@webassemblyjs/ieee754@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b" - integrity sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ== +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== dependencies: - "@xtuc/ieee754" "^1.2.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" "@webassemblyjs/ieee754@1.7.6": version "1.7.6" @@ -2229,12 +2222,12 @@ dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63" - integrity sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw== +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== dependencies: - "@xtuc/long" "4.2.1" + "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.7.6": version "1.7.6" @@ -2243,29 +2236,22 @@ dependencies: "@xtuc/long" "4.2.1" -"@webassemblyjs/utf8@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82" - integrity sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA== +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.7.6": version "1.7.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.6.tgz#eb62c66f906af2be70de0302e29055d25188797d" integrity sha512-oId+tLxQ+AeDC34ELRYNSqJRaScB0TClUU6KQfpB8rNT6oelYlz8axsPhf6yPTg7PBJ/Z5WcXmUYiHEWgbbHJw== -"@webassemblyjs/wasm-edit@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005" - integrity sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-buffer" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/helper-wasm-section" "1.7.11" - "@webassemblyjs/wasm-gen" "1.7.11" - "@webassemblyjs/wasm-opt" "1.7.11" - "@webassemblyjs/wasm-parser" "1.7.11" - "@webassemblyjs/wast-printer" "1.7.11" +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== "@webassemblyjs/wasm-edit@1.7.6": version "1.7.6" @@ -2281,16 +2267,19 @@ "@webassemblyjs/wasm-parser" "1.7.6" "@webassemblyjs/wast-printer" "1.7.6" -"@webassemblyjs/wasm-gen@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8" - integrity sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA== +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/ieee754" "1.7.11" - "@webassemblyjs/leb128" "1.7.11" - "@webassemblyjs/utf8" "1.7.11" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" "@webassemblyjs/wasm-gen@1.7.6": version "1.7.6" @@ -2303,15 +2292,16 @@ "@webassemblyjs/leb128" "1.7.6" "@webassemblyjs/utf8" "1.7.6" -"@webassemblyjs/wasm-opt@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7" - integrity sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg== +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-buffer" "1.7.11" - "@webassemblyjs/wasm-gen" "1.7.11" - "@webassemblyjs/wasm-parser" "1.7.11" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" "@webassemblyjs/wasm-opt@1.7.6": version "1.7.6" @@ -2323,17 +2313,15 @@ "@webassemblyjs/wasm-gen" "1.7.6" "@webassemblyjs/wasm-parser" "1.7.6" -"@webassemblyjs/wasm-parser@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a" - integrity sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg== +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-api-error" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/ieee754" "1.7.11" - "@webassemblyjs/leb128" "1.7.11" - "@webassemblyjs/utf8" "1.7.11" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" "@webassemblyjs/wasm-parser@1.7.6": version "1.7.6" @@ -2347,17 +2335,17 @@ "@webassemblyjs/leb128" "1.7.6" "@webassemblyjs/utf8" "1.7.6" -"@webassemblyjs/wast-parser@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c" - integrity sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ== +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/floating-point-hex-parser" "1.7.11" - "@webassemblyjs/helper-api-error" "1.7.11" - "@webassemblyjs/helper-code-frame" "1.7.11" - "@webassemblyjs/helper-fsm" "1.7.11" - "@xtuc/long" "4.2.1" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" "@webassemblyjs/wast-parser@1.7.6": version "1.7.6" @@ -2372,14 +2360,17 @@ "@xtuc/long" "4.2.1" mamacro "^0.0.3" -"@webassemblyjs/wast-printer@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813" - integrity sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg== +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/wast-parser" "1.7.11" - "@xtuc/long" "4.2.1" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" "@webassemblyjs/wast-printer@1.7.6": version "1.7.6" @@ -2390,6 +2381,15 @@ "@webassemblyjs/wast-parser" "1.7.6" "@xtuc/long" "4.2.1" +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" + "@webpack-contrib/schema-utils@^1.0.0-beta.0": version "1.0.0-beta.0" resolved "https://registry.yarnpkg.com/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz#bf9638c9464d177b48209e84209e23bee2eb4f65" @@ -2412,6 +2412,11 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.1.tgz#5c85d662f76fa1d34575766c5dcd6615abcd30d8" integrity sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g== +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + JSONStream@^1.3.2: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2485,10 +2490,10 @@ acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0, acorn@^5.5.3, acorn@^5.6.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1, acorn@^6.0.5: - version "6.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.0.tgz#b0a3be31752c97a0f7013c5f4903b71a05db6818" - integrity sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw== +acorn@^6.0.1, acorn@^6.0.5, acorn@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" + integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== acorn@~2.6.4: version "2.6.4" @@ -2512,7 +2517,7 @@ address@1.0.3, address@^1.0.1: resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" integrity sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg== -agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: +agent-base@4, agent-base@^4.1.0, agent-base@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== @@ -2571,9 +2576,9 @@ ajv@^4.7.0: json-stable-stringify "^1.0.1" ajv@^6.1.0, ajv@^6.1.1, ajv@^6.5.5: - version "6.9.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1" - integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA== + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -2651,9 +2656,9 @@ ansi-align@^3.0.0: string-width "^3.0.0" ansi-colors@^3.0.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== ansi-escapes@^1.1.0: version "1.4.0" @@ -2680,10 +2685,10 @@ ansi-regex@^3.0.0, ansi-regex@~3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" - integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-styles@^2.2.1: version "2.2.1" @@ -3001,9 +3006,9 @@ astral-regex@^1.0.0: integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== async-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - integrity sha1-GdOGodntxufByF04iu28xW0zYC0= + version "1.0.2" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.2.tgz#8b8a7ca2a658f927e9f307d6d1a42f4199f0f735" + integrity sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg== async-foreach@^0.1.3: version "0.1.3" @@ -3020,12 +3025,12 @@ async@^1.5.0, async@^1.5.2, async@~1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@^2.0.0, async@^2.1.4, async@^2.5.0, async@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== +async@^2.0.0, async@^2.1.4, async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== dependencies: - lodash "^4.17.10" + lodash "^4.17.11" async@~0.2.6: version "0.2.10" @@ -3060,12 +3065,12 @@ autoprefixer@^6.3.1, autoprefixer@^6.4.0: postcss-value-parser "^3.2.3" autoprefixer@^9.3.1: - version "9.4.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.7.tgz#f997994f9a810eae47b38fa6d8a119772051c4ff" - integrity sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA== + version "9.5.0" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.5.0.tgz#7e51d0355c11596e6cf9a0afc9a44e86d1596c70" + integrity sha512-hMKcyHsZn5+qL6AUeP3c8OyuteZ4VaUlg+fWbyl8z7PqsKHF/Bf8/px3K6AT8aMzDkBo8Bc11245MM+itDBOxQ== dependencies: - browserslist "^4.4.1" - caniuse-lite "^1.0.30000932" + browserslist "^4.4.2" + caniuse-lite "^1.0.30000947" normalize-range "^0.1.2" num2fraction "^1.2.2" postcss "^7.0.14" @@ -4439,14 +4444,14 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" -browserslist@^4.1.0, browserslist@^4.3.4, browserslist@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.4.1.tgz#42e828954b6b29a7a53e352277be429478a69062" - integrity sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A== +browserslist@^4.1.0, browserslist@^4.3.4, browserslist@^4.4.2: + version "4.5.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.5.1.tgz#2226cada1947b33f4cfcf7b608dcb519b6128106" + integrity sha512-/pPw5IAUyqaQXGuD5vS8tcbudyPZ241jk1W5pQBsGDfcjNQt7p8qxZhgMNuygDShte1PibLFexecWUPgmVLfrg== dependencies: - caniuse-lite "^1.0.30000929" - electron-to-chromium "^1.3.103" - node-releases "^1.1.3" + caniuse-lite "^1.0.30000949" + electron-to-chromium "^1.3.116" + node-releases "^1.1.11" bs-logger@0.x: version "0.2.6" @@ -4702,9 +4707,9 @@ camelcase@^4.0.0, camelcase@^4.1.0: integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= camelcase@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + version "5.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.2.0.tgz#e7522abda5ed94cc0489e1b8466610e88404cf45" + integrity sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ== caniuse-api@^1.5.2: version "1.6.1" @@ -4721,10 +4726,10 @@ caniuse-db@1.0.30000772, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, can resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" integrity sha1-UarokXaChureSj2DGep21qAbUSs= -caniuse-lite@^1.0.30000884, caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000932: - version "1.0.30000936" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000936.tgz#5d33b118763988bf721b9b8ad436d0400e4a116b" - integrity sha512-orX4IdpbFhdNO7bTBhSbahp1EBpqzBc+qrvTRVUFfZgA4zta7TdM6PN5ZxkEUgDnz36m+PfWGcdX7AVfFWItJw== +caniuse-lite@^1.0.30000884, caniuse-lite@^1.0.30000947, caniuse-lite@^1.0.30000949: + version "1.0.30000950" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000950.tgz#8c559d66e332b34e919d1086cc6d29c1948856ae" + integrity sha512-Cs+4U9T0okW2ftBsCIHuEYXXkki7mjXmjCh4c6PzYShk04qDEr76/iC7KwhLoWoY65wcra1XOsRD+S7BptEb5A== capture-exit@^1.2.0: version "1.2.0" @@ -4860,9 +4865,9 @@ child-process-promise@^2.2.1: promise-polyfill "^6.0.1" chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.1.tgz#adc39ad55a2adf26548bd2afa048f611091f9184" - integrity sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ== + version "2.1.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.2.tgz#9c23ea40b01638439e0513864d362aeacc5ad058" + integrity sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -4994,10 +4999,10 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" - integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== +cli-spinners@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.0.0.tgz#4b078756fc17a8f72043fdc9f1f14bf4fa87e2df" + integrity sha512-yiEBmhaKPPeBj7wWm4GEdtPZK940p9pl3EANIrnJ3JnvWyrPjcFcsEq6qRUuQ7fzB0+Y82ld3p6B34xo95foWw== cli-table2@~0.2.0: version "0.2.0" @@ -5159,14 +5164,7 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -coa@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" - integrity sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0= - dependencies: - q "^1.1.2" - -coa@~2.0.1: +coa@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== @@ -5175,6 +5173,13 @@ coa@~2.0.1: chalk "^2.4.1" q "^1.1.2" +coa@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" + integrity sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0= + dependencies: + q "^1.1.2" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -5272,7 +5277,7 @@ comma-separated-tokens@^1.0.0: dependencies: trim "0.0.1" -commander@*, commander@2, commander@^2.12.1, commander@^2.13.0, commander@^2.14.1, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0: +commander@*, commander@2, commander@^2.12.1, commander@^2.13.0, commander@^2.14.1, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0, commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== @@ -5282,7 +5287,7 @@ commander@2.11.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== -commander@2.17.x, commander@~2.17.1: +commander@2.17.x: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== @@ -5343,23 +5348,23 @@ compress-commons@^1.2.0: normalize-path "^2.0.0" readable-stream "^2.0.0" -compressible@~2.0.14: - version "2.0.15" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.15.tgz#857a9ab0a7e5a07d8d837ed43fe2defff64fe212" - integrity sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw== +compressible@~2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.16.tgz#a49bf9858f3821b64ce1be0296afc7380466a77f" + integrity sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA== dependencies: - mime-db ">= 1.36.0 < 2" + mime-db ">= 1.38.0 < 2" compression@^1.5.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" - integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== dependencies: accepts "~1.3.5" bytes "3.0.0" - compressible "~2.0.14" + compressible "~2.0.16" debug "2.6.9" - on-headers "~1.0.1" + on-headers "~1.0.2" safe-buffer "5.1.2" vary "~1.1.2" @@ -5505,9 +5510,9 @@ core-js@^1.0.0: integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= core-js@^2.0.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.5.7: - version "2.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.4.tgz#b8897c062c4d769dd30a0ac5c73976c47f92ea0d" - integrity sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A== + version "2.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" + integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -5525,13 +5530,14 @@ cosmiconfig@^4.0.0: require-from-string "^2.0.1" cosmiconfig@^5.0.2, cosmiconfig@^5.0.5, cosmiconfig@^5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" - integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA== + version "5.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.1.0.tgz#6c5c35e97f37f985061cdf653f114784231185cf" + integrity sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q== dependencies: import-fresh "^2.0.0" is-directory "^0.3.1" js-yaml "^3.9.0" + lodash.get "^4.4.2" parse-json "^4.0.0" crc32-stream@^2.0.0: @@ -5726,7 +5732,7 @@ css-loader@^1.0.1: postcss-value-parser "^3.3.0" source-list-map "^2.0.0" -css-select-base-adapter@~0.1.0: +css-select-base-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== @@ -5782,9 +5788,9 @@ css-url-regex@^1.1.0: integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w= css-what@2.1, css-what@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" - integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== cssesc@^0.1.0: version "0.1.0" @@ -5834,7 +5840,7 @@ cssnano@^3.10.0: postcss-value-parser "^3.2.3" postcss-zindex "^2.0.1" -csso@^3.5.0: +csso@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== @@ -5855,16 +5861,16 @@ cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== cssstyle@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.1.1.tgz#18b038a9c44d65f7a8e428a653b9f6fe42faf5fb" - integrity sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog== + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.1.tgz#3aceb2759eaf514ac1a21628d723d6043a819495" + integrity sha512-7DYm8qe+gPx/h77QlCyFmX80+fGaE/6A/Ekl0zaszYOubvySO2saYFdQ78P29D0UsULxFKCetDGNaNRUdSF+2A== dependencies: cssom "0.3.x" csstype@^2.2.0, csstype@^2.5.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01" - integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow== + version "2.6.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.3.tgz#b701e5968245bf9b08d54ac83d00b624e622a9fa" + integrity sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg== currently-unhandled@^0.4.1: version "0.4.1" @@ -6283,28 +6289,28 @@ debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3. dependencies: ms "2.0.0" -debug@3.1.0, debug@=3.1.0: +debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" -debug@^3.1.0, debug@^3.2.5: +debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" -debuglog@^1.0.1: +debuglog@*, debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -6365,12 +6371,12 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -default-gateway@^2.6.0: - version "2.7.2" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f" - integrity sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ== +default-gateway@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== dependencies: - execa "^0.10.0" + execa "^1.0.0" ip-regex "^2.1.0" default-require-extensions@^1.0.0: @@ -6617,11 +6623,11 @@ doctrine@^2.0.0: esutils "^2.0.2" dom-align@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.8.0.tgz#c0e89b5b674c6e836cd248c52c2992135f093654" - integrity sha512-B85D4ef2Gj5lw0rK0KM2+D5/pH7yqNxg2mB+E8uzFaolpm7RQmsxEfjyEuNiF8UBBkffumYDeKRzTzc3LePP+w== + version "1.8.2" + resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.8.2.tgz#fdcd36bce25ba8d34fe3582efd57ac767df490bd" + integrity sha512-17vInOylbB7H4qua7QRsmQT05FFTZemO8BhnOPgF9BPqjAPDyQr/9V8fmJbn05vQ31m2gu3EJSSYN2u94szUZg== -dom-converter@~0.2: +dom-converter@^0.2: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== @@ -6645,12 +6651,12 @@ dom-css@^2.0.0: "@babel/runtime" "^7.1.2" dom-serializer@0, dom-serializer@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" + domelementtype "^1.3.0" + entities "^1.1.1" dom-walk@^0.1.0: version "0.1.1" @@ -6662,16 +6668,11 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.0: +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= - domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -6679,13 +6680,6 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" -domhandler@2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" - integrity sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ= - dependencies: - domelementtype "1" - domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -6693,13 +6687,6 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domutils@1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" - integrity sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU= - dependencies: - domelementtype "1" - domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -6815,10 +6802,10 @@ ejs@^2.5.7, ejs@^2.5.9, ejs@^2.6.1: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.103, electron-to-chromium@^1.3.62: - version "1.3.113" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9" - integrity sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g== +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.116, electron-to-chromium@^1.3.62: + version "1.3.116" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.116.tgz#1dbfee6a592a0c14ade77dbdfe54fef86387d702" + integrity sha512-NKwKAXzur5vFCZYBHpdWjTMO8QptNLNP80nItkSIgUOapPAo9Uia+RvkCaZJtO7fhQaVElSvBPWEc2ku6cKsPA== elegant-spinner@^1.0.1: version "1.0.1" @@ -6918,27 +6905,27 @@ envinfo@^5.7.0: integrity sha512-pwdo0/G3CIkQ0y6PCXq4RdkvId2elvtPCJMG0konqlrfkWQbf1DWeH9K2b/cvu2YgGvPPTOnonZxXM1gikFu1w== enzyme-adapter-react-16@^1.5.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz#6d49a3a31c3a0fccf527610f31b837e0f307128a" - integrity sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg== + version "1.11.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.11.2.tgz#8efeafb27e96873a5492fdef3f423693182eb9d4" + integrity sha512-2ruTTCPRb0lPuw/vKTXGVZVBZqh83MNDnakMhzxhpJcIbneEwNy2Cv0KvL97pl57/GOazJHflWNLjwWhex5AAA== dependencies: - enzyme-adapter-utils "^1.10.0" - function.prototype.name "^1.1.0" + enzyme-adapter-utils "^1.10.1" object.assign "^4.1.0" object.values "^1.1.0" - prop-types "^15.6.2" - react-is "^16.7.0" + prop-types "^15.7.2" + react-is "^16.8.4" react-test-renderer "^16.0.0-0" + semver "^5.6.0" -enzyme-adapter-utils@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz#5836169f68b9e8733cb5b69cad5da2a49e34f550" - integrity sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA== +enzyme-adapter-utils@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.1.tgz#58264efa19a7befdbf964fb7981a108a5452ac96" + integrity sha512-oasinhhLoBuZsIkTe8mx0HiudtfErUtG0Ooe1FOplu/t4c9rOmyG5gtrBASK6u4whHIRWvv0cbZMElzNTR21SA== dependencies: function.prototype.name "^1.1.0" object.assign "^4.1.0" object.fromentries "^2.0.0" - prop-types "^15.6.2" + prop-types "^15.7.2" semver "^5.6.0" enzyme-to-json@^3.3.4: @@ -6949,17 +6936,19 @@ enzyme-to-json@^3.3.4: lodash "^4.17.4" enzyme@^3.6.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.8.0.tgz#646d2d5d0798cb98fdec39afcee8a53237b47ad5" - integrity sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.9.0.tgz#2b491f06ca966eb56b6510068c7894a7e0be3909" + integrity sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg== dependencies: array.prototype.flat "^1.2.1" cheerio "^1.0.0-rc.2" function.prototype.name "^1.1.0" has "^1.0.3" + html-element-map "^1.0.0" is-boolean-object "^1.0.0" is-callable "^1.1.4" is-number-object "^1.0.3" + is-regex "^1.0.4" is-string "^1.0.4" is-subset "^0.1.1" lodash.escape "^4.0.1" @@ -7029,13 +7018,13 @@ es-to-primitive@^1.2.0: is-symbol "^1.0.2" es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.47" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.47.tgz#d24232e1380daad5449a817be19bde9729024a11" - integrity sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw== + version "0.10.49" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.49.tgz#059a239de862c94494fec28f8150c977028c6c5e" + integrity sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" - next-tick "1" + next-tick "^1.0.0" es5-shim@^4.5.10: version "4.5.12" @@ -7069,9 +7058,9 @@ es6-promise@^3.0.2: integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= es6-promise@^4.0.3: - version "4.2.5" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054" - integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg== + version "4.2.6" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" + integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== es6-promisify@^5.0.0: version "5.0.0" @@ -7092,9 +7081,9 @@ es6-set@~0.1.5: event-emitter "~0.3.5" es6-shim@^0.35.3: - version "0.35.4" - resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.4.tgz#8d5a4109756383d3f0323421089c423acf8378f1" - integrity sha512-oJidbXjN/VWXZJs41E9JEqWzcFbjt43JupimIoVX82Thzt5qy1CiYezdhRmWkj3KOuwJ106IG/ZZrcFC6fgIUQ== + version "0.35.5" + resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.5.tgz#46f59dc0a84a1c5029e8ff1166ca0a902077a9ab" + integrity sha512-E9kK/bjtCQRpN1K28Xh4BlmP8egvZBGJJ+9GtnzOwt7mdqtrjHFuVGr7QJfdjBIKqrlU5duPf3pCBoDrkjVYFg== es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: version "3.1.1" @@ -7133,9 +7122,9 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escodegen@^1.9.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" - integrity sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw== + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== dependencies: esprima "^3.1.3" estraverse "^4.2.0" @@ -7155,9 +7144,9 @@ escope@^3.6.0: estraverse "^4.1.1" eslint-scope@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" - integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA== + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -7253,6 +7242,11 @@ estree-walker@^0.5.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" integrity sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig== +estree-walker@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.0.tgz#5d865327c44a618dde5699f763891ae31f257dae" + integrity sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw== + esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -7320,19 +7314,6 @@ exec-sh@^0.2.0: dependencies: merge "^1.2.0" -execa@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -7748,12 +7729,12 @@ find-cache-dir@^1.0.0: pkg-dir "^2.0.0" find-cache-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d" - integrity sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA== + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" - make-dir "^1.0.0" + make-dir "^2.0.0" pkg-dir "^3.0.0" find-npm-prefix@^1.0.2: @@ -7823,9 +7804,9 @@ flatten@^1.0.2: integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= flow-parser@^0.*: - version "0.92.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.92.1.tgz#c81fb52b202c7b1195f253bb01ac6dd2f5c15dfc" - integrity sha512-l6rlAGgMTTRYPOj5XUzbCeZB2bsK2cimPcoQD06YtQC2BI2wu9AhMQH+FkKV2Kd1Aa9EMNxVyF05QzNaiYdObQ== + version "0.95.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.95.1.tgz#e1045d958b7e05f65e4fd3304f266f1fd0619698" + integrity sha512-HKkivrDpJduju6PfvnTdycPp5U8jemBDBwT/PlqiiW4TNOqmlSIbgniMcssl0h7vpoCUrQ/LX3CCIc/QSIDJWA== flush-write-stream@^1.0.0: version "1.1.1" @@ -7841,11 +7822,11 @@ fn-name@~2.0.1: integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc= follow-redirects@^1.0.0, follow-redirects@^1.2.5: - version "1.6.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb" - integrity sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ== + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== dependencies: - debug "=3.1.0" + debug "^3.2.6" for-in@^0.1.3: version "0.1.8" @@ -8053,9 +8034,9 @@ function.prototype.name@^1.1.0: is-callable "^1.1.3" fuse.js@^3.0.1, fuse.js@^3.3.0: - version "3.4.2" - resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.2.tgz#d7a638c436ecd7b9c4c0051478c09594eb956212" - integrity sha512-WVbrm+cAxPtyMqdtL7cYhR7aZJPhtOfjNClPya8GKMVukKDYs7pEnPINeRVX1C9WmWgU8MdYGYbUPAP2AJXdoQ== + version "3.4.4" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.4.tgz#f98f55fcb3b595cf6a3e629c5ffaf10982103e95" + integrity sha512-pyLQo/1oR5Ywf+a/tY8z4JygnIglmRxVUOiyFAbd11o9keUDpUJSMGRWJngcnkURj30kDHPmhoKY8ChJiz3EpQ== g-status@^2.0.2: version "2.0.2" @@ -8675,11 +8656,11 @@ handle-thing@^2.0.0: integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== handlebars@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" - integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== + version "4.1.1" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.1.tgz#6e4e41c18ebe7719ae4d38e5aca3d32fa3dd23d3" + integrity sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA== dependencies: - async "^2.5.0" + neo-async "^2.6.0" optimist "^0.6.1" source-map "^0.6.1" optionalDependencies: @@ -8872,12 +8853,7 @@ hoist-non-react-statics@1.x.x, hoist-non-react-statics@^1.2.0: resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" integrity sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs= -hoist-non-react-statics@^2.5.0: - version "2.5.5" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" - integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - -hoist-non-react-statics@^3.1.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== @@ -8893,9 +8869,9 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" homedir-polyfill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - integrity sha1-TCu8inWJmP7r9e1oWA921GdotLw= + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== dependencies: parse-passwd "^1.0.0" @@ -8924,6 +8900,13 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== +html-element-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.0.tgz#19a41940225153ecdfead74f8509154ff1cdc18b" + integrity sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw== + dependencies: + array-filter "^1.0.0" + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -9006,27 +8989,17 @@ html-webpack-plugin@^4.0.0-beta.2: tapable "^1.1.0" util.promisify "1.0.0" -htmlparser2@^3.9.1: - version "3.10.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" - integrity sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== +htmlparser2@^3.3.0, htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== dependencies: - domelementtype "^1.3.0" + domelementtype "^1.3.1" domhandler "^2.3.0" domutils "^1.5.1" entities "^1.1.1" inherits "^2.0.1" - readable-stream "^3.0.6" - -htmlparser2@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" - integrity sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4= - dependencies: - domelementtype "1" - domhandler "2.1" - domutils "1.1" - readable-stream "1.0" + readable-stream "^3.1.1" http-cache-semantics@3.8.1, http-cache-semantics@^3.8.0, http-cache-semantics@^3.8.1: version "3.8.1" @@ -9061,17 +9034,17 @@ http-proxy-agent@^2.0.0, http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-middleware@~0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz#0987e6bb5a5606e5a69168d8f967a87f15dd8aab" - integrity sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q== +http-proxy-middleware@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== dependencies: - http-proxy "^1.16.2" + http-proxy "^1.17.0" is-glob "^4.0.0" - lodash "^4.17.5" - micromatch "^3.1.9" + lodash "^4.17.11" + micromatch "^3.1.10" -http-proxy@^1.16.2: +http-proxy@^1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== @@ -9241,7 +9214,7 @@ import-local@^2.0.0: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" -imurmurhash@^0.1.4: +imurmurhash@*, imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -9386,13 +9359,13 @@ inquirer@^6.0.0, inquirer@^6.2.0, inquirer@^6.2.2: strip-ansi "^5.0.0" through "^2.3.6" -internal-ip@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-3.0.1.tgz#df5c99876e1d2eb2ea2d74f520e3f669a00ece27" - integrity sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q== +internal-ip@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.2.0.tgz#46e81b638d84c338e5c67e42b1a17db67d0814fa" + integrity sha512-ZY8Rk+hlvFeuMmG5uH1MXhhdeMntmIaxaInvAmzMq/SHV8rv4Kh+6GiQNNDQd0wZFrcO+FiTBo8lui/osKOyJw== dependencies: - default-gateway "^2.6.0" - ipaddr.js "^1.5.2" + default-gateway "^4.0.1" + ipaddr.js "^1.9.0" interpret@^1.0.0, interpret@^1.1.0: version "1.2.0" @@ -9439,7 +9412,7 @@ ipaddr.js@1.8.0: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= -ipaddr.js@^1.5.2: +ipaddr.js@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== @@ -10351,10 +10324,11 @@ jest-worker@^23.2.0: merge-stream "^1.0.1" jest-worker@^24.0.0: - version "24.0.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.0.0.tgz#3d3483b077bf04f412f47654a27bba7e947f8b6d" - integrity sha512-s64/OThpfQvoCeHG963MiEZOAAxu8kHsaL/rCMF7lpdzo7vgF0CtPml9hfguOMgykgH/eOm4jFP4ibfHLruytg== + version "24.4.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.4.0.tgz#fbc452b0120bb5c2a70cdc88fa132b48eeb11dd0" + integrity sha512-BH9X/klG9vxwoO99ZBUbZFfV8qO0XNZ5SIiCyYK2zOuJBl6YJVAeNIQjcoOVNu4HGEHeYEKsUWws8kSlSbZ9YQ== dependencies: + "@types/node" "*" merge-stream "^1.0.1" supports-color "^6.1.0" @@ -10392,9 +10366,9 @@ js-tokens@^3.0.2: integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= js-yaml@^3.12.0, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@^3.7.0, js-yaml@^3.9.0: - version "3.12.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" - integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== + version "3.12.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc" + integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -10795,11 +10769,10 @@ libnpx@^10.2.0: yargs "^11.0.0" lint-staged@^8.1.3: - version "8.1.3" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-8.1.3.tgz#bb069db5466c0fe16710216e633a84f2b362fa60" - integrity sha512-6TGkikL1B+6mIOuSNq2TV6oP21IhPMnV8q0cf9oYZ296ArTVNcbFh1l1pfVOHHbBIYLlziWNsQ2q45/ffmJ4AA== + version "8.1.5" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-8.1.5.tgz#372476fe1a58b8834eb562ed4c99126bd60bdd79" + integrity sha512-e5ZavfnSLcBJE1BTzRTqw6ly8OkqVyO3GL2M6teSmTBYQ/2BuueD5GIt2RPsP31u/vjKdexUyDCxSyK75q4BDA== dependencies: - "@iamstarkov/listr-update-renderer" "0.4.1" chalk "^2.3.1" commander "^2.14.1" cosmiconfig "^5.0.2" @@ -10812,7 +10785,8 @@ lint-staged@^8.1.3: is-glob "^4.0.0" is-windows "^1.0.2" listr "^0.14.2" - lodash "^4.17.5" + listr-update-renderer "^0.5.0" + lodash "^4.17.11" log-symbols "^2.2.0" micromatch "^3.1.8" npm-which "^3.0.1" @@ -10950,11 +10924,11 @@ locate-path@^3.0.0: path-exists "^3.0.0" lock-verify@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.0.2.tgz#148e4f85974915c9e3c34d694b7de9ecb18ee7a8" - integrity sha512-QNVwK0EGZBS4R3YQ7F1Ox8p41Po9VGl2QG/2GsuvTbkJZYSsPeWHKMbbH6iZMCHWSMww5nrJroZYnGzI4cePuw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.1.0.tgz#fff4c918b8db9497af0c5fa7f6d71555de3ceb47" + integrity sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg== dependencies: - npm-package-arg "^5.1.2 || 6" + npm-package-arg "^6.1.0" semver "^5.4.1" lockfile@^1.0.4: @@ -10964,6 +10938,11 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" +lodash._baseindexof@*: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" + integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= + lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -10972,12 +10951,29 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" +lodash._bindcallback@*: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= + +lodash._cacheindexof@*: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" + integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= + +lodash._createcache@*: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" + integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= + dependencies: + lodash._getnative "^3.0.0" + lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@^3.0.0: +lodash._getnative@*, lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -11022,6 +11018,11 @@ lodash.flattendeep@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -11061,16 +11062,16 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.merge@^4.6.1: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" - integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ== - lodash.mergewith@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" integrity sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ== +lodash.restparam@*: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= + lodash.some@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" @@ -11233,6 +11234,14 @@ make-dir@^1.0.0, make-dir@^1.1.0: dependencies: pify "^3.0.0" +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + make-error@1.x, make-error@^1.1.1, make-error@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" @@ -11419,12 +11428,12 @@ mem@^1.1.0: mimic-fn "^1.0.0" mem@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a" - integrity sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg== + version "4.2.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.2.0.tgz#5ee057680ed9cb8dad8a78d820f9a8897a102025" + integrity sha512-5fJxa68urlY0Ir8ijatKa3eRz5lwXnRCTvo9+TbTGAuTFJOwpGcY0X05moBd0nW45965Njt4CDI2GFQoG8DvqA== dependencies: map-age-cleaner "^0.1.1" - mimic-fn "^1.0.0" + mimic-fn "^2.0.0" p-is-promise "^2.0.0" memoize-one@^4.0.0: @@ -11437,7 +11446,7 @@ memoize-one@^5.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.0.tgz#d55007dffefb8de7546659a1722a5d42e128286e" integrity sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw== -memory-fs@^0.4.0, memory-fs@~0.4.1: +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -11543,22 +11552,17 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.36.0 < 2": +"mime-db@>= 1.38.0 < 2", mime-db@~1.38.0: version "1.38.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== -mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" - integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== - mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" - integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== + version "2.1.22" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" + integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== dependencies: - mime-db "~1.37.0" + mime-db "~1.38.0" mime@1.4.1: version "1.4.1" @@ -11575,6 +11579,11 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.0.0.tgz#0913ff0b121db44ef5848242c38bbb35d44cabde" + integrity sha512-jbex9Yd/3lmICXwYT6gA/j2mNQGU48wCh/VzRd+/Y/PjYQtlg1gLMdZqvu9s/xH7qKvngxRObl56XZR609IMbA== + mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -11768,9 +11777,9 @@ mousetrap-global-bind@^1.1.0: integrity sha1-zX3pIivQZG+i4BDVTISnTCaojt0= mousetrap@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.2.tgz#caadd9cf886db0986fb2fee59a82f6bd37527587" - integrity sha512-jDjhi7wlHwdO6q6DS7YRmSHcuI+RVxadBkLt3KHrhd3C2b+w5pKefg3oj5beTcHZyVFA9Aksf+yEE1y5jxUjVA== + version "1.6.3" + resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.3.tgz#80fee49665fd478bccf072c9d46bdf1bfed3558a" + integrity sha512-bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA== move-concurrently@^1.0.1: version "1.0.1" @@ -11833,9 +11842,9 @@ mute-stream@~0.0.4: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.10.0, nan@^2.6.2, nan@^2.9.2: - version "2.12.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" - integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== + version "2.13.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.1.tgz#a15bee3790bde247e8f38f1d446edcdaeb05f2dd" + integrity sha512-I6YB/YEuDeUZMmhscXKxGgZlFnhsn5y0hgOZBadkzfTRrZBtJDZeg6eQf7PYMIEclwmorTKK8GztsyOUSVBREA== nanomatch@^1.2.9: version "1.2.13" @@ -11891,7 +11900,7 @@ negotiator@0.6.1: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= -neo-async@^2.5.0: +neo-async@^2.5.0, neo-async@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== @@ -11901,7 +11910,7 @@ nested-object-assign@^1.0.1: resolved "https://registry.yarnpkg.com/nested-object-assign/-/nested-object-assign-1.0.3.tgz#5aca69390d9affe5a612152b5f0843ae399ac597" integrity sha512-kgq1CuvLyUcbcIuTiCA93cQ2IJFSlRwXcN+hLcb2qLJwC2qrePHGZZa7IipyWqaWF6tQjdax2pQnVxdq19Zzwg== -next-tick@1: +next-tick@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= @@ -12106,10 +12115,10 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-releases@^1.0.0-alpha.11, node-releases@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.7.tgz#b09a10394d0ed8f7778f72bb861dde68b146303b" - integrity sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA== +node-releases@^1.0.0-alpha.11, node-releases@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.11.tgz#9a0841a4b0d92b7d5141ed179e764f42ad22724a" + integrity sha512-8v1j5KfP+s5WOTa1spNUAOfreajQPN12JXbRR0oDE+YrJBQCXBnNqUDj27EKpPLOoSiU3tKi3xGPB+JaOdUEQQ== dependencies: semver "^5.3.0" @@ -12299,7 +12308,7 @@ npm-logical-tree@^1.2.1: resolved "https://registry.yarnpkg.com/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz#44610141ca24664cad35d1e607176193fd8f5b88" integrity sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg== -"npm-package-arg@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", "npm-package-arg@^5.1.2 || 6", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: +"npm-package-arg@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA== @@ -12310,9 +12319,9 @@ npm-logical-tree@^1.2.1: validate-npm-package-name "^3.0.0" npm-packlist@^1.1.10, npm-packlist@^1.1.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.3.0.tgz#7f01e8e44408341379ca98cfd756e7b29bd2626c" - integrity sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -12542,9 +12551,9 @@ number-is-nan@^1.0.0: integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= nwsapi@^2.0.7: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.0.tgz#781065940aed90d9bb01ca5d0ce0fcf81c32712f" - integrity sha512-ZG3bLAvdHmhIjaQ/Db1qvBxsGvFMLIRpQszyqbg31VJ53UP++uZX1/gf3Ut96pdwN9AuDwlMqIYLm0UPCdUeHg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.1.tgz#08d6d75e69fd791bdea31507ffafe8c843b67e9c" + integrity sha512-T5GaA1J/d34AC8mkrFD2O0DR17kwJ702ZOtJOsS8RpbsQZVOC2/xYFb1i/cw+xdM54JIlMuojjDOYct8GIWtwg== oauth-sign@~0.9.0: version "0.9.0" @@ -12672,10 +12681,10 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c= +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: version "1.4.0" @@ -12706,13 +12715,20 @@ opener@~1.4.3: resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= -opn@5.4.0, opn@^5.1.0, opn@^5.3.0, opn@^5.4.0: +opn@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== dependencies: is-wsl "^1.1.0" +opn@^5.1.0, opn@^5.3.0, opn@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optimist@^0.6.1, optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -12742,13 +12758,13 @@ optionator@^0.8.1: wordwrap "~1.0.0" ora@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.1.0.tgz#dbedd8c03b5d017fb67083e87ee52f5ec89823ed" - integrity sha512-vRBPaNCclUi8pUxRF/G8+5qEQkc6EgzKK1G2ZNJUIGu088Un5qIxFXeDgymvPRM9nmrcUOGzQgS1Vmtz+NtlMw== + version "3.2.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.2.0.tgz#67e98a7e11f7f0ac95deaaaf11bb04de3d09e481" + integrity sha512-XHMZA5WieCbtg+tu0uPF8CjvwQdNzKCX6BVh3N6GFsEXH40mTk5dsw/ya1lBTUGJslcEFJFQ8cBhOgkkZXQtMA== dependencies: chalk "^2.4.2" cli-cursor "^2.1.0" - cli-spinners "^1.3.1" + cli-spinners "^2.0.0" log-symbols "^2.2.0" strip-ansi "^5.0.0" wcwidth "^1.0.1" @@ -12870,9 +12886,9 @@ p-limit@^1.1.0: p-try "^1.0.0" p-limit@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.1.0.tgz#1d5a0d20fb12707c758a655f6bbc4386b5930d68" - integrity sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g== + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== dependencies: p-try "^2.0.0" @@ -12925,9 +12941,9 @@ p-try@^1.0.0: integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" - integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.1.0.tgz#c1a0f1030e97de018bb2c718929d2af59463e505" + integrity sha512-H2RyIJ7+A3rjkwKC2l5GGtU4H1vkxKCAGsWasNVd0Set+6i4znxbWy6/j16YDPJDWxhsgZiKAstMEP8wCdSpjA== package-json@^4.0.0: version "4.0.1" @@ -13001,9 +13017,9 @@ pacote@^8.1.6: which "^1.3.0" pako@~1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.8.tgz#6844890aab9c635af868ad5fecc62e8acbba3ea4" - integrity sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA== + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== papaparse@^4.6.3: version "4.6.3" @@ -13027,9 +13043,9 @@ param-case@2.1.x, param-case@^2.1.0: no-case "^2.2.0" parse-asn1@^5.0.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.3.tgz#1600c6cc0727365d68b97f3aa78939e735a75204" - integrity sha512-VrPoetlz7B/FqjBLD2f5wBVZvsZVLnRUrxVLfRYhGXCODa/NWE4p3Wp+6+aV3ZPL3KM7/OZmxDIwwijD7yuucg== + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -13224,6 +13240,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -13897,11 +13918,12 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: - version "15.7.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.1.tgz#2fa61e0a699d428b40320127733ee2931f05d9d1" - integrity sha512-f8Lku2z9kERjOCcnDOPm68EBJAO2K00Q5mSgPAUE/gJuBgsYLbVy6owSrtcHj90zt8PvW+z0qaIIgsIhHOa1Qw== +prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== dependencies: + loose-envify "^1.4.0" object-assign "^4.1.1" react-is "^16.8.1" @@ -14055,9 +14077,9 @@ query-string@^5.0.1: strict-uri-encode "^1.0.0" query-string@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.2.0.tgz#468edeb542b7e0538f9f9b1aeb26f034f19c86e1" - integrity sha512-5wupExkIt8RYL4h/FE+WTg3JHk62e6fFPWtAZA9J5IWK1PfTfKkMS93HBUHcFpeYi9KsY5pFbh+ldvEyaz5MyA== + version "6.4.0" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.4.0.tgz#1566c0cec3a2da2d82c222ed3f9e2a921dba5e6a" + integrity sha512-Werid2I41/tJTqOGPJ3cC3vwrIh/8ZupBQbp7BSsqXzr+pTin3aMJ/EZb8UEuk7ZO3VqQFvq2qck/ihc6wqIdw== dependencies: decode-uri-component "^0.2.0" strict-uri-encode "^2.0.0" @@ -14117,9 +14139,9 @@ randomatic@^3.0.0: math-random "^1.0.1" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" - integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" @@ -14152,9 +14174,9 @@ raw-loader@^0.5.1: integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= rc-align@^2.4.0: - version "2.4.3" - resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.4.3.tgz#b9b3c2a6d68adae71a8e1d041cd5e3b2a655f99a" - integrity sha512-h5KgyB5IXYR7iKpYFcMr54cuQ2eozPCZ11kbXPG5+6CWvmyJ+c0R/yjndVndiNk2G3MKcTMbJNdDv5DIckLAxQ== + version "2.4.5" + resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.4.5.tgz#c941a586f59d1017f23a428f0b468663fb7102ab" + integrity sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw== dependencies: babel-runtime "^6.26.0" dom-align "^1.7.0" @@ -14312,28 +14334,28 @@ react-docgen@^3.0.0: node-dir "^0.1.10" recast "^0.16.0" -react-dom@^16.6.3, react-dom@^16.7.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.1.tgz#ec860f98853d09d39bafd3a6f1e12389d283dbb4" - integrity sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg== +react-dom@^16.6.3, react-dom@^16.7.0, react-dom@^16.8.4: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.4.tgz#1061a8e01a2b3b0c8160037441c3bf00a0e3bc48" + integrity sha512-Ob2wK7XG2tUDt7ps7LtLzGYYB6DXMCLj0G5fO6WeEICtT4/HdpOi7W/xLzZnR6RCG1tYza60nMdqtxzA8FaPJQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.1" + scheduler "^0.13.4" react-draggable@3.x, "react-draggable@^2.2.6 || ^3.0.3": - version "3.1.1" - resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.1.1.tgz#ed1db43e09b146805f6d158296e58eae4f768d36" - integrity sha512-tqIgDUm4XPSFbxelYpcsnayPU79P26ChnszDl5/RDFKfMuHnRxypS+OFfEyAEO1CtqaB3lrecQ2dyNIE2G0TlQ== + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.2.1.tgz#45d09a9a227988dc85674b23ab3c75b6e820dae5" + integrity sha512-r+3Bs9InID2lyIEbR8UIRVtpn4jgu1ArFEZgIy8vibJjijLSdNLX7rH9U68BBVD4RD9v44RXbaK4EHLyKXzNQw== dependencies: classnames "^2.2.5" prop-types "^15.6.0" react-error-overlay@^5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.3.tgz#16fcbde75ed4dc6161dc6dc959b48e92c6ffa9ad" - integrity sha512-GoqeM3Xadie7XUApXOjkY3Qhs8RkwB/Za4WMedBGrOKH1eTuKGyoAECff7jiVonJchOx6KZ9i8ILO5XIoHB+Tg== + version "5.1.4" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.4.tgz#88dfb88857c18ceb3b9f95076f850d7121776991" + integrity sha512-fp+U98OMZcnduQ+NSEiQa4s/XMsbp+5KlydmkbESOw4P69iWZ68ZMFM5a2BuE0FgqPBKApJyRuYHR95jM8lAmg== react-fuzzy@^0.5.2: version "0.5.2" @@ -14365,15 +14387,15 @@ react-highlight-words@0.11.0: prop-types "^15.5.8" react-hot-loader@^4.3.6: - version "4.6.5" - resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.6.5.tgz#059619c8ac2aae9c6e8178ddc2eac535093cdd2e" - integrity sha512-ZPAJEWVd8KDdm6dcK0iWrnJiGHruLrcbkIpqn/wQmNjnROpsm2nzrWh23Yh3I/XAjB+35pMa/ZgariwGqwFD9A== + version "4.8.0" + resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.8.0.tgz#0b7c7dd9407415e23eb8246fdd28b0b839f54cb6" + integrity sha512-HY9F0vITYSVmXhAR6tPkMk240nxmoH8+0rca9iO2B82KVguiCiBJkieS0Wb4CeSIzLWecYx3iOcq8dcbnp0bxA== dependencies: fast-levenshtein "^2.0.6" global "^4.3.0" - hoist-non-react-statics "^2.5.0" + hoist-non-react-statics "^3.3.0" loader-utils "^1.1.0" - lodash.merge "^4.6.1" + lodash "^4.17.11" prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" shallowequal "^1.0.2" @@ -14400,10 +14422,10 @@ react-inspector@^2.3.0: is-dom "^1.0.9" prop-types "^15.6.1" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.1.tgz#a80141e246eb894824fb4f2901c0c50ef31d4cdb" - integrity sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA== +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2" + integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA== react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" @@ -14490,21 +14512,21 @@ react-style-proptype@^3.0.0: prop-types "^15.5.4" react-table@^6.8.6: - version "6.9.1" - resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.9.1.tgz#50a1713a4de90089f14dcb8cf0f313992ad93a20" - integrity sha512-QWAwEX24kZPjimhrqRMcdJRi0LX+4MWJG7p9WmAPrfEoYG2HTR1odxa5jLPu7sKVkgsUp0PtZ2jxQ4aXabRJNA== + version "6.9.2" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.9.2.tgz#6a59adfeb8d5deced288241ed1c7847035b5ec5f" + integrity sha512-sTbNHU8Um0xRtmCd1js873HXnXaMWeBwZoiljuj0l1d44eaqjKyYPK/3HCBbJg1yeE2O5pQJ3Km0tlm9niNL9w== dependencies: classnames "^2.2.5" react-test-renderer@^16.0.0-0, react-test-renderer@^16.5.0, react-test-renderer@^16.7.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.1.tgz#72845ad9269be526126e97853311982f781767be" - integrity sha512-Bd21TN3+YVl6GZwav6O0T6m5UwGfOj+2+xZH5VH93ToD6M5uclN/c+R1DGX49ueG413KZPUx7Kw3sOYz2aJgfg== + version "16.8.4" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.4.tgz#abee4c2c3bf967a8892a7b37f77370c5570d5329" + integrity sha512-jQ9Tf/ilIGSr55Cz23AZ/7H3ABEdo9oy2zF9nDHZyhLHDSLKuoILxw2ifpBfuuwQvj4LCoqdru9iZf7gwFH28A== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" - react-is "^16.8.1" - scheduler "^0.13.1" + react-is "^16.8.4" + scheduler "^0.13.4" react-textarea-autosize@^7.0.4: version "7.1.0" @@ -14515,9 +14537,9 @@ react-textarea-autosize@^7.0.4: prop-types "^15.6.0" react-transition-group@^2.0.0, react-transition-group@^2.2.1: - version "2.5.3" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.3.tgz#26de363cab19e5c88ae5dbae105c706cf953bb92" - integrity sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg== + version "2.6.1" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.6.1.tgz#abf4a95e2f13fb9ba83a970a896fedbc5c4856a2" + integrity sha512-9DHwCy0aOYEe35frlEN68N9ut/THDQBLnVoQuKTvzF4/s3tk7lqkefCqxK2Nv96fOh6JXk6tQtliygk6tl3bQA== dependencies: dom-helpers "^3.3.1" loose-envify "^1.4.0" @@ -14549,15 +14571,15 @@ react-virtualized@^9.21.0: prop-types "^15.6.0" react-lifecycles-compat "^3.0.4" -react@^16.6.3, react@^16.7.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.1.tgz#ae11831f6cb2a05d58603a976afc8a558e852c4a" - integrity sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ== +react@^16.6.3, react@^16.7.0, react@^16.8.4: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.4.tgz#fdf7bd9ae53f03a9c4cd1a371432c206be1c4768" + integrity sha512-0GQ6gFXfUH7aZcjGVymlPOASTuSjlQL4ZtVC5YKH+3JL6bBLCVO21DknzmaPlI90LN253ojj02nsapy+j7wIjg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.1" + scheduler "^0.13.4" reactcss@^1.2.0: version "1.2.3" @@ -14608,9 +14630,9 @@ read-installed@~4.0.3: graceful-fs "^4.1.2" read-package-tree@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.2.1.tgz#6218b187d6fac82289ce4387bbbaf8eef536ad63" - integrity sha512-2CNoRoh95LxY47LvqrehIAfUVda2JbuFE/HaGYs42bNrGG+ojbw1h3zOcPcQ+1GQ3+rkzNndZn85u1XyZ3UsIA== + version "5.2.2" + resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.2.2.tgz#4b6a0ef2d943c1ea36a578214c9a7f6b7424f7a8" + integrity sha512-rW3XWUUkhdKmN2JKB4FL563YAgtINifso5KShykufR03nJ5loGFlkUMe1g/yxmqX073SoYYTsgXu7XdDinKZuA== dependencies: debuglog "^1.0.1" dezalgo "^1.0.0" @@ -14681,20 +14703,10 @@ read@1, read@~1.0.1, read@~1.0.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.0: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^3.0.6: - version "3.1.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" - integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== +readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" + integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -14710,7 +14722,7 @@ readable-stream@~1.1.10: isarray "0.0.1" string_decoder "~0.10.x" -readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c= @@ -14866,10 +14878,10 @@ redux@^4.0.0, redux@^4.0.1: loose-envify "^1.4.0" symbol-observable "^1.2.0" -regenerate-unicode-properties@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" - integrity sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw== +regenerate-unicode-properties@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662" + integrity sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ== dependencies: regenerate "^1.4.0" @@ -14897,10 +14909,10 @@ regenerator-transform@^0.10.0: babel-types "^6.19.0" private "^0.1.6" -regenerator-transform@^0.13.3: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb" - integrity sha512-5ipTrZFSq5vU2YoGoww4uaRVAK4wyYC4TSICibbfEPOruUu8FFP7ErV0BjmbIOEpn3O/k9na9UEdYR/3m7N6uA== +regenerator-transform@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb" + integrity sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A== dependencies: private "^0.1.6" @@ -14927,13 +14939,9 @@ regexp-replace-loader@^1.0.1: loader-utils "^1.0.2" regexp-tree@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.1.tgz#27b455f9b138ca2e84c090e9aff1ffe2a04d97fa" - integrity sha512-HwRjOquc9QOwKTgbxvZTcddS5mlNlwePMQ3NFL8broajMLD5CXDAqas8Y5yxJH5QtZp5iRor3YCILd5pz71Cgw== - dependencies: - cli-table3 "^0.5.0" - colors "^1.1.2" - yargs "^12.0.5" + version "0.1.5" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.5.tgz#7cd71fca17198d04b4176efd79713f2998009397" + integrity sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ== regexp.prototype.flags@^1.2.0: version "1.2.0" @@ -14961,16 +14969,16 @@ regexpu-core@^2.0.0: regjsparser "^0.1.4" regexpu-core@^4.1.3, regexpu-core@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.4.0.tgz#8d43e0d1266883969720345e70c275ee0aec0d32" - integrity sha512-eDDWElbwwI3K0Lo6CqbQbA6FwgtCz4kYTarrri1okfkRLZAqstU+B3voZBCjg8Fl6iq0gXrJG6MvRgLthfvgOA== + version "4.5.4" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae" + integrity sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ== dependencies: regenerate "^1.4.0" - regenerate-unicode-properties "^7.0.0" + regenerate-unicode-properties "^8.0.2" regjsgen "^0.5.0" regjsparser "^0.6.0" unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.0.2" + unicode-match-property-value-ecmascript "^1.1.0" registry-auth-token@^3.0.1: version "3.3.2" @@ -15044,13 +15052,13 @@ render-fragment@^0.1.1: integrity sha512-+DnAcalJYR8GE5VRuQGGu78Q0GDe8EXnkuk4DF8gbAhIeS6LRt4j+aaggLLj4PtQVfXNC61McXvXI58WqmRleQ== renderkid@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.2.tgz#12d310f255360c07ad8fde253f6c9e9de372d2aa" - integrity sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== dependencies: css-select "^1.1.0" - dom-converter "~0.2" - htmlparser2 "~3.3.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" strip-ansi "^3.0.0" utila "^0.4.0" @@ -15088,21 +15096,21 @@ request-progress@^2.0.1: dependencies: throttleit "^1.0.0" -request-promise-core@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" - integrity sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY= +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== dependencies: - lodash "^4.13.1" + lodash "^4.17.11" request-promise-native@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" - integrity sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU= + version "1.0.7" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" + integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== dependencies: - request-promise-core "1.1.1" - stealthy-require "^1.1.0" - tough-cookie ">=2.3.3" + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" request@^2.74.0, request@^2.81.0, request@^2.85.0, request@^2.87.0, request@^2.88.0: version "2.88.0" @@ -15305,23 +15313,23 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: inherits "^2.0.1" rollup-plugin-commonjs@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.0.tgz#4604e25069e0c78a09e08faa95dc32dec27f7c89" - integrity sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA== + version "9.2.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.1.tgz#bb151ca8fa23600c7a03e25f9f0a45b1ee922dac" + integrity sha512-X0A/Cp/t+zbONFinBhiTZrfuUaVwRIp4xsbKq/2ohA2CDULa/7ONSJTelqxon+Vds2R2t2qJTqJQucKUC8GKkw== dependencies: estree-walker "^0.5.2" magic-string "^0.25.1" - resolve "^1.8.1" + resolve "^1.10.0" rollup-pluginutils "^2.3.3" rollup-plugin-node-resolve@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.0.tgz#9bc6b8205e9936cc0e26bba2415f1ecf1e64d9b2" - integrity sha512-7Ni+/M5RPSUBfUaP9alwYQiIKnKeXCOHiqBpKUl9kwp3jX5ZJtgXAait1cne6pGEVUUztPD6skIKH9Kq9sNtfw== + version "4.0.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.1.tgz#f95765d174e5daeef9ea6268566141f53aa9d422" + integrity sha512-fSS7YDuCe0gYqKsr5OvxMloeZYUSgN43Ypi1WeRZzQcWtHgFayV5tUSPYpxuaioIIWaBXl6NrVk0T2/sKwueLg== dependencies: builtin-modules "^3.0.0" is-module "^1.0.0" - resolve "^1.8.1" + resolve "^1.10.0" rollup-plugin-sourcemaps@^0.4.2: version "0.4.2" @@ -15342,9 +15350,9 @@ rollup-plugin-terser@^4.0.4: terser "^3.14.1" rollup-plugin-typescript2@^0.19.2: - version "0.19.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.19.2.tgz#87d9c799cd6e02efbedbba25af12753a1e92b6c2" - integrity sha512-DRG7SaYX0QzBIz6rII5nm1UkiceS95r8mJjujugybyIueNF3auvzGTHMK62O7As/0q5RHjXsOguWOUv+KJKLFA== + version "0.19.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.19.3.tgz#713063233461765f030a2baa2640905c2656164f" + integrity sha512-lsRqfBCZhMl/tq9AT5YnQvzQWzXtnx3EQYFcHD72gul7nyyoOrzx5yCEH20smpw58v6UkHHZz03FbdLEPoHWjA== dependencies: fs-extra "7.0.1" resolve "1.8.1" @@ -15361,7 +15369,7 @@ rollup-plugin-visualizer@^0.9.2: source-map "^0.7.3" typeface-oswald "0.0.54" -rollup-pluginutils@2.3.3, rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.3: +rollup-pluginutils@2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz#3aad9b1eb3e7fe8262820818840bf091e5ae6794" integrity sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA== @@ -15369,14 +15377,22 @@ rollup-pluginutils@2.3.3, rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.3: estree-walker "^0.5.2" micromatch "^2.3.11" +rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.3: + version "2.5.0" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.5.0.tgz#23be0f05ac3972ea7b08fc7870cb91fde5b23a09" + integrity sha512-9Muh1H+XB5f5ONmKMayUoTYR1EZwHbwJJ9oZLrKT5yuTf/RLIQ5mYIGsrERquVucJmjmaAW0Y7+6Qo1Ep+5w3Q== + dependencies: + estree-walker "^0.6.0" + micromatch "^3.1.10" + rollup@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.1.2.tgz#8d094b85683b810d0c05a16bd7618cf70d48eba7" - integrity sha512-OkdMxqMl8pWoQc5D8y1cIinYQPPLV8ZkfLgCzL6SytXeNA2P7UHynEQXI9tYxuAjAMsSyvRaWnyJDLHMxq0XAg== + version "1.6.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.6.0.tgz#4329f4634718197c678d18491724d50d8b7ee76c" + integrity sha512-qu9iWyuiOxAuBM8cAwLuqPclYdarIpayrkfQB7aTGTiyYPbvx+qVF33sIznfq4bxZCiytQux/FvZieUBAXivCw== dependencies: "@types/estree" "0.0.39" - "@types/node" "*" - acorn "^6.0.5" + "@types/node" "^11.9.5" + acorn "^6.1.1" rst-selector-parser@^2.2.3: version "2.2.3" @@ -15542,10 +15558,10 @@ sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.1.tgz#1a217df1bfaabaf4f1b92a9127d5d732d85a9591" - integrity sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A== +scheduler@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.4.tgz#8fef05e7a3580c76c0364d2df5e550e4c9140298" + integrity sha512-cvSOlRPxOHs5dAhP9yiS/6IDmVAVxmk33f0CtTJRkmUWcb1Us+t7b1wqdzoC0REw2muC9V5f1L/w5R5uKGaepA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -15880,9 +15896,9 @@ slash@^2.0.0: integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== slate-base64-serializer@^0.2.36: - version "0.2.95" - resolved "https://registry.yarnpkg.com/slate-base64-serializer/-/slate-base64-serializer-0.2.95.tgz#018ccf749f0f0f62fc2d5f4722fd770d359cf76b" - integrity sha512-WK8roQUQBM7lHXNS6HYNmMSJ5tJmuoLeZkHJEHWCEl+1op1m5sC2onzBfpIRNP8AijlZ3m+lGlxfLO+3VtBMxw== + version "0.2.97" + resolved "https://registry.yarnpkg.com/slate-base64-serializer/-/slate-base64-serializer-0.2.97.tgz#232220da1320cdc918369834940f1c997b1e80f9" + integrity sha512-f8TnU8rPz8qzKsGaJJwEzPoR5JQ55kB03nO/aIeXRVg4PPY/Rp9VaEcBP6vmeimH51gf3vpe8S5RUITbW9pXHw== dependencies: isomorphic-base64 "^1.0.2" @@ -16064,12 +16080,12 @@ socks-proxy-agent@^3.0.1: socks "^1.1.10" socks-proxy-agent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" - integrity sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== dependencies: - agent-base "~4.2.0" - socks "~2.2.0" + agent-base "~4.2.1" + socks "~2.3.2" socks@^1.1.10: version "1.1.10" @@ -16079,10 +16095,10 @@ socks@^1.1.10: ip "^1.1.4" smart-buffer "^1.0.13" -socks@~2.2.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.3.tgz#7399ce11e19b2a997153c983a9ccb6306721f2dc" - integrity sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA== +socks@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" + integrity sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ== dependencies: ip "^1.1.5" smart-buffer "4.0.2" @@ -16142,10 +16158,10 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.3, source-map-support@^0.5.6, source-map-support@~0.5.9: - version "0.5.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" - integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ== +source-map-support@^0.5.3, source-map-support@^0.5.6, source-map-support@~0.5.10: + version "0.5.11" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.11.tgz#efac2ce0800355d026326a0ca23e162aeac9a4e2" + integrity sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -16296,7 +16312,7 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -stable@~0.1.3, stable@~0.1.5, stable@~0.1.6: +stable@^0.1.8, stable@~0.1.3, stable@~0.1.5: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== @@ -16341,7 +16357,7 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stealthy-require@^1.1.0: +stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= @@ -16437,13 +16453,13 @@ string-width@^1.0.1, string-width@^1.0.2: strip-ansi "^4.0.0" string-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.0.0.tgz#5a1690a57cc78211fffd9bf24bbe24d090604eb1" - integrity sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew== + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.0.0" + strip-ansi "^5.1.0" string.prototype.matchall@^3.0.0: version "3.0.1" @@ -16544,12 +16560,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" - integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow== +strip-ansi@^5.0.0, strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: - ansi-regex "^4.0.0" + ansi-regex "^4.1.0" strip-ansi@~0.1.0: version "0.1.1" @@ -16650,7 +16666,7 @@ supports-color@^4.5.0: dependencies: has-flag "^2.0.0" -supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -16686,22 +16702,22 @@ svgo@^0.7.0: whet.extend "~0.9.9" svgo@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985" - integrity sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g== + version "1.2.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.2.0.tgz#305a8fc0f4f9710828c65039bb93d5793225ffc3" + integrity sha512-xBfxJxfk4UeVN8asec9jNxHiv3UAMv/ujwBWGYvQhhMb2u3YTGKkiybPcLFDLq7GLLWE9wa73e0/m8L5nTzQbw== dependencies: - coa "~2.0.1" - colors "~1.1.2" + chalk "^2.4.1" + coa "^2.0.2" css-select "^2.0.0" - css-select-base-adapter "~0.1.0" + css-select-base-adapter "^0.1.1" css-tree "1.0.0-alpha.28" css-url-regex "^1.1.0" - csso "^3.5.0" + csso "^3.5.1" js-yaml "^3.12.0" mkdirp "~0.5.1" - object.values "^1.0.4" + object.values "^1.1.0" sax "~1.2.4" - stable "~0.1.6" + stable "^0.1.8" unquote "~1.1.1" util.promisify "~1.0.0" @@ -16828,9 +16844,9 @@ term-size@^1.2.0: execa "^0.7.0" terser-webpack-plugin@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz#9bff3a891ad614855a7dde0d707f7db5a927e3d9" - integrity sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8" + integrity sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA== dependencies: cacache "^11.0.2" find-cache-dir "^2.0.0" @@ -16842,13 +16858,13 @@ terser-webpack-plugin@^1.1.0: worker-farm "^1.5.2" terser@^3.14.1, terser@^3.16.1: - version "3.16.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.16.1.tgz#5b0dd4fa1ffd0b0b43c2493b2c364fd179160493" - integrity sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow== + version "3.17.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" + integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== dependencies: - commander "~2.17.1" + commander "^2.19.0" source-map "~0.6.1" - source-map-support "~0.5.9" + source-map-support "~0.5.10" test-exclude@^4.2.1: version "4.2.3" @@ -17050,16 +17066,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@>=2.3.3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@^2.3.4: +tough-cookie@^2.3.3, tough-cookie@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -17156,9 +17163,9 @@ ts-loader@^5.1.0: semver "^5.0.1" ts-node@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.0.2.tgz#9ecdf8d782a0ca4c80d1d641cbb236af4ac1b756" - integrity sha512-MosTrinKmaAcWgO8tqMjMJB22h+sp3Rd1i4fdoWY4mhBDekOwIAKI/bzmRi7IcbCmjquccYg2gcF6NBkLgr0Tw== + version "8.0.3" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.0.3.tgz#aa60b836a24dafd8bf21b54766841a232fdbc641" + integrity sha512-2qayBA4vdtVRuDo11DEFSsD/SFsBXQBRZZhbRGSIkmYmVkWjULn/GGMdG10KVqkaGndljfaTD8dKjWgcejO8YA== dependencies: arg "^4.1.0" diff "^3.1.0" @@ -17190,9 +17197,9 @@ tslint-react@^3.6.0: tsutils "^2.13.1" tslint@^5.8.0: - version "5.12.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1" - integrity sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw== + version "5.14.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.14.0.tgz#be62637135ac244fc9b37ed6ea5252c9eba1616e" + integrity sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ== dependencies: babel-code-frame "^6.22.0" builtin-modules "^1.1.1" @@ -17202,12 +17209,13 @@ tslint@^5.8.0: glob "^7.1.1" js-yaml "^3.7.0" minimatch "^3.0.4" + mkdirp "^0.5.1" resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.27.2" + tsutils "^2.29.0" -tsutils@^2.13.1, tsutils@^2.27.2: +tsutils@^2.13.1, tsutils@^2.29.0: version "2.29.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== @@ -17272,9 +17280,9 @@ typeface-oswald@0.0.54: integrity sha512-U1WMNp4qfy4/3khIfHMVAIKnNu941MXUfs3+H9R8PFgnoz42Hh9pboSFztWr86zut0eXC8byalmVhfkiKON/8Q== typescript@^3.0.3, typescript@^3.2.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3.tgz#f1657fc7daa27e1a8930758ace9ae8da31403221" - integrity sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A== + version "3.3.3333" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6" + integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw== ua-parser-js@^0.7.18: version "0.7.19" @@ -17300,11 +17308,11 @@ uglify-js@2.6.x: yargs "~3.10.0" uglify-js@3.4.x, uglify-js@^3.1.4: - version "3.4.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" - integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== + version "3.4.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" + integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== dependencies: - commander "~2.17.1" + commander "~2.19.0" source-map "~0.6.1" uglify-to-browserify@~1.0.0: @@ -17376,15 +17384,15 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" -unicode-match-property-value-ecmascript@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4" - integrity sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ== +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" - integrity sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg== + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== unified@^7.0.2: version "7.1.0" @@ -17496,9 +17504,9 @@ unzip-response@^2.0.1: integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= upath@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" - integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== update-notifier@^2.3.0, update-notifier@^2.5.0: version "2.5.0" @@ -17914,43 +17922,33 @@ webpack-core@^0.6.5: source-list-map "~0.1.7" source-map "~0.4.1" -webpack-dev-middleware@3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz#1132fecc9026fd90f0ecedac5cbff75d1fb45890" - integrity sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA== +webpack-dev-middleware@^3.4.0, webpack-dev-middleware@^3.5.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.6.1.tgz#91f2531218a633a99189f7de36045a331a4b9cd4" + integrity sha512-XQmemun8QJexMEvNFbD2BIg4eSKrmSIMrTfnl2nql2Sc6OGAYFyb8rwuYrCjl/IiEYYuyTEiimMscu7EXji/Dw== dependencies: - memory-fs "~0.4.1" - mime "^2.3.1" - range-parser "^1.0.3" - webpack-log "^2.0.0" - -webpack-dev-middleware@^3.4.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.5.2.tgz#d768b6194f3fe8d72d51feded49de359e8d96ffb" - integrity sha512-nPmshdt1ckcwWjI0Ubrdp8KroeuprW6xFKYqk0u3MflNMBXvHPnMATsC7/L/enwav2zvLCfj/Usr47qnF3KQyA== - dependencies: - memory-fs "~0.4.1" + memory-fs "^0.4.1" mime "^2.3.1" range-parser "^1.0.3" webpack-log "^2.0.0" webpack-dev-server@^3.1.0: - version "3.1.14" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz#60fb229b997fc5a0a1fc6237421030180959d469" - integrity sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ== + version "3.2.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz#1b45ce3ecfc55b6ebe5e36dab2777c02bc508c4e" + integrity sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" chokidar "^2.0.0" compression "^1.5.2" connect-history-api-fallback "^1.3.0" - debug "^3.1.0" + debug "^4.1.1" del "^3.0.0" express "^4.16.2" html-entities "^1.2.0" - http-proxy-middleware "~0.18.0" + http-proxy-middleware "^0.19.1" import-local "^2.0.0" - internal-ip "^3.0.1" + internal-ip "^4.2.0" ip "^1.1.5" killable "^1.0.0" loglevel "^1.4.1" @@ -17964,9 +17962,9 @@ webpack-dev-server@^3.1.0: sockjs-client "1.3.0" spdy "^4.0.0" strip-ansi "^3.0.0" - supports-color "^5.1.0" + supports-color "^6.1.0" url "^0.11.0" - webpack-dev-middleware "3.4.0" + webpack-dev-middleware "^3.5.1" webpack-log "^2.0.0" yargs "12.0.2" @@ -18044,14 +18042,14 @@ webpack@4.19.1: webpack-sources "^1.2.0" webpack@^4.23.1: - version "4.29.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.3.tgz#e0b406a7b4201ed5e4fb4f84fd7359f9a7db4647" - integrity sha512-xPJvFeB+8tUflXFq+OgdpiSnsCD5EANyv56co5q8q8+YtEasn5Sj3kzY44mta+csCIEB0vneSxnuaHkOL2h94A== + version "4.29.6" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.6.tgz#66bf0ec8beee4d469f8b598d3988ff9d8d90e955" + integrity sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw== dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-module-context" "1.7.11" - "@webassemblyjs/wasm-edit" "1.7.11" - "@webassemblyjs/wasm-parser" "1.7.11" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" acorn "^6.0.5" acorn-dynamic-import "^4.0.0" ajv "^6.1.0" @@ -18385,7 +18383,7 @@ yargs@^11.0.0, yargs@^11.1.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@^12.0.1, yargs@^12.0.5: +yargs@^12.0.1: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== From 1178115d50ba85b61fa03172dc26768904a8ded4 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 19 Mar 2019 16:15:19 +0100 Subject: [PATCH 098/101] fix: ts issue on SelectOption test --- packages/grafana-ui/src/components/Select/SelectOption.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grafana-ui/src/components/Select/SelectOption.test.tsx b/packages/grafana-ui/src/components/Select/SelectOption.test.tsx index 0a5a8864d64..d91bc9ee6af 100644 --- a/packages/grafana-ui/src/components/Select/SelectOption.test.tsx +++ b/packages/grafana-ui/src/components/Select/SelectOption.test.tsx @@ -25,6 +25,7 @@ const model: OptionProps = { key: '', onClick: jest.fn(), onMouseOver: jest.fn(), + onMouseMove: jest.fn(), tabIndex: 1, }, label: 'Option label', From 12d452ce92d5fe8c0f6854c29f8802eaa0ace68f Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 19 Mar 2019 16:31:26 +0100 Subject: [PATCH 099/101] Snapshot update --- .../components/Select/__snapshots__/SelectOption.test.tsx.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap b/packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap index c52be902edd..95c15ab013f 100644 --- a/packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap +++ b/packages/grafana-ui/src/components/Select/__snapshots__/SelectOption.test.tsx.snap @@ -4,6 +4,7 @@ exports[`SelectOption renders correctly 1`] = `
From 9205b82f19d44ef79806d136444617626c46e393 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 19 Mar 2019 16:38:14 +0100 Subject: [PATCH 100/101] Remove leftover from first iteration --- public/app/plugins/panel/graph/thresholds_form.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/app/plugins/panel/graph/thresholds_form.ts b/public/app/plugins/panel/graph/thresholds_form.ts index c67a030188d..08acd887559 100644 --- a/public/app/plugins/panel/graph/thresholds_form.ts +++ b/public/app/plugins/panel/graph/thresholds_form.ts @@ -31,8 +31,6 @@ export class ThresholdFormCtrl { fill: true, line: true, yaxis: 'left', - fillColor: 'rgba(234,112, 112, 0.12)', - lineColor: 'rgba(237, 46, 24, 0.60)', }); this.panelCtrl.render(); } From d845aacbdc8bff3e5daa7efddc7817bbb261e126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Tue, 19 Mar 2019 17:44:58 +0100 Subject: [PATCH 101/101] refactor: merged types and updated references --- .../SharedPreferences/SharedPreferences.tsx | 19 ++++++++++++-- public/app/core/services/backend_srv.ts | 25 ++----------------- public/app/core/services/search_srv.ts | 5 ++-- public/app/types/search.ts | 17 ++++++++++--- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index 171e0e8109e..a39eefcec4b 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -3,7 +3,7 @@ import React, { PureComponent } from 'react'; import { FormLabel, Select } from '@grafana/ui'; import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv'; -import { DashboardSearchHit } from 'app/types'; +import { DashboardSearchHit, DashboardSearchHitType } from 'app/types'; export interface Props { resourceUri: string; @@ -41,6 +41,21 @@ export class SharedPreferences extends PureComponent { async componentDidMount() { const prefs = await this.backendSrv.get(`/api/${this.props.resourceUri}/preferences`); const dashboards = await this.backendSrv.search({ starred: true }); + const defaultDashboardHit: DashboardSearchHit = { + id: 0, + title: 'Default', + tags: [], + type: '' as DashboardSearchHitType, + uid: '', + uri: '', + url: '', + folderId: 0, + folderTitle: '', + folderUid: '', + folderUrl: '', + isStarred: false, + slug: '', + }; if (prefs.homeDashboardId > 0 && !dashboards.find(d => d.id === prefs.homeDashboardId)) { const missing = await this.backendSrv.search({ dashboardIds: [prefs.homeDashboardId] }); @@ -53,7 +68,7 @@ export class SharedPreferences extends PureComponent { homeDashboardId: prefs.homeDashboardId, theme: prefs.theme, timezone: prefs.timezone, - dashboards: [{ id: 0, title: 'Default', tags: [], type: '', uid: '', uri: '', url: '' }, ...dashboards], + dashboards: [defaultDashboardHit, ...dashboards], }); } diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 53ab3ab6ce7..0d7d098dcea 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -3,28 +3,7 @@ import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; import config from 'app/core/config'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; - -export enum HitType { - DashHitDB = 'dash-db', - DashHitHome = 'dash-home', - DashHitFolder = 'dash-folder', -} - -export interface Hit { - id: number; - uid: string; - title: string; - uri: string; - url: string; - slug: string; - type: HitType; - tags: string[]; - isStarred: boolean; - folderId: number; - folderUid: string; - folderTitle: string; - folderUrl: string; -} +import { DashboardSearchHit } from 'app/types/search'; export class BackendSrv { private inFlightRequests = {}; @@ -259,7 +238,7 @@ export class BackendSrv { return this.request({ url: '/api/login/ping', method: 'GET', retry: 1 }); } - search(query): Promise { + search(query): Promise { return this.get('/api/search', query); } diff --git a/public/app/core/services/search_srv.ts b/public/app/core/services/search_srv.ts index 4a605d3fc50..068fe3ffbc3 100644 --- a/public/app/core/services/search_srv.ts +++ b/public/app/core/services/search_srv.ts @@ -7,8 +7,9 @@ import coreModule from 'app/core/core_module'; import impressionSrv from 'app/core/services/impression_srv'; import store from 'app/core/store'; import { contextSrv } from 'app/core/services/context_srv'; -import { BackendSrv, Hit } from './backend_srv'; +import { BackendSrv } from './backend_srv'; import { Section } from '../components/manage_dashboards/manage_dashboards'; +import { DashboardSearchHit } from 'app/types/search'; interface Sections { [key: string]: Partial
; @@ -128,7 +129,7 @@ export class SearchSrv { }); } - private handleSearchResult(sections: Sections, results: Hit[]): any { + private handleSearchResult(sections: Sections, results: DashboardSearchHit[]): any { if (results.length === 0) { return sections; } diff --git a/public/app/types/search.ts b/public/app/types/search.ts index e5e17288de1..e15797f41a3 100644 --- a/public/app/types/search.ts +++ b/public/app/types/search.ts @@ -1,9 +1,20 @@ +export enum DashboardSearchHitType { + DashHitDB = 'dash-db', + DashHitHome = 'dash-home', + DashHitFolder = 'dash-folder', +} export interface DashboardSearchHit { id: number; - tags: string[]; - title: string; - type: string; uid: string; + title: string; uri: string; url: string; + slug: string; + type: DashboardSearchHitType; + tags: string[]; + isStarred: boolean; + folderId: number; + folderUid: string; + folderTitle: string; + folderUrl: string; }