diff --git a/CHANGELOG.md b/CHANGELOG.md index a603fbb1a78..7b97da0e81c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,39 @@ -# 6.0.0-beta2 (unreleased) +# 6.0.0-beta3 (unreleased) + +# 6.0.0-beta2 (2019-02-11) + +### New Features +* **AzureMonitor**: Enable alerting by converting Azure Monitor API to Go [#14623](https://github.com/grafana/grafana/issues/14623) ### Minor -* **Pushover**: Adds support for images in pushover notifier [#10780](https://github.com/grafana/grafana/issues/10780), thx [@jpenalbae](https://github.com/jpenalbae) +* **Alerting**: Adds support for images in pushover notifier [#10780](https://github.com/grafana/grafana/issues/10780), thx [@jpenalbae](https://github.com/jpenalbae) +* **Graphite/InfluxDB/OpenTSDB**: Fix always take dashboard timezone into consideration when handle custom time ranges [#15284](https://github.com/grafana/grafana/issues/15284) * **Stackdriver**: Template variables in filters using globbing format [#15182](https://github.com/grafana/grafana/issues/15182) * **Cloudwatch**: Add `resource_arns` template variable query function [#8207](https://github.com/grafana/grafana/issues/8207), thx [@jeroenvollenbrock](https://github.com/jeroenvollenbrock) * **Cloudwatch**: Add AWS/Neptune metrics [#14231](https://github.com/grafana/grafana/issues/14231), thx [@tcpatterson](https://github.com/tcpatterson) +* **Cloudwatch**: Add AWS/EC2/API metrics [#14233](https://github.com/grafana/grafana/issues/14233), thx [@tcpatterson](https://github.com/tcpatterson) * **Cloudwatch**: Add AWS RDS ServerlessDatabaseCapacity metric [#15265](https://github.com/grafana/grafana/pull/15265), thx [@larsjoergensen](https://github.com/larsjoergensen) +* **MySQL**: Adds datasource SSL CA/client certificates support [#8570](https://github.com/grafana/grafana/issues/8570), thx [@bugficks](https://github.com/bugficks) +* **MSSQL**: Timerange are now passed for template variable queries [#13324](https://github.com/grafana/grafana/issues/13324), thx [@thatsparesh](https://github.com/thatsparesh) * **Annotations**: Support PATCH verb in annotations http api [#12546](https://github.com/grafana/grafana/issues/12546), thx [@SamuelToh](https://github.com/SamuelToh) * **Templating**: Add json formatting to variable interpolation [#15291](https://github.com/grafana/grafana/issues/15291), thx [@mtanda](https://github.com/mtanda) +* **Login**: Anonymous usage stats for token auth [#15288](https://github.com/grafana/grafana/issues/15288) +* **AzureMonitor**: improve autocomplete for Log Analytics and App Insights editor [#15131](https://github.com/grafana/grafana/issues/15131) +* **LDAP**: Fix IPA/FreeIPA v4.6.4 does not allow LDAP searches with empty attributes [#14432](https://github.com/grafana/grafana/issues/14432) + +### Breaking changes + +* **Internal Metrics** Edition has been added to the build_info metric. This will break any Graphite queries using this metric. Edition will be a new label for the Prometheus metric. [#15363](https://github.com/grafana/grafana/pull/15363) + +### 6.0.0-beta1 fixes + +* **Postgres**: Fix default port not added when port not configured [#15189](https://github.com/grafana/grafana/issues/15189) +* **Alerting**: Fixes crash bug when alert notifier folders are missing [#15295](https://github.com/grafana/grafana/issues/15295) +* **Dashboard**: Fix save provisioned dashboard modal [#15219](https://github.com/grafana/grafana/pull/15219) +* **Dashboard**: Fix having a long query in prometheus dashboard query editor blocks 30% of the query field when on OSX and having native scrollbars [#15122](https://github.com/grafana/grafana/issues/15122) +* **Explore**: Fix issue with wrapping on long queries [#15222](https://github.com/grafana/grafana/issues/15222) +* **Explore**: Fix cut & paste adds newline before and after selection [#15223](https://github.com/grafana/grafana/issues/15223) +* **Dataproxy**: Fix global datasource proxy timeout not added to correct http client [#15258](https://github.com/grafana/grafana/issues/15258) [#5699](https://github.com/grafana/grafana/issues/5699) # 6.0.0-beta1 (2019-01-30) @@ -87,7 +113,7 @@ * **Stackdriver**: Fixes issue with data proxy and Authorization header [#14262](https://github.com/grafana/grafana/issues/14262) * **Units**: fixedUnit for Flow:l/min and mL/min [#14294](https://github.com/grafana/grafana/issues/14294), thx [@flopp999](https://github.com/flopp999). * **Logging**: Fix for issue where data proxy logged a secret when debug logging was enabled, now redacted. [#14319](https://github.com/grafana/grafana/issues/14319) -* **InfluxDB**: Add support for alerting on InfluxDB queries that use the cumulative_sum function. [#14314](https://github.com/grafana/grafana/pull/14314), thx [@nitti](https://github.com/nitti) +* TSDB**: Fix always take dashboard timezone into consideration when handle custom time ranges**: Add support for alerting on InfluxDB queries that use the cumulative_sum function. [#14314](https://github.com/grafana/grafana/pull/14314), thx [@nitti](https://github.com/nitti) * **Plugins**: Panel plugins should no receive the panel-initialized event again as usual. * **Embedded Graphs**: Iframe graph panels should now work as usual. [#14284](https://github.com/grafana/grafana/issues/14284) * **Postgres**: Improve PostgreSQL Query Editor if using different Schemas, [#14313]( @@ -1022,7 +1048,7 @@ Pull Request: [#8472](https://github.com/grafana/grafana/pull/8472) * **Docs**: Added some details about Sessions in Postgres [#7694](https://github.com/grafana/grafana/pull/7694) thx [@rickard-von-essen](https://github.com/rickard-von-essen) * **Influxdb**: Allow commas in template variables [#7681](https://github.com/grafana/grafana/issues/7681) thx [@thuck](https://github.com/thuck) * **Cloudwatch**: stop using deprecated session.New() [#7736](https://github.com/grafana/grafana/issues/7736) thx [@mtanda](https://github.com/mtanda) -* **OpenTSDB**: Pass dropcounter rate option if no max counter and no reset value or reset value as 0 is specified [#7743](https://github.com/grafana/grafana/pull/7743) thx [@r4um](https://github.com/r4um) +*TSDB**: Fix always take dashboard timezone into consideration when handle custom time ranges**: Pass dropcounter rate option if no max counter and no reset value or reset value as 0 is specified [#7743](https://github.com/grafana/grafana/pull/7743) thx [@r4um](https://github.com/r4um) * **Templating**: support full resolution for $interval variable [#7696](https://github.com/grafana/grafana/pull/7696) thx [@mtanda](https://github.com/mtanda) * **Elasticsearch**: Unique Count on string fields in ElasticSearch [#3536](https://github.com/grafana/grafana/issues/3536), thx [@pyro2927](https://github.com/pyro2927) * **Templating**: Data source template variable that refers to other variable in regex filter [#6365](https://github.com/grafana/grafana/issues/6365) thx [@rlodge](https://github.com/rlodge) diff --git a/Dockerfile b/Dockerfile index c3e59c8048e..9f07dc79c1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,6 +64,7 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \ useradd -r -u $GF_UID -g grafana grafana && \ mkdir -p "$GF_PATHS_PROVISIONING/datasources" \ "$GF_PATHS_PROVISIONING/dashboards" \ + "$GF_PATHS_PROVISIONING/notifiers" \ "$GF_PATHS_LOGS" \ "$GF_PATHS_PLUGINS" \ "$GF_PATHS_DATA" && \ diff --git a/README.md b/README.md index 658f1e34257..2cb8bfee306 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,18 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. +![](https://www.grafanacon.org/2019/images/grafanacon_la_nav-logo.png) + +Join us Feb 25-26 in Los Angeles, California for GrafanaCon - a two-day event with talks focused on Grafana and the surrounding open source monitoring ecosystem. Get deep dives into Loki, the Explore workflow and all of the new features of Grafana 6, plus participate in hands on workshops to help you get the most out of your data. + +Time is running out - grab your ticket now! http://grafanacon.org + + ## Installation -Head to [docs.grafana.org](http://docs.grafana.org/installation/) and [download](https://grafana.com/get) -the latest release. - -If you have any problems please read the [troubleshooting guide](http://docs.grafana.org/installation/troubleshooting/). +Head to [docs.grafana.org](http://docs.grafana.org/installation/) for documentation or [download](https://grafana.com/get) to get the latest release. ## Documentation & Support Be sure to read the [getting started guide](http://docs.grafana.org/guides/gettingstarted/) and the other feature guides. diff --git a/devenv/docker/blocks/elastic5/docker-compose.yaml b/devenv/docker/blocks/elastic5/docker-compose.yaml index 33a7d9855b0..3a2ef39faba 100644 --- a/devenv/docker/blocks/elastic5/docker-compose.yaml +++ b/devenv/docker/blocks/elastic5/docker-compose.yaml @@ -1,6 +1,3 @@ -# You need to run 'sysctl -w vm.max_map_count=262144' on the host machine -version: '2' -services: elasticsearch5: image: elasticsearch:5 command: elasticsearch diff --git a/package.json b/package.json index bd11bb3e431..b9a7fc691a6 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "company": "Grafana Labs" }, "name": "grafana", - "version": "6.0.0-prebeta2", + "version": "6.0.0-pre3", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" @@ -28,6 +28,7 @@ "@types/react-dom": "^16.0.9", "@types/react-grid-layout": "^0.16.6", "@types/react-select": "^2.0.4", + "@types/react-transition-group": "^2.0.15", "@types/react-virtualized": "^9.18.12", "angular-mocks": "1.6.6", "autoprefixer": "^6.4.0", @@ -66,10 +67,10 @@ "html-loader": "^0.5.1", "html-webpack-harddisk-plugin": "^0.2.0", "html-webpack-plugin": "^3.2.0", - "husky": "^0.14.3", + "husky": "^1.3.1", "jest": "^23.6.0", "jest-date-mock": "^1.0.6", - "lint-staged": "^6.0.0", + "lint-staged": "^8.1.3", "load-grunt-tasks": "3.5.2", "log-timestamp": "^0.2.1", "mini-css-extract-plugin": "^0.4.0", @@ -156,6 +157,7 @@ "dependencies": { "@babel/polyfill": "^7.0.0", "@torkelo/react-select": "2.1.1", + "@types/reselect": "^2.2.0", "angular": "1.6.6", "angular-bindonce": "0.3.1", "angular-native-dragdrop": "1.2.2", @@ -192,6 +194,7 @@ "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", "remarkable": "^1.7.1", + "reselect": "^4.0.0", "rst2html": "github:thoward/rst2html#990cb89", "rxjs": "^6.3.3", "slate": "^0.33.4", diff --git a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx index 4cb8c15c002..5fdad9c43cf 100644 --- a/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx @@ -69,8 +69,8 @@ export class AxisSelector extends React.PureComponent diff --git a/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss b/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss index 993bf086c95..4ce9c5264ea 100644 --- a/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss +++ b/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss @@ -29,14 +29,14 @@ &:hover { .panel-options-group__add-circle { - background-color: $btn-success-bg; + background-color: $btn-primary-bg; color: $white; } } } .panel-options-group__add-circle { - @include gradientBar($btn-success-bg, $btn-success-bg-hl); + @include gradientBar($btn-success-bg, $btn-success-bg-hl, #fff); border-radius: 50px; width: 20px; diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 845ff5f6bf4..2b6af67df22 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ChangeEvent } from 'react'; import { shallow } from 'enzyme'; import { ThresholdsEditor, Props } from './ThresholdsEditor'; @@ -118,7 +118,7 @@ describe('change threshold value', () => { ]; const instance = setup({ thresholds }); - const mockEvent = { target: { value: 12 } }; + const mockEvent = ({ target: { value: '12' } } as any) as ChangeEvent; instance.onChangeThresholdValue(mockEvent, thresholds[0]); @@ -137,7 +137,7 @@ describe('change threshold value', () => { thresholds, }; - const mockEvent = { target: { value: 78 } }; + const mockEvent = ({ target: { value: '78' } } as any) as ChangeEvent; instance.onChangeThresholdValue(mockEvent, thresholds[1]); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index b2a2e07c58d..f4db23d6656 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, ChangeEvent } from 'react'; import { Threshold } from '../../types'; import { ColorPicker } from '../ColorPicker/ColorPicker'; import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; @@ -94,14 +94,15 @@ export class ThresholdsEditor extends PureComponent { ); }; - onChangeThresholdValue = (event: any, threshold: Threshold) => { + onChangeThresholdValue = (event: ChangeEvent, threshold: Threshold) => { if (threshold.index === 0) { return; } const { thresholds } = this.state; - const parsedValue = parseInt(event.target.value, 10); - const value = isNaN(parsedValue) ? null : parsedValue; + const cleanValue = event.target.value.replace(/,/g, '.'); + const parsedValue = parseFloat(cleanValue); + const value = isNaN(parsedValue) ? '' : parsedValue; const newThresholds = thresholds.map(t => { if (t === threshold && t.index !== 0) { @@ -164,16 +165,14 @@ export class ThresholdsEditor extends PureComponent {
{threshold.color && (
- this.onChangeThresholdColor(threshold, color)} - /> + this.onChangeThresholdColor(threshold, color)} />
)}
this.onChangeThresholdValue(event, threshold)} value={value} onBlur={this.onBlur} diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss index 490b452234f..8ef59bf08af 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss +++ b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss @@ -21,7 +21,7 @@ } .thresholds-row-add-button { - @include buttonBackground($btn-success-bg, $btn-success-bg-hl); + @include buttonBackground($btn-success-bg, $btn-success-bg-hl, #fff); align-self: center; margin-right: 5px; diff --git a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss index 503b977bbf0..a33724b3460 100644 --- a/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss +++ b/packages/grafana-ui/src/components/Tooltip/_Tooltip.scss @@ -31,7 +31,7 @@ $popper-margin-from-ref: 5px; // Themes &.popper__background--error { - @include popper-theme($tooltipBackgroundError, $tooltipBackgroundError); + @include popper-theme($tooltipBackgroundError, $white); } &.popper__background--info { diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.test.ts b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts index aa57b46636c..3edc71c94ae 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.test.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.test.ts @@ -44,8 +44,8 @@ describe('colors', () => { }); describe('getColorFromHexRgbOrName', () => { - it('returns undefined for unknown color', () => { - expect(() => getColorFromHexRgbOrName('aruba-sunshine')).toThrow(); + it('returns black for unknown color', () => { + expect(getColorFromHexRgbOrName('aruba-sunshine')).toBe('#000000'); }); it('returns dark hex variant for known color if theme not specified', () => { @@ -64,5 +64,9 @@ describe('colors', () => { expect(getColorFromHexRgbOrName('rgb(0,0,0)')).toBe('rgb(0,0,0)'); expect(getColorFromHexRgbOrName('rgba(0,0,0,1)')).toBe('rgba(0,0,0,1)'); }); + + it('returns hex for named color that is not a part of named colors palette', () => { + expect(getColorFromHexRgbOrName('lime')).toBe('#00ff00'); + }); }); }); diff --git a/packages/grafana-ui/src/utils/namedColorsPalette.ts b/packages/grafana-ui/src/utils/namedColorsPalette.ts index ee5741e794e..ac1c1be6fa5 100644 --- a/packages/grafana-ui/src/utils/namedColorsPalette.ts +++ b/packages/grafana-ui/src/utils/namedColorsPalette.ts @@ -1,5 +1,6 @@ import { flatten } from 'lodash'; import { GrafanaThemeType } from '../types'; +import tinycolor from 'tinycolor2'; type Hue = 'green' | 'yellow' | 'red' | 'blue' | 'orange' | 'purple'; @@ -69,7 +70,9 @@ export const getColorDefinitionByName = (name: Color): ColorDefinition => { }; export const getColorDefinition = (hex: string, theme: GrafanaThemeType): ColorDefinition | undefined => { - return flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.variants[theme] === hex)[0]; + return flatten(Array.from(getNamedColorPalette().values())).filter( + definition => definition.variants[theme] === hex + )[0]; }; const isHex = (color: string) => { @@ -94,7 +97,9 @@ export const getColorName = (color?: string, theme?: GrafanaThemeType): Color | }; export const getColorByName = (colorName: string) => { - const definition = flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.name === colorName); + const definition = flatten(Array.from(getNamedColorPalette().values())).filter( + definition => definition.name === colorName + ); return definition.length > 0 ? definition[0] : undefined; }; @@ -106,7 +111,7 @@ export const getColorFromHexRgbOrName = (color: string, theme?: GrafanaThemeType const colorDefinition = getColorByName(color); if (!colorDefinition) { - throw new Error('Unknown color'); + return new tinycolor(color).toHexString(); } return theme ? colorDefinition.variants[theme] : colorDefinition.variants.dark; diff --git a/packaging/deb/control/postinst b/packaging/deb/control/postinst index 351c966a8e6..957f8aef307 100755 --- a/packaging/deb/control/postinst +++ b/packaging/deb/control/postinst @@ -31,11 +31,16 @@ case "$1" in cp /usr/share/grafana/conf/ldap.toml /etc/grafana/ldap.toml fi - if [ ! -f $PROVISIONING_CFG_DIR ]; then + if [ ! -d $PROVISIONING_CFG_DIR ]; then mkdir -p $PROVISIONING_CFG_DIR/dashboards $PROVISIONING_CFG_DIR/datasources cp /usr/share/grafana/conf/provisioning/dashboards/sample.yaml $PROVISIONING_CFG_DIR/dashboards/sample.yaml cp /usr/share/grafana/conf/provisioning/datasources/sample.yaml $PROVISIONING_CFG_DIR/datasources/sample.yaml - fi + fi + + if [ ! -d $PROVISIONING_CFG_DIR/notifiers ]; then + mkdir -p $PROVISIONING_CFG_DIR/notifiers + cp /usr/share/grafana/conf/provisioning/notifiers/sample.yaml $PROVISIONING_CFG_DIR/notifiers/sample.yaml + fi # configuration files should not be modifiable by grafana user, as this can be a security issue chown -Rh root:$GRAFANA_GROUP /etc/grafana/* diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index d4f2f2aa7a3..d783cb14377 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -39,6 +39,7 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \ useradd -r -u $GF_UID -g grafana grafana && \ mkdir -p "$GF_PATHS_PROVISIONING/datasources" \ "$GF_PATHS_PROVISIONING/dashboards" \ + "$GF_PATHS_PROVISIONING/notifiers" \ "$GF_PATHS_LOGS" \ "$GF_PATHS_PLUGINS" \ "$GF_PATHS_DATA" && \ diff --git a/packaging/rpm/control/postinst b/packaging/rpm/control/postinst index e75850f258e..cd679838487 100755 --- a/packaging/rpm/control/postinst +++ b/packaging/rpm/control/postinst @@ -45,11 +45,16 @@ if [ $1 -eq 1 ] ; then cp /usr/share/grafana/conf/ldap.toml /etc/grafana/ldap.toml fi - if [ ! -f $PROVISIONING_CFG_DIR ]; then + if [ ! -d $PROVISIONING_CFG_DIR ]; then mkdir -p $PROVISIONING_CFG_DIR/dashboards $PROVISIONING_CFG_DIR/datasources cp /usr/share/grafana/conf/provisioning/dashboards/sample.yaml $PROVISIONING_CFG_DIR/dashboards/sample.yaml cp /usr/share/grafana/conf/provisioning/datasources/sample.yaml $PROVISIONING_CFG_DIR/datasources/sample.yaml - fi + fi + + if [ ! -d $PROVISIONING_CFG_DIR/notifiers ]; then + mkdir -p $PROVISIONING_CFG_DIR/notifiers + cp /usr/share/grafana/conf/provisioning/notifiers/sample.yaml $PROVISIONING_CFG_DIR/notifiers/sample.yaml + fi # Set user permissions on /var/log/grafana, /var/lib/grafana mkdir -p /var/log/grafana /var/lib/grafana diff --git a/pkg/api/api.go b/pkg/api/api.go index 0685ef3814d..6da127fb550 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -16,7 +16,7 @@ func (hs *HTTPServer) registerRoutes() { reqOrgAdmin := middleware.ReqOrgAdmin redirectFromLegacyDashboardURL := middleware.RedirectFromLegacyDashboardURL() redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL() - quota := middleware.Quota + quota := middleware.Quota(hs.QuotaService) bind := binding.Bind r := hs.RouteRegister @@ -286,7 +286,7 @@ func (hs *HTTPServer) registerRoutes() { dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), Wrap(CalculateDashboardDiff)) - dashboardRoute.Post("/db", bind(m.SaveDashboardCommand{}), Wrap(PostDashboard)) + dashboardRoute.Post("/db", bind(m.SaveDashboardCommand{}), Wrap(hs.PostDashboard)) dashboardRoute.Get("/home", Wrap(GetHomeDashboard)) dashboardRoute.Get("/tags", GetDashboardTags) dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), Wrap(ImportDashboard)) @@ -294,7 +294,7 @@ func (hs *HTTPServer) registerRoutes() { dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) { dashIdRoute.Get("/versions", Wrap(GetDashboardVersions)) dashIdRoute.Get("/versions/:id", Wrap(GetDashboardVersion)) - dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), Wrap(RestoreDashboardVersion)) + dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), Wrap(hs.RestoreDashboardVersion)) dashIdRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) { dashboardPermissionRoute.Get("/", Wrap(GetDashboardPermissionList)) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 2789b0bf51e..20d717ef8fa 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -18,7 +18,6 @@ import ( m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/guardian" - "github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -208,14 +207,14 @@ func DeleteDashboardByUID(c *m.ReqContext) Response { }) } -func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response { +func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response { cmd.OrgId = c.OrgId cmd.UserId = c.UserId dash := cmd.GetDashboardModel() if dash.Id == 0 && dash.Uid == "" { - limitReached, err := quota.QuotaReached(c, "dashboard") + limitReached, err := hs.QuotaService.QuotaReached(c, "dashboard") if err != nil { return Error(500, "failed to get quota", err) } @@ -463,7 +462,7 @@ func CalculateDashboardDiff(c *m.ReqContext, apiOptions dtos.CalculateDiffOption } // RestoreDashboardVersion restores a dashboard to the given version. -func RestoreDashboardVersion(c *m.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) Response { +func (hs *HTTPServer) RestoreDashboardVersion(c *m.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) Response { dash, rsp := getDashboardHelper(c.OrgId, "", c.ParamsInt64(":dashboardId"), "") if rsp != nil { return rsp @@ -490,7 +489,7 @@ func RestoreDashboardVersion(c *m.ReqContext, apiCmd dtos.RestoreDashboardVersio saveCmd.Dashboard.Set("uid", dash.Uid) saveCmd.Message = fmt.Sprintf("Restored from version %d", version.Version) - return PostDashboard(c, saveCmd) + return hs.PostDashboard(c, saveCmd) } func GetDashboardTags(c *m.ReqContext) { diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 8ee40920cbc..44d5cd32430 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -881,12 +881,16 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d Convey(desc+" "+url, func() { defer bus.ClearBusHandlers() + hs := HTTPServer{ + Bus: bus.GetBus(), + } + sc := setupScenarioContext(url) sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.SignedInUser = &m.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId} - return PostDashboard(c, cmd) + return hs.PostDashboard(c, cmd) }) origNewDashboardService := dashboards.NewService diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index cadf6896bf4..2a430147b55 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -24,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/services/cache" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/hooks" + "github.com/grafana/grafana/pkg/services/quota" "github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/session" "github.com/grafana/grafana/pkg/setting" @@ -55,6 +56,7 @@ type HTTPServer struct { CacheService *cache.CacheService `inject:""` DatasourceCache datasources.CacheService `inject:""` AuthTokenService models.UserTokenService `inject:""` + QuotaService *quota.QuotaService `inject:""` } func (hs *HTTPServer) Init() error { diff --git a/pkg/api/pluginproxy/ds_proxy.go b/pkg/api/pluginproxy/ds_proxy.go index a0ad96a6977..b1950998297 100644 --- a/pkg/api/pluginproxy/ds_proxy.go +++ b/pkg/api/pluginproxy/ds_proxy.go @@ -54,7 +54,7 @@ func NewDataSourceProxy(ds *m.DataSource, plugin *plugins.DataSourcePlugin, ctx func newHTTPClient() httpClient { return &http.Client{ - Timeout: time.Duration(setting.DataProxyTimeout) * time.Second, + Timeout: 30 * time.Second, Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, } } diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index 3bdaf0cc80e..d371d4e91da 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -19,6 +19,7 @@ import ( _ "github.com/grafana/grafana/pkg/services/alerting/conditions" _ "github.com/grafana/grafana/pkg/services/alerting/notifiers" "github.com/grafana/grafana/pkg/setting" + _ "github.com/grafana/grafana/pkg/tsdb/azuremonitor" _ "github.com/grafana/grafana/pkg/tsdb/cloudwatch" _ "github.com/grafana/grafana/pkg/tsdb/elasticsearch" _ "github.com/grafana/grafana/pkg/tsdb/graphite" diff --git a/pkg/infra/usagestats/service.go b/pkg/infra/usagestats/service.go new file mode 100644 index 00000000000..c2bf0d06349 --- /dev/null +++ b/pkg/infra/usagestats/service.go @@ -0,0 +1,54 @@ +package usagestats + +import ( + "context" + "time" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/social" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/registry" + "github.com/grafana/grafana/pkg/setting" +) + +var metricsLogger log.Logger = log.New("metrics") + +func init() { + registry.RegisterService(&UsageStatsService{}) +} + +type UsageStatsService struct { + Cfg *setting.Cfg `inject:""` + Bus bus.Bus `inject:""` + SQLStore *sqlstore.SqlStore `inject:""` + + oauthProviders map[string]bool +} + +func (uss *UsageStatsService) Init() error { + + uss.oauthProviders = social.GetOAuthProviders(uss.Cfg) + return nil +} + +func (uss *UsageStatsService) Run(ctx context.Context) error { + uss.updateTotalStats() + + onceEveryDayTick := time.NewTicker(time.Hour * 24) + everyMinuteTicker := time.NewTicker(time.Minute) + defer onceEveryDayTick.Stop() + defer everyMinuteTicker.Stop() + + for { + select { + case <-onceEveryDayTick.C: + uss.sendUsageStats(uss.oauthProviders) + case <-everyMinuteTicker.C: + uss.updateTotalStats() + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/pkg/infra/usagestats/usage_stats.go b/pkg/infra/usagestats/usage_stats.go new file mode 100644 index 00000000000..9d7501b7765 --- /dev/null +++ b/pkg/infra/usagestats/usage_stats.go @@ -0,0 +1,177 @@ +package usagestats + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "runtime" + "strings" + "time" + + "github.com/grafana/grafana/pkg/metrics" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/setting" +) + +var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" + +func (uss *UsageStatsService) sendUsageStats(oauthProviders map[string]bool) { + if !setting.ReportingEnabled { + return + } + + metricsLogger.Debug(fmt.Sprintf("Sending anonymous usage stats to %s", usageStatsURL)) + + version := strings.Replace(setting.BuildVersion, ".", "_", -1) + + metrics := map[string]interface{}{} + report := map[string]interface{}{ + "version": version, + "metrics": metrics, + "os": runtime.GOOS, + "arch": runtime.GOARCH, + "edition": getEdition(), + "packaging": setting.Packaging, + } + + statsQuery := models.GetSystemStatsQuery{} + if err := uss.Bus.Dispatch(&statsQuery); err != nil { + metricsLogger.Error("Failed to get system stats", "error", err) + return + } + + metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards + metrics["stats.users.count"] = statsQuery.Result.Users + metrics["stats.orgs.count"] = statsQuery.Result.Orgs + metrics["stats.playlist.count"] = statsQuery.Result.Playlists + metrics["stats.plugins.apps.count"] = len(plugins.Apps) + metrics["stats.plugins.panels.count"] = len(plugins.Panels) + metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) + metrics["stats.alerts.count"] = statsQuery.Result.Alerts + metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers + metrics["stats.datasources.count"] = statsQuery.Result.Datasources + metrics["stats.stars.count"] = statsQuery.Result.Stars + metrics["stats.folders.count"] = statsQuery.Result.Folders + metrics["stats.dashboard_permissions.count"] = statsQuery.Result.DashboardPermissions + metrics["stats.folder_permissions.count"] = statsQuery.Result.FolderPermissions + metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards + metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots + metrics["stats.teams.count"] = statsQuery.Result.Teams + metrics["stats.total_auth_token.count"] = statsQuery.Result.AuthTokens + + userCount := statsQuery.Result.Users + avgAuthTokensPerUser := statsQuery.Result.AuthTokens + if userCount != 0 { + avgAuthTokensPerUser = avgAuthTokensPerUser / userCount + } + + metrics["stats.avg_auth_token_per_user.count"] = avgAuthTokensPerUser + + dsStats := models.GetDataSourceStatsQuery{} + if err := uss.Bus.Dispatch(&dsStats); err != nil { + metricsLogger.Error("Failed to get datasource stats", "error", err) + return + } + + // send counters for each data source + // but ignore any custom data sources + // as sending that name could be sensitive information + dsOtherCount := 0 + for _, dsStat := range dsStats.Result { + if models.IsKnownDataSourcePlugin(dsStat.Type) { + metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count + } else { + dsOtherCount += dsStat.Count + } + } + metrics["stats.ds.other.count"] = dsOtherCount + + metrics["stats.packaging."+setting.Packaging+".count"] = 1 + + dsAccessStats := models.GetDataSourceAccessStatsQuery{} + if err := uss.Bus.Dispatch(&dsAccessStats); err != nil { + metricsLogger.Error("Failed to get datasource access stats", "error", err) + return + } + + // send access counters for each data source + // but ignore any custom data sources + // as sending that name could be sensitive information + dsAccessOtherCount := make(map[string]int64) + for _, dsAccessStat := range dsAccessStats.Result { + if dsAccessStat.Access == "" { + continue + } + + access := strings.ToLower(dsAccessStat.Access) + + if models.IsKnownDataSourcePlugin(dsAccessStat.Type) { + metrics["stats.ds_access."+dsAccessStat.Type+"."+access+".count"] = dsAccessStat.Count + } else { + old := dsAccessOtherCount[access] + dsAccessOtherCount[access] = old + dsAccessStat.Count + } + } + + for access, count := range dsAccessOtherCount { + metrics["stats.ds_access.other."+access+".count"] = count + } + + anStats := models.GetAlertNotifierUsageStatsQuery{} + if err := uss.Bus.Dispatch(&anStats); err != nil { + metricsLogger.Error("Failed to get alert notification stats", "error", err) + return + } + + for _, stats := range anStats.Result { + metrics["stats.alert_notifiers."+stats.Type+".count"] = stats.Count + } + + authTypes := map[string]bool{} + authTypes["anonymous"] = setting.AnonymousEnabled + authTypes["basic_auth"] = setting.BasicAuthEnabled + authTypes["ldap"] = setting.LdapEnabled + authTypes["auth_proxy"] = setting.AuthProxyEnabled + + for provider, enabled := range oauthProviders { + authTypes["oauth_"+provider] = enabled + } + + for authType, enabled := range authTypes { + enabledValue := 0 + if enabled { + enabledValue = 1 + } + metrics["stats.auth_enabled."+authType+".count"] = enabledValue + } + + out, _ := json.MarshalIndent(report, "", " ") + data := bytes.NewBuffer(out) + + client := http.Client{Timeout: 5 * time.Second} + go client.Post(usageStatsURL, "application/json", data) +} + +func (uss *UsageStatsService) updateTotalStats() { + statsQuery := models.GetSystemStatsQuery{} + if err := uss.Bus.Dispatch(&statsQuery); err != nil { + metricsLogger.Error("Failed to get system stats", "error", err) + return + } + + metrics.M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards)) + metrics.M_StatTotal_Users.Set(float64(statsQuery.Result.Users)) + metrics.M_StatActive_Users.Set(float64(statsQuery.Result.ActiveUsers)) + metrics.M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists)) + metrics.M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs)) +} + +func getEdition() string { + if setting.IsEnterprise { + return "enterprise" + } else { + return "oss" + } +} diff --git a/pkg/metrics/metrics_test.go b/pkg/infra/usagestats/usage_stats_test.go similarity index 91% rename from pkg/metrics/metrics_test.go rename to pkg/infra/usagestats/usage_stats_test.go index c27d6f64b8c..ea5b95d6ef0 100644 --- a/pkg/metrics/metrics_test.go +++ b/pkg/infra/usagestats/usage_stats_test.go @@ -1,4 +1,4 @@ -package metrics +package usagestats import ( "bytes" @@ -15,14 +15,21 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" ) func TestMetrics(t *testing.T) { Convey("Test send usage stats", t, func() { + uss := &UsageStatsService{ + Bus: bus.New(), + SQLStore: sqlstore.InitTestDB(t), + } + var getSystemStatsQuery *models.GetSystemStatsQuery - bus.AddHandler("test", func(query *models.GetSystemStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetSystemStatsQuery) error { + query.Result = &models.SystemStats{ Dashboards: 1, Datasources: 2, @@ -38,13 +45,14 @@ func TestMetrics(t *testing.T) { ProvisionedDashboards: 12, Snapshots: 13, Teams: 14, + AuthTokens: 15, } getSystemStatsQuery = query return nil }) var getDataSourceStatsQuery *models.GetDataSourceStatsQuery - bus.AddHandler("test", func(query *models.GetDataSourceStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetDataSourceStatsQuery) error { query.Result = []*models.DataSourceStats{ { Type: models.DS_ES, @@ -68,7 +76,7 @@ func TestMetrics(t *testing.T) { }) var getDataSourceAccessStatsQuery *models.GetDataSourceAccessStatsQuery - bus.AddHandler("test", func(query *models.GetDataSourceAccessStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetDataSourceAccessStatsQuery) error { query.Result = []*models.DataSourceAccessStats{ { Type: models.DS_ES, @@ -116,7 +124,7 @@ func TestMetrics(t *testing.T) { }) var getAlertNotifierUsageStatsQuery *models.GetAlertNotifierUsageStatsQuery - bus.AddHandler("test", func(query *models.GetAlertNotifierUsageStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetAlertNotifierUsageStatsQuery) error { query.Result = []*models.NotifierUsageStats{ { Type: "slack", @@ -155,11 +163,11 @@ func TestMetrics(t *testing.T) { "grafana_com": true, } - sendUsageStats(oauthProviders) + uss.sendUsageStats(oauthProviders) Convey("Given reporting not enabled and sending usage stats", func() { setting.ReportingEnabled = false - sendUsageStats(oauthProviders) + uss.sendUsageStats(oauthProviders) Convey("Should not gather stats or call http endpoint", func() { So(getSystemStatsQuery, ShouldBeNil) @@ -179,7 +187,7 @@ func TestMetrics(t *testing.T) { setting.Packaging = "deb" wg.Add(1) - sendUsageStats(oauthProviders) + uss.sendUsageStats(oauthProviders) Convey("Should gather stats and call http endpoint", func() { if waitTimeout(&wg, 2*time.Second) { @@ -221,6 +229,8 @@ func TestMetrics(t *testing.T) { So(metrics.Get("stats.provisioned_dashboards.count").MustInt(), ShouldEqual, getSystemStatsQuery.Result.ProvisionedDashboards) So(metrics.Get("stats.snapshots.count").MustInt(), ShouldEqual, getSystemStatsQuery.Result.Snapshots) So(metrics.Get("stats.teams.count").MustInt(), ShouldEqual, getSystemStatsQuery.Result.Teams) + So(metrics.Get("stats.total_auth_token.count").MustInt64(), ShouldEqual, 15) + So(metrics.Get("stats.avg_auth_token_per_user.count").MustInt64(), ShouldEqual, 5) So(metrics.Get("stats.ds."+models.DS_ES+".count").MustInt(), ShouldEqual, 9) So(metrics.Get("stats.ds."+models.DS_PROMETHEUS+".count").MustInt(), ShouldEqual, 10) @@ -246,6 +256,7 @@ func TestMetrics(t *testing.T) { So(metrics.Get("stats.auth_enabled.oauth_grafana_com.count").MustInt(), ShouldEqual, 1) So(metrics.Get("stats.packaging.deb.count").MustInt(), ShouldEqual, 1) + }) }) diff --git a/pkg/login/ext_user.go b/pkg/login/ext_user.go index 42fb37ff9d0..f217f9fe33c 100644 --- a/pkg/login/ext_user.go +++ b/pkg/login/ext_user.go @@ -4,18 +4,30 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/quota" ) func init() { - bus.AddHandler("auth", UpsertUser) + registry.RegisterService(&LoginService{}) } var ( logger = log.New("login.ext_user") ) -func UpsertUser(cmd *m.UpsertUserCommand) error { +type LoginService struct { + Bus bus.Bus `inject:""` + QuotaService *quota.QuotaService `inject:""` +} + +func (ls *LoginService) Init() error { + ls.Bus.AddHandler(ls.UpsertUser) + + return nil +} + +func (ls *LoginService) UpsertUser(cmd *m.UpsertUserCommand) error { extUser := cmd.ExternalUser userQuery := &m.GetUserByAuthInfoQuery{ @@ -37,7 +49,7 @@ func UpsertUser(cmd *m.UpsertUserCommand) error { return ErrInvalidCredentials } - limitReached, err := quota.QuotaReached(cmd.ReqContext, "user") + limitReached, err := ls.QuotaService.QuotaReached(cmd.ReqContext, "user") if err != nil { log.Warn("Error getting user quota. error: %v", err) return ErrGettingUserQuota @@ -57,7 +69,7 @@ func UpsertUser(cmd *m.UpsertUserCommand) error { AuthModule: extUser.AuthModule, AuthId: extUser.AuthId, } - if err := bus.Dispatch(cmd2); err != nil { + if err := ls.Bus.Dispatch(cmd2); err != nil { return err } } @@ -78,12 +90,12 @@ func UpsertUser(cmd *m.UpsertUserCommand) error { // Sync isGrafanaAdmin permission if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin { - if err := bus.Dispatch(&m.UpdateUserPermissionsCommand{UserId: cmd.Result.Id, IsGrafanaAdmin: *extUser.IsGrafanaAdmin}); err != nil { + if err := ls.Bus.Dispatch(&m.UpdateUserPermissionsCommand{UserId: cmd.Result.Id, IsGrafanaAdmin: *extUser.IsGrafanaAdmin}); err != nil { return err } } - err = bus.Dispatch(&m.SyncTeamsCommand{ + err = ls.Bus.Dispatch(&m.SyncTeamsCommand{ User: cmd.Result, ExternalUser: extUser, }) diff --git a/pkg/login/ldap_test.go b/pkg/login/ldap_test.go index c02fa02e030..ef20feb1373 100644 --- a/pkg/login/ldap_test.go +++ b/pkg/login/ldap_test.go @@ -395,8 +395,11 @@ func ldapAutherScenario(desc string, fn scenarioFunc) { defer bus.ClearBusHandlers() sc := &scenarioContext{} + loginService := &LoginService{ + Bus: bus.GetBus(), + } - bus.AddHandler("test", UpsertUser) + bus.AddHandler("test", loginService.UpsertUser) bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.SyncTeamsCommand) error { return nil diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 326514a9687..bab2fb45127 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -1,17 +1,10 @@ package metrics import ( - "bytes" - "encoding/json" - "net/http" "runtime" - "strings" - "time" - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" + "github.com/prometheus/client_golang/prometheus" ) @@ -68,23 +61,6 @@ var ( grafanaBuildVersion *prometheus.GaugeVec ) -func newCounterVecStartingAtZero(opts prometheus.CounterOpts, labels []string, labelValues ...string) *prometheus.CounterVec { - counter := prometheus.NewCounterVec(opts, labels) - - for _, label := range labelValues { - counter.WithLabelValues(label).Add(0) - } - - return counter -} - -func newCounterStartingAtZero(opts prometheus.CounterOpts, labelValues ...string) prometheus.Counter { - counter := prometheus.NewCounter(opts) - counter.Add(0) - - return counter -} - func init() { M_Instance_Start = prometheus.NewCounter(prometheus.CounterOpts{ Name: "instance_start_total", @@ -308,7 +284,7 @@ func init() { Name: "build_info", Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built.", Namespace: exporterName, - }, []string{"version", "revision", "branch", "goversion"}) + }, []string{"version", "revision", "branch", "goversion", "edition"}) } // SetBuildInformation sets the build information for this binary @@ -317,8 +293,13 @@ func SetBuildInformation(version, revision, branch string) { // Once this have been released for some time we should be able to remote `M_Grafana_Version` // The reason we added a new one is that its common practice in the prometheus community // to name this metric `*_build_info` so its easy to do aggregation on all programs. + edition := "oss" + if setting.IsEnterprise { + edition = "enterprise" + } + M_Grafana_Version.WithLabelValues(version).Set(1) - grafanaBuildVersion.WithLabelValues(version, revision, branch, runtime.Version()).Set(1) + grafanaBuildVersion.WithLabelValues(version, revision, branch, runtime.Version(), edition).Set(1) } func initMetricVars() { @@ -362,154 +343,19 @@ func initMetricVars() { } -func updateTotalStats() { - statsQuery := models.GetSystemStatsQuery{} - if err := bus.Dispatch(&statsQuery); err != nil { - metricsLogger.Error("Failed to get system stats", "error", err) - return +func newCounterVecStartingAtZero(opts prometheus.CounterOpts, labels []string, labelValues ...string) *prometheus.CounterVec { + counter := prometheus.NewCounterVec(opts, labels) + + for _, label := range labelValues { + counter.WithLabelValues(label).Add(0) } - M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards)) - M_StatTotal_Users.Set(float64(statsQuery.Result.Users)) - M_StatActive_Users.Set(float64(statsQuery.Result.ActiveUsers)) - M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists)) - M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs)) + return counter } -var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" +func newCounterStartingAtZero(opts prometheus.CounterOpts, labelValues ...string) prometheus.Counter { + counter := prometheus.NewCounter(opts) + counter.Add(0) -func getEdition() string { - if setting.IsEnterprise { - return "enterprise" - } else { - return "oss" - } -} - -func sendUsageStats(oauthProviders map[string]bool) { - if !setting.ReportingEnabled { - return - } - - metricsLogger.Debug("Sending anonymous usage stats to stats.grafana.org") - - version := strings.Replace(setting.BuildVersion, ".", "_", -1) - - metrics := map[string]interface{}{} - report := map[string]interface{}{ - "version": version, - "metrics": metrics, - "os": runtime.GOOS, - "arch": runtime.GOARCH, - "edition": getEdition(), - "packaging": setting.Packaging, - } - - statsQuery := models.GetSystemStatsQuery{} - if err := bus.Dispatch(&statsQuery); err != nil { - metricsLogger.Error("Failed to get system stats", "error", err) - return - } - - metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards - metrics["stats.users.count"] = statsQuery.Result.Users - metrics["stats.orgs.count"] = statsQuery.Result.Orgs - metrics["stats.playlist.count"] = statsQuery.Result.Playlists - metrics["stats.plugins.apps.count"] = len(plugins.Apps) - metrics["stats.plugins.panels.count"] = len(plugins.Panels) - metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) - metrics["stats.alerts.count"] = statsQuery.Result.Alerts - metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers - metrics["stats.datasources.count"] = statsQuery.Result.Datasources - metrics["stats.stars.count"] = statsQuery.Result.Stars - metrics["stats.folders.count"] = statsQuery.Result.Folders - metrics["stats.dashboard_permissions.count"] = statsQuery.Result.DashboardPermissions - metrics["stats.folder_permissions.count"] = statsQuery.Result.FolderPermissions - metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards - metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots - metrics["stats.teams.count"] = statsQuery.Result.Teams - - dsStats := models.GetDataSourceStatsQuery{} - if err := bus.Dispatch(&dsStats); err != nil { - metricsLogger.Error("Failed to get datasource stats", "error", err) - return - } - - // send counters for each data source - // but ignore any custom data sources - // as sending that name could be sensitive information - dsOtherCount := 0 - for _, dsStat := range dsStats.Result { - if models.IsKnownDataSourcePlugin(dsStat.Type) { - metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count - } else { - dsOtherCount += dsStat.Count - } - } - metrics["stats.ds.other.count"] = dsOtherCount - - metrics["stats.packaging."+setting.Packaging+".count"] = 1 - - dsAccessStats := models.GetDataSourceAccessStatsQuery{} - if err := bus.Dispatch(&dsAccessStats); err != nil { - metricsLogger.Error("Failed to get datasource access stats", "error", err) - return - } - - // send access counters for each data source - // but ignore any custom data sources - // as sending that name could be sensitive information - dsAccessOtherCount := make(map[string]int64) - for _, dsAccessStat := range dsAccessStats.Result { - if dsAccessStat.Access == "" { - continue - } - - access := strings.ToLower(dsAccessStat.Access) - - if models.IsKnownDataSourcePlugin(dsAccessStat.Type) { - metrics["stats.ds_access."+dsAccessStat.Type+"."+access+".count"] = dsAccessStat.Count - } else { - old := dsAccessOtherCount[access] - dsAccessOtherCount[access] = old + dsAccessStat.Count - } - } - - for access, count := range dsAccessOtherCount { - metrics["stats.ds_access.other."+access+".count"] = count - } - - anStats := models.GetAlertNotifierUsageStatsQuery{} - if err := bus.Dispatch(&anStats); err != nil { - metricsLogger.Error("Failed to get alert notification stats", "error", err) - return - } - - for _, stats := range anStats.Result { - metrics["stats.alert_notifiers."+stats.Type+".count"] = stats.Count - } - - authTypes := map[string]bool{} - authTypes["anonymous"] = setting.AnonymousEnabled - authTypes["basic_auth"] = setting.BasicAuthEnabled - authTypes["ldap"] = setting.LdapEnabled - authTypes["auth_proxy"] = setting.AuthProxyEnabled - - for provider, enabled := range oauthProviders { - authTypes["oauth_"+provider] = enabled - } - - for authType, enabled := range authTypes { - enabledValue := 0 - if enabled { - enabledValue = 1 - } - metrics["stats.auth_enabled."+authType+".count"] = enabledValue - } - - out, _ := json.MarshalIndent(report, "", " ") - data := bytes.NewBuffer(out) - - client := http.Client{Timeout: 5 * time.Second} - go client.Post(usageStatsURL, "application/json", data) + return counter } diff --git a/pkg/metrics/service.go b/pkg/metrics/service.go index d2c0c815da9..44b83187cac 100644 --- a/pkg/metrics/service.go +++ b/pkg/metrics/service.go @@ -2,7 +2,6 @@ package metrics import ( "context" - "time" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/metrics/graphitebridge" @@ -30,7 +29,6 @@ type InternalMetricsService struct { intervalSeconds int64 graphiteCfg *graphitebridge.Config - oauthProviders map[string]bool } func (im *InternalMetricsService) Init() error { @@ -50,22 +48,6 @@ func (im *InternalMetricsService) Run(ctx context.Context) error { M_Instance_Start.Inc() - // set the total stats gauges before we publishing metrics - updateTotalStats() - - onceEveryDayTick := time.NewTicker(time.Hour * 24) - everyMinuteTicker := time.NewTicker(time.Minute) - defer onceEveryDayTick.Stop() - defer everyMinuteTicker.Stop() - - for { - select { - case <-onceEveryDayTick.C: - sendUsageStats(im.oauthProviders) - case <-everyMinuteTicker.C: - updateTotalStats() - case <-ctx.Done(): - return ctx.Err() - } - } + <-ctx.Done() + return ctx.Err() } diff --git a/pkg/metrics/settings.go b/pkg/metrics/settings.go index 18b9e78d6ff..048e4134690 100644 --- a/pkg/metrics/settings.go +++ b/pkg/metrics/settings.go @@ -5,8 +5,6 @@ import ( "strings" "time" - "github.com/grafana/grafana/pkg/social" - "github.com/grafana/grafana/pkg/metrics/graphitebridge" "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/client_golang/prometheus" @@ -24,8 +22,6 @@ func (im *InternalMetricsService) readSettings() error { return fmt.Errorf("Unable to parse metrics graphite section, %v", err) } - im.oauthProviders = social.GetOAuthProviders(im.Cfg) - return nil } diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 8545c3856c9..1fbd303bebd 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -682,6 +682,7 @@ type fakeUserAuthTokenService struct { tryRotateTokenProvider func(token *m.UserToken, clientIP, userAgent string) (bool, error) lookupTokenProvider func(unhashedToken string) (*m.UserToken, error) revokeTokenProvider func(token *m.UserToken) error + activeAuthTokenCount func() (int64, error) } func newFakeUserAuthTokenService() *fakeUserAuthTokenService { @@ -704,6 +705,9 @@ func newFakeUserAuthTokenService() *fakeUserAuthTokenService { revokeTokenProvider: func(token *m.UserToken) error { return nil }, + activeAuthTokenCount: func() (int64, error) { + return 10, nil + }, } } @@ -722,3 +726,7 @@ func (s *fakeUserAuthTokenService) TryRotateToken(token *m.UserToken, clientIP, func (s *fakeUserAuthTokenService) RevokeToken(token *m.UserToken) error { return s.revokeTokenProvider(token) } + +func (s *fakeUserAuthTokenService) ActiveTokenCount() (int64, error) { + return s.activeAuthTokenCount() +} diff --git a/pkg/middleware/quota.go b/pkg/middleware/quota.go index 43efca43485..51f906e2c92 100644 --- a/pkg/middleware/quota.go +++ b/pkg/middleware/quota.go @@ -9,16 +9,20 @@ import ( "github.com/grafana/grafana/pkg/services/quota" ) -func Quota(target string) macaron.Handler { - return func(c *m.ReqContext) { - limitReached, err := quota.QuotaReached(c, target) - if err != nil { - c.JsonApiErr(500, "failed to get quota", err) - return - } - if limitReached { - c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil) - return +// Quota returns a function that returns a function used to call quotaservice based on target name +func Quota(quotaService *quota.QuotaService) func(target string) macaron.Handler { + //https://open.spotify.com/track/7bZSoBEAEEUsGEuLOf94Jm?si=T1Tdju5qRSmmR0zph_6RBw fuuuuunky + return func(target string) macaron.Handler { + return func(c *m.ReqContext) { + limitReached, err := quotaService.QuotaReached(c, target) + if err != nil { + c.JsonApiErr(500, "failed to get quota", err) + return + } + if limitReached { + c.JsonApiErr(403, fmt.Sprintf("%s Quota reached", target), nil) + return + } } } } diff --git a/pkg/middleware/quota_test.go b/pkg/middleware/quota_test.go index e2a6ef63377..52b696cf037 100644 --- a/pkg/middleware/quota_test.go +++ b/pkg/middleware/quota_test.go @@ -3,9 +3,10 @@ package middleware import ( "testing" + "github.com/grafana/grafana/pkg/services/quota" + "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/session" "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" ) @@ -13,10 +14,6 @@ import ( func TestMiddlewareQuota(t *testing.T) { Convey("Given the grafana quota middleware", t, func() { - session.GetSessionCount = func() int { - return 4 - } - setting.AnonymousEnabled = false setting.Quota = setting.QuotaSettings{ Enabled: true, @@ -39,6 +36,12 @@ func TestMiddlewareQuota(t *testing.T) { }, } + fakeAuthTokenService := newFakeUserAuthTokenService() + qs := "a.QuotaService{ + AuthTokenService: fakeAuthTokenService, + } + QuotaFn := Quota(qs) + middlewareScenario("with user not logged in", func(sc *scenarioContext) { bus.AddHandler("globalQuota", func(query *m.GetGlobalQuotaByTargetQuery) error { query.Result = &m.GlobalQuotaDTO{ @@ -48,26 +51,30 @@ func TestMiddlewareQuota(t *testing.T) { } return nil }) + Convey("global quota not reached", func() { - sc.m.Get("/user", Quota("user"), sc.defaultHandler) + sc.m.Get("/user", QuotaFn("user"), sc.defaultHandler) sc.fakeReq("GET", "/user").exec() So(sc.resp.Code, ShouldEqual, 200) }) + Convey("global quota reached", func() { setting.Quota.Global.User = 4 - sc.m.Get("/user", Quota("user"), sc.defaultHandler) + sc.m.Get("/user", QuotaFn("user"), sc.defaultHandler) sc.fakeReq("GET", "/user").exec() So(sc.resp.Code, ShouldEqual, 403) }) + Convey("global session quota not reached", func() { setting.Quota.Global.Session = 10 - sc.m.Get("/user", Quota("session"), sc.defaultHandler) + sc.m.Get("/user", QuotaFn("session"), sc.defaultHandler) sc.fakeReq("GET", "/user").exec() So(sc.resp.Code, ShouldEqual, 200) }) + Convey("global session quota reached", func() { setting.Quota.Global.Session = 1 - sc.m.Get("/user", Quota("session"), sc.defaultHandler) + sc.m.Get("/user", QuotaFn("session"), sc.defaultHandler) sc.fakeReq("GET", "/user").exec() So(sc.resp.Code, ShouldEqual, 403) }) @@ -95,6 +102,7 @@ func TestMiddlewareQuota(t *testing.T) { } return nil }) + bus.AddHandler("userQuota", func(query *m.GetUserQuotaByTargetQuery) error { query.Result = &m.UserQuotaDTO{ Target: query.Target, @@ -103,6 +111,7 @@ func TestMiddlewareQuota(t *testing.T) { } return nil }) + bus.AddHandler("orgQuota", func(query *m.GetOrgQuotaByTargetQuery) error { query.Result = &m.OrgQuotaDTO{ Target: query.Target, @@ -111,45 +120,49 @@ func TestMiddlewareQuota(t *testing.T) { } return nil }) + Convey("global datasource quota reached", func() { setting.Quota.Global.DataSource = 4 - sc.m.Get("/ds", Quota("data_source"), sc.defaultHandler) + sc.m.Get("/ds", QuotaFn("data_source"), sc.defaultHandler) sc.fakeReq("GET", "/ds").exec() So(sc.resp.Code, ShouldEqual, 403) }) + Convey("user Org quota not reached", func() { setting.Quota.User.Org = 5 - sc.m.Get("/org", Quota("org"), sc.defaultHandler) + sc.m.Get("/org", QuotaFn("org"), sc.defaultHandler) sc.fakeReq("GET", "/org").exec() So(sc.resp.Code, ShouldEqual, 200) }) + Convey("user Org quota reached", func() { setting.Quota.User.Org = 4 - sc.m.Get("/org", Quota("org"), sc.defaultHandler) + sc.m.Get("/org", QuotaFn("org"), sc.defaultHandler) sc.fakeReq("GET", "/org").exec() So(sc.resp.Code, ShouldEqual, 403) }) + Convey("org dashboard quota not reached", func() { setting.Quota.Org.Dashboard = 10 - sc.m.Get("/dashboard", Quota("dashboard"), sc.defaultHandler) + sc.m.Get("/dashboard", QuotaFn("dashboard"), sc.defaultHandler) sc.fakeReq("GET", "/dashboard").exec() So(sc.resp.Code, ShouldEqual, 200) }) + Convey("org dashboard quota reached", func() { setting.Quota.Org.Dashboard = 4 - sc.m.Get("/dashboard", Quota("dashboard"), sc.defaultHandler) + sc.m.Get("/dashboard", QuotaFn("dashboard"), sc.defaultHandler) sc.fakeReq("GET", "/dashboard").exec() So(sc.resp.Code, ShouldEqual, 403) }) + Convey("org dashboard quota reached but quotas disabled", func() { setting.Quota.Org.Dashboard = 4 setting.Quota.Enabled = false - sc.m.Get("/dashboard", Quota("dashboard"), sc.defaultHandler) + sc.m.Get("/dashboard", QuotaFn("dashboard"), sc.defaultHandler) sc.fakeReq("GET", "/dashboard").exec() So(sc.resp.Code, ShouldEqual, 200) }) - }) - }) } diff --git a/pkg/middleware/recovery_test.go b/pkg/middleware/recovery_test.go index e041d42e56b..6736d699a39 100644 --- a/pkg/middleware/recovery_test.go +++ b/pkg/middleware/recovery_test.go @@ -6,7 +6,6 @@ import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/session" "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" macaron "gopkg.in/macaron.v1" @@ -66,7 +65,6 @@ func recoveryScenario(desc string, url string, fn scenarioFunc) { sc.userAuthTokenService = newFakeUserAuthTokenService() sc.m.Use(GetContextHandler(sc.userAuthTokenService)) // mock out gc goroutine - session.StartSessionGC = func() {} sc.m.Use(OrgRedirect()) sc.m.Use(AddDefaultResponseHeaders()) diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index e1cb185d92a..22c53dfa0dd 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -23,7 +23,7 @@ const ( DS_ACCESS_DIRECT = "direct" DS_ACCESS_PROXY = "proxy" DS_STACKDRIVER = "stackdriver" - DS_AZURE_MONITOR = "azure-monitor" + DS_AZURE_MONITOR = "grafana-azure-monitor-datasource" ) var ( diff --git a/pkg/models/datasource_cache.go b/pkg/models/datasource_cache.go index 66ba66e4d39..864adb4a5a6 100644 --- a/pkg/models/datasource_cache.go +++ b/pkg/models/datasource_cache.go @@ -8,6 +8,8 @@ import ( "net/http" "sync" "time" + + "github.com/grafana/grafana/pkg/setting" ) type proxyTransportCache struct { @@ -46,21 +48,18 @@ func (ds *DataSource) GetHttpTransport() (*http.Transport, error) { return t.Transport, nil } - var tlsSkipVerify, tlsClientAuth, tlsAuthWithCACert bool - if ds.JsonData != nil { - tlsClientAuth = ds.JsonData.Get("tlsAuth").MustBool(false) - tlsAuthWithCACert = ds.JsonData.Get("tlsAuthWithCACert").MustBool(false) - tlsSkipVerify = ds.JsonData.Get("tlsSkipVerify").MustBool(false) + tlsConfig, err := ds.GetTLSConfig() + if err != nil { + return nil, err } + tlsConfig.Renegotiation = tls.RenegotiateFreelyAsClient + transport := &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: tlsSkipVerify, - Renegotiation: tls.RenegotiateFreelyAsClient, - }, - Proxy: http.ProxyFromEnvironment, + TLSClientConfig: tlsConfig, + Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ - Timeout: 30 * time.Second, + Timeout: time.Duration(setting.DataProxyTimeout) * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).Dial, @@ -70,6 +69,26 @@ func (ds *DataSource) GetHttpTransport() (*http.Transport, error) { IdleConnTimeout: 90 * time.Second, } + ptc.cache[ds.Id] = cachedTransport{ + Transport: transport, + updated: ds.Updated, + } + + return transport, nil +} + +func (ds *DataSource) GetTLSConfig() (*tls.Config, error) { + var tlsSkipVerify, tlsClientAuth, tlsAuthWithCACert bool + if ds.JsonData != nil { + tlsClientAuth = ds.JsonData.Get("tlsAuth").MustBool(false) + tlsAuthWithCACert = ds.JsonData.Get("tlsAuthWithCACert").MustBool(false) + tlsSkipVerify = ds.JsonData.Get("tlsSkipVerify").MustBool(false) + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: tlsSkipVerify, + } + if tlsClientAuth || tlsAuthWithCACert { decrypted := ds.SecureJsonData.Decrypt() if tlsAuthWithCACert && len(decrypted["tlsCACert"]) > 0 { @@ -78,7 +97,7 @@ func (ds *DataSource) GetHttpTransport() (*http.Transport, error) { if !ok { return nil, errors.New("Failed to parse TLS CA PEM certificate") } - transport.TLSClientConfig.RootCAs = caPool + tlsConfig.RootCAs = caPool } if tlsClientAuth { @@ -86,14 +105,9 @@ func (ds *DataSource) GetHttpTransport() (*http.Transport, error) { if err != nil { return nil, err } - transport.TLSClientConfig.Certificates = []tls.Certificate{cert} + tlsConfig.Certificates = []tls.Certificate{cert} } } - ptc.cache[ds.Id] = cachedTransport{ - Transport: transport, - updated: ds.Updated, - } - - return transport, nil + return tlsConfig, nil } diff --git a/pkg/models/stats.go b/pkg/models/stats.go index d3e145dedf4..0edd204ec03 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -15,6 +15,7 @@ type SystemStats struct { FolderPermissions int64 Folders int64 ProvisionedDashboards int64 + AuthTokens int64 } type DataSourceStats struct { diff --git a/pkg/models/user_token.go b/pkg/models/user_token.go index c8084cf1eba..388bc2dd4a2 100644 --- a/pkg/models/user_token.go +++ b/pkg/models/user_token.go @@ -29,4 +29,5 @@ type UserTokenService interface { LookupToken(unhashedToken string) (*UserToken, error) TryRotateToken(token *UserToken, clientIP, userAgent string) (bool, error) RevokeToken(token *UserToken) error + ActiveTokenCount() (int64, error) } diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index ef5dccd779f..648575d54cd 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -35,6 +35,13 @@ func (s *UserAuthTokenService) Init() error { return nil } +func (s *UserAuthTokenService) ActiveTokenCount() (int64, error) { + var model userAuthToken + count, err := s.SQLStore.NewSession().Where(`created_at > ? AND rotated_at > ?`, s.createdAfterParam(), s.rotatedAfterParam()).Count(&model) + + return count, err +} + func (s *UserAuthTokenService) CreateToken(userId int64, clientIP, userAgent string) (*models.UserToken, error) { clientIP = util.ParseIPAddress(clientIP) token, err := util.RandomHex(16) @@ -79,13 +86,8 @@ func (s *UserAuthTokenService) LookupToken(unhashedToken string) (*models.UserTo s.log.Debug("looking up token", "unhashed", unhashedToken, "hashed", hashedToken) } - tokenMaxLifetime := time.Duration(s.Cfg.LoginMaxLifetimeDays) * 24 * time.Hour - tokenMaxInactiveLifetime := time.Duration(s.Cfg.LoginMaxInactiveLifetimeDays) * 24 * time.Hour - createdAfter := getTime().Add(-tokenMaxLifetime).Unix() - rotatedAfter := getTime().Add(-tokenMaxInactiveLifetime).Unix() - var model userAuthToken - exists, err := s.SQLStore.NewSession().Where("(auth_token = ? OR prev_auth_token = ?) AND created_at > ? AND rotated_at > ?", hashedToken, hashedToken, createdAfter, rotatedAfter).Get(&model) + exists, err := s.SQLStore.NewSession().Where("(auth_token = ? OR prev_auth_token = ?) AND created_at > ? AND rotated_at > ?", hashedToken, hashedToken, s.createdAfterParam(), s.rotatedAfterParam()).Get(&model) if err != nil { return nil, err } @@ -219,6 +221,16 @@ func (s *UserAuthTokenService) RevokeToken(token *models.UserToken) error { return nil } +func (s *UserAuthTokenService) createdAfterParam() int64 { + tokenMaxLifetime := time.Duration(s.Cfg.LoginMaxLifetimeDays) * 24 * time.Hour + return getTime().Add(-tokenMaxLifetime).Unix() +} + +func (s *UserAuthTokenService) rotatedAfterParam() int64 { + tokenMaxInactiveLifetime := time.Duration(s.Cfg.LoginMaxInactiveLifetimeDays) * 24 * time.Hour + return getTime().Add(-tokenMaxInactiveLifetime).Unix() +} + func hashToken(token string) string { hashBytes := sha256.Sum256([]byte(token + setting.SecretKey)) return hex.EncodeToString(hashBytes[:]) diff --git a/pkg/services/auth/auth_token_test.go b/pkg/services/auth/auth_token_test.go index 26dcbc5c868..49e7acc3a5b 100644 --- a/pkg/services/auth/auth_token_test.go +++ b/pkg/services/auth/auth_token_test.go @@ -31,6 +31,12 @@ func TestUserAuthToken(t *testing.T) { So(userToken, ShouldNotBeNil) So(userToken.AuthTokenSeen, ShouldBeFalse) + Convey("Can count active tokens", func() { + count, err := userAuthTokenService.ActiveTokenCount() + So(err, ShouldBeNil) + So(count, ShouldEqual, 1) + }) + Convey("When lookup unhashed token should return user auth token", func() { userToken, err := userAuthTokenService.LookupToken(userToken.UnhashedToken) So(err, ShouldBeNil) @@ -114,6 +120,12 @@ func TestUserAuthToken(t *testing.T) { notGood, err := userAuthTokenService.LookupToken(userToken.UnhashedToken) So(err, ShouldEqual, models.ErrUserTokenNotFound) So(notGood, ShouldBeNil) + + Convey("should not find active token when expired", func() { + count, err := userAuthTokenService.ActiveTokenCount() + So(err, ShouldBeNil) + So(count, ShouldEqual, 0) + }) }) Convey("when rotated_at is 5 days ago and created_at is 29 days and 23:59:59 ago should not find token", func() { diff --git a/pkg/services/provisioning/dashboards/config_reader.go b/pkg/services/provisioning/dashboards/config_reader.go index fa08972961d..c57ca1c55e1 100644 --- a/pkg/services/provisioning/dashboards/config_reader.go +++ b/pkg/services/provisioning/dashboards/config_reader.go @@ -59,7 +59,7 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) { files, err := ioutil.ReadDir(cr.path) if err != nil { - cr.log.Error("can't read dashboard provisioning files from directory", "path", cr.path) + cr.log.Error("can't read dashboard provisioning files from directory", "path", cr.path, "error", err) return dashboards, nil } diff --git a/pkg/services/provisioning/datasources/config_reader.go b/pkg/services/provisioning/datasources/config_reader.go index b2930c2b679..34c1418aa98 100644 --- a/pkg/services/provisioning/datasources/config_reader.go +++ b/pkg/services/provisioning/datasources/config_reader.go @@ -19,7 +19,7 @@ func (cr *configReader) readConfig(path string) ([]*DatasourcesAsConfig, error) files, err := ioutil.ReadDir(path) if err != nil { - cr.log.Error("can't read datasource provisioning files from directory", "path", path) + cr.log.Error("can't read datasource provisioning files from directory", "path", path, "error", err) return datasources, nil } diff --git a/pkg/services/provisioning/notifiers/config_reader.go b/pkg/services/provisioning/notifiers/config_reader.go index e712e8e3eff..c1b4cbf9f29 100644 --- a/pkg/services/provisioning/notifiers/config_reader.go +++ b/pkg/services/provisioning/notifiers/config_reader.go @@ -23,7 +23,7 @@ func (cr *configReader) readConfig(path string) ([]*notificationsAsConfig, error files, err := ioutil.ReadDir(path) if err != nil { - cr.log.Error("Can't read alert notification provisioning files from directory", "path", path) + cr.log.Error("Can't read alert notification provisioning files from directory", "path", path, "error", err) return notifications, nil } diff --git a/pkg/services/quota/quota.go b/pkg/services/quota/quota.go index 2ec399437e6..ff2528e31e8 100644 --- a/pkg/services/quota/quota.go +++ b/pkg/services/quota/quota.go @@ -3,11 +3,23 @@ package quota import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/services/session" + "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/setting" ) -func QuotaReached(c *m.ReqContext, target string) (bool, error) { +func init() { + registry.RegisterService(&QuotaService{}) +} + +type QuotaService struct { + AuthTokenService m.UserTokenService `inject:""` +} + +func (qs *QuotaService) Init() error { + return nil +} + +func (qs *QuotaService) QuotaReached(c *m.ReqContext, target string) (bool, error) { if !setting.Quota.Enabled { return false, nil } @@ -30,7 +42,12 @@ func QuotaReached(c *m.ReqContext, target string) (bool, error) { return true, nil } if target == "session" { - usedSessions := session.GetSessionCount() + + usedSessions, err := qs.AuthTokenService.ActiveTokenCount() + if err != nil { + return false, err + } + if int64(usedSessions) > scope.DefaultLimit { c.Logger.Debug("Sessions limit reached", "active", usedSessions, "limit", scope.DefaultLimit) return true, nil diff --git a/pkg/services/session/session.go b/pkg/services/session/session.go index 2e60b8a25d7..3481c99ce58 100644 --- a/pkg/services/session/session.go +++ b/pkg/services/session/session.go @@ -19,7 +19,7 @@ const ( var sessionManager *ms.Manager var sessionOptions *ms.Options -var StartSessionGC func() +var StartSessionGC func() = func() {} var GetSessionCount func() int var sessionLogger = log.New("session") var sessionConnMaxLifetime int64 diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index 2cec86e7239..2b7c35a4b4a 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -74,7 +74,8 @@ func GetSystemStats(query *m.GetSystemStatsQuery) error { sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_provisioning") + `) AS provisioned_dashboards,`) sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_snapshot") + `) AS snapshots,`) - sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("team") + `) AS teams`) + sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("team") + `) AS teams,`) + sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("user_auth_token") + `) AS auth_tokens`) var stats m.SystemStats _, err := x.SQL(sb.GetSqlString(), sb.params...).Get(&stats) diff --git a/pkg/tsdb/azuremonitor/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/azuremonitor-datasource.go new file mode 100644 index 00000000000..cae8d8bfb73 --- /dev/null +++ b/pkg/tsdb/azuremonitor/azuremonitor-datasource.go @@ -0,0 +1,312 @@ +package azuremonitor + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "path" + "strings" + "time" + + "github.com/grafana/grafana/pkg/api/pluginproxy" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/setting" + opentracing "github.com/opentracing/opentracing-go" + "golang.org/x/net/context/ctxhttp" + + "github.com/grafana/grafana/pkg/components/null" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/tsdb" +) + +// AzureMonitorDatasource calls the Azure Monitor API - one of the four API's supported +type AzureMonitorDatasource struct { + httpClient *http.Client + dsInfo *models.DataSource +} + +var ( + // 1m, 5m, 15m, 30m, 1h, 6h, 12h, 1d in milliseconds + allowedIntervalsMS = []int64{60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000} +) + +// executeTimeSeriesQuery does the following: +// 1. build the AzureMonitor url and querystring for each query +// 2. executes each query by calling the Azure Monitor API +// 3. parses the responses for each query into the timeseries format +func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, originalQueries []*tsdb.Query, timeRange *tsdb.TimeRange) (*tsdb.Response, error) { + result := &tsdb.Response{ + Results: map[string]*tsdb.QueryResult{}, + } + + queries, err := e.buildQueries(originalQueries, timeRange) + if err != nil { + return nil, err + } + + for _, query := range queries { + queryRes, resp, err := e.executeQuery(ctx, query, originalQueries, timeRange) + if err != nil { + return nil, err + } + // azlog.Debug("AzureMonitor", "Response", resp) + + err = e.parseResponse(queryRes, resp, query) + if err != nil { + queryRes.Error = err + } + result.Results[query.RefID] = queryRes + } + + return result, nil +} + +func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *tsdb.TimeRange) ([]*AzureMonitorQuery, error) { + azureMonitorQueries := []*AzureMonitorQuery{} + startTime, err := timeRange.ParseFrom() + if err != nil { + return nil, err + } + + endTime, err := timeRange.ParseTo() + if err != nil { + return nil, err + } + + for _, query := range queries { + var target string + + azureMonitorTarget := query.Model.Get("azureMonitor").MustMap() + azlog.Debug("AzureMonitor", "target", azureMonitorTarget) + + urlComponents := map[string]string{} + urlComponents["resourceGroup"] = fmt.Sprintf("%v", azureMonitorTarget["resourceGroup"]) + urlComponents["metricDefinition"] = fmt.Sprintf("%v", azureMonitorTarget["metricDefinition"]) + urlComponents["resourceName"] = fmt.Sprintf("%v", azureMonitorTarget["resourceName"]) + + ub := urlBuilder{ + ResourceGroup: urlComponents["resourceGroup"], + MetricDefinition: urlComponents["metricDefinition"], + ResourceName: urlComponents["resourceName"], + } + azureURL := ub.Build() + + alias := fmt.Sprintf("%v", azureMonitorTarget["alias"]) + + timeGrain := fmt.Sprintf("%v", azureMonitorTarget["timeGrain"]) + if timeGrain == "auto" { + autoInterval := e.findClosestAllowedIntervalMS(query.IntervalMs) + tg := &TimeGrain{} + timeGrain, err = tg.createISO8601DurationFromIntervalMS(autoInterval) + if err != nil { + return nil, err + } + } + + params := url.Values{} + params.Add("api-version", "2018-01-01") + params.Add("timespan", fmt.Sprintf("%v/%v", startTime.UTC().Format(time.RFC3339), endTime.UTC().Format(time.RFC3339))) + params.Add("interval", timeGrain) + params.Add("aggregation", fmt.Sprintf("%v", azureMonitorTarget["aggregation"])) + params.Add("metricnames", fmt.Sprintf("%v", azureMonitorTarget["metricName"])) + + dimension := strings.TrimSpace(fmt.Sprintf("%v", azureMonitorTarget["dimension"])) + dimensionFilter := strings.TrimSpace(fmt.Sprintf("%v", azureMonitorTarget["dimensionFilter"])) + if azureMonitorTarget["dimension"] != nil && azureMonitorTarget["dimensionFilter"] != nil && len(dimension) > 0 && len(dimensionFilter) > 0 { + params.Add("$filter", fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter)) + } + + target = params.Encode() + + if setting.Env == setting.DEV { + azlog.Debug("Azuremonitor request", "params", params) + } + + azureMonitorQueries = append(azureMonitorQueries, &AzureMonitorQuery{ + URL: azureURL, + UrlComponents: urlComponents, + Target: target, + Params: params, + RefID: query.RefId, + Alias: alias, + }) + } + + return azureMonitorQueries, nil +} + +func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureMonitorQuery, queries []*tsdb.Query, timeRange *tsdb.TimeRange) (*tsdb.QueryResult, AzureMonitorResponse, error) { + queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefID} + + req, err := e.createRequest(ctx, e.dsInfo) + if err != nil { + queryResult.Error = err + return queryResult, AzureMonitorResponse{}, nil + } + + req.URL.Path = path.Join(req.URL.Path, query.URL) + req.URL.RawQuery = query.Params.Encode() + queryResult.Meta.Set("rawQuery", req.URL.RawQuery) + + span, ctx := opentracing.StartSpanFromContext(ctx, "azuremonitor query") + span.SetTag("target", query.Target) + span.SetTag("from", timeRange.From) + span.SetTag("until", timeRange.To) + span.SetTag("datasource_id", e.dsInfo.Id) + span.SetTag("org_id", e.dsInfo.OrgId) + + defer span.Finish() + + opentracing.GlobalTracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header)) + + azlog.Debug("AzureMonitor", "Request URL", req.URL.String()) + res, err := ctxhttp.Do(ctx, e.httpClient, req) + if err != nil { + queryResult.Error = err + return queryResult, AzureMonitorResponse{}, nil + } + + data, err := e.unmarshalResponse(res) + if err != nil { + queryResult.Error = err + return queryResult, AzureMonitorResponse{}, nil + } + + return queryResult, data, nil +} + +func (e *AzureMonitorDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) { + // find plugin + plugin, ok := plugins.DataSources[dsInfo.Type] + if !ok { + return nil, errors.New("Unable to find datasource plugin Azure Monitor") + } + + var azureMonitorRoute *plugins.AppPluginRoute + for _, route := range plugin.Routes { + if route.Path == "azuremonitor" { + azureMonitorRoute = route + break + } + } + + cloudName := dsInfo.JsonData.Get("cloudName").MustString("azuremonitor") + subscriptionID := dsInfo.JsonData.Get("subscriptionId").MustString() + proxyPass := fmt.Sprintf("%s/subscriptions/%s", cloudName, subscriptionID) + + u, _ := url.Parse(dsInfo.Url) + u.Path = path.Join(u.Path, "render") + + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + azlog.Error("Failed to create request", "error", err) + return nil, fmt.Errorf("Failed to create request. error: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion)) + + pluginproxy.ApplyRoute(ctx, req, proxyPass, azureMonitorRoute, dsInfo) + + return req, nil +} + +func (e *AzureMonitorDatasource) unmarshalResponse(res *http.Response) (AzureMonitorResponse, error) { + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + if err != nil { + return AzureMonitorResponse{}, err + } + + if res.StatusCode/100 != 2 { + azlog.Error("Request failed", "status", res.Status, "body", string(body)) + return AzureMonitorResponse{}, fmt.Errorf(string(body)) + } + + var data AzureMonitorResponse + err = json.Unmarshal(body, &data) + if err != nil { + azlog.Error("Failed to unmarshal AzureMonitor response", "error", err, "status", res.Status, "body", string(body)) + return AzureMonitorResponse{}, err + } + + return data, nil +} + +func (e *AzureMonitorDatasource) parseResponse(queryRes *tsdb.QueryResult, data AzureMonitorResponse, query *AzureMonitorQuery) error { + if len(data.Value) == 0 { + return nil + } + + for _, series := range data.Value[0].Timeseries { + points := []tsdb.TimePoint{} + + metadataName := "" + metadataValue := "" + if len(series.Metadatavalues) > 0 { + metadataName = series.Metadatavalues[0].Name.LocalizedValue + metadataValue = series.Metadatavalues[0].Value + } + defaultMetricName := formatLegendKey(query.UrlComponents["resourceName"], data.Value[0].Name.LocalizedValue, metadataName, metadataValue) + + for _, point := range series.Data { + var value float64 + switch query.Params.Get("aggregation") { + case "Average": + value = point.Average + case "Total": + value = point.Total + case "Maximum": + value = point.Maximum + case "Minimum": + value = point.Minimum + case "Count": + value = point.Count + default: + value = point.Count + } + points = append(points, tsdb.NewTimePoint(null.FloatFrom(value), float64((point.TimeStamp).Unix())*1000)) + } + + queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{ + Name: defaultMetricName, + Points: points, + }) + } + + return nil +} + +// findClosestAllowedIntervalMs is used for the auto time grain setting. +// It finds the closest time grain from the list of allowed time grains for Azure Monitor +// using the Grafana interval in milliseconds +func (e *AzureMonitorDatasource) findClosestAllowedIntervalMS(intervalMs int64) int64 { + closest := allowedIntervalsMS[0] + + for i, allowed := range allowedIntervalsMS { + if intervalMs > allowed { + if i+1 < len(allowedIntervalsMS) { + closest = allowedIntervalsMS[i+1] + } else { + closest = allowed + } + } + } + return closest +} + +// formatLegendKey builds the legend key or timeseries name +func formatLegendKey(resourceName string, metricName string, metadataName string, metadataValue string) string { + if len(metadataName) > 0 { + return fmt.Sprintf("%s{%s=%s}.%s", resourceName, metadataName, metadataValue, metricName) + } + return fmt.Sprintf("%s.%s", resourceName, metricName) +} diff --git a/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go b/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go new file mode 100644 index 00000000000..b8d1d6cc266 --- /dev/null +++ b/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go @@ -0,0 +1,264 @@ +package azuremonitor + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/url" + "testing" + "time" + + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/tsdb" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestAzureMonitorDatasource(t *testing.T) { + Convey("AzureMonitorDatasource", t, func() { + datasource := &AzureMonitorDatasource{} + + Convey("Parse queries from frontend and build AzureMonitor API queries", func() { + fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local) + tsdbQuery := &tsdb.TsdbQuery{ + TimeRange: &tsdb.TimeRange{ + From: fmt.Sprintf("%v", fromStart.Unix()*1000), + To: fmt.Sprintf("%v", fromStart.Add(34*time.Minute).Unix()*1000), + }, + Queries: []*tsdb.Query{ + { + Model: simplejson.NewFromAny(map[string]interface{}{ + "azureMonitor": map[string]interface{}{ + "timeGrain": "PT1M", + "aggregation": "Average", + "resourceGroup": "grafanastaging", + "resourceName": "grafana", + "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricName": "Percentage CPU", + "alias": "testalias", + "queryType": "Azure Monitor", + }, + }), + RefId: "A", + }, + }, + } + Convey("and is a normal query", func() { + queries, err := datasource.buildQueries(tsdbQuery.Queries, tsdbQuery.TimeRange) + So(err, ShouldBeNil) + + So(len(queries), ShouldEqual, 1) + So(queries[0].RefID, ShouldEqual, "A") + So(queries[0].URL, ShouldEqual, "resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics") + So(queries[0].Target, ShouldEqual, "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") + So(len(queries[0].Params), ShouldEqual, 5) + So(queries[0].Params["timespan"][0], ShouldEqual, "2018-03-15T13:00:00Z/2018-03-15T13:34:00Z") + So(queries[0].Params["api-version"][0], ShouldEqual, "2018-01-01") + So(queries[0].Params["aggregation"][0], ShouldEqual, "Average") + So(queries[0].Params["metricnames"][0], ShouldEqual, "Percentage CPU") + So(queries[0].Params["interval"][0], ShouldEqual, "PT1M") + So(queries[0].Alias, ShouldEqual, "testalias") + }) + + Convey("and has a dimension filter", func() { + tsdbQuery.Queries[0].Model = simplejson.NewFromAny(map[string]interface{}{ + "azureMonitor": map[string]interface{}{ + "timeGrain": "PT1M", + "aggregation": "Average", + "resourceGroup": "grafanastaging", + "resourceName": "grafana", + "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricName": "Percentage CPU", + "alias": "testalias", + "queryType": "Azure Monitor", + "dimension": "blob", + "dimensionFilter": "*", + }, + }) + + queries, err := datasource.buildQueries(tsdbQuery.Queries, tsdbQuery.TimeRange) + So(err, ShouldBeNil) + + So(queries[0].Target, ShouldEqual, "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") + + }) + }) + + Convey("Parse AzureMonitor API response in the time series format", func() { + Convey("when data from query aggregated as average to one time series", func() { + data, err := loadTestFile("./test-data/1-azure-monitor-response-avg.json") + So(err, ShouldBeNil) + So(data.Interval, ShouldEqual, "PT1M") + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + Params: url.Values{ + "aggregation": {"Average"}, + }, + } + err = datasource.parseResponse(res, data, query) + So(err, ShouldBeNil) + + So(len(res.Series), ShouldEqual, 1) + So(res.Series[0].Name, ShouldEqual, "grafana.Percentage CPU") + So(len(res.Series[0].Points), ShouldEqual, 5) + + So(res.Series[0].Points[0][0].Float64, ShouldEqual, 2.0875) + So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1549620780000) + + So(res.Series[0].Points[1][0].Float64, ShouldEqual, 2.1525) + So(res.Series[0].Points[1][1].Float64, ShouldEqual, 1549620840000) + + So(res.Series[0].Points[2][0].Float64, ShouldEqual, 2.155) + So(res.Series[0].Points[2][1].Float64, ShouldEqual, 1549620900000) + + So(res.Series[0].Points[3][0].Float64, ShouldEqual, 3.6925) + So(res.Series[0].Points[3][1].Float64, ShouldEqual, 1549620960000) + + So(res.Series[0].Points[4][0].Float64, ShouldEqual, 2.44) + So(res.Series[0].Points[4][1].Float64, ShouldEqual, 1549621020000) + }) + + Convey("when data from query aggregated as total to one time series", func() { + data, err := loadTestFile("./test-data/2-azure-monitor-response-total.json") + So(err, ShouldBeNil) + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + Params: url.Values{ + "aggregation": {"Total"}, + }, + } + err = datasource.parseResponse(res, data, query) + So(err, ShouldBeNil) + + So(res.Series[0].Points[0][0].Float64, ShouldEqual, 8.26) + So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1549718940000) + }) + + Convey("when data from query aggregated as maximum to one time series", func() { + data, err := loadTestFile("./test-data/3-azure-monitor-response-maximum.json") + So(err, ShouldBeNil) + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + Params: url.Values{ + "aggregation": {"Maximum"}, + }, + } + err = datasource.parseResponse(res, data, query) + So(err, ShouldBeNil) + + So(res.Series[0].Points[0][0].Float64, ShouldEqual, 3.07) + So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1549722360000) + }) + + Convey("when data from query aggregated as minimum to one time series", func() { + data, err := loadTestFile("./test-data/4-azure-monitor-response-minimum.json") + So(err, ShouldBeNil) + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + Params: url.Values{ + "aggregation": {"Minimum"}, + }, + } + err = datasource.parseResponse(res, data, query) + So(err, ShouldBeNil) + + So(res.Series[0].Points[0][0].Float64, ShouldEqual, 1.51) + So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1549723380000) + }) + + Convey("when data from query aggregated as Count to one time series", func() { + data, err := loadTestFile("./test-data/5-azure-monitor-response-count.json") + So(err, ShouldBeNil) + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + Params: url.Values{ + "aggregation": {"Count"}, + }, + } + err = datasource.parseResponse(res, data, query) + So(err, ShouldBeNil) + + So(res.Series[0].Points[0][0].Float64, ShouldEqual, 4) + So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1549723440000) + }) + + Convey("when data from query aggregated as total and has dimension filter", func() { + data, err := loadTestFile("./test-data/6-azure-monitor-response-multi-dimension.json") + So(err, ShouldBeNil) + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + Params: url.Values{ + "aggregation": {"Average"}, + }, + } + err = datasource.parseResponse(res, data, query) + So(err, ShouldBeNil) + So(len(res.Series), ShouldEqual, 3) + + So(res.Series[0].Name, ShouldEqual, "grafana{blobtype=PageBlob}.Blob Count") + So(res.Series[0].Points[0][0].Float64, ShouldEqual, 3) + + So(res.Series[1].Name, ShouldEqual, "grafana{blobtype=BlockBlob}.Blob Count") + So(res.Series[1].Points[0][0].Float64, ShouldEqual, 1) + + So(res.Series[2].Name, ShouldEqual, "grafana{blobtype=Azure Data Lake Storage}.Blob Count") + So(res.Series[2].Points[0][0].Float64, ShouldEqual, 0) + }) + }) + + Convey("Find closest allowed interval for auto time grain", func() { + intervals := map[string]int64{ + "3m": 180000, + "5m": 300000, + "10m": 600000, + "15m": 900000, + "1d": 86400000, + "2d": 172800000, + } + + closest := datasource.findClosestAllowedIntervalMS(intervals["3m"]) + So(closest, ShouldEqual, intervals["5m"]) + + closest = datasource.findClosestAllowedIntervalMS(intervals["10m"]) + So(closest, ShouldEqual, intervals["15m"]) + + closest = datasource.findClosestAllowedIntervalMS(intervals["2d"]) + So(closest, ShouldEqual, intervals["1d"]) + }) + }) +} + +func loadTestFile(path string) (AzureMonitorResponse, error) { + var data AzureMonitorResponse + + jsonBody, err := ioutil.ReadFile(path) + if err != nil { + return data, err + } + err = json.Unmarshal(jsonBody, &data) + return data, err +} diff --git a/pkg/tsdb/azuremonitor/azuremonitor.go b/pkg/tsdb/azuremonitor/azuremonitor.go new file mode 100644 index 00000000000..31a42d21a12 --- /dev/null +++ b/pkg/tsdb/azuremonitor/azuremonitor.go @@ -0,0 +1,70 @@ +package azuremonitor + +import ( + "context" + "fmt" + "net/http" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/tsdb" +) + +var ( + azlog log.Logger +) + +// AzureMonitorExecutor executes queries for the Azure Monitor datasource - all four services +type AzureMonitorExecutor struct { + httpClient *http.Client + dsInfo *models.DataSource +} + +// NewAzureMonitorExecutor initializes a http client +func NewAzureMonitorExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) { + httpClient, err := dsInfo.GetHttpClient() + if err != nil { + return nil, err + } + + return &AzureMonitorExecutor{ + httpClient: httpClient, + dsInfo: dsInfo, + }, nil +} + +func init() { + azlog = log.New("tsdb.azuremonitor") + tsdb.RegisterTsdbQueryEndpoint("grafana-azure-monitor-datasource", NewAzureMonitorExecutor) +} + +// Query takes in the frontend queries, parses them into the query format +// expected by chosen Azure Monitor service (Azure Monitor, App Insights etc.) +// executes the queries against the API and parses the response into +// the right format +func (e *AzureMonitorExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) { + var result *tsdb.Response + var err error + + var azureMonitorQueries []*tsdb.Query + + for _, query := range tsdbQuery.Queries { + queryType := query.Model.Get("queryType").MustString("") + + switch queryType { + case "Azure Monitor": + azureMonitorQueries = append(azureMonitorQueries, query) + default: + return nil, fmt.Errorf("Alerting not supported for %s", queryType) + } + } + + azDatasource := &AzureMonitorDatasource{ + httpClient: e.httpClient, + dsInfo: e.dsInfo, + } + + result, err = azDatasource.executeTimeSeriesQuery(ctx, azureMonitorQueries, tsdbQuery.TimeRange) + + return result, err +} diff --git a/pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response-avg.json b/pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response-avg.json new file mode 100644 index 00000000000..5fc84f6afa6 --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response-avg.json @@ -0,0 +1,47 @@ +{ + "cost": 0, + "timespan": "2019-02-08T10:13:50Z\/2019-02-08T16:13:50Z", + "interval": "PT1M", + "value": [ + { + "id": "\/subscriptions\/xxx\/resourceGroups\/grafanastaging\/providers\/Microsoft.Compute\/virtualMachines\/grafana\/providers\/Microsoft.Insights\/metrics\/Percentage CPU", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "Percentage CPU", + "localizedValue": "Percentage CPU" + }, + "unit": "Percent", + "timeseries": [ + { + "metadatavalues": [ + + ], + "data": [ + { + "timeStamp": "2019-02-08T10:13:00Z", + "average": 2.0875 + }, + { + "timeStamp": "2019-02-08T10:14:00Z", + "average": 2.1525 + }, + { + "timeStamp": "2019-02-08T10:15:00Z", + "average": 2.155 + }, + { + "timeStamp": "2019-02-08T10:16:00Z", + "average": 3.6925 + }, + { + "timeStamp": "2019-02-08T10:17:00Z", + "average": 2.44 + } + ] + } + ] + } + ], + "namespace": "Microsoft.Compute\/virtualMachines", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/test-data/2-azure-monitor-response-total.json b/pkg/tsdb/azuremonitor/test-data/2-azure-monitor-response-total.json new file mode 100644 index 00000000000..d0b22f1b02c --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/2-azure-monitor-response-total.json @@ -0,0 +1,47 @@ +{ + "cost": 0, + "timespan": "2019-02-09T13:29:41Z\/2019-02-09T19:29:41Z", + "interval": "PT1M", + "value": [ + { + "id": "\/subscriptions\/xxx\/resourceGroups\/grafanastaging\/providers\/Microsoft.Compute\/virtualMachines\/grafana\/providers\/Microsoft.Insights\/metrics\/Percentage CPU", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "Percentage CPU", + "localizedValue": "Percentage CPU" + }, + "unit": "Percent", + "timeseries": [ + { + "metadatavalues": [ + + ], + "data": [ + { + "timeStamp": "2019-02-09T13:29:00Z", + "total": 8.26 + }, + { + "timeStamp": "2019-02-09T13:30:00Z", + "total": 8.7 + }, + { + "timeStamp": "2019-02-09T13:31:00Z", + "total": 14.82 + }, + { + "timeStamp": "2019-02-09T13:32:00Z", + "total": 10.07 + }, + { + "timeStamp": "2019-02-09T13:33:00Z", + "total": 8.52 + } + ] + } + ] + } + ], + "namespace": "Microsoft.Compute\/virtualMachines", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/test-data/3-azure-monitor-response-maximum.json b/pkg/tsdb/azuremonitor/test-data/3-azure-monitor-response-maximum.json new file mode 100644 index 00000000000..1e46cceb2be --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/3-azure-monitor-response-maximum.json @@ -0,0 +1,47 @@ +{ + "cost": 0, + "timespan": "2019-02-09T14:26:12Z\/2019-02-09T20:26:12Z", + "interval": "PT1M", + "value": [ + { + "id": "\/subscriptions\/xxx\/resourceGroups\/grafanastaging\/providers\/Microsoft.Compute\/virtualMachines\/grafana\/providers\/Microsoft.Insights\/metrics\/Percentage CPU", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "Percentage CPU", + "localizedValue": "Percentage CPU" + }, + "unit": "Percent", + "timeseries": [ + { + "metadatavalues": [ + + ], + "data": [ + { + "timeStamp": "2019-02-09T14:26:00Z", + "maximum": 3.07 + }, + { + "timeStamp": "2019-02-09T14:27:00Z", + "maximum": 2.92 + }, + { + "timeStamp": "2019-02-09T14:28:00Z", + "maximum": 2.87 + }, + { + "timeStamp": "2019-02-09T14:29:00Z", + "maximum": 2.27 + }, + { + "timeStamp": "2019-02-09T14:30:00Z", + "maximum": 2.52 + } + ] + } + ] + } + ], + "namespace": "Microsoft.Compute\/virtualMachines", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/test-data/4-azure-monitor-response-minimum.json b/pkg/tsdb/azuremonitor/test-data/4-azure-monitor-response-minimum.json new file mode 100644 index 00000000000..16310614214 --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/4-azure-monitor-response-minimum.json @@ -0,0 +1,47 @@ +{ + "cost": 0, + "timespan": "2019-02-09T14:43:21Z\/2019-02-09T20:43:21Z", + "interval": "PT1M", + "value": [ + { + "id": "\/subscriptions\/xxx\/resourceGroups\/grafanastaging\/providers\/Microsoft.Compute\/virtualMachines\/grafana\/providers\/Microsoft.Insights\/metrics\/Percentage CPU", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "Percentage CPU", + "localizedValue": "Percentage CPU" + }, + "unit": "Percent", + "timeseries": [ + { + "metadatavalues": [ + + ], + "data": [ + { + "timeStamp": "2019-02-09T14:43:00Z", + "minimum": 1.51 + }, + { + "timeStamp": "2019-02-09T14:44:00Z", + "minimum": 2.38 + }, + { + "timeStamp": "2019-02-09T14:45:00Z", + "minimum": 1.69 + }, + { + "timeStamp": "2019-02-09T14:46:00Z", + "minimum": 2.27 + }, + { + "timeStamp": "2019-02-09T14:47:00Z", + "minimum": 1.96 + } + ] + } + ] + } + ], + "namespace": "Microsoft.Compute\/virtualMachines", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/test-data/5-azure-monitor-response-count.json b/pkg/tsdb/azuremonitor/test-data/5-azure-monitor-response-count.json new file mode 100644 index 00000000000..91afc33f070 --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/5-azure-monitor-response-count.json @@ -0,0 +1,47 @@ +{ + "cost": 0, + "timespan": "2019-02-09T14:44:52Z\/2019-02-09T20:44:52Z", + "interval": "PT1M", + "value": [ + { + "id": "\/subscriptions\/xxx\/resourceGroups\/grafanastaging\/providers\/Microsoft.Compute\/virtualMachines\/grafana\/providers\/Microsoft.Insights\/metrics\/Percentage CPU", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "Percentage CPU", + "localizedValue": "Percentage CPU" + }, + "unit": "Percent", + "timeseries": [ + { + "metadatavalues": [ + + ], + "data": [ + { + "timeStamp": "2019-02-09T14:44:00Z", + "count": 4 + }, + { + "timeStamp": "2019-02-09T14:45:00Z", + "count": 4 + }, + { + "timeStamp": "2019-02-09T14:46:00Z", + "count": 4 + }, + { + "timeStamp": "2019-02-09T14:47:00Z", + "count": 4 + }, + { + "timeStamp": "2019-02-09T14:48:00Z", + "count": 4 + } + ] + } + ] + } + ], + "namespace": "Microsoft.Compute\/virtualMachines", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/test-data/6-azure-monitor-response-multi-dimension.json b/pkg/tsdb/azuremonitor/test-data/6-azure-monitor-response-multi-dimension.json new file mode 100644 index 00000000000..dddcef0e79c --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/6-azure-monitor-response-multi-dimension.json @@ -0,0 +1,128 @@ +{ + "cost": 0, + "timespan": "2019-02-09T15:21:39Z\/2019-02-09T21:21:39Z", + "interval": "PT1H", + "value": [ + { + "id": "\/subscriptions\/xxx\/resourceGroups\/grafanastaging\/providers\/Microsoft.Storage\/storageAccounts\/grafanastaging\/blobServices\/default\/providers\/Microsoft.Insights\/metrics\/BlobCount", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "BlobCount", + "localizedValue": "Blob Count" + }, + "unit": "Count", + "timeseries": [ + { + "metadatavalues": [ + { + "name": { + "value": "blobtype", + "localizedValue": "blobtype" + }, + "value": "PageBlob" + } + ], + "data": [ + { + "timeStamp": "2019-02-09T15:21:00Z", + "average": 3 + }, + { + "timeStamp": "2019-02-09T16:21:00Z", + "average": 3 + }, + { + "timeStamp": "2019-02-09T17:21:00Z", + "average": 3 + }, + { + "timeStamp": "2019-02-09T18:21:00Z", + "average": 3 + }, + { + "timeStamp": "2019-02-09T19:21:00Z", + "average": 3 + }, + { + "timeStamp": "2019-02-09T20:21:00Z" + } + ] + }, + { + "metadatavalues": [ + { + "name": { + "value": "blobtype", + "localizedValue": "blobtype" + }, + "value": "BlockBlob" + } + ], + "data": [ + { + "timeStamp": "2019-02-09T15:21:00Z", + "average": 1 + }, + { + "timeStamp": "2019-02-09T16:21:00Z", + "average": 1 + }, + { + "timeStamp": "2019-02-09T17:21:00Z", + "average": 1 + }, + { + "timeStamp": "2019-02-09T18:21:00Z", + "average": 1 + }, + { + "timeStamp": "2019-02-09T19:21:00Z", + "average": 1 + }, + { + "timeStamp": "2019-02-09T20:21:00Z" + } + ] + }, + { + "metadatavalues": [ + { + "name": { + "value": "blobtype", + "localizedValue": "blobtype" + }, + "value": "Azure Data Lake Storage" + } + ], + "data": [ + { + "timeStamp": "2019-02-09T15:21:00Z", + "average": 0 + }, + { + "timeStamp": "2019-02-09T16:21:00Z", + "average": 0 + }, + { + "timeStamp": "2019-02-09T17:21:00Z", + "average": 0 + }, + { + "timeStamp": "2019-02-09T18:21:00Z", + "average": 0 + }, + { + "timeStamp": "2019-02-09T19:21:00Z", + "average": 0 + }, + { + "timeStamp": "2019-02-09T20:21:00Z" + } + ] + } + ] + } + ], + "namespace": "Microsoft.Storage\/storageAccounts\/blobServices", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/time-grain.go b/pkg/tsdb/azuremonitor/time-grain.go new file mode 100644 index 00000000000..425e39b6208 --- /dev/null +++ b/pkg/tsdb/azuremonitor/time-grain.go @@ -0,0 +1,52 @@ +package azuremonitor + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/grafana/grafana/pkg/tsdb" +) + +// TimeGrain handles convertions between +// the ISO 8601 Duration format (PT1H), Kbn units (1h) and Time Grains (1 hour) +// Also handles using the automatic Grafana interval to calculate a ISO 8601 Duration. +type TimeGrain struct{} + +var ( + smallTimeUnits = []string{"hour", "minute", "h", "m"} +) + +func (tg *TimeGrain) createISO8601DurationFromIntervalMS(interval int64) (string, error) { + formatted := tsdb.FormatDuration(time.Duration(interval) * time.Millisecond) + + if strings.Contains(formatted, "ms") { + return "PT1M", nil + } + + timeValueString := formatted[0 : len(formatted)-1] + timeValue, err := strconv.Atoi(timeValueString) + if err != nil { + return "", fmt.Errorf("Could not parse interval %v to an ISO 8061 duration", interval) + } + + unit := formatted[len(formatted)-1:] + + if unit == "s" && timeValue < 60 { + // minimum interval is 1m for Azure Monitor + return "PT1M", nil + } + + return tg.createISO8601Duration(timeValue, unit), nil +} + +func (tg *TimeGrain) createISO8601Duration(timeValue int, timeUnit string) string { + for _, smallTimeUnit := range smallTimeUnits { + if timeUnit == smallTimeUnit { + return fmt.Sprintf("PT%v%v", timeValue, strings.ToUpper(timeUnit[0:1])) + } + } + + return fmt.Sprintf("P%v%v", timeValue, strings.ToUpper(timeUnit[0:1])) +} diff --git a/pkg/tsdb/azuremonitor/time-grain_test.go b/pkg/tsdb/azuremonitor/time-grain_test.go new file mode 100644 index 00000000000..2df3c92b0ff --- /dev/null +++ b/pkg/tsdb/azuremonitor/time-grain_test.go @@ -0,0 +1,71 @@ +package azuremonitor + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestTimeGrain(t *testing.T) { + Convey("TimeGrain", t, func() { + tgc := &TimeGrain{} + + Convey("create ISO 8601 Duration", func() { + Convey("when given a time unit smaller than a day", func() { + minuteKbnDuration := tgc.createISO8601Duration(1, "m") + hourKbnDuration := tgc.createISO8601Duration(2, "h") + minuteDuration := tgc.createISO8601Duration(1, "minute") + hourDuration := tgc.createISO8601Duration(2, "hour") + + Convey("should convert it to a time duration", func() { + So(minuteKbnDuration, ShouldEqual, "PT1M") + So(hourKbnDuration, ShouldEqual, "PT2H") + + So(minuteDuration, ShouldEqual, "PT1M") + So(hourDuration, ShouldEqual, "PT2H") + }) + }) + + Convey("when given the day time unit", func() { + kbnDuration := tgc.createISO8601Duration(1, "d") + duration := tgc.createISO8601Duration(2, "day") + + Convey("should convert it to a date duration", func() { + So(kbnDuration, ShouldEqual, "P1D") + So(duration, ShouldEqual, "P2D") + }) + }) + }) + + Convey("create ISO 8601 Duration from Grafana interval in milliseconds", func() { + Convey("and interval is less than a minute", func() { + durationMS, err := tgc.createISO8601DurationFromIntervalMS(100) + So(err, ShouldBeNil) + + durationS, err := tgc.createISO8601DurationFromIntervalMS(59999) + So(err, ShouldBeNil) + + Convey("should be rounded up to a minute as is the minimum interval for Azure Monitor", func() { + So(durationMS, ShouldEqual, "PT1M") + So(durationS, ShouldEqual, "PT1M") + }) + }) + + Convey("and interval is more than a minute", func() { + intervals := map[string]int64{ + "10m": 600000, + "2d": 172800000, + } + durationM, err := tgc.createISO8601DurationFromIntervalMS(intervals["10m"]) + So(err, ShouldBeNil) + durationD, err := tgc.createISO8601DurationFromIntervalMS(intervals["2d"]) + So(err, ShouldBeNil) + + Convey("should be rounded up to a minute as is the minimum interval for Azure Monitor", func() { + So(durationM, ShouldEqual, "PT10M") + So(durationD, ShouldEqual, "P2D") + }) + }) + }) + }) +} diff --git a/pkg/tsdb/azuremonitor/types.go b/pkg/tsdb/azuremonitor/types.go new file mode 100644 index 00000000000..b547c71f185 --- /dev/null +++ b/pkg/tsdb/azuremonitor/types.go @@ -0,0 +1,77 @@ +package azuremonitor + +import ( + "net/url" + "time" +) + +// AzureMonitorQuery is the query for all the services as they have similar queries +// with a url, a querystring and an alias field +type AzureMonitorQuery struct { + URL string + UrlComponents map[string]string + Target string + Params url.Values + RefID string + Alias string +} + +// AzureMonitorResponse is the json response from the Azure Monitor API +type AzureMonitorResponse struct { + Cost int `json:"cost"` + Timespan string `json:"timespan"` + Interval string `json:"interval"` + Value []struct { + ID string `json:"id"` + Type string `json:"type"` + Name struct { + Value string `json:"value"` + LocalizedValue string `json:"localizedValue"` + } `json:"name"` + Unit string `json:"unit"` + Timeseries []struct { + Metadatavalues []struct { + Name struct { + Value string `json:"value"` + LocalizedValue string `json:"localizedValue"` + } `json:"name"` + Value string `json:"value"` + } `json:"metadatavalues"` + Data []struct { + TimeStamp time.Time `json:"timeStamp"` + Average float64 `json:"average,omitempty"` + Total float64 `json:"total,omitempty"` + Count float64 `json:"count,omitempty"` + Maximum float64 `json:"maximum,omitempty"` + Minimum float64 `json:"minimum,omitempty"` + } `json:"data"` + } `json:"timeseries"` + } `json:"value"` + Namespace string `json:"namespace"` + Resourceregion string `json:"resourceregion"` +} + +// ApplicationInsightsResponse is the json response from the Application Insights API +type ApplicationInsightsResponse struct { + Tables []struct { + TableName string `json:"TableName"` + Columns []struct { + ColumnName string `json:"ColumnName"` + DataType string `json:"DataType"` + ColumnType string `json:"ColumnType"` + } `json:"Columns"` + Rows [][]interface{} `json:"Rows"` + } `json:"Tables"` +} + +// AzureLogAnalyticsResponse is the json response object from the Azure Log Analytics API. +type AzureLogAnalyticsResponse struct { + Tables []struct { + Name string `json:"name"` + Columns []struct { + Name string `json:"name"` + Type string `json:"type"` + } `json:"columns"` + Rows [][]interface{} `json:"rows"` + } `json:"tables"` +} diff --git a/pkg/tsdb/azuremonitor/url-builder.go b/pkg/tsdb/azuremonitor/url-builder.go new file mode 100644 index 00000000000..c252048f517 --- /dev/null +++ b/pkg/tsdb/azuremonitor/url-builder.go @@ -0,0 +1,28 @@ +package azuremonitor + +import ( + "fmt" + "strings" +) + +// urlBuilder builds the URL for calling the Azure Monitor API +type urlBuilder struct { + ResourceGroup string + MetricDefinition string + ResourceName string +} + +// Build checks the metric definition property to see which form of the url +// should be returned +func (ub *urlBuilder) Build() string { + + if strings.Count(ub.MetricDefinition, "/") > 1 { + rn := strings.Split(ub.ResourceName, "/") + lastIndex := strings.LastIndex(ub.MetricDefinition, "/") + service := ub.MetricDefinition[lastIndex+1:] + md := ub.MetricDefinition[0:lastIndex] + return fmt.Sprintf("resourceGroups/%s/providers/%s/%s/%s/%s/providers/microsoft.insights/metrics", ub.ResourceGroup, md, rn[0], service, rn[1]) + } + + return fmt.Sprintf("resourceGroups/%s/providers/%s/%s/providers/microsoft.insights/metrics", ub.ResourceGroup, ub.MetricDefinition, ub.ResourceName) +} diff --git a/pkg/tsdb/azuremonitor/url-builder_test.go b/pkg/tsdb/azuremonitor/url-builder_test.go new file mode 100644 index 00000000000..85c4f81bc83 --- /dev/null +++ b/pkg/tsdb/azuremonitor/url-builder_test.go @@ -0,0 +1,45 @@ +package azuremonitor + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestURLBuilder(t *testing.T) { + Convey("AzureMonitor URL Builder", t, func() { + + Convey("when metric definition is in the short form", func() { + ub := &urlBuilder{ + ResourceGroup: "rg", + MetricDefinition: "Microsoft.Compute/virtualMachines", + ResourceName: "rn", + } + + url := ub.Build() + So(url, ShouldEqual, "resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metrics") + }) + + Convey("when metric definition is Microsoft.Storage/storageAccounts/blobServices", func() { + ub := &urlBuilder{ + ResourceGroup: "rg", + MetricDefinition: "Microsoft.Storage/storageAccounts/blobServices", + ResourceName: "rn1/default", + } + + url := ub.Build() + So(url, ShouldEqual, "resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default/providers/microsoft.insights/metrics") + }) + + Convey("when metric definition is Microsoft.Storage/storageAccounts/fileServices", func() { + ub := &urlBuilder{ + ResourceGroup: "rg", + MetricDefinition: "Microsoft.Storage/storageAccounts/fileServices", + ResourceName: "rn1/default", + } + + url := ub.Build() + So(url, ShouldEqual, "resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/fileServices/default/providers/microsoft.insights/metrics") + }) + }) +} diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index 34181d19673..ddda26dfd24 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -55,6 +55,7 @@ func init() { "AWS/DynamoDB": {"ConditionalCheckFailedRequests", "ConsumedReadCapacityUnits", "ConsumedWriteCapacityUnits", "OnlineIndexConsumedWriteCapacity", "OnlineIndexPercentageProgress", "OnlineIndexThrottleEvents", "ProvisionedReadCapacityUnits", "ProvisionedWriteCapacityUnits", "ReadThrottleEvents", "ReturnedBytes", "ReturnedItemCount", "ReturnedRecordsCount", "SuccessfulRequestLatency", "SystemErrors", "TimeToLiveDeletedItemCount", "ThrottledRequests", "UserErrors", "WriteThrottleEvents"}, "AWS/EBS": {"VolumeReadBytes", "VolumeWriteBytes", "VolumeReadOps", "VolumeWriteOps", "VolumeTotalReadTime", "VolumeTotalWriteTime", "VolumeIdleTime", "VolumeQueueLength", "VolumeThroughputPercentage", "VolumeConsumedReadWriteOps", "BurstBalance"}, "AWS/EC2": {"CPUCreditUsage", "CPUCreditBalance", "CPUUtilization", "DiskReadOps", "DiskWriteOps", "DiskReadBytes", "DiskWriteBytes", "NetworkIn", "NetworkOut", "NetworkPacketsIn", "NetworkPacketsOut", "StatusCheckFailed", "StatusCheckFailed_Instance", "StatusCheckFailed_System"}, + "AWS/EC2/API": {"ClientErrors", "RequestLimitExceeded", "ServerErrors", "SuccessfulCalls"}, "AWS/EC2Spot": {"AvailableInstancePoolsCount", "BidsSubmittedForCapacity", "EligibleInstancePoolCount", "FulfilledCapacity", "MaxPercentCapacityAllocation", "PendingCapacity", "PercentCapacityAllocation", "TargetCapacity", "TerminatingCapacity"}, "AWS/ECS": {"CPUReservation", "MemoryReservation", "CPUUtilization", "MemoryUtilization"}, "AWS/EFS": {"BurstCreditBalance", "ClientConnections", "DataReadIOBytes", "DataWriteIOBytes", "MetadataIOBytes", "TotalIOBytes", "PermittedThroughput", "PercentIOLimit"}, @@ -133,6 +134,7 @@ func init() { "AWS/DynamoDB": {"TableName", "GlobalSecondaryIndexName", "Operation", "StreamLabel"}, "AWS/EBS": {"VolumeId"}, "AWS/EC2": {"AutoScalingGroupName", "ImageId", "InstanceId", "InstanceType"}, + "AWS/EC2/API": {}, "AWS/EC2Spot": {"AvailabilityZone", "FleetRequestId", "InstanceType"}, "AWS/ECS": {"ClusterName", "ServiceName"}, "AWS/EFS": {"FileSystemId"}, diff --git a/pkg/tsdb/interval.go b/pkg/tsdb/interval.go index fd6adee39d7..7819ef3ecad 100644 --- a/pkg/tsdb/interval.go +++ b/pkg/tsdb/interval.go @@ -59,11 +59,11 @@ func (ic *intervalCalculator) Calculate(timerange *TimeRange, minInterval time.D interval := time.Duration((to - from) / defaultRes) if interval < minInterval { - return Interval{Text: formatDuration(minInterval), Value: minInterval} + return Interval{Text: FormatDuration(minInterval), Value: minInterval} } rounded := roundInterval(interval) - return Interval{Text: formatDuration(rounded), Value: rounded} + return Interval{Text: FormatDuration(rounded), Value: rounded} } func GetIntervalFrom(dsInfo *models.DataSource, queryModel *simplejson.Json, defaultInterval time.Duration) (time.Duration, error) { @@ -89,7 +89,8 @@ func GetIntervalFrom(dsInfo *models.DataSource, queryModel *simplejson.Json, def return parsedInterval, nil } -func formatDuration(inter time.Duration) string { +// FormatDuration converts a duration into the kbn format e.g. 1m 2h or 3d +func FormatDuration(inter time.Duration) string { if inter >= year { return fmt.Sprintf("%dy", inter/year) } diff --git a/pkg/tsdb/interval_test.go b/pkg/tsdb/interval_test.go index 941b08dd554..4cd3fcea532 100644 --- a/pkg/tsdb/interval_test.go +++ b/pkg/tsdb/interval_test.go @@ -51,11 +51,11 @@ func TestInterval(t *testing.T) { }) Convey("Format value", func() { - So(formatDuration(time.Second*61), ShouldEqual, "1m") - So(formatDuration(time.Millisecond*30), ShouldEqual, "30ms") - So(formatDuration(time.Hour*23), ShouldEqual, "23h") - So(formatDuration(time.Hour*24), ShouldEqual, "1d") - So(formatDuration(time.Hour*24*367), ShouldEqual, "1y") + So(FormatDuration(time.Second*61), ShouldEqual, "1m") + So(FormatDuration(time.Millisecond*30), ShouldEqual, "30ms") + So(FormatDuration(time.Hour*23), ShouldEqual, "23h") + So(FormatDuration(time.Hour*24), ShouldEqual, "1d") + So(FormatDuration(time.Hour*24*367), ShouldEqual, "1y") }) }) } diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 35b03e489a0..d307e12166c 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -32,6 +32,18 @@ func newMysqlQueryEndpoint(datasource *models.DataSource) (tsdb.TsdbQueryEndpoin datasource.Url, datasource.Database, ) + + tlsConfig, err := datasource.GetTLSConfig() + if err != nil { + return nil, err + } + + if tlsConfig.RootCAs != nil || len(tlsConfig.Certificates) > 0 { + tlsConfigString := fmt.Sprintf("ds%d", datasource.Id) + mysql.RegisterTLSConfig(tlsConfigString, tlsConfig) + cnnstr += "&tls=" + tlsConfigString + } + logger.Debug("getEngine", "connection", cnnstr) config := tsdb.SqlQueryEndpointConfiguration{ diff --git a/public/app/core/components/Animations/FadeIn.tsx b/public/app/core/components/Animations/FadeIn.tsx index ea9a92d5f0f..d667b54261e 100644 --- a/public/app/core/components/Animations/FadeIn.tsx +++ b/public/app/core/components/Animations/FadeIn.tsx @@ -1,11 +1,12 @@ import React, { FC } from 'react'; -import Transition from 'react-transition-group/Transition'; +import Transition, { ExitHandler } from 'react-transition-group/Transition'; interface Props { duration: number; children: JSX.Element; in: boolean; unmountOnExit?: boolean; + onExited?: ExitHandler; } export const FadeIn: FC = props => { @@ -22,7 +23,12 @@ export const FadeIn: FC = props => { }; return ( - + {state => (
{ return (
{title}
- + {buttonTitle} diff --git a/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap b/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap index b85660bcc6f..21c2ed294b4 100644 --- a/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap +++ b/public/app/core/components/EmptyListCTA/__snapshots__/EmptyListCTA.test.tsx.snap @@ -10,7 +10,7 @@ exports[`EmptyListCTA renders correctly 1`] = ` Title
diff --git a/public/app/core/components/OrgActionBar/OrgActionBar.tsx b/public/app/core/components/OrgActionBar/OrgActionBar.tsx index 8fc34a018e1..b6b2046736f 100644 --- a/public/app/core/components/OrgActionBar/OrgActionBar.tsx +++ b/public/app/core/components/OrgActionBar/OrgActionBar.tsx @@ -35,7 +35,7 @@ export default class OrgActionBar extends PureComponent { onSetLayoutMode(mode)} />
diff --git a/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap b/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap index dc53e7863ea..25de037930a 100644 --- a/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap +++ b/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap @@ -29,7 +29,7 @@ exports[`Render should render component 1`] = ` className="page-action-bar__spacer" /> diff --git a/public/app/core/components/PermissionList/AddPermission.tsx b/public/app/core/components/PermissionList/AddPermission.tsx index 30219371257..80afcedf873 100644 --- a/public/app/core/components/PermissionList/AddPermission.tsx +++ b/public/app/core/components/PermissionList/AddPermission.tsx @@ -130,7 +130,7 @@ class AddPermissions extends Component {
-
diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index 33aca1de2aa..171e0e8109e 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -126,7 +126,7 @@ export class SharedPreferences extends PureComponent { />
-
diff --git a/public/app/core/components/manage_dashboards/manage_dashboards.html b/public/app/core/components/manage_dashboards/manage_dashboards.html index 6fbd65afaf5..4ef2d7c9a66 100644 --- a/public/app/core/components/manage_dashboards/manage_dashboards.html +++ b/public/app/core/components/manage_dashboards/manage_dashboards.html @@ -5,16 +5,13 @@
-
- - Dashboard + + New Dashboard - - - Folder + + New Folder - - + Import diff --git a/public/app/core/components/sidemenu/SideMenu.test.tsx b/public/app/core/components/sidemenu/SideMenu.test.tsx index 2a262adca5a..f7b7df69cfc 100644 --- a/public/app/core/components/sidemenu/SideMenu.test.tsx +++ b/public/app/core/components/sidemenu/SideMenu.test.tsx @@ -8,6 +8,16 @@ jest.mock('../../app_events', () => ({ emit: jest.fn(), })); +jest.mock('app/store/store', () => ({ + store: { + getState: jest.fn().mockReturnValue({ + location: { + lastUpdated: 0, + }, + }), + }, +})); + jest.mock('app/core/services/context_srv', () => ({ contextSrv: { sidemenu: true, diff --git a/public/app/core/components/sidemenu/SideMenu.tsx b/public/app/core/components/sidemenu/SideMenu.tsx index fd3e0d95564..29ef0fed069 100644 --- a/public/app/core/components/sidemenu/SideMenu.tsx +++ b/public/app/core/components/sidemenu/SideMenu.tsx @@ -3,9 +3,16 @@ import appEvents from '../../app_events'; import { contextSrv } from 'app/core/services/context_srv'; import TopSection from './TopSection'; import BottomSection from './BottomSection'; +import { store } from 'app/store/store'; export class SideMenu extends PureComponent { toggleSideMenu = () => { + // ignore if we just made a location change, stops hiding sidemenu on double clicks of back button + const timeSinceLocationChanged = new Date().getTime() - store.getState().location.lastUpdated; + if (timeSinceLocationChanged < 1000) { + return; + } + contextSrv.toggleSideMenu(); appEvents.emit('toggle-sidemenu'); }; diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 1f289fc4b27..80987b8fc88 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -1,4 +1,3 @@ -import './directives/dash_class'; import './directives/dropdown_typeahead'; import './directives/autofill_event_fix'; import './directives/metric_segment'; diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts deleted file mode 100644 index 1fb93d29cf3..00000000000 --- a/public/app/core/directives/dash_class.ts +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'jquery'; -import _ from 'lodash'; -import coreModule from '../core_module'; - -/** @ngInject */ -function dashClass($timeout) { - return { - link: ($scope, elem) => { - const body = $('body'); - - $scope.ctrl.dashboard.events.on('view-mode-changed', panel => { - console.log('view-mode-changed', panel.fullscreen); - if (panel.fullscreen) { - body.addClass('panel-in-fullscreen'); - } else { - $timeout(() => { - body.removeClass('panel-in-fullscreen'); - }); - } - }); - - body.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); - - $scope.$watch('ctrl.dashboardViewState.state.editview', newValue => { - if (newValue) { - elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); - setTimeout(() => { - elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); - }, 10); - } else { - elem.removeClass('dashboard-page--settings-opening'); - elem.removeClass('dashboard-page--settings-open'); - } - }); - }, - }; -} - -coreModule.directive('dashClass', dashClass); diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index abcd5563bd0..f4ad11e1b0e 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -340,6 +340,11 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Time return a[1] - b[1]; }); - return { datapoints: series.datapoints, target: series.alias, color: series.color }; + return { + datapoints: series.datapoints, + target: series.alias, + alias: series.alias, + color: series.color, + }; }); } diff --git a/public/app/core/reducers/location.ts b/public/app/core/reducers/location.ts index c038ab53c9f..dff1ac8f5c1 100644 --- a/public/app/core/reducers/location.ts +++ b/public/app/core/reducers/location.ts @@ -9,6 +9,7 @@ export const initialState: LocationState = { query: {}, routeParams: {}, replace: false, + lastUpdated: 0, }; export const locationReducer = (state = initialState, action: Action): LocationState => { @@ -28,6 +29,7 @@ export const locationReducer = (state = initialState, action: Action): LocationS query: { ...query }, routeParams: routeParams || state.routeParams, replace: replace === true, + lastUpdated: new Date().getTime(), }; } } diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 917d1801c0e..7dab7cffd6f 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -139,6 +139,10 @@ export class KeybindingSrv { ); } + unbind(keyArg: string, keyType?: string) { + Mousetrap.unbind(keyArg, keyType); + } + showDashEditView() { const search = _.extend(this.$location.search(), { editview: 'settings' }); this.$location.search(search); @@ -291,3 +295,17 @@ export class KeybindingSrv { } coreModule.service('keybindingSrv', KeybindingSrv); + +/** + * Code below exports the service to react components + */ + +let singletonInstance: KeybindingSrv; + +export function setKeybindingSrv(instance: KeybindingSrv) { + singletonInstance = instance; +} + +export function getKeybindingSrv(): KeybindingSrv { + return singletonInstance; +} diff --git a/public/app/core/utils/explore.test.ts b/public/app/core/utils/explore.test.ts index 1c00142c3b8..a8872bcc53d 100644 --- a/public/app/core/utils/explore.test.ts +++ b/public/app/core/utils/explore.test.ts @@ -8,6 +8,7 @@ import { } from './explore'; import { ExploreUrlState } from 'app/types/explore'; import store from 'app/core/store'; +import { LogsDedupStrategy } from 'app/core/logs_model'; const DEFAULT_EXPLORE_STATE: ExploreUrlState = { datasource: null, @@ -17,7 +18,8 @@ const DEFAULT_EXPLORE_STATE: ExploreUrlState = { showingGraph: true, showingTable: true, showingLogs: true, - } + dedupStrategy: LogsDedupStrategy.none, + }, }; describe('state functions', () => { @@ -78,7 +80,7 @@ describe('state functions', () => { expect(serializeStateToUrlParam(state)).toBe( '{"datasource":"foo","queries":[{"expr":"metric{test=\\"a/b\\"}"},' + '{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"},' + - '"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}' + '"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true,"dedupStrategy":"none"}}' ); }); @@ -100,7 +102,7 @@ describe('state functions', () => { }, }; expect(serializeStateToUrlParam(state, true)).toBe( - '["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"},{"ui":[true,true,true]}]' + '["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"},{"ui":[true,true,true,"none"]}]' ); }); }); diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 107f411353c..913d8d1704f 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -21,6 +21,7 @@ import { QueryIntervals, QueryOptions, } from 'app/types/explore'; +import { LogsDedupStrategy } from 'app/core/logs_model'; export const DEFAULT_RANGE = { from: 'now-6h', @@ -31,6 +32,7 @@ export const DEFAULT_UI_STATE = { showingTable: true, showingGraph: true, showingLogs: true, + dedupStrategy: LogsDedupStrategy.none, }; const MAX_HISTORY_ITEMS = 100; @@ -183,6 +185,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { showingGraph: segment.ui[0], showingLogs: segment.ui[1], showingTable: segment.ui[2], + dedupStrategy: segment.ui[3], }; } }); @@ -204,7 +207,14 @@ export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: bo urlState.range.to, urlState.datasource, ...urlState.queries, - { ui: [!!urlState.ui.showingGraph, !!urlState.ui.showingLogs, !!urlState.ui.showingTable] }, + { + ui: [ + !!urlState.ui.showingGraph, + !!urlState.ui.showingLogs, + !!urlState.ui.showingTable, + urlState.ui.dedupStrategy, + ], + }, ]); } return JSON.stringify(urlState); diff --git a/public/app/core/utils/reselect.ts b/public/app/core/utils/reselect.ts new file mode 100644 index 00000000000..7c8fc7727b0 --- /dev/null +++ b/public/app/core/utils/reselect.ts @@ -0,0 +1,5 @@ +import { memoize } from 'lodash'; +import { createSelectorCreator } from 'reselect'; + +const hashFn = (...args) => args.reduce((acc, val) => acc + '-' + JSON.stringify(val), ''); +export const createLodashMemoizedSelector = createSelectorCreator(memoize, hashFn); diff --git a/public/app/features/admin/partials/edit_org.html b/public/app/features/admin/partials/edit_org.html index 975d663e9b0..911181ef999 100644 --- a/public/app/features/admin/partials/edit_org.html +++ b/public/app/features/admin/partials/edit_org.html @@ -10,7 +10,7 @@
- +
diff --git a/public/app/features/admin/partials/edit_user.html b/public/app/features/admin/partials/edit_user.html index 5b0efa8bdf3..7e6457a8a76 100644 --- a/public/app/features/admin/partials/edit_user.html +++ b/public/app/features/admin/partials/edit_user.html @@ -21,7 +21,7 @@
- +
@@ -34,7 +34,7 @@
- +
@@ -46,7 +46,7 @@
- +
@@ -65,7 +65,7 @@
- +
diff --git a/public/app/features/admin/partials/new_user.html b/public/app/features/admin/partials/new_user.html index 5199d957c33..e3374d080ca 100644 --- a/public/app/features/admin/partials/new_user.html +++ b/public/app/features/admin/partials/new_user.html @@ -24,7 +24,7 @@
- +
diff --git a/public/app/features/admin/partials/orgs.html b/public/app/features/admin/partials/orgs.html index d28cf4dc967..b40aed6faab 100644 --- a/public/app/features/admin/partials/orgs.html +++ b/public/app/features/admin/partials/orgs.html @@ -3,7 +3,7 @@
- + New Org diff --git a/public/app/features/admin/partials/users.html b/public/app/features/admin/partials/users.html index 806c10851e5..08704dc0459 100644 --- a/public/app/features/admin/partials/users.html +++ b/public/app/features/admin/partials/users.html @@ -7,7 +7,7 @@
- + Add new user diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index b2cd2f21e4d..5e7201cdfdd 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -68,7 +68,7 @@
- + Back
diff --git a/public/app/features/alerting/partials/notifications_list.html b/public/app/features/alerting/partials/notifications_list.html index 246cb45b4db..ce4fea9ff49 100644 --- a/public/app/features/alerting/partials/notifications_list.html +++ b/public/app/features/alerting/partials/notifications_list.html @@ -7,8 +7,7 @@
- - + New Channel
diff --git a/public/app/features/all.ts b/public/app/features/all.ts index 83146596ea0..d5e684e4a4e 100644 --- a/public/app/features/all.ts +++ b/public/app/features/all.ts @@ -12,3 +12,4 @@ import './manage-dashboards'; import './teams/CreateTeamCtrl'; import './profile/all'; import './datasources/settings/HttpSettingsCtrl'; +import './datasources/settings/TlsAuthSettingsCtrl'; diff --git a/public/app/features/annotations/partials/editor.html b/public/app/features/annotations/partials/editor.html index 65ee7e52bd0..9a7a8cb738a 100644 --- a/public/app/features/annotations/partials/editor.html +++ b/public/app/features/annotations/partials/editor.html @@ -9,7 +9,7 @@
@@ -48,7 +48,7 @@
There are no custom annotation queries added yet
- + Add Annotation Query @@ -105,8 +105,8 @@
- - + +
diff --git a/public/app/features/annotations/partials/event_editor.html b/public/app/features/annotations/partials/event_editor.html index 529434755f1..286decb34ce 100644 --- a/public/app/features/annotations/partials/event_editor.html +++ b/public/app/features/annotations/partials/event_editor.html @@ -26,7 +26,7 @@
- + Cancel
diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx index 41b9b0c8a55..8f5dcf06a1f 100644 --- a/public/app/features/api-keys/ApiKeysPage.tsx +++ b/public/app/features/api-keys/ApiKeysPage.tsx @@ -107,7 +107,7 @@ export class ApiKeysPage extends PureComponent { renderEmptyList() { const { isAdding } = this.state; return ( -
+ <> {!isAdding && ( { /> )} {this.renderAddApiKeyForm()} -
+ ); } @@ -169,7 +169,7 @@ export class ApiKeysPage extends PureComponent {
- +
@@ -183,7 +183,7 @@ export class ApiKeysPage extends PureComponent { const { apiKeys, searchQuery } = this.props; return ( -
+ <>
-
@@ -231,7 +231,7 @@ export class ApiKeysPage extends PureComponent { ) : null}
-
+ ); } @@ -241,13 +241,7 @@ export class ApiKeysPage extends PureComponent { return ( - {hasFetched && ( - apiKeysCount > 0 ? ( - this.renderApiKeyList() - ) : ( - this.renderEmptyList() - ) - )} + {hasFetched && (apiKeysCount > 0 ? this.renderApiKeyList() : this.renderEmptyList())} ); diff --git a/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap b/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap index f40894426ae..2deb7fa5e7f 100644 --- a/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap +++ b/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap @@ -35,118 +35,114 @@ exports[`Render should render CTA if there are no API keys 1`] = ` -
- - + +
-
- -
- Add API Key -
-
+ +
+ Add API Key +
+ +
-
- - Key name - - + +
+
+ + Role + + + - - - - - -
-
- -
+ Viewer + + + + +
- -
- -
+
+ +
+
+ +
+
`; diff --git a/public/app/features/dashboard/components/DashExportModal/template.html b/public/app/features/dashboard/components/DashExportModal/template.html index 3c14c4b184d..e399d166d04 100644 --- a/public/app/features/dashboard/components/DashExportModal/template.html +++ b/public/app/features/dashboard/components/DashExportModal/template.html @@ -12,7 +12,7 @@
-
+ ); } @@ -141,9 +141,7 @@ export class TeamList extends PureComponent { return ( - - {hasFetched && this.renderList()} - + {hasFetched && this.renderList()} ); } diff --git a/public/app/features/teams/TeamMembers.tsx b/public/app/features/teams/TeamMembers.tsx index a25f1786a5b..ab8d43cb1a3 100644 --- a/public/app/features/teams/TeamMembers.tsx +++ b/public/app/features/teams/TeamMembers.tsx @@ -103,7 +103,7 @@ export class TeamMembers extends PureComponent {
-
@@ -117,7 +117,7 @@ export class TeamMembers extends PureComponent {
{this.state.newTeamMember && ( - )} diff --git a/public/app/features/teams/TeamPages.tsx b/public/app/features/teams/TeamPages.tsx index ebbde595601..235936aa8f5 100644 --- a/public/app/features/teams/TeamPages.tsx +++ b/public/app/features/teams/TeamPages.tsx @@ -49,9 +49,9 @@ export class TeamPages extends PureComponent { async fetchTeam() { const { loadTeam, teamId } = this.props; - this.setState({isLoading: true}); + this.setState({ isLoading: true }); const team = await loadTeam(teamId); - this.setState({isLoading: false}); + this.setState({ isLoading: false }); return team; } @@ -84,7 +84,7 @@ export class TeamPages extends PureComponent { return ( - {team && Object.keys(team).length !== 0 &&
{this.renderPage()}
} + {team && Object.keys(team).length !== 0 && this.renderPage()}
); diff --git a/public/app/features/teams/TeamSettings.tsx b/public/app/features/teams/TeamSettings.tsx index 01a4a3347b2..8d77cd32017 100644 --- a/public/app/features/teams/TeamSettings.tsx +++ b/public/app/features/teams/TeamSettings.tsx @@ -75,7 +75,7 @@ export class TeamSettings extends React.Component {
-
diff --git a/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap b/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap index e28eb86dc7a..5d384e99ef8 100644 --- a/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/TeamGroupSync.test.tsx.snap @@ -62,7 +62,7 @@ exports[`Render should render component 1`] = ` className="gf-form" > diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index 78733b3b2be..b34ef1f4807 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -11,7 +11,7 @@
There are no variables added yet
-
+ Add variable @@ -34,7 +34,7 @@
@@ -317,8 +317,8 @@
- - + +
diff --git a/public/app/features/users/UsersActionBar.tsx b/public/app/features/users/UsersActionBar.tsx index 28ed4754d01..c7ce8c6f894 100644 --- a/public/app/features/users/UsersActionBar.tsx +++ b/public/app/features/users/UsersActionBar.tsx @@ -65,12 +65,12 @@ export class UsersActionBar extends PureComponent { )}
{canInvite && ( - + Invite )} {externalUserMngLinkUrl && ( - + {externalUserMngLinkName} )} diff --git a/public/app/features/users/__snapshots__/UsersActionBar.test.tsx.snap b/public/app/features/users/__snapshots__/UsersActionBar.test.tsx.snap index e69accb011b..a73d298581e 100644 --- a/public/app/features/users/__snapshots__/UsersActionBar.test.tsx.snap +++ b/public/app/features/users/__snapshots__/UsersActionBar.test.tsx.snap @@ -105,7 +105,7 @@ exports[`Render should show external user management button 1`] = ` className="page-action-bar__spacer" /> @@ -143,7 +143,7 @@ exports[`Render should show invite button 1`] = ` className="page-action-bar__spacer" /> diff --git a/public/app/partials/confirm_modal.html b/public/app/partials/confirm_modal.html index d0b0d260f78..5d80f59a41f 100644 --- a/public/app/partials/confirm_modal.html +++ b/public/app/partials/confirm_modal.html @@ -26,7 +26,7 @@
- +
diff --git a/public/app/partials/edit_json.html b/public/app/partials/edit_json.html index 91552f95d41..fb411e662fc 100644 --- a/public/app/partials/edit_json.html +++ b/public/app/partials/edit_json.html @@ -15,7 +15,7 @@
diff --git a/public/app/partials/reset_password.html b/public/app/partials/reset_password.html index bba38af0235..085cc34d111 100644 --- a/public/app/partials/reset_password.html +++ b/public/app/partials/reset_password.html @@ -16,7 +16,7 @@
-
diff --git a/public/app/partials/signup_invited.html b/public/app/partials/signup_invited.html index c4c08c9ded8..966dba2d352 100644 --- a/public/app/partials/signup_invited.html +++ b/public/app/partials/signup_invited.html @@ -30,7 +30,7 @@
-
diff --git a/public/app/partials/signup_step2.html b/public/app/partials/signup_step2.html index b01c8160b16..5fae3563600 100644 --- a/public/app/partials/signup_step2.html +++ b/public/app/partials/signup_step2.html @@ -37,7 +37,7 @@
- diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.ts index 950fa73a16b..97f76d229fb 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.ts @@ -224,4 +224,13 @@ export default class AppInsightsDatasource { return new ResponseParser(result).parseGroupBys(); }); } + + getQuerySchema() { + const url = `${this.baseUrl}/query/schema`; + return this.doRequest(url).then(result => { + const schema = new ResponseParser(result).parseQuerySchema(); + // console.log(schema); + return schema; + }); + } } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/response_parser.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/response_parser.ts index 848472cf101..6b5f006ca30 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/response_parser.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/response_parser.ts @@ -199,6 +199,30 @@ export default class ResponseParser { return ResponseParser.toTextValueList(this.results.supportedGroupBy); } + parseQuerySchema() { + const result = { + Type: 'AppInsights', + Tables: {}, + }; + if (this.results && this.results.data && this.results.data.Tables) { + for (let i = 0; i < this.results.data.Tables[0].Rows.length; i++) { + const column = this.results.data.Tables[0].Rows[i]; + const columnTable = column[0]; + const columnName = column[1]; + const columnType = column[2]; + if (result.Tables[columnTable]) { + result.Tables[columnTable].OrderedColumns.push({ Name: columnName, Type: columnType }); + } else { + result.Tables[columnTable] = { + Name: columnTable, + OrderedColumns: [{ Name: columnName, Type: columnType }], + }; + } + } + } + return result; + } + static toTextValueList(values) { const list: any[] = []; for (let i = 0; i < values.length; i++) { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/config_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/config_ctrl.ts index 98fe5a87a56..4ee5c94fad6 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/config_ctrl.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/config_ctrl.ts @@ -1,6 +1,6 @@ import AzureLogAnalyticsDatasource from './azure_log_analytics/azure_log_analytics_datasource'; import config from 'app/core/config'; -import { isVersionGtOrEq } from './version'; +import { isVersionGtOrEq } from 'app/core/utils/version'; export class AzureMonitorConfigCtrl { static templateUrl = 'public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/config.html'; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx index 849cf62efe0..587ab807e33 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx @@ -1,3 +1,4 @@ +import _ from 'lodash'; import Plain from 'slate-plain-serializer'; import QueryField from './query_field'; @@ -6,11 +7,10 @@ import QueryField from './query_field'; import debounce from 'app/features/explore/utils/debounce'; import { getNextCharacter } from 'app/features/explore/utils/dom'; -import { FUNCTIONS, KEYWORDS } from './kusto'; +import { KEYWORDS, functionTokens, operatorTokens, grafanaMacros } from './kusto/kusto'; // import '../sass/editor.base.scss'; - -const TYPEAHEAD_DELAY = 500; +const TYPEAHEAD_DELAY = 100; interface Suggestion { text: string; @@ -25,24 +25,46 @@ interface SuggestionGroup { skipFilter?: boolean; } +interface KustoSchema { + Databases: { + Default?: KustoDBSchema; + }; + Plugins?: any[]; +} + +interface KustoDBSchema { + Name?: string; + Functions?: any; + Tables?: any; +} + +const defaultSchema = () => ({ + Databases: { + Default: {}, + }, +}); + const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim(); const wrapText = text => ({ text }); export default class KustoQueryField extends QueryField { fields: any; events: any; + schema: KustoSchema; constructor(props, context) { super(props, context); + this.schema = defaultSchema(); this.onTypeahead = debounce(this.onTypeahead, TYPEAHEAD_DELAY); } componentDidMount() { - this.updateMenu(); + super.componentDidMount(); + this.fetchSchema(); } - onTypeahead = () => { + onTypeahead = (force?: boolean) => { const selection = window.getSelection(); if (selection.anchorNode) { const wrapperNode = selection.anchorNode.parentElement; @@ -73,81 +95,98 @@ export default class KustoQueryField extends QueryField { const wrapperClasses = wrapperNode.classList; let typeaheadContext: string | null = null; + // Built-in functions if (wrapperClasses.contains('function-context')) { typeaheadContext = 'context-function'; - if (this.fields) { - suggestionGroups = this._getKeywordSuggestions(); - } else { - this._fetchFields(); - return; - } - } else if (modelPrefix.match(/(facet\s$)/i)) { - typeaheadContext = 'context-facet'; - if (this.fields) { - suggestionGroups = this._getKeywordSuggestions(); - } else { - this._fetchFields(); - return; - } - } else if (modelPrefix.match(/(,\s*$)/)) { - typeaheadContext = 'context-multiple-fields'; - if (this.fields) { - suggestionGroups = this._getKeywordSuggestions(); - } else { - this._fetchFields(); - return; - } - } else if (modelPrefix.match(/(from\s$)/i)) { - typeaheadContext = 'context-from'; - if (this.events) { - suggestionGroups = this._getKeywordSuggestions(); - } else { - this._fetchEvents(); - return; - } - } else if (modelPrefix.match(/(^select\s\w*$)/i)) { - typeaheadContext = 'context-select'; - if (this.fields) { - suggestionGroups = this._getKeywordSuggestions(); - } else { - this._fetchFields(); - return; - } - } else if (modelPrefix.match(/from\s\S+\s\w*$/i)) { - prefix = ''; - typeaheadContext = 'context-since'; - suggestionGroups = this._getKeywordSuggestions(); - // } else if (modelPrefix.match(/\d+\s\w*$/)) { - // typeaheadContext = 'context-number'; - // suggestionGroups = this._getAfterNumberSuggestions(); - } else if (modelPrefix.match(/ago\b/i) || modelPrefix.match(/facet\b/i) || modelPrefix.match(/\$__timefilter\b/i)) { - typeaheadContext = 'context-timeseries'; - suggestionGroups = this._getKeywordSuggestions(); - } else if (prefix && !wrapperClasses.contains('argument')) { - typeaheadContext = 'context-builtin'; - suggestionGroups = this._getKeywordSuggestions(); - } else if (Plain.serialize(this.state.value) === '') { + suggestionGroups = this.getColumnSuggestions(); + + // where + } else if (modelPrefix.match(/(where\s(\w+\b)?$)/i)) { + typeaheadContext = 'context-where'; + suggestionGroups = this.getColumnSuggestions(); + + // summarize by + } else if (modelPrefix.match(/(summarize\s(\w+\b)?$)/i)) { + typeaheadContext = 'context-summarize'; + suggestionGroups = this.getFunctionSuggestions(); + } else if (modelPrefix.match(/(summarize\s(.+\s)?by\s+([^,\s]+,\s*)*([^,\s]+\b)?$)/i)) { + typeaheadContext = 'context-summarize-by'; + suggestionGroups = this.getColumnSuggestions(); + + // order by, top X by, ... by ... + } else if (modelPrefix.match(/(by\s+([^,\s]+,\s*)*([^,\s]+\b)?$)/i)) { + typeaheadContext = 'context-by'; + suggestionGroups = this.getColumnSuggestions(); + + // join + } else if (modelPrefix.match(/(on\s(.+\b)?$)/i)) { + typeaheadContext = 'context-join-on'; + suggestionGroups = this.getColumnSuggestions(); + } else if (modelPrefix.match(/(join\s+(\(\s+)?(\w+\b)?$)/i)) { + typeaheadContext = 'context-join'; + suggestionGroups = this.getTableSuggestions(); + + // distinct + } else if (modelPrefix.match(/(distinct\s(.+\b)?$)/i)) { + typeaheadContext = 'context-distinct'; + suggestionGroups = this.getColumnSuggestions(); + + // database() + } else if (modelPrefix.match(/(database\(\"(\w+)\"\)\.(.+\b)?$)/i)) { + typeaheadContext = 'context-database-table'; + const db = this.getDBFromDatabaseFunction(modelPrefix); + console.log(db); + suggestionGroups = this.getTableSuggestions(db); + prefix = prefix.replace('.', ''); + + // new + } else if (normalizeQuery(Plain.serialize(this.state.value)).match(/^\s*\w*$/i)) { typeaheadContext = 'context-new'; - suggestionGroups = this._getInitialSuggestions(); + if (this.schema) { + suggestionGroups = this.getInitialSuggestions(); + } else { + this.fetchSchema(); + setTimeout(this.onTypeahead, 0); + return; + } + + // built-in + } else if (prefix && !wrapperClasses.contains('argument') && !force) { + // Use only last typed word as a prefix for searching + if (modelPrefix.match(/\s$/i)) { + prefix = ''; + return; + } + prefix = getLastWord(prefix); + typeaheadContext = 'context-builtin'; + suggestionGroups = this.getKeywordSuggestions(); + } else if (force === true) { + typeaheadContext = 'context-builtin-forced'; + if (modelPrefix.match(/\s$/i)) { + prefix = ''; + } + suggestionGroups = this.getKeywordSuggestions(); } let results = 0; prefix = prefix.toLowerCase(); - const filteredSuggestions = suggestionGroups.map(group => { - if (group.items && prefix && !group.skipFilter) { - group.items = group.items.filter(c => c.text.length >= prefix.length); - if (group.prefixMatch) { - group.items = group.items.filter(c => c.text.toLowerCase().indexOf(prefix) === 0); - } else { - group.items = group.items.filter(c => c.text.toLowerCase().indexOf(prefix) > -1); + const filteredSuggestions = suggestionGroups + .map(group => { + if (group.items && prefix && !group.skipFilter) { + group.items = group.items.filter(c => c.text.length >= prefix.length); + if (group.prefixMatch) { + group.items = group.items.filter(c => c.text.toLowerCase().indexOf(prefix) === 0); + } else { + group.items = group.items.filter(c => c.text.toLowerCase().indexOf(prefix) > -1); + } } - } - results += group.items.length; - return group; - }) + results += group.items.length; + return group; + }) .filter(group => group.items.length > 0); // console.log('onTypeahead', selection.anchorNode, wrapperClasses, text, offset, prefix, typeaheadContext); + // console.log('onTypeahead', prefix, typeaheadContext, force); this.setState({ typeaheadPrefix: prefix, @@ -156,7 +195,7 @@ export default class KustoQueryField extends QueryField { suggestions: results > 0 ? filteredSuggestions : [], }); } - } + }; applyTypeahead(change, suggestion) { const { typeaheadPrefix, typeaheadContext, typeaheadText } = this.state; @@ -253,60 +292,157 @@ export default class KustoQueryField extends QueryField { // ]; // } - private _getKeywordSuggestions(): SuggestionGroup[] { + private getInitialSuggestions(): SuggestionGroup[] { + return this.getTableSuggestions(); + } + + private getKeywordSuggestions(): SuggestionGroup[] { return [ { prefixMatch: true, label: 'Keywords', - items: KEYWORDS.map(wrapText) + items: KEYWORDS.map(wrapText), + }, + { + prefixMatch: true, + label: 'Operators', + items: operatorTokens, }, { prefixMatch: true, label: 'Functions', - items: FUNCTIONS.map((s: any) => { s.type = 'function'; return s; }) - } + items: functionTokens.map((s: any) => { + s.type = 'function'; + return s; + }), + }, + { + prefixMatch: true, + label: 'Macros', + items: grafanaMacros.map((s: any) => { + s.type = 'function'; + return s; + }), + }, + { + prefixMatch: true, + label: 'Tables', + items: _.map(this.schema.Databases.Default.Tables, (t: any) => ({ text: t.Name })), + }, ]; } - private _getInitialSuggestions(): SuggestionGroup[] { - // TODO: return datbase tables as an initial suggestion + private getFunctionSuggestions(): SuggestionGroup[] { return [ { prefixMatch: true, - label: 'Keywords', - items: KEYWORDS.map(wrapText) + label: 'Functions', + items: functionTokens.map((s: any) => { + s.type = 'function'; + return s; + }), }, { prefixMatch: true, - label: 'Functions', - items: FUNCTIONS.map((s: any) => { s.type = 'function'; return s; }) - } + label: 'Macros', + items: grafanaMacros.map((s: any) => { + s.type = 'function'; + return s; + }), + }, ]; } - private async _fetchEvents() { - // const query = 'events'; - // const result = await this.request(query); - - // if (result === undefined) { - // this.events = []; - // } else { - // this.events = result; - // } - // setTimeout(this.onTypeahead, 0); - - //Stub - this.events = []; + getTableSuggestions(db = 'Default'): SuggestionGroup[] { + if (this.schema.Databases[db]) { + return [ + { + prefixMatch: true, + label: 'Tables', + items: _.map(this.schema.Databases[db].Tables, (t: any) => ({ text: t.Name })), + }, + ]; + } else { + return []; + } } - private async _fetchFields() { - // const query = 'fields'; - // const result = await this.request(query); + private getColumnSuggestions(): SuggestionGroup[] { + const table = this.getTableFromContext(); + if (table) { + const tableSchema = this.schema.Databases.Default.Tables[table]; + if (tableSchema) { + return [ + { + prefixMatch: true, + label: 'Fields', + items: _.map(tableSchema.OrderedColumns, (f: any) => ({ + text: f.Name, + hint: f.Type, + })), + }, + ]; + } + } + return []; + } - // this.fields = result || []; + private getTableFromContext() { + const query = Plain.serialize(this.state.value); + const tablePattern = /^\s*(\w+)\s*|/g; + const normalizedQuery = normalizeQuery(query); + const match = tablePattern.exec(normalizedQuery); + if (match && match.length > 1 && match[0] && match[1]) { + return match[1]; + } else { + return null; + } + } - // setTimeout(this.onTypeahead, 0); - // Stub - this.fields = []; + private getDBFromDatabaseFunction(prefix: string) { + const databasePattern = /database\(\"(\w+)\"\)/gi; + const match = databasePattern.exec(prefix); + if (match && match.length > 1 && match[0] && match[1]) { + return match[1]; + } else { + return null; + } + } + + private async fetchSchema() { + let schema = await this.props.getSchema(); + if (schema) { + if (schema.Type === 'AppInsights') { + schema = castSchema(schema); + } + this.schema = schema; + } else { + this.schema = defaultSchema(); + } } } + +/** + * Cast schema from App Insights to default Kusto schema + */ +function castSchema(schema) { + const defaultSchemaTemplate = defaultSchema(); + defaultSchemaTemplate.Databases.Default = schema; + return defaultSchemaTemplate; +} + +function normalizeQuery(query: string): string { + const commentPattern = /\/\/.*$/gm; + let normalizedQuery = query.replace(commentPattern, ''); + normalizedQuery = normalizedQuery.replace('\n', ' '); + return normalizedQuery; +} + +function getLastWord(str: string): string { + const lastWordPattern = /(?:.*\s)?([^\s]+\s*)$/gi; + const match = lastWordPattern.exec(str); + if (match && match.length > 1) { + return match[1]; + } + return ''; +} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/editor_component.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/editor_component.tsx index da7db58567f..f483d585ac8 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/editor_component.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/editor_component.tsx @@ -1,10 +1,23 @@ import KustoQueryField from './KustoQueryField'; -import Kusto from './kusto'; +import Kusto from './kusto/kusto'; import React, { Component } from 'react'; import coreModule from 'app/core/core_module'; -class Editor extends Component { +interface EditorProps { + index: number; + placeholder?: string; + change: (value: string, index: number) => void; + variables: () => string[] | string[]; + getSchema?: () => Promise; + execute?: () => void; +} + +class Editor extends Component { + static defaultProps = { + placeholder: 'Enter a query', + }; + constructor(props) { super(props); this.state = { @@ -31,7 +44,7 @@ class Editor extends Component { }; render() { - const { request, variables } = this.props; + const { variables, getSchema, placeholder } = this.props; const { edited, query } = this.state; return ( @@ -42,9 +55,9 @@ class Editor extends Component { onQueryChange={this.onChangeQuery} prismLanguage="kusto" prismDefinition={Kusto} - placeholder="Enter a query" - request={request} + placeholder={placeholder} templateVariables={variables} + getSchema={getSchema} />
); @@ -54,6 +67,14 @@ class Editor extends Component { coreModule.directive('kustoEditor', [ 'reactDirective', reactDirective => { - return reactDirective(Editor, ['change', 'database', 'execute', 'query', 'request', 'variables']); + return reactDirective(Editor, [ + 'change', + 'database', + 'execute', + 'query', + 'variables', + 'placeholder', + ['getSchema', { watchDepth: 'reference' }], + ]); }, ]); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/kusto.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/kusto.ts deleted file mode 100644 index 647ebb8024a..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/kusto.ts +++ /dev/null @@ -1,114 +0,0 @@ -export const FUNCTIONS = [ - { text: 'countof', display: 'countof()', hint: '' }, - { text: 'bin', display: 'bin()', hint: '' }, - { text: 'extentid', display: 'extentid()', hint: '' }, - { text: 'extract', display: 'extract()', hint: '' }, - { text: 'extractjson', display: 'extractjson()', hint: '' }, - { text: 'floor', display: 'floor()', hint: '' }, - { text: 'iif', display: 'iif()', hint: '' }, - { text: 'isnull', display: 'isnull()', hint: '' }, - { text: 'isnotnull', display: 'isnotnull()', hint: '' }, - { text: 'notnull', display: 'notnull()', hint: '' }, - { text: 'isempty', display: 'isempty()', hint: '' }, - { text: 'isnotempty', display: 'isnotempty()', hint: '' }, - { text: 'notempty', display: 'notempty()', hint: '' }, - { text: 'now', display: 'now()', hint: '' }, - { text: 're2', display: 're2()', hint: '' }, - { text: 'strcat', display: 'strcat()', hint: '' }, - { text: 'strlen', display: 'strlen()', hint: '' }, - { text: 'toupper', display: 'toupper()', hint: '' }, - { text: 'tostring', display: 'tostring()', hint: '' }, - { text: 'count', display: 'count()', hint: '' }, - { text: 'cnt', display: 'cnt()', hint: '' }, - { text: 'sum', display: 'sum()', hint: '' }, - { text: 'min', display: 'min()', hint: '' }, - { text: 'max', display: 'max()', hint: '' }, - { text: 'avg', display: 'avg()', hint: '' }, - { - text: '$__timeFilter', - display: '$__timeFilter()', - hint: 'Macro that uses the selected timerange in Grafana to filter the query.', - }, - { - text: '$__escapeMulti', - display: '$__escapeMulti()', - hint: 'Macro to escape multi-value template variables that contain illegal characters.', - }, - { text: '$__contains', display: '$__contains()', hint: 'Macro for multi-value template variables.' }, -]; - -export const KEYWORDS = [ - 'by', - 'on', - 'contains', - 'notcontains', - 'containscs', - 'notcontainscs', - 'startswith', - 'has', - 'matches', - 'regex', - 'true', - 'false', - 'and', - 'or', - 'typeof', - 'int', - 'string', - 'date', - 'datetime', - 'time', - 'long', - 'real', - '​boolean', - 'bool', - // add some more keywords - 'where', - 'order', -]; - -// Kusto operators -// export const OPERATORS = ['+', '-', '*', '/', '>', '<', '==', '<>', '<=', '>=', '~', '!~']; - -export const DURATION = ['SECONDS', 'MINUTES', 'HOURS', 'DAYS', 'WEEKS', 'MONTHS', 'YEARS']; - -const tokenizer = { - comment: { - pattern: /(^|[^\\:])\/\/.*/, - lookbehind: true, - greedy: true, - }, - 'function-context': { - pattern: /[a-z0-9_]+\([^)]*\)?/i, - inside: {}, - }, - duration: { - pattern: new RegExp(`${DURATION.join('?|')}?`, 'i'), - alias: 'number', - }, - builtin: new RegExp(`\\b(?:${FUNCTIONS.map(f => f.text).join('|')})(?=\\s*\\()`, 'i'), - string: { - pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, - greedy: true, - }, - keyword: new RegExp(`\\b(?:${KEYWORDS.join('|')}|\\*)\\b`, 'i'), - boolean: /\b(?:true|false)\b/, - number: /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i, - operator: /-|\+|\*|\/|>|<|==|<=?|>=?|<>|!~|~|=|\|/, - punctuation: /[{};(),.:]/, - variable: /(\[\[(.+?)\]\])|(\$(.+?))\b/, -}; - -tokenizer['function-context'].inside = { - argument: { - pattern: /[a-z0-9_]+(?=:)/i, - alias: 'symbol', - }, - duration: tokenizer.duration, - number: tokenizer.number, - builtin: tokenizer.builtin, - string: tokenizer.string, - variable: tokenizer.variable, -}; - -export default tokenizer; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/kusto/kusto.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/kusto/kusto.ts new file mode 100644 index 00000000000..5bf7dfb19eb --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/kusto/kusto.ts @@ -0,0 +1,710 @@ +/* tslint:disable:max-line-length */ +export const operatorTokens = [ + { text: '!between', hint: 'Matches the input that is outside the inclusive range.' }, + { text: 'as', hint: "Binds a name to the operator's input tabular expression." }, + { text: 'between', hint: 'Matches the input that is inside the inclusive range.' }, + { + text: 'consume', + hint: + 'The `consume` operator consumes the tabular data stream handed to it. It is\r\nmostly used for triggering the query side-effect without actually returning\r\nthe results back to the caller.', + }, + { text: 'count', hint: 'Returns the number of records in the input record set.' }, + { text: 'datatable', hint: 'Returns a table whose schema and values are defined in the query itself.' }, + { + text: 'distinct', + hint: 'Produces a table with the distinct combination of the provided columns of the input table.', + }, + { text: 'evaluate', hint: 'Invokes a service-side query extension (plugin).' }, + { text: 'extend', hint: 'Create calculated columns and append them to the result set.' }, + { + text: 'externaldata', + hint: + 'Returns a table whose schema is defined in the query itself, and whose data is read from an external raw file.', + }, + { + text: 'facet', + hint: + 'Returns a set of tables, one for each specified column.\r\nEach table specifies the list of values taken by its column.\r\nAn additional table can be created by using the `with` clause.', + }, + { text: 'find', hint: 'Finds rows that match a predicate across a set of tables.' }, + { text: 'fork', hint: 'Runs multiple consumer operators in parallel.' }, + { text: 'getschema', hint: 'Produce a table that represents a tabular schema of the input.' }, + { text: 'in', hint: 'Filters a recordset based on the provided set of values.' }, + { text: 'invoke', hint: 'Invokes lambda that receives the source of `invoke` as tabular parameter argument.' }, + { + text: 'join', + hint: + 'Merge the rows of two tables to form a new table by matching values of the specified column(s) from each table.', + }, + { text: 'limit', hint: 'Return up to the specified number of rows.' }, + { text: 'make-series', hint: 'Create series of specified aggregated values along specified axis.' }, + { text: 'mvexpand', hint: 'Expands multi-value array or property bag.' }, + { text: 'order', hint: 'Sort the rows of the input table into order by one or more columns.' }, + { text: 'parse', hint: 'Evaluates a string expression and parses its value into one or more calculated columns.' }, + { + text: 'print', + hint: + 'Evaluates one or more scalar expressions and inserts the results (as a single-row table with as many columns as there are expressions) into the output.', + }, + { text: 'project', hint: 'Select the columns to include, rename or drop, and insert new computed columns.' }, + { text: 'project-away', hint: 'Select what columns to exclude from the input.' }, + { text: 'project-rename', hint: 'Renames columns in the result output.' }, + { text: 'range', hint: 'Generates a single-column table of values.' }, + { text: 'reduce', hint: 'Groups a set of strings together based on values similarity.' }, + { text: 'render', hint: 'Instructs the user agent to render the results of the query in a particular way.' }, + { text: 'sample', hint: 'Returns up to the specified number of random rows from the input table.' }, + { + text: 'sample-distinct', + hint: + 'Returns a single column that contains up to the specified number of distinct values of the requested column.', + }, + { text: 'search', hint: 'The search operator provides a multi-table/multi-column search experience.' }, + { text: 'serialize', hint: 'Marks that order of the input row set is safe for window functions usage.' }, + { text: 'sort', hint: 'Sort the rows of the input table into order by one or more columns.' }, + { text: 'summarize', hint: 'Produces a table that aggregates the content of the input table.' }, + { text: 'take', hint: 'Return up to the specified number of rows.' }, + { text: 'top', hint: 'Returns the first *N* records sorted by the specified columns.' }, + { + text: 'top-hitters', + hint: 'Returns an approximation of the first *N* results (assuming skewed distribution of the input).', + }, + { + text: 'top-nested', + hint: 'Produces hierarchical top results, where each level is a drill-down based on previous level values.', + }, + { text: 'union', hint: 'Takes two or more tables and returns the rows of all of them.' }, + { text: 'where', hint: 'Filters a table to the subset of rows that satisfy a predicate.' }, +]; + +export const functionTokens = [ + { text: 'abs', hint: 'Calculates the absolute value of the input.' }, + { + text: 'acos', + hint: + 'Returns the angle whose cosine is the specified number (the inverse operation of [`cos()`](cosfunction.md)) .', + }, + { text: 'ago', hint: 'Subtracts the given timespan from the current UTC clock time.' }, + { text: 'any', hint: 'Returns random non-empty value from the specified expression values.' }, + { + text: 'arg_max', + hint: + 'Finds a row in the group that maximizes *ExprToMaximize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).', + }, + { + text: 'arg_min', + hint: + 'Finds a row in the group that minimizes *ExprToMinimize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).', + }, + { + text: 'argmax', + hint: + 'Finds a row in the group that maximizes *ExprToMaximize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).', + }, + { + text: 'argmin', + hint: + 'Finds a row in the group that minimizes *ExprToMinimize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).', + }, + { text: 'array_concat', hint: 'Concatenates a number of dynamic arrays to a single array.' }, + { text: 'array_length', hint: 'Calculates the number of elements in a dynamic array.' }, + { text: 'array_slice', hint: 'Extracts a slice of a dynamic array.' }, + { + text: 'array_split', + hint: + 'Splits an array to multiple arrays according to the split indices and packs the generated array in a dynamic array.', + }, + { + text: 'asin', + hint: 'Returns the angle whose sine is the specified number (the inverse operation of [`sin()`](sinfunction.md)) .', + }, + { + text: 'assert', + hint: 'Checks for a condition; if the condition is false, outputs error messages and fails the query.', + }, + { + text: 'atan', + hint: + 'Returns the angle whose tangent is the specified number (the inverse operation of [`tan()`](tanfunction.md)) .', + }, + { + text: 'atan2', + hint: + 'Calculates the angle, in radians, between the positive x-axis and the ray from the origin to the point (y, x).', + }, + { text: 'avg', hint: 'Calculates the average of *Expr* across the group.' }, + { + text: 'avgif', + hint: + 'Calculates the [average](avg-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`.', + }, + { text: 'bag_keys', hint: 'Enumerates all the root keys in a dynamic property-bag object.' }, + { text: 'base64_decodestring', hint: 'Decodes a base64 string to a UTF-8 string' }, + { text: 'base64_encodestring', hint: 'Encodes a string as base64 string' }, + { text: 'beta_cdf', hint: 'Returns the standard cumulative beta distribution function.' }, + { text: 'beta_inv', hint: 'Returns the inverse of the beta cumulative probability beta density function.' }, + { text: 'beta_pdf', hint: 'Returns the probability density beta function.' }, + { text: 'bin', hint: 'Rounds values down to an integer multiple of a given bin size.' }, + { + text: 'bin_at', + hint: + "Rounds values down to a fixed-size 'bin', with control over the bin's starting point.\r\n(See also [`bin function`](./binfunction.md).)", + }, + { + text: 'bin_auto', + hint: + "Rounds values down to a fixed-size 'bin', with control over the bin size and starting point provided by a query property.", + }, + { text: 'binary_and', hint: 'Returns a result of the bitwise `and` operation between two values.' }, + { text: 'binary_not', hint: 'Returns a bitwise negation of the input value.' }, + { text: 'binary_or', hint: 'Returns a result of the bitwise `or` operation of the two values.' }, + { text: 'binary_shift_left', hint: 'Returns binary shift left operation on a pair of numbers.' }, + { text: 'binary_shift_right', hint: 'Returns binary shift right operation on a pair of numbers.' }, + { text: 'binary_xor', hint: 'Returns a result of the bitwise `xor` operation of the two values.' }, + { text: 'buildschema', hint: 'Returns the minimal schema that admits all values of *DynamicExpr*.' }, + { + text: 'case', + hint: 'Evaluates a list of predicates and returns the first result expression whose predicate is satisfied.', + }, + { + text: 'ceiling', + hint: 'Calculates the smallest integer greater than, or equal to, the specified numeric expression.', + }, + { text: 'cluster', hint: 'Changes the reference of the query to a remote cluster.' }, + { + text: 'coalesce', + hint: 'Evaluates a list of expressions and returns the first non-null (or non-empty for string) expression.', + }, + { text: 'cos', hint: 'Returns the cosine function.' }, + { text: 'cot', hint: 'Calculates the trigonometric cotangent of the specified angle, in radians.' }, + { + text: 'count', + hint: + 'Returns a count of the records per summarization group (or in total if summarization is done without grouping).', + }, + { text: 'countif', hint: 'Returns a count of rows for which *Predicate* evaluates to `true`.' }, + { + text: 'countof', + hint: 'Counts occurrences of a substring in a string. Plain string matches may overlap; regex matches do not.', + }, + { text: 'current_principal', hint: 'Returns the current principal running this query.' }, + { + text: 'cursor_after', + hint: 'A predicate over the records of a table to compare their ingestion time\r\nagainst a database cursor.', + }, + { + text: 'cursor_before_or_at', + hint: 'A predicate over the records of a table to compare their ingestion time\r\nagainst a database cursor.', + }, + { text: 'database', hint: 'Changes the reference of the query to a specific database within the cluster scope.' }, + { + text: 'datetime_add', + hint: + 'Calculates a new [datetime](./scalar-data-types/datetime.md) from a specified datepart multiplied by a specified amount, added to a specified [datetime](./scalar-data-types/datetime.md).', + }, + { + text: 'datetime_diff', + hint: 'Calculates calendarian difference between two [datetime](./scalar-data-types/datetime.md) values.', + }, + { text: 'datetime_part', hint: 'Extracts the requested date part as an integer value.' }, + { text: 'dayofmonth', hint: 'Returns the integer number representing the day number of the given month' }, + { text: 'dayofweek', hint: 'Returns the integer number of days since the preceding Sunday, as a `timespan`.' }, + { text: 'dayofyear', hint: 'Returns the integer number represents the day number of the given year.' }, + { text: 'dcount', hint: 'Returns an estimate of the number of distinct values of *Expr* in the group.' }, + { + text: 'dcount_hll', + hint: + 'Calculates the dcount from hll results (which was generated by [hll](hll-aggfunction.md) or [hll_merge](hll-merge-aggfunction.md)).', + }, + { + text: 'dcountif', + hint: + 'Returns an estimate of the number of distinct values of *Expr* of rows for which *Predicate* evaluates to `true`.', + }, + { + text: 'degrees', + hint: + 'Converts angle value in radians into value in degrees, using formula `degrees = (180 / PI ) * angle_in_radians`', + }, + { text: 'distance', hint: 'Returns the distance between two points in meters.' }, + { text: 'endofday', hint: 'Returns the end of the day containing the date, shifted by an offset, if provided.' }, + { text: 'endofmonth', hint: 'Returns the end of the month containing the date, shifted by an offset, if provided.' }, + { text: 'endofweek', hint: 'Returns the end of the week containing the date, shifted by an offset, if provided.' }, + { text: 'endofyear', hint: 'Returns the end of the year containing the date, shifted by an offset, if provided.' }, + { + text: 'estimate_data_size', + hint: 'Returns an estimated data size of the selected columns of the tabular expression.', + }, + { text: 'exp', hint: 'The base-e exponential function of x, which is e raised to the power x: e^x.' }, + { + text: 'exp10', + hint: 'The base-10 exponential function of x, which is 10 raised to the power x: 10^x. \r\n**Syntax**', + }, + { text: 'exp2', hint: 'The base-2 exponential function of x, which is 2 raised to the power x: 2^x.' }, + { + text: 'extent_id', + hint: 'Returns a unique identifier that identifies the data shard ("extent") that the current record resides in.', + }, + { + text: 'extent_tags', + hint: + 'Returns a dynamic array with the [tags](../management/extents-overview.md#extent-tagging) of the data shard ("extent") that the current record resides in.', + }, + { text: 'extract', hint: 'Get a match for a [regular expression](./re2.md) from a text string.' }, + { text: 'extract_all', hint: 'Get all matches for a [regular expression](./re2.md) from a text string.' }, + { text: 'extractjson', hint: 'Get a specified element out of a JSON text using a path expression.' }, + { text: 'floor', hint: 'An alias for [`bin()`](binfunction.md).' }, + { text: 'format_datetime', hint: 'Formats a datetime parameter based on the format pattern parameter.' }, + { text: 'format_timespan', hint: 'Formats a timespan parameter based on the format pattern parameter.' }, + { text: 'gamma', hint: 'Computes [gamma function](https://en.wikipedia.org/wiki/Gamma_function)' }, + { text: 'getmonth', hint: 'Get the month number (1-12) from a datetime.' }, + { text: 'gettype', hint: 'Returns the runtime type of its single argument.' }, + { text: 'getyear', hint: 'Returns the year part of the `datetime` argument.' }, + { text: 'hash', hint: 'Returns a hash value for the input value.' }, + { text: 'hash_sha256', hint: 'Returns a sha256 hash value for the input value.' }, + { text: 'hll', hint: 'Calculates the Intermediate results of [dcount](dcount-aggfunction.md) across the group.' }, + { + text: 'hll_merge', + hint: 'Merges hll results (scalar version of the aggregate version [`hll_merge()`](hll-merge-aggfunction.md)).', + }, + { text: 'hourofday', hint: 'Returns the integer number representing the hour number of the given date' }, + { + text: 'iff', + hint: + 'Evaluates the first argument (the predicate), and returns the value of either the second or third arguments, depending on whether the predicate evaluated to `true` (second) or `false` (third).', + }, + { + text: 'iif', + hint: + 'Evaluates the first argument (the predicate), and returns the value of either the second or third arguments, depending on whether the predicate evaluated to `true` (second) or `false` (third).', + }, + { + text: 'indexof', + hint: 'Function reports the zero-based index of the first occurrence of a specified string within input string.', + }, + { text: 'ingestion_time', hint: "Retrieves the record's `$IngestionTime` hidden `datetime` column, or null." }, + { + text: 'iscolumnexists', + hint: + 'Returns a boolean value indicating if the given string argument exists in the schema produced by the preceding tabular operator.', + }, + { text: 'isempty', hint: 'Returns `true` if the argument is an empty string or is null.' }, + { text: 'isfinite', hint: 'Returns whether input is a finite value (is neither infinite nor NaN).' }, + { text: 'isinf', hint: 'Returns whether input is an infinite (positive or negative) value.' }, + { text: 'isnan', hint: 'Returns whether input is Not-a-Number (NaN) value.' }, + { text: 'isnotempty', hint: 'Returns `true` if the argument is not an empty string nor it is a null.' }, + { text: 'isnotnull', hint: 'Returns `true` if the argument is not null.' }, + { + text: 'isnull', + hint: + 'Evaluates its sole argument and returns a `bool` value indicating if the argument evaluates to a null value.', + }, + { text: 'log', hint: 'Returns the natural logarithm function.' }, + { text: 'log10', hint: 'Returns the common (base-10) logarithm function.' }, + { text: 'log2', hint: 'Returns the base-2 logarithm function.' }, + { + text: 'loggamma', + hint: 'Computes log of absolute value of the [gamma function](https://en.wikipedia.org/wiki/Gamma_function)', + }, + { + text: 'make_datetime', + hint: 'Creates a [datetime](./scalar-data-types/datetime.md) scalar value from the specified date and time.', + }, + { + text: 'make_dictionary', + hint: 'Returns a `dynamic` (JSON) property-bag (dictionary) of all the values of *Expr* in the group.', + }, + { text: 'make_string', hint: 'Returns the string generated by the Unicode characters.' }, + { + text: 'make_timespan', + hint: 'Creates a [timespan](./scalar-data-types/timespan.md) scalar value from the specified time period.', + }, + { text: 'makelist', hint: 'Returns a `dynamic` (JSON) array of all the values of *Expr* in the group.' }, + { + text: 'makeset', + hint: 'Returns a `dynamic` (JSON) array of the set of distinct values that *Expr* takes in the group.', + }, + { + text: 'materialize', + hint: + 'Allows caching a sub-query result during the time of query execution in a way that other subqueries can reference the partial result.', + }, + { text: 'max', hint: 'Returns the maximum value across the group.' }, + { text: 'max_of', hint: 'Returns the maximum value of several evaluated numeric expressions.' }, + { + text: 'merge_tdigests', + hint: + 'Merges tdigest results (scalar version of the aggregate version [`merge_tdigests()`](merge-tdigests-aggfunction.md)).', + }, + { text: 'min', hint: 'Returns the minimum value agross the group.' }, + { text: 'min_of', hint: 'Returns the minimum value of several evaluated numeric expressions.' }, + { text: 'monthofyear', hint: 'Returns the integer number represents the month number of the given year.' }, + { + text: 'next', + hint: + 'Returns the value of a column in a row that it at some offset following the\r\ncurrent row in a [serialized row set](./windowsfunctions.md#serialized-row-set).', + }, + { text: 'not', hint: 'Reverses the value of its `bool` argument.' }, + { + text: 'now', + hint: + 'Returns the current UTC clock time, optionally offset by a given timespan.\r\nThis function can be used multiple times in a statement and the clock time being referenced will be the same for all instances.', + }, + { text: 'pack', hint: 'Creates a `dynamic` object (property bag) from a list of names and values.' }, + { + text: 'pack_all', + hint: 'Creates a `dynamic` object (property bag) from all the columns of the tabular expression.', + }, + { text: 'pack_array', hint: 'Packs all input values into a dynamic array.' }, + { text: 'parse_ipv4', hint: 'Converts input to integer (signed 64-bit) number representation.' }, + { + text: 'parse_json', + hint: + 'Interprets a `string` as a [JSON value](https://json.org/)) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md). \r\nIt is superior to using [extractjson() function](./extractjsonfunction.md)\r\nwhen you need to extract more than one element of a JSON compound object.', + }, + { + text: 'parse_path', + hint: + 'Parses a file path `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object that contains the following parts of the path: \r\nScheme, RootPath, DirectoryPath, DirectoryName, FileName, Extension, AlternateDataStreamName.\r\nIn addition to the simple paths with both types of slashes, supports paths with schemas (e.g. "file://..."), shared paths (e.g. "\\\\shareddrive\\users..."), long paths (e.g "\\\\?\\C:...""), alternate data streams (e.g. "file1.exe:file2.exe")', + }, + { + text: 'parse_url', + hint: + 'Parses an absolute URL `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object contains all parts of the URL (Scheme, Host, Port, Path, Username, Password, Query Parameters, Fragment).', + }, + { + text: 'parse_urlquery', + hint: + 'Parses a url query `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object contains the Query parameters.', + }, + { + text: 'parse_user_agent', + hint: + "Interprets a user-agent string, which identifies the user's browser and provides certain system details to servers hosting the websites the user visits. The result is returned as [`dynamic`](./scalar-data-types/dynamic.md).", + }, + { text: 'parse_version', hint: 'Converts input string representation of version to a comparable decimal number.' }, + { + text: 'parse_xml', + hint: + 'Interprets a `string` as a XML value, converts the value to a [JSON value](https://json.org/) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md).', + }, + { + text: 'percentile', + hint: + 'Returns an estimate for the specified [nearest-rank percentile](#nearest-rank-percentile) of the population defined by *Expr*. \r\nThe accuracy depends on the density of population in the region of the percentile.', + }, + { + text: 'percentile_tdigest', + hint: + 'Calculates the percentile result from tdigest results (which was generated by [tdigest](tdigest-aggfunction.md) or [merge-tdigests](merge-tdigests-aggfunction.md))', + }, + { + text: 'percentrank_tdigest', + hint: + "Calculates the approximate rank of the value in a set where rank is expressed as percentage of set's size. \r\nThis function can be viewed as the inverse of the percentile.", + }, + { text: 'pi', hint: 'Returns the constant value of Pi (π).' }, + { text: 'point', hint: 'Returns a dynamic array representation of a point.' }, + { text: 'pow', hint: 'Returns a result of raising to power' }, + { + text: 'prev', + hint: + 'Returns the value of a column in a row that it at some offset prior to the\r\ncurrent row in a [serialized row set](./windowsfunctions.md#serialized-row-set).', + }, + { + text: 'radians', + hint: + 'Converts angle value in degrees into value in radians, using formula `radians = (PI / 180 ) * angle_in_degrees`', + }, + { text: 'rand', hint: 'Returns a random number.' }, + { text: 'range', hint: 'Generates a dynamic array holding a series of equally-spaced values.' }, + { text: 'repeat', hint: 'Generates a dynamic array holding a series of equal values.' }, + { text: 'replace', hint: 'Replace all regex matches with another string.' }, + { text: 'reverse', hint: 'Function makes reverse of input string.' }, + { text: 'round', hint: 'Returns the rounded source to the specified precision.' }, + { + text: 'row_cumsum', + hint: + 'Calculates the cumulative sum of a column in a [serialized row set](./windowsfunctions.md#serialized-row-set).', + }, + { + text: 'row_number', + hint: + "Returns the current row's index in a [serialized row set](./windowsfunctions.md#serialized-row-set).\r\nThe row index starts by default at `1` for the first row, and is incremented by `1` for each additional row.\r\nOptionally, the row index can start at a different value than `1`.\r\nAdditionally, the row index may be reset according to some provided predicate.", + }, + { text: 'series_add', hint: 'Calculates the element-wise addition of two numeric series inputs.' }, + { text: 'series_decompose', hint: 'Applies a decomposition transformation on a series.' }, + { + text: 'series_decompose_anomalies', + hint: + 'Anomaly Detection based on series decomposition (refer to [series_decompose()](series-decomposefunction.md))', + }, + { text: 'series_decompose_forecast', hint: 'Forecast based on series decomposition.' }, + { text: 'series_divide', hint: 'Calculates the element-wise division of two numeric series inputs.' }, + { + text: 'series_equals', + hint: 'Calculates the element-wise equals (`==`) logic operation of two numeric series inputs.', + }, + { text: 'series_fill_backward', hint: 'Performs backward fill interpolation of missing values in a series.' }, + { text: 'series_fill_const', hint: 'Replaces missing values in a series with a specified constant value.' }, + { text: 'series_fill_forward', hint: 'Performs forward fill interpolation of missing values in a series.' }, + { text: 'series_fill_linear', hint: 'Performs linear interpolation of missing values in a series.' }, + { text: 'series_fir', hint: 'Applies a Finite Impulse Response filter on a series.' }, + { + text: 'series_fit_2lines', + hint: 'Applies two segments linear regression on a series, returning multiple columns.', + }, + { + text: 'series_fit_2lines_dynamic', + hint: 'Applies two segments linear regression on a series, returning dynamic object.', + }, + { text: 'series_fit_line', hint: 'Applies linear regression on a series, returning multiple columns.' }, + { text: 'series_fit_line_dynamic', hint: 'Applies linear regression on a series, returning dynamic object.' }, + { + text: 'series_greater', + hint: 'Calculates the element-wise greater (`>`) logic operation of two numeric series inputs.', + }, + { + text: 'series_greater_equals', + hint: 'Calculates the element-wise greater or equals (`>=`) logic operation of two numeric series inputs.', + }, + { text: 'series_iir', hint: 'Applies a Infinite Impulse Response filter on a series.' }, + { text: 'series_less', hint: 'Calculates the element-wise less (`<`) logic operation of two numeric series inputs.' }, + { + text: 'series_less_equals', + hint: 'Calculates the element-wise less or equal (`<=`) logic operation of two numeric series inputs.', + }, + { text: 'series_multiply', hint: 'Calculates the element-wise multiplication of two numeric series inputs.' }, + { + text: 'series_not_equals', + hint: 'Calculates the element-wise not equals (`!=`) logic operation of two numeric series inputs.', + }, + { text: 'series_outliers', hint: 'Scores anomaly points in a series.' }, + { text: 'series_periods_detect', hint: 'Finds the most significant periods that exist in a time series.' }, + { + text: 'series_periods_validate', + hint: 'Checks whether a time series contains periodic patterns of given lengths.', + }, + { + text: 'series_seasonal', + hint: 'Calculates the seasonal component of a series according to the detected or given seasonal period.', + }, + { text: 'series_stats', hint: 'Returns statistics for a series in multiple columns.' }, + { text: 'series_stats_dynamic', hint: 'Returns statistics for a series in dynamic object.' }, + { text: 'series_subtract', hint: 'Calculates the element-wise subtraction of two numeric series inputs.' }, + { text: 'sign', hint: 'Sign of a numeric expression' }, + { text: 'sin', hint: 'Returns the sine function.' }, + { + text: 'split', + hint: + 'Splits a given string according to a given delimiter and returns a string array with the contained substrings.', + }, + { text: 'sqrt', hint: 'Returns the square root function.' }, + { text: 'startofday', hint: 'Returns the start of the day containing the date, shifted by an offset, if provided.' }, + { + text: 'startofmonth', + hint: 'Returns the start of the month containing the date, shifted by an offset, if provided.', + }, + { + text: 'startofweek', + hint: 'Returns the start of the week containing the date, shifted by an offset, if provided.', + }, + { + text: 'startofyear', + hint: 'Returns the start of the year containing the date, shifted by an offset, if provided.', + }, + { + text: 'stdev', + hint: + 'Calculates the standard deviation of *Expr* across the group, considering the group as a [sample](https://en.wikipedia.org/wiki/Sample_%28statistics%29).', + }, + { + text: 'stdevif', + hint: + 'Calculates the [stdev](stdev-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`.', + }, + { + text: 'stdevp', + hint: + 'Calculates the standard deviation of *Expr* across the group, considering the group as a [population](https://en.wikipedia.org/wiki/Statistical_population).', + }, + { text: 'strcat', hint: 'Concatenates between 1 and 64 arguments.' }, + { text: 'strcat_array', hint: 'Creates a concatenated string of array values using specified delimiter.' }, + { + text: 'strcat_delim', + hint: 'Concatenates between 2 and 64 arguments, with delimiter, provided as first argument.', + }, + { text: 'strcmp', hint: 'Compares two strings.' }, + { text: 'string_size', hint: 'Returns the size, in bytes, of the input string.' }, + { text: 'strlen', hint: 'Returns the length, in characters, of the input string.' }, + { text: 'strrep', hint: 'Repeats given [string](./scalar-data-types/string.md) provided amount of times.' }, + { + text: 'substring', + hint: 'Extracts a substring from a source string starting from some index to the end of the string.', + }, + { text: 'sum', hint: 'Calculates the sum of *Expr* across the group.' }, + { text: 'sumif', hint: 'Returns a sum of *Expr* for which *Predicate* evaluates to `true`.' }, + { text: 'table', hint: 'References specific table using an query-time evaluated string-expression.' }, + { text: 'tan', hint: 'Returns the tangent function.' }, + { + text: 'tdigest', + hint: 'Calculates the Intermediate results of [`percentiles()`](percentiles-aggfunction.md) across the group.', + }, + { + text: 'tdigest_merge', + hint: + 'Merges tdigest results (scalar version of the aggregate version [`tdigest_merge()`](tdigest-merge-aggfunction.md)).', + }, + { text: 'tobool', hint: 'Converts input to boolean (signed 8-bit) representation.' }, + { text: 'todatetime', hint: 'Converts input to [datetime](./scalar-data-types/datetime.md) scalar.' }, + { text: 'todecimal', hint: 'Converts input to decimal number representation.' }, + { + text: 'todouble', + hint: 'Converts the input to a value of type `real`. (`todouble()` and `toreal()` are synonyms.)', + }, + { + text: 'todynamic', + hint: + 'Interprets a `string` as a [JSON value](https://json.org/) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md).', + }, + { text: 'toguid', hint: 'Converts input to [`guid`](./scalar-data-types/guid.md) representation.' }, + { text: 'tohex', hint: 'Converts input to a hexadecimal string.' }, + { text: 'toint', hint: 'Converts input to integer (signed 32-bit) number representation.' }, + { text: 'tolong', hint: 'Converts input to long (signed 64-bit) number representation.' }, + { text: 'tolower', hint: 'Converts input string to lower case.' }, + { text: 'toscalar', hint: 'Returns a scalar constant value of the evaluated expression.' }, + { text: 'tostring', hint: 'Converts input to a string representation.' }, + { text: 'totimespan', hint: 'Converts input to [timespan](./scalar-data-types/timespan.md) scalar.' }, + { text: 'toupper', hint: 'Converts a string to upper case.' }, + { + text: 'translate', + hint: + "Replaces a set of characters ('searchList') with another set of characters ('replacementList') in a given a string.\r\nThe function searches for characters in the 'searchList' and replaces them with the corresponding characters in 'replacementList'", + }, + { text: 'treepath', hint: 'Enumerates all the path expressions that identify leaves in a dynamic object.' }, + { text: 'trim', hint: 'Removes all leading and trailing matches of the specified regular expression.' }, + { text: 'trim_end', hint: 'Removes trailing match of the specified regular expression.' }, + { text: 'trim_start', hint: 'Removes leading match of the specified regular expression.' }, + { text: 'url_decode', hint: 'The function converts encoded URL into a to regular URL representation.' }, + { + text: 'url_encode', + hint: 'The function converts characters of the input URL into a format that can be transmitted over the Internet.', + }, + { + text: 'variance', + hint: + 'Calculates the variance of *Expr* across the group, considering the group as a [sample](https://en.wikipedia.org/wiki/Sample_%28statistics%29).', + }, + { + text: 'varianceif', + hint: + 'Calculates the [variance](variance-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`.', + }, + { + text: 'variancep', + hint: + 'Calculates the variance of *Expr* across the group, considering the group as a [population](https://en.wikipedia.org/wiki/Statistical_population).', + }, + { text: 'weekofyear', hint: 'Returns the integer number represents the week number.' }, + { + text: 'welch_test', + hint: 'Computes the p_value of the [Welch-test function](https://en.wikipedia.org/wiki/Welch%27s_t-test)', + }, + { + text: 'zip', + hint: + 'The `zip` function accepts any number of `dynamic` arrays, and returns an\r\narray whose elements are each an array holding the elements of the input\r\narrays of the same index.', + }, +]; + +export const KEYWORDS = [ + 'by', + 'on', + 'contains', + 'notcontains', + 'containscs', + 'notcontainscs', + 'startswith', + 'has', + 'matches', + 'regex', + 'true', + 'false', + 'and', + 'or', + 'typeof', + 'int', + 'string', + 'date', + 'datetime', + 'time', + 'long', + 'real', + '​boolean', + 'bool', +]; + +export const grafanaMacros = [ + { + text: '$__timeFilter', + display: '$__timeFilter()', + hint: 'Macro that uses the selected timerange in Grafana to filter the query.', + }, + { + text: '$__escapeMulti', + display: '$__escapeMulti()', + hint: 'Macro to escape multi-value template variables that contain illegal characters.', + }, + { text: '$__contains', display: '$__contains()', hint: 'Macro for multi-value template variables.' }, +]; + +// Kusto operators +// export const OPERATORS = ['+', '-', '*', '/', '>', '<', '==', '<>', '<=', '>=', '~', '!~']; + +export const DURATION = ['SECONDS', 'MINUTES', 'HOURS', 'DAYS', 'WEEKS', 'MONTHS', 'YEARS']; + +const tokenizer = { + comment: { + pattern: /(^|[^\\:])\/\/.*/, + lookbehind: true, + greedy: true, + }, + 'function-context': { + pattern: /[a-z0-9_]+\([^)]*\)?/i, + inside: {}, + }, + duration: { + pattern: new RegExp(`${DURATION.join('?|')}?`, 'i'), + alias: 'number', + }, + builtin: new RegExp(`\\b(?:${functionTokens.map(f => f.text).join('|')})(?=\\s*\\()`, 'i'), + string: { + pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + greedy: true, + }, + keyword: new RegExp(`\\b(?:${KEYWORDS.join('|')}|${operatorTokens.map(f => f.text).join('|')}|\\*)\\b`, 'i'), + boolean: /\b(?:true|false)\b/, + number: /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i, + operator: /-|\+|\*|\/|>|<|==|<=?|>=?|<>|!~|~|=|\|/, + punctuation: /[{};(),.:]/, + variable: /(\[\[(.+?)\]\])|(\$(.+?))\b/, +}; + +tokenizer['function-context'].inside = { + argument: { + pattern: /[a-z0-9_]+(?=:)/i, + alias: 'symbol', + }, + duration: tokenizer.duration, + number: tokenizer.number, + builtin: tokenizer.builtin, + string: tokenizer.string, + variable: tokenizer.variable, +}; + +// console.log(tokenizer.builtin); + +export default tokenizer; + +// function escapeRegExp(str: string): string { +// return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +// } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx index 1c883a40c31..f24fd9e9dd1 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx @@ -1,14 +1,11 @@ -import PluginPrism from './slate-plugins/prism'; -// import PluginPrism from 'slate-prism'; -// import Prism from 'prismjs'; - +import PluginPrism from 'app/features/explore/slate-plugins/prism'; import BracesPlugin from 'app/features/explore/slate-plugins/braces'; import ClearPlugin from 'app/features/explore/slate-plugins/clear'; -// Custom plugins (new line on Enter and run on Shift+Enter) -import NewlinePlugin from './slate-plugins/newline'; -import RunnerPlugin from './slate-plugins/runner'; +import NewlinePlugin from 'app/features/explore/slate-plugins/newline'; +import RunnerPlugin from 'app/features/explore/slate-plugins/runner'; import Typeahead from './typeahead'; +import { getKeybindingSrv, KeybindingSrv } from 'app/core/services/keybindingSrv'; import { Block, Document, Text, Value } from 'slate'; import { Editor } from 'slate-react'; @@ -61,6 +58,7 @@ class QueryField extends React.Component { menuEl: any; plugins: any; resetTimer: any; + keybindingSrv: KeybindingSrv = getKeybindingSrv(); constructor(props, context) { super(props, context); @@ -90,6 +88,7 @@ class QueryField extends React.Component { } componentWillUnmount() { + this.restoreEscapeKeyBinding(); clearTimeout(this.resetTimer); } @@ -101,11 +100,11 @@ class QueryField extends React.Component { const changed = value.document !== this.state.value.document; this.setState({ value }, () => { if (changed) { + // call typeahead only if query changed + requestAnimationFrame(() => this.onTypeahead()); this.onChangeQuery(); } }); - - window.requestAnimationFrame(this.onTypeahead); }; request = (url?) => { @@ -140,7 +139,7 @@ class QueryField extends React.Component { case ' ': { if (event.ctrlKey) { event.preventDefault(); - this.onTypeahead(); + this.onTypeahead(true); return true; } break; @@ -218,6 +217,7 @@ class QueryField extends React.Component { if (onBlur) { onBlur(); } + this.restoreEscapeKeyBinding(); }; handleFocus = () => { @@ -225,8 +225,18 @@ class QueryField extends React.Component { if (onFocus) { onFocus(); } + // Don't go back to dashboard if Escape pressed inside the editor. + this.removeEscapeKeyBinding(); }; + removeEscapeKeyBinding() { + this.keybindingSrv.unbind('esc', 'keydown'); + } + + restoreEscapeKeyBinding() { + this.keybindingSrv.setupGlobal(); + } + onClickItem = item => { const { suggestions } = this.state; if (!suggestions || suggestions.length === 0) { @@ -269,12 +279,18 @@ class QueryField extends React.Component { const rect = node.parentElement.getBoundingClientRect(); const scrollX = window.scrollX; const scrollY = window.scrollY; + const screenHeight = window.innerHeight; + + const menuLeft = rect.left + scrollX - 2; + const menuTop = rect.top + scrollY + rect.height + 4; + const menuHeight = screenHeight - menuTop - 10; // Write DOM requestAnimationFrame(() => { menu.style.opacity = 1; - menu.style.top = `${rect.top + scrollY + rect.height + 4}px`; - menu.style.left = `${rect.left + scrollX - 2}px`; + menu.style.top = `${menuTop}px`; + menu.style.left = `${menuLeft}px`; + menu.style.maxHeight = `${menuHeight}px`; }); } }; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/slate-plugins/newline.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/slate-plugins/newline.ts deleted file mode 100644 index d484d93a542..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/slate-plugins/newline.ts +++ /dev/null @@ -1,35 +0,0 @@ -function getIndent(text) { - let offset = text.length - text.trimLeft().length; - if (offset) { - let indent = text[0]; - while (--offset) { - indent += text[0]; - } - return indent; - } - return ''; -} - -export default function NewlinePlugin() { - return { - onKeyDown(event, change) { - const { value } = change; - if (!value.isCollapsed) { - return undefined; - } - - if (event.key === 'Enter' && !event.shiftKey) { - event.preventDefault(); - - const { startBlock } = value; - const currentLineText = startBlock.text; - const indent = getIndent(currentLineText); - - return change - .splitBlock() - .insertText(indent) - .focus(); - } - }, - }; -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/slate-plugins/runner.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/slate-plugins/runner.ts deleted file mode 100644 index 068bd9f0ad1..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/slate-plugins/runner.ts +++ /dev/null @@ -1,14 +0,0 @@ -export default function RunnerPlugin({ handler }) { - return { - onKeyDown(event) { - // Handle enter - if (handler && event.key === 'Enter' && event.shiftKey) { - // Submit on Enter - event.preventDefault(); - handler(event); - return true; - } - return undefined; - }, - }; -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html index 49f02ec8355..6299947b30a 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html @@ -124,12 +124,11 @@
@@ -285,9 +284,20 @@
-
+ +
+
diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json b/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json index 76a56f2baaa..e4f48c581e3 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json @@ -158,5 +158,6 @@ }, "metrics": true, - "annotations": true + "annotations": true, + "alerting": true } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts index fd42c172f11..6374af8c599 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts @@ -304,7 +304,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { /* Azure Log Analytics */ - getWorkspaces() { + getWorkspaces = () => { return this.datasource.azureLogAnalyticsDatasource .getWorkspaces() .then(list => { @@ -314,23 +314,23 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { } }) .catch(this.handleQueryCtrlError.bind(this)); - } + }; - getAzureLogAnalyticsSchema() { + getAzureLogAnalyticsSchema = () => { return this.getWorkspaces() .then(() => { return this.datasource.azureLogAnalyticsDatasource.getSchema(this.target.azureLogAnalytics.workspace); }) .catch(this.handleQueryCtrlError.bind(this)); - } + }; onLogAnalyticsQueryChange = (nextQuery: string) => { this.target.azureLogAnalytics.query = nextQuery; - } + }; onLogAnalyticsQueryExecute = () => { this.panelCtrl.refresh(); - } + }; get templateVariables() { return this.templateSrv.variables.map(t => '$' + t.name); @@ -345,6 +345,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { } return interval; } + getAppInsightsMetricNames() { if (!this.datasource.appInsightsDatasource.isConfigured()) { return; @@ -377,6 +378,18 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { .catch(this.handleQueryCtrlError.bind(this)); } + onAppInsightsQueryChange = (nextQuery: string) => { + this.target.appInsights.rawQueryString = nextQuery; + }; + + onAppInsightsQueryExecute = () => { + return this.refresh(); + }; + + getAppInsightsQuerySchema = () => { + return this.datasource.appInsightsDatasource.getQuerySchema().catch(this.handleQueryCtrlError.bind(this)); + }; + getAppInsightsGroupBySegments(query) { return _.map(this.target.appInsights.groupByOptions, option => { return { text: option, value: option }; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/version.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/version.test.ts deleted file mode 100644 index 17a6ce9bb0b..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/version.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { SemVersion, isVersionGtOrEq } from './version'; - -describe('SemVersion', () => { - let version = '1.0.0-alpha.1'; - - describe('parsing', () => { - it('should parse version properly', () => { - const semver = new SemVersion(version); - expect(semver.major).toBe(1); - expect(semver.minor).toBe(0); - expect(semver.patch).toBe(0); - expect(semver.meta).toBe('alpha.1'); - }); - }); - - describe('comparing', () => { - beforeEach(() => { - version = '3.4.5'; - }); - - it('should detect greater version properly', () => { - const semver = new SemVersion(version); - const cases = [ - { value: '3.4.5', expected: true }, - { value: '3.4.4', expected: true }, - { value: '3.4.6', expected: false }, - { value: '4', expected: false }, - { value: '3.5', expected: false }, - ]; - cases.forEach(testCase => { - expect(semver.isGtOrEq(testCase.value)).toBe(testCase.expected); - }); - }); - }); - - describe('isVersionGtOrEq', () => { - it('should compare versions properly (a >= b)', () => { - const cases = [ - { values: ['3.4.5', '3.4.5'], expected: true }, - { values: ['3.4.5', '3.4.4'], expected: true }, - { values: ['3.4.5', '3.4.6'], expected: false }, - { values: ['3.4', '3.4.0'], expected: true }, - { values: ['3', '3.0.0'], expected: true }, - { values: ['3.1.1-beta1', '3.1'], expected: true }, - { values: ['3.4.5', '4'], expected: false }, - { values: ['3.4.5', '3.5'], expected: false }, - ]; - cases.forEach(testCase => { - expect(isVersionGtOrEq(testCase.values[0], testCase.values[1])).toBe(testCase.expected); - }); - }); - }); -}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/version.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/version.ts deleted file mode 100644 index 1131e1d2ab8..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/version.ts +++ /dev/null @@ -1,34 +0,0 @@ -import _ from 'lodash'; - -const versionPattern = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z\.]+))?/; - -export class SemVersion { - major: number; - minor: number; - patch: number; - meta: string; - - constructor(version: string) { - const match = versionPattern.exec(version); - if (match) { - this.major = Number(match[1]); - this.minor = Number(match[2] || 0); - this.patch = Number(match[3] || 0); - this.meta = match[4]; - } - } - - isGtOrEq(version: string): boolean { - const compared = new SemVersion(version); - return !(this.major < compared.major || this.minor < compared.minor || this.patch < compared.patch); - } - - isValid(): boolean { - return _.isNumber(this.major); - } -} - -export function isVersionGtOrEq(a: string, b: string): boolean { - const aSemver = new SemVersion(a); - return aSemver.isGtOrEq(b); -} diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index a89c0ea0034..8a720cb3f35 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -31,8 +31,8 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, this.query = function(options) { const graphOptions = { - from: this.translateTime(options.rangeRaw.from, false), - until: this.translateTime(options.rangeRaw.to, true), + from: this.translateTime(options.rangeRaw.from, false, options.timezone), + until: this.translateTime(options.rangeRaw.to, true, options.timezone), targets: options.targets, format: options.format, cacheTimeout: options.cacheTimeout || this.cacheTimeout, @@ -165,9 +165,9 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, method: 'GET', url: '/events/get_data?from=' + - this.translateTime(options.range.from, false) + + this.translateTime(options.range.from, false, options.timezone) + '&until=' + - this.translateTime(options.range.to, true) + + this.translateTime(options.range.to, true, options.timezone) + tags, }); } catch (err) { @@ -179,7 +179,7 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, return templateSrv.variableExists(target.target); }; - this.translateTime = (date, roundUp) => { + this.translateTime = (date, roundUp, timezone) => { if (_.isString(date)) { if (date === 'now') { return 'now'; @@ -189,7 +189,7 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, date = date.replace('M', 'mon'); return date; } - date = dateMath.parse(date, roundUp); + date = dateMath.parse(date, roundUp, timezone); } // graphite' s from filter is exclusive @@ -255,8 +255,8 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, }; if (options.range) { - httpOptions.params.from = this.translateTime(options.range.from, false); - httpOptions.params.until = this.translateTime(options.range.to, true); + httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); + httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } return this.doGraphiteRequest(httpOptions).then(results => { @@ -280,8 +280,8 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, }; if (options.range) { - httpOptions.params.from = this.translateTime(options.range.from, false); - httpOptions.params.until = this.translateTime(options.range.to, true); + httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); + httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } return this.doGraphiteRequest(httpOptions).then(results => { @@ -305,8 +305,8 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, }; if (options.range) { - httpOptions.params.from = this.translateTime(options.range.from, false); - httpOptions.params.until = this.translateTime(options.range.to, true); + httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); + httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } return this.doGraphiteRequest(httpOptions).then(results => { @@ -343,8 +343,8 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, httpOptions.params.limit = options.limit; } if (options.range) { - httpOptions.params.from = this.translateTime(options.range.from, false); - httpOptions.params.until = this.translateTime(options.range.to, true); + httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); + httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } return this.doGraphiteRequest(httpOptions).then(results => { @@ -379,8 +379,8 @@ export function GraphiteDatasource(this: any, instanceSettings, $q, backendSrv, httpOptions.params.limit = options.limit; } if (options.range) { - httpOptions.params.from = this.translateTime(options.range.from, false); - httpOptions.params.until = this.translateTime(options.range.to, true); + httpOptions.params.from = this.translateTime(options.range.from, false, options.timezone); + httpOptions.params.until = this.translateTime(options.range.to, true, options.timezone); } return this.doGraphiteRequest(httpOptions).then(results => { diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 8f1904dbc55..4e4c3feaebd 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -127,7 +127,7 @@ export default class InfluxDatasource { }); } - const timeFilter = this.getTimeFilter({ rangeRaw: options.rangeRaw }); + const timeFilter = this.getTimeFilter({ rangeRaw: options.rangeRaw, timezone: options.timezone }); let query = options.annotation.query.replace('$timeFilter', timeFilter); query = this.templateSrv.replace(query, null, 'regex'); @@ -184,7 +184,7 @@ export default class InfluxDatasource { } if (options && options.range) { - const timeFilter = this.getTimeFilter({ rangeRaw: options.range }); + const timeFilter = this.getTimeFilter({ rangeRaw: options.range, timezone: options.timezone }); query = query.replace('$timeFilter', timeFilter); } @@ -291,8 +291,8 @@ export default class InfluxDatasource { } getTimeFilter(options) { - const from = this.getInfluxTime(options.rangeRaw.from, false); - const until = this.getInfluxTime(options.rangeRaw.to, true); + const from = this.getInfluxTime(options.rangeRaw.from, false, options.timezone); + const until = this.getInfluxTime(options.rangeRaw.to, true, options.timezone); const fromIsAbsolute = from[from.length - 1] === 'ms'; if (until === 'now()' && !fromIsAbsolute) { @@ -302,7 +302,7 @@ export default class InfluxDatasource { return 'time >= ' + from + ' and time <= ' + until; } - getInfluxTime(date, roundUp) { + getInfluxTime(date, roundUp, timezone) { if (_.isString(date)) { if (date === 'now') { return 'now()'; @@ -314,7 +314,7 @@ export default class InfluxDatasource { const unit = parts[2]; return 'now() - ' + amount + unit; } - date = dateMath.parse(date, roundUp); + date = dateMath.parse(date, roundUp, timezone); } return date.valueOf() + 'ms'; diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx index a1b9e7a5df9..f36623596d2 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx @@ -2,61 +2,67 @@ import React, { PureComponent } from 'react'; // Components -import { Select, SelectOptionItem } from '@grafana/ui'; +// import { Select, SelectOptionItem } from '@grafana/ui'; // Types import { QueryEditorProps } from '@grafana/ui/src/types'; import { LokiDatasource } from '../datasource'; import { LokiQuery } from '../types'; -import { LokiQueryField } from './LokiQueryField'; +// import { LokiQueryField } from './LokiQueryField'; type Props = QueryEditorProps; -interface State { - query: LokiQuery; -} +// interface State { +// query: LokiQuery; +// } export class LokiQueryEditor extends PureComponent { - state: State = { - query: this.props.query, - }; - - onRunQuery = () => { - const { query } = this.state; - - this.props.onChange(query); - this.props.onRunQuery(); - }; - - onFieldChange = (query: LokiQuery, override?) => { - this.setState({ - query: { - ...this.state.query, - expr: query.expr, - }, - }); - }; - - onFormatChanged = (option: SelectOptionItem) => { - this.props.onChange({ - ...this.state.query, - resultFormat: option.value, - }); - }; + // state: State = { + // query: this.props.query, + // }; + // + // onRunQuery = () => { + // const { query } = this.state; + // + // this.props.onChange(query); + // this.props.onRunQuery(); + // }; + // + // onFieldChange = (query: LokiQuery, override?) => { + // this.setState({ + // query: { + // ...this.state.query, + // expr: query.expr, + // }, + // }); + // }; + // + // onFormatChanged = (option: SelectOptionItem) => { + // this.props.onChange({ + // ...this.state.query, + // resultFormat: option.value, + // }); + // }; render() { - const { query } = this.state; - const { datasource } = this.props; - const formatOptions: SelectOptionItem[] = [ - { label: 'Time Series', value: 'time_series' }, - { label: 'Table', value: 'table' }, - ]; - - query.resultFormat = query.resultFormat || 'time_series'; - const currentFormat = formatOptions.find(item => item.value === query.resultFormat); + // const { query } = this.state; + // const { datasource } = this.props; + // const formatOptions: SelectOptionItem[] = [ + // { label: 'Time Series', value: 'time_series' }, + // { label: 'Table', value: 'table' }, + // ]; + // + // query.resultFormat = query.resultFormat || 'time_series'; + // const currentFormat = formatOptions.find(item => item.value === query.resultFormat); return (
+
+
+ Loki is currently not supported as dashboard data source. We are working on it! +
+
+ {/* {
+ */}
); } diff --git a/public/app/plugins/datasource/mssql/datasource.ts b/public/app/plugins/datasource/mssql/datasource.ts index 23aa5504d3e..303cd0471d7 100644 --- a/public/app/plugins/datasource/mssql/datasource.ts +++ b/public/app/plugins/datasource/mssql/datasource.ts @@ -8,7 +8,7 @@ export class MssqlDatasource { interval: string; /** @ngInject */ - constructor(instanceSettings, private backendSrv, private $q, private templateSrv) { + constructor(instanceSettings, private backendSrv, private $q, private templateSrv, private timeSrv) { this.name = instanceSettings.name; this.id = instanceSettings.id; this.responseParser = new ResponseParser(this.$q); @@ -107,13 +107,18 @@ export class MssqlDatasource { format: 'table', }; + const range = this.timeSrv.timeRange(); + const data = { + queries: [interpolatedQuery], + from: range.from.valueOf().toString(), + to: range.to.valueOf().toString(), + }; + return this.backendSrv .datasourceRequest({ url: '/api/tsdb/query', method: 'POST', - data: { - queries: [interpolatedQuery], - }, + data: data, }) .then(data => this.responseParser.parseMetricFindQueryResult(refId, data)); } diff --git a/public/app/plugins/datasource/mssql/specs/datasource.test.ts b/public/app/plugins/datasource/mssql/specs/datasource.test.ts index 0dd496bfe59..49b13387369 100644 --- a/public/app/plugins/datasource/mssql/specs/datasource.test.ts +++ b/public/app/plugins/datasource/mssql/specs/datasource.test.ts @@ -1,6 +1,6 @@ import moment from 'moment'; import { MssqlDatasource } from '../datasource'; -import { TemplateSrvStub } from 'test/specs/helpers'; +import { TemplateSrvStub, TimeSrvStub } from 'test/specs/helpers'; import { CustomVariable } from 'app/features/templating/custom_variable'; import q from 'q'; @@ -8,13 +8,14 @@ describe('MSSQLDatasource', () => { const ctx: any = { backendSrv: {}, templateSrv: new TemplateSrvStub(), + timeSrv: new TimeSrvStub(), }; beforeEach(() => { ctx.$q = q; ctx.instanceSettings = { name: 'mssql' }; - ctx.ds = new MssqlDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.$q, ctx.templateSrv); + ctx.ds = new MssqlDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.$q, ctx.templateSrv, ctx.timeSrv); }); describe('When performing annotationQuery', () => { @@ -188,6 +189,49 @@ describe('MSSQLDatasource', () => { }); }); + describe('When performing metricFindQuery', () => { + let results; + const query = 'select * from atable'; + const response = { + results: { + tempvar: { + meta: { + rowCount: 1, + }, + refId: 'tempvar', + tables: [ + { + columns: [{ text: 'title' }], + rows: [['aTitle']], + }, + ], + }, + }, + }; + const time = { + from: moment(1521545610656), + to: moment(1521546251185), + }; + + beforeEach(() => { + ctx.timeSrv.setTime(time); + + ctx.backendSrv.datasourceRequest = options => { + results = options.data; + return ctx.$q.when({ data: response, status: 200 }); + }; + + return ctx.ds.metricFindQuery(query); + }); + + it('should pass timerange to datasourceRequest', () => { + expect(results.from).toBe(time.from.valueOf().toString()); + expect(results.to).toBe(time.to.valueOf().toString()); + expect(results.queries.length).toBe(1); + expect(results.queries[0].rawSql).toBe(query); + }); + }); + describe('When interpolating variables', () => { beforeEach(() => { ctx.variable = new CustomVariable({}, {}); diff --git a/public/app/plugins/datasource/mysql/partials/config.html b/public/app/plugins/datasource/mysql/partials/config.html index a35633c626a..8221a06e1ee 100644 --- a/public/app/plugins/datasource/mysql/partials/config.html +++ b/public/app/plugins/datasource/mysql/partials/config.html @@ -1,4 +1,3 @@ -

MySQL Connection

@@ -22,7 +21,23 @@
-
+ +
+
+ + +
+
+ +
+
+ + + Connection limits @@ -84,4 +99,3 @@

- diff --git a/public/app/plugins/datasource/opentsdb/datasource.ts b/public/app/plugins/datasource/opentsdb/datasource.ts index 772f2aa7ff9..bab86a04765 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.ts +++ b/public/app/plugins/datasource/opentsdb/datasource.ts @@ -33,8 +33,8 @@ export default class OpenTsDatasource { // Called once per panel (graph) query(options) { - const start = this.convertToTSDBTime(options.rangeRaw.from, false); - const end = this.convertToTSDBTime(options.rangeRaw.to, true); + const start = this.convertToTSDBTime(options.rangeRaw.from, false, options.timezone); + const end = this.convertToTSDBTime(options.rangeRaw.to, true, options.timezone); const qs = []; _.each(options.targets, target => { @@ -86,8 +86,8 @@ export default class OpenTsDatasource { } annotationQuery(options) { - const start = this.convertToTSDBTime(options.rangeRaw.from, false); - const end = this.convertToTSDBTime(options.rangeRaw.to, true); + const start = this.convertToTSDBTime(options.rangeRaw.from, false, options.timezone); + const end = this.convertToTSDBTime(options.rangeRaw.to, true, options.timezone); const qs = []; const eventList = []; @@ -484,12 +484,12 @@ export default class OpenTsDatasource { }); } - convertToTSDBTime(date, roundUp) { + convertToTSDBTime(date, roundUp, timezone) { if (date === 'now') { return null; } - date = dateMath.parse(date, roundUp); + date = dateMath.parse(date, roundUp, timezone); return date.valueOf(); } } diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index a6d97856e74..d327bc0cf7d 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -12,6 +12,7 @@ import appEvents from 'app/core/app_events'; import { BackendSrv, setBackendSrv } from 'app/core/services/backend_srv'; import { TimeSrv, setTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { DatasourceSrv, setDatasourceSrv } from 'app/features/plugins/datasource_srv'; +import { KeybindingSrv, setKeybindingSrv } from 'app/core/services/keybindingSrv'; import { AngularLoader, setAngularLoader } from 'app/core/services/AngularLoader'; import { configureStore } from 'app/store/configureStore'; @@ -30,6 +31,7 @@ export class GrafanaCtrl { backendSrv: BackendSrv, timeSrv: TimeSrv, datasourceSrv: DatasourceSrv, + keybindingSrv: KeybindingSrv, angularLoader: AngularLoader ) { // make angular loader service available to react components @@ -37,6 +39,7 @@ export class GrafanaCtrl { setBackendSrv(backendSrv); setDatasourceSrv(datasourceSrv); setTimeSrv(timeSrv); + setKeybindingSrv(keybindingSrv); configureStore(); $scope.init = () => { diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index e0029cf2464..4c9c5fd5304 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -150,8 +150,8 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { controllerAs: 'ctrl', }) .when('/dashboards/f/:uid', { - templateUrl: 'public/app/features/dashboard/partials/folder_dashboards.html', - controller: 'FolderDashboardsCtrl', + templateUrl: 'public/app/features/folders/partials/folder_dashboards.html', + controller: FolderDashboardsCtrl, controllerAs: 'ctrl', }) .when('/explore', { diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index e2c33523271..2638587e96d 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -1,6 +1,6 @@ import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; import thunk from 'redux-thunk'; -import { createLogger } from 'redux-logger'; +// import { createLogger } from 'redux-logger'; import sharedReducers from 'app/core/reducers'; import alertingReducers from 'app/features/alerting/state/reducers'; import teamsReducers from 'app/features/teams/state/reducers'; @@ -41,7 +41,7 @@ export function configureStore() { if (process.env.NODE_ENV !== 'production') { // DEV builds we had the logger middleware - setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk, createLogger())))); + setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk)))); } else { setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk)))); } diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 9c8d977c3ad..7a6af04b2ee 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -11,7 +11,7 @@ import { } from '@grafana/ui'; import { Emitter } from 'app/core/core'; -import { LogsModel } from 'app/core/logs_model'; +import { LogsModel, LogsDedupStrategy, LogLevel } from 'app/core/logs_model'; import TableModel from 'app/core/table_model'; export interface CompletionItem { @@ -237,12 +237,23 @@ export interface ExploreItemState { * React keys for rendering of QueryRows */ queryKeys: string[]; + + /** + * Current logs deduplication strategy + */ + dedupStrategy?: LogsDedupStrategy; + + /** + * Currently hidden log series + */ + hiddenLogLevels?: LogLevel[]; } export interface ExploreUIState { showingTable: boolean; showingGraph: boolean; showingLogs: boolean; + dedupStrategy?: LogsDedupStrategy; } export interface ExploreUrlState { diff --git a/public/app/types/location.ts b/public/app/types/location.ts index a47ef05d2be..4730f9d6ed7 100644 --- a/public/app/types/location.ts +++ b/public/app/types/location.ts @@ -15,6 +15,7 @@ export interface LocationState { query: UrlQueryMap; routeParams: UrlQueryMap; replace: boolean; + lastUpdated: number; } export type UrlQueryValue = string | number | boolean | string[] | number[] | boolean[]; diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index ce5ccfdf1f9..704c1cb7785 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -1,384 +1,383 @@ - - // Global values - // -------------------------------------------------- - - $theme-name: dark; - - // Grays - // ------------------------- - $black: #000; - $dark-1: #141414; - $dark-2: #1f1f20; - $dark-3: #262628; - $dark-4: #333333; - $dark-5: #444444; - $gray-1: #555555; - $gray-2: #8e8e8e; - $gray-3: #b3b3b3; - $gray-4: #d8d9da; - $gray-5: #ececec; - $gray-7: #fbfbfb; - - $gray-blue: #212327; - $input-black: #09090b; - - $white: #fff; - - // Accent colors - // ------------------------- - $blue: #ff0000; - $blue-dark: #005f81; - $green: #299c46; - $red: #d44a3a; - $yellow: #ecbb13; - $purple: #9933cc; - $variable: #32d1df; - $orange: #eb7b18; - - $brand-primary: $orange; - $brand-success: $green; - $brand-warning: $brand-primary; - $brand-danger: $red; - - $query-red: #e24d42; - $query-green: #74e680; - $query-purple: #fe85fc; - $query-keyword: #66d9ef; - $query-orange: $orange; - - // Status colors - // ------------------------- - $online: #10a345; - $warn: #f79520; - $critical: #ed2e18; - - // Scaffolding - // ------------------------- - $body-bg: rgb(23, 24, 25); - $page-bg: rgb(22, 23, 25); - - $body-color: $gray-4; - $text-color: $gray-4; - $text-color-strong: $white; - $text-color-weak: $gray-2; - $text-color-faint: $dark-5; - $text-color-emphasis: $gray-5; - - $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); - $textShadow: none; - - // gradients - $brand-gradient: linear-gradient( - to right, - rgba(255, 213, 0, 0.7) 0%, - rgba(255, 68, 0, 0.7) 99%, - rgba(255, 68, 0, 0.7) 100% - ); - - $page-gradient: linear-gradient(180deg, #222426 10px, rgb(22, 23, 25) 100px); - $edit-gradient: linear-gradient(180deg, rgb(22, 23, 25) 50%, #090909); - - // Links - // ------------------------- - $link-color: darken($white, 11%); - $link-color-disabled: darken($link-color, 30%); - $link-hover-color: $white; - $external-link-color: $blue; - - // Typography - // ------------------------- - $headings-color: darken($white, 11%); - $abbr-border-color: $gray-3 !default; - $text-muted: $text-color-weak; - - $hr-border-color: $dark-4; - - // Panel - // ------------------------- - $panel-bg: #212124; - $panel-border: solid 1px $dark-1; - $panel-header-hover-bg: $dark-4; - $panel-corner: $panel-bg; - - // page header - $page-header-bg: linear-gradient(90deg, #292a2d, black); - $page-header-shadow: inset 0px -4px 14px $dark-2; - $page-header-border-color: $dark-4; - - $divider-border-color: $gray-1; - - // Graphite Target Editor - $tight-form-bg: $dark-3; - $tight-form-func-bg: $dark-4; - $tight-form-func-highlight-bg: $dark-5; - - $modal-backdrop-bg: #353c42; - $code-tag-bg: $dark-1; - $code-tag-border: $dark-4; - - // cards - $card-background: linear-gradient(135deg, #2f2f32, #262628); - $card-background-hover: linear-gradient(135deg, #343436, #262628); - $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3); - - // Lists - $list-item-bg: $card-background; - $list-item-hover-bg: lighten($gray-blue, 2%); - $list-item-link-color: $text-color; - $list-item-shadow: $card-shadow; - - $empty-list-cta-bg: $gray-blue; - - // Scrollbars - $scrollbarBackground: #404357; - $scrollbarBackground2: #3a3a3a; - $scrollbarBorder: black; - - // Tables - // ------------------------- - $table-bg: transparent; // overall background-color - $table-bg-accent: $dark-3; // for striping - $table-border: $dark-3; // table and cell border - - $table-bg-odd: $dark-2; - $table-bg-hover: $dark-3; - - // Buttons - // ------------------------- - $btn-primary-bg: #ff6600; - $btn-primary-bg-hl: #bc3e06; - - $btn-secondary-bg-hl: lighten($blue-dark, 5%); - $btn-secondary-bg: $blue-dark; - - $btn-success-bg: $green; - $btn-success-bg-hl: darken($green, 6%); - - $btn-warning-bg: $brand-warning; - $btn-warning-bg-hl: lighten($brand-warning, 8%); - - $btn-danger-bg: $red; - $btn-danger-bg-hl: darken($red, 8%); - - $btn-inverse-bg: $dark-3; - $btn-inverse-bg-hl: lighten($dark-3, 4%); - $btn-inverse-text-color: $link-color; - $btn-inverse-text-shadow: 0px 1px 0 rgba(0, 0, 0, 0.1); - - $btn-link-color: $gray-3; - - $iconContainerBackground: $black; - - $btn-divider-left: $dark-4; - $btn-divider-right: $dark-2; - - $btn-drag-image: '../img/grab_dark.svg'; - - // Forms - // ------------------------- - $input-bg: $input-black; - $input-bg-disabled: $dark-3; - - $input-color: $gray-4; - $input-border-color: $dark-3; - $input-box-shadow: inset 1px 0px 0.3rem 0px rgba(150, 150, 150, 0.1); - $input-border-focus: $input-border-color; - $input-box-shadow-focus: rgba(102, 175, 233, 0.6); - $input-color-placeholder: $gray-1 !default; - $input-label-bg: $gray-blue; - $input-label-border-color: $dark-3; - $input-color-select-arrow: $white; - - // Input placeholder text color - $placeholderText: darken($text-color, 25%); - - // Search - $search-shadow: 0 0 30px 0 $black; - $search-filter-box-bg: $gray-blue; - - // Typeahead - $typeahead-shadow: 0 5px 10px 0 $black; - $typeahead-selected-bg: $dark-4; - $typeahead-selected-color: $yellow; - - // Dropdowns - // ------------------------- - $dropdownBackground: $dark-3; - $dropdownBorder: rgba(0, 0, 0, 0.2); - $dropdownDividerTop: transparent; - $dropdownDividerBottom: #444; - - $dropdownLinkColor: $text-color; - $dropdownLinkColorHover: $white; - $dropdownLinkColorActive: $white; - - $dropdownLinkBackgroundHover: $dark-4; - - // Horizontal forms & lists - // ------------------------- - $horizontalComponentOffset: 180px; - - // Navbar - // ------------------------- - $navbarHeight: 55px; - - $navbarBackground: $panel-bg; - $navbarBorder: 1px solid $dark-3; - $navbarShadow: 0 0 20px black; - - $navbarLinkColor: $gray-4; - - $navbarButtonBackground: $navbarBackground; - $navbarButtonBackgroundHighlight: $body-bg; - - $navbar-button-border: #2f2f32; - - // Sidemenu - // ------------------------- - $side-menu-bg: $black; - $side-menu-bg-mobile: $side-menu-bg; - $side-menu-item-hover-bg: $dark-2; - $side-menu-shadow: 0 0 20px black; - $side-menu-link-color: $link-color; - - // Menu dropdowns - // ------------------------- - $menu-dropdown-bg: $body-bg; - $menu-dropdown-hover-bg: $dark-2; - $menu-dropdown-shadow: 5px 5px 20px -5px $black; - - // Tabs - // ------------------------- - $tab-border-color: $dark-4; - - // Toolbar - $toolbar-bg: $input-black; - - // Form states and alerts - // ------------------------- - $warning-text-color: $warn; - $error-text-color: #e84d4d; - $success-text-color: #12d95a; - $info-text-color: $blue-dark; - - $alert-error-bg: linear-gradient(90deg, #d44939, #e0603d); - $alert-success-bg: linear-gradient(90deg, #3aa655, #47b274); - $alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d); - $alert-info-bg: linear-gradient(100deg, #1a4552, #00374a); - - // popover - $popover-bg: $page-bg; - $popover-color: $text-color; - $popover-border-color: $dark-4; - $popover-shadow: 0 0 20px black; - - $popover-help-bg: $btn-secondary-bg; - $popover-help-color: $text-color; - - $popover-error-bg: $btn-danger-bg; - - // Tooltips and popovers - // ------------------------- - $tooltipColor: $popover-help-color; - $tooltipArrowWidth: 5px; - $tooltipLinkColor: $link-color; - $graph-tooltip-bg: $dark-1; - - $tooltipBackground: $black; - $tooltipColor: $gray-4; - $tooltipArrowColor: $tooltipBackground; - $tooltipBackgroundError: $brand-danger; - - // images - $checkboxImageUrl: '../img/checkbox.png'; - - // info box - $info-box-border-color: darken($blue, 12%); - - // footer - $footer-link-color: $gray-2; - $footer-link-hover: $gray-4; - - // json-explorer - $json-explorer-default-color: $text-color; - $json-explorer-string-color: #23d662; - $json-explorer-number-color: $variable; - $json-explorer-boolean-color: $variable; - $json-explorer-null-color: #eec97d; - $json-explorer-undefined-color: rgb(239, 143, 190); - $json-explorer-function-color: #fd48cb; - $json-explorer-rotate-time: 100ms; - $json-explorer-toggler-opacity: 0.6; - $json-explorer-bracket-color: #9494ff; - $json-explorer-key-color: #23a0db; - $json-explorer-url-color: #027bff; - - // Changelog and diff - // ------------------------- - $diff-label-bg: $dark-2; - $diff-label-fg: $white; - - $diff-group-bg: $dark-4; - $diff-arrow-color: $white; - - $diff-json-bg: $dark-4; - $diff-json-fg: $gray-5; - - $diff-json-added: #457740; - $diff-json-deleted: #a04338; - - $diff-json-old: #a04338; - $diff-json-new: #457740; - - $diff-json-changed-fg: $gray-5; - $diff-json-changed-num: $text-color; - - $diff-json-icon: $gray-7; - - //Submenu - $variable-option-bg: $blue-dark; - - //Switch Slider - // ------------------------- - $switch-bg: $input-bg; - $switch-slider-color: $dark-2; - $switch-slider-off-bg: $gray-1; - $switch-slider-on-bg: linear-gradient(90deg, $orange, $red); - $switch-slider-shadow: 0 0 3px black; - - //Checkbox - // ------------------------- - $checkbox-bg: $dark-1; - $checkbox-border: 1px solid $gray-1; - $checkbox-checked-bg: linear-gradient(0deg, $orange, $red); - $checkbox-color: $dark-1; - - //Panel Edit - // ------------------------- - $panel-editor-shadow: 0 0 20px black; - $panel-editor-side-menu-shadow: drop-shadow(0 0 10px $black); - $panel-editor-viz-item-shadow: 0 0 8px $dark-5; - $panel-editor-viz-item-border: 1px solid $dark-5; - $panel-editor-viz-item-shadow-hover: 0 0 4px $blue; - $panel-editor-viz-item-border-hover: 1px solid $blue; - $panel-editor-viz-item-bg: $input-black; - $panel-editor-tabs-line-color: #e3e3e3; - $panel-editor-viz-item-bg-hover: darken($blue, 47%); - - $panel-options-group-border: none; - $panel-options-group-header-bg: $gray-blue; - - $panel-grid-placeholder-bg: darken($blue, 47%); - $panel-grid-placeholder-shadow: 0 0 4px $blue; - - // logs - $logs-color-unkown: $gray-2; - - // toggle-group - $button-toggle-group-btn-active-bg: linear-gradient(90deg, $orange, $red); - $button-toggle-group-btn-active-shadow: inset 0 0 4px $black; - $button-toggle-group-btn-seperator-border: 1px solid $page-bg; - - $vertical-resize-handle-bg: $dark-5; - $vertical-resize-handle-dots: $gray-1; +// Global values +// -------------------------------------------------- + +$theme-name: dark; + +// Grays +// ------------------------- +$black: #000; +$dark-1: #141414; +$dark-2: #1f1f20; +$dark-3: #262628; +$dark-4: #333333; +$dark-5: #444444; +$gray-1: #555555; +$gray-2: #8e8e8e; +$gray-3: #b3b3b3; +$gray-4: #d8d9da; +$gray-5: #ececec; +$gray-7: #fbfbfb; + +$gray-blue: #212327; +$input-black: #09090b; + +$white: #fff; + +// Accent colors +// ------------------------- +$blue: #ff0000; +$blue-dark: #005f81; +$green: #299c46; +$red: #d44a3a; +$yellow: #ecbb13; +$purple: #9933cc; +$variable: #32d1df; +$orange: #eb7b18; + +$brand-primary: $orange; +$brand-success: $green; +$brand-warning: $brand-primary; +$brand-danger: $red; + +$query-red: #e24d42; +$query-green: #74e680; +$query-purple: #fe85fc; +$query-keyword: #66d9ef; +$query-orange: $orange; + +// Status colors +// ------------------------- +$online: #10a345; +$warn: #f79520; +$critical: #ed2e18; + +// Scaffolding +// ------------------------- +$body-bg: rgb(23, 24, 25); +$page-bg: rgb(22, 23, 25); + +$body-color: $gray-4; +$text-color: $gray-4; +$text-color-strong: $white; +$text-color-weak: $gray-2; +$text-color-faint: $dark-5; +$text-color-emphasis: $gray-5; + +$text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); +$textShadow: none; + +// gradients +$brand-gradient: linear-gradient( + to right, + rgba(255, 213, 0, 0.7) 0%, + rgba(255, 68, 0, 0.7) 99%, + rgba(255, 68, 0, 0.7) 100% +); + +$page-gradient: linear-gradient(180deg, #222426 10px, rgb(22, 23, 25) 100px); +$edit-gradient: linear-gradient(180deg, rgb(22, 23, 25) 50%, #090909); + +// Links +// ------------------------- +$link-color: darken($white, 11%); +$link-color-disabled: darken($link-color, 30%); +$link-hover-color: $white; +$external-link-color: $blue; + +// Typography +// ------------------------- +$headings-color: darken($white, 11%); +$abbr-border-color: $gray-3 !default; +$text-muted: $text-color-weak; + +$hr-border-color: $dark-4; + +// Panel +// ------------------------- +$panel-bg: #212124; +$panel-border: solid 1px $dark-1; +$panel-header-hover-bg: $dark-4; +$panel-corner: $panel-bg; + +// page header +$page-header-bg: linear-gradient(90deg, #292a2d, black); +$page-header-shadow: inset 0px -4px 14px $dark-2; +$page-header-border-color: $dark-4; + +$divider-border-color: $gray-1; + +// Graphite Target Editor +$tight-form-bg: $dark-3; +$tight-form-func-bg: $dark-4; +$tight-form-func-highlight-bg: $dark-5; + +$modal-backdrop-bg: #353c42; +$code-tag-bg: $dark-1; +$code-tag-border: $dark-4; + +// cards +$card-background: linear-gradient(135deg, #2f2f32, #262628); +$card-background-hover: linear-gradient(135deg, #343436, #262628); +$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3); + +// Lists +$list-item-bg: $card-background; +$list-item-hover-bg: lighten($gray-blue, 2%); +$list-item-link-color: $text-color; +$list-item-shadow: $card-shadow; + +$empty-list-cta-bg: $gray-blue; + +// Scrollbars +$scrollbarBackground: #404357; +$scrollbarBackground2: #3a3a3a; +$scrollbarBorder: black; + +// Tables +// ------------------------- +$table-bg: transparent; // overall background-color +$table-bg-accent: $dark-3; // for striping +$table-border: $dark-3; // table and cell border + +$table-bg-odd: $dark-2; +$table-bg-hover: $dark-3; + +// Buttons +// ------------------------- +$btn-primary-bg: #ff6600; +$btn-primary-bg-hl: #bc3e06; + +$btn-secondary-bg-hl: lighten($blue-dark, 5%); +$btn-secondary-bg: $blue-dark; + +$btn-success-bg: $green; +$btn-success-bg-hl: darken($green, 6%); + +$btn-warning-bg: $brand-warning; +$btn-warning-bg-hl: lighten($brand-warning, 8%); + +$btn-danger-bg: $red; +$btn-danger-bg-hl: darken($red, 8%); + +$btn-inverse-bg: $dark-3; +$btn-inverse-bg-hl: lighten($dark-3, 4%); +$btn-inverse-text-color: $link-color; +$btn-inverse-text-shadow: 0px 1px 0 rgba(0, 0, 0, 0.1); + +$btn-link-color: $gray-3; + +$iconContainerBackground: $black; + +$btn-divider-left: $dark-4; +$btn-divider-right: $dark-2; + +$btn-drag-image: '../img/grab_dark.svg'; + +// Forms +// ------------------------- +$input-bg: $input-black; +$input-bg-disabled: $dark-3; + +$input-color: $gray-4; +$input-border-color: $dark-3; +$input-box-shadow: inset 1px 0px 0.3rem 0px rgba(150, 150, 150, 0.1); +$input-border-focus: $input-border-color; +$input-box-shadow-focus: rgba(102, 175, 233, 0.6); +$input-color-placeholder: $gray-1 !default; +$input-label-bg: $gray-blue; +$input-label-border-color: $dark-3; +$input-color-select-arrow: $white; + +// Input placeholder text color +$placeholderText: darken($text-color, 25%); + +// Search +$search-shadow: 0 0 30px 0 $black; +$search-filter-box-bg: $gray-blue; + +// Typeahead +$typeahead-shadow: 0 5px 10px 0 $black; +$typeahead-selected-bg: $dark-4; +$typeahead-selected-color: $yellow; + +// Dropdowns +// ------------------------- +$dropdownBackground: $dark-3; +$dropdownBorder: rgba(0, 0, 0, 0.2); +$dropdownDividerTop: transparent; +$dropdownDividerBottom: #444; + +$dropdownLinkColor: $text-color; +$dropdownLinkColorHover: $white; +$dropdownLinkColorActive: $white; + +$dropdownLinkBackgroundHover: $dark-4; + +// Horizontal forms & lists +// ------------------------- +$horizontalComponentOffset: 180px; + +// Navbar +// ------------------------- +$navbarHeight: 55px; + +$navbarBackground: $panel-bg; +$navbarBorder: 1px solid $dark-3; +$navbarShadow: 0 0 20px black; + +$navbarLinkColor: $gray-4; + +$navbarButtonBackground: $navbarBackground; +$navbarButtonBackgroundHighlight: $body-bg; + +$navbar-button-border: #2f2f32; + +// Sidemenu +// ------------------------- +$side-menu-bg: $black; +$side-menu-bg-mobile: $side-menu-bg; +$side-menu-item-hover-bg: $dark-2; +$side-menu-shadow: 0 0 20px black; +$side-menu-link-color: $link-color; + +// Menu dropdowns +// ------------------------- +$menu-dropdown-bg: $body-bg; +$menu-dropdown-hover-bg: $dark-2; +$menu-dropdown-shadow: 5px 5px 20px -5px $black; + +// Tabs +// ------------------------- +$tab-border-color: $dark-4; + +// Toolbar +$toolbar-bg: $input-black; + +// Form states and alerts +// ------------------------- +$warning-text-color: $warn; +$error-text-color: #e84d4d; +$success-text-color: #12d95a; +$info-text-color: $blue-dark; + +$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d); +$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274); +$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d); +$alert-info-bg: linear-gradient(100deg, #1a4552, #00374a); + +// popover +$popover-bg: $page-bg; +$popover-color: $text-color; +$popover-border-color: $dark-4; +$popover-shadow: 0 0 20px black; + +$popover-help-bg: $btn-secondary-bg; +$popover-help-color: $text-color; + +$popover-error-bg: $btn-danger-bg; + +// Tooltips and popovers +// ------------------------- +$tooltipColor: $popover-help-color; +$tooltipArrowWidth: 5px; +$tooltipLinkColor: $link-color; +$graph-tooltip-bg: $dark-1; + +$tooltipBackground: $black; +$tooltipColor: $gray-4; +$tooltipArrowColor: $tooltipBackground; +$tooltipBackgroundError: $brand-danger; + +// images +$checkboxImageUrl: '../img/checkbox.png'; + +// info box +$info-box-border-color: darken($blue, 12%); + +// footer +$footer-link-color: $gray-2; +$footer-link-hover: $gray-4; + +// json-explorer +$json-explorer-default-color: $text-color; +$json-explorer-string-color: #23d662; +$json-explorer-number-color: $variable; +$json-explorer-boolean-color: $variable; +$json-explorer-null-color: #eec97d; +$json-explorer-undefined-color: rgb(239, 143, 190); +$json-explorer-function-color: #fd48cb; +$json-explorer-rotate-time: 100ms; +$json-explorer-toggler-opacity: 0.6; +$json-explorer-bracket-color: #9494ff; +$json-explorer-key-color: #23a0db; +$json-explorer-url-color: #027bff; + +// Changelog and diff +// ------------------------- +$diff-label-bg: $dark-2; +$diff-label-fg: $white; + +$diff-group-bg: $dark-4; +$diff-arrow-color: $white; + +$diff-json-bg: $dark-4; +$diff-json-fg: $gray-5; + +$diff-json-added: #457740; +$diff-json-deleted: #a04338; + +$diff-json-old: #a04338; +$diff-json-new: #457740; + +$diff-json-changed-fg: $gray-5; +$diff-json-changed-num: $text-color; + +$diff-json-icon: $gray-7; + +//Submenu +$variable-option-bg: $blue-dark; + +//Switch Slider +// ------------------------- +$switch-bg: $input-bg; +$switch-slider-color: $dark-2; +$switch-slider-off-bg: $gray-1; +$switch-slider-on-bg: linear-gradient(90deg, $orange, $red); +$switch-slider-shadow: 0 0 3px black; + +//Checkbox +// ------------------------- +$checkbox-bg: $dark-1; +$checkbox-border: 1px solid $gray-1; +$checkbox-checked-bg: linear-gradient(0deg, $orange, $red); +$checkbox-color: $dark-1; + +//Panel Edit +// ------------------------- +$panel-editor-shadow: 0 0 20px black; +$panel-editor-side-menu-shadow: drop-shadow(0 0 10px $black); +$panel-editor-viz-item-shadow: 0 0 8px $dark-5; +$panel-editor-viz-item-border: 1px solid $dark-5; +$panel-editor-viz-item-shadow-hover: 0 0 4px $blue; +$panel-editor-viz-item-border-hover: 1px solid $blue; +$panel-editor-viz-item-bg: $input-black; +$panel-editor-tabs-line-color: #e3e3e3; +$panel-editor-viz-item-bg-hover: darken($blue, 47%); + +$panel-options-group-border: none; +$panel-options-group-header-bg: $gray-blue; + +$panel-grid-placeholder-bg: darken($blue, 47%); +$panel-grid-placeholder-shadow: 0 0 4px $blue; + +// logs +$logs-color-unkown: $gray-2; + +// toggle-group +$button-toggle-group-btn-active-bg: linear-gradient(90deg, $orange, $red); +$button-toggle-group-btn-active-shadow: inset 0 0 4px $black; +$button-toggle-group-btn-seperator-border: 1px solid $page-bg; + +$vertical-resize-handle-bg: $dark-5; +$vertical-resize-handle-dots: $gray-1; $vertical-resize-handle-dots-hover: $gray-2; diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index 97d7a374765..57ab0d9e12a 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -3,6 +3,21 @@ $theme-name: light; +// New Colors +// ------------------------- +$sapphire-faint: #f5f9ff; +$sapphire-light: #a8caff; +$sapphire-base: #3274d9; +$sapphire-shade: #1f60c4; +$lobster-base: #e02f44; +$lobster-shade: #c4162a; +$green-base: #37872d; +$green-shade: #19730e; +$green-base: #3eb15b; +$green-shade: #369b4f; +$purple-shade: #8f3bb8; +$yellow-base: #f2cc0c; + // Grays // ------------------------- $black: #000; @@ -25,28 +40,28 @@ $white: #fff; $blue: #0083b3; $blue-light: #00a8e6; $green: #3aa655; -$red: #d44939; +$red: $lobster-base; $yellow: #ff851b; $orange: #ff7941; $purple: #9954bb; -$variable: $blue; +$variable: $purple-shade; $brand-primary: $orange; $brand-success: $green; $brand-warning: $orange; -$brand-danger: $red; +$brand-danger: $lobster-base; -$query-red: $red; +$query-red: $lobster-base; $query-green: $green; $query-purple: $purple; $query-orange: $orange; -$query-keyword: $blue; +$query-keyword: $sapphire-base; // Status colors // ------------------------- -$online: #01a64f; +$online: $green-shade; $warn: #f79520; -$critical: #ec2128; +$critical: $lobster-shade; // Scaffolding // ------------------------- @@ -61,15 +76,9 @@ $text-color-faint: $gray-4; $text-color-emphasis: $dark-5; $text-shadow-faint: none; -$textShadow: none; // gradients -$brand-gradient: linear-gradient( - to right, - rgba(255, 213, 0, 1) 0%, - rgba(255, 68, 0, 1) 99%, - rgba(255, 68, 0, 1) 100% -); +$brand-gradient: linear-gradient(to right, rgba(255, 213, 0, 1) 0%, rgba(255, 68, 0, 1) 99%, rgba(255, 68, 0, 1) 100%); $page-gradient: linear-gradient(180deg, $white 10px, $gray-7 100px); $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); @@ -79,7 +88,7 @@ $edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%); $link-color: $gray-1; $link-color-disabled: lighten($link-color, 30%); $link-hover-color: darken($link-color, 20%); -$external-link-color: $blue-light; +$external-link-color: $sapphire-shade; // Typography // ------------------------- @@ -141,20 +150,17 @@ $table-bg-hover: $gray-5; // Buttons // ------------------------- -$btn-primary-bg: $brand-primary; -$btn-primary-bg-hl: lighten($brand-primary, 8%); +$btn-primary-bg: $green-base; +$btn-primary-bg-hl: $green-shade; -$btn-secondary-bg: $blue; -$btn-secondary-bg-hl: lighten($blue, 4%); +$btn-secondary-bg: $sapphire-base; +$btn-secondary-bg-hl: $sapphire-shade; -$btn-success-bg: lighten($green, 3%); -$btn-success-bg-hl: darken($green, 3%); +$btn-success-bg: $green-base; +$btn-success-bg-hl: $green-shade; -$btn-warning-bg: lighten($orange, 3%); -$btn-warning-bg-hl: darken($orange, 3%); - -$btn-danger-bg: lighten($red, 3%); -$btn-danger-bg-hl: darken($red, 3%); +$btn-danger-bg: $lobster-base; +$btn-danger-bg-hl: $lobster-shade; $btn-inverse-bg: $gray-6; $btn-inverse-bg-hl: darken($gray-6, 5%); @@ -178,8 +184,8 @@ $input-bg-disabled: $gray-5; $input-color: $dark-3; $input-border-color: $gray-5; $input-box-shadow: none; -$input-border-focus: $blue !default; -$input-box-shadow-focus: $blue !default; +$input-border-focus: $sapphire-light !default; +$input-box-shadow-focus: $sapphire-light !default; $input-color-placeholder: $gray-4 !default; $input-label-bg: $gray-5; $input-label-border-color: $gray-5; @@ -253,14 +259,13 @@ $toolbar-bg: white; // Form states and alerts // ------------------------- $warning-text-color: lighten($orange, 10%); -$error-text-color: lighten($red, 10%); +$error-text-color: $lobster-shade; $success-text-color: lighten($green, 10%); -$info-text-color: $blue; -$alert-error-bg: linear-gradient(90deg, #d44939, #e04d3d); -$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274); -$alert-warning-bg: linear-gradient(90deg, #d44939, #e04d3d); -$alert-info-bg: $blue; +$alert-error-bg: linear-gradient(90deg, $lobster-base, $lobster-shade); +$alert-success-bg: linear-gradient(90deg, $green-base, $green-shade); +$alert-warning-bg: linear-gradient(90deg, $lobster-base, $lobster-shade); +$alert-info-bg: $sapphire-base; // popover $popover-bg: $page-bg; @@ -268,7 +273,7 @@ $popover-color: $text-color; $popover-border-color: $gray-5; $popover-shadow: 0 0 20px $white; -$popover-help-bg: $blue; +$popover-help-bg: $sapphire-base; $popover-help-color: $gray-6; $popover-error-bg: $btn-danger-bg; @@ -289,7 +294,7 @@ $tooltipBackgroundError: $brand-danger; $checkboxImageUrl: '../img/checkbox_white.png'; // info box -$info-box-border-color: lighten($blue, 20%); +$info-box-border-color: $sapphire-base; // footer $footer-link-color: $gray-3; @@ -298,16 +303,16 @@ $footer-link-hover: $dark-5; // json explorer $json-explorer-default-color: black; $json-explorer-string-color: green; -$json-explorer-number-color: blue; -$json-explorer-boolean-color: red; +$json-explorer-number-color: $sapphire-base; +$json-explorer-boolean-color: $lobster-base; $json-explorer-null-color: #855a00; $json-explorer-undefined-color: rgb(202, 11, 105); $json-explorer-function-color: #ff20ed; $json-explorer-rotate-time: 100ms; $json-explorer-toggler-opacity: 0.6; -$json-explorer-bracket-color: blue; +$json-explorer-bracket-color: $sapphire-base; $json-explorer-key-color: #00008b; -$json-explorer-url-color: blue; +$json-explorer-url-color: $sapphire-base; // Changelog and diff // ------------------------- @@ -318,35 +323,35 @@ $diff-arrow-color: $dark-3; $diff-group-bg: $gray-7; $diff-json-bg: $gray-5; -$diff-json-fg: $gray-2; +$diff-json-fg: $gray-1; -$diff-json-added: lighten(desaturate($green, 30%), 10%); -$diff-json-deleted: desaturate($red, 35%); +$diff-json-added: $sapphire-shade; +$diff-json-deleted: $lobster-shade; $diff-json-old: #5a372a; $diff-json-new: #664e33; -$diff-json-changed-fg: $gray-6; +$diff-json-changed-fg: $gray-7; $diff-json-changed-num: $gray-4; $diff-json-icon: $gray-4; //Submenu -$variable-option-bg: $blue-light; +$variable-option-bg: $dropdownLinkBackgroundHover; //Switch Slider // ------------------------- $switch-bg: $white; $switch-slider-color: $gray-7; $switch-slider-off-bg: $gray-5; -$switch-slider-on-bg: linear-gradient(90deg, $yellow, $red); +$switch-slider-on-bg: linear-gradient(90deg, #ff9830, #e55400); $switch-slider-shadow: 0 0 3px $dark-5; //Checkbox // ------------------------- $checkbox-bg: $gray-6; $checkbox-border: 1px solid $gray-3; -$checkbox-checked-bg: linear-gradient(0deg, $yellow, $red); +$checkbox-checked-bg: linear-gradient(0deg, #ff9830, #e55400); $checkbox-color: $gray-7; //Panel Edit @@ -360,12 +365,11 @@ $panel-editor-viz-item-border-hover: 1px solid $blue-light; $panel-editor-viz-item-bg: $white; $panel-editor-tabs-line-color: $dark-5; $panel-editor-viz-item-bg-hover: lighten($blue, 62%); - $panel-options-group-border: none; $panel-options-group-header-bg: $gray-5; -$panel-grid-placeholder-bg: lighten($blue, 62%); -$panel-grid-placeholder-shadow: 0 0 4px $blue-light; +$panel-grid-placeholder-bg: $sapphire-faint; +$panel-grid-placeholder-shadow: 0 0 4px $sapphire-light; // logs $logs-color-unkown: $gray-5; diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss index 1a005b0d511..9919a370a87 100644 --- a/public/sass/base/_type.scss +++ b/public/sass/base/_type.scss @@ -59,14 +59,6 @@ a.text-error:focus { color: darken($error-text-color, 10%); } -.text-info { - color: $info-text-color; -} -a.text-info:hover, -a.text-info:focus { - color: darken($info-text-color, 10%); -} - .text-success { color: $success-text-color; } diff --git a/public/sass/components/_alerts.scss b/public/sass/components/_alerts.scss index 710c4d1ec0f..1c4f1b7fcb7 100644 --- a/public/sass/components/_alerts.scss +++ b/public/sass/components/_alerts.scss @@ -6,7 +6,7 @@ // ------------------------- .alert { - padding: 1.25rem 2rem 1.25rem 1.5rem; + padding: 15px 20px; margin-bottom: $panel-margin / 2; text-shadow: 0 2px 0 rgba(255, 255, 255, 0.5); background: $alert-error-bg; @@ -16,6 +16,7 @@ border-radius: $border-radius; display: flex; flex-direction: row; + align-items: center; } // Alternate styles @@ -62,7 +63,6 @@ .alert-title { font-weight: $font-weight-semi-bold; - padding-bottom: 2px; } .alert-icon { diff --git a/public/sass/components/_buttons.scss b/public/sass/components/_buttons.scss index 4e032d7b9d1..0c1ac726690 100644 --- a/public/sass/components/_buttons.scss +++ b/public/sass/components/_buttons.scss @@ -83,41 +83,20 @@ // Set the backgrounds // ------------------------- +.btn-success, .btn-primary { @include buttonBackground($btn-primary-bg, $btn-primary-bg-hl); } + .btn-secondary { @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl); } -// Warning appears are orange -.btn-warning { - @include buttonBackground($btn-warning-bg, $btn-warning-bg-hl); -} // Danger and error appear as red .btn-danger { @include buttonBackground($btn-danger-bg, $btn-danger-bg-hl); } -// Success appears as green -.btn-success { - @include buttonBackground($btn-success-bg, $btn-success-bg-hl); - &--processing { - @include button-outline-variant($gray-1); - @include box-shadow(none); - cursor: default; - - &:hover, - &:active, - &:active:hover, - &:focus, - &:disabled { - color: $gray-1; - background-color: transparent; - border-color: $gray-1; - } - } -} // Info appears as a neutral blue .btn-secondary { @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl); @@ -138,20 +117,15 @@ @include button-outline-variant($btn-primary-bg); } .btn-outline-secondary { - @include button-outline-variant($btn-secondary-bg); + @include button-outline-variant($btn-secondary-bg-hl); } .btn-outline-inverse { @include button-outline-variant($btn-inverse-bg); } -.btn-outline-success { - @include button-outline-variant($btn-success-bg); -} -.btn-outline-warning { - @include button-outline-variant($btn-warning-bg); -} .btn-outline-danger { - @include button-outline-variant($btn-danger-bg); + @include button-outline-variant(green); } + .btn-outline-disabled { @include button-outline-variant($gray-1); @include box-shadow(none); diff --git a/public/sass/components/_navbar.scss b/public/sass/components/_navbar.scss index 0cfa314a985..a915d003eab 100644 --- a/public/sass/components/_navbar.scss +++ b/public/sass/components/_navbar.scss @@ -1,6 +1,6 @@ .navbar { position: relative; - padding-left: 40px; + padding-left: 20px; z-index: $zindex-navbar-fixed; height: $navbarHeight; padding-right: 20px; @@ -41,16 +41,12 @@ .panel-in-fullscreen { .navbar { - padding-left: 15px; + padding-left: 20px; } .navbar-button--add-panel, .navbar-button--star, .navbar-button--tv, - .navbar-page-btn .fa-caret-down { - display: none; - } - .navbar-buttons--close { display: flex; } @@ -120,7 +116,8 @@ .navbar-button { @include buttonBackground($btn-inverse-bg, $btn-inverse-bg-hl, $btn-inverse-text-color, $btn-inverse-text-shadow); - display: inline-block; + display: flex; + align-items: center; font-weight: $btn-font-weight; padding: 6px 11px; line-height: 16px; @@ -157,7 +154,7 @@ } &--primary { - @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl); + @include buttonBackground($btn-primary-bg, $btn-primary-bg-hl); } } @@ -179,3 +176,31 @@ } } } + +.navbar-edit { + display: flex; + height: $navbarHeight; + align-items: center; + padding-left: 7px; +} + +.navbar-edit__back-btn { + background: transparent; + border: 2px solid $text-color; + border-radius: 50%; + width: 34px; + height: 34px; + transition: transform 0.1s ease 0.1s; + color: $text-color; + + i { + font-size: $font-size-lg; + position: relative; + top: 2px; + } + + &:hover { + color: $text-color-strong; + border-color: $text-color-strong; + } +} diff --git a/public/sass/components/_panel_editor.scss b/public/sass/components/_panel_editor.scss index 1de136c09f1..78e21da00ad 100644 --- a/public/sass/components/_panel_editor.scss +++ b/public/sass/components/_panel_editor.scss @@ -86,6 +86,10 @@ .panel-editor-container__panel { margin: 0 $dashboard-padding; } + + .search-container { + left: 0 !important; + } } .panel-editor-container__resizer { @@ -150,7 +154,6 @@ box-shadow: $panel-editor-viz-item-shadow-hover; background: $panel-editor-viz-item-bg-hover; border: $panel-editor-viz-item-border-hover; - } &--current { @@ -232,4 +235,3 @@ position: relative; min-width: 200px; } - diff --git a/public/sass/components/_panel_gettingstarted.scss b/public/sass/components/_panel_gettingstarted.scss index 5bbc4ba29ca..b51bd3a9ef9 100644 --- a/public/sass/components/_panel_gettingstarted.scss +++ b/public/sass/components/_panel_gettingstarted.scss @@ -118,7 +118,7 @@ $path-position: $marker-size-half - ($path-height / 2); .progress-step-cta { @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius); - @include buttonBackground($btn-success-bg, $btn-success-bg-hl); + @include buttonBackground($btn-primary-bg, $btn-primary-bg-hl); display: none; } diff --git a/public/sass/components/_query_editor.scss b/public/sass/components/_query_editor.scss index b57e575dc5b..71332541b2e 100644 --- a/public/sass/components/_query_editor.scss +++ b/public/sass/components/_query_editor.scss @@ -124,10 +124,9 @@ input[type='text'].tight-form-func-param { &--disabled { .query-keyword { - color: darken($blue, 20%); + color: $text-color-weak; } } - } .query-editor-row__header { @@ -138,7 +137,6 @@ input[type='text'].tight-form-func-param { background: $page-bg; flex-wrap: nowrap; align-items: center; - } .query-editor-row__ref-id { font-weight: $font-weight-semi-bold; @@ -206,4 +204,3 @@ input[type='text'].tight-form-func-param { color: $text-muted; padding-left: 10px; } - diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index daad8fd10da..eba03283510 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -21,9 +21,9 @@ // Search .search-field-wrapper { width: 100%; + height: $navbarHeight; display: flex; background-color: $navbarBackground; - box-shadow: $navbarShadow; position: relative; & > input { diff --git a/public/sass/components/_timepicker.scss b/public/sass/components/_timepicker.scss index 6f075c4d92e..d25d85b3d74 100644 --- a/public/sass/components/_timepicker.scss +++ b/public/sass/components/_timepicker.scss @@ -10,6 +10,10 @@ .gf-timepicker-nav-btn { text-overflow: ellipsis; overflow: hidden; + + .fa-clock-o { + margin-right: 4px; + } } .gf-timepicker-dropdown { @@ -48,7 +52,6 @@ } .gf-timepicker-utc { - background-color: $tight-form-func-bg; color: $orange; font-size: 75%; padding: 3px; diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index 1570c7dd9b7..b8307186540 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -143,7 +143,7 @@ export function DashboardViewStateStub(this: any) { } export function TimeSrvStub(this: any) { - this.init = sinon.spy(); + this.init = () => {}; this.time = { from: 'now-1h', to: 'now' }; this.timeRange = function(parse) { if (parse === false) { diff --git a/public/vendor/angular-ui/ui-bootstrap-tpls.js b/public/vendor/angular-ui/ui-bootstrap-tpls.js index 87120b66ce1..ad6f3b4b4bc 100644 --- a/public/vendor/angular-ui/ui-bootstrap-tpls.js +++ b/public/vendor/angular-ui/ui-bootstrap-tpls.js @@ -1245,7 +1245,7 @@ angular.module("template/datepicker/popup.html", []).run(["$templateCache", func " \n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + "\n" + ""); diff --git a/public/vendor/flot/jquery.flot.gauge.js b/public/vendor/flot/jquery.flot.gauge.js index d256a5db7ed..b6468d5824f 100644 --- a/public/vendor/flot/jquery.flot.gauge.js +++ b/public/vendor/flot/jquery.flot.gauge.js @@ -935,16 +935,7 @@ } }, values: [ - { - value: 50, - color: "lightgreen" - }, { - value: 80, - color: "yellow" - }, { - value: 100, - color: "red" - } + ] } } diff --git a/yarn.lock b/yarn.lock index f14b7ae3ee1..cedf13bd558 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,11 +50,11 @@ source-map "^0.5.0" "@babel/generator@^7.0.0", "@babel/generator@^7.2.2": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.0.tgz#f663838cd7b542366de3aa608a657b8ccb2a99eb" - integrity sha512-dZTwMvTgWfhmibq4V9X+LMf6Bgl7zAodRn9PvcPdhlzFMbvUutx74dbEv7Atz3ToeEpevYEJtAwfxq/bDCzHWg== + version "7.3.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.2.tgz#fff31a7b2f2f3dad23ef8e01be45b0d5c2fc0132" + integrity sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ== dependencies: - "@babel/types" "^7.3.0" + "@babel/types" "^7.3.2" jsesc "^2.5.1" lodash "^4.17.10" source-map "^0.5.0" @@ -93,9 +93,9 @@ "@babel/types" "^7.0.0" "@babel/helper-create-class-features-plugin@^7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.0.tgz#2b01a81b3adc2b1287f9ee193688ef8dc71e718f" - integrity sha512-DUsQNS2CGLZZ7I3W3fvh0YpPDd6BuWJlDl+qmZZpABZHza2ErE3LxtEzLJFHFC1ZwtlAXvHhbFYbtM5o5B0WBw== + 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== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/helper-member-expression-to-functions" "^7.0.0" @@ -253,9 +253,9 @@ 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.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.1.tgz#8f4ffd45f779e6132780835ffa7a215fa0b2d181" - integrity sha512-ATz6yX/L8LEnC3dtLQnIx4ydcPxhLcoy9Vl6re00zb2w5lG6itY6Vhnr1KFRPq/FHNsgl/gh2mjNN20f9iJTTA== + version "7.3.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.2.tgz#95cdeddfc3992a6ca2a1315191c1679ca32c55cd" + integrity sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ== "@babel/plugin-proposal-async-generator-functions@^7.1.0", "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" @@ -313,9 +313,9 @@ "@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.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.1.tgz#f69fb6a1ea6a4e1c503994a91d9cf76f3c4b36e8" - integrity sha512-Nmmv1+3LqxJu/V5jU9vJmxR/KIRWFk2qLHmbB56yRRRFhlaSuOVXscX3gUmhaKgUhzA3otOHVubbIEVYsZ0eZg== + 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== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" @@ -488,9 +488,9 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-destructuring@^7.0.0", "@babel/plugin-transform-destructuring@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.2.0.tgz#e75269b4b7889ec3a332cd0d0c8cff8fed0dc6f3" - integrity sha512-coVO2Ayv7g0qdDbrNiadE4bU7lvCd9H539m2gMknyVjjMdwF/iCOM7R+E8PkntoqLkltO0rk+3axhpp/0v68VQ== + version "7.3.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.3.2.tgz#f2f5520be055ba1c38c41c0e094d8a461dd78f2d" + integrity sha512-Lrj/u53Ufqxl/sGxyjsJ2XNtNuEjDyjpqdhMNh5aZ+XFOdThL46KBj27Uem4ggoezSYBxKWAil6Hu8HtwqesYw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -730,9 +730,9 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-typescript@^7.1.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.2.0.tgz#bce7c06300434de6a860ae8acf6a442ef74a99d1" - integrity sha512-EnI7i2/gJ7ZNr2MuyvN2Hu+BHJENlxWte5XygPvfj/MbvtOkWor9zcnHpMMQL2YYaaCcqtIvJUyJ7QVfoGs7ew== + 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== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-typescript" "^7.2.0" @@ -915,10 +915,10 @@ globals "^11.1.0" lodash "^4.17.10" -"@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": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.0.tgz#61dc0b336a93badc02bf5f69c4cd8e1353f2ffc0" - integrity sha512-QkFPw68QqWU1/RVPyBe8SO7lXbPfjtqAxRYQKpFpaB8yMq7X2qAqfwK5LKoQufEkSmO5NQ70O6Kc3Afk03RwXw== +"@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== dependencies: esutils "^2.0.2" lodash "^4.17.10" @@ -1040,6 +1040,20 @@ 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" @@ -1506,9 +1520,9 @@ integrity sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg== "@types/d3-array@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.4.tgz#7088445c8717ba1fba416a1df7bbd11cc72a3763" - integrity sha512-3r1fOAAb+SGfcOGXty/LGvoP0ovMec4UtGNUyHOSzYyvSGpmt+eNMxLowol/3HryusevznSfcHZebEShXMwsZA== + 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== "@types/d3-axis@*": version "1.0.11" @@ -1562,9 +1576,9 @@ integrity sha1-k6MBhovp4VBh89RDQ7GrP4rLbwk= "@types/d3-force@*": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-1.1.1.tgz#185c18b77932df63457894bd36d0d6e9692546c0" - integrity sha512-ePkELuaFWY4yOuf+Bvx5Xd+ihFiYG4bdnW0BlvigovIm8Sob2t76e9RGO6lybQbv6AlW9Icn9HuZ9fmdzEoJyg== + 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== "@types/d3-format@*": version "1.3.0" @@ -1635,9 +1649,9 @@ integrity sha512-WQ6Ivy7VuUlZ/Grqc8493ZxC+y/fpvZLy5+8ELvmCr2hll8eJPUqC05l6fgRRA7kjqlpbH7lbmvY6pRKf6yzxw== "@types/d3-shape@*": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.2.8.tgz#7750d34b8f817d543d62eaeb8d38b77886211b5c" - integrity sha512-eHAi4Nuw1/69hjBFNXNWYifcNTFhwy360PI969ssMX22Si9henYiNKLeJoBhNfyXFajeFjI1HGsYzyCWKOozdA== + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.0.tgz#ea195c543a1f6d3e250ceb07a7d208d80ee37b01" + integrity sha512-ERWJ8bNZjkzfWfPAlkN3XCqYOOsWTnqTX0jX2Bx+WLd2AfEl97WXr2igwssFc91MadrZLw7HNS/JTUZPQL5sZQ== dependencies: "@types/d3-path" "*" @@ -1713,9 +1727,9 @@ "@types/d3-zoom" "*" "@types/enzyme@^3.1.13": - version "3.1.15" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.15.tgz#fc9a9695ba9f90cd50c4967e64a8c66ec96913d1" - integrity sha512-6b4JWgV+FNec1c4+8HauGbXg5gRc1oQK93t2+4W+bHjG/PzO+iPvagY6d6bXAZ+t+ps51Zb2F9LQ4vl0S0Epog== + version "3.1.17" + resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.17.tgz#41f553bfdbaa00880488feabb3ade47d5489db42" + integrity sha512-pZ+Blk1hODkprPZ9cxXd8njxdBnbLGWOKAmKk0QhpJvWzI4q4F20FHHUnkZXPXJt5WnK6SbbY5lfTKoz1M/CTw== dependencies: "@types/cheerio" "*" "@types/react" "*" @@ -1731,9 +1745,9 @@ integrity sha512-rLlMXpd3rdlrp0+xsrda/hFfOpIxgqFcRpk005UKbHtcdFK+QXAjhBAPnvO58qF4O1LdDXrcaiJxMgstCIlcaw== "@types/jest@^23.3.2": - version "23.3.13" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.13.tgz#c81484b6f4ca007bb09887ed15ecb3286d58f928" - integrity sha512-ePl4l+7dLLmCucIwgQHAgjiepY++qcI6nb8eAwGNkB6OxmTe3Z9rQU3rSpomqu42PCCnlThZbOoxsf+qylJsLA== + version "23.3.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.14.tgz#37daaf78069e7948520474c87b80092ea912520a" + integrity sha512-Q5hTcfdudEL2yOmluA1zaSyPbzWPmJ3XfSWeP3RyoYvS9hnje1ZyagrZOuQ6+1nQC1Gw+7gap3pLNL3xL6UBug== "@types/jquery@^1.10.35": version "1.10.35" @@ -1745,15 +1759,15 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.120.tgz#cf265d06f6c7a710db087ed07523ab8c1a24047b" integrity sha512-jQ21kQ120mo+IrDs1nFNVm/AsdFxIx2+vZ347DbogHJPd/JzKNMOqU6HCYin1W6v8l5R9XSO2/e9cxmn7HAnVw== -"@types/node@*", "@types/node@^10.12.18": - version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" - integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== +"@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@^10.12.24": - version "10.12.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" - integrity sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ== +"@types/node@^10.12.18", "@types/node@^10.12.24": + version "10.12.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.25.tgz#0d01a7dd6127de60d861ece4a650963042abb538" + integrity sha512-IcvnGLGSQFDvC07Bz2I8SX+QKErDZbUdiQq7S2u3XyzTyJfUmT0sWJMbeQkMzpTAkO7/N7sZpW/arUM2jfKsbQ== "@types/prop-types@*": version "15.5.8" @@ -1780,16 +1794,16 @@ "@types/react" "*" "@types/react-dom@*", "@types/react-dom@^16.0.9": - version "16.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.11.tgz#bd10ccb0d9260343f4b9a49d4f7a8330a5c1f081" - integrity sha512-x6zUx9/42B5Kl2Vl9HlopV8JF64wLpX3c+Pst9kc1HgzrsH+mkehe/zmHMQTplIrR48H2gpU7ZqurQolYu8XBA== + 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== dependencies: "@types/react" "*" "@types/react-grid-layout@^0.16.6": - version "0.16.6" - resolved "https://registry.yarnpkg.com/@types/react-grid-layout/-/react-grid-layout-0.16.6.tgz#9149efe128e05d59c54063c7781d18b8febe112c" - integrity sha512-Jp0VfCHJE4uxekPBPpRkADKOjoSHssF2ba1ZMMAfCEqkoSkE+K+3bhI39++fbd7MqGySaqADVHeOoxlBnA3p5g== + version "0.16.7" + resolved "https://registry.yarnpkg.com/@types/react-grid-layout/-/react-grid-layout-0.16.7.tgz#53d5f5034deb0c60e25a0fa578141e9a0982011f" + integrity sha512-A3tW9xySd03KGONkp8gP4+QRLuT1Mcx++m0hO0nZIM4H/Qwz8GsiDv+9okbmHk5HcsHwY5Jdsn6Cv50hwoNG+A== dependencies: "@types/react" "*" @@ -1803,9 +1817,9 @@ "@types/react-transition-group" "*" "@types/react-test-renderer@^16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.0.3.tgz#cce5c983d66cc5c3582e7c2f44b274ab635a8acc" - integrity sha512-NWOAxVQeJxpXuNKgw83Hah0nquiw1nUexM9qY/Hk3a+XhZwgMtaa6GLA9E1TKMT75Odb3/KE/jiBO4enTuEJjQ== + 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== dependencies: "@types/react" "*" @@ -1832,17 +1846,25 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/reselect@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/reselect/-/reselect-2.2.0.tgz#c667206cfdc38190e1d379babe08865b2288575f" + integrity sha1-xmcgbP3DgZDh03m6vgiGWyKIV18= + dependencies: + reselect "*" + "@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== "@types/storybook__addon-info@^3.4.2": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@types/storybook__addon-info/-/storybook__addon-info-3.4.2.tgz#31a27e66867f02e593579cc58f54d22e726b0925" - integrity sha512-1af9u42JAMbi54MOkfzZt2+RTbUnpVJCN6vkkcAEtlyML4tXpmW482d3TZNiYPk3ePHKkielNZUtfpijT/XJ+A== + 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== dependencies: "@types/react" "*" + "@types/storybook__react" "*" "@types/storybook__addon-knobs@^4.0.0": version "4.0.0" @@ -1912,9 +1934,9 @@ "@types/vfile-message" "*" "@types/webpack-env@*": - version "1.13.6" - resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" - integrity sha512-5Th3OsZ4gTRdr9Mho83BQ23cex4sRhOR4XTG+m+cJc0FhtUBK9Vn62hBJ+pnQYnSxoPOsKoAPOx6FcphxBC8ng== + version "1.13.7" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.7.tgz#137a4e57aa31ab57b1baf66f5dc3b6bf085e9944" + integrity sha512-rzi6fw7hhxPcCoNVsgysHFlKnhYYvVj7AJwdAO0HQNP5vg9sY0DoRRC1pfuCQm94cOa1sab82HGUtdFlWHIhBg== "@types/webpack@^3.0.5": version "3.8.17" @@ -2312,9 +2334,9 @@ acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0, acorn@^5.5.3, acorn@^5.6.2: integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== acorn@^6.0.1, acorn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.5.tgz#81730c0815f3f3b34d8efa95cb7430965f4d887a" - integrity sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg== + 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@~2.6.4: version "2.6.4" @@ -2384,9 +2406,9 @@ ajv-keywords@^1.0.0: integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= ajv-keywords@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.3.0.tgz#cb6499da9b83177af8bc1732b2f0a1a1a3aacf8c" - integrity sha512-CMzN9S62ZOO4sA/mJZIO4S++ZM7KFWzH3PPWkveLhy4OZ9i1/VatgwWMD46w/XbGCBy7Ye0gCk+Za6mmyfKK7g== + version "3.4.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" + integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== ajv@^4.7.0: version "4.11.8" @@ -2397,9 +2419,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.7.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96" - integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg== + version "6.9.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1" + integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -2481,12 +2503,12 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: +ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= -ansi-escapes@^3.0.0: +ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -2538,11 +2560,6 @@ ansistyles@~0.1.3: resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539" integrity sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk= -any-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242" - integrity sha1-xnhwBYADV5AJCD9UrAq6+1wz0kI= - any-observable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" @@ -2561,11 +2578,6 @@ app-root-dir@^1.0.2: resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" integrity sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg= -app-root-path@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a" - integrity sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo= - append-transform@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" @@ -2811,6 +2823,11 @@ ast-types@0.10.1: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" integrity sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ== +ast-types@0.11.3: + version "0.11.3" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8" + integrity sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA== + ast-types@0.11.5: version "0.11.5" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.5.tgz#9890825d660c03c28339f315e9fa0a360e31ec28" @@ -2831,7 +2848,7 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -async-each@^1.0.0, async-each@^1.0.1: +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= @@ -2891,15 +2908,15 @@ autoprefixer@^6.3.1, autoprefixer@^6.4.0: postcss-value-parser "^3.2.3" autoprefixer@^9.3.1: - version "9.4.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.6.tgz#0ace275e33b37de16b09a5547dbfe73a98c1d446" - integrity sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw== + version "9.4.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.7.tgz#f997994f9a810eae47b38fa6d8a119772051c4ff" + integrity sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA== dependencies: browserslist "^4.4.1" - caniuse-lite "^1.0.30000929" + caniuse-lite "^1.0.30000932" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.13" + postcss "^7.0.14" postcss-value-parser "^3.3.1" awesome-typescript-loader@^5.2.1: @@ -3254,9 +3271,9 @@ babel-plugin-macros@2.4.2: resolve "^1.8.1" babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.4.2: - version "2.4.5" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.4.5.tgz#7000a9b1f72d19ceee19a5804f1d23d6daf38c13" - integrity sha512-+/9yteNQw3yuZ3krQUfjAeoT/f4EAdn3ELwhFfDj0rTMIaoHfIdrcLePOfIaL0qmFLpIcgPIL2Lzm58h+CGWaw== + version "2.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.5.0.tgz#01f4d3b50ed567a67b80a30b9da066e94f4097b6" + integrity sha512-BWw0lD0kVZAXRD3Od1kMrdmfudqzDzYv2qrN3l2ISR1HVp1EgLKfbOrYV9xmY5k3qx3RIu5uPAUZZZHpo0o5Iw== dependencies: cosmiconfig "^5.0.5" resolve "^1.8.1" @@ -3341,12 +3358,13 @@ babel-plugin-named-asset-import@^0.2.3: integrity sha512-9mx2Z9M4EGbutvXxoLV7aUBCY6ps3sqLFl094FeA2tFQzQffIh0XSsmwwQRxiSfpg3rnb5x/o46qRLxS/OzFTg== babel-plugin-react-docgen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-2.0.0.tgz#039d90f5a1a37131c8cc3015017eecafa8d78882" - integrity sha512-AaA6IPxCF1EkzpFG41GkVh/VGdoBejPF6oIub2K8E6AD3kwnTZ0DIKG7f20a7zmqBEeO8GkFWdM7tYd9Owkc+Q== + version "2.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-2.0.2.tgz#3307e27414c370365710576b7fadbcaf8984d862" + integrity sha512-fFendfUUU2KqqE1ki2NyQoZm4uHPoEWPUgBZiPBiowcPZos+4q+chdQh0nlwY5hxs08AMHSH4Pp98RQL0VFS/g== dependencies: lodash "^4.17.10" - react-docgen "^3.0.0-rc.1" + react-docgen "^3.0.0" + recast "^0.14.7" babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" @@ -3990,7 +4008,7 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -batch-processor@1.0.0: +batch-processor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" integrity sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg= @@ -4038,9 +4056,9 @@ bin-links@^1.1.0, bin-links@^1.1.2: write-file-atomic "^2.3.0" binary-extensions@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" - integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== + version "1.13.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.0.tgz#9523e001306a32444b907423f1de2164222f6ab1" + integrity sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw== binaryextensions@^2.1.2: version "2.1.2" @@ -4155,7 +4173,7 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -braces@^2.3.0, braces@^2.3.1, braces@^2.3.2: +braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -4546,10 +4564,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: - version "1.0.30000932" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000932.tgz#d01763e9ce77810962ca7391ff827b5949ce4272" - integrity sha512-4bghJFItvzz8m0T3lLZbacmEY9X1Z2AtIzTr7s7byqZIOumASfr4ynDx7rtm0J85nDmx8vsgR6vnaSoeU8Oh0A== +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== capture-exit@^1.2.0: version "1.2.0" @@ -4606,7 +4624,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4684,30 +4702,10 @@ child-process-promise@^2.2.1: node-version "^1.0.0" promise-polyfill "^6.0.1" -chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" - integrity sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" - glob-parent "^3.1.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - lodash.debounce "^4.0.8" - normalize-path "^2.1.1" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.5" - optionalDependencies: - fsevents "^1.2.2" - -chokidar@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.0.tgz#5fcb70d0b28ebe0867eb0f09d5f6a08f29a1efa0" - integrity sha512-5t6G2SH8eO6lCvYOoUpaRnF5Qfd//gd7qJAkwRUw9qlGVkiQ13uwQngqbWWaurOsaAm9+kUGbITADxt6H0XFNQ== +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== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -4745,6 +4743,11 @@ ci-info@^1.5.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + cidr-regex@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-1.0.6.tgz#74abfd619df370b9d54ab14475568e97dd64c0c1" @@ -4820,7 +4823,7 @@ cli-columns@^3.1.2: string-width "^2.0.0" strip-ansi "^3.0.1" -cli-cursor@^1.0.1, cli-cursor@^1.0.2: +cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= @@ -4834,11 +4837,6 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= - cli-table2@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/cli-table2/-/cli-table2-0.2.0.tgz#2d1ef7f218a0e786e214540562d4bd177fe32d97" @@ -5112,7 +5110,7 @@ comma-separated-tokens@^1.0.0: dependencies: trim "0.0.1" -commander@2, commander@^2.11.0, commander@^2.12.1, commander@^2.13.0, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0: +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: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== @@ -5330,9 +5328,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.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.3.tgz#4b70938bdffdaf64931e66e2db158f0892289c49" - integrity sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ== + version "2.6.4" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.4.tgz#b8897c062c4d769dd30a0ac5c73976c47f92ea0d" + integrity sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -5349,7 +5347,7 @@ cosmiconfig@^4.0.0: parse-json "^4.0.0" require-from-string "^2.0.1" -cosmiconfig@^5.0.5, cosmiconfig@^5.0.7: +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== @@ -5675,9 +5673,9 @@ csso@~2.3.1: source-map "^0.5.3" cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797" - integrity sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog== + version "0.3.6" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" + integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== cssstyle@^1.0.0: version "1.1.1" @@ -5687,9 +5685,9 @@ cssstyle@^1.0.0: cssom "0.3.x" csstype@^2.2.0, csstype@^2.5.2: - version "2.6.1" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.1.tgz#4cfbf637a577497036ebcd7e32647ef19a0b8076" - integrity sha512-wv7IRqCGsL7WGKB8gPvrl+++HlFM9kxAM6jL1EXNPNTshEJYilMkbfS2SnuHha77uosp/YVK0wAp2jmlBzn1tg== + version "2.6.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01" + integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow== currently-unhandled@^0.4.1: version "0.4.1" @@ -5784,9 +5782,9 @@ d3-drag@1.2.1: d3-selection "1" d3-dsv@1: - version "1.0.10" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.10.tgz#4371c489a2a654a297aca16fcaf605a6f31a6f51" - integrity sha512-vqklfpxmtO2ZER3fq/B33R/BIz3A1PV0FaZRuFM8w6jLo7sUX1BZDh73fPlr0s327rzq4H6EN1q9U+eCBCSN8g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.1.1.tgz#aaa830ecb76c4b5015572c647cc6441e3c7bb701" + integrity sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw== dependencies: commander "2" iconv-lite "0.4" @@ -5873,9 +5871,9 @@ d3-polygon@1.0.3: integrity sha1-FoiOkCZGCTPysXllKtN4Ik04LGI= d3-quadtree@1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.5.tgz#305394840b01f51a341a0da5008585e837fe7e9b" - integrity sha512-U2tjwDFbZ75JRAg8A+cqMvqPg1G3BE7UTJn3h8DHjY/pnsAfWdbJKgyfcy7zKjqGtLAmI0q8aDSeG1TVIKRaHQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.6.tgz#d1ab2a95a7f27bbde88582c94166f6ae35f32056" + integrity sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA== d3-quadtree@1.0.3: version "1.0.3" @@ -5955,9 +5953,9 @@ d3-time-format@2.1.1: d3-time "1" d3-time@1: - version "1.0.10" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.10.tgz#8259dd71288d72eeacfd8de281c4bf5c7393053c" - integrity sha512-hF+NTLCaJHF/JqHN5hE8HVGAXPStEq6/omumPE/SxyHVrR7/qQxusFDo0t0c/44+sCGHthC7yNGFZIEgju0P8g== + version "1.0.11" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.11.tgz#1d831a3e25cd189eb256c17770a666368762bbce" + integrity sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw== d3-time@1.0.8: version "1.0.8" @@ -6122,19 +6120,19 @@ debug@^3.1.0, debug@^3.2.5: dependencies: ms "^2.1.1" -debug@^4.1.0: +debug@^4.0.1, debug@^4.1.0: 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= -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -6595,9 +6593,9 @@ duplexer@^0.1.1: integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= duplexify@^3.4.2, duplexify@^3.6.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.1.tgz#b1a7a29c4abfd639585efaecce80d666b1e34125" - integrity sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA== + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -6641,9 +6639,9 @@ ejs@^2.5.7, ejs@^2.5.9, ejs@^2.6.1: 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.108" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.108.tgz#2e79a6fcaa4b3e7c75abf871505bda8e268c910e" - integrity sha512-/QI4hMpAh48a1Sea6PALGv+kuVne9A2EWGd8HrWHMdYhIzGtbhVVHh6heL5fAzGaDnZuPyrlWJRl8WPm4RyiQQ== + 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== elegant-spinner@^1.0.1: version "1.0.1" @@ -6651,11 +6649,11 @@ elegant-spinner@^1.0.1: integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= element-resize-detector@^1.1.12: - version "1.2.0" - resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.0.tgz#63344fd6f4e5ecff6f018d027e17b281fd4fa338" - integrity sha512-UmhNB8sIJVZeg56gEjgmMd6p37sCg8j8trVW0LZM7Wzv+kxQ5CnRHcgRKBTB/kFUSn3e7UP59kl2V2U8Du1hmg== + version "1.1.15" + resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.1.15.tgz#48eba1a2eaa26969a4c998d972171128c971d8d2" + integrity sha512-16/5avDegXlUxytGgaumhjyQoM6hpp5j3+L79sYq5hlXfTNRy5WMMuTVWkZU3egp/CokCmTmvf18P3KeB57Iog== dependencies: - batch-processor "1.0.0" + batch-processor "^1.0.0" elliptic@^6.0.0: version "6.4.1" @@ -6743,9 +6741,9 @@ envinfo@^5.7.0: integrity sha512-pwdo0/G3CIkQ0y6PCXq4RdkvId2elvtPCJMG0konqlrfkWQbf1DWeH9K2b/cvu2YgGvPPTOnonZxXM1gikFu1w== enzyme-adapter-react-16@^1.5.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.8.0.tgz#7055d8e908d8d27b807cf4292244db3c815ca11d" - integrity sha512-7cVHIKutqnesGeM3CjNFHSvktpypSWBokrBO8wIW+BVx+HGxWCF87W9TpkIIYJqgCtdw9FQGFrAbLg8kSwPRuQ== + 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== dependencies: enzyme-adapter-utils "^1.10.0" function.prototype.name "^1.1.0" @@ -6952,7 +6950,7 @@ escape-html@^1.0.3, escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -7166,19 +7164,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= - dependencies: - cross-spawn "^5.0.1" - 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@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -7333,7 +7318,7 @@ external-editor@^2.1.0: iconv-lite "^0.4.17" tmp "^0.0.33" -external-editor@^3.0.0: +external-editor@^3.0.0, external-editor@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== @@ -7656,17 +7641,22 @@ flatten@^1.0.2: integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= flow-parser@^0.*: - version "0.91.0" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.91.0.tgz#bc42c5bf9d12e3121dad6667b1dcdab09049aee6" - integrity sha512-qvKoEaVmyZhTsRb2Qyp6hxewy7EEm1ahEb/fgrnB0rB6VgBvyLOuKoxpUpGgX1scd7oubShZqi0q56B97pfyOw== + version "0.92.1" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.92.1.tgz#c81fb52b202c7b1195f253bb01ac6dd2f5c15dfc" + integrity sha512-l6rlAGgMTTRYPOj5XUzbCeZB2bsK2cimPcoQD06YtQC2BI2wu9AhMQH+FkKV2Kd1Aa9EMNxVyF05QzNaiYdObQ== flush-write-stream@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" - integrity sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw== + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== dependencies: - inherits "^2.0.1" - readable-stream "^2.0.4" + inherits "^2.0.3" + readable-stream "^2.3.6" + +fn-name@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" + integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc= follow-redirects@^1.0.0, follow-redirects@^1.2.5: version "1.6.1" @@ -7848,7 +7838,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.2.2, fsevents@^1.2.3, fsevents@^1.2.7: +fsevents@^1.2.3, fsevents@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4" integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw== @@ -7881,9 +7871,18 @@ function.prototype.name@^1.1.0: is-callable "^1.1.3" fuse.js@^3.0.1, fuse.js@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.3.0.tgz#1e4fe172a60687230fb54a5cb247eb96e2e7e885" - integrity sha512-ESBRkGLWMuVkapqYCcNO1uqMg5qbCKkgb+VS6wsy17Rix0/cMS9kSOZoYkjH8Ko//pgJ/EEGu0GTjk2mjX2LGQ== + version "3.4.2" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.2.tgz#d7a638c436ecd7b9c4c0051478c09594eb956212" + integrity sha512-WVbrm+cAxPtyMqdtL7cYhR7aZJPhtOfjNClPya8GKMVukKDYs7pEnPINeRVX1C9WmWgU8MdYGYbUPAP2AJXdoQ== + +g-status@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97" + integrity sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA== + dependencies: + arrify "^1.0.1" + matcher "^1.0.0" + simple-git "^1.85.0" gauge@~2.7.3: version "2.7.4" @@ -7959,6 +7958,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -8134,9 +8138,9 @@ global@^4.3.0, global@^4.3.2: process "~0.5.1" globals@^11.1.0: - version "11.10.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.10.0.tgz#1e09776dffda5e01816b3bb4077c8b59c24eaa50" - integrity sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ== + version "11.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.11.0.tgz#dcf93757fa2de5486fbeed7118538adf789e9c2e" + integrity sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw== globals@^9.18.0, globals@^9.2.0: version "9.18.0" @@ -8489,9 +8493,9 @@ handle-thing@^2.0.0: integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== handlebars@^4.0.3: - version "4.0.12" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" - integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA== + version "4.1.0" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" + integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== dependencies: async "^2.5.0" optimist "^0.6.1" @@ -8923,14 +8927,21 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^0.14.3: - version "0.14.3" - resolved "https://registry.yarnpkg.com/husky/-/husky-0.14.3.tgz#c69ed74e2d2779769a17ba8399b54ce0b63c12c3" - integrity sha512-e21wivqHpstpoiWA/Yi8eFti8E+sQDSS53cpJsPptPs295QTOQR0ZwnHo2TXy1XOpZFD9rPOd3NpmqTK6uMLJA== +husky@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/husky/-/husky-1.3.1.tgz#26823e399300388ca2afff11cfa8a86b0033fae0" + integrity sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg== dependencies: - is-ci "^1.0.10" - normalize-path "^1.0.0" - strip-indent "^2.0.0" + cosmiconfig "^5.0.7" + execa "^1.0.0" + find-up "^3.0.0" + get-stdin "^6.0.0" + is-ci "^2.0.0" + pkg-dir "^3.0.0" + please-upgrade-node "^3.1.1" + read-pkg "^4.0.1" + run-node "^1.0.0" + slash "^2.0.0" iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" @@ -9043,7 +9054,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= @@ -9170,20 +9181,20 @@ inquirer@^5.2.0: through "^2.3.6" inquirer@^6.0.0, inquirer@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.1.tgz#9943fc4882161bdb0b0c9276769c75b32dbfcd52" - integrity sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg== + version "6.2.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406" + integrity sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA== dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" + ansi-escapes "^3.2.0" + chalk "^2.4.2" cli-cursor "^2.1.0" cli-width "^2.0.0" - external-editor "^3.0.0" + external-editor "^3.0.3" figures "^2.0.0" - lodash "^4.17.10" + lodash "^4.17.11" mute-stream "0.0.7" run-async "^2.2.0" - rxjs "^6.1.0" + rxjs "^6.4.0" string-width "^2.1.0" strip-ansi "^5.0.0" through "^2.3.6" @@ -9231,11 +9242,6 @@ ip-regex@^2.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-3.0.0.tgz#0a934694b4066558c46294244a23cc33116bf732" - integrity sha512-T8wDtjy+Qf2TAPDQmBp0eGKJ8GavlWlUnamr3wRn6vvdZlKVuJXXMlSncYFRYgVHOM3If5NR1H4+OvVQU9Idvg== - ip@^1.1.0, ip@^1.1.4, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -9247,9 +9253,9 @@ ipaddr.js@1.8.0: integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= ipaddr.js@^1.5.2: - version "1.8.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.1.tgz#fa4b79fa47fd3def5e3b159825161c0a519c9427" - integrity sha1-+kt5+kf9Pe9eOxWYJRYcClGclCc= + 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== is-absolute-url@^2.0.0: version "2.1.0" @@ -9316,6 +9322,13 @@ is-ci@^1.0.10: dependencies: ci-info "^1.5.0" +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-cidr@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-1.0.0.tgz#fb5aacf659255310359da32cae03e40c6a1c2afc" @@ -9534,13 +9547,6 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= -is-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2" - integrity sha1-s2ExHYPG5dcmyr9eJQsCNxBvWuI= - dependencies: - symbol-observable "^0.2.2" - is-observable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" @@ -9954,11 +9960,6 @@ jest-environment-node@^23.4.0: jest-mock "^23.2.0" jest-util "^23.4.0" -jest-get-type@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" - integrity sha512-y2fFw3C+D0yjNSDp7ab1kcd6NUYfy3waPTlD8yWkAtiocJdBRQqNoRqVfMNxgj+IjT0V5cBIHJO0z9vuSSZ43Q== - jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" @@ -10131,16 +10132,6 @@ jest-util@^23.4.0: slash "^1.0.0" source-map "^0.6.0" -jest-validate@^21.1.0: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" - integrity sha512-k4HLI1rZQjlU+EC682RlQ6oZvLrE5SCh3brseQc24vbZTxzT/k/3urar5QMCVgjadmSO7lECeGdc6YxnM3yEGg== - dependencies: - chalk "^2.0.1" - jest-get-type "^21.2.0" - leven "^2.1.0" - pretty-format "^21.2.1" - jest-validate@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474" @@ -10603,51 +10594,42 @@ libnpx@^10.2.0: y18n "^4.0.0" yargs "^11.0.0" -lint-staged@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-6.1.1.tgz#cd08c4d9b8ccc2d37198d1c47ce77d22be6cf324" - integrity sha512-M/7bwLdXbeG7ZNLcasGeLMBDg60/w6obj3KOtINwJyxAxb53XGY0yH5FSZlWklEzuVbTtqtIfAajh6jYIN90AA== +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== dependencies: - app-root-path "^2.0.0" - chalk "^2.1.0" - commander "^2.11.0" - cosmiconfig "^4.0.0" + "@iamstarkov/listr-update-renderer" "0.4.1" + chalk "^2.3.1" + commander "^2.14.1" + cosmiconfig "^5.0.2" debug "^3.1.0" dedent "^0.7.0" - execa "^0.8.0" + del "^3.0.0" + execa "^1.0.0" find-parent-dir "^0.3.0" + g-status "^2.0.2" is-glob "^4.0.0" - jest-validate "^21.1.0" - listr "^0.13.0" - lodash "^4.17.4" - log-symbols "^2.0.0" - minimatch "^3.0.0" + is-windows "^1.0.2" + listr "^0.14.2" + lodash "^4.17.5" + log-symbols "^2.2.0" + micromatch "^3.1.8" npm-which "^3.0.1" p-map "^1.1.1" path-is-inside "^1.0.2" pify "^3.0.0" - staged-git-files "1.0.0" - stringify-object "^3.2.0" + please-upgrade-node "^3.0.2" + staged-git-files "1.1.2" + string-argv "^0.0.2" + stringify-object "^3.2.2" + yup "^0.26.10" listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= -listr-update-renderer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" - integrity sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc= - 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 "^1.0.2" - strip-ansi "^3.0.1" - listr-update-renderer@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" @@ -10662,16 +10644,6 @@ listr-update-renderer@^0.5.0: log-update "^2.3.0" strip-ansi "^3.0.1" -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - listr-verbose-renderer@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" @@ -10682,30 +10654,7 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" -listr@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d" - integrity sha1-ILsLowuuZg7oTMBQPfS+PVYjiH0= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-observable "^0.2.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.4.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.4.2" - stream-to-observable "^0.2.0" - strip-ansi "^3.0.1" - -listr@^0.14.1: +listr@^0.14.1, listr@^0.14.2: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== @@ -10815,6 +10764,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" @@ -10823,12 +10777,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= @@ -10922,6 +10893,11 @@ lodash.mergewith@^4.6.0: 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" @@ -10998,14 +10974,6 @@ log-timestamp@^0.2.1: dependencies: log-prefix "0.1.1" -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - log-update@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -11203,6 +11171,13 @@ marksy@^6.1.0: he "^1.1.1" marked "^0.3.12" +matcher@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-1.1.1.tgz#51d8301e138f840982b338b116bb0c09af62c1c2" + integrity sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg== + dependencies: + escape-string-regexp "^1.0.4" + material-colors@^1.2.1: version "1.2.6" resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" @@ -11276,13 +11251,13 @@ mem@^1.1.0: mimic-fn "^1.0.0" mem@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf" - integrity sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA== + version "4.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a" + integrity sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg== dependencies: map-age-cleaner "^0.1.1" mimic-fn "^1.0.0" - p-is-promise "^1.1.0" + p-is-promise "^2.0.0" memoize-one@^4.0.0: version "4.1.0" @@ -11395,7 +11370,12 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.36.0 < 2", mime-db@~1.37.0: +"mime-db@>= 1.36.0 < 2": + 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== @@ -11829,9 +11809,9 @@ no-case@^2.2.0, no-case@^2.3.2: lower-case "^1.1.1" node-abi@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.6.0.tgz#3adc69b28b334a0556fbadca378c7d18b8c82397" - integrity sha512-kCnEh6af6Z6DB7RFI/7LHNwqRjvJW7rgrv3lhIFoQ/+XhLPI/lJYwsk5vzvkldPWWgqnAMcuPF5S8/jj56kVOA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.7.1.tgz#a8997ae91176a5fbaa455b194976e32683cda643" + integrity sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw== dependencies: semver "^5.4.1" @@ -11927,11 +11907,12 @@ node-libs-browser@^2.0.0: vm-browserify "0.0.4" node-notifier@^5.2.1: - version "5.3.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.3.0.tgz#c77a4a7b84038733d5fb351aafd8a268bfe19a01" - integrity sha512-AhENzCSGZnZJgBARsUjnQ7DnZbzyP+HxlVXuD0xqAnvL8q+OqtSX7lGg9e8nHzwXkMMXNdVeqq4E2M3EUAqX6Q== + version "5.4.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.0.tgz#7b455fdce9f7de0c63538297354f3db468426e6a" + integrity sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ== dependencies: growly "^1.3.0" + is-wsl "^1.1.0" semver "^5.5.0" shellwords "^0.1.1" which "^1.3.0" @@ -11953,9 +11934,9 @@ node-pre-gyp@^0.10.0: tar "^4" node-releases@^1.0.0-alpha.11, node-releases@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.5.tgz#1dbee1380742125fe99e0476c456670bf3590b89" - integrity sha512-6C2K0x1QlYTz9wCueMN/DVZFcBVg/qsj2k9iV5gV/+OvG4KNrl7Nu7TWbWFQ3/Z2V10qVFQWtj5Xa+VBodcI6g== + version "1.1.7" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.7.tgz#b09a10394d0ed8f7778f72bb861dde68b146303b" + integrity sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA== dependencies: semver "^5.3.0" @@ -12024,10 +12005,20 @@ nopt@~1.0.10: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.4.0, "normalize-package-data@~1.0.1 || ^2.0.0", normalize-package-data@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== +normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.4.0, "normalize-package-data@~1.0.1 || ^2.0.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@~2.4.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.2.tgz#6b2abd85774e51f7936f1395e45acb905dc849b2" + integrity sha512-YcMnjqeoUckXTPKZSAsPjUPLxH85XotbpqK3w4RyCwdFQSU5FxxBys8buehkSfg0j9fKvV1hn7O0+8reEgkAiw== dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -12039,11 +12030,6 @@ normalize-path@2.0.1: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" integrity sha1-R4hqwWYnYNQmG32XnSQXCdPOP3o= -normalize-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" - integrity sha1-MtDkcvkf80VwHBWoMRAY07CpA3k= - normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -12089,9 +12075,9 @@ npm-audit-report@^1.0.9: console-control-strings "^1.1.0" npm-bundled@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" - integrity sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g== + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== npm-cache-filename@~1.0.2: version "1.0.2" @@ -12135,9 +12121,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.2.0" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.2.0.tgz#55a60e793e272f00862c7089274439a4cc31fc7f" - integrity sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ== + version "1.3.0" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.3.0.tgz#7f01e8e44408341379ca98cfd756e7b29bd2626c" + integrity sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -12367,9 +12353,9 @@ number-is-nan@^1.0.0: integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= nwsapi@^2.0.7: - version "2.0.9" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.9.tgz#77ac0cdfdcad52b6a1151a84e73254edc33ed016" - integrity sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.0.tgz#781065940aed90d9bb01ca5d0ce0fcf81c32712f" + integrity sha512-ZG3bLAvdHmhIjaQ/Db1qvBxsGvFMLIRpQszyqbg31VJ53UP++uZX1/gf3Ut96pdwN9AuDwlMqIYLm0UPCdUeHg== oauth-sign@~0.9.0: version "0.9.0" @@ -12401,9 +12387,9 @@ object-is@^1.0.1: integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= object-keys@^1.0.0, object-keys@^1.0.11, object-keys@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" - integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== + version "1.1.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" + integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== object-visit@^1.0.0: version "1.0.1" @@ -12566,16 +12552,6 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - ordered-ast-traverse@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz#6843a170bc0eee8b520cc8ddc1ddd3aa30fa057c" @@ -12675,6 +12651,11 @@ p-is-promise@^1.1.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= +p-is-promise@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5" + integrity sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg== + p-lazy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-lazy/-/p-lazy-1.0.0.tgz#ec53c802f2ee3ac28f166cc82d0b2b02de27a835" @@ -13077,6 +13058,13 @@ pkg-up@^1.0.0: dependencies: find-up "^1.0.0" +please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" + integrity sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ== + dependencies: + semver-compare "^1.0.0" + pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" @@ -13088,9 +13076,9 @@ pn@^1.1.0: integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== popper.js@^1.14.4: - version "1.14.6" - resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.6.tgz#ab20dd4edf9288b8b3b6531c47c361107b60b4b0" - integrity sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA== + version "1.14.7" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e" + integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== portfinder@^1.0.9: version "1.0.20" @@ -13437,7 +13425,7 @@ postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.23, postcss@^6.0.8: source-map "^0.6.1" supports-color "^5.4.0" -postcss@^7.0.0, postcss@^7.0.13: +postcss@^7.0.0, postcss@^7.0.14: version "7.0.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5" integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg== @@ -13597,9 +13585,9 @@ prettier@1.9.2: integrity sha512-piXx9N2WT8hWb7PBbX1glAuJVIkEyUV9F5fMXFINpZ0x3otVOFKKeGmeuiclFJlP/UrgTckyV606VjH2rNK4bw== prettier@^1.12.1: - version "1.16.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.1.tgz#534c2c9d7853f8845e5e078384e71973bd74089f" - integrity sha512-XXUITwIkGb3CPJ2hforHah/zTINRyie5006Jd2HKy2qz7snEJXl0KLfsJZW/wst9g6R2rFvqba3VpNYdu1hDcA== + version "1.16.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" + integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== pretty-bytes@^4.0.2: version "4.0.2" @@ -13614,14 +13602,6 @@ pretty-error@^2.0.2, pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" - integrity sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A== - dependencies: - ansi-regex "^3.0.0" - ansi-styles "^3.2.0" - pretty-format@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" @@ -13717,12 +13697,17 @@ promzard@^0.3.0: 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.6.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" - integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ== + version "15.7.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.1.tgz#2fa61e0a699d428b40320127733ee2931f05d9d1" + integrity sha512-f8Lku2z9kERjOCcnDOPm68EBJAO2K00Q5mSgPAUE/gJuBgsYLbVy6owSrtcHj90zt8PvW+z0qaIIgsIhHOa1Qw== dependencies: - loose-envify "^1.3.1" object-assign "^4.1.1" + react-is "^16.8.1" + +property-expr@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f" + integrity sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g== property-information@^5.0.0, property-information@^5.0.1: version "5.0.1" @@ -14108,10 +14093,10 @@ react-docgen-typescript@^1.2.3, react-docgen-typescript@^1.9.0: resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.12.3.tgz#fe62a5ce82e93573e316366e53adfe8273121c70" integrity sha512-s1XswWs4ykNdWKsPyfM4qptV5dT8nnjnVi2IcjoS/vGlRNYrc0TkW0scVOrinHZ+ndKhPqA4iVNrdwhxZBzJcg== -react-docgen@^3.0.0-rc.1: - version "3.0.0-rc.2" - resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-3.0.0-rc.2.tgz#5939c64699fd9959da6d97d890f7b648e542dbcc" - integrity sha512-tXbIvq7Hxdc92jW570rztqsz0adtWEM5FX8bShJYozT2Y6L/LeHvBMQcED6mSqJ72niiNMPV8fi3S37OHrGMEw== +react-docgen@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-3.0.0.tgz#79c6e1b1870480c3c2bc1a65bede0577a11c38cd" + integrity sha512-2UseoLWabFNXuk1Foz4VDPSIAkxz+1Hmmq4qijzUmYHDq0ZSloKDLXtGLpQRcAi/M76hRpPtH1rV4BI5jNAOnQ== dependencies: "@babel/parser" "^7.1.3" "@babel/runtime" "^7.0.0" @@ -14122,14 +14107,14 @@ react-docgen@^3.0.0-rc.1: recast "^0.16.0" react-dom@^16.6.3, react-dom@^16.7.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8" - integrity sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg== + version "16.8.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.1.tgz#ec860f98853d09d39bafd3a6f1e12389d283dbb4" + integrity sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.12.0" + scheduler "^0.13.1" react-draggable@3.x, "react-draggable@^2.2.6 || ^3.0.3": version "3.1.1" @@ -14140,9 +14125,9 @@ react-draggable@3.x, "react-draggable@^2.2.6 || ^3.0.3": prop-types "^15.6.0" react-error-overlay@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.2.tgz#888957b884d4b25b083a82ad550f7aad96585394" - integrity sha512-7kEBKwU9R8fKnZJBRa5RSIfay4KJwnYvKB6gODGicUmDSAhQJ7Tdnll5S0RLtYrzRfMVXlqYw61rzrSpP4ThLQ== + 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== react-fuzzy@^0.5.2: version "0.5.2" @@ -14174,9 +14159,9 @@ react-highlight-words@0.11.0: prop-types "^15.5.8" react-hot-loader@^4.3.6: - version "4.6.3" - resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.6.3.tgz#d9c8923c45b35fd51538ba4297081a00be6bccb1" - integrity sha512-FUvRO8dwbeLnc3mgLn8ARuSh8NnLBYJyiRjFn+grY/5GupSyPqv0U7ixgwXro77hwDplhm8z9wU7FlJ8kZqiAQ== + 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== dependencies: fast-levenshtein "^2.0.6" global "^4.3.0" @@ -14209,10 +14194,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: - version "16.7.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa" - integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g== +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-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" @@ -14230,9 +14215,9 @@ react-modal@^3.6.1: warning "^3.0.0" react-popper@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.2.tgz#e723a0a7fe1c42099a13e5d494e9d7d74b352af4" - integrity sha512-UbFWj55Yt9uqvy0oZ+vULDL2Bw1oxeZF9/JzGyxQ5ypgauRH/XlarA5+HLZWro/Zss6Ht2kqpegtb6sYL8GUGw== + version "1.3.3" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.3.tgz#2c6cef7515a991256b4f0536cd4bdcb58a7b6af6" + integrity sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w== dependencies: "@babel/runtime" "^7.1.2" create-react-context "<=0.2.2" @@ -14299,21 +14284,21 @@ react-style-proptype@^3.0.0: prop-types "^15.5.4" react-table@^6.8.6: - version "6.8.6" - resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.8.6.tgz#a0ad8b4839319052d5befc012603fb161e52ede3" - integrity sha1-oK2LSDkxkFLVvvwBJgP7Fh5S7eM= + version "6.9.1" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.9.1.tgz#50a1713a4de90089f14dcb8cf0f313992ad93a20" + integrity sha512-QWAwEX24kZPjimhrqRMcdJRi0LX+4MWJG7p9WmAPrfEoYG2HTR1odxa5jLPu7sKVkgsUp0PtZ2jxQ4aXabRJNA== 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.7.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.7.0.tgz#1ca96c2b450ab47c36ba92cd8c03fcefc52ea01c" - integrity sha512-tFbhSjknSQ6+ttzmuGdv+SjQfmvGcq3PFKyPItohwhhOBmRoTf1We3Mlt3rJtIn85mjPXOkKV+TaKK4irvk9Yg== + 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== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" - react-is "^16.7.0" - scheduler "^0.12.0" + react-is "^16.8.1" + scheduler "^0.13.1" react-textarea-autosize@^7.0.4: version "7.1.0" @@ -14359,14 +14344,14 @@ react-virtualized@^9.21.0: react-lifecycles-compat "^3.0.4" react@^16.6.3, react@^16.7.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381" - integrity sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A== + version "16.8.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.1.tgz#ae11831f6cb2a05d58603a976afc8a558e852c4a" + integrity sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.12.0" + scheduler "^0.13.1" reactcss@^1.2.0: version "1.2.3" @@ -14461,6 +14446,15 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" + integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc= + dependencies: + normalize-package-data "^2.3.2" + parse-json "^4.0.0" + pify "^3.0.0" + read@1, read@~1.0.1, read@~1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" @@ -14468,7 +14462,7 @@ read@1, read@~1.0.1, read@~1.0.7: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -14510,7 +14504,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= @@ -14520,7 +14514,7 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@^2.0.0, readdirp@^2.2.1: +readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== @@ -14539,9 +14533,9 @@ readline2@^1.0.1: mute-stream "0.0.5" realpath-native@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560" - integrity sha512-+S3zTvVt9yTntFrBpm7TQmQ3tzpCrnA1a/y+3cUHAc9ZR6aIjG0WNLR+Rj79QpJktY+VeW/TQtFlQ1bzsehI8g== + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== dependencies: util.promisify "^1.0.0" @@ -14556,6 +14550,16 @@ recast@^0.12.5: private "~0.1.5" source-map "~0.6.1" +recast@^0.14.7: + version "0.14.7" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" + integrity sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A== + dependencies: + ast-types "0.11.3" + esprima "~4.0.0" + private "~0.1.5" + source-map "~0.6.1" + recast@^0.15.0: version "0.15.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.15.5.tgz#6871177ee26720be80d7624e4283d5c855a5cb0b" @@ -14717,13 +14721,13 @@ regexp-replace-loader@^1.0.1: loader-utils "^1.0.2" regexp-tree@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.0.tgz#a56ad7746097888ea16457479029ec9345b96ab0" - integrity sha512-rHQv+tzu+0l3KS/ERabas1yK49ahNVxuH40WcPg53CzP5p8TgmmyBgHELLyJcvjhTD0e5ahSY6C76LbEVtr7cg== + 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 "^10.0.3" + yargs "^12.0.5" regexp.prototype.flags@^1.2.0: version "1.2.0" @@ -14948,6 +14952,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +reselect@*, reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" + integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -15000,7 +15009,7 @@ resolve@1.1.7, resolve@~1.1.0: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.x, resolve@^1.1.6, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: +resolve@1.x, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== @@ -15115,6 +15124,11 @@ run-async@^2.0.0, run-async@^2.2.0: dependencies: is-promise "^2.1.0" +run-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" + integrity sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -15132,17 +15146,17 @@ rx-lite@^3.1.2: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= -rxjs@^5.4.2, rxjs@^5.5.2: +rxjs@^5.5.2: version "5.5.12" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== dependencies: symbol-observable "1.0.1" -rxjs@^6.1.0, rxjs@^6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" - integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw== +rxjs@^6.1.0, rxjs@^6.3.3, rxjs@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504" + integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw== dependencies: tslib "^1.9.0" @@ -15241,10 +15255,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.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b" - integrity sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw== +scheduler@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.1.tgz#1a217df1bfaabaf4f1b92a9127d5d732d85a9591" + integrity sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -15301,6 +15315,11 @@ selfsigned@^1.9.1: dependencies: node-forge "0.7.5" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -15455,9 +15474,9 @@ shallow-clone@^1.0.0: mixin-object "^2.0.1" shallow-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.0.0.tgz#508d1838b3de590ab8757b011b25e430900945f7" - integrity sha1-UI0YOLPeWQq4dXsBGyXkMJAJRfc= + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.1.0.tgz#cc022f030dcba0d1c198abf658a3c6c744e171ca" + integrity sha512-0SW1nWo1hnabO62SEeHsl8nmTVVEzguVWZCj5gaQrgWAxz/BaCja4OWdJBWLVPDxdtE/WU7c98uUCCXyPHSCvw== shallowequal@^0.2.2: version "0.2.2" @@ -15536,6 +15555,13 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +simple-git@^1.85.0: + version "1.107.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.107.0.tgz#12cffaf261c14d6f450f7fdb86c21ccee968b383" + integrity sha512-t4OK1JRlp4ayKRfcW6owrWcRVLyHRUlhGd0uN6ZZTqfDq8a5XpcUdOKiGRNobHEuMtNqzp0vcJNvhYWwh5PsQA== + dependencies: + debug "^4.0.1" + simple-is@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" @@ -15561,10 +15587,15 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slate-base64-serializer@^0.2.36: - version "0.2.94" - resolved "https://registry.yarnpkg.com/slate-base64-serializer/-/slate-base64-serializer-0.2.94.tgz#b908c3af481b9a0ead78f313653414c4b2b4b2d5" - integrity sha512-jltk0PwOS1lu/TRHTN/qW67+VSXlbX1d0GottPloKjYwHBbH47GVT/G+hmkOdrGXzV7P6220BvQwN8Xq2RaJ5A== + 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== dependencies: isomorphic-base64 "^1.0.2" @@ -15824,7 +15855,7 @@ 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.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== @@ -15983,10 +16014,10 @@ stack-utils@^1.0.1: resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== -staged-git-files@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.0.0.tgz#cdb847837c1fcc52c08a872d4883cc0877668a80" - integrity sha1-zbhHg3wfzFLAioctSIPMCHdmioA= +staged-git-files@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.2.tgz#4326d33886dc9ecfa29a6193bf511ba90a46454b" + integrity sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA== static-extend@^0.1.1: version "0.1.2" @@ -16063,13 +16094,6 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= -stream-to-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10" - integrity sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA= - dependencies: - any-observable "^0.2.0" - strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -16080,6 +16104,11 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= +string-argv@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736" + integrity sha1-2sMECGkMIfPDYwo/86BYd73L1zY= + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -16185,7 +16214,7 @@ stringifier@^1.3.0: traverse "^0.6.6" type-name "^2.0.1" -stringify-object@^3.2.0: +stringify-object@^3.2.2: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -16262,11 +16291,6 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-indent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= - strip-json-comments@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" @@ -16390,11 +16414,6 @@ symbol-observable@1.0.1: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= -symbol-observable@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" - integrity sha1-lag9smGG1q9+ehjb2XYKL4bQj0A= - symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -16412,6 +16431,11 @@ symbol.prototype.description@^1.0.0: dependencies: has-symbols "^1.0.0" +synchronous-promise@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.6.tgz#de76e0ea2b3558c1e673942e47e714a930fa64aa" + integrity sha512-TyOuWLwkmtPL49LHCX1caIwHjRzcVd62+GF6h8W/jHOeZUFHpnd2XJDVuUlaTaLPH1nuu2M69mfHr5XbQJnf/g== + systemjs-plugin-css@^0.1.36: version "0.1.37" resolved "https://registry.yarnpkg.com/systemjs-plugin-css/-/systemjs-plugin-css-0.1.37.tgz#684847252ca69b7da24a1201094c86274324e82f" @@ -16500,27 +16524,27 @@ term-size@^1.2.0: execa "^0.7.0" terser-webpack-plugin@^1.1.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz#7545da9ae5f4f9ae6a0ac961eb46f5e7c845cc26" - integrity sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw== + 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== dependencies: cacache "^11.0.2" find-cache-dir "^2.0.0" schema-utils "^1.0.0" serialize-javascript "^1.4.0" source-map "^0.6.1" - terser "^3.8.1" + terser "^3.16.1" webpack-sources "^1.1.0" worker-farm "^1.5.2" -terser@^3.8.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.14.1.tgz#cc4764014af570bc79c79742358bd46926018a32" - integrity sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw== +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== dependencies: commander "~2.17.1" source-map "~0.6.1" - source-map-support "~0.5.6" + source-map-support "~0.5.9" test-exclude@^4.2.1: version "4.2.3" @@ -16595,9 +16619,9 @@ timers-browserify@^2.0.4: setimmediate "^1.0.4" tiny-emitter@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" - integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow== + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== tiny-relative-date@^1.3.0: version "1.3.0" @@ -16703,6 +16727,11 @@ toposort@^1.0.0: resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + touch@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/touch/-/touch-2.0.2.tgz#ca0b2a3ae3211246a61b16ba9e6cbf1596287164" @@ -16711,11 +16740,11 @@ touch@^2.0.1: nopt "~1.0.10" tough-cookie@>=2.3.3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.0.tgz#d2bceddebde633153ff20a52fa844a0dc71dacef" - integrity sha512-LHMvg+RBP/mAVNqVbOX8t+iJ+tqhBA/t49DuI7+IDAWHrASnesqSu1vWbKB7UrE2yk+HMFUBMadRGMkB4VCfog== + 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 "^3.0.0" + ip-regex "^2.1.0" psl "^1.1.28" punycode "^2.1.1" @@ -16922,9 +16951,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.0.3, typescript@^3.2.2: - version "3.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d" - integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg== + version "3.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3.tgz#f1657fc7daa27e1a8930758ace9ae8da31403221" + integrity sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A== ua-parser-js@^0.7.18: version "0.7.19" @@ -17138,7 +17167,7 @@ unzip-response@^2.0.1: resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= -upath@^1.0.5, upath@^1.1.0: +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== @@ -17436,9 +17465,9 @@ warning@^3.0.0: loose-envify "^1.0.0" warning@^4.0.1, warning@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.2.tgz#aa6876480872116fa3e11d434b0d0d8d91e44607" - integrity sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug== + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== dependencies: loose-envify "^1.0.0" @@ -17568,9 +17597,9 @@ webpack-dev-middleware@3.4.0: webpack-log "^2.0.0" webpack-dev-middleware@^3.4.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.5.1.tgz#9265b7742ef50f54f54c1d9af022fc17c1be9b88" - integrity sha512-4dwCh/AyMOYAybggUr8fiCkRnjVDp+Cqlr9c+aaNB3GJYgRGYQWJ1YX/WAKUNA9dPNHZ6QSN2lYDKqjKSI8Vqw== + 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" mime "^2.3.1" @@ -17687,9 +17716,9 @@ webpack@4.19.1: webpack-sources "^1.2.0" webpack@^4.23.1: - version "4.29.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.0.tgz#f2cfef83f7ae404ba889ff5d43efd285ca26e750" - integrity sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w== + version "4.29.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.3.tgz#e0b406a7b4201ed5e4fb4f84fd7359f9a7db4647" + integrity sha512-xPJvFeB+8tUflXFq+OgdpiSnsCD5EANyv56co5q8q8+YtEasn5Sj3kzY44mta+csCIEB0vneSxnuaHkOL2h94A== dependencies: "@webassemblyjs/ast" "1.7.11" "@webassemblyjs/helper-module-context" "1.7.11" @@ -17710,7 +17739,7 @@ webpack@^4.23.1: mkdirp "~0.5.0" neo-async "^2.5.0" node-libs-browser "^2.0.0" - schema-utils "^0.4.4" + schema-utils "^1.0.0" tapable "^1.1.0" terser-webpack-plugin "^1.1.0" watchpack "^1.5.0" @@ -17970,6 +17999,14 @@ yargs-parser@10.x, yargs-parser@^10.1.0: dependencies: camelcase "^4.1.0" +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" @@ -17977,13 +18014,6 @@ yargs-parser@^5.0.0: dependencies: camelcase "^3.0.0" -yargs-parser@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" - integrity sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ== - dependencies: - camelcase "^4.1.0" - yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" @@ -18009,24 +18039,6 @@ yargs@12.0.2: y18n "^3.2.1 || ^4.0.0" yargs-parser "^10.1.0" -yargs@^10.0.3: - version "10.1.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" - integrity sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^8.1.0" - yargs@^11.0.0, yargs@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" @@ -18045,6 +18057,24 @@ yargs@^11.0.0, yargs@^11.1.0: y18n "^3.2.1" yargs-parser "^9.0.2" +yargs@^12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + yargs@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" @@ -18145,6 +18175,18 @@ yn@^3.0.0: resolved "https://registry.yarnpkg.com/yn/-/yn-3.0.0.tgz#0073c6b56e92aed652fbdfd62431f2d6b9a7a091" integrity sha512-+Wo/p5VRfxUgBUGy2j/6KX2mj9AYJWOHuhMjMcbBFc3y54o9/4buK1ksBvuiK01C3kby8DH9lSmJdSxw+4G/2Q== +yup@^0.26.10: + version "0.26.10" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.26.10.tgz#3545839663289038faf25facfc07e11fd67c0cb1" + integrity sha512-keuNEbNSnsOTOuGCt3UJW69jDE3O4P+UHAakO7vSeFMnjaitcmlbij/a3oNb9g1Y1KvSKH/7O1R2PQ4m4TRylw== + dependencies: + "@babel/runtime" "7.0.0" + fn-name "~2.0.1" + lodash "^4.17.10" + property-expr "^1.5.0" + synchronous-promise "^2.0.5" + toposort "^2.0.2" + zip-stream@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"